Add geoHasLineIntersections, better for testing multipolygon rings

(closes #4741)
This commit is contained in:
Bryan Housel
2018-01-24 23:16:51 -05:00
parent fb196bee8c
commit d5bf2d9762
4 changed files with 123 additions and 3 deletions
+47 -1
View File
@@ -80,12 +80,57 @@ export function geoChooseEdge(nodes, point, projection, activeID) {
}
// check active (dragged or drawing) segments against inactive segments
// Test active (dragged or drawing) segments against inactive segments
// This is used to test e.g. multipolygon rings that cross
// `activeNodes` is the ring containing the activeID being dragged.
// `inactiveNodes` is the other ring to test against
export function geoHasLineIntersections(activeNodes, inactiveNodes, activeID) {
var actives = [];
var inactives = [];
var j, k, n1, n2, segment;
// gather active segments (only segments in activeNodes that contain the activeID)
for (j = 0; j < activeNodes.length - 1; j++) {
n1 = activeNodes[j];
n2 = activeNodes[j+1];
segment = [n1.loc, n2.loc];
if (n1.id === activeID || n2.id === activeID) {
actives.push(segment);
}
}
// gather inactive segments
for (j = 0; j < inactiveNodes.length - 1; j++) {
n1 = inactiveNodes[j];
n2 = inactiveNodes[j+1];
segment = [n1.loc, n2.loc];
inactives.push(segment);
}
// test
for (j = 0; j < actives.length; j++) {
for (k = 0; k < inactives.length; k++) {
var p = actives[j];
var q = inactives[k];
var hit = geoLineIntersection(p, q);
if (hit) {
return true;
}
}
}
return false;
}
// Test active (dragged or drawing) segments against inactive segments
// This is used to test whether a way intersects with itself.
export function geoHasSelfIntersections(nodes, activeID) {
var actives = [];
var inactives = [];
var j, k;
// group active and passive segments along the nodes
for (j = 0; j < nodes.length - 1; j++) {
var n1 = nodes[j];
var n2 = nodes[j+1];
@@ -97,6 +142,7 @@ export function geoHasSelfIntersections(nodes, activeID) {
}
}
// test
for (j = 0; j < actives.length; j++) {
for (k = 0; k < inactives.length; k++) {
var p = actives[j];
+1
View File
@@ -13,6 +13,7 @@ export { geoZoomToScale } from './geo.js';
export { geoAngle } from './geom.js';
export { geoChooseEdge } from './geom.js';
export { geoEdgeEqual } from './geom.js';
export { geoHasLineIntersections } from './geom.js';
export { geoHasSelfIntersections } from './geom.js';
export { geoRotate } from './geom.js';
export { geoLineIntersection } from './geom.js';
+2 -2
View File
@@ -24,8 +24,8 @@ import {
import {
geoChooseEdge,
geoHasLineIntersections,
geoHasSelfIntersections,
geoPathHasIntersections,
geoVecSubtract,
geoViewportEdge
} from '../geo';
@@ -245,7 +245,7 @@ export function modeDragNode(context) {
if (k === activeIndex) continue;
// make sure active ring doesnt cross passive rings
if (geoPathHasIntersections(rings[activeIndex].coords, rings[k].coords)) {
if (geoHasLineIntersections(rings[activeIndex].nodes, rings[k].nodes, entity.id)) {
return true;
}
}
+73
View File
@@ -160,6 +160,79 @@ describe('iD.geo - geometry', function() {
});
});
describe('geoHasLineIntersections', function() {
it('returns false for a degenerate way (no nodes)', function() {
expect(iD.geoHasLineIntersections([], '')).to.be.false;
});
it('returns false if no activeID', function() {
var a = iD.osmNode({id: 'a', loc: [2, 2]});
var b = iD.osmNode({id: 'b', loc: [4, 2]});
var c = iD.osmNode({id: 'c', loc: [4, 4]});
var d = iD.osmNode({id: 'd', loc: [2, 4]});
var nodes = [a, b, c, d, a];
expect(iD.geoHasLineIntersections(nodes, '')).to.be.false;
});
it('returns false if there are no intersections', function() {
// e --------- f
// | |
// | a --- b |
// | | | |
// | | | |
// | d --- c |
// | |
// h --------- g
var a = iD.osmNode({id: 'a', loc: [2, 2]});
var b = iD.osmNode({id: 'b', loc: [4, 2]});
var c = iD.osmNode({id: 'c', loc: [4, 4]});
var d = iD.osmNode({id: 'd', loc: [2, 4]});
var e = iD.osmNode({id: 'e', loc: [0, 0]});
var f = iD.osmNode({id: 'f', loc: [8, 0]});
var g = iD.osmNode({id: 'g', loc: [8, 8]});
var h = iD.osmNode({id: 'h', loc: [0, 8]});
var inner = [a, b, c, d, a];
var outer = [e, f, g, h, e];
expect(iD.geoHasLineIntersections(inner, outer, 'a')).to.be.false;
expect(iD.geoHasLineIntersections(inner, outer, 'b')).to.be.false;
expect(iD.geoHasLineIntersections(inner, outer, 'c')).to.be.false;
expect(iD.geoHasLineIntersections(inner, outer, 'd')).to.be.false;
expect(iD.geoHasLineIntersections(outer, inner, 'e')).to.be.false;
expect(iD.geoHasLineIntersections(outer, inner, 'f')).to.be.false;
expect(iD.geoHasLineIntersections(outer, inner, 'g')).to.be.false;
expect(iD.geoHasLineIntersections(outer, inner, 'h')).to.be.false;
});
it('returns true if the activeID is causing intersections', function() {
// e --------- f
// | |
// | a --------- b
// | | |/
// | | /|
// | d --- c |
// | |
// h --------- g
var a = iD.osmNode({id: 'a', loc: [2, 2]});
var b = iD.osmNode({id: 'b', loc: [10, 2]});
var c = iD.osmNode({id: 'c', loc: [4, 4]});
var d = iD.osmNode({id: 'd', loc: [2, 4]});
var e = iD.osmNode({id: 'e', loc: [0, 0]});
var f = iD.osmNode({id: 'f', loc: [8, 0]});
var g = iD.osmNode({id: 'g', loc: [8, 8]});
var h = iD.osmNode({id: 'h', loc: [0, 8]});
var inner = [a, b, c, d, a];
var outer = [e, f, g, h, e];
expect(iD.geoHasLineIntersections(inner, outer, 'a')).to.be.true;
expect(iD.geoHasLineIntersections(inner, outer, 'b')).to.be.true;
expect(iD.geoHasLineIntersections(inner, outer, 'c')).to.be.true;
expect(iD.geoHasLineIntersections(inner, outer, 'd')).to.be.false;
expect(iD.geoHasLineIntersections(outer, inner, 'e')).to.be.false;
expect(iD.geoHasLineIntersections(outer, inner, 'f')).to.be.true;
expect(iD.geoHasLineIntersections(outer, inner, 'g')).to.be.true;
expect(iD.geoHasLineIntersections(outer, inner, 'h')).to.be.false;
});
});
describe('geoHasSelfIntersections', function() {
it('returns false for a degenerate way (no nodes)', function() {
expect(iD.geoHasSelfIntersections([], '')).to.be.false;