diff --git a/css/20_map.css b/css/20_map.css index 1004a3d1d..6eec27261 100644 --- a/css/20_map.css +++ b/css/20_map.css @@ -29,6 +29,8 @@ stroke-width: 12; stroke-opacity: 0.8; stroke: currentColor; + stroke-linecap: round; + stroke-linejoin: round; } /* `.target-nope` objects are explicitly forbidden to join to */ diff --git a/modules/behavior/draw.js b/modules/behavior/draw.js index d2421ffc3..513c0b30c 100644 --- a/modules/behavior/draw.js +++ b/modules/behavior/draw.js @@ -33,8 +33,7 @@ export function behaviorDraw(context) { var keybinding = d3_keybinding('draw'); - var hover = behaviorHover(context) - .altDisables(true) + var hover = behaviorHover(context).altDisables(true) .on('hover', context.ui().sidebar.hover); var tail = behaviorTail(); var edit = behaviorEdit(context); @@ -58,10 +57,8 @@ export function behaviorDraw(context) { // When drawing, connect only to things classed as targets.. // (this excludes area fills and active drawing elements) var selection = d3_select(element); - if (selection.classed('target')) return {}; - - var d = selection.datum(); - return (d && d.id && context.hasEntity(d.id)) || {}; + if (!selection.classed('target')) return {}; + return selection.datum(); } @@ -124,28 +121,33 @@ export function behaviorDraw(context) { } + // related code + // - `mode/drag_node.js` `doMode()` + // - `behavior/draw.js` `click()` + // - `behavior/draw_way.js` `move()` function click() { + var d = datum(); + var target = d && d.id && context.hasEntity(d.id); + var trySnap = geoViewportEdge(context.mouse(), context.map().dimensions()) === null; if (trySnap) { - // If we're not at the edge of the viewport, try to snap.. - // See also: `modes/drag_node.js doMove()` - var d = datum(); - - // Snap to a node - if (d.type === 'node') { - dispatch.call('clickNode', this, d); + if (target && target.type === 'node') { // Snap to a node + dispatch.call('clickNode', this, target); return; - // Snap to a way - } else if (d.type === 'way') { - var choice = geoChooseEdge(context.childNodes(d), context.mouse(), context.projection); - var edge = [d.nodes[choice.index - 1], d.nodes[choice.index]]; - dispatch.call('clickWay', this, choice.loc, edge); - return; + } else if (target && target.type === 'way') { // Snap to a way + var choice = geoChooseEdge( + context.childNodes(target), context.mouse(), context.projection, context.activeID() + ); + if (choice) { + var edge = [target.nodes[choice.index - 1], target.nodes[choice.index]]; + dispatch.call('clickWay', this, choice.loc, edge); + return; + } } } - dispatch.call('click', this, context.map().mouseCoordinates()); + dispatch.call('click', this, context.map().mouseCoordinates(), d); } diff --git a/modules/behavior/draw_way.js b/modules/behavior/draw_way.js index 163513b3c..c9a34c850 100644 --- a/modules/behavior/draw_way.js +++ b/modules/behavior/draw_way.js @@ -10,37 +10,22 @@ import { } from '../actions'; import { behaviorDraw } from './draw'; - -import { - geoChooseEdge, - geoEdgeEqual -} from '../geo'; - -import { - modeBrowse, - modeSelect -} from '../modes'; - -import { - osmNode, - osmWay -} from '../osm'; - -import { utilEntitySelector } from '../util'; +import { geoChooseEdge, geoEdgeEqual } from '../geo'; +import { modeBrowse, modeSelect } from '../modes'; +import { osmNode, osmWay } from '../osm'; export function behaviorDrawWay(context, wayId, index, mode, startGraph) { - var origWay = context.entity(wayId); var isArea = context.geometry(wayId) === 'area'; - var tempEdits = 0; var annotation = t((origWay.isDegenerate() ? 'operations.start.annotation.' : - 'operations.continue.annotation.') + context.geometry(wayId)); - var draw = behaviorDraw(context); - // var _activeIDs = []; - var _activeID; - var startIndex; + 'operations.continue.annotation.') + context.geometry(wayId) + ); + var behavior = behaviorDraw(context); + var _tempEdits = 0; + var _startIndex; + var start; var end; var segment; @@ -48,9 +33,15 @@ export function behaviorDrawWay(context, wayId, index, mode, startGraph) { // initialize the temporary drawing entities if (!isArea) { - startIndex = typeof index === 'undefined' ? origWay.nodes.length - 1 : 0; - start = osmNode({ id: 'nStart', loc: context.entity(origWay.nodes[startIndex]).loc }); - end = osmNode({ id: 'nEnd', loc: context.map().mouseCoordinates() }); + _startIndex = (typeof index === 'undefined' ? origWay.nodes.length - 1 : 0); + start = osmNode({ + id: 'nStart', + loc: context.entity(origWay.nodes[_startIndex]).loc + }); + end = osmNode({ + id: 'nEnd', + loc: context.map().mouseCoordinates() + }); segment = osmWay({ id: 'wTemp', nodes: typeof index === 'undefined' ? [start.id, end.id] : [end.id, start.id], @@ -63,21 +54,30 @@ export function behaviorDrawWay(context, wayId, index, mode, startGraph) { // Push an annotated state for undo to return back to. // We must make sure to remove this edit later. context.perform(actionNoop(), annotation); - tempEdits++; + _tempEdits++; // Add the temporary drawing entities to the graph. // We must make sure to remove this edit later. context.perform(AddDrawEntities()); - tempEdits++; + _tempEdits++; + // related code + // - `mode/drag_node.js` `doMode()` + // - `behavior/draw.js` `click()` + // - `behavior/draw_way.js` `move()` function move(datum) { var loc; - if (datum.type === 'node' && datum.id !== end.id) { + var target = datum && datum.id && context.hasEntity(datum.id); + if (target && target.type === 'node') { // snap to node loc = datum.loc; - - } else if (datum.type === 'way') { - loc = geoChooseEdge(context.childNodes(datum), context.mouse(), context.projection).loc; + } else if (target && target.type === 'way') { // snap to way + var choice = geoChooseEdge( + context.childNodes(target), context.mouse(), context.projection, end.id + ); + if (choice) { + loc = choice.loc; + } } if (!loc) { @@ -93,7 +93,7 @@ export function behaviorDrawWay(context, wayId, index, mode, startGraph) { // Undo popped the history back to the initial annotated no-op edit. // Remove initial no-op edit and whatever edit happened immediately before it. context.pop(2); - tempEdits = 0; + _tempEdits = 0; if (context.hasEntity(wayId)) { context.enter(mode); @@ -104,17 +104,14 @@ export function behaviorDrawWay(context, wayId, index, mode, startGraph) { function setActiveElements() { - // _activeIDs = isArea ? [wayId, end.id] : [segment.id, start.id, end.id]; - // context.surface().selectAll(utilEntitySelector(_activeIDs)) - // .classed('active', true); - _activeID = end.id; context.surface().selectAll('.' + end.id) .classed('active', true); } var drawWay = function(surface) { - draw.on('move', move) + behavior + .on('move', move) .on('click', drawWay.add) .on('clickWay', drawWay.addWay) .on('clickNode', drawWay.addNode) @@ -128,7 +125,7 @@ export function behaviorDrawWay(context, wayId, index, mode, startGraph) { setActiveElements(); - surface.call(draw); + surface.call(behavior); context.history() .on('undone.draw', undone); @@ -139,8 +136,8 @@ export function behaviorDrawWay(context, wayId, index, mode, startGraph) { // Drawing was interrupted unexpectedly. // This can happen if the user changes modes, // clicks geolocate button, a hashchange event occurs, etc. - if (tempEdits) { - context.pop(tempEdits); + if (_tempEdits) { + context.pop(_tempEdits); while (context.graph() !== startGraph) { context.pop(); } @@ -149,7 +146,7 @@ export function behaviorDrawWay(context, wayId, index, mode, startGraph) { context.map() .on('drawn.draw', null); - surface.call(draw.off) + surface.call(behavior.off) .selectAll('.active') .classed('active', false); @@ -201,12 +198,18 @@ export function behaviorDrawWay(context, wayId, index, mode, startGraph) { // Accept the current position of the temporary node and continue drawing. - drawWay.add = function(loc) { + drawWay.add = function(loc, datum) { +// shouldn't happen now? // prevent duplicate nodes - var last = context.hasEntity(origWay.nodes[origWay.nodes.length - (isArea ? 2 : 1)]); - if (last && last.loc[0] === loc[0] && last.loc[1] === loc[1]) return; + // var last = context.hasEntity(origWay.nodes[origWay.nodes.length - (isArea ? 2 : 1)]); + // if (last && last.loc[0] === loc[0] && last.loc[1] === loc[1]) return; - context.pop(tempEdits); + if (datum && datum.id && /-nope/.test(datum.id)) { // can't click here + return; + } + + context.pop(_tempEdits); + _tempEdits = 0; if (isArea) { context.perform( @@ -222,31 +225,30 @@ export function behaviorDrawWay(context, wayId, index, mode, startGraph) { ); } - tempEdits = 0; context.enter(mode); }; // Connect the way to an existing way. drawWay.addWay = function(loc, edge) { - if (isArea) { - context.pop(tempEdits); + context.pop(_tempEdits); + _tempEdits = 0; + if (isArea) { context.perform( AddDrawEntities(), actionAddMidpoint({ loc: loc, edge: edge}, end), annotation ); } else { - var previousEdge = startIndex ? - [origWay.nodes[startIndex], origWay.nodes[startIndex - 1]] : - [origWay.nodes[0], origWay.nodes[1]]; +// shouldn't happen now? + // var previousEdge = _startIndex ? + // [origWay.nodes[_startIndex], origWay.nodes[_startIndex - 1]] : + // [origWay.nodes[0], origWay.nodes[1]]; - // Avoid creating duplicate segments - if (geoEdgeEqual(edge, previousEdge)) - return; - - context.pop(tempEdits); + // // Avoid creating duplicate segments + // if (geoEdgeEqual(edge, previousEdge)) + // return; var newNode = osmNode({ loc: loc }); context.perform( @@ -256,7 +258,6 @@ export function behaviorDrawWay(context, wayId, index, mode, startGraph) { ); } - tempEdits = 0; context.enter(mode); }; @@ -264,23 +265,25 @@ export function behaviorDrawWay(context, wayId, index, mode, startGraph) { // Connect the way to an existing node and continue drawing. drawWay.addNode = function(node) { // Avoid creating duplicate segments - if (origWay.areAdjacent(node.id, origWay.nodes[origWay.nodes.length - 1])) return; +// shouldn't happen now? + // if (origWay.areAdjacent(node.id, origWay.nodes[origWay.nodes.length - 1])) return; // Clicks should not occur on the drawing node, however a space keypress can // sometimes grab that node's datum (before it gets classed as `active`?) #4016 - if (node.id === end.id) { - drawWay.add(node.loc); - return; - } +// shouldn't happen now? + // if (node.id === end.id) { + // drawWay.add(node.loc); + // return; + // } - context.pop(tempEdits); + context.pop(_tempEdits); + _tempEdits = 0; context.perform( ReplaceDrawEntities(node), annotation ); - tempEdits = 0; context.enter(mode); }; @@ -289,8 +292,8 @@ export function behaviorDrawWay(context, wayId, index, mode, startGraph) { // If the way has enough nodes to be valid, it's selected. // Otherwise, delete everything and return to browse mode. drawWay.finish = function() { - context.pop(tempEdits); - tempEdits = 0; + context.pop(_tempEdits); + _tempEdits = 0; var way = context.hasEntity(wayId); if (!way || way.isDegenerate()) { @@ -308,8 +311,8 @@ export function behaviorDrawWay(context, wayId, index, mode, startGraph) { // Cancel the draw operation, delete everything, and return to browse mode. drawWay.cancel = function() { - context.pop(tempEdits); - tempEdits = 0; + context.pop(_tempEdits); + _tempEdits = 0; while (context.graph() !== startGraph) { context.pop(); @@ -323,20 +326,15 @@ export function behaviorDrawWay(context, wayId, index, mode, startGraph) { }; - // drawWay.activeIDs = function() { - // if (!arguments.length) return _activeIDs; - // // no assign - // return drawWay; - // }; drawWay.activeID = function() { - if (!arguments.length) return _activeID; + if (!arguments.length) return end.id; // no assign return drawWay; }; drawWay.tail = function(text) { - draw.tail(text); + behavior.tail(text); return drawWay; }; diff --git a/modules/core/context.js b/modules/core/context.js index dcf0b6412..1f606d5fe 100644 --- a/modules/core/context.js +++ b/modules/core/context.js @@ -255,13 +255,6 @@ export function coreContext() { return []; } }; - // context.activeIDs = function() { - // if (mode && mode.activeIDs) { - // return mode.activeIDs(); - // } else { - // return []; - // } - // }; context.activeID = function() { return mode && mode.activeID && mode.activeID(); }; diff --git a/modules/modes/drag_node.js b/modules/modes/drag_node.js index 2446ffbfa..4e905f98d 100644 --- a/modules/modes/drag_node.js +++ b/modules/modes/drag_node.js @@ -12,25 +12,10 @@ import { actionNoop } from '../actions'; -import { - behaviorEdit, - behaviorHover, - behaviorDrag -} from '../behavior'; - -import { - modeBrowse, - modeSelect -} from './index'; - -import { - geoChooseEdge, - geoVecSubtract, - geoViewportEdge -} from '../geo'; - +import { behaviorEdit, behaviorHover, behaviorDrag } from '../behavior'; +import { geoChooseEdge, geoVecSubtract, geoViewportEdge } from '../geo'; +import { modeBrowse, modeSelect } from './index'; import { osmNode } from '../osm'; -import { utilEntitySelector } from '../util'; import { uiFlash } from '../ui'; @@ -39,16 +24,15 @@ export function modeDragNode(context) { id: 'drag-node', button: 'browse' }; - var hover = behaviorHover(context).altDisables(true).on('hover', context.ui().sidebar.hover); + var hover = behaviorHover(context).altDisables(true) + .on('hover', context.ui().sidebar.hover); var edit = behaviorEdit(context); var _nudgeInterval; var _restoreSelectedIDs = []; - // var _activeIDs = []; - var _activeID; var _wasMidpoint = false; var _isCancelled = false; - var _dragEntity; + var _activeEntity; var _lastLoc; @@ -94,7 +78,7 @@ export function modeDragNode(context) { if (hasHidden) { uiFlash().text(t('modes.drag_node.connected_to_hidden'))(); } - return behavior.cancel(); + return drag.cancel(); } if (_wasMidpoint) { @@ -103,20 +87,15 @@ export function modeDragNode(context) { context.perform(actionAddMidpoint(midpoint, entity)); var vertex = context.surface().selectAll('.' + entity.id); - behavior.target(vertex.node(), entity); + drag.target(vertex.node(), entity); } else { context.perform(actionNoop()); } - _dragEntity = entity; - - // `.active` elements have `pointer-events: none`. - // This prevents the node or vertex being dragged from trying to connect to itself. - // _activeIDs = context.graph().parentWays(entity).map(function(parent) { return parent.id; }); - // _activeIDs.push(entity.id); - _activeID = entity.id; - setActiveElements(); + _activeEntity = entity; + context.surface().selectAll('.' + _activeEntity.id) + .classed('active', true); context.enter(mode); } @@ -127,8 +106,7 @@ export function modeDragNode(context) { if (!event || event.altKey || !d3_select(event.target).classed('target')) { return {}; } else { - var d = event.target.__data__; - return (d && d.id && context.hasEntity(d.id)) || {}; + return event.target.__data__ || {}; } } @@ -140,20 +118,21 @@ export function modeDragNode(context) { var currMouse = geoVecSubtract(currPoint, nudge); var loc = context.projection.invert(currMouse); - if (!_nudgeInterval) { - // If we're not nudging at the edge of the viewport, try to snap.. - // See also `behavior/draw.js click()` + if (!_nudgeInterval) { // If not nudging at the edge of the viewport, try to snap.. + // related code + // - `mode/drag_node.js` `doMode()` + // - `behavior/draw.js` `click()` + // - `behavior/draw_way.js` `move()` var d = datum(); + var target = d && d.id && context.hasEntity(d.id); - // Snap to a node (not self) - if (d.type === 'node' && d.id !== entity.id) { - loc = d.loc; - - // Snap to a way - } else if (d.type === 'way') { - var choice = geoChooseEdge(context.childNodes(d), context.mouse(), context.projection); - // (not along a segment adjacent to self) - if (entity.id !== d.nodes[choice.index - 1] && entity.id !== d.nodes[choice.index]) { + if (target && target.type === 'node') { + loc = target.loc; + } else if (target && target.type === 'way') { + var choice = geoChooseEdge( + context.childNodes(target), context.mouse(), context.projection, entity.id + ); + if (choice) { loc = choice.loc; } } @@ -188,18 +167,22 @@ export function modeDragNode(context) { if (_isCancelled) return; var d = datum(); + var target = d && d.id && context.hasEntity(d.id); - if (d.type === 'way') { - var choice = geoChooseEdge(context.childNodes(d), context.mouse(), context.projection); + if (target && target.type === 'way') { + var choice = geoChooseEdge(context.childNodes(target), context.mouse(), context.projection, entity.id); context.replace( - actionAddMidpoint({ loc: choice.loc, edge: [d.nodes[choice.index - 1], d.nodes[choice.index]] }, entity), - connectAnnotation(d) + actionAddMidpoint({ + loc: choice.loc, + edge: [target.nodes[choice.index - 1], target.nodes[choice.index]] + }, entity), + connectAnnotation(target) ); - } else if (d.type === 'node' && d.id !== entity.id) { + } else if (target && target.type === 'node') { context.replace( - actionConnect([d.id, entity.id]), - connectAnnotation(d) + actionConnect([target.id, entity.id]), + connectAnnotation(target) ); } else if (_wasMidpoint) { @@ -228,20 +211,12 @@ export function modeDragNode(context) { function cancel() { - behavior.cancel(); + drag.cancel(); context.enter(modeBrowse(context)); } - function setActiveElements() { - // context.surface().selectAll(utilEntitySelector(_activeIDs)) - // .classed('active', true); - context.surface().selectAll('.' + _activeID) - .classed('active', true); - } - - - var behavior = behaviorDrag() + var drag = behaviorDrag() .selector('.layer-points-targets .target') .surface(d3_select('#map').node()) .origin(origin) @@ -256,11 +231,6 @@ export function modeDragNode(context) { context.history() .on('undone.drag-node', cancel); - - context.map() - .on('drawn.drag-node', setActiveElements); - - setActiveElements(); }; @@ -275,8 +245,8 @@ export function modeDragNode(context) { context.map() .on('drawn.drag-node', null); - // _activeIDs = []; - _activeID = null; + _activeEntity = null; + context.surface() .selectAll('.active') .classed('active', false); @@ -286,19 +256,14 @@ export function modeDragNode(context) { mode.selectedIDs = function() { - if (!arguments.length) return _dragEntity ? [_dragEntity.id] : []; + if (!arguments.length) return _activeEntity ? [_activeEntity.id] : []; // no assign return mode; }; - // mode.activeIDs = function() { - // if (!arguments.length) return _activeIDs; - // // no assign - // return mode; - // }; mode.activeID = function() { - if (!arguments.length) return _activeID; + if (!arguments.length) return _activeEntity && _activeEntity.id; // no assign return mode; }; @@ -311,7 +276,7 @@ export function modeDragNode(context) { }; - mode.behavior = behavior; + mode.behavior = drag; return mode; diff --git a/modules/modes/draw_area.js b/modules/modes/draw_area.js index 0dd5b1272..0890e3592 100644 --- a/modules/modes/draw_area.js +++ b/modules/modes/draw_area.js @@ -43,9 +43,7 @@ export function modeDrawArea(context, wayId, startGraph) { return [wayId]; }; - // mode.activeIDs = function() { - // return (behavior && behavior.activeIDs()) || []; - // }; + mode.activeID = function() { return (behavior && behavior.activeID()) || []; }; diff --git a/modules/modes/draw_line.js b/modules/modes/draw_line.js index 3e638cbd9..ca567601e 100644 --- a/modules/modes/draw_line.js +++ b/modules/modes/draw_line.js @@ -41,9 +41,7 @@ export function modeDrawLine(context, wayId, startGraph, affix) { return [wayId]; }; - // mode.activeIDs = function() { - // return (behavior && behavior.activeIDs()) || []; - // }; + mode.activeID = function() { return (behavior && behavior.activeID()) || []; }; diff --git a/modules/svg/points.js b/modules/svg/points.js index ef1d504d9..0b3366ab3 100644 --- a/modules/svg/points.js +++ b/modules/svg/points.js @@ -31,7 +31,6 @@ export function svgPoints(projection, context) { var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor '; var passive = entities.filter(function(d) { return d.id !== context.activeID(); - // return context.activeIDs().indexOf(d.id) === -1; }); var targets = selection.selectAll('.point.target')