import { select as d3_select } from 'd3-selection'; import { geoExtent, geoPointInPolygon } from '../geo'; import { modeSelect } from '../modes/select'; import { uiLasso } from '../ui/lasso'; import { utilArrayIntersection } from '../util/array'; import { utilGetAllNodes } from '../util/util'; export function behaviorLasso(context) { // use pointer events on supported platforms; fallback to mouse events var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; var behavior = function(selection) { var lasso; function pointerdown(d3_event) { var button = 0; // left if (d3_event.button === button && d3_event.shiftKey === true) { lasso = null; d3_select(window) .on(_pointerPrefix + 'move.lasso', pointermove) .on(_pointerPrefix + 'up.lasso', pointerup); d3_event.stopPropagation(); } } function pointermove() { if (!lasso) { lasso = uiLasso(context); context.surface().call(lasso); } lasso.p(context.map().mouse()); } function normalize(a, b) { return [ [Math.min(a[0], b[0]), Math.min(a[1], b[1])], [Math.max(a[0], b[0]), Math.max(a[1], b[1])] ]; } function lassoed() { if (!lasso) return []; var graph = context.graph(); var limitToNodes; if (context.map().editableDataEnabled(true /* skipZoomCheck */) && context.map().isInWideSelection()) { // only select from the visible nodes limitToNodes = new Set(utilGetAllNodes(context.selectedIDs(), graph)); } else if (!context.map().editableDataEnabled()) { return []; } var bounds = lasso.extent().map(context.projection.invert); var extent = geoExtent(normalize(bounds[0], bounds[1])); var intersects = context.history().intersects(extent).filter(function(entity) { return entity.type === 'node' && (!limitToNodes || limitToNodes.has(entity)) && geoPointInPolygon(context.projection(entity.loc), lasso.coordinates) && !context.features().isHidden(entity, graph, entity.geometry(graph)); }); // sort the lassoed nodes as best we can intersects.sort(function(node1, node2) { var parents1 = graph.parentWays(node1); var parents2 = graph.parentWays(node2); if (parents1.length && parents2.length) { // both nodes are vertices var sharedParents = utilArrayIntersection(parents1, parents2); if (sharedParents.length) { var sharedParentNodes = sharedParents[0].nodes; // vertices are members of the same way; sort them in their listed order return sharedParentNodes.indexOf(node1.id) - sharedParentNodes.indexOf(node2.id); } else { // vertices do not share a way; group them by their respective parent ways return Number(parents1[0].id.slice(1)) - Number(parents2[0].id.slice(1)); } } else if (parents1.length || parents2.length) { // only one node is a vertex; sort standalone points before vertices return parents1.length - parents2.length; } // both nodes are standalone points; sort left to right return node1.loc[0] - node2.loc[0]; }); return intersects.map(function(entity) { return entity.id; }); } function pointerup() { d3_select(window) .on(_pointerPrefix + 'move.lasso', null) .on(_pointerPrefix + 'up.lasso', null); if (!lasso) return; var ids = lassoed(); lasso.close(); if (ids.length) { context.enter(modeSelect(context, ids)); } } selection .on(_pointerPrefix + 'down.lasso', pointerdown); }; behavior.off = function(selection) { selection.on(_pointerPrefix + 'down.lasso', null); }; return behavior; }