Skip to content

Commit

Permalink
Extract Draw behavior
Browse files Browse the repository at this point in the history
Fixed some bugs but introduced others.
  • Loading branch information
jfirebaugh committed Jan 24, 2013
1 parent 4d74789 commit 18c7267
Show file tree
Hide file tree
Showing 11 changed files with 333 additions and 328 deletions.
2 changes: 2 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@
<script src='js/id/behavior/drag_midpoint.js'></script>
<script src='js/id/behavior/drag_node.js'></script>
<script src='js/id/behavior/drag_way.js'></script>
<script src='js/id/behavior/draw.js'></script>
<script src='js/id/behavior/draw_way.js'></script>
<script src='js/id/behavior/hover.js'></script>

<script src='js/id/modes.js'></script>
Expand Down
54 changes: 54 additions & 0 deletions js/id/behavior/draw.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
iD.behavior.Draw = function () {
var event = d3.dispatch('move', 'add', 'drop', 'cancel', 'finish'),
keybinding = d3.keybinding('draw');

function draw(selection) {
function mousemove() {
event.move();
}

function click() {
event.add();
}

function backspace() {
d3.event.preventDefault();
event.drop();
}

function del() {
d3.event.preventDefault();
event.cancel();
}

function ret() {
d3.event.preventDefault();
event.finish();
}

selection
.on('mousemove.draw', mousemove)
.on('click.draw', click);

keybinding
.on('⌫', backspace)
.on('⌦', del)
.on('⎋', ret)
.on('↩', ret);

d3.select(document)
.call(keybinding);

return draw;
}

draw.off = function(selection) {
selection
.on('mousemove.draw', null)
.on('click.draw', null);

keybinding.off();
};

return d3.rebind(draw, event, 'on');
};
146 changes: 146 additions & 0 deletions js/id/behavior/draw_way.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
iD.behavior.DrawWay = function(wayId, headId, tailId, index, mode) {
var map = mode.map,
history = mode.history,
controller = mode.controller,
event = d3.dispatch('add', 'addHead', 'addTail', 'addNode', 'addWay'),
way = mode.history.graph().entity(wayId),
nodeId = way.nodes[index],
hover, draw;

function move() {
history.replace(
iD.actions.MoveNode(nodeId, map.mouseCoordinates()),
history.undoAnnotation());
}

function add() {
var datum = d3.select(d3.event.target).datum() || {};

if (datum.id === headId) {
event.addHead(datum);
} else if (datum.id === tailId) {
event.addTail(datum);
} else if (datum.type === 'node' && datum.id !== nodeId) {
event.addNode(datum);
} else if (datum.type === 'way') {
var choice = iD.geo.chooseIndex(datum, d3.mouse(map.surface.node()), map);
event.addWay(datum, choice.loc, choice.index);
} else if (datum.midpoint) {
var way = history.graph().entity(datum.way);
event.addWay(way, datum.loc, datum.index);
} else {
event.add(map.mouseCoordinates());
}
}

function undone() {
var way = history.graph().entity(wayId);
if (way) {
controller.enter(mode);
} else {
controller.enter(iD.modes.Browse());
}
}

var drawWay = function(surface) {
map.fastEnable(false)
.minzoom(16)
.dblclickEnable(false);

surface.call(hover)
.call(draw)
.selectAll('.way, .node')
.filter(function (d) { return d.id === wayId || d.id === nodeId; })
.classed('active', true);

history.on('undone.draw', undone);
};

drawWay.off = function(surface) {
map.fastEnable(true)
.minzoom(0)
.tail(false);

window.setTimeout(function() {
map.dblclickEnable(true);
}, 1000);

surface.call(hover.off)
.call(draw.off)
.selectAll('.way, .node')
.classed('active', false);

history.on('undone.draw', null);
};

// Connect the way to an existing node. Continue drawing, or enter the optional `newMode`.
drawWay.addNode = function(node, annotation, newMode) {
history.perform(
iD.actions.AddWayNode(wayId, node.id, index),
annotation);

controller.enter(newMode || mode);
};

// Connect the way to an existing way.
drawWay.addWay = function(way, loc, wayIndex, annotation) {
var newNode = iD.Node({loc: loc});

history.perform(
iD.actions.AddNode(newNode),
iD.actions.AddWayNode(wayId, newNode.id, index),
iD.actions.AddWayNode(way.id, newNode.id, wayIndex),
annotation);

controller.enter(mode);
};

// Accept the current position of the temporary node and continue drawing.
drawWay.add = function(loc, annotation) {
var newNode = iD.Node({loc: loc});

history.perform(
iD.actions.AddNode(newNode),
iD.actions.AddWayNode(wayId, newNode.id, index),
annotation);

controller.enter(mode);
};

// Remove the temporary node and the last connected node but continue drawing.
drawWay.drop = function() {
history.undo();
};

// Finish the draw operation, removing the temporary node. If the way has enough
// nodes to be valid, it's selected. Otherwise, return to browse mode.
drawWay.finish = function() {
history.replace(
iD.actions.DeleteNode(nodeId),
history.undoAnnotation());

var way = history.graph().entity(wayId);
if (way) {
controller.enter(iD.modes.Select(way, true));
} else {
controller.enter(iD.modes.Browse());
}
};

// Cancel the draw operation and return to browse, deleting everything drawn.
drawWay.cancel = function() {
history.perform(iD.actions.DeleteWay(wayId), 'cancelled drawing');
controller.enter(iD.modes.Browse());
};

hover = iD.behavior.Hover();

draw = iD.behavior.Draw()
.on('move', move)
.on('add', add)
.on('drop', drawWay.drop)
.on('cancel', drawWay.cancel)
.on('finish', drawWay.finish);

return d3.rebind(drawWay, event, 'on');
};
2 changes: 1 addition & 1 deletion js/id/graph/history.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ iD.History = function() {

var annotation;

if (_.isString(_.last(actions))) {
if (!_.isFunction(_.last(actions))) {
annotation = actions.pop();
}

Expand Down
45 changes: 30 additions & 15 deletions js/id/modes/add_area.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,55 +6,70 @@ iD.modes.AddArea = function() {
description: 'Add parks, buildings, lakes, or other areas to the map.'
};

var keybinding = d3.keybinding('add-area');
var behavior;

mode.enter = function() {
var map = mode.map,
surface = map.surface,
history = mode.history,
controller = mode.controller;

map.dblclickEnable(false)
.tail('Click on the map to start drawing an area, like a park, lake, or building.');

map.surface.on('click.addarea', function() {
function add() {
var datum = d3.select(d3.event.target).datum() || {},
way = iD.Way({tags: { area: 'yes' }});
way = iD.Way({tags: { area: 'yes' }}),
node;

if (datum.type === 'node') {
// start from an existing node
node = datum;
history.perform(
iD.actions.AddWay(way),
iD.actions.AddWayNode(way.id, datum.id),
iD.actions.AddWayNode(way.id, datum.id));
iD.actions.AddWayNode(way.id, node.id),
iD.actions.AddWayNode(way.id, node.id));

} else {
// start from a new node
var node = iD.Node({loc: map.mouseCoordinates()});
node = iD.Node({loc: map.mouseCoordinates()});
history.perform(
iD.actions.AddWay(way),
iD.actions.AddNode(node),
iD.actions.AddWayNode(way.id, node.id),
iD.actions.AddWayNode(way.id, node.id));
}

node = iD.Node({loc: node.loc});

history.replace(
iD.actions.AddNode(node),
iD.actions.AddWayNode(way.id, node.id, -1),
'started an area');

controller.enter(iD.modes.DrawArea(way.id));
});
}

keybinding.on('⎋', function() {
function cancel() {
controller.exit();
});
}

d3.select(document)
.call(keybinding);
behavior = iD.behavior.Draw()
.on('add', add)
.on('cancel', cancel)
.on('finish', cancel)
(surface);
};

mode.exit = function() {
var map = mode.map,
surface = map.surface;

window.setTimeout(function() {
mode.map.dblclickEnable(true);
map.dblclickEnable(true);
}, 1000);
mode.map.tail(false);
mode.map.surface.on('click.addarea', null);
keybinding.off();
map.tail(false);
behavior.off(surface);
};

return mode;
Expand Down
Loading

0 comments on commit 18c7267

Please sign in to comment.