mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-18 14:45:12 +02:00
Merge branch 'transitioned-actions'
This commit is contained in:
@@ -14,8 +14,16 @@ export function actionCircularize(wayId, projection, maxAngle) {
|
||||
maxAngle = (maxAngle || 20) * Math.PI / 180;
|
||||
|
||||
|
||||
var action = function(graph) {
|
||||
var way = graph.entity(wayId);
|
||||
var action = function(graph, t) {
|
||||
if (t === null || !isFinite(t)) t = 1;
|
||||
t = Math.min(Math.max(+t, 0), 1);
|
||||
|
||||
var way = graph.entity(wayId),
|
||||
origNodes = {};
|
||||
|
||||
graph.childNodes(way).forEach(function(node) {
|
||||
if (!origNodes[node.id]) origNodes[node.id] = node;
|
||||
});
|
||||
|
||||
if (!way.isConvex(graph)) {
|
||||
graph = action.makeConvex(graph);
|
||||
@@ -56,21 +64,27 @@ export function actionCircularize(wayId, projection, maxAngle) {
|
||||
endNodeIndex = nodes.indexOf(endNode),
|
||||
numberNewPoints = -1,
|
||||
indexRange = endNodeIndex - startNodeIndex,
|
||||
distance, totalAngle, eachAngle, startAngle, endAngle,
|
||||
angle, loc, node, j,
|
||||
inBetweenNodes = [];
|
||||
nearNodes = {},
|
||||
inBetweenNodes = [],
|
||||
startAngle, endAngle, totalAngle, eachAngle,
|
||||
angle, loc, node, origNode, j;
|
||||
|
||||
if (indexRange < 0) {
|
||||
indexRange += nodes.length;
|
||||
}
|
||||
|
||||
// position this key node
|
||||
distance = geoEuclideanDistance(centroid, keyPoints[i]);
|
||||
var distance = geoEuclideanDistance(centroid, keyPoints[i]);
|
||||
if (distance === 0) { distance = 1e-4; }
|
||||
keyPoints[i] = [
|
||||
centroid[0] + (keyPoints[i][0] - centroid[0]) / distance * radius,
|
||||
centroid[1] + (keyPoints[i][1] - centroid[1]) / distance * radius];
|
||||
graph = graph.replace(keyNodes[i].move(projection.invert(keyPoints[i])));
|
||||
centroid[1] + (keyPoints[i][1] - centroid[1]) / distance * radius
|
||||
];
|
||||
loc = projection.invert(keyPoints[i]);
|
||||
node = keyNodes[i];
|
||||
origNode = origNodes[node.id];
|
||||
node = node.move(geoInterp(origNode.loc, loc, t));
|
||||
graph = graph.replace(node);
|
||||
|
||||
// figure out the between delta angle we want to match to
|
||||
startAngle = Math.atan2(keyPoints[i][1] - centroid[1], keyPoints[i][0] - centroid[0]);
|
||||
@@ -87,14 +101,20 @@ export function actionCircularize(wayId, projection, maxAngle) {
|
||||
eachAngle = totalAngle / (indexRange + numberNewPoints);
|
||||
} while (Math.abs(eachAngle) > maxAngle);
|
||||
|
||||
// move existing points
|
||||
|
||||
// move existing nodes
|
||||
for (j = 1; j < indexRange; j++) {
|
||||
angle = startAngle + j * eachAngle;
|
||||
loc = projection.invert([
|
||||
centroid[0] + Math.cos(angle)*radius,
|
||||
centroid[1] + Math.sin(angle)*radius]);
|
||||
centroid[0] + Math.cos(angle) * radius,
|
||||
centroid[1] + Math.sin(angle) * radius
|
||||
]);
|
||||
|
||||
node = nodes[(j + startNodeIndex) % nodes.length].move(loc);
|
||||
node = nodes[(j + startNodeIndex) % nodes.length];
|
||||
origNode = origNodes[node.id];
|
||||
nearNodes[node.id] = angle;
|
||||
|
||||
node = node.move(geoInterp(origNode.loc, loc, t));
|
||||
graph = graph.replace(node);
|
||||
}
|
||||
|
||||
@@ -103,9 +123,21 @@ export function actionCircularize(wayId, projection, maxAngle) {
|
||||
angle = startAngle + (indexRange + j) * eachAngle;
|
||||
loc = projection.invert([
|
||||
centroid[0] + Math.cos(angle) * radius,
|
||||
centroid[1] + Math.sin(angle) * radius]);
|
||||
centroid[1] + Math.sin(angle) * radius
|
||||
]);
|
||||
|
||||
node = osmNode({loc: loc});
|
||||
// choose a nearnode to use as the original
|
||||
var min = Infinity;
|
||||
for (var nodeId in nearNodes) {
|
||||
var nearAngle = nearNodes[nodeId],
|
||||
dist = Math.abs(nearAngle - angle);
|
||||
if (dist < min) {
|
||||
dist = min;
|
||||
origNode = origNodes[nodeId];
|
||||
}
|
||||
}
|
||||
|
||||
node = osmNode({ loc: geoInterp(origNode.loc, loc, t) });
|
||||
graph = graph.replace(node);
|
||||
|
||||
nodes.splice(endNodeIndex + j, 0, node);
|
||||
@@ -195,5 +227,8 @@ export function actionCircularize(wayId, projection, maxAngle) {
|
||||
};
|
||||
|
||||
|
||||
action.transitionable = true;
|
||||
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import _ from 'lodash';
|
||||
import { actionDeleteNode } from './delete_node';
|
||||
import { geoEuclideanDistance } from '../geo/index';
|
||||
import { geoEuclideanDistance, geoInterp } from '../geo/index';
|
||||
|
||||
/*
|
||||
* Based on https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/potlatch2/tools/Quadrilateralise.as
|
||||
@@ -11,26 +11,30 @@ export function actionOrthogonalize(wayId, projection) {
|
||||
upperThreshold = Math.cos(threshold * Math.PI / 180);
|
||||
|
||||
|
||||
var action = function(graph) {
|
||||
var action = function(graph, t) {
|
||||
if (t === null || !isFinite(t)) t = 1;
|
||||
t = Math.min(Math.max(+t, 0), 1);
|
||||
|
||||
var way = graph.entity(wayId),
|
||||
nodes = graph.childNodes(way),
|
||||
points = _.uniq(nodes).map(function(n) { return projection(n.loc); }),
|
||||
corner = {i: 0, dotp: 1},
|
||||
epsilon = 1e-4,
|
||||
i, j, score, motions;
|
||||
node, loc, score, motions, i, j;
|
||||
|
||||
if (nodes.length === 4) {
|
||||
if (points.length === 3) { // move only one vertex for right triangle
|
||||
for (i = 0; i < 1000; i++) {
|
||||
motions = points.map(calcMotion);
|
||||
points[corner.i] = addPoints(points[corner.i],motions[corner.i]);
|
||||
points[corner.i] = addPoints(points[corner.i], motions[corner.i]);
|
||||
score = corner.dotp;
|
||||
if (score < epsilon) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
graph = graph.replace(graph.entity(nodes[corner.i].id)
|
||||
.move(projection.invert(points[corner.i])));
|
||||
node = graph.entity(nodes[corner.i].id);
|
||||
loc = projection.invert(points[corner.i]);
|
||||
graph = graph.replace(node.move(geoInterp(node.loc, loc, t)));
|
||||
|
||||
} else {
|
||||
var best,
|
||||
@@ -57,25 +61,25 @@ export function actionOrthogonalize(wayId, projection) {
|
||||
for (i = 0; i < points.length; i++) {
|
||||
// only move the points that actually moved
|
||||
if (originalPoints[i][0] !== points[i][0] || originalPoints[i][1] !== points[i][1]) {
|
||||
graph = graph.replace(graph.entity(nodes[i].id)
|
||||
.move(projection.invert(points[i])));
|
||||
loc = projection.invert(points[i]);
|
||||
node = graph.entity(nodes[i].id);
|
||||
graph = graph.replace(node.move(geoInterp(node.loc, loc, t)));
|
||||
}
|
||||
}
|
||||
|
||||
// remove empty nodes on straight sections
|
||||
for (i = 0; i < points.length; i++) {
|
||||
var node = nodes[i];
|
||||
for (i = 0; t === 1 && i < points.length; i++) {
|
||||
node = graph.entity(nodes[i].id);
|
||||
|
||||
if (graph.parentWays(node).length > 1 ||
|
||||
graph.parentRelations(node).length ||
|
||||
node.hasInterestingTags()) {
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
var dotp = normalizedDotProduct(i, points);
|
||||
if (dotp < -1 + epsilon) {
|
||||
graph = actionDeleteNode(nodes[i].id)(graph);
|
||||
graph = actionDeleteNode(node.id)(graph);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -182,5 +186,8 @@ export function actionOrthogonalize(wayId, projection) {
|
||||
};
|
||||
|
||||
|
||||
action.transitionable = true;
|
||||
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
import {
|
||||
geoEuclideanDistance,
|
||||
geoExtent,
|
||||
geoInterp,
|
||||
geoRotate
|
||||
} from '../geo';
|
||||
|
||||
@@ -52,7 +53,10 @@ export function actionReflect(reflectIds, projection) {
|
||||
}
|
||||
|
||||
|
||||
var action = function(graph) {
|
||||
var action = function(graph, t) {
|
||||
if (t === null || !isFinite(t)) t = 1;
|
||||
t = Math.min(Math.max(+t, 0), 1);
|
||||
|
||||
var nodes = utilGetAllNodes(reflectIds, graph),
|
||||
ssr = getSmallestSurroundingRectangle(graph, nodes);
|
||||
|
||||
@@ -87,7 +91,8 @@ export function actionReflect(reflectIds, projection) {
|
||||
a * (c[0] - p[0]) + b * (c[1] - p[1]) + p[0],
|
||||
b * (c[0] - p[0]) - a * (c[1] - p[1]) + p[1]
|
||||
];
|
||||
node = node.move(projection.invert(c2));
|
||||
var loc2 = projection.invert(c2);
|
||||
node = node.move(geoInterp(node.loc, loc2, t));
|
||||
graph = graph.replace(node);
|
||||
}
|
||||
|
||||
@@ -102,5 +107,8 @@ export function actionReflect(reflectIds, projection) {
|
||||
};
|
||||
|
||||
|
||||
action.transitionable = true;
|
||||
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { actionDeleteNode } from './delete_node';
|
||||
import { geoEuclideanDistance } from '../geo/index';
|
||||
import { geoEuclideanDistance, geoInterp } from '../geo/index';
|
||||
|
||||
|
||||
/*
|
||||
@@ -8,12 +8,15 @@ import { geoEuclideanDistance } from '../geo/index';
|
||||
export function actionStraighten(wayId, projection) {
|
||||
|
||||
function positionAlongWay(n, s, e) {
|
||||
return ((n[0] - s[0]) * (e[0] - s[0]) + (n[1] - s[1]) * (e[1] - s[1]))/
|
||||
return ((n[0] - s[0]) * (e[0] - s[0]) + (n[1] - s[1]) * (e[1] - s[1])) /
|
||||
(Math.pow(e[0] - s[0], 2) + Math.pow(e[1] - s[1], 2));
|
||||
}
|
||||
|
||||
|
||||
var action = function(graph) {
|
||||
var action = function(graph, t) {
|
||||
if (t === null || !isFinite(t)) t = 1;
|
||||
t = Math.min(Math.max(+t, 0), 1);
|
||||
|
||||
var way = graph.entity(wayId),
|
||||
nodes = graph.childNodes(way),
|
||||
points = nodes.map(function(n) { return projection(n.loc); }),
|
||||
@@ -26,17 +29,18 @@ export function actionStraighten(wayId, projection) {
|
||||
var node = nodes[i],
|
||||
point = points[i];
|
||||
|
||||
if (graph.parentWays(node).length > 1 ||
|
||||
if (t < 1 || graph.parentWays(node).length > 1 ||
|
||||
graph.parentRelations(node).length ||
|
||||
node.hasInterestingTags()) {
|
||||
|
||||
var u = positionAlongWay(point, startPoint, endPoint),
|
||||
p0 = startPoint[0] + u * (endPoint[0] - startPoint[0]),
|
||||
p1 = startPoint[1] + u * (endPoint[1] - startPoint[1]);
|
||||
p = [
|
||||
startPoint[0] + u * (endPoint[0] - startPoint[0]),
|
||||
startPoint[1] + u * (endPoint[1] - startPoint[1])
|
||||
],
|
||||
loc2 = projection.invert(p);
|
||||
|
||||
graph = graph
|
||||
.replace(graph.entity(node.id)
|
||||
.move(projection.invert([p0, p1])));
|
||||
graph = graph.replace(node.move(geoInterp(node.loc, loc2, t)));
|
||||
|
||||
} else {
|
||||
// safe to delete
|
||||
@@ -83,5 +87,8 @@ export function actionStraighten(wayId, projection) {
|
||||
};
|
||||
|
||||
|
||||
action.transitionable = true;
|
||||
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
+77
-30
@@ -11,13 +11,15 @@ import { utilRebind } from '../util/rebind';
|
||||
|
||||
|
||||
export function coreHistory(context) {
|
||||
var stack, index, tree,
|
||||
imageryUsed = ['Bing'],
|
||||
var imageryUsed = ['Bing'],
|
||||
dispatch = d3.dispatch('change', 'undone', 'redone'),
|
||||
lock = utilSessionMutex('lock');
|
||||
lock = utilSessionMutex('lock'),
|
||||
duration = 150,
|
||||
stack, index, tree;
|
||||
|
||||
|
||||
function perform(actions) {
|
||||
// internal _act, accepts list of actions and eased time
|
||||
function _act(actions, t) {
|
||||
actions = Array.prototype.slice.call(actions);
|
||||
|
||||
var annotation;
|
||||
@@ -31,7 +33,7 @@ export function coreHistory(context) {
|
||||
|
||||
var graph = stack[index].graph;
|
||||
for (var i = 0; i < actions.length; i++) {
|
||||
graph = actions[i](graph);
|
||||
graph = actions[i](graph, t);
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -42,6 +44,40 @@ export function coreHistory(context) {
|
||||
}
|
||||
|
||||
|
||||
// internal _perform with eased time
|
||||
function _perform(args, t) {
|
||||
var previous = stack[index].graph;
|
||||
stack = stack.slice(0, index + 1);
|
||||
stack.push(_act(args, t));
|
||||
index++;
|
||||
return change(previous);
|
||||
}
|
||||
|
||||
|
||||
// internal _replace with eased time
|
||||
function _replace(args, t) {
|
||||
var previous = stack[index].graph;
|
||||
// assert(index == stack.length - 1)
|
||||
stack[index] = _act(args, t);
|
||||
return change(previous);
|
||||
}
|
||||
|
||||
|
||||
// internal _overwrite with eased time
|
||||
function _overwrite(args, t) {
|
||||
var previous = stack[index].graph;
|
||||
if (index > 0) {
|
||||
index--;
|
||||
stack.pop();
|
||||
}
|
||||
stack = stack.slice(0, index + 1);
|
||||
stack.push(_act(args, t));
|
||||
index++;
|
||||
return change(previous);
|
||||
}
|
||||
|
||||
|
||||
// determine diffrence and dispatch a change event
|
||||
function change(previous) {
|
||||
var difference = coreDifference(previous, history.graph());
|
||||
dispatch.call('change', this, difference);
|
||||
@@ -76,23 +112,50 @@ export function coreHistory(context) {
|
||||
|
||||
|
||||
perform: function() {
|
||||
var previous = stack[index].graph;
|
||||
// complete any transition already in progress
|
||||
d3.select(document)
|
||||
.interrupt('history.perform');
|
||||
|
||||
stack = stack.slice(0, index + 1);
|
||||
stack.push(perform(arguments));
|
||||
index++;
|
||||
var transitionable = false,
|
||||
action0 = arguments[0];
|
||||
|
||||
return change(previous);
|
||||
if (arguments.length === 1 ||
|
||||
arguments.length === 2 && !_.isFunction(arguments[1])) {
|
||||
transitionable = !!action0.transitionable;
|
||||
}
|
||||
|
||||
if (transitionable) {
|
||||
var origArguments = arguments;
|
||||
d3.select(document)
|
||||
.transition('history.perform')
|
||||
.duration(duration)
|
||||
.ease(d3.easeLinear)
|
||||
.tween('history.tween', function() {
|
||||
return function(t) {
|
||||
if (t < 1) _overwrite([action0], t);
|
||||
};
|
||||
})
|
||||
.on('start', function() {
|
||||
_perform([action0], 0);
|
||||
})
|
||||
.on('end interrupt', function() {
|
||||
_overwrite(origArguments, 1);
|
||||
});
|
||||
|
||||
} else {
|
||||
return _perform(arguments);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
replace: function() {
|
||||
var previous = stack[index].graph;
|
||||
return _replace(arguments, 1);
|
||||
},
|
||||
|
||||
// assert(index == stack.length - 1)
|
||||
stack[index] = perform(arguments);
|
||||
|
||||
return change(previous);
|
||||
// Same as calling pop and then perform
|
||||
overwrite: function() {
|
||||
return _overwrite(arguments, 1);
|
||||
},
|
||||
|
||||
|
||||
@@ -107,22 +170,6 @@ export function coreHistory(context) {
|
||||
},
|
||||
|
||||
|
||||
// Same as calling pop and then perform
|
||||
overwrite: function() {
|
||||
var previous = stack[index].graph;
|
||||
|
||||
if (index > 0) {
|
||||
index--;
|
||||
stack.pop();
|
||||
}
|
||||
stack = stack.slice(0, index + 1);
|
||||
stack.push(perform(arguments));
|
||||
index++;
|
||||
|
||||
return change(previous);
|
||||
},
|
||||
|
||||
|
||||
undo: function() {
|
||||
var previous = stack[index].graph;
|
||||
|
||||
|
||||
@@ -271,4 +271,53 @@ describe('iD.actionCircularize', function () {
|
||||
expect(isCircular('-', graph)).to.be.ok;
|
||||
});
|
||||
|
||||
|
||||
describe('transitions', function () {
|
||||
it('is transitionable', function() {
|
||||
expect(iD.actionCircularize().transitionable).to.be.true;
|
||||
});
|
||||
|
||||
it('circularize at t = 0', function() {
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'a', loc: [0, 0]}),
|
||||
iD.Node({id: 'b', loc: [2, 0]}),
|
||||
iD.Node({id: 'c', loc: [2, 2]}),
|
||||
iD.Node({id: 'd', loc: [0, 2]}),
|
||||
iD.Way({id: '-', nodes: ['a', 'b', 'c', 'd', 'a']})
|
||||
]);
|
||||
graph = iD.actionCircularize('-', projection)(graph, 0);
|
||||
expect(isCircular('-', graph)).to.be.not.ok;
|
||||
expect(graph.entity('-').nodes).to.have.length(20);
|
||||
expect(area('-', graph)).to.be.closeTo(-4, 1e-2);
|
||||
});
|
||||
|
||||
it('circularize at t = 0.5', function() {
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'a', loc: [0, 0]}),
|
||||
iD.Node({id: 'b', loc: [2, 0]}),
|
||||
iD.Node({id: 'c', loc: [2, 2]}),
|
||||
iD.Node({id: 'd', loc: [0, 2]}),
|
||||
iD.Way({id: '-', nodes: ['a', 'b', 'c', 'd', 'a']})
|
||||
]);
|
||||
graph = iD.actionCircularize('-', projection)(graph, 0.5);
|
||||
expect(isCircular('-', graph)).to.be.not.ok;
|
||||
expect(graph.entity('-').nodes).to.have.length(20);
|
||||
expect(area('-', graph)).to.be.closeTo(-4.812, 1e-2);
|
||||
});
|
||||
|
||||
it('circularize at t = 1', function() {
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'a', loc: [0, 0]}),
|
||||
iD.Node({id: 'b', loc: [2, 0]}),
|
||||
iD.Node({id: 'c', loc: [2, 2]}),
|
||||
iD.Node({id: 'd', loc: [0, 2]}),
|
||||
iD.Way({id: '-', nodes: ['a', 'b', 'c', 'd', 'a']})
|
||||
]);
|
||||
graph = iD.actionCircularize('-', projection)(graph, 1);
|
||||
expect(isCircular('-', graph)).to.be.ok;
|
||||
expect(graph.entity('-').nodes).to.have.length(20);
|
||||
expect(area('-', graph)).to.be.closeTo(-6.168, 1e-2);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -11,7 +11,6 @@ describe('iD.actionOrthogonalize', function () {
|
||||
]);
|
||||
|
||||
graph = iD.actionOrthogonalize('-', projection)(graph);
|
||||
|
||||
expect(graph.entity('-').nodes).to.have.length(5);
|
||||
});
|
||||
|
||||
@@ -25,7 +24,6 @@ describe('iD.actionOrthogonalize', function () {
|
||||
]);
|
||||
|
||||
graph = iD.actionOrthogonalize('-', projection)(graph);
|
||||
|
||||
expect(graph.entity('-').nodes).to.have.length(5);
|
||||
});
|
||||
|
||||
@@ -38,7 +36,6 @@ describe('iD.actionOrthogonalize', function () {
|
||||
]);
|
||||
|
||||
graph = iD.actionOrthogonalize('-', projection)(graph);
|
||||
|
||||
expect(graph.entity('-').nodes).to.have.length(4);
|
||||
});
|
||||
|
||||
@@ -53,7 +50,6 @@ describe('iD.actionOrthogonalize', function () {
|
||||
]);
|
||||
|
||||
graph = iD.actionOrthogonalize('-', projection)(graph);
|
||||
|
||||
expect(graph.hasEntity('d')).to.eq(undefined);
|
||||
});
|
||||
|
||||
@@ -68,7 +64,6 @@ describe('iD.actionOrthogonalize', function () {
|
||||
]);
|
||||
|
||||
graph = iD.actionOrthogonalize('-', projection)(graph);
|
||||
|
||||
expect(graph.entity('-').nodes).to.have.length(6);
|
||||
expect(graph.hasEntity('d')).to.not.eq(undefined);
|
||||
});
|
||||
@@ -121,4 +116,69 @@ describe('iD.actionOrthogonalize', function () {
|
||||
|
||||
expect(Object.keys(diff.changes()).sort()).to.eql(['a', 'b', 'c', 'f']);
|
||||
});
|
||||
|
||||
|
||||
describe('transitions', function () {
|
||||
it('is transitionable', function() {
|
||||
expect(iD.actionOrthogonalize().transitionable).to.be.true;
|
||||
});
|
||||
|
||||
it('orthogonalize at t = 0', function() {
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'a', loc: [0, 0]}),
|
||||
iD.Node({id: 'b', loc: [1, 0.01], tags: {foo: 'bar'}}),
|
||||
iD.Node({id: 'c', loc: [2, -0.01]}),
|
||||
iD.Node({id: 'd', loc: [3, 0]}),
|
||||
iD.Node({id: 'e', loc: [3, 1]}),
|
||||
iD.Node({id: 'f', loc: [0, 1]}),
|
||||
iD.Way({id: '-', nodes: ['a', 'b', 'c', 'd', 'e', 'f', 'a']})
|
||||
]);
|
||||
|
||||
graph = iD.actionOrthogonalize('-', projection)(graph, 0);
|
||||
expect(graph.entity('-').nodes).to.eql(['a', 'b', 'c', 'd', 'e', 'f', 'a']);
|
||||
expect(graph.entity('b').loc[0]).to.be.closeTo(1, 1e-6);
|
||||
expect(graph.entity('b').loc[1]).to.be.closeTo(0.01, 1e-6);
|
||||
expect(graph.entity('c').loc[0]).to.be.closeTo(2, 1e-6);
|
||||
expect(graph.entity('c').loc[1]).to.be.closeTo(-0.01, 1e-6);
|
||||
|
||||
});
|
||||
|
||||
it('orthogonalize at t = 0.5', function() {
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'a', loc: [0, 0]}),
|
||||
iD.Node({id: 'b', loc: [1, 0.01], tags: {foo: 'bar'}}),
|
||||
iD.Node({id: 'c', loc: [2, -0.01]}),
|
||||
iD.Node({id: 'd', loc: [3, 0]}),
|
||||
iD.Node({id: 'e', loc: [3, 1]}),
|
||||
iD.Node({id: 'f', loc: [0, 1]}),
|
||||
iD.Way({id: '-', nodes: ['a', 'b', 'c', 'd', 'e', 'f', 'a']})
|
||||
]);
|
||||
|
||||
graph = iD.actionOrthogonalize('-', projection)(graph, 0.5);
|
||||
expect(graph.entity('-').nodes).to.eql(['a', 'b', 'c', 'd', 'e', 'f', 'a']);
|
||||
expect(graph.entity('b').loc[0]).to.be.closeTo(1, 1e-3);
|
||||
expect(graph.entity('b').loc[1]).to.be.closeTo(0.005, 1e-3);
|
||||
expect(graph.entity('c').loc[0]).to.be.closeTo(2, 1e-3);
|
||||
expect(graph.entity('c').loc[1]).to.be.closeTo(-0.005, 1e-3);
|
||||
});
|
||||
|
||||
it('orthogonalize at t = 1', function() {
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'a', loc: [0, 0]}),
|
||||
iD.Node({id: 'b', loc: [1, 0.01], tags: {foo: 'bar'}}),
|
||||
iD.Node({id: 'c', loc: [2, -0.01]}),
|
||||
iD.Node({id: 'd', loc: [3, 0]}),
|
||||
iD.Node({id: 'e', loc: [3, 1]}),
|
||||
iD.Node({id: 'f', loc: [0, 1]}),
|
||||
iD.Way({id: '-', nodes: ['a', 'b', 'c', 'd', 'e', 'f', 'a']})
|
||||
]);
|
||||
|
||||
graph = iD.actionOrthogonalize('-', projection)(graph, 1);
|
||||
expect(graph.entity('-').nodes).to.eql(['a', 'b', 'd', 'e', 'f', 'a']);
|
||||
expect(graph.entity('b').loc[0]).to.be.closeTo(1, 2e-3);
|
||||
expect(graph.entity('b').loc[1]).to.be.closeTo(0, 2e-3);
|
||||
expect(graph.hasEntity('c')).to.eq(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -63,4 +63,125 @@ describe('iD.actionReflect', function() {
|
||||
expect(graph.entity('d').loc[1]).to.be.closeTo(2, 1e-6);
|
||||
});
|
||||
|
||||
|
||||
describe('transitions', function () {
|
||||
it('is transitionable', function() {
|
||||
expect(iD.actionReflect().transitionable).to.be.true;
|
||||
});
|
||||
|
||||
it('reflect long at t = 0', function() {
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'a', loc: [0, 0]}),
|
||||
iD.Node({id: 'b', loc: [4, 0]}),
|
||||
iD.Node({id: 'c', loc: [4, 2]}),
|
||||
iD.Node({id: 'd', loc: [1, 2]}),
|
||||
iD.Way({id: '-', nodes: ['a', 'b', 'c', 'd', 'a']})
|
||||
]);
|
||||
graph = iD.actionReflect(['-'], projection)(graph, 0);
|
||||
expect(graph.entity('a').loc[0]).to.be.closeTo(0, 1e-6);
|
||||
expect(graph.entity('a').loc[1]).to.be.closeTo(0, 1e-6);
|
||||
expect(graph.entity('b').loc[0]).to.be.closeTo(4, 1e-6);
|
||||
expect(graph.entity('b').loc[1]).to.be.closeTo(0, 1e-6);
|
||||
expect(graph.entity('c').loc[0]).to.be.closeTo(4, 1e-6);
|
||||
expect(graph.entity('c').loc[1]).to.be.closeTo(2, 1e-6);
|
||||
expect(graph.entity('d').loc[0]).to.be.closeTo(1, 1e-6);
|
||||
expect(graph.entity('d').loc[1]).to.be.closeTo(2, 1e-6);
|
||||
});
|
||||
|
||||
it('reflect long at t = 0.5', function() {
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'a', loc: [0, 0]}),
|
||||
iD.Node({id: 'b', loc: [4, 0]}),
|
||||
iD.Node({id: 'c', loc: [4, 2]}),
|
||||
iD.Node({id: 'd', loc: [1, 2]}),
|
||||
iD.Way({id: '-', nodes: ['a', 'b', 'c', 'd', 'a']})
|
||||
]);
|
||||
graph = iD.actionReflect(['-'], projection)(graph, 0.5);
|
||||
expect(graph.entity('a').loc[0]).to.be.closeTo(0, 1e-6);
|
||||
expect(graph.entity('a').loc[1]).to.be.closeTo(1, 1e-6);
|
||||
expect(graph.entity('b').loc[0]).to.be.closeTo(4, 1e-6);
|
||||
expect(graph.entity('b').loc[1]).to.be.closeTo(1, 1e-6);
|
||||
expect(graph.entity('c').loc[0]).to.be.closeTo(4, 1e-6);
|
||||
expect(graph.entity('c').loc[1]).to.be.closeTo(1, 1e-6);
|
||||
expect(graph.entity('d').loc[0]).to.be.closeTo(1, 1e-6);
|
||||
expect(graph.entity('d').loc[1]).to.be.closeTo(1, 1e-6);
|
||||
});
|
||||
|
||||
it('reflect long at t = 1', function() {
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'a', loc: [0, 0]}),
|
||||
iD.Node({id: 'b', loc: [4, 0]}),
|
||||
iD.Node({id: 'c', loc: [4, 2]}),
|
||||
iD.Node({id: 'd', loc: [1, 2]}),
|
||||
iD.Way({id: '-', nodes: ['a', 'b', 'c', 'd', 'a']})
|
||||
]);
|
||||
graph = iD.actionReflect(['-'], projection)(graph, 1);
|
||||
expect(graph.entity('a').loc[0]).to.be.closeTo(0, 1e-6);
|
||||
expect(graph.entity('a').loc[1]).to.be.closeTo(2, 1e-6);
|
||||
expect(graph.entity('b').loc[0]).to.be.closeTo(4, 1e-6);
|
||||
expect(graph.entity('b').loc[1]).to.be.closeTo(2, 1e-6);
|
||||
expect(graph.entity('c').loc[0]).to.be.closeTo(4, 1e-6);
|
||||
expect(graph.entity('c').loc[1]).to.be.closeTo(0, 1e-6);
|
||||
expect(graph.entity('d').loc[0]).to.be.closeTo(1, 1e-6);
|
||||
expect(graph.entity('d').loc[1]).to.be.closeTo(0, 1e-6);
|
||||
});
|
||||
|
||||
it('reflect short at t = 0', function() {
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'a', loc: [0, 0]}),
|
||||
iD.Node({id: 'b', loc: [4, 0]}),
|
||||
iD.Node({id: 'c', loc: [4, 2]}),
|
||||
iD.Node({id: 'd', loc: [1, 2]}),
|
||||
iD.Way({id: '-', nodes: ['a', 'b', 'c', 'd', 'a']})
|
||||
]);
|
||||
graph = iD.actionReflect(['-'], projection).useLongAxis(false)(graph, 0);
|
||||
expect(graph.entity('a').loc[0]).to.be.closeTo(0, 1e-6);
|
||||
expect(graph.entity('a').loc[1]).to.be.closeTo(0, 1e-6);
|
||||
expect(graph.entity('b').loc[0]).to.be.closeTo(4, 1e-6);
|
||||
expect(graph.entity('b').loc[1]).to.be.closeTo(0, 1e-6);
|
||||
expect(graph.entity('c').loc[0]).to.be.closeTo(4, 1e-6);
|
||||
expect(graph.entity('c').loc[1]).to.be.closeTo(2, 1e-6);
|
||||
expect(graph.entity('d').loc[0]).to.be.closeTo(1, 1e-6);
|
||||
expect(graph.entity('d').loc[1]).to.be.closeTo(2, 1e-6);
|
||||
});
|
||||
|
||||
it('reflect short at t = 0.5', function() {
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'a', loc: [0, 0]}),
|
||||
iD.Node({id: 'b', loc: [4, 0]}),
|
||||
iD.Node({id: 'c', loc: [4, 2]}),
|
||||
iD.Node({id: 'd', loc: [1, 2]}),
|
||||
iD.Way({id: '-', nodes: ['a', 'b', 'c', 'd', 'a']})
|
||||
]);
|
||||
graph = iD.actionReflect(['-'], projection).useLongAxis(false)(graph, 0.5);
|
||||
expect(graph.entity('a').loc[0]).to.be.closeTo(2, 1e-6);
|
||||
expect(graph.entity('a').loc[1]).to.be.closeTo(0, 1e-6);
|
||||
expect(graph.entity('b').loc[0]).to.be.closeTo(2, 1e-6);
|
||||
expect(graph.entity('b').loc[1]).to.be.closeTo(0, 1e-6);
|
||||
expect(graph.entity('c').loc[0]).to.be.closeTo(2, 1e-6);
|
||||
expect(graph.entity('c').loc[1]).to.be.closeTo(2, 1e-6);
|
||||
expect(graph.entity('d').loc[0]).to.be.closeTo(2, 1e-6);
|
||||
expect(graph.entity('d').loc[1]).to.be.closeTo(2, 1e-6);
|
||||
});
|
||||
|
||||
it('reflect short at t = 1', function() {
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'a', loc: [0, 0]}),
|
||||
iD.Node({id: 'b', loc: [4, 0]}),
|
||||
iD.Node({id: 'c', loc: [4, 2]}),
|
||||
iD.Node({id: 'd', loc: [1, 2]}),
|
||||
iD.Way({id: '-', nodes: ['a', 'b', 'c', 'd', 'a']})
|
||||
]);
|
||||
graph = iD.actionReflect(['-'], projection).useLongAxis(false)(graph, 1);
|
||||
expect(graph.entity('a').loc[0]).to.be.closeTo(4, 1e-6);
|
||||
expect(graph.entity('a').loc[1]).to.be.closeTo(0, 1e-6);
|
||||
expect(graph.entity('b').loc[0]).to.be.closeTo(0, 1e-6);
|
||||
expect(graph.entity('b').loc[1]).to.be.closeTo(0, 1e-6);
|
||||
expect(graph.entity('c').loc[0]).to.be.closeTo(0, 1e-6);
|
||||
expect(graph.entity('c').loc[1]).to.be.closeTo(2, 1e-6);
|
||||
expect(graph.entity('d').loc[0]).to.be.closeTo(3, 1e-6);
|
||||
expect(graph.entity('d').loc[1]).to.be.closeTo(2, 1e-6);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,12 +5,11 @@ describe('iD.actionStraighten', function () {
|
||||
it('returns falsy for ways with internal nodes near centerline', function () {
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'a', loc: [0, 0]}),
|
||||
iD.Node({id: 'b', loc: [1, 0.1]}),
|
||||
iD.Node({id: 'b', loc: [1, 0.01]}),
|
||||
iD.Node({id: 'c', loc: [2, 0]}),
|
||||
iD.Node({id: 'd', loc: [3, 0]}),
|
||||
iD.Way({id: '-', nodes: ['a', 'b', 'c', 'd']})
|
||||
]);
|
||||
|
||||
expect(iD.actionStraighten('-', projection).disabled(graph)).not.to.be.ok;
|
||||
});
|
||||
|
||||
@@ -22,7 +21,6 @@ describe('iD.actionStraighten', function () {
|
||||
iD.Node({id: 'd', loc: [3, 0]}),
|
||||
iD.Way({id: '-', nodes: ['a', 'b', 'c', 'd']})
|
||||
]);
|
||||
|
||||
expect(iD.actionStraighten('-', projection).disabled(graph)).to.equal('too_bendy');
|
||||
});
|
||||
|
||||
@@ -34,49 +32,108 @@ describe('iD.actionStraighten', function () {
|
||||
iD.Node({id: 'd', loc: [0, 0]}),
|
||||
iD.Way({id: '-', nodes: ['a', 'b', 'c', 'd']})
|
||||
]);
|
||||
|
||||
expect(iD.actionStraighten('-', projection).disabled(graph)).to.equal('too_bendy');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('deletes empty nodes', function() {
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'a', loc: [0, 0]}),
|
||||
iD.Node({id: 'b', loc: [2, 0], tags: {}}),
|
||||
iD.Node({id: 'c', loc: [2, 2]}),
|
||||
iD.Node({id: 'b', loc: [1, 0.01], tags: {}}),
|
||||
iD.Node({id: 'c', loc: [2, 0]}),
|
||||
iD.Way({id: '-', nodes: ['a', 'b', 'c']})
|
||||
]);
|
||||
|
||||
graph = iD.actionStraighten('-', projection)(graph);
|
||||
|
||||
expect(graph.entity('-').nodes).to.eql(['a', 'c']);
|
||||
expect(graph.hasEntity('b')).to.eq(undefined);
|
||||
});
|
||||
|
||||
it('does not delete tagged nodes', function() {
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'a', loc: [0, 0]}),
|
||||
iD.Node({id: 'b', loc: [2, 0], tags: {foo: 'bar'}}),
|
||||
iD.Node({id: 'c', loc: [2, 2]}),
|
||||
iD.Node({id: 'b', loc: [1, 0.01], tags: {foo: 'bar'}}),
|
||||
iD.Node({id: 'c', loc: [2, 0]}),
|
||||
iD.Way({id: '-', nodes: ['a', 'b', 'c']})
|
||||
]);
|
||||
|
||||
graph = iD.actionStraighten('-', projection)(graph);
|
||||
|
||||
expect(graph.entity('-').nodes).to.eql(['a', 'b', 'c']);
|
||||
expect(graph.entity('b').loc[0]).to.be.closeTo(1, 1e-6);
|
||||
expect(graph.entity('b').loc[1]).to.be.closeTo(0, 1e-6);
|
||||
});
|
||||
|
||||
it('does not delete nodes connected to other ways', function() {
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'a', loc: [0, 0]}),
|
||||
iD.Node({id: 'b', loc: [2, 0]}),
|
||||
iD.Node({id: 'c', loc: [2, 2]}),
|
||||
iD.Node({id: 'd', loc: [0, 2]}),
|
||||
iD.Way({id: '-', nodes: ['a', 'b', 'c', 'd']}),
|
||||
iD.Node({id: 'b', loc: [1, 0.01]}),
|
||||
iD.Node({id: 'c', loc: [2, 0]}),
|
||||
iD.Way({id: '-', nodes: ['a', 'b', 'c']}),
|
||||
iD.Way({id: '=', nodes: ['b']})
|
||||
]);
|
||||
|
||||
graph = iD.actionStraighten('-', projection)(graph);
|
||||
|
||||
expect(graph.entity('-').nodes).to.have.length(3);
|
||||
expect(graph.entity('-').nodes).to.eql(['a', 'b', 'c']);
|
||||
expect(graph.entity('b').loc[0]).to.be.closeTo(1, 1e-6);
|
||||
expect(graph.entity('b').loc[1]).to.be.closeTo(0, 1e-6);
|
||||
});
|
||||
|
||||
|
||||
describe('transitions', function () {
|
||||
it('is transitionable', function() {
|
||||
expect(iD.actionStraighten().transitionable).to.be.true;
|
||||
});
|
||||
|
||||
it('straighten at t = 0', function() {
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'a', loc: [0, 0]}),
|
||||
iD.Node({id: 'b', loc: [1, 0.01], tags: {foo: 'bar'}}),
|
||||
iD.Node({id: 'c', loc: [2, -0.01]}),
|
||||
iD.Node({id: 'd', loc: [3, 0]}),
|
||||
iD.Way({id: '-', nodes: ['a', 'b', 'c', 'd']})
|
||||
]);
|
||||
|
||||
graph = iD.actionStraighten('-', projection)(graph, 0);
|
||||
expect(graph.entity('-').nodes).to.eql(['a', 'b', 'c', 'd']);
|
||||
expect(graph.entity('b').loc[0]).to.be.closeTo(1, 1e-6);
|
||||
expect(graph.entity('b').loc[1]).to.be.closeTo(0.01, 1e-6);
|
||||
expect(graph.entity('c').loc[0]).to.be.closeTo(2, 1e-6);
|
||||
expect(graph.entity('c').loc[1]).to.be.closeTo(-0.01, 1e-6);
|
||||
});
|
||||
|
||||
it('straighten at t = 0.5', function() {
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'a', loc: [0, 0]}),
|
||||
iD.Node({id: 'b', loc: [1, 0.01], tags: {foo: 'bar'}}),
|
||||
iD.Node({id: 'c', loc: [2, -0.01]}),
|
||||
iD.Node({id: 'd', loc: [3, 0]}),
|
||||
iD.Way({id: '-', nodes: ['a', 'b', 'c', 'd']})
|
||||
]);
|
||||
|
||||
graph = iD.actionStraighten('-', projection)(graph, 0.5);
|
||||
expect(graph.entity('-').nodes).to.eql(['a', 'b', 'c', 'd']);
|
||||
expect(graph.entity('b').loc[0]).to.be.closeTo(1, 1e-6);
|
||||
expect(graph.entity('b').loc[1]).to.be.closeTo(0.005, 1e-6);
|
||||
expect(graph.entity('c').loc[0]).to.be.closeTo(2, 1e-6);
|
||||
expect(graph.entity('c').loc[1]).to.be.closeTo(-0.005, 1e-6);
|
||||
});
|
||||
|
||||
it('straighten at t = 1', function() {
|
||||
var graph = iD.Graph([
|
||||
iD.Node({id: 'a', loc: [0, 0]}),
|
||||
iD.Node({id: 'b', loc: [1, 0.01], tags: {foo: 'bar'}}),
|
||||
iD.Node({id: 'c', loc: [2, -0.01]}),
|
||||
iD.Node({id: 'd', loc: [3, 0]}),
|
||||
iD.Way({id: '-', nodes: ['a', 'b', 'c', 'd']})
|
||||
]);
|
||||
|
||||
graph = iD.actionStraighten('-', projection)(graph, 1);
|
||||
expect(graph.entity('-').nodes).to.eql(['a', 'b', 'd']);
|
||||
expect(graph.entity('b').loc[0]).to.be.closeTo(1, 1e-6);
|
||||
expect(graph.entity('b').loc[1]).to.be.closeTo(0, 1e-6);
|
||||
expect(graph.hasEntity('c')).to.eq(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -51,6 +51,7 @@ describe('iD.History', function () {
|
||||
history.on('change', spy);
|
||||
var difference = history.perform(action);
|
||||
expect(spy).to.have.been.calledWith(difference);
|
||||
expect(spy.callCount).to.eql(1);
|
||||
});
|
||||
|
||||
it('performs multiple actions', function () {
|
||||
@@ -61,6 +62,17 @@ describe('iD.History', function () {
|
||||
expect(action2).to.have.been.called;
|
||||
expect(history.undoAnnotation()).to.equal('annotation');
|
||||
});
|
||||
|
||||
it('performs transitionable actions in a transition', function (done) {
|
||||
var action1 = function() { return iD.Graph(); };
|
||||
action1.transitionable = true;
|
||||
history.on('change', spy);
|
||||
history.perform(action1);
|
||||
window.setTimeout(function() {
|
||||
expect(spy.callCount).to.be.above(2);
|
||||
done();
|
||||
}, 300);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#replace', function () {
|
||||
|
||||
Reference in New Issue
Block a user