@@ -46,6 +46,9 @@ RingVertex{VT}(point::Point{M,C}) where {VT<:VertexType,M<:Manifold,C<:CRS} = Ri
4646isnormal (:: RingVertex{Normal} ) = true
4747isnormal (:: RingVertex ) = false
4848
49+ isentering (:: RingVertex{Entering} ) = true
50+ isentering (:: RingVertex ) = false
51+
4952function appendvertices! (v₁:: RingVertex{Normal} , v₂:: RingVertex{Normal} )
5053 v₂. left = v₁. left
5154 v₁. left = v₂
@@ -100,45 +103,51 @@ function clip(poly::Polygon, ring::Ring, ::WeilerAthertonClipping)
100103
101104 # Convert the subject polygon rings and the clipping ring to the RingVertex data structure.
102105 clippedrings = [gettraversalring (r) for r in polyrings]
103- startclipping = gettraversalring (ring)
106+ clipping = gettraversalring (ring)
104107
105108 # For keeping track of intersected rings, as the non-intersected ones need additional
106109 # processing at the end.
107110 intersected = zeros (Bool, length (clippedrings))
108111
112+ # For marking of clipping ring vertices which touch rings of clipped polygon.
113+ clippingtouches = zeros (Bool, nvertices (ring))
114+
109115 # For collecting all entering vertices, as they are used as starting points for collection
110116 # of the output rings.
111117 entering = RingVertex{Entering}[]
112118
113- clipping = startclipping
114- while true
119+ for l in 1 : (nvertices (ring))
115120 # Three consecutive clipping vertices are used to properly identify intersection vertex type
116121 # for corner cases, so the clipping segment is constructed with the two following vertices
117122 # after the current one.
118123 clippingedgestart = nextnormal (clipping)
119124 clippingedgeend = nextnormal (clippingedgestart)
120125 clippingsegment = Segment (clippingedgestart. point, clippingedgeend. point)
121126
122- for (k, startclipped) in enumerate (clippedrings)
123- clipped = startclipped
124- while true
127+ for (k, clipped) in enumerate (clippedrings)
128+ for _ in 1 : (nvertices (polyrings[k]))
125129 # Like for the clipping, the clipped also uses three consecutive vertices.
126130 clippededgestart = nextnormal (clipped)
127131 clippededgeend = nextnormal (clippededgestart)
128132 clippedsegment = Segment (clippededgestart. point, clippededgeend. point)
129133
130134 I = intersection (clippingsegment, clippedsegment)
131- vertex = vertexfromintersection (I, clipping, clipped)
132- success = insertintersections! (vertex, clippingedgestart, clippededgestart, entering)
133- intersected[k] = intersected[k] || success
135+
136+ if type (I) != NotIntersecting
137+ vertices = vertexfromintersection (I, clipping, clipped)
138+ if ! isnothing (vertices)
139+ insertintersections! (vertices, clippingedgestart, clippededgestart, entering)
140+ intersected[k] = true
141+ elseif type (I) in [EdgeTouching, CornerTouching] && get (I) ≈ clippingedgestart. point
142+ clippingtouches[l] = true
143+ end
144+ end
134145
135146 clipped = nextnormal (clipped)
136- nextnormal (clipped) == nextnormal (startclipped) && break
137147 end
138148 end
139149
140150 clipping = nextnormal (clipping)
141- nextnormal (clipping) == nextnormal (startclipping) && break
142151 end
143152
144153 if ! any (intersected)
@@ -147,8 +156,11 @@ function clip(poly::Polygon, ring::Ring, ::WeilerAthertonClipping)
147156 # For an inner clipping ring take all clipped rings outside of it.
148157 return PolyArea (collectoutsiderings (ring, polyrings)... )
149158 else
159+ # Align the clippingtouches with the ring vertices.
160+ clippingtouches = [clippingtouches[end ], clippingtouches[1 : (end - 1 )]. .. ]
150161 # For an outer clipping ring add it to act as the outer ring of the clipped ring.
151- collectedrings = all (v ∈ PolyArea (polyrings[1 ]) for v in vertices (ring)) ? [ring] : []
162+ collectedrings =
163+ all (t || v ∈ PolyArea (polyrings[1 ]) for (t, v) in zip (clippingtouches, vertices (ring))) ? [ring] : []
152164 end
153165 else
154166 # Collect rings formed from the intersected rings.
@@ -223,36 +235,27 @@ function insertintersection!(head::RingVertex{Normal}, intersection::RingVertex,
223235 currentdistance = measure (Segment (head. point, current. point))
224236
225237 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)
232- end
238+ setnext! (intersection, current)
239+ setnext! (before, intersection)
233240 break
234241 end
235242 end
236243end
237244
245+ insertintersections! (_:: Nothing , _, _, _) = nothing
246+
238247# Inserts the intersection into both the clipping and the clipped rings.
239- function insertintersections! (vertex:: Tuple , clipping:: RingVertex{Normal} , clipped:: RingVertex{Normal} , entering)
240- (vtype, point) = vertex
241- if ! isnothing (vtype)
242- intersection = RingVertex {vtype} (point)
243- insertintersection! (clipping, intersection, getnextclippingvertex, setnextclippingvertex!)
244- insertintersection! (clipped, intersection, getnextclippedvertex, setnextclippedvertex!)
245-
246- if vtype == Entering
247- push! (entering, intersection)
248- end
249- return true
248+ function insertintersections! (vertex:: RingVertex , clipping:: RingVertex{Normal} , clipped:: RingVertex{Normal} , entering)
249+ insertintersection! (clipping, vertex, getnextclippingvertex, setnextclippingvertex!)
250+ insertintersection! (clipped, vertex, getnextclippedvertex, setnextclippedvertex!)
251+
252+ if isentering (vertex)
253+ push! (entering, vertex)
250254 end
251- false
252255end
253256
254257insertintersections! (vertices:: Array , clipping, clipped, entering) =
255- any ( insertintersections! .(vertices, Ref (clipping), Ref (clipped), Ref (entering) ))
258+ insertintersections! .(vertices, Ref (clipping), Ref (clipped), Ref (entering))
256259
257260# Takes a list of entering vertices and returns all rings that contain those vertices.
258261function collectclipped (entering:: Vector{RingVertex{Entering}} )
@@ -270,28 +273,14 @@ function collectclipped(entering::Vector{RingVertex{Entering}})
270273 break
271274 end
272275
273- push! (ring, vertex)
274- push! (visited, vertex)
275- vertex = getnextresultvertex (vertex)
276- end
277-
278- # Remove duplicates.
279- newring = RingVertex[ring[1 ]]
280- for i in 2 : length (ring)
281- if ! (ring[i]. point ≈ newring[end ]. point)
282- push! (newring, ring[i])
276+ nextvertex = getnextresultvertex (vertex)
277+ # Skip adding sequential duplicates, and sequential entering vertices
278+ # which can happen with overlapping edge intersections.
279+ if ! (isentering (vertex) && isentering (nextvertex)) && ! (vertex. point ≈ nextvertex. point)
280+ push! (ring, vertex)
283281 end
284- end
285- ring = newring
286-
287- # Polygon might start several vertices after the first collected.
288- # This generally happens when there are overlapping edges that lead
289- # to several entering vertices without exiting in between. Then, the
290- # actual polygon is found by discarding the extra vertices before the
291- # proper loop.
292- k = findfirst (x -> ring[end ]. point == x. point, ring[1 : (end - 1 )])
293- if ! isnothing (k)
294- ring = ring[(k + 1 ): end ]
282+ push! (visited, vertex)
283+ vertex = nextvertex
295284 end
296285
297286 if length (ring) > 2
@@ -306,13 +295,13 @@ function vertexfromintersection(I, clipping, clipped)
306295 type (I) == CornerTouching && return vertexfromcornertouching (get (I), clipping, clipped)
307296 type (I) == EdgeTouching && return vertexfromedgetouching (get (I), clipping, clipped)
308297 type (I) == Overlapping && return vertexfromoverlapping (get (I), clipping, clipped)
309- ( nothing , nothing )
298+ @assert false # No other intersection types expected.
310299end
311300
312301function vertexfromcrossing (point, clipping, clipped)
313302 cl = Line (nextnormal (clipping). point, nextnormal (nextnormal (clipping)). point)
314303 vertextype = sideof (nextnormal (nextnormal (clipped)). point, cl) == LEFT ? Entering : Exiting
315- ( vertextype, point)
304+ RingVertex { vertextype} ( point)
316305end
317306
318307function vertexfromedgetouching (point, clipping, clipped)
@@ -336,7 +325,7 @@ function vertexfromedgetouching(point, clipping, clipped)
336325 Segment (nextnormal (clipping). point, nextnormal (nextnormal (clipping)). point)
337326 )
338327 end
339- (vertextype, point)
328+ isnothing (vertextype) ? nothing : RingVertex {vertextype} ( point)
340329end
341330
342331function vertexfromcornertouching (point, clipping, clipped)
@@ -352,15 +341,15 @@ function vertexfromcornertouching(point, clipping, clipped)
352341 Segment (point, nextnormal (nextnormal (clipping)). point)
353342 )
354343 end
355- (vertextype, point)
344+ isnothing (vertextype) ? nothing : RingVertex {vertextype} ( point)
356345end
357346
358347function vertexfromoverlapping (segment, clipping, clipped)
359348 # For both ends of the intersecting segment, check if it coincides with the middle of
360349 # the observed vertices for clipped and clipping rings. If it does, attempt adding a
361350 # point.
362351
363- ret = Tuple []
352+ ret = RingVertex []
364353 for point in extrema (segment)
365354 if point ≈ nextnormal (clipped). point
366355 clippingprev = Segment (nextnormal (clipping). point, point)
@@ -373,7 +362,9 @@ function vertexfromoverlapping(segment, clipping, clipped)
373362 clippingprev,
374363 Segment (point, nextnormal (nextnormal (clipping)). point)
375364 )
376- push! (ret, (vertextype, point))
365+ if ! isnothing (vertextype)
366+ push! (ret, RingVertex {vertextype} (point))
367+ end
377368 end
378369
379370 if point ≈ nextnormal (clipping). point
@@ -387,10 +378,12 @@ function vertexfromoverlapping(segment, clipping, clipped)
387378 Segment (clipping. point, point),
388379 Segment (point, nextnormal (nextnormal (clipping)). point)
389380 )
390- push! (ret, (vertextype, point))
381+ if ! isnothing (vertextype)
382+ push! (ret, RingVertex {vertextype} (point))
383+ end
391384 end
392385 end
393- ret
386+ ( length (ret) == 0 ) ? nothing : ret
394387end
395388
396389# Used to figure out the type of the vertex to add when intersection is other than the crossing
0 commit comments