Skip to content

Commit

Permalink
Resweep an undivided endpoint if it divided the next segment, since t…
Browse files Browse the repository at this point in the history
…hat may also change the sweep context. (akavel#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.
  • Loading branch information
oceanful authored May 17, 2019
1 parent 2ec8891 commit 6758def
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 15 deletions.
33 changes: 31 additions & 2 deletions bugs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
}

Expand Down
38 changes: 25 additions & 13 deletions clipper.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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.
Expand Down

0 comments on commit 6758def

Please sign in to comment.