diff --git a/css/20_map.css b/css/20_map.css index 3b7225dd2..0c0905799 100644 --- a/css/20_map.css +++ b/css/20_map.css @@ -16,7 +16,6 @@ /* `.target` objects are interactive */ /* They can be picked up, clicked, hovered, or things can connect to them */ -.layer-blocker.target, .node.target { pointer-events: fill; fill-opacity: 0.8; @@ -35,12 +34,12 @@ } /* `.target-nope` objects are explicitly forbidden to join to */ -.layer-blocker.target.target-nope, .node.target.target-nope, .way.target.target-nope { cursor: not-allowed; } + /* `.active` objects (currently being drawn or dragged) are not interactive */ /* This is important to allow the events to drop through to whatever is */ /* below them on the map, so you can still hover and connect to other things. */ diff --git a/css/55_cursors.css b/css/55_cursors.css index 9e72d7ddf..0e4732b58 100644 --- a/css/55_cursors.css +++ b/css/55_cursors.css @@ -1,5 +1,9 @@ /* Cursors */ +.nope { + cursor: not-allowed !important; +} + .map-in-map, #map { cursor: auto; /* Opera */ diff --git a/modules/modes/drag_node.js b/modules/modes/drag_node.js index 3759a1c3d..d165949d5 100644 --- a/modules/modes/drag_node.js +++ b/modules/modes/drag_node.js @@ -28,7 +28,6 @@ import { import { modeBrowse, modeSelect } from './index'; import { osmNode } from '../osm'; -import { svgBlocker } from '../svg'; import { uiFlash } from '../ui'; @@ -40,7 +39,6 @@ export function modeDragNode(context) { var hover = behaviorHover(context).altDisables(true) .on('hover', context.ui().sidebar.hover); var edit = behaviorEdit(context); - var blocker = svgBlocker(context.projection, context); var _nudgeInterval; var _restoreSelectedIDs = []; @@ -135,6 +133,7 @@ export function modeDragNode(context) { var currPoint = (d3_event && d3_event.point) || context.projection(_lastLoc); var currMouse = geoVecSubtract(currPoint, nudge); var loc = context.projection.invert(currMouse); + var didSnap = false; if (!_nudgeInterval) { // If not nudging at the edge of the viewport, try to snap.. // related code @@ -142,16 +141,22 @@ export function modeDragNode(context) { // - `behavior/draw.js` `click()` // - `behavior/draw_way.js` `move()` var d = datum(); - var target = d && d.id && context.hasEntity(d.id); + var nodegroups = d && d.properties && d.properties.nodes; if (d.loc) { // snap to node/vertex - a real entity or a nope target with a `loc` loc = d.loc; - } else if (target && target.type === 'way') { // snap to way - var choice = geoChooseEdge( - context.childNodes(target), context.mouse(), context.projection, entity.id - ); - if (choice) { - loc = choice.loc; + didSnap = true; + + } else if (nodegroups) { // snap to way - a line touch target or nope target with nodes + var best = Infinity; + for (var i = 0; i < nodegroups.length; i++) { + var childNodes = nodegroups[i].map(function(id) { return context.entity(id); }); + var choice = geoChooseEdge(childNodes, context.mouse(), context.projection, entity.id); + if (choice && choice.distance < best) { + best = choice.distance; + loc = choice.loc; + didSnap = true; + } } } } @@ -162,17 +167,23 @@ export function modeDragNode(context) { ); - checkGeometry(entity); + // check if this movement causes the geometry to break + var doBlock = false; + if (!didSnap) { + doBlock = invalidGeometry(entity, context.graph()); + } + + context.surface() + .classed('nope', doBlock); + _lastLoc = loc; } - function checkGeometry(entity) { - var doBlock = false; - var graph = context.graph(); + function invalidGeometry(entity, graph) { var parents = graph.parentWays(entity); - function checkSelfIntersections(way, activeID) { + function hasSelfIntersections(way, activeID) { // check active (dragged) segments against inactive segments var actives = []; var inactives = []; @@ -206,15 +217,13 @@ export function modeDragNode(context) { for (var i = 0; i < parents.length; i++) { var parent = parents[i]; if (parent.isClosed()) { // check for self intersections - if (checkSelfIntersections(parent, entity.id)) { - doBlock = true; - break; + if (hasSelfIntersections(parent, entity.id)) { + return true; } } } - d3_select('.data-layer-osm') - .call(doBlock ? blocker : blocker.off); + return false; } @@ -238,7 +247,7 @@ export function modeDragNode(context) { if (_isCancelled) return; var d = datum(); - var nope = d && d.id && /-nope$/.test(d.id); // can't drag here + var nope = (d && d.id && /-nope$/.test(d.id)) || context.surface().classed('nope'); var target = d && d.id && context.hasEntity(d.id); // entity to snap to if (nope) { // bounce back @@ -337,10 +346,8 @@ export function modeDragNode(context) { _activeEntity = null; - d3_select('.data-layer-osm') - .call(blocker.off); - context.surface() + .classed('nope', false) .selectAll('.active') .classed('active', false); diff --git a/modules/svg/blocker.js b/modules/svg/blocker.js deleted file mode 100644 index 93a3a5f1c..000000000 --- a/modules/svg/blocker.js +++ /dev/null @@ -1,26 +0,0 @@ -export function svgBlocker(projection, context) { - - function blocker(selection) { - var dimensions = projection.clipExtent()[1]; - var fillClass = context.getDebug('target') ? 'red ' : 'nocolor '; - - var blocker = selection.selectAll('.layer-blocker') - .data([{id: 'target-nope'}]); - - blocker.enter() - .append('rect') - .attr('class', 'layer-blocker target target-nope ' + fillClass) - .attr('x', 0) - .attr('y', 0) - .merge(blocker) - .attr('width', dimensions[0]) - .attr('height', dimensions[1]); - } - - blocker.off = function(selection) { - selection.selectAll('.layer-blocker') - .remove(); - }; - - return blocker; -} diff --git a/modules/svg/helpers.js b/modules/svg/helpers.js index bbd22f11d..b4d00264e 100644 --- a/modules/svg/helpers.js +++ b/modules/svg/helpers.js @@ -180,14 +180,17 @@ export function svgRelationMemberTags(graph) { export function svgSegmentWay(way, graph, activeID) { var features = { passive: [], active: [] }; var coordGroups = { passive: [], active: [] }; - var segment = []; + var nodeGroups = { passive: [], active: [] }; + var coords = []; + var nodes = []; var startType = null; // 0 = active, 1 = passive, 2 = adjacent var currType = null; var node; for (var i = 0; i < way.nodes.length; i++) { if (way.nodes[i] === activeID) { // vertex is the activeID - segment = []; // draw no segment here + coords = []; // draw no segment here + nodes = []; startType = null; continue; } @@ -201,32 +204,41 @@ export function svgSegmentWay(way, graph, activeID) { if (currType !== startType) { // line changes here - try to save a segment - if (segment.length > 0) { // finish previous segment - segment.push(node.loc); + if (coords.length > 0) { // finish previous segment + coords.push(node.loc); + nodes.push(node.id); if (startType === 2 || currType === 2) { // one adjacent vertex - coordGroups.active.push(segment); + coordGroups.active.push(coords); + nodeGroups.active.push(nodes); } else if (startType === 0 && currType === 0) { // both active vertices - coordGroups.active.push(segment); + coordGroups.active.push(coords); + nodeGroups.active.push(nodes); } else { - coordGroups.passive.push(segment); + coordGroups.passive.push(coords); + nodeGroups.passive.push(nodes); } } - segment = []; + coords = []; + nodes = []; startType = currType; } - segment.push(node.loc); + coords.push(node.loc); + nodes.push(node.id); } // complete whatever segment we ended on - if (segment.length > 1) { + if (coords.length > 1) { if (startType === 2 || currType === 2) { // one adjacent vertex - coordGroups.active.push(segment); + coordGroups.active.push(coords); + nodeGroups.active.push(nodes); } else if (startType === 0 && currType === 0) { // both active vertices - coordGroups.active.push(segment); + coordGroups.active.push(coords); + nodeGroups.active.push(nodes); } else { - coordGroups.passive.push(segment); + coordGroups.passive.push(coords); + nodeGroups.passive.push(nodes); } } @@ -234,6 +246,9 @@ export function svgSegmentWay(way, graph, activeID) { features.passive.push({ 'type': 'Feature', 'id': way.id, + 'properties': { + 'nodes': nodeGroups.passive + }, 'geometry': { 'type': 'MultiLineString', 'coordinates': coordGroups.passive @@ -246,7 +261,8 @@ export function svgSegmentWay(way, graph, activeID) { 'type': 'Feature', 'id': way.id + '-nope', // break the ids on purpose 'properties': { - 'originalID': way.id + 'originalID': way.id, + 'nodes': nodeGroups.active }, 'geometry': { 'type': 'MultiLineString', diff --git a/modules/svg/index.js b/modules/svg/index.js index 798893dbb..68e2f2f28 100644 --- a/modules/svg/index.js +++ b/modules/svg/index.js @@ -1,5 +1,4 @@ export { svgAreas } from './areas.js'; -export { svgBlocker } from './blocker.js'; export { svgDebug } from './debug.js'; export { svgDefs } from './defs.js'; export { svgGpx } from './gpx.js';