mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-20 23:44:47 +02:00
Merge pull request #2257 from bhousel/bhousel-midpoints
Always draw midpoint handles within viewport (like #1840)
This commit is contained in:
@@ -87,6 +87,39 @@ iD.geo.chooseEdge = function(nodes, point, projection) {
|
||||
};
|
||||
};
|
||||
|
||||
// Return the intersection point of 2 line segments.
|
||||
// From https://github.com/pgkelley4/line-segments-intersect
|
||||
// This uses the vector cross product approach described below:
|
||||
// http://stackoverflow.com/a/565282/786339
|
||||
iD.geo.lineIntersection = function(a, b) {
|
||||
function subtractPoints(point1, point2) {
|
||||
return [point1[0] - point2[0], point1[1] - point2[1]];
|
||||
}
|
||||
function crossProduct(point1, point2) {
|
||||
return point1[0] * point2[1] - point1[1] * point2[0];
|
||||
}
|
||||
|
||||
var p = [a[0][0], a[0][1]],
|
||||
p2 = [a[1][0], a[1][1]],
|
||||
q = [b[0][0], b[0][1]],
|
||||
q2 = [b[1][0], b[1][1]],
|
||||
r = subtractPoints(p2, p),
|
||||
s = subtractPoints(q2, q),
|
||||
uNumerator = crossProduct(subtractPoints(q, p), r),
|
||||
denominator = crossProduct(r, s);
|
||||
|
||||
if (uNumerator && denominator) {
|
||||
var u = uNumerator / denominator,
|
||||
t = crossProduct(subtractPoints(q, p), s) / denominator;
|
||||
|
||||
if ((t >= 0) && (t <= 1) && (u >= 0) && (u <= 1)) {
|
||||
return iD.geo.interp(p, p2, t);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
// Return whether point is contained in polygon.
|
||||
//
|
||||
// `point` should be a 2-item array of coordinates.
|
||||
|
||||
@@ -76,10 +76,9 @@ iD.Map = function(context) {
|
||||
if (map.editable() && !transformed) {
|
||||
var all = context.intersects(map.extent()),
|
||||
filter = d3.functor(true),
|
||||
extent = map.extent(),
|
||||
graph = context.graph();
|
||||
surface.call(vertices, graph, all, filter, extent, map.zoom());
|
||||
surface.call(midpoints, graph, all, filter, extent);
|
||||
surface.call(vertices, graph, all, filter, map.extent(), map.zoom());
|
||||
surface.call(midpoints, graph, all, filter, map.trimmedExtent());
|
||||
dispatch.drawn({full: false});
|
||||
}
|
||||
});
|
||||
@@ -114,7 +113,7 @@ iD.Map = function(context) {
|
||||
.call(vertices, graph, all, filter, map.extent(), map.zoom())
|
||||
.call(lines, graph, all, filter)
|
||||
.call(areas, graph, all, filter)
|
||||
.call(midpoints, graph, all, filter, map.extent())
|
||||
.call(midpoints, graph, all, filter, map.trimmedExtent())
|
||||
.call(labels, graph, all, filter, dimensions, !difference && !extent);
|
||||
|
||||
if (points.points(context.intersects(map.extent()), 100).length >= 100) {
|
||||
@@ -366,6 +365,12 @@ iD.Map = function(context) {
|
||||
}
|
||||
};
|
||||
|
||||
map.trimmedExtent = function() {
|
||||
var headerY = 60, footerY = 30, pad = 10;
|
||||
return new iD.geo.Extent(projection.invert([pad, dimensions[1] - footerY - pad]),
|
||||
projection.invert([dimensions[0] - pad, headerY + pad]));
|
||||
};
|
||||
|
||||
map.extentZoom = function(_) {
|
||||
var extent = iD.geo.Extent(_),
|
||||
tl = projection([extent[0][0], extent[1][1]]),
|
||||
|
||||
+30
-10
@@ -1,6 +1,7 @@
|
||||
iD.svg.Midpoints = function(projection, context) {
|
||||
return function drawMidpoints(surface, graph, entities, filter, extent) {
|
||||
var midpoints = {};
|
||||
var poly = extent.polygon(),
|
||||
midpoints = {};
|
||||
|
||||
for (var i = 0; i < entities.length; i++) {
|
||||
var entity = entities[i];
|
||||
@@ -22,15 +23,34 @@ iD.svg.Midpoints = function(projection, context) {
|
||||
if (midpoints[id]) {
|
||||
midpoints[id].parents.push(entity);
|
||||
} else {
|
||||
var loc = iD.geo.interp(a.loc, b.loc, 0.5);
|
||||
if (extent.intersects(loc) && iD.geo.euclideanDistance(projection(a.loc), projection(b.loc)) > 40) {
|
||||
midpoints[id] = {
|
||||
type: 'midpoint',
|
||||
id: id,
|
||||
loc: loc,
|
||||
edge: [a.id, b.id],
|
||||
parents: [entity]
|
||||
};
|
||||
if (iD.geo.euclideanDistance(projection(a.loc), projection(b.loc)) > 40) {
|
||||
var point = iD.geo.interp(a.loc, b.loc, 0.5),
|
||||
loc = null;
|
||||
|
||||
if (extent.intersects(point)) {
|
||||
loc = point;
|
||||
} else {
|
||||
for (var k = 0; k < 4; k++) {
|
||||
point = iD.geo.lineIntersection([a.loc, b.loc], [poly[k], poly[k+1]]);
|
||||
if (point &&
|
||||
iD.geo.euclideanDistance(projection(a.loc), projection(point)) > 20 &&
|
||||
iD.geo.euclideanDistance(projection(b.loc), projection(point)) > 20)
|
||||
{
|
||||
loc = point;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (loc) {
|
||||
midpoints[id] = {
|
||||
type: 'midpoint',
|
||||
id: id,
|
||||
loc: loc,
|
||||
edge: [a.id, b.id],
|
||||
parents: [entity]
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,6 +156,34 @@ describe('iD.geo', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('.lineIntersection', function() {
|
||||
it('returns null if lines are colinear with overlap', function() {
|
||||
var a = [[0, 0], [10, 0]],
|
||||
b = [[-5, 0], [5, 0]];
|
||||
expect(iD.geo.lineIntersection(a, b)).to.be.null;
|
||||
});
|
||||
it('returns null if lines are colinear but disjoint', function() {
|
||||
var a = [[5, 0], [10, 0]],
|
||||
b = [[-10, 0], [-5, 0]];
|
||||
expect(iD.geo.lineIntersection(a, b)).to.be.null;
|
||||
});
|
||||
it('returns null if lines are parallel', function() {
|
||||
var a = [[0, 0], [10, 0]],
|
||||
b = [[0, 5], [10, 5]];
|
||||
expect(iD.geo.lineIntersection(a, b)).to.be.null;
|
||||
});
|
||||
it('returns the intersection point between 2 lines', function() {
|
||||
var a = [[0, 0], [10, 0]],
|
||||
b = [[5, 10], [5, -10]];
|
||||
expect(iD.geo.lineIntersection(a, b)).to.eql([5, 0]);
|
||||
});
|
||||
it('returns null if lines are not parallel but not intersecting', function() {
|
||||
var a = [[0, 0], [10, 0]],
|
||||
b = [[-5, 10], [-5, -10]];
|
||||
expect(iD.geo.lineIntersection(a, b)).to.be.null;
|
||||
});
|
||||
});
|
||||
|
||||
describe('.pointInPolygon', function() {
|
||||
it('says a point in a polygon is on a polygon', function() {
|
||||
var poly = [[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]];
|
||||
|
||||
@@ -10,7 +10,7 @@ describe("iD.svg.Midpoints", function () {
|
||||
.call(iD.svg.Surface(context));
|
||||
});
|
||||
|
||||
it("finds the location of the midpoints", function () {
|
||||
it("creates midpoint on segment completely within the extent", function () {
|
||||
var a = iD.Node({loc: [0, 0]}),
|
||||
b = iD.Node({loc: [50, 0]}),
|
||||
line = iD.Way({nodes: [a.id, b.id]}),
|
||||
@@ -23,7 +23,7 @@ describe("iD.svg.Midpoints", function () {
|
||||
expect(surface.select('.midpoint').datum().loc).to.eql([25, 0]);
|
||||
});
|
||||
|
||||
it("doesn't create midpoints on segments with pixel length less than 40", function () {
|
||||
it("doesn't create midpoint on segment with pixel length less than 40", function () {
|
||||
var a = iD.Node({loc: [0, 0]}),
|
||||
b = iD.Node({loc: [39, 0]}),
|
||||
line = iD.Way({nodes: [a.id, b.id]}),
|
||||
@@ -35,4 +35,44 @@ describe("iD.svg.Midpoints", function () {
|
||||
|
||||
expect(surface.selectAll('.midpoint')[0]).to.have.length(0);
|
||||
});
|
||||
|
||||
it("doesn't create midpoint on segment completely outside of the extent", function () {
|
||||
var a = iD.Node({loc: [-100, 0]}),
|
||||
b = iD.Node({loc: [-50, 0]}),
|
||||
line = iD.Way({nodes: [a.id, b.id]}),
|
||||
graph = iD.Graph([a, b, line]),
|
||||
extent = iD.geo.Extent([0, 0], [100, 100]);
|
||||
|
||||
context.selectedIDs = function() { return [line.id]; };
|
||||
surface.call(iD.svg.Midpoints(projection, context), graph, [line], filter, extent);
|
||||
|
||||
expect(surface.selectAll('.midpoint')[0]).to.have.length(0);
|
||||
});
|
||||
|
||||
it("creates midpoint on extent edge for segment partially outside of the extent", function () {
|
||||
var a = iD.Node({loc: [50, 0]}),
|
||||
b = iD.Node({loc: [500, 0]}),
|
||||
line = iD.Way({nodes: [a.id, b.id]}),
|
||||
graph = iD.Graph([a, b, line]),
|
||||
extent = iD.geo.Extent([0, 0], [100, 100]);
|
||||
|
||||
context.selectedIDs = function() { return [line.id]; };
|
||||
surface.call(iD.svg.Midpoints(projection, context), graph, [line], filter, extent);
|
||||
|
||||
expect(surface.select('.midpoint').datum().loc).to.eql([100, 0]);
|
||||
});
|
||||
|
||||
it("doesn't create midpoint on extent edge for segment with pixel length less than 20", function () {
|
||||
var a = iD.Node({loc: [81, 0]}),
|
||||
b = iD.Node({loc: [500, 0]}),
|
||||
line = iD.Way({nodes: [a.id, b.id]}),
|
||||
graph = iD.Graph([a, b, line]),
|
||||
extent = iD.geo.Extent([0, 0], [100, 100]);
|
||||
|
||||
context.selectedIDs = function() { return [line.id]; };
|
||||
surface.call(iD.svg.Midpoints(projection, context), graph, [line], filter, extent);
|
||||
|
||||
expect(surface.selectAll('.midpoint')[0]).to.have.length(0);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user