Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Orthogonal / Building drawing tool #2699

Closed
wants to merge 13 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions css/map.css
Original file line number Diff line number Diff line change
Expand Up @@ -1451,6 +1451,12 @@ g.turn circle {
cursor: url(img/cursor-draw.png) 9 9, crosshair; /* FF */
}

.mode-add-line .behavior-draworthogonal,
.mode-add-area .behavior-draworthogonal {
cursor: crosshair; /* Opera */
cursor: url(img/cursor-draw-square.png) 9 9, crosshair; /* FF */
}

.mode-draw-line .way.hover,
.mode-draw-area .way.hover,
.mode-add-line .way.hover,
Expand Down
Binary file added dist/img/cursor-draw-square.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added dist/img/cursor-draw-square2x.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion js/id/behavior/add_way.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ iD.behavior.AddWay = function(context) {
.on('clickWay', event.startFromWay)
.on('clickNode', event.startFromNode)
.on('cancel', addWay.cancel)
.on('finish', addWay.cancel);
.on('finish', addWay.cancel)
.startSegment(null);

context.map()
.dblclickEnable(false);
Expand Down
194 changes: 154 additions & 40 deletions js/id/behavior/draw.js
Original file line number Diff line number Diff line change
@@ -1,78 +1,163 @@
iD.behavior.Draw = function(context) {
var event = d3.dispatch('move', 'click', 'clickWay',
'clickNode', 'undo', 'cancel', 'finish'),
'clickNode', 'clickTargets', 'undo', 'cancel', 'finish'),
keybinding = d3.keybinding('draw'),
hover = iD.behavior.Hover(context)
.altDisables(true)
.on('hover', context.ui().sidebar.hover),
tail = iD.behavior.Tail(),
edit = iD.behavior.Edit(context),
startSegment = [],
closeTolerance = 4,
tolerance = 12;

function datum() {
if (d3.event.altKey) return {};
else return d3.event.target.__data__ || {};

function keydown() {
if (d3.event && d3.event.shiftKey) {
context.surface()
.classed('behavior-draworthogonal', true);
}
}

function mousedown() {
function keyup() {
if (!d3.event || !d3.event.shiftKey) {
context.surface()
.classed('behavior-draworthogonal', false);
}
}

// Depending on mode option, return an array of touch targets:
// [{ entity: entity, point: [x,y], loc: [lon,lat] }]
// There will normally be a singular touch target at mousePoint,
// unless we're in a special drawing mode.
function getTargets() {
function vecAdd(a, b) { return [a[0] + b[0], a[1] + b[1]]; }
function perpendicularVector(a, b, mag) {
var len = iD.geo.euclideanDistance(a, b);
return len === 0 ? [0, 0] : [
((b[1] - a[1]) / len) * mag,
((b[0] - a[0]) / len) * mag * -1
];
}

var altKey = d3.event.altKey,
mousePoint = context.map().mouse(),
mouseLoc = context.map().mouseCoordinates();

if (context.mode().option === 'orthogonal' && startSegment.length === 2) {
var p0 = context.projection(startSegment[0]),
p1 = context.projection(startSegment[1]),
surface = context.surfaceRect(),
theta = Math.atan2(p1[1] - mousePoint[1], p1[0] - mousePoint[0]) -
Math.atan2(p1[1] - p0[1], p1[0] - p0[0]),
height = iD.geo.euclideanDistance(p1, mousePoint) * Math.sin(theta),
perpVec = perpendicularVector(p0, p1, height),
q0 = vecAdd(p0, perpVec),
q1 = vecAdd(p1, perpVec),
points = [q1, q0];

return _.map(points, function(p) {
var target = document.elementFromPoint(p[0] + surface.left, p[1] + surface.top),
data = target && target.__data__,
entity = data instanceof iD.Entity ? data : null;
return {
entity: altKey ? null : entity,
point: iD.geo.roundCoords(p),
loc: context.projection.invert(p)
};
});

} else {
return [{ entity: altKey ? null : d3.event.target.__data__, point: mousePoint, loc: mouseLoc }];
}
}

function mousedown() {
function point() {
var p = context.container().node();
return touchId !== null ? d3.touches(p).filter(function(p) {
return p.identifier === touchId;
})[0] : d3.mouse(p);
}

var element = d3.select(this),
touchId = d3.event.touches ? d3.event.changedTouches[0].identifier : null,
t1 = +new Date(),
p1 = point();
var mode = context.mode();
if (d3.event.shiftKey && (mode.id === 'add-area' || mode.id === 'add-line')) {
mode.option = 'orthogonal';
d3.event.preventDefault();
d3.event.stopPropagation();
click();

} else {
var element = d3.select(this),
touchId = d3.event.touches ? d3.event.changedTouches[0].identifier : null,
t1 = +new Date(),
p1 = point();

element.on('mousemove.draw', null);
element.on('mousemove.draw', null);

d3.select(window).on('mouseup.draw', function() {
var t2 = +new Date(),
p2 = point(),
dist = iD.geo.euclideanDistance(p1, p2);
d3.select(window).on('mouseup.draw', function () {
var t2 = +new Date(),
p2 = point(),
dist = iD.geo.euclideanDistance(p1, p2);

element.on('mousemove.draw', mousemove);
d3.select(window).on('mouseup.draw', null);
d3.select(window).on('mouseup.draw', null);
element.on('mousemove.draw', mousemove);

if (dist < closeTolerance || (dist < tolerance && (t2 - t1) < 500)) {
// Prevent a quick second click
d3.select(window).on('click.draw-block', function() {
d3.event.stopPropagation();
}, true);
if (dist < closeTolerance || (dist < tolerance && (t2 - t1) < 500)) {
// Prevent a quick second click
d3.select(window).on('click.draw-block', function() {
d3.event.stopPropagation();
}, true);

context.map().dblclickEnable(false);
context.map().dblclickEnable(false);

window.setTimeout(function() {
context.map().dblclickEnable(true);
d3.select(window).on('click.draw-block', null);
}, 500);
window.setTimeout(function() {
context.map().dblclickEnable(true);
d3.select(window).on('click.draw-block', null);
}, 500);

click();
}
});
click();
}
});
}
}

function mousemove() {
event.move(datum());
event.move(getTargets());
}

function needsSegment() {
return context.mode().option === 'orthogonal' && startSegment.length < 2;
}

function mouseup() {
if (needsSegment()) click();
}

function click() {
var d = datum();
if (d.type === 'way') {
var choice = iD.geo.chooseEdge(context.childNodes(d), context.mouse(), context.projection),
edge = [d.nodes[choice.index - 1], d.nodes[choice.index]];
var targets = getTargets();

if (targets.length > 1) {
event.clickTargets(targets);
return;
}

var d = targets[0],
e = d.entity;

if (e && e.type === 'way') {
var choice = iD.geo.chooseEdge(context.childNodes(e), context.mouse(), context.projection),
edge = [e.nodes[choice.index - 1], e.nodes[choice.index]];
if (needsSegment()) startSegment.push(choice.loc);
event.clickWay(choice.loc, edge);

} else if (d.type === 'node') {
event.clickNode(d);
} else if (e && e.type === 'node') {
if (needsSegment()) startSegment.push(e.loc);
event.clickNode(e);

} else {
event.click(context.map().mouseCoordinates());
if (needsSegment()) startSegment.push(d.loc);
event.click(d.loc);
}
}

Expand All @@ -92,7 +177,13 @@ iD.behavior.Draw = function(context) {
}

function draw(selection) {
context.install(hover);
if (context.mode().option === 'orthogonal' && startSegment.length === 2) {
hover = null;
context.map().vertexHoverEnable(false);
} else {
context.install(hover);
}

context.install(edit);

if (!context.inIntro() && !iD.behavior.Draw.usedTails[tail.text()]) {
Expand All @@ -112,12 +203,25 @@ iD.behavior.Draw = function(context) {
d3.select(document)
.call(keybinding);

d3.select(window)
.on('mouseup.draw', mouseup)
.on('keydown.draw', keydown)
.on('keyup.draw', keyup);

keydown();

return draw;
}

draw.off = function(selection) {
context.ui().sidebar.hover.cancel();
context.uninstall(hover);

if (hover) {
context.uninstall(hover);
} else {
context.map().vertexHoverEnable(true);
}

context.uninstall(edit);

if (!context.inIntro() && !iD.behavior.Draw.usedTails[tail.text()]) {
Expand All @@ -129,8 +233,12 @@ iD.behavior.Draw = function(context) {
.on('mousedown.draw', null)
.on('mousemove.draw', null);

keyup();

d3.select(window)
.on('mouseup.draw', null);
.on('mouseup.draw', null)
.on('keydown.draw', null)
.on('keyup.draw', null);

d3.select(document)
.call(keybinding.off);
Expand All @@ -141,6 +249,12 @@ iD.behavior.Draw = function(context) {
return draw;
};

draw.startSegment = function(_) {
if (!arguments.length) return startSegment;
startSegment = _ || [];
return draw;
};

return d3.rebind(draw, event, 'on');
};

Expand Down
Loading