@@ -14,20 +14,20 @@ The Wieler-Atherton algorithm for clipping polygons.
1414"""
1515struct WeilerAthertonClipping <: ClippingMethod end
1616
17- # VertexType has three different types of vertices used for deciding how to navigate the data
18- # structure when collecting the polygon rings after clipping.
17+ # VertexType distinguishes three different types of vertices used for constructing the structure
18+ # used by the Weiler-Atherton clipping algorithm. Normal type represents regular vertices, the
19+ # connecting points of edges of a rings. Entering and Exiting types represent intersections of
20+ # two ring edges, where one belongs to a clipping and other belongs to a clipped ring. The
21+ # Entering and Exiting types describe, from the perspective of the edges of the clipping ring,
22+ # whether it enters or exits the interior of the clipped ring.
1923abstract type VertexType end
2024abstract type Normal <: VertexType end
2125abstract type Entering <: VertexType end
2226abstract type Exiting <: VertexType end
2327
2428# Data structure for clipping the polygons. Fields left and right are used depending on the
25- # VertexType. If Normal, left points to the following vertex of the original ring, and right
26- # to the next intersection vertex on the edge between point and left.point, or the following
27- # original ring vertex if no intersections on the edge. For Entering and Exiting types, left
28- # poitns to the following vertex on the clipping ring and right to the following vertex on one
29- # of the clipped rings.
30- # TODO Either properly document the usage of left and right, or separate RingVertex into Entering, Exiting and Normal vertices.
29+ # VertexType, as designated with the helper functions. The data structure forms a directed
30+ # graph with each element always pointing to two elements.
3131mutable struct RingVertex{VT<: VertexType ,M<: Manifold ,C<: CRS }
3232 point:: Point{M,C}
3333 left:: RingVertex{<:VertexType,M,C}
@@ -53,11 +53,41 @@ function appendvertices!(v₁::RingVertex{Normal}, v₂::RingVertex{Normal})
5353 v₁. right = v₂
5454end
5555
56- # Traversing and selecting following vertices for collecting of the rings after clipping depends
57- # on the vertex type. Helper nextvertex follows the correct path.
56+ # Helper functions to designate the use of the left and right branches of the RingVertex.
57+
58+ # Normal vertex uses the left branch for the next Normal vertex,
59+ # which is the end of the ring edge for which the current vertex is the edge start.
60+ nextnormal (v:: RingVertex{Normal} ) = v. left
61+ # Normal vertex uses the right branch for the next intersection on the ring edge if any.
62+ # Otherwise, it holds the next Normal vertex, same as for the left branch.
5863nextvertex (v:: RingVertex{Normal} ) = v. right
59- nextvertex (v:: RingVertex{Entering} ) = v. right
60- nextvertex (v:: RingVertex{Exiting} ) = v. left
64+ # Next clipping vertex is a following vertex, normal or an intersection, on a clipping ring.
65+ # Next clipped vertex is a following vertex, normal or an intersection, on a clipped ring.
66+ # Normal vertices are either on a clipping or a clipped, but the implementation is same
67+ # and helper functions can be used interchangeably.
68+ getnextclippingvertex (v:: RingVertex{Normal} ) = v. right
69+ setnextclippingvertex! (v:: RingVertex{Normal} , next:: RingVertex ) = v. right = next
70+ getnextclippedvertex (v:: RingVertex{Normal} ) = v. right
71+ setnextclippedvertex! (v:: RingVertex{Normal} , next:: RingVertex ) = v. right = next
72+
73+ # Both Entering and Exiting vertices are always intersections and use the left branch for
74+ # the following vertex on the clipping ring.
75+ getnextclippingvertex (v:: RingVertex ) = v. left
76+ setnextclippingvertex! (v:: RingVertex , next:: RingVertex ) = v. left = next
77+
78+ # Similarly, the right branch holds the following vertex on the clipped ring.
79+ getnextclippedvertex (v:: RingVertex ) = v. right
80+ setnextclippedvertex! (v:: RingVertex , next:: RingVertex ) = v. right = next
81+
82+ # Traversing and selecting correct following vertices from the directed graph of RingVertex
83+ # elements should switch between rings whenever an intersection is encountered. If the current
84+ # edge is entering the interior of the clipped ring, follow to the next vertex on the clipping
85+ # ring. In case the edge is exiting the interior of the clipped ring, then follow the next vertex
86+ # on the clipped ring. For a Normal vertex take the next vertex which can be either an intersection
87+ # or an edge end.
88+ getnextresultvertex (v:: RingVertex{Normal} ) = nextvertex (v)
89+ getnextresultvertex (v:: RingVertex{Entering} ) = getnextclippedvertex (v)
90+ getnextresultvertex (v:: RingVertex{Exiting} ) = getnextclippingvertex (v)
6191
6292function clip (poly:: Polygon , ring:: Ring , :: WeilerAthertonClipping )
6393 polyrings = rings (poly)
@@ -85,35 +115,39 @@ function clip(poly::Polygon, ring::Ring, ::WeilerAthertonClipping)
85115 # Three consecutive clipping vertices are used to properly identify intersection vertex type
86116 # for corner cases, so the clipping segment is constructed with the two following vertices
87117 # after the current one.
88- clippingsegment = Segment (clipping. left. point, clipping. left. left. point)
118+ clippingedgestart = nextnormal (clipping)
119+ clippingedgeend = nextnormal (clippingedgestart)
120+ clippingsegment = Segment (clippingedgestart. point, clippingedgeend. point)
89121
90122 for (k, startclipped) in enumerate (clippedrings)
91123 clipped = startclipped
92124 while true
93125 # Like for the clipping, the clipped also uses three consecutive vertices.
94- clippedsegment = Segment (clipped. left. point, clipped. left. left. point)
126+ clippededgestart = nextnormal (clipped)
127+ clippededgeend = nextnormal (clippededgestart)
128+ clippedsegment = Segment (clippededgestart. point, clippededgeend. point)
95129
96130 I = intersection (clippingsegment, clippedsegment)
97131 vertex = vertexfromintersection (I, clipping, clipped)
98- success = insertintersections! (vertex, clipping, clipped , entering)
132+ success = insertintersections! (vertex, clippingedgestart, clippededgestart , entering)
99133 intersected[k] = intersected[k] || success
100134
101- clipped = clipped. left
102- clipped. left == startclipped. left && break
135+ clipped = nextnormal ( clipped)
136+ nextnormal ( clipped) == nextnormal ( startclipped) && break
103137 end
104138 end
105139
106- clipping = clipping. left
107- clipping. left == startclipping. left && break
140+ clipping = nextnormal ( clipping)
141+ nextnormal ( clipping) == nextnormal ( startclipping) && break
108142 end
109143
110- # Handle the case when no interections have been found.
111144 if ! any (intersected)
145+ # Handle the case when no interections have been found.
112146 if orientation (ring) == CW
113- # For an inner clipping ring take all outside rings.
147+ # For an inner clipping ring take all clipped rings outside of it .
114148 return PolyArea (collectoutsiderings (ring, polyrings)... )
115149 else
116- # For an outer clipping ring add it to act as the outer ring.
150+ # For an outer clipping ring add it to act as the outer ring of the clipped ring .
117151 collectedrings = all (v ∈ PolyArea (polyrings[1 ]) for v in vertices (ring)) ? [ring] : []
118152 end
119153 else
@@ -177,34 +211,37 @@ function perhapsaddnonintersected!(ring, polyrings, intersected)
177211 newpolyrings
178212end
179213
180- # Inserts the intersection in the ring.
181- function insertintersection! (head :: RingVertex , intersection :: RingVertex , side :: Symbol )
182- tail = head. left
183- vertex = head
184-
185- new = measure ( Segment ( head. point, intersection . point))
214+ function insertintersection! (head :: RingVertex{Normal} , intersection:: RingVertex , getnext, setnext!)
215+ tail = nextnormal (head )
216+ current = head
217+ newdistance = measure ( Segment ( head. point, intersection . point))
218+ # Search for the place to insert the intersection by comparing its distance
219+ # from the head with other inserted intersections.
186220 while true
187- os = isnormal (vertex) ? :right : side
188- current = measure (Segment (head. point, getfield (vertex, os). point))
189- if (new < current) || (getfield (vertex, os) == tail)
190- next = getfield (vertex, os)
191- if ! (new ≈ measure (Segment (head. point, next. point)))
192- setfield! (intersection, side, next)
193- setfield! (vertex, os, intersection)
221+ before = current
222+ current = getnext (current)
223+ currentdistance = measure (Segment (head. point, current. point))
224+
225+ if (newdistance < currentdistance) || (current == tail)
226+ if ! (newdistance ≈ currentdistance) # TODO Check if this is needed.
227+ # Only add if it is not coincident with the following vertex, or it could be added twice,
228+ # if it is the tail vertex. In such case, the intersection will be detected second time
229+ # and added then.
230+ setnext! (intersection, current)
231+ setnext! (before, intersection)
194232 end
195233 break
196234 end
197- vertex = getfield (vertex, os)
198235 end
199236end
200237
201238# Inserts the intersection into both the clipping and the clipped rings.
202- function insertintersections! (vertex:: Tuple , clipping, clipped, entering)
239+ function insertintersections! (vertex:: Tuple , clipping:: RingVertex{Normal} , clipped:: RingVertex{Normal} , entering)
203240 (vtype, point) = vertex
204241 if ! isnothing (vtype)
205242 intersection = RingVertex {vtype} (point)
206- insertintersection! (clipping. left , intersection, :left )
207- insertintersection! (clipped. left , intersection, :right )
243+ insertintersection! (clipping, intersection, getnextclippingvertex, setnextclippingvertex! )
244+ insertintersection! (clipped, intersection, getnextclippedvertex, setnextclippedvertex! )
208245
209246 if vtype == Entering
210247 push! (entering, intersection)
@@ -235,7 +272,7 @@ function collectclipped(entering::Vector{RingVertex{Entering}})
235272
236273 push! (ring, vertex)
237274 push! (visited, vertex)
238- vertex = nextvertex (vertex)
275+ vertex = getnextresultvertex (vertex)
239276 end
240277
241278 # Remove duplicates.
@@ -273,30 +310,30 @@ function vertexfromintersection(I, clipping, clipped)
273310end
274311
275312function vertexfromcrossing (point, clipping, clipped)
276- cl = Line (clipping. left . point, clipping. left . left . point)
277- vertextype = sideof (clipped. left . left . point, cl) == LEFT ? Entering : Exiting
313+ cl = Line (nextnormal ( clipping) . point, nextnormal ( nextnormal ( clipping)) . point)
314+ vertextype = sideof (nextnormal ( nextnormal ( clipped)) . point, cl) == LEFT ? Entering : Exiting
278315 (vertextype, point)
279316end
280317
281318function vertexfromedgetouching (point, clipping, clipped)
282319 vertextype = nothing
283- if point ≈ clipped. left . point
320+ if point ≈ nextnormal ( clipped) . point
284321 # When intersection is at the shared vertex of two edges of the clipped ring,
285- # then split the interscting edge of the clipping ring at the intersection point.
322+ # then split the intersecting edge of the clipping ring at the intersection point.
286323 vertextype = decidedirection (
287- Segment (clipped. point, clipped. left . point),
288- Segment (clipped. left . point, clipped. left . left . point),
289- Segment (clipping. left . point, point),
290- Segment (point, clipping. left . left . point)
324+ Segment (clipped. point, nextnormal ( clipped) . point),
325+ Segment (nextnormal ( clipped) . point, nextnormal ( nextnormal ( clipped)) . point),
326+ Segment (nextnormal ( clipping) . point, point),
327+ Segment (point, nextnormal ( nextnormal ( clipping)) . point)
291328 )
292- elseif point ≈ clipping. left . point
329+ elseif point ≈ nextnormal ( clipping) . point
293330 # When intersection is at the shared vertex of two edges of the clipping ring,
294- # then split the interscting edge of the clipped ring at the intersection point.
331+ # then split the intersecting edge of the clipped ring at the intersection point.
295332 vertextype = decidedirection (
296- Segment (clipped. left . point, point),
297- Segment (point, clipped. left . left . point),
298- Segment (clipping. point, clipping. left . point),
299- Segment (clipping. left . point, clipping. left . left . point)
333+ Segment (nextnormal ( clipped) . point, point),
334+ Segment (point, nextnormal ( nextnormal ( clipped)) . point),
335+ Segment (clipping. point, nextnormal ( clipping) . point),
336+ Segment (nextnormal ( clipping) . point, nextnormal ( nextnormal ( clipping)) . point)
300337 )
301338 end
302339 (vertextype, point)
@@ -305,50 +342,50 @@ end
305342function vertexfromcornertouching (point, clipping, clipped)
306343 vertextype = nothing
307344 # When intersection is at the shared vertices of both the clipping and the clipped rings.
308- if (point ≈ clipped. left . point) && (point ≈ clipping. left . point)
345+ if (point ≈ nextnormal ( clipped) . point) && (point ≈ nextnormal ( clipping) . point)
309346 # Only applies if the intersection coincides with the middles of the currently observed
310347 # vertices for both clipping and clipped rings.
311348 vertextype = decidedirection (
312349 Segment (clipped. point, point),
313- Segment (point, clipped. left . left . point),
350+ Segment (point, nextnormal ( nextnormal ( clipped)) . point),
314351 Segment (clipping. point, point),
315- Segment (point, clipping. left . left . point)
352+ Segment (point, nextnormal ( nextnormal ( clipping)) . point)
316353 )
317354 end
318355 (vertextype, point)
319356end
320357
321358function vertexfromoverlapping (segment, clipping, clipped)
322359 # For both ends of the intersecting segment, check if it coincides with the middle of
323- # the obeserved vertices for clipped and clipping rings. If it does, attempt adding a
360+ # the observed vertices for clipped and clipping rings. If it does, attempt adding a
324361 # point.
325362
326363 ret = Tuple[]
327364 for point in extrema (segment)
328- if point ≈ clipped. left . point
329- clippingprev = Segment (clipping. left . point, point)
365+ if point ≈ nextnormal ( clipped) . point
366+ clippingprev = Segment (nextnormal ( clipping) . point, point)
330367 if measure (clippingprev) ≈ 0.0 u " m"
331368 clippingprev = Segment (clipping. point, point)
332369 end
333370 vertextype = decidedirection (
334371 Segment (clipped. point, point),
335- Segment (point, clipped. left . left . point),
372+ Segment (point, nextnormal ( nextnormal ( clipped)) . point),
336373 clippingprev,
337- Segment (point, clipping. left . left . point)
374+ Segment (point, nextnormal ( nextnormal ( clipping)) . point)
338375 )
339376 push! (ret, (vertextype, point))
340377 end
341378
342- if point ≈ clipping. left . point
343- clippedprev = Segment (clipped. left . point, point)
379+ if point ≈ nextnormal ( clipping) . point
380+ clippedprev = Segment (nextnormal ( clipped) . point, point)
344381 if measure (clippedprev) ≈ 0.0 u " m"
345382 clippedprev = Segment (clipped. point, point)
346383 end
347384 vertextype = decidedirection (
348385 clippedprev,
349- Segment (point, clipped. left . left . point),
386+ Segment (point, nextnormal ( nextnormal ( clipped)) . point),
350387 Segment (clipping. point, point),
351- Segment (point, clipping. left . left . point)
388+ Segment (point, nextnormal ( nextnormal ( clipping)) . point)
352389 )
353390 push! (ret, (vertextype, point))
354391 end
@@ -416,5 +453,5 @@ function gettraversalring(ring::Ring)
416453 start = new
417454 end
418455
419- return start. left
456+ return nextnormal ( start)
420457end
0 commit comments