Merge branch 'transitioned-actions'

This commit is contained in:
Bryan Housel
2017-01-04 17:26:22 -05:00
10 changed files with 492 additions and 89 deletions
+49 -14
View File
@@ -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;
}
+20 -13
View File
@@ -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;
}
+10 -2
View File
@@ -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;
}
+16 -9
View File
@@ -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
View File
@@ -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;
+49
View File
@@ -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);
});
});
});
+65 -5
View File
@@ -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);
});
});
});
+121
View File
@@ -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);
});
});
});
+73 -16
View File
@@ -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);
});
});
});
+12
View File
@@ -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 () {