Refactor rotation code to geoRotate, add tests

This commit is contained in:
Bryan Housel
2016-12-21 11:21:06 -05:00
parent 063e7712b8
commit cad4c0090c
5 changed files with 70 additions and 27 deletions
+4 -12
View File
@@ -6,7 +6,8 @@ import {
import {
geoEuclideanDistance,
geoExtent
geoExtent,
geoRotate
} from '../geo';
@@ -14,15 +15,6 @@ import {
export function actionReflect(wayId, projection) {
var useLongAxis = true;
function rotatePolygon(polygon, angle, centroid) {
return polygon.map(function(point) {
var radial = [point[0] - centroid[0], point[1] - centroid[1]];
return [
radial[0] * Math.cos(angle) - radial[1] * Math.sin(angle) + centroid[0],
radial[0] * Math.sin(angle) + radial[1] * Math.cos(angle) + centroid[1]
];
});
}
// http://gis.stackexchange.com/questions/22895/finding-minimum-area-rectangle-for-given-points
// http://gis.stackexchange.com/questions/3739/generalisation-strategies-for-building-outlines/3756#3756
@@ -39,7 +31,7 @@ export function actionReflect(wayId, projection) {
for (var i = 0; i < hull.length - 1; i++) {
var c2 = hull[i + 1],
angle = Math.atan2(c2[1] - c1[1], c2[0] - c1[0]),
poly = rotatePolygon(hull, -angle, centroid),
poly = geoRotate(hull, -angle, centroid),
extent = poly.reduce(function(extent, point) {
return extent.extend(geoExtent(point));
}, geoExtent()),
@@ -54,7 +46,7 @@ export function actionReflect(wayId, projection) {
}
return {
poly: rotatePolygon(ssrExtent.polygon(), ssrAngle, centroid),
poly: geoRotate(ssrExtent.polygon(), ssrAngle, centroid),
angle: ssrAngle
};
}
+5 -15
View File
@@ -1,29 +1,19 @@
import _ from 'lodash';
import { geoRotate } from '../geo';
export function actionRotate(wayId, pivot, angle, projection) {
return function(graph) {
var action = function(graph) {
return graph.update(function(graph) {
var way = graph.entity(wayId);
_.uniq(way.nodes).forEach(function(id) {
var node = graph.entity(id),
point = projection(node.loc),
radial = [0,0];
radial[0] = point[0] - pivot[0];
radial[1] = point[1] - pivot[1];
point = [
radial[0] * Math.cos(angle) - radial[1] * Math.sin(angle) + pivot[0],
radial[0] * Math.sin(angle) + radial[1] * Math.cos(angle) + pivot[1]
];
point = geoRotate([projection(node.loc)], angle, pivot)[0];
graph = graph.replace(node.move(projection.invert(point)));
});
});
};
return action;
}
+12
View File
@@ -104,6 +104,18 @@ export function geoAngle(a, b, projection) {
}
// Rotate all points counterclockwise around a pivot point by given angle
export function geoRotate(points, angle, around) {
return points.map(function(point) {
var radial = [point[0] - around[0], point[1] - around[1]];
return [
radial[0] * Math.cos(angle) - radial[1] * Math.sin(angle) + around[0],
radial[0] * Math.sin(angle) + radial[1] * Math.cos(angle) + around[1]
];
});
}
// Choose the edge with the minimal distance from `point` to its orthogonal
// projection onto that edge, if such a projection exists, or the distance to
// the closest vertex on that edge. Returns an object with the `index` of the
+1
View File
@@ -7,6 +7,7 @@ export { geoExtent } from './extent.js';
export { geoInterp } from './geo.js';
export { geoRawMercator } from './raw_mercator.js';
export { geoRoundCoords } from './geo.js';
export { geoRotate } from './geo.js';
export { geoLatToMeters } from './geo.js';
export { geoLineIntersection } from './geo.js';
export { geoLonToMeters } from './geo.js';
+48
View File
@@ -185,6 +185,54 @@ describe('iD.geo', function() {
});
});
describe('geoEdgeEqual', function() {
it('returns false for inequal edges', function() {
expect(iD.geoEdgeEqual(['a','b'], ['a','c'])).to.be.false;
});
it('returns true for equal edges along same direction', function() {
expect(iD.geoEdgeEqual(['a','b'], ['a','b'])).to.be.true;
});
it('returns true for equal edges along opposite direction', function() {
expect(iD.geoEdgeEqual(['a','b'], ['b','a'])).to.be.true;
});
});
describe('geoAngle', function() {
it('returns angle between a and b', function() {
var projection = function (_) { return _; };
expect(iD.geoAngle({loc:[0, 0]}, {loc:[1, 0]}, projection)).to.be.closeTo(0, 1e-6);
expect(iD.geoAngle({loc:[0, 0]}, {loc:[0, 1]}, projection)).to.be.closeTo(Math.PI / 2, 1e-6);
expect(iD.geoAngle({loc:[0, 0]}, {loc:[-1, 0]}, projection)).to.be.closeTo(Math.PI, 1e-6);
expect(iD.geoAngle({loc:[0, 0]}, {loc:[0, -1]}, projection)).to.be.closeTo(-Math.PI / 2, 1e-6);
});
});
describe('geoRotate', function() {
it('rotates points around [0, 0]', function() {
var points = [[5, 0], [5, 1]],
angle = Math.PI,
around = [0, 0],
result = iD.geoRotate(points, angle, around);
expect(result[0][0]).to.be.closeTo(-5, 1e-6);
expect(result[0][1]).to.be.closeTo(0, 1e-6);
expect(result[1][0]).to.be.closeTo(-5, 1e-6);
expect(result[1][1]).to.be.closeTo(-1, 1e-6);
});
it('rotates points around [3, 0]', function() {
var points = [[5, 0], [5, 1]],
angle = Math.PI,
around = [3, 0],
result = iD.geoRotate(points, angle, around);
expect(result[0][0]).to.be.closeTo(1, 1e-6);
expect(result[0][1]).to.be.closeTo(0, 1e-6);
expect(result[1][0]).to.be.closeTo(1, 1e-6);
expect(result[1][1]).to.be.closeTo(-1, 1e-6);
});
});
describe('geoChooseEdge', function() {
var projection = function (l) { return l; };
projection.invert = projection;