diff --git a/js/id/actions/orthogonalize.js b/js/id/actions/orthogonalize.js
index 41fca79e3..d04317d78 100644
--- a/js/id/actions/orthogonalize.js
+++ b/js/id/actions/orthogonalize.js
@@ -6,34 +6,48 @@ iD.actions.Orthogonalize = function(wayId, projection) {
var action = function(graph) {
var way = graph.entity(wayId),
nodes = graph.childNodes(way),
- points = nodes.map(function(n) { return projection(n.loc); }),
- best, i, j;
+ corner = {i: 0, dotp: 1},
+ points, i, j, score, motions;
- var score = squareness();
- for (i = 0; i < 1000; i++) {
- var motions = points.map(stepMap);
- for (j = 0; j < motions.length; j++) {
- points[j] = addPoints(points[j],motions[j]);
+ if (nodes.length === 4) {
+ points = _.uniq(nodes).map(function(n) { return projection(n.loc); });
+ for (i = 0; i < 1000; i++) {
+ motions = points.map(calcMotion);
+ points[corner.i] = addPoints(points[corner.i],motions[corner.i]);
+ score = corner.dotp;
+ if (score < 1.0e-8) {
+ break;
+ }
}
- var newScore = squareness();
- if (newScore < score) {
- best = _.clone(points);
- score = newScore;
+ graph = graph.replace(graph.entity(nodes[corner.i].id)
+ .move(projection.invert(points[corner.i])));
+ } else {
+ var best;
+ points = nodes.map(function(n) { return projection(n.loc); });
+ score = squareness();
+ for (i = 0; i < 1000; i++) {
+ motions = points.map(calcMotion);
+ for (j = 0; j < motions.length; j++) {
+ points[j] = addPoints(points[j],motions[j]);
+ }
+ var newScore = squareness();
+ if (newScore < score) {
+ best = _.clone(points);
+ score = newScore;
+ }
+ if (score < 1.0e-8) {
+ break;
+ }
}
- if (score < 1.0e-8) {
- break;
+ points = best;
+ for (i = 0; i < points.length - 1; i++) {
+ graph = graph.replace(graph.entity(nodes[i].id)
+ .move(projection.invert(points[i])));
}
}
- points = best;
-
- for (i = 0; i < points.length - 1; i++) {
- graph = graph.replace(graph.entity(nodes[i].id)
- .move(projection.invert(points[i])));
- }
-
return graph;
- function stepMap(b, i, array) {
+ function calcMotion(b, i, array) {
var a = array[(i - 1 + array.length) % array.length],
c = array[(i + 1) % array.length],
p = subtractPoints(a, b),
@@ -44,9 +58,17 @@ iD.actions.Orthogonalize = function(wayId, projection) {
q = normalizePoint(q, 1.0);
var dotp = p[0] * q[0] + p[1] * q[1];
+
// nasty hack to deal with almost-straight segments (angle is closer to 180 than to 90/270).
- if (dotp < -0.707106781186547) {
- dotp += 1.0;
+ if (array.length > 3) {
+ if (dotp < -0.707106781186547) {
+ dotp += 1.0;
+ }
+ } else {
+ if( Math.abs(dotp) < corner.dotp){
+ corner.i = i;
+ corner.dotp = Math.abs(dotp);
+ }
}
return normalizePoint(addPoints(p, q), 0.1 * dotp * scale);
@@ -86,7 +108,7 @@ iD.actions.Orthogonalize = function(wayId, projection) {
return [a[0] + b[0], a[1] + b[1]];
}
- function normalizePoint(point, thickness) {
+ function normalizePoint(point, scale) {
var vector = [0, 0];
var length = Math.sqrt(point[0] * point[0] + point[1] * point[1]);
if (length !== 0) {
@@ -94,8 +116,8 @@ iD.actions.Orthogonalize = function(wayId, projection) {
vector[1] = point[1] / length;
}
- vector[0] *= thickness;
- vector[1] *= thickness;
+ vector[0] *= scale;
+ vector[1] *= scale;
return vector;
}
diff --git a/js/id/operations/orthogonalize.js b/js/id/operations/orthogonalize.js
index 482c4ba5b..2e5844398 100644
--- a/js/id/operations/orthogonalize.js
+++ b/js/id/operations/orthogonalize.js
@@ -10,7 +10,7 @@ iD.operations.Orthogonalize = function(selection, context) {
operation.available = function() {
return selection.length === 1 &&
context.entity(entityId).type === 'way' &&
- _.uniq(context.entity(entityId).nodes).length > 3;
+ _.uniq(context.entity(entityId).nodes).length > 2;
};
operation.enabled = function() {
diff --git a/test/index.html b/test/index.html
index 173e22592..c0f7be5f2 100644
--- a/test/index.html
+++ b/test/index.html
@@ -184,6 +184,7 @@
+
diff --git a/test/spec/actions/orthogonalize.js b/test/spec/actions/orthogonalize.js
new file mode 100644
index 000000000..690d73140
--- /dev/null
+++ b/test/spec/actions/orthogonalize.js
@@ -0,0 +1,30 @@
+describe("iD.actions.Orthogonalize", function () {
+ var projection = d3.geo.mercator();
+
+ it("orthoganalizes a quad", function () {
+ var graph = iD.Graph({
+ 'a': iD.Node({id: 'a', loc: [0, 0]}),
+ 'b': iD.Node({id: 'b', loc: [4, 0]}),
+ 'c': iD.Node({id: 'c', loc: [3, 2]}),
+ 'd': iD.Node({id: 'd', loc: [0, 2]}),
+ '-': iD.Way({id: '-', nodes: ['a', 'b', 'c', 'd', 'a']})
+ });
+
+ graph = iD.actions.Orthogonalize('-', projection)(graph);
+
+ expect(graph.entity('-').nodes).to.have.length(5);
+ });
+
+ it("orthoganalizes a triangle", function () {
+ var graph = iD.Graph({
+ 'a': iD.Node({id: 'a', loc: [0, 0]}),
+ 'b': iD.Node({id: 'b', loc: [3, 0]}),
+ 'c': iD.Node({id: 'c', loc: [2, 2]}),
+ '-': iD.Way({id: '-', nodes: ['a', 'b', 'c', 'a']})
+ });
+
+ graph = iD.actions.Orthogonalize('-', projection)(graph);
+
+ expect(graph.entity('-').nodes).to.have.length(4);
+ });
+});