Sutherland-Hodgman polygon clipping: Difference between revisions
→{{header|MATLAB}}: Added PureBasic |
|||
Line 359: | Line 359: | ||
</pre> |
</pre> |
||
[[File:Sutherland-Hodgman_haskell.png]] |
[[File:Sutherland-Hodgman_haskell.png]] |
||
=={{header|J}}== |
|||
'''Solution:''' |
|||
<lang j> NB. assumes counterclockwise orientation. |
|||
NB. determine whether point y is inside edge x. |
|||
isinside=:0< [:-/ .* {.@[ -~"1 {:@[,:] |
|||
NB. (p0,:p1) intersection (p2,:p3) |
|||
intersection=:|:@[ (+/ .* (,-.)) [:{. ,.&(-~/) %.~ -&{: |
|||
SutherlandHodgman=:4 :0 NB. clip S-H subject |
|||
clip=.2 ]\ (,{.) x |
|||
subject=.y |
|||
for_edge. clip do. |
|||
S=.{:input=.subject |
|||
subject=.0 2$0 |
|||
for_E. input do. |
|||
if. edge isinside E do. |
|||
if. -.edge isinside S do. |
|||
subject=.subject,edge intersection S,:E end. |
|||
subject=.subject,E |
|||
elseif. edge isinside S do. |
|||
subject=.subject,edge intersection S,:E end. |
|||
S=.E |
|||
end. |
|||
end. |
|||
subject |
|||
)</lang> |
|||
'''Example use:''' |
|||
<lang j> subject=. 50 150,200 50,350 150,350 300,250 300,200 250,150 350,100 250,:100 200 |
|||
clip=. 100 100,300 100,300 300,:100 300 |
|||
clip SutherlandHodgman subject |
|||
100 116.667 |
|||
125 100 |
|||
275 100 |
|||
300 116.667 |
|||
300 300 |
|||
250 300 |
|||
200 250 |
|||
175 300 |
|||
125 300 |
|||
100 250</lang> |
|||
=={{header|MATLAB}}== |
=={{header|MATLAB}}== |
||
Line 456: | Line 499: | ||
>> patch(clippedSubject(:,1),clippedSubject(:,2),0);</lang> |
>> patch(clippedSubject(:,1),clippedSubject(:,2),0);</lang> |
||
[[File:Sutherland-Hodgman_MATLAB.png]] |
[[File:Sutherland-Hodgman_MATLAB.png]] |
||
=={{header|PureBasic}}== |
=={{header|PureBasic}}== |
||
{{trans|Go}} |
{{trans|Go}} |
Revision as of 23:51, 22 June 2011
![Task](http://static.miraheze.org/rosettacodewiki/thumb/b/ba/Rcode-button-task-crushed.png/64px-Rcode-button-task-crushed.png)
You are encouraged to solve this task according to the task description, using any language you may know.
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.
For this task, take the closed polygon defined by the points:
and clip it by the rectangle defined by the points:
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.)
Ada
<lang Ada>with Ada.Containers.Doubly_Linked_Lists; with Ada.Text_IO;
procedure Main is
package FIO is new Ada.Text_IO.Float_IO (Float);
type Point is record X, Y : Float; end record;
function "-" (Left, Right : Point) return Point is begin return (Left.X - Right.X, Left.Y - Right.Y); end "-";
type Edge is array (1 .. 2) of Point;
package Point_Lists is new Ada.Containers.Doubly_Linked_Lists (Element_Type => Point); use type Point_Lists.List; subtype Polygon is Point_Lists.List;
function Inside (P : Point; E : Edge) return Boolean is begin return (E (2).X - E (1).X) * (P.Y - E (1).Y) > (E (2).Y - E (1).Y) * (P.X - E (1).X); end Inside;
function Intersecton (P1, P2 : Point; E : Edge) return Point is DE : Point := E (1) - E (2); DP : Point := P1 - P2; N1 : Float := E (1).X * E (2).Y - E (1).Y * E (2).X; N2 : Float := P1.X * P2.Y - P1.Y * P2.X; N3 : Float := 1.0 / (DE.X * DP.Y - DE.Y * DP.X); begin return ((N1 * DP.X - N2 * DE.X) * N3, (N1 * DP.Y - N2 * DE.Y) * N3); end Intersecton;
function Clip (P, C : Polygon) return Polygon is use Point_Lists; A, B, S, E : Cursor; Inputlist : List; Outputlist : List := P; AB : Edge; begin A := C.First; B := C.Last; while A /= No_Element loop AB := (Element (B), Element (A)); Inputlist := Outputlist; Outputlist.Clear; S := Inputlist.Last; E := Inputlist.First; while E /= No_Element loop if Inside (Element (E), AB) then if not Inside (Element (S), AB) then Outputlist.Append (Intersecton (Element (S), Element (E), AB)); end if; Outputlist.Append (Element (E)); elsif Inside (Element (S), AB) then Outputlist.Append (Intersecton (Element (S), Element (E), AB)); end if; S := E; E := Next (E); end loop; B := A; A := Next (A); end loop; return Outputlist; end Clip;
procedure Print (P : Polygon) is use Point_Lists; C : Cursor := P.First; begin Ada.Text_IO.Put_Line ("{"); while C /= No_Element loop Ada.Text_IO.Put (" ("); FIO.Put (Element (C).X, Exp => 0); Ada.Text_IO.Put (','); FIO.Put (Element (C).Y, Exp => 0); Ada.Text_IO.Put (')'); C := Next (C); if C /= No_Element then Ada.Text_IO.Put (','); end if; Ada.Text_IO.New_Line; end loop; Ada.Text_IO.Put_Line ("}"); end Print;
Source : Polygon; Clipper : Polygon; Result : Polygon;
begin
Source.Append ((50.0, 150.0)); Source.Append ((200.0, 50.0)); Source.Append ((350.0, 150.0)); Source.Append ((350.0, 300.0)); Source.Append ((250.0, 300.0)); Source.Append ((200.0, 250.0)); Source.Append ((150.0, 350.0)); Source.Append ((100.0, 250.0)); Source.Append ((100.0, 200.0)); Clipper.Append ((100.0, 100.0)); Clipper.Append ((300.0, 100.0)); Clipper.Append ((300.0, 300.0)); Clipper.Append ((100.0, 300.0)); Result := Clip (Source, Clipper); Print (Result);
end Main;</lang>
output:
{ (100.00000,116.66667), (125.00000,100.00000), (275.00000,100.00000), (300.00000,116.66667), (300.00000,300.00000), (250.00000,300.00000), (200.00000,250.00000), (175.00000,300.00000), (125.00000,300.00000), (100.00000,250.00000) }
D
No extra credit.
<lang d>import std.stdio, std.typecons;
alias Tuple!(double, "x", double, "y") Point;
Point[] clip(const Point[] pts, const Point[] clips) {
bool isInside(Point p, Point cp1, Point cp2) { return (cp2.x - cp1.x) * (p.y - cp1.y) > (cp2.y - cp1.y) * (p.x - cp1.x); }
Point intersection(Point cp1, Point cp2, Point s, Point e) { auto dc = Point(cp1.x - cp2.x, cp1.y - cp2.y); auto dp = Point(s.x - e.x, s.y - e.y); auto n1 = cp1.x * cp2.y - cp1.y * cp2.x; auto n2 = s.x * e.y - s.y * e.x; auto n3 = 1 / (dc.x * dp.y - dc.y * dp.x); return Point((n1 * dp.x - n2 * dc.x) * n3, (n1 * dp.y - n2 * dc.y) * n3); }
auto result = pts.dup; // save input Point cp1 = clips[$ - 1]; // not const foreach (cp2; clips) { auto inputList = result; result = []; auto s = inputList[$ - 1]; foreach (e; inputList) { if (isInside(e, cp1, cp2)) { if (!isInside(s, cp1, cp2)) result ~= intersection(cp1, cp2, s, e); result ~= e; } else if (isInside(s, cp1, cp2)) { result ~= intersection(cp1, cp2, s, e); } s = e; } cp1 = cp2; } return result;
}
void main() {
alias Point P; auto subjectPolygon = [P(50, 150), P(200, 50), P(350, 150), P(350, 300), P(250, 300), P(200, 250), P(150, 350), P(100, 250), P(100, 200)];
auto clipPolygon = [P(100, 100), P(300, 100), P(300, 300), P(100, 300)];
foreach (p; clip(subjectPolygon, clipPolygon)) writeln(p);
}</lang> Output:
Tuple!(double,"x",double,"y")(100, 116.667) Tuple!(double,"x",double,"y")(125, 100) Tuple!(double,"x",double,"y")(275, 100) Tuple!(double,"x",double,"y")(300, 116.667) Tuple!(double,"x",double,"y")(300, 300) Tuple!(double,"x",double,"y")(250, 300) Tuple!(double,"x",double,"y")(200, 250) Tuple!(double,"x",double,"y")(175, 300) Tuple!(double,"x",double,"y")(125, 300) Tuple!(double,"x",double,"y")(100, 250)
Go
No extra credit today. <lang go>package main
import "fmt"
type point struct {
x, y float32
}
var subjectPolygon = []point{{50, 150}, {200, 50}, {350, 150}, {350, 300},
{250, 300}, {200, 250}, {150, 350}, {100, 250}, {100, 200}}
var clipPolygon = []point{{100, 100}, {300, 100}, {300, 300}, {100, 300}}
func main() {
var cp1, cp2, s, e point inside := func(p point) bool { return (cp2.x-cp1.x)*(p.y-cp1.y) > (cp2.y-cp1.y)*(p.x-cp1.x) } intersection := func() (p point) { 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 / (dcx*dpy - dcy*dpx) p.x = (n1*dpx - n2*dcx) * n3 p.y = (n1*dpy - n2*dcy) * n3 return } outputList := subjectPolygon cp1 = clipPolygon[len(clipPolygon)-1] for _, cp2 = range clipPolygon { // WP clipEdge is cp1,cp2 here inputList := outputList outputList = nil s = inputList[len(inputList)-1] for _, e = range inputList { if inside(e) { if !inside(s) { outputList = append(outputList, intersection()) } outputList = append(outputList, e) } else if inside(s) { outputList = append(outputList, intersection()) } s = e } cp1 = cp2 } fmt.Println(outputList)
}</lang> Output:
[{100 116.66667} {125 100} {275 100} {300 116.66667} {300 300} {250 300} {200 250} {175 300} {125 300} {100 250}]
Haskell
<lang haskell> module SuthHodgClip (clipTo) where
import Data.List
type Pt a = (a, a) type Ln a = (Pt a, Pt a) type Poly a = [Pt a]
-- Return a polygon from a list of points. polyFrom ps = last ps : ps
-- Return a list of lines from a list of points. linesFrom pps@(_:ps) = zip pps ps
-- Return true if the point (x,y) is on or to the left of the oriented line -- defined by (px,py) and (qx,qy). (.|) :: (Num a, Ord a) => Pt a -> Ln a -> Bool (x,y) .| ((px,py),(qx,qy)) = (qx-px)*(y-py) >= (qy-py)*(x-px)
-- Return the intersection of two lines. (><) :: Fractional a => Ln a -> Ln a -> Pt a ((x1,y1),(x2,y2)) >< ((x3,y3),(x4,y4)) =
let (r,s) = (x1*y2-y1*x2, x3*y4-y3*x4) (t,u,v,w) = (x1-x2, y3-y4, y1-y2, x3-x4) d = t*u-v*w in ((r*w-t*s)/d, (r*u-v*s)/d)
-- Intersect the line segment (p0,p1) with the clipping line's left halfspace, -- returning the point closest to p1. In the special case where p0 lies outside -- the halfspace and p1 lies inside we return both the intersection point and -- p1. This ensures we will have the necessary segment along the clipping line. (-|) :: (Fractional a, Ord a) => Ln a -> Ln a -> [Pt a] ln@(p0, p1) -| clipLn =
case (p0 .| clipLn, p1 .| clipLn) of (False, False) -> [] (False, True) -> [isect, p1] (True, False) -> [isect] (True, True) -> [p1] where isect = ln >< clipLn
-- Intersect the polygon with the clipping line's left halfspace. (<|) :: (Fractional a, Ord a) => Poly a -> Ln a -> Poly a poly <| clipLn = polyFrom $ concatMap (-| clipLn) (linesFrom poly)
-- Intersect a target polygon with a clipping polygon. The latter is assumed to -- be convex. clipTo :: (Fractional a, Ord a) => [Pt a] -> [Pt a] -> [Pt a] targPts `clipTo` clipPts =
let targPoly = polyFrom targPts clipLines = linesFrom (polyFrom clipPts) in foldl' (<|) targPoly clipLines
</lang>
Print the resulting list of points and display the polygons in a window.
<lang haskell>
import Graphics.HGL
import SuthHodgClip
targPts = [( 50,150), (200, 50), (350,150), (350,300), (250,300),
(200,250), (150,350), (100,250), (100,200)] :: [(Float,Float)]
clipPts = [(100,100), (300,100), (300,300), (100,300)] :: [(Float,Float)]
toInts = map (\(a,b) -> (round a, round b)) complete xs = last xs : xs
drawSolid w c = drawInWindow w . withRGB c . polygon drawLines w p = drawInWindow w . withPen p . polyline . toInts . complete
blue = RGB 0x99 0x99 0xff green = RGB 0x99 0xff 0x99 pink = RGB 0xff 0x99 0x99 white = RGB 0xff 0xff 0xff
main = do
let resPts = targPts `clipTo` clipPts sz = 400 win = [(0,0), (sz,0), (sz,sz), (0,sz)] runWindow "Sutherland-Hodgman Polygon Clipping" (sz,sz) $ \w -> do print $ toInts resPts penB <- createPen Solid 3 blue penP <- createPen Solid 5 pink drawSolid w white win drawLines w penB targPts drawLines w penP clipPts drawSolid w green $ toInts resPts getKey w
</lang> Output:
[(100,200),(100,200),(100,117),(125,100),(275,100),(300,117),(300,300),(250,300),(200,250),(175,300),(125,300),(100,250),(100,200)]
J
Solution: <lang j> NB. assumes counterclockwise orientation.
NB. determine whether point y is inside edge x. isinside=:0< [:-/ .* {.@[ -~"1 {:@[,:]
NB. (p0,:p1) intersection (p2,:p3) intersection=:|:@[ (+/ .* (,-.)) [:{. ,.&(-~/) %.~ -&{:
SutherlandHodgman=:4 :0 NB. clip S-H subject
clip=.2 ]\ (,{.) x subject=.y for_edge. clip do.
S=.{:input=.subject subject=.0 2$0 for_E. input do. if. edge isinside E do. if. -.edge isinside S do. subject=.subject,edge intersection S,:E end. subject=.subject,E elseif. edge isinside S do. subject=.subject,edge intersection S,:E end. S=.E end.
end. subject )</lang>
Example use: <lang j> subject=. 50 150,200 50,350 150,350 300,250 300,200 250,150 350,100 250,:100 200
clip=. 100 100,300 100,300 300,:100 300 clip SutherlandHodgman subject
100 116.667 125 100 275 100 300 116.667 300 300 250 300 200 250 175 300 125 300 100 250</lang>
MATLAB
<lang MATLAB>%The inputs are a table of x-y pairs for the verticies of the subject %polygon and boundary polygon. (x values in column 1 and y values in column %2) The output is a table of x-y pairs for the clipped version of the %subject polygon.
function clippedPolygon = sutherlandHodgman(subjectPolygon,clipPolygon)
%% Helper Functions
%computerIntersection() assumes the two lines intersect function intersection = computeIntersection(line1,line2)
%this is an implementation of %http://en.wikipedia.org/wiki/Line-line_intersection intersection = zeros(1,2);
detL1 = det(line1); detL2 = det(line2);
detL1x = det([line1(:,1),[1;1]]); detL1y = det([line1(:,2),[1;1]]);
detL2x = det([line2(:,1),[1;1]]); detL2y = det([line2(:,2),[1;1]]);
denominator = det([detL1x detL1y;detL2x detL2y]);
intersection(1) = det([detL1 detL1x;detL2 detL2x]) / denominator; intersection(2) = det([detL1 detL1y;detL2 detL2y]) / denominator;
end %computeIntersection
%inside() assumes the boundary is oriented counter-clockwise function in = inside(point,boundary) pointPositionVector = [diff([point;boundary(1,:)]) 0]; boundaryVector = [diff(boundary) 0]; crossVector = cross(pointPositionVector,boundaryVector); if ( crossVector(3) <= 0 ) in = true; else in = false; end end %inside
%% Sutherland-Hodgman Algorithm
clippedPolygon = subjectPolygon; numVerticies = size(clipPolygon,1); clipVertexPrevious = clipPolygon(end,:); for clipVertex = (1:numVerticies) clipBoundary = [clipPolygon(clipVertex,:) ; clipVertexPrevious]; inputList = clippedPolygon; clippedPolygon = []; previousVertex = inputList(end,:); for subjectVertex = (1:size(inputList,1))
if ( inside(inputList(subjectVertex,:),clipBoundary) ) if( not(inside(previousVertex,clipBoundary)) ) subjectLineSegment = [previousVertex;inputList(subjectVertex,:)]; clippedPolygon(end+1,1:2) = computeIntersection(clipBoundary,subjectLineSegment); end clippedPolygon(end+1,1:2) = inputList(subjectVertex,:); elseif( inside(previousVertex,clipBoundary) ) subjectLineSegment = [previousVertex;inputList(subjectVertex,:)]; clippedPolygon(end+1,1:2) = computeIntersection(clipBoundary,subjectLineSegment); end previousVertex = inputList(subjectVertex,:); clipVertexPrevious = clipPolygon(clipVertex,:); end %for subject verticies end %for boundary verticies
end %sutherlandHodgman</lang>
Output:
<lang MATLAB>>> subject = [[50;200;350;350;250;200;150;100;100],[150;50;150;300;300;250;350;250;200]];
>> clipPolygon = [[100;300;300;100],[100;100;300;300]];
>> clippedSubject = sutherlandHodgman(subject,clipPolygon);
>> plot([subject(:,1);subject(1,1)],[subject(:,2);subject(1,2)],'blue')
>> hold on
>> plot([clipPolygon(:,1);clipPolygon(1,1)],[clipPolygon(:,2);clipPolygon(1,2)],'r')
>> patch(clippedSubject(:,1),clippedSubject(:,2),0);</lang>
PureBasic
<lang PureBasic>Structure point_f
x.f y.f
EndStructure
Procedure isInside(*p.point_f, *cp1.point_f, *cp2.point_f)
If (*cp2\x - *cp1\x) * (*p\y - *cp1\y) > (*cp2\y - *cp1\y) * (*p\x - *cp1\x) ProcedureReturn 1 EndIf
EndProcedure
Procedure intersection(*cp1.point_f, *cp2.point_f, *s.point_f, *e.point_f, *newPoint.point_f)
Protected.point_f dc, dp Protected.f n1, n2, n3 dc\x = *cp1\x - *cp2\x: dc\y = *cp1\y - *cp2\y dp\x = *s\x - *e\x: dp\y = *s\y - *e\y n1 = *cp1\x * *cp2\y - *cp1\y * *cp2\x n2 = *s\x * *e\y - *s\y * *e\x n3 = 1 / (dc\x * dp\y - dc\y * dp\x) *newPoint\x = (n1 * dp\x - n2 * dc\x) * n3: *newPoint\y = (n1 * dp\y - n2 * dc\y) * n3
EndProcedure
Procedure clip(List vPolygon.point_f(), List vClippedBy.point_f(), List vClippedPolygon.point_f())
Protected.point_f cp1, cp2, s, e, newPoint CopyList(vPolygon(), vClippedPolygon()) If LastElement(vClippedBy()) cp1 = vClippedBy() NewList vPreClipped.point_f() ForEach vClippedBy() cp2 = vClippedBy() CopyList(vClippedPolygon(), vPreClipped()) ClearList(vClippedPolygon()) If LastElement(vPreClipped()) s = vPreClipped() ForEach vPreClipped() e = vPreClipped() If isInside(e, cp1, cp2) If Not isInside(s, cp1, cp2) intersection(cp1, cp2, s, e, newPoint) AddElement(vClippedPolygon()): vClippedPolygon() = newPoint EndIf AddElement(vClippedPolygon()): vClippedPolygon() = e ElseIf isInside(s, cp1, cp2) intersection(cp1, cp2, s, e, newPoint) AddElement(vClippedPolygon()): vClippedPolygon() = newPoint EndIf s = e Next EndIf cp1 = cp2 Next EndIf
EndProcedure
DataSection
Data.f 50,150, 200,50, 350,150, 350,300, 250,300, 200,250, 150,350, 100,250, 100,200 ;subjectPolygon's vertices (x,y) Data.f 100,100, 300,100, 300,300, 100,300 ;clipPolygon's vertices (x,y)
EndDataSection
NewList subjectPolygon.point_f() For i = 1 To 9
AddElement(subjectPolygon()) Read.f subjectPolygon()\x Read.f subjectPolygon()\y
Next
NewList clipPolygon.point_f() For i = 1 To 4
AddElement(clipPolygon()) Read.f clipPolygon()\x Read.f clipPolygon()\y
Next
NewList newPolygon.point_f() clip(subjectPolygon(), clipPolygon(), newPolygon()) If OpenConsole()
ForEach newPolygon() PrintN("(" + StrF(newPolygon()\x, 2) + ", " + StrF(newPolygon()\y, 2) + ")") Next Print(#CRLF$ + #CRLF$ + "Press ENTER to exit"): Input() CloseConsole()
EndIf</lang> Sample output:
(100.00, 116.67) (125.00, 100.00) (275.00, 100.00) (300.00, 116.67) (300.00, 300.00) (250.00, 300.00) (200.00, 250.00) (175.00, 300.00) (125.00, 300.00) (100.00, 250.00)
Tcl
<lang tcl># Find intersection of an arbitrary polygon with a convex one. package require Tcl 8.6
- Does the path (x0,y0)->(x1,y1)->(x2,y2) turn clockwise
- or counterclockwise?
proc cw {x0 y0 x1 y1 x2 y2} {
set dx1 [expr {$x1 - $x0}]; set dy1 [expr {$y1 - $y0}] set dx2 [expr {$x2 - $x0}]; set dy2 [expr {$y2 - $y0}] # (0,0,$dx1*$dy2 - $dx2*$dy1) is the crossproduct of # ($x1-$x0,$y1-$y0,0) and ($x2-$x0,$y2-$y0,0). # Its z-component is positive if the turn # is clockwise, negative if the turn is counterclockwise. set pr1 [expr {$dx1 * $dy2}] set pr2 [expr {$dx2 * $dy1}] if {$pr1 > $pr2} {
# Clockwise return 1
} elseif {$pr1 < $pr2} {
# Counter-clockwise return -1
} elseif {$dx1*$dx2 < 0 || $dy1*$dy2 < 0} {
# point 0 is the middle point return 0
} elseif {($dx1*$dx1 + $dy1*$dy1) < ($dx2*$dx2 + $dy2+$dy2)} {
# point 1 is the middle point return 0
} else {
# point 2 lies on the segment joining points 0 and 1 return 1
}
}
- Calculate the point of intersection of two lines
- containing the line segments (x1,y1)-(x2,y2) and (x3,y3)-(x4,y4)
proc intersect {x1 y1 x2 y2 x3 y3 x4 y4} {
set d [expr {($y4 - $y3) * ($x2 - $x1) - ($x4 - $x3) * ($y2 - $y1)}] set na [expr {($x4 - $x3) * ($y1 - $y3) - ($y4 - $y3) * ($x1 - $x3)}] if {$d == 0} {
return {}
} set r [list \
[expr {$x1 + $na * ($x2 - $x1) / $d}] \ [expr {$y1 + $na * ($y2 - $y1) / $d}]]
return $r
}
- Coroutine that yields the elements of a list in pairs
proc pairs {list} {
yield [info coroutine] foreach {x y} $list {
yield [list $x $y]
} return {}
}
- Coroutine to clip one segment of a polygon against a line.
proc clipsegment {inside0 cx0 cy0 cx1 cy1 sx0 sy0 sx1 sy1} {
set inside1 [expr {[cw $cx0 $cy0 $cx1 $cy1 $sx1 $sy1] > 0}] if {$inside1} {
if {!$inside0} { set int [intersect $cx0 $cy0 $cx1 $cy1 \ $sx0 $sy0 $sx1 $sy1] if {[llength $int] >= 0} { yield $int } } yield [list $sx1 $sy1]
} else {
if {$inside0} { set int [intersect $cx0 $cy0 $cx1 $cy1 \ $sx0 $sy0 $sx1 $sy1] if {[llength $int] >= 0} { yield $int } }
} return $inside1
}
- Coroutine to perform one step of Sutherland-Hodgman polygon clipping
proc clipstep {source cx0 cy0 cx1 cy1} {
yield [info coroutine] set pt0 [{*}$source] if {[llength $pt0] == 0} {
return
} lassign $pt0 sx0 sy0 set inside0 [expr {[cw $cx0 $cy0 $cx1 $cy1 $sx0 $sy0] > 0}] set finished 0 while {!$finished} {
set thispt [{*}$source] if {[llength $thispt] == 0} { set thispt $pt0 set finished 1 } lassign $thispt sx1 sy1 set inside0 [clipsegment $inside0 \ $cx0 $cy0 $cx1 $cy1 $sx0 $sy0 $sx1 $sy1] set sx0 $sx1 set sy0 $sy1
} return {}
}
- Perform Sutherland-Hodgman polygon clipping
proc clippoly {cpoly spoly} {
variable clipindx set source [coroutine clipper[incr clipindx] pairs $spoly] set cx0 [lindex $cpoly end-1] set cy0 [lindex $cpoly end] foreach {cx1 cy1} $cpoly {
set source [coroutine clipper[incr clipindx] \ clipstep $source $cx0 $cy0 $cx1 $cy1] set cx0 $cx1; set cy0 $cy1
} set result {} while {[llength [set pt [{*}$source]]] > 0} {
lappend result {*}$pt
} return $result
}</lang>
The specifics of the task:
<lang tcl>package require Tk
grid [canvas .c -width 400 -height 400 -background \#ffffff] proc demonstrate {cpoly spoly} {
set rpoly [clippoly $cpoly $spoly] puts $rpoly .c create polygon $cpoly -outline \#ff9999 -fill {} -width 5 .c create polygon $spoly -outline \#9999ff -fill {} -width 3 .c create polygon $rpoly -fill \#99ff99 -outline black -width 1
}
demonstrate {100 100 300 100 300 300 100 300} \
{50 150 200 50 350 150 350 300 250 300 200 250 150 350 100 250 100 200}</lang>
Output:
300 116 300 300 250 300 200 250 175 300 125 300 100 250 100 200 100 200 100 116 124 100 275 100