Snap to ways/vertices/midpoints when drawing (#240)

Also add anti-snapping behavior when option key is down.
This commit is contained in:
John Firebaugh
2013-01-25 11:47:05 -05:00
parent 7b374de5f8
commit b37ef978f4
6 changed files with 126 additions and 78 deletions

View File

@@ -16,7 +16,7 @@ g.point .shadow {
transition: transform 100ms linear;
-moz-transition: fill 100ms linear;
}
g.point.hover .shadow {
.behavior-hover g.point.hover .shadow {
fill: #E96666;
fill-opacity: 0.3;
}
@@ -106,7 +106,7 @@ g.vertex .shadow {
transition: transform 100ms linear;
-moz-transition: fill 100ms linear;
}
g.vertex.hover .shadow {
.behavior-hover g.vertex.hover .shadow {
fill: #E96666;
fill-opacity: 0.3;
}
@@ -120,7 +120,7 @@ g.vertex.selected .shadow {
g.midpoint .fill {
fill:#aaa;
}
g.midpoint .fill.hover {
.behavior-hover g.midpoint .fill.hover {
fill:#fff;
stroke:#000;
}
@@ -133,7 +133,7 @@ g.midpoint .shadow {
transition: transform 100ms linear;
-moz-transition: fill 100ms linear;
}
g.midpoint .shadow.hover {
.behavior-hover g.midpoint .shadow.hover {
fill:#E96666;
fill-opacity: 0.3;
}
@@ -161,7 +161,7 @@ path.shadow {
-webkit-transition: stroke 100ms linear;
}
path.shadow.hover {
.behavior-hover path.shadow.hover {
stroke: #E96666;
stroke-opacity: 0.3;
}
@@ -691,17 +691,17 @@ text.point.tag-amenity {
cursor:url(../img/cursor-draw.png) 9 9, auto;
}
.mode-draw-line .way,
.mode-draw-area .way,
.mode-add-line .way,
.mode-add-area .way {
.mode-draw-line .behavior-hover .way,
.mode-draw-area .behavior-hover .way,
.mode-add-line .behavior-hover .way,
.mode-add-area .behavior-hover .way {
cursor:url(../img/cursor-draw-connect-line.png) 9 9, auto;
}
.mode-draw-line .vertex,
.mode-draw-area .vertex,
.mode-add-line .vertex,
.mode-add-area .vertex {
.mode-draw-line .behavior-hover .vertex,
.mode-draw-area .behavior-hover .vertex,
.mode-add-line .behavior-hover .vertex,
.mode-add-area .behavior-hover .vertex {
cursor:url(../img/cursor-draw-connect-vertex.png) 9 9, auto;
}

View File

@@ -3,11 +3,9 @@ iD.behavior.AddWay = function(mode) {
history = mode.history,
controller = mode.controller,
event = d3.dispatch('startFromNode', 'startFromWay', 'start'),
hover, draw;
function add() {
var datum = d3.select(d3.event.target).datum() || {};
draw;
function add(datum) {
if (datum.type === 'node') {
event.startFromNode(datum);
} else if (datum.type === 'way') {
@@ -26,8 +24,7 @@ iD.behavior.AddWay = function(mode) {
.minzoom(16)
.dblclickEnable(false);
surface.call(hover)
.call(draw);
surface.call(draw);
};
addWay.off = function(surface) {
@@ -39,16 +36,13 @@ iD.behavior.AddWay = function(mode) {
map.dblclickEnable(true);
}, 1000);
surface.call(hover.off)
.call(draw.off);
surface.call(draw.off);
};
addWay.cancel = function() {
controller.exit();
};
hover = iD.behavior.Hover();
draw = iD.behavior.Draw()
.on('add', add)
.on('cancel', addWay.cancel)

View File

@@ -1,45 +1,64 @@
iD.behavior.Draw = function () {
var event = d3.dispatch('move', 'add', 'undo', 'cancel', 'finish'),
keybinding = d3.keybinding('draw'),
down;
down, surface, hover;
function datum() {
if (d3.event.altKey) {
return {};
} else {
return d3.event.target.__data__ || {};
}
}
function mousedown() {
down = true;
}
function mouseup() {
down = false;
}
function mousemove() {
if (!down) {
event.move(datum());
}
}
function click() {
event.add(datum());
}
function keydown() {
if (d3.event.keyCode === d3.keybinding.modifierCodes.alt) {
surface.call(hover.off);
}
}
function keyup() {
if (d3.event.keyCode === d3.keybinding.modifierCodes.alt) {
surface.call(hover);
}
}
function backspace() {
d3.event.preventDefault();
event.undo();
}
function del() {
d3.event.preventDefault();
event.cancel();
}
function ret() {
d3.event.preventDefault();
event.finish();
}
function draw(selection) {
function mousemove() {
if (!down) event.move();
}
function click() {
event.add();
}
function mousedown() {
down = true;
}
function mouseup() {
down = false;
}
function backspace() {
d3.event.preventDefault();
event.undo();
}
function del() {
d3.event.preventDefault();
event.cancel();
}
function ret() {
d3.event.preventDefault();
event.finish();
}
selection
.on('mousedown.draw', mousedown)
.on('mouseup.draw', mouseup)
.on('mousemove.draw', mousemove)
.on('click.draw', click);
surface = selection;
hover = iD.behavior.Hover();
keybinding
.on('⌫', backspace)
@@ -47,18 +66,33 @@ iD.behavior.Draw = function () {
.on('⎋', ret)
.on('↩', ret);
selection
.on('mousedown.draw', mousedown)
.on('mouseup.draw', mouseup)
.on('mousemove.draw', mousemove)
.on('click.draw', click)
.call(hover);
d3.select(document)
.call(keybinding);
.call(keybinding)
.on('keydown.draw', keydown)
.on('keyup.draw', keyup);
return draw;
}
draw.off = function(selection) {
selection
.on('mousedown.draw', null)
.on('mouseup.draw', null)
.on('mousemove.draw', null)
.on('click.draw', null);
.on('click.draw', null)
.call(hover.off);
keybinding.off();
d3.select(document)
.call(keybinding.off)
.on('keydown.draw', null)
.on('keyup.draw', null);
};
return d3.rebind(draw, event, 'on');

View File

@@ -5,7 +5,7 @@ iD.behavior.DrawWay = function(wayId, headId, tailId, index, mode, baseGraph) {
event = d3.dispatch('add', 'addHead', 'addTail', 'addNode', 'addWay'),
way = mode.history.graph().entity(wayId),
finished = false,
hover, draw;
draw;
var node = iD.Node({loc: map.mouseCoordinates()}),
nodeId = node.id;
@@ -14,13 +14,19 @@ iD.behavior.DrawWay = function(wayId, headId, tailId, index, mode, baseGraph) {
iD.actions.AddNode(node),
iD.actions.AddWayNode(wayId, node.id, index));
function move() {
history.replace(iD.actions.MoveNode(nodeId, map.mouseCoordinates()));
function move(datum) {
var loc = map.mouseCoordinates();
if (datum.type === 'node' || datum.midpoint) {
loc = datum.loc;
} else if (datum.type === 'way') {
loc = iD.geo.chooseIndex(datum, d3.mouse(map.surface.node()), map).loc;
}
history.replace(iD.actions.MoveNode(nodeId, loc));
}
function add() {
var datum = d3.select(d3.event.target).datum() || {};
function add(datum) {
if (datum.id === headId) {
event.addHead(datum);
} else if (datum.id === tailId) {
@@ -47,8 +53,7 @@ iD.behavior.DrawWay = function(wayId, headId, tailId, index, mode, baseGraph) {
.minzoom(16)
.dblclickEnable(false);
surface.call(hover)
.call(draw)
surface.call(draw)
.selectAll('.way, .node')
.filter(function (d) { return d.id === wayId || d.id === nodeId; })
.classed('active', true);
@@ -68,8 +73,7 @@ iD.behavior.DrawWay = function(wayId, headId, tailId, index, mode, baseGraph) {
map.dblclickEnable(true);
}, 1000);
surface.call(hover.off)
.call(draw.off)
surface.call(draw.off)
.selectAll('.way, .node')
.classed('active', false);
@@ -145,8 +149,6 @@ iD.behavior.DrawWay = function(wayId, headId, tailId, index, mode, baseGraph) {
controller.enter(iD.modes.Browse());
};
hover = iD.behavior.Hover();
draw = iD.behavior.Draw()
.on('move', move)
.on('add', add)

View File

@@ -9,6 +9,8 @@
*/
iD.behavior.Hover = function () {
var hover = function(selection) {
selection.classed('behavior-hover', true);
selection.on('mouseover.hover', function () {
var datum = d3.event.target.__data__;
if (datum) {
@@ -25,7 +27,8 @@ iD.behavior.Hover = function () {
};
hover.off = function(selection) {
selection.on('mouseover.hover', null)
selection.classed('behavior-hover', false)
.on('mouseover.hover', null)
.on('mouseout.hover', null);
};

View File

@@ -9,8 +9,23 @@ describe("iD.behavior.Hover", function() {
container.remove();
});
describe("#on", function () {
it("adds the .behavior-hover class to the selection", function () {
container.call(iD.behavior.Hover());
expect(container).to.be.classed('behavior-hover')
});
});
describe("#off", function () {
it("removes the .behavior-hover class from the selection", function () {
container.classed('behavior-hover', true);
container.call(iD.behavior.Hover().off);
expect(container).not.to.be.classed('behavior-hover')
});
});
describe("mouseover", function () {
it("adds the 'hover' class to all elements to which the same datum is bound", function () {
it("adds the .hover class to all elements to which the same datum is bound", function () {
container.selectAll('span')
.data(['a', 'b', 'a', 'b'])
.enter().append('span').attr('class', Object);
@@ -24,7 +39,7 @@ describe("iD.behavior.Hover", function() {
});
describe("mouseout", function () {
it("removes the 'hover' class from all elements", function () {
it("removes the .hover class from all elements", function () {
container.append('span').attr('class', 'hover');
container.call(iD.behavior.Hover());