mirror of
https://github.com/FoggedLens/iD.git
synced 2026-02-12 16:52:50 +00:00
Extract viewport nudging code from several places to geoViewportEdge
This commit is contained in:
@@ -14,7 +14,8 @@ import { behaviorTail } from './tail';
|
||||
|
||||
import {
|
||||
geoChooseEdge,
|
||||
geoEuclideanDistance
|
||||
geoEuclideanDistance,
|
||||
geoViewportEdge
|
||||
} from '../geo';
|
||||
|
||||
import { utilRebind } from '../util/rebind';
|
||||
@@ -115,31 +116,28 @@ export function behaviorDraw(context) {
|
||||
|
||||
|
||||
function click() {
|
||||
var d = datum();
|
||||
var trySnap = geoViewportEdge(context.mouse, context.map().dimensions()) !== null;
|
||||
|
||||
// Try to snap..
|
||||
// See also: `modes/drag_node.js doMove()`
|
||||
if (d.type === 'way') {
|
||||
var dims = context.map().dimensions();
|
||||
var mouse = context.mouse();
|
||||
var pad = 5;
|
||||
var trySnap = mouse[0] > pad && mouse[0] < dims[0] - pad &&
|
||||
mouse[1] > pad && mouse[1] < dims[1] - pad;
|
||||
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();
|
||||
|
||||
if (trySnap) {
|
||||
// Snap to a node
|
||||
if (d.type === 'node') {
|
||||
dispatch.call('clickNode', this, d);
|
||||
return;
|
||||
|
||||
// Snap to a way (not an area fill)
|
||||
} else if (d.type === 'way' && !d3_select(d3_event.sourceEvent.target).classed('fill')) {
|
||||
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);
|
||||
} else {
|
||||
dispatch.call('click', this, context.map().mouseCoordinates());
|
||||
return;
|
||||
}
|
||||
|
||||
} else if (d.type === 'node') {
|
||||
dispatch.call('clickNode', this, d);
|
||||
|
||||
} else {
|
||||
dispatch.call('click', this, context.map().mouseCoordinates());
|
||||
}
|
||||
|
||||
dispatch.call('click', this, context.map().mouseCoordinates());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -275,3 +275,27 @@ export function geoPathLength(path) {
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
|
||||
// If the given point is at the edge of the padded viewport,
|
||||
// return a vector that will nudge the viewport in that direction
|
||||
export function geoViewportEdge(point, dimensions) {
|
||||
var pad = [80, 20, 50, 20]; // top, right, bottom, left
|
||||
var x = 0;
|
||||
var y = 0;
|
||||
|
||||
if (point[0] > dimensions[0] - pad[1])
|
||||
x = -10;
|
||||
if (point[0] < pad[3])
|
||||
x = 10;
|
||||
if (point[1] > dimensions[1] - pad[2])
|
||||
y = -10;
|
||||
if (point[1] < pad[0])
|
||||
y = 10;
|
||||
|
||||
if (x || y) {
|
||||
return [x, y];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,3 +21,4 @@ export { geoPointInPolygon } from './geo.js';
|
||||
export { geoPolygonContainsPolygon } from './geo.js';
|
||||
export { geoPolygonIntersectsPolygon } from './geo.js';
|
||||
export { geoSphericalDistance } from './geo.js';
|
||||
export { geoViewportEdge } from './geo.js';
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
modeSelect
|
||||
} from './index';
|
||||
|
||||
import { geoChooseEdge } from '../geo';
|
||||
import { geoChooseEdge, geoViewportEdge } from '../geo';
|
||||
import { osmNode } from '../osm';
|
||||
import { utilEntitySelector } from '../util';
|
||||
import { uiFlash } from '../ui';
|
||||
@@ -50,27 +50,6 @@ export function modeDragNode(context) {
|
||||
return [a[0] - b[0], a[1] - b[1]];
|
||||
}
|
||||
|
||||
function edge(point, size) {
|
||||
var pad = [80, 20, 50, 20]; // top, right, bottom, left
|
||||
var x = 0;
|
||||
var y = 0;
|
||||
|
||||
if (point[0] > size[0] - pad[1])
|
||||
x = -10;
|
||||
if (point[0] < pad[3])
|
||||
x = 10;
|
||||
if (point[1] > size[1] - pad[2])
|
||||
y = -10;
|
||||
if (point[1] < pad[0])
|
||||
y = 10;
|
||||
|
||||
if (x || y) {
|
||||
return [x, y];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function startNudge(entity, nudge) {
|
||||
if (_nudgeInterval) window.clearInterval(_nudgeInterval);
|
||||
@@ -197,7 +176,7 @@ export function modeDragNode(context) {
|
||||
_lastLoc = context.projection.invert(d3_event.point);
|
||||
|
||||
doMove(entity);
|
||||
var nudge = edge(d3_event.point, context.map().dimensions());
|
||||
var nudge = geoViewportEdge(d3_event.point, context.map().dimensions());
|
||||
if (nudge) {
|
||||
startNudge(entity, nudge);
|
||||
} else {
|
||||
|
||||
@@ -8,6 +8,7 @@ import { t } from '../util/locale';
|
||||
|
||||
import { actionMove } from '../actions';
|
||||
import { behaviorEdit } from '../behavior';
|
||||
import { geoViewportEdge } from '../geo';
|
||||
|
||||
import {
|
||||
modeBrowse,
|
||||
@@ -30,23 +31,24 @@ export function modeMove(context, entityIDs, baseGraph) {
|
||||
button: 'browse'
|
||||
};
|
||||
|
||||
var keybinding = d3_keybinding('move'),
|
||||
behaviors = [
|
||||
behaviorEdit(context),
|
||||
operationCircularize(entityIDs, context).behavior,
|
||||
operationDelete(entityIDs, context).behavior,
|
||||
operationOrthogonalize(entityIDs, context).behavior,
|
||||
operationReflectLong(entityIDs, context).behavior,
|
||||
operationReflectShort(entityIDs, context).behavior,
|
||||
operationRotate(entityIDs, context).behavior
|
||||
],
|
||||
annotation = entityIDs.length === 1 ?
|
||||
t('operations.move.annotation.' + context.geometry(entityIDs[0])) :
|
||||
t('operations.move.annotation.multiple'),
|
||||
prevGraph,
|
||||
cache,
|
||||
origin,
|
||||
nudgeInterval;
|
||||
var keybinding = d3_keybinding('move');
|
||||
var behaviors = [
|
||||
behaviorEdit(context),
|
||||
operationCircularize(entityIDs, context).behavior,
|
||||
operationDelete(entityIDs, context).behavior,
|
||||
operationOrthogonalize(entityIDs, context).behavior,
|
||||
operationReflectLong(entityIDs, context).behavior,
|
||||
operationReflectShort(entityIDs, context).behavior,
|
||||
operationRotate(entityIDs, context).behavior
|
||||
];
|
||||
var annotation = entityIDs.length === 1 ?
|
||||
t('operations.move.annotation.' + context.geometry(entityIDs[0])) :
|
||||
t('operations.move.annotation.multiple');
|
||||
|
||||
var _prevGraph;
|
||||
var _cache;
|
||||
var _origin;
|
||||
var _nudgeInterval;
|
||||
|
||||
|
||||
function vecSub(a, b) {
|
||||
@@ -54,52 +56,30 @@ export function modeMove(context, entityIDs, baseGraph) {
|
||||
}
|
||||
|
||||
|
||||
function edge(point, size) {
|
||||
var pad = [80, 20, 50, 20], // top, right, bottom, left
|
||||
x = 0,
|
||||
y = 0;
|
||||
|
||||
if (point[0] > size[0] - pad[1])
|
||||
x = -10;
|
||||
if (point[0] < pad[3])
|
||||
x = 10;
|
||||
if (point[1] > size[1] - pad[2])
|
||||
y = -10;
|
||||
if (point[1] < pad[0])
|
||||
y = 10;
|
||||
|
||||
if (x || y) {
|
||||
return [x, y];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function doMove(nudge) {
|
||||
nudge = nudge || [0, 0];
|
||||
|
||||
var fn;
|
||||
if (prevGraph !== context.graph()) {
|
||||
cache = {};
|
||||
origin = context.map().mouseCoordinates();
|
||||
if (_prevGraph !== context.graph()) {
|
||||
_cache = {};
|
||||
_origin = context.map().mouseCoordinates();
|
||||
fn = context.perform;
|
||||
} else {
|
||||
fn = context.overwrite;
|
||||
}
|
||||
|
||||
var currMouse = context.mouse(),
|
||||
origMouse = context.projection(origin),
|
||||
delta = vecSub(vecSub(currMouse, origMouse), nudge);
|
||||
var currMouse = context.mouse();
|
||||
var origMouse = context.projection(_origin);
|
||||
var delta = vecSub(vecSub(currMouse, origMouse), nudge);
|
||||
|
||||
fn(actionMove(entityIDs, delta, context.projection, cache), annotation);
|
||||
prevGraph = context.graph();
|
||||
fn(actionMove(entityIDs, delta, context.projection, _cache), annotation);
|
||||
_prevGraph = context.graph();
|
||||
}
|
||||
|
||||
|
||||
function startNudge(nudge) {
|
||||
if (nudgeInterval) window.clearInterval(nudgeInterval);
|
||||
nudgeInterval = window.setInterval(function() {
|
||||
if (_nudgeInterval) window.clearInterval(_nudgeInterval);
|
||||
_nudgeInterval = window.setInterval(function() {
|
||||
context.pan(nudge);
|
||||
doMove(nudge);
|
||||
}, 50);
|
||||
@@ -107,16 +87,16 @@ export function modeMove(context, entityIDs, baseGraph) {
|
||||
|
||||
|
||||
function stopNudge() {
|
||||
if (nudgeInterval) {
|
||||
window.clearInterval(nudgeInterval);
|
||||
nudgeInterval = null;
|
||||
if (_nudgeInterval) {
|
||||
window.clearInterval(_nudgeInterval);
|
||||
_nudgeInterval = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function move() {
|
||||
doMove();
|
||||
var nudge = edge(context.mouse(), context.map().dimensions());
|
||||
var nudge = geoViewportEdge(context.mouse(), context.map().dimensions());
|
||||
if (nudge) {
|
||||
startNudge(nudge);
|
||||
} else {
|
||||
@@ -150,9 +130,9 @@ export function modeMove(context, entityIDs, baseGraph) {
|
||||
|
||||
|
||||
mode.enter = function() {
|
||||
origin = context.map().mouseCoordinates();
|
||||
prevGraph = null;
|
||||
cache = {};
|
||||
_origin = context.map().mouseCoordinates();
|
||||
_prevGraph = null;
|
||||
_cache = {};
|
||||
|
||||
behaviors.forEach(function(behavior) {
|
||||
context.install(behavior);
|
||||
|
||||
@@ -38,66 +38,67 @@ export function modeRotate(context, entityIDs) {
|
||||
button: 'browse'
|
||||
};
|
||||
|
||||
var keybinding = d3_keybinding('rotate'),
|
||||
behaviors = [
|
||||
behaviorEdit(context),
|
||||
operationCircularize(entityIDs, context).behavior,
|
||||
operationDelete(entityIDs, context).behavior,
|
||||
operationMove(entityIDs, context).behavior,
|
||||
operationOrthogonalize(entityIDs, context).behavior,
|
||||
operationReflectLong(entityIDs, context).behavior,
|
||||
operationReflectShort(entityIDs, context).behavior
|
||||
],
|
||||
annotation = entityIDs.length === 1 ?
|
||||
t('operations.rotate.annotation.' + context.geometry(entityIDs[0])) :
|
||||
t('operations.rotate.annotation.multiple'),
|
||||
prevGraph,
|
||||
prevAngle,
|
||||
prevTransform,
|
||||
pivot;
|
||||
var keybinding = d3_keybinding('rotate');
|
||||
var behaviors = [
|
||||
behaviorEdit(context),
|
||||
operationCircularize(entityIDs, context).behavior,
|
||||
operationDelete(entityIDs, context).behavior,
|
||||
operationMove(entityIDs, context).behavior,
|
||||
operationOrthogonalize(entityIDs, context).behavior,
|
||||
operationReflectLong(entityIDs, context).behavior,
|
||||
operationReflectShort(entityIDs, context).behavior
|
||||
];
|
||||
var annotation = entityIDs.length === 1 ?
|
||||
t('operations.rotate.annotation.' + context.geometry(entityIDs[0])) :
|
||||
t('operations.rotate.annotation.multiple');
|
||||
|
||||
var _prevGraph;
|
||||
var _prevAngle;
|
||||
var _prevTransform;
|
||||
var _pivot;
|
||||
|
||||
|
||||
function doRotate() {
|
||||
var fn;
|
||||
if (context.graph() !== prevGraph) {
|
||||
if (context.graph() !== _prevGraph) {
|
||||
fn = context.perform;
|
||||
} else {
|
||||
fn = context.replace;
|
||||
}
|
||||
|
||||
// projection changed, recalculate pivot
|
||||
// projection changed, recalculate _pivot
|
||||
var projection = context.projection;
|
||||
var currTransform = projection.transform();
|
||||
if (!prevTransform ||
|
||||
currTransform.k !== prevTransform.k ||
|
||||
currTransform.x !== prevTransform.x ||
|
||||
currTransform.y !== prevTransform.y) {
|
||||
if (!_prevTransform ||
|
||||
currTransform.k !== _prevTransform.k ||
|
||||
currTransform.x !== _prevTransform.x ||
|
||||
currTransform.y !== _prevTransform.y) {
|
||||
|
||||
var nodes = utilGetAllNodes(entityIDs, context.graph()),
|
||||
points = nodes.map(function(n) { return projection(n.loc); });
|
||||
var nodes = utilGetAllNodes(entityIDs, context.graph());
|
||||
var points = nodes.map(function(n) { return projection(n.loc); });
|
||||
|
||||
if (points.length === 1) { // degenerate case
|
||||
pivot = points[0];
|
||||
_pivot = points[0];
|
||||
} else if (points.length === 2) {
|
||||
pivot = geoInterp(points[0], points[1], 0.5);
|
||||
_pivot = geoInterp(points[0], points[1], 0.5);
|
||||
} else {
|
||||
pivot = d3_polygonCentroid(d3_polygonHull(points));
|
||||
_pivot = d3_polygonCentroid(d3_polygonHull(points));
|
||||
}
|
||||
prevAngle = undefined;
|
||||
_prevAngle = undefined;
|
||||
}
|
||||
|
||||
|
||||
var currMouse = context.mouse(),
|
||||
currAngle = Math.atan2(currMouse[1] - pivot[1], currMouse[0] - pivot[0]);
|
||||
var currMouse = context.mouse();
|
||||
var currAngle = Math.atan2(currMouse[1] - _pivot[1], currMouse[0] - _pivot[0]);
|
||||
|
||||
if (typeof prevAngle === 'undefined') prevAngle = currAngle;
|
||||
var delta = currAngle - prevAngle;
|
||||
if (typeof _prevAngle === 'undefined') _prevAngle = currAngle;
|
||||
var delta = currAngle - _prevAngle;
|
||||
|
||||
fn(actionRotate(entityIDs, pivot, delta, projection), annotation);
|
||||
fn(actionRotate(entityIDs, _pivot, delta, projection), annotation);
|
||||
|
||||
prevTransform = currTransform;
|
||||
prevAngle = currAngle;
|
||||
prevGraph = context.graph();
|
||||
_prevTransform = currTransform;
|
||||
_prevAngle = currAngle;
|
||||
_prevGraph = context.graph();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -411,4 +411,36 @@ describe('iD.geo', function() {
|
||||
expect(iD.geoPathLength(path)).to.eql(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('geoViewportEdge', function() {
|
||||
var dimensions = [1000, 1000];
|
||||
it('returns null if the point is not at the edge', function() {
|
||||
expect(iD.geoViewportEdge([500, 500], dimensions)).to.be.null;
|
||||
});
|
||||
it('nudges top edge', function() {
|
||||
expect(iD.geoViewportEdge([500, 5], dimensions)).to.eql([0, 10]);
|
||||
});
|
||||
it('nudges top-right corner', function() {
|
||||
expect(iD.geoViewportEdge([995, 5], dimensions)).to.eql([-10, 10]);
|
||||
});
|
||||
it('nudges right edge', function() {
|
||||
expect(iD.geoViewportEdge([995, 500], dimensions)).to.eql([-10, 0]);
|
||||
});
|
||||
it('nudges bottom-right corner', function() {
|
||||
expect(iD.geoViewportEdge([995, 995], dimensions)).to.eql([-10, -10]);
|
||||
});
|
||||
it('nudges bottom edge', function() {
|
||||
expect(iD.geoViewportEdge([500, 995], dimensions)).to.eql([0, -10]);
|
||||
});
|
||||
it('nudges bottom-left corner', function() {
|
||||
expect(iD.geoViewportEdge([5, 995], dimensions)).to.eql([10, -10]);
|
||||
});
|
||||
it('nudges left edge', function() {
|
||||
expect(iD.geoViewportEdge([5, 500], dimensions)).to.eql([10, 0]);
|
||||
});
|
||||
it('nudges top-left corner', function() {
|
||||
expect(iD.geoViewportEdge([5, 5], dimensions)).to.eql([10, 10]);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user