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.