diff --git a/modules/actions/circularize.js b/modules/actions/circularize.js index 2902aabab..b388ec52b 100644 --- a/modules/actions/circularize.js +++ b/modules/actions/circularize.js @@ -6,9 +6,10 @@ import { polygonCentroid as d3_polygonCentroid } from 'd3-polygon'; -import { geoVecInterp, geoVecLength} from '../geo'; +import { geoVecInterp, geoVecLength } from '../geo'; import { osmNode } from '../osm/node'; import { utilArrayUniq } from '../util'; +import { geoVecLengthSquare } from '../geo/vector'; export function actionCircularize(wayId, projection, maxAngle) { @@ -229,41 +230,44 @@ export function actionCircularize(wayId, projection, maxAngle) { return 'not_closed'; } + //disable when already circular var way = graph.entity(wayId); var nodes = utilArrayUniq(graph.childNodes(way)); var points = nodes.map(function(n) { return projection(n.loc); }); - var sign = d3_polygonArea(points) > 0 ? 1 : -1; var hull = d3_polygonHull(points); + var epsilonAngle = Math.PI / 180; if (hull.length !== points.length || hull.length < 3){ return false; } var centroid = d3_polygonCentroid(points); - var radius = d3_median(points, function(p) { return geoVecLength(centroid, p); }); + var radius = geoVecLengthSquare(centroid, points[0]); // compare distances between centroid and points for (var i = 0; i 1)) { + var actualDist = geoVecLengthSquare(actualPoint, centroid); + var diff = Math.abs(actualDist - radius); + //compare distances with epsilon-error (5%) + if (diff > 0.05*radius) { return false; } } - - var startAngle = Math.atan2(hull[0][1] - centroid[1], hull[0][0] - centroid[0]); - var endAngle = Math.atan2(hull[1][1] - centroid[1], hull[1][0] - centroid[0]); - var eachAngle = (endAngle - startAngle); - //compare central angles + //check if central angles are smaller than maxAngle for (i = 0; i 0) { - angle = -sign * (2 * Math.PI - Math.abs(angle)); + if (angle < 0) { + angle = -angle; } - if (Math.abs((angle) - eachAngle) > 0.05) { + if (angle > Math.PI){ + angle = (2*Math.PI - angle); + } + + if (angle > maxAngle + epsilonAngle) { return false; } } diff --git a/modules/geo/index.js b/modules/geo/index.js index 88402d97a..609e9c8a9 100644 --- a/modules/geo/index.js +++ b/modules/geo/index.js @@ -37,6 +37,7 @@ export { geoVecEqual } from './vector.js'; export { geoVecFloor } from './vector.js'; export { geoVecInterp } from './vector.js'; export { geoVecLength } from './vector.js'; +export { geoVecLengthSquare } from '/vector.js'; export { geoVecNormalize } from './vector.js'; export { geoVecNormalizedDot } from './vector.js'; export { geoVecProject } from './vector.js'; diff --git a/modules/geo/vector.js b/modules/geo/vector.js index 97318607d..8dade4393 100644 --- a/modules/geo/vector.js +++ b/modules/geo/vector.js @@ -37,10 +37,15 @@ export function geoVecInterp(a, b, t) { // http://jsperf.com/id-dist-optimization export function geoVecLength(a, b) { + return Math.sqrt(geoVecLengthSquare(a,b)); +} + +// length of vector raised to the power two +export function geoVecLengthSquare(a, b) { b = b || [0, 0]; var x = a[0] - b[0]; var y = a[1] - b[1]; - return Math.sqrt((x * x) + (y * y)); + return (x * x) + (y * y); } // get a unit vector diff --git a/test/spec/actions/circularize.js b/test/spec/actions/circularize.js index e20b268fe..c6c0f9813 100644 --- a/test/spec/actions/circularize.js +++ b/test/spec/actions/circularize.js @@ -288,6 +288,33 @@ describe('iD.actionCircularize', function () { expect(isCircular('-', graph)).to.be.ok; }); + it('not disable circularize when its not circular', function(){ + var graph = iD.coreGraph([ + iD.osmNode({id: 'a', loc: [0, 0]}), + iD.osmNode({id: 'b', loc: [2, 0]}), + iD.osmNode({id: 'c', loc: [2, 2]}), + iD.osmNode({id: 'd', loc: [0, 2]}), + iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd', 'a']}) + ]); + var result = iD.actionCircularize('-', projection).disabled(graph); + expect(result).to.be.false; + + }); + + it('disable circularize twice', function(){ + var graph = iD.coreGraph([ + iD.osmNode({id: 'a', loc: [0, 0]}), + iD.osmNode({id: 'b', loc: [2, 0]}), + iD.osmNode({id: 'c', loc: [2, 2]}), + iD.osmNode({id: 'd', loc: [0, 2]}), + iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd', 'a']}) + ]); + graph = iD.actionCircularize('-', projection)(graph); + var result = iD.actionCircularize('-', projection).disabled(graph); + expect(result).to.eql('already_circular'); + + }); + describe('transitions', function () { it('is transitionable', function() {