From 6758def046ec718a15f1b113f92180a080d8d6a6 Mon Sep 17 00:00:00 2001 From: oceanful <35951082+oceanful@users.noreply.github.com> Date: Fri, 17 May 2019 14:50:17 -0700 Subject: [PATCH] Resweep an undivided endpoint if it divided the next segment, since that may also change the sweep context. (#7) Expand the resweep logic to resweep an endpoint if it divides the next or previous segment but remains undivided. Both types of divisions can alter the sweep context for the endpoint. --- bugs_test.go | 33 +++++++++++++++++++++++++++++++-- clipper.go | 38 +++++++++++++++++++++++++------------- 2 files changed, 56 insertions(+), 15 deletions(-) diff --git a/bugs_test.go b/bugs_test.go index f70a615..4cb3b7a 100644 --- a/bugs_test.go +++ b/bugs_test.go @@ -83,8 +83,8 @@ func (cases testCases) verify(t *T) { for i, c := range cases { expected := c.result for i, path := range expected { - // Remove duplicate endpoints to eliminate zero-length segments that the library won't produce. - for path[0] == path[len(path)-1] { + // Remove duplicate endpoints to eliminate zero-length segments that the clipper won't produce. + for len(path) > 1 && path[0] == path[len(path)-1] { path = path[0 : len(path)-1] } expected[i] = path @@ -255,6 +255,35 @@ func TestResweepingIntersectingEndpoints(t *T) { {70.78432620601497, -7.668842337087888}, }}, }, + { + op: polyclip.INTERSECTION, + subject: polyclip.Polygon{{ + {131.59597133486625, 287.9385241571817}, + {100.00000000000004, 273.20508075688775}, + {71.44247806269215, 253.20888862379562}, + {71.44247806269209, -53.20888862379559}, + {99.99999999999991, -73.20508075688767}, + {131.59597133486614, -87.93852415718163}, + }}, + clipping: polyclip.Polygon{{ + {128.55752193730785, -53.208888623795616}, + {100, -73.20508075688772}, + {99.99999999999991, -73.20508075688767}, + {71.44247806269209, -53.20888862379559}, + {71.44247806269215, 253.20888862379562}, + {100.00000000000003, 273.2050807568877}, + {128.55752193730788, 253.20888862379562}, + }}, + result: polyclip.Polygon{{ + {128.55752193730785, -53.208888623795616}, + {100, -73.20508075688772}, + {99.99999999999991, -73.20508075688767}, + {71.44247806269209, -53.20888862379559}, + {71.44247806269215, 253.20888862379562}, + {100.00000000000003, 273.2050807568877}, + {128.55752193730788, 253.20888862379562}, + }}, + }, }.verify(t) } diff --git a/clipper.go b/clipper.go index 3d07fa9..c490d99 100644 --- a/clipper.go +++ b/clipper.go @@ -196,24 +196,33 @@ func (c *clipper) compute(operation Op) Polygon { } }) + divided := make(map[*endpoint]bool) // Process a possible intersection between "e" and its next neighbor in S if next != nil { - c.possibleIntersection(e, next) + for _, seg := range c.possibleIntersection(e, next) { + if seg != nil { + divided[seg] = true + } + } } // Process a possible intersection between "e" and its previous neighbor in S if prev != nil { - divided := c.possibleIntersection(prev, e) - // If [prev] was divided, the context (sweep line S) for [e] may have changed, - // altering what e.inout and e.inside should be. [e] must thus be reenqueued to - // recompute e.inout and e.inside. - // - // (This should not be done if [e] was also divided; in that case - // the divided segments are already enqueued). - if len(divided) == 1 && divided[0] == prev { - S.remove(e) - c.eventQueue.enqueue(e) + for _, seg := range c.possibleIntersection(prev, e) { + if seg != nil { + divided[seg] = true + } } } + // If [prev] or [next] was divided but [e] was not, the context (sweep line S) + // for [e] may have changed, altering what e.inout and e.inside should be. + // [e] must thus be reenqueued to recompute e.inout and e.inside. + // + // (This should not be done if [e] was also divided; in that case + // the divided segments are already enqueued). + if len(divided) > 0 && !divided[e] { + S.remove(e) + c.eventQueue.enqueue(e) + } } else { // the line segment must be removed from S otherPos := -1 for i := range S { @@ -503,13 +512,16 @@ func (c *clipper) possibleIntersection(e1, e2 *endpoint) []*endpoint { // one line segment includes the other one sortedEvents[1].edgeType, sortedEvents[1].other.edgeType = _EDGE_NON_CONTRIBUTING, _EDGE_NON_CONTRIBUTING - c.divideSegment(sortedEvents[0], sortedEvents[1].p) + firstDivided := c.divideSegment(sortedEvents[0], sortedEvents[1].p) if e1.inout == e2.inout { sortedEvents[3].other.edgeType = _EDGE_SAME_TRANSITION } else { sortedEvents[3].other.edgeType = _EDGE_DIFFERENT_TRANSITION } - return []*endpoint{c.divideSegment(sortedEvents[3].other, sortedEvents[2].p)} + return []*endpoint{ + firstDivided, + c.divideSegment(sortedEvents[3].other, sortedEvents[2].p), + } } // Returns the original endpoint if successfully divided, otherwise nil.