Skip to content

Commit

Permalink
Support polys with overlapping segments
Browse files Browse the repository at this point in the history
Closes #48
  • Loading branch information
mfogel committed Nov 14, 2018
1 parent 958de5d commit c0a4dec
Show file tree
Hide file tree
Showing 9 changed files with 98 additions and 19 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ The Martinez-Rueda-Feito polygon clipping algorithm is used to compute the resul

## Changelog

### vNext

* Support polygons with infinitely thin sections ([#48](https://github.com/mfogel/polygon-clipping/issues/48))

### v0.9.1 (2018-11-12)

* Bug fixes ([#36](https://github.com/mfogel/polygon-clipping/issues/36) again, [#44](https://github.com/mfogel/polygon-clipping/issues/44))
Expand Down
17 changes: 14 additions & 3 deletions src/segment.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ export default class Segment {
if (a.ringIn.id !== b.ringIn.id) {
return a.ringIn.id < b.ringIn.id ? -1 : 1
}

// overlapping segments from the same ring
// https://github.com/mfogel/polygon-clipping/issues/48
if (a.tiebreaker < b.tiebreaker) return -1
if (a.tiebreaker > b.tiebreaker) return 1

} else {
// not colinear

Expand All @@ -65,9 +71,8 @@ export default class Segment {
}

throw new Error(
`Segment comparison (from [${a.leftSE.point.x}, ${a.leftSE.point.y}])` +
` -> to [${a.rightSE.point.x}, ${a.rightSE.point.y}]) failed... ` +
` segments equal but not identical?`
`Segment comparison from [${a.leftSE.point.x}, ${a.leftSE.point.y}]` +
` to [${a.rightSE.point.x}, ${a.rightSE.point.y}] failed`
)
}

Expand Down Expand Up @@ -99,6 +104,12 @@ export default class Segment {
return new Segment(leftSE, rightSE, ring)
}

// used for sorting equal sweep events, segments consistently
get tiebreaker () {
if (this._tiebreaker === undefined) this._tiebreaker = Math.random()
return this._tiebreaker
}

get bbox () {
const y1 = this.leftSE.point.y
const y2 = this.rightSE.point.y
Expand Down
8 changes: 6 additions & 2 deletions src/sweep-event.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,13 @@ export default class SweepEvent {
// they appear to be the same point... are they?
if (a === b) return 0

// they're from overlapping segments of the same ring
// https://github.com/mfogel/polygon-clipping/issues/48
if (a.segment.tiebreaker < b.segment.tiebreaker) return -1
if (a.segment.tiebreaker > b.segment.tiebreaker) return 1

throw new Error(
`SweepEvent comparison failed at [${a.point.x}, ${a.point.y}]... ` +
`equal but not identical?`
`SweepEvent comparison failed at [${a.point.x}, ${a.point.y}]`
)
}

Expand Down
33 changes: 33 additions & 0 deletions test/end-to-end/infinitely-thin-polygon/args.geojson
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [[[-3, -3], [3, -3], [3, 3], [-3, 3], [-3, -3]]]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[-2, -1],
[-1, 0],
[1, 0],
[2, -1],
[2, 1],
[1, 0],
[-1, 0],
[-2, 1],
[-2, -1]
]
]
}
}
]
}
14 changes: 14 additions & 0 deletions test/end-to-end/infinitely-thin-polygon/difference.geojson
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "MultiPolygon",
"coordinates": [
[
[[-3, -3], [3, -3], [3, 3], [-3, 3], [-3, -3]],
[[-2, -1], [-2, 1], [-1, 0], [-2, -1]],
[[1, 0], [2, 1], [2, -1], [1, 0]]
]
]
}
}
11 changes: 11 additions & 0 deletions test/end-to-end/infinitely-thin-polygon/intersection.geojson
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "MultiPolygon",
"coordinates": [
[[[-2, -1], [-1, 0], [-2, 1], [-2, -1]]],
[[[1, 0], [2, -1], [2, 1], [1, 0]]]
]
}
}
8 changes: 8 additions & 0 deletions test/end-to-end/infinitely-thin-polygon/union.geojson
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "MultiPolygon",
"coordinates": [[[[-3, -3], [3, -3], [3, 3], [-3, 3], [-3, -3]]]]
}
}
6 changes: 4 additions & 2 deletions test/segment.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -817,9 +817,11 @@ describe('compare segments', () => {
expect(Segment.compare(seg2, seg1)).toBe(1)
})

test('exactly equal segments (but not identical) should throw error', () => {
test('exactly equal segments (but not identical) are consistent', () => {
const seg1 = Segment.fromRing({ x: 0, y: 0 }, { x: 4, y: 4 }, { id: 1 })
const seg2 = Segment.fromRing({ x: 0, y: 0 }, { x: 4, y: 4 }, { id: 1 })
expect(() => Segment.compare(seg1, seg2)).toThrow()
const result = Segment.compare(seg1, seg2)
expect(Segment.compare(seg1, seg2)).toBe(result)
expect(Segment.compare(seg2, seg1)).toBe(result * -1)
})
})
16 changes: 4 additions & 12 deletions test/sweep-event.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,23 +61,15 @@ describe('sweep event compare', () => {
expect(SweepEvent.compare(s1, s1)).toBe(0)
})

test('totally equal but not identical', () => {
test('totally equal but not identical events are consistent', () => {
const s1 = new SweepEvent({ x: 0, y: 0 })
const s2 = new SweepEvent({ x: 0, y: 0 })
const s3 = new SweepEvent({ x: 3, y: 3 })
new Segment(s1, s3, { id: 1 })
new Segment(s2, s3, { id: 1 })
expect(() => SweepEvent.compare(s1, s2)).toThrow()
})

test('length does not matter', () => {
const s1 = new SweepEvent({ x: 0, y: 0 })
const s2 = new SweepEvent({ x: 0, y: 0 })
const s3 = new SweepEvent({ x: 3, y: 3 })
const s4 = new SweepEvent({ x: 4, y: 4 })
new Segment(s1, s3, { id: 1 })
new Segment(s2, s4, { id: 1 })
expect(() => SweepEvent.compare(s1, s2)).toThrow()
const result = SweepEvent.compare(s1, s2)
expect(SweepEvent.compare(s1, s2)).toBe(result)
expect(SweepEvent.compare(s2, s1)).toBe(result * -1)
})

test('events are linked as side effect', () => {
Expand Down

0 comments on commit c0a4dec

Please sign in to comment.