The   Sutherland-Hodgman clipping algorithm   finds the polygon that is the intersection between an arbitrary polygon (the “subject polygon”) and a convex polygon (the “clip polygon”).

It is used in computer graphics (especially 2D graphics) to reduce the complexity of a scene being displayed by eliminating parts of a polygon that do not need to be displayed.



Task

Take the closed polygon defined by the points:

 



[
(
50
,
150
)
,
(
200
,
50
)
,
(
350
,
150
)
,
(
350
,
300
)
,
(
250
,
300
)
,
(
200
,
250
)
,
(
150
,
350
)
,
(
100
,
250
)
,
(
100
,
200
)
]


{\displaystyle [(50,150),(200,50),(350,150),(350,300),(250,300),(200,250),(150,350),(100,250),(100,200)]}


and clip it by the rectangle defined by the points:

 



[
(
100
,
100
)
,
(
300
,
100
)
,
(
300
,
300
)
,
(
100
,
300
)
]


{\displaystyle [(100,100),(300,100),(300,300),(100,300)]}


Print the sequence of points that define the resulting clipped polygon.



Extra credit

Display all three polygons on a graphical surface, using a different color for each polygon and filling the resulting polygon.

(When displaying you may use either a north-west or a south-west origin, whichever is more convenient for your display mechanism.)




defmodule SutherlandHodgman do
  defp inside(cp1, cp2, p), do: (cp2.x-cp1.x)*(p.y-cp1.y) > (cp2.y-cp1.y)*(p.x-cp1.x)
 
  defp intersection(cp1, cp2, s, e) do
    {dcx, dcy} = {cp1.x-cp2.x, cp1.y-cp2.y}
    {dpx, dpy} = {s.x-e.x, s.y-e.y}
    n1 = cp1.x*cp2.y - cp1.y*cp2.x
    n2 = s.x*e.y - s.y*e.x
    n3 = 1.0 / (dcx*dpy - dcy*dpx)
    %{x: (n1*dpx - n2*dcx) * n3, y: (n1*dpy - n2*dcy) * n3}
  end
 
  def polygon_clipping(subjectPolygon, clipPolygon) do
    Enum.chunk([List.last(clipPolygon) | clipPolygon], 2, 1)
    |> Enum.reduce(subjectPolygon, fn [cp1,cp2],acc ->
         Enum.chunk([List.last(acc) | acc], 2, 1)
         |> Enum.reduce([], fn [s,e],outputList ->
              case {inside(cp1, cp2, e), inside(cp1, cp2, s)} do
                {true,  true} -> [e | outputList]
                {true, false} -> [e, intersection(cp1,cp2,s,e) | outputList]
                {false, true} -> [intersection(cp1,cp2,s,e) | outputList]
                _             -> outputList
              end
            end)
         |> Enum.reverse
       end)
  end
end
 
subjectPolygon = [[50, 150], [200, 50], [350, 150], [350, 300], [250, 300],
                  [200, 250], [150, 350], [100, 250], [100, 200]]
                 |> Enum.map(fn [x,y] -> %{x: x, y: y} end)
 
clipPolygon = [[100, 100], [300, 100], [300, 300], [100, 300]]
              |> Enum.map(fn [x,y] -> %{x: x, y: y} end)
 
SutherlandHodgman.polygon_clipping(subjectPolygon, clipPolygon)
|> Enum.each(&IO.inspect/1)