Refactor vector math functions from geo.js to vector.js

This commit is contained in:
Bryan Housel
2017-12-28 01:08:11 -05:00
parent fa7a6ebb12
commit 96afbbd785
23 changed files with 268 additions and 248 deletions
+8 -12
View File
@@ -10,11 +10,7 @@ import {
polygonCentroid as d3_polygonCentroid
} from 'd3-polygon';
import {
geoEuclideanDistance,
geoInterp
} from '../geo';
import { geoVecInterp, geoVecLength } from '../geo';
import { osmNode } from '../osm';
@@ -41,8 +37,8 @@ export function actionCircularize(wayId, projection, maxAngle) {
keyNodes = nodes.filter(function(n) { return graph.parentWays(n).length !== 1; }),
points = nodes.map(function(n) { return projection(n.loc); }),
keyPoints = keyNodes.map(function(n) { return projection(n.loc); }),
centroid = (points.length === 2) ? geoInterp(points[0], points[1], 0.5) : d3_polygonCentroid(points),
radius = d3_median(points, function(p) { return geoEuclideanDistance(centroid, p); }),
centroid = (points.length === 2) ? geoVecInterp(points[0], points[1], 0.5) : d3_polygonCentroid(points),
radius = d3_median(points, function(p) { return geoVecLength(centroid, p); }),
sign = d3_polygonArea(points) > 0 ? 1 : -1,
ids;
@@ -82,7 +78,7 @@ export function actionCircularize(wayId, projection, maxAngle) {
}
// position this key node
var distance = geoEuclideanDistance(centroid, keyPoints[i]);
var distance = geoVecLength(centroid, keyPoints[i]);
if (distance === 0) { distance = 1e-4; }
keyPoints[i] = [
centroid[0] + (keyPoints[i][0] - centroid[0]) / distance * radius,
@@ -91,7 +87,7 @@ export function actionCircularize(wayId, projection, maxAngle) {
loc = projection.invert(keyPoints[i]);
node = keyNodes[i];
origNode = origNodes[node.id];
node = node.move(geoInterp(origNode.loc, loc, t));
node = node.move(geoVecInterp(origNode.loc, loc, t));
graph = graph.replace(node);
// figure out the between delta angle we want to match to
@@ -122,7 +118,7 @@ export function actionCircularize(wayId, projection, maxAngle) {
origNode = origNodes[node.id];
nearNodes[node.id] = angle;
node = node.move(geoInterp(origNode.loc, loc, t));
node = node.move(geoVecInterp(origNode.loc, loc, t));
graph = graph.replace(node);
}
@@ -145,7 +141,7 @@ export function actionCircularize(wayId, projection, maxAngle) {
}
}
node = osmNode({ loc: geoInterp(origNode.loc, loc, t) });
node = osmNode({ loc: geoVecInterp(origNode.loc, loc, t) });
graph = graph.replace(node);
nodes.splice(endNodeIndex + j, 0, node);
@@ -220,7 +216,7 @@ export function actionCircularize(wayId, projection, maxAngle) {
// move interior nodes to the surface of the convex hull..
for (var j = 1; j < indexRange; j++) {
var point = geoInterp(hull[i], hull[i+1], j / indexRange),
var point = geoVecInterp(hull[i], hull[i+1], j / indexRange),
node = nodes[(j + startIndex) % nodes.length].move(projection.invert(point));
graph = graph.replace(node);
}
+2 -2
View File
@@ -13,11 +13,11 @@ import { osmNode } from '../osm';
import {
geoAngle,
geoChooseEdge,
geoInterp,
geoPathIntersections,
geoPathLength,
geoSphericalDistance,
geoVecAdd,
geoVecInterp,
geoVecSubtract
} from '../geo';
@@ -206,7 +206,7 @@ export function actionMove(moveIds, tryDelta, projection, cache) {
if (!isEP1 && !isEP2) {
var epsilon = 1e-4, maxIter = 10;
for (var i = 0; i < maxIter; i++) {
loc = geoInterp(edge1.loc, edge2.loc, 0.5);
loc = geoVecInterp(edge1.loc, edge2.loc, 0.5);
edge1 = geoChooseEdge(nodes1, projection(loc), projection);
edge2 = geoChooseEdge(nodes2, projection(loc), projection);
if (Math.abs(edge1.distance - edge2.distance) < epsilon) break;
+2 -2
View File
@@ -1,4 +1,4 @@
import { geoInterp } from '../geo';
import { geoVecInterp } from '../geo';
export function actionMoveNode(nodeID, toLoc) {
@@ -8,7 +8,7 @@ export function actionMoveNode(nodeID, toLoc) {
var node = graph.entity(nodeID);
return graph.replace(
node.move(geoInterp(node.loc, toLoc, t))
node.move(geoVecInterp(node.loc, toLoc, t))
);
};
+4 -7
View File
@@ -2,10 +2,7 @@ import _clone from 'lodash-es/clone';
import _uniq from 'lodash-es/uniq';
import { actionDeleteNode } from './delete_node';
import {
geoEuclideanDistance,
geoInterp
} from '../geo';
import { geoVecInterp, geoVecLength } from '../geo';
/*
@@ -40,7 +37,7 @@ export function actionOrthogonalize(wayId, projection) {
node = graph.entity(nodes[corner.i].id);
loc = projection.invert(points[corner.i]);
graph = graph.replace(node.move(geoInterp(node.loc, loc, t)));
graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
} else {
var best,
@@ -69,7 +66,7 @@ export function actionOrthogonalize(wayId, projection) {
if (originalPoints[i][0] !== points[i][0] || originalPoints[i][1] !== points[i][1]) {
loc = projection.invert(points[i]);
node = graph.entity(nodes[i].id);
graph = graph.replace(node.move(geoInterp(node.loc, loc, t)));
graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
}
}
@@ -100,7 +97,7 @@ export function actionOrthogonalize(wayId, projection) {
q = subtractPoints(c, b),
scale, dotp;
scale = 2 * Math.min(geoEuclideanDistance(p, [0, 0]), geoEuclideanDistance(q, [0, 0]));
scale = 2 * Math.min(geoVecLength(p, [0, 0]), geoVecLength(q, [0, 0]));
p = normalizePoint(p, 1.0);
q = normalizePoint(q, 1.0);
+5 -5
View File
@@ -4,10 +4,10 @@ import {
} from 'd3-polygon';
import {
geoEuclideanDistance,
geoExtent,
geoInterp,
geoRotate
geoRotate,
geoVecInterp,
geoVecLength
} from '../geo';
import { utilGetAllNodes } from '../util';
@@ -69,7 +69,7 @@ export function actionReflect(reflectIds, projection) {
q2 = [(ssr.poly[1][0] + ssr.poly[2][0]) / 2, (ssr.poly[1][1] + ssr.poly[2][1]) / 2 ],
p, q;
var isLong = (geoEuclideanDistance(p1, q1) > geoEuclideanDistance(p2, q2));
var isLong = (geoVecLength(p1, q1) > geoVecLength(p2, q2));
if ((useLongAxis && isLong) || (!useLongAxis && !isLong)) {
p = p1;
q = q1;
@@ -92,7 +92,7 @@ export function actionReflect(reflectIds, projection) {
b * (c[0] - p[0]) - a * (c[1] - p[1]) + p[1]
];
var loc2 = projection.invert(c2);
node = node.move(geoInterp(node.loc, loc2, t));
node = node.move(geoVecInterp(node.loc, loc2, t));
graph = graph.replace(node);
}
+4 -4
View File
@@ -1,8 +1,8 @@
import { actionDeleteNode } from './delete_node';
import {
geoEuclideanDistance,
geoInterp
geoVecInterp,
geoVecLength
} from '../geo';
@@ -44,7 +44,7 @@ export function actionStraighten(wayId, projection) {
],
loc2 = projection.invert(p);
graph = graph.replace(node.move(geoInterp(node.loc, loc2, t)));
graph = graph.replace(node.move(geoVecInterp(node.loc, loc2, t)));
} else {
// safe to delete
@@ -69,7 +69,7 @@ export function actionStraighten(wayId, projection) {
points = nodes.map(function(n) { return projection(n.loc); }),
startPoint = points[0],
endPoint = points[points.length-1],
threshold = 0.2 * geoEuclideanDistance(startPoint, endPoint),
threshold = 0.2 * geoVecLength(startPoint, endPoint),
i;
if (threshold === 0) {
+3 -3
View File
@@ -14,7 +14,7 @@ import { behaviorTail } from './tail';
import {
geoChooseEdge,
geoEuclideanDistance,
geoVecLength,
geoViewportEdge
} from '../geo';
@@ -81,7 +81,7 @@ export function behaviorDraw(context) {
d3_select(window).on('mouseup.draw', function() {
var t2 = +new Date();
var p2 = point();
var dist = geoEuclideanDistance(p1, p2);
var dist = geoVecLength(p1, p2);
element.on('mousemove.draw', mousemove);
d3_select(window).on('mouseup.draw', null);
@@ -157,7 +157,7 @@ export function behaviorDraw(context) {
var currSpace = context.mouse();
if (_disableSpace && _lastSpace) {
var dist = geoEuclideanDistance(_lastSpace, currSpace);
var dist = geoVecLength(_lastSpace, currSpace);
if (dist > tolerance) {
_disableSpace = false;
}
+2 -2
View File
@@ -6,7 +6,7 @@ import {
select as d3_select
} from 'd3-selection';
import { geoEuclideanDistance } from '../geo';
import { geoVecLength } from '../geo';
import {
modeBrowse,
@@ -103,7 +103,7 @@ export function behaviorSelect(context) {
if (!p1) return;
var p2 = point();
var dist = geoEuclideanDistance(p1, p2);
var dist = geoVecLength(p1, p2);
p1 = null;
if (dist > tolerance) {
+18 -71
View File
@@ -1,6 +1,15 @@
import _every from 'lodash-es/every';
import _some from 'lodash-es/some';
import {
geoVecAngle,
geoVecCross,
geoVecDot,
geoVecInterp,
geoVecLength,
geoVecSubtract
} from './vector.js';
// constants
var TAU = 2 * Math.PI;
@@ -8,66 +17,6 @@ var EQUATORIAL_RADIUS = 6356752.314245179;
var POLAR_RADIUS = 6378137.0;
// vector addition
export function geoVecEquals(a, b) {
return (a[0] === b[0]) && (a[1] === b[1]);
}
// vector addition
export function geoVecAdd(a, b) {
return [ a[0] + b[0], a[1] + b[1] ];
}
// vector subtraction
export function geoVecSubtract(a, b) {
return [ a[0] - b[0], a[1] - b[1] ];
}
// vector multiplication
export function geoVecScale(a, b) {
return [ a[0] * b, a[1] * b ];
}
// vector rounding (was: geoRoundCoordinates)
export function geoVecFloor(a) {
return [ Math.floor(a[0]), Math.floor(a[1]) ];
}
// linear interpolation
export function geoInterp(p1, p2, t) {
return [
p1[0] + (p2[0] - p1[0]) * t,
p1[1] + (p2[1] - p1[1]) * t
];
}
// dot product
export function geoDot(a, b, origin) {
origin = origin || [0, 0];
return (a[0] - origin[0]) * (b[0] - origin[0]) +
(a[1] - origin[1]) * (b[1] - origin[1]);
}
// 2D cross product of OA and OB vectors, returns magnitude of Z vector
// Returns a positive value, if OAB makes a counter-clockwise turn,
// negative for clockwise turn, and zero if the points are collinear.
export function geoCross(a, b, origin) {
origin = origin || [0, 0];
return (a[0] - origin[0]) * (b[1] - origin[1]) -
(a[1] - origin[1]) * (b[0] - origin[0]);
}
// http://jsperf.com/id-dist-optimization
export function geoEuclideanDistance(a, b) {
var x = a[0] - b[0];
var y = a[1] - b[1];
return Math.sqrt((x * x) + (y * y));
}
export function geoLatToMeters(dLat) {
return dLat * (TAU * POLAR_RADIUS / 360);
}
@@ -140,9 +89,7 @@ export function geoEdgeEqual(a, b) {
// Return the counterclockwise angle in the range (-pi, pi)
// between the positive X axis and the line intersecting a and b.
export function geoAngle(a, b, projection) {
a = projection(a.loc);
b = projection(b.loc);
return Math.atan2(b[1] - a[1], b[0] - a[0]);
return geoVecAngle(projection(a.loc), projection(b.loc));
}
@@ -163,7 +110,7 @@ export function geoRotate(points, angle, around) {
// the closest vertex on that edge. Returns an object with the `index` of the
// chosen edge, the chosen `loc` on that edge, and the `distance` to to it.
export function geoChooseEdge(nodes, point, projection, skipID) {
var dist = geoEuclideanDistance;
var dist = geoVecLength;
var points = nodes.map(function(n) { return projection(n.loc); });
var ids = nodes.map(function(n) { return n.id; });
var min = Infinity;
@@ -176,7 +123,7 @@ export function geoChooseEdge(nodes, point, projection, skipID) {
var o = points[i];
var s = geoVecSubtract(points[i + 1], o);
var v = geoVecSubtract(point, o);
var proj = geoDot(v, s) / geoDot(s, s);
var proj = geoVecDot(v, s) / geoVecDot(s, s);
var p;
if (proj < 0) {
@@ -214,15 +161,15 @@ export function geoLineIntersection(a, b) {
var q2 = [b[1][0], b[1][1]];
var r = geoVecSubtract(p2, p);
var s = geoVecSubtract(q2, q);
var uNumerator = geoCross(geoVecSubtract(q, p), r);
var denominator = geoCross(r, s);
var uNumerator = geoVecCross(geoVecSubtract(q, p), r);
var denominator = geoVecCross(r, s);
if (uNumerator && denominator) {
var u = uNumerator / denominator;
var t = geoCross(geoVecSubtract(q, p), s) / denominator;
var t = geoVecCross(geoVecSubtract(q, p), s) / denominator;
if ((t >= 0) && (t <= 1) && (u >= 0) && (u <= 1)) {
return geoInterp(p, p2, t);
return geoVecInterp(p, p2, t);
}
}
@@ -284,7 +231,7 @@ export function geoPolygonIntersectsPolygon(outer, inner, checkSegments) {
function testSegments(outer, inner) {
for (var i = 0; i < outer.length - 1; i++) {
for (var j = 0; j < inner.length - 1; j++) {
var a = [ outer[i], outer[i +1 ] ];
var a = [ outer[i], outer[i + 1] ];
var b = [ inner[j], inner[j + 1] ];
if (geoLineIntersection(a, b)) return true;
}
@@ -305,7 +252,7 @@ export function geoPolygonIntersectsPolygon(outer, inner, checkSegments) {
export function geoPathLength(path) {
var length = 0;
for (var i = 0; i < path.length - 1; i++) {
length += geoEuclideanDistance(path[i], path[i + 1]);
length += geoVecLength(path[i], path[i + 1]);
}
return length;
}
+10 -9
View File
@@ -1,11 +1,7 @@
export { geoAngle } from './geo.js';
export { geoChooseEdge } from './geo.js';
export { geoCross } from './geo.js';
export { geoDot } from './geo.js';
export { geoEdgeEqual } from './geo.js';
export { geoEuclideanDistance } from './geo.js';
export { geoExtent } from './extent.js';
export { geoInterp } from './geo.js';
export { geoRawMercator } from './raw_mercator.js';
export { geoRotate } from './geo.js';
export { geoLatToMeters } from './geo.js';
@@ -22,10 +18,15 @@ export { geoPolygonContainsPolygon } from './geo.js';
export { geoPolygonIntersectsPolygon } from './geo.js';
export { geoScaleToZoom } from './geo.js';
export { geoSphericalDistance } from './geo.js';
export { geoVecAdd } from './geo.js';
export { geoVecEquals } from './geo.js';
export { geoVecFloor } from './geo.js';
export { geoVecSubtract } from './geo.js';
export { geoVecScale } from './geo.js';
export { geoVecAdd } from './vector.js';
export { geoVecAngle } from './vector.js';
export { geoVecCross } from './vector.js';
export { geoVecDot } from './vector.js';
export { geoVecEqual } from './vector.js';
export { geoVecFloor } from './vector.js';
export { geoVecInterp } from './vector.js';
export { geoVecLength } from './vector.js';
export { geoVecSubtract } from './vector.js';
export { geoVecScale } from './vector.js';
export { geoZoomToScale } from './geo.js';
export { geoViewportEdge } from './geo.js';
+62
View File
@@ -0,0 +1,62 @@
// vector equals
export function geoVecEqual(a, b) {
return (a[0] === b[0]) && (a[1] === b[1]);
}
// vector addition
export function geoVecAdd(a, b) {
return [ a[0] + b[0], a[1] + b[1] ];
}
// vector subtraction
export function geoVecSubtract(a, b) {
return [ a[0] - b[0], a[1] - b[1] ];
}
// vector multiplication
export function geoVecScale(a, b) {
return [ a[0] * b, a[1] * b ];
}
// vector rounding (was: geoRoundCoordinates)
export function geoVecFloor(a) {
return [ Math.floor(a[0]), Math.floor(a[1]) ];
}
// linear interpolation
export function geoVecInterp(a, b, t) {
return [
a[0] + (b[0] - a[0]) * t,
a[1] + (b[1] - a[1]) * t
];
}
// http://jsperf.com/id-dist-optimization
export function geoVecLength(a, b) {
var x = a[0] - b[0];
var y = a[1] - b[1];
return Math.sqrt((x * x) + (y * y));
}
// Return the counterclockwise angle in the range (-pi, pi)
// between the positive X axis and the line intersecting a and b.
export function geoVecAngle(a, b) {
return Math.atan2(b[1] - a[1], b[0] - a[0]);
}
// dot product
export function geoVecDot(a, b, origin) {
origin = origin || [0, 0];
return (a[0] - origin[0]) * (b[0] - origin[0]) +
(a[1] - origin[1]) * (b[1] - origin[1]);
}
// 2D cross product of OA and OB vectors, returns magnitude of Z vector
// Returns a positive value, if OAB makes a counter-clockwise turn,
// negative for clockwise turn, and zero if the points are collinear.
export function geoVecCross(a, b, origin) {
origin = origin || [0, 0];
return (a[0] - origin[0]) * (b[1] - origin[1]) -
(a[1] - origin[1]) * (b[0] - origin[0]);
}
+4
View File
@@ -29,6 +29,10 @@ export { coreDifference as Difference } from './core/difference';
export { coreGraph as Graph } from './core/graph';
export { coreHistory as History } from './core/history';
export { coreTree as Tree } from './core/tree';
export { geoVecCross as geoCross } from './geo/vector';
export { geoVecInterp as geoInterp } from './geo/vector';
export { geoVecFloor as geoRoundCoordinates } from './geo/vector';
export { geoVecLength as geoEuclideanDistance } from './geo/vector';
export { osmEntity as Entity } from './osm/entity';
export { osmNode as Node } from './osm/node';
export { osmRelation as Relation } from './osm/relation';
+3 -3
View File
@@ -21,7 +21,7 @@ import {
import {
geoChooseEdge,
geoLineIntersection,
geoVecEquals,
geoVecEqual,
geoVecSubtract,
geoViewportEdge
} from '../geo';
@@ -203,8 +203,8 @@ export function modeDragNode(context) {
var p = actives[j];
var q = inactives[k];
// skip if segments share an endpoint
if (geoVecEquals(p[1], q[0]) || geoVecEquals(p[0], q[1]) ||
geoVecEquals(p[0], q[0]) || geoVecEquals(p[1], q[1]) ) {
if (geoVecEqual(p[1], q[0]) || geoVecEqual(p[0], q[1]) ||
geoVecEqual(p[0], q[0]) || geoVecEqual(p[1], q[1]) ) {
continue;
} else if (geoLineIntersection(p, q)) {
return true;
+2 -2
View File
@@ -13,7 +13,7 @@ import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js';
import { t } from '../util/locale';
import { actionRotate } from '../actions';
import { behaviorEdit } from '../behavior';
import { geoInterp } from '../geo';
import { geoVecInterp } from '../geo';
import {
modeBrowse,
@@ -80,7 +80,7 @@ export function modeRotate(context, entityIDs) {
if (points.length === 1) { // degenerate case
_pivot = points[0];
} else if (points.length === 2) {
_pivot = geoInterp(points[0], points[1], 0.5);
_pivot = geoVecInterp(points[0], points[1], 0.5);
} else {
_pivot = d3_polygonCentroid(d3_polygonHull(points));
}
+2 -2
View File
@@ -4,7 +4,7 @@ import _uniq from 'lodash-es/uniq';
import { geoArea as d3_geoArea } from 'd3-geo';
import { geoExtent, geoCross } from '../geo';
import { geoExtent, geoVecCross } from '../geo';
import { osmEntity } from './entity';
import { osmLanes } from './lanes';
import { osmOneWayTags } from './tags';
@@ -142,7 +142,7 @@ _extend(osmWay.prototype, {
var o = coords[(i+1) % coords.length];
var a = coords[i];
var b = coords[(i+2) % coords.length];
var res = geoCross(a, b, o);
var res = geoVecCross(a, b, o);
curr = (res > 0) ? 1 : (res < 0) ? -1 : 0;
if (curr === 0) {
+2 -2
View File
@@ -2,7 +2,7 @@ import { select as d3_select } from 'd3-selection';
import { t } from '../util/locale';
import { d3geoTile as d3_geoTile } from '../lib/d3.geo.tile';
import { geoEuclideanDistance, geoScaleToZoom } from '../geo';
import { geoScaleToZoom, geoVecLength } from '../geo';
import { utilPrefixCSSProperty } from '../util';
@@ -187,7 +187,7 @@ export function rendererTileLayer(context) {
requests.forEach(function(d) {
var c = tileCenter(d);
var dist = geoEuclideanDistance(c, mapCenter);
var dist = geoVecLength(c, mapCenter);
if (dist < minDist) {
minDist = dist;
nearCenter = d;
+2 -2
View File
@@ -6,7 +6,7 @@ import {
geoStream as d3_geoStream
} from 'd3-geo';
import { geoEuclideanDistance } from '../geo';
import { geoVecLength } from '../geo';
// Touch targets control which other vertices we can drag a vertex onto.
@@ -82,7 +82,7 @@ export function svgOneWaySegments(projection, graph, dt) {
b = [x, y];
if (a) {
var span = geoEuclideanDistance(a, b) - offset;
var span = geoVecLength(a, b) - offset;
if (span >= 0) {
var angle = Math.atan2(b[1] - a[1], b[0] - a[0]);
+6 -6
View File
@@ -10,11 +10,11 @@ import { textDirection } from '../util/locale';
import {
geoExtent,
geoEuclideanDistance,
geoInterp,
geoPolygonIntersectsPolygon,
geoPathLength,
geoScaleToZoom
geoScaleToZoom,
geoVecInterp,
geoVecLength
} from '../geo';
import { osmEntity } from '../osm';
@@ -489,10 +489,10 @@ export function svgLabels(projection, context) {
var b = sub[j + 1];
// split up the text into small collision boxes
var num = Math.max(1, Math.floor(geoEuclideanDistance(a, b) / boxsize / 2));
var num = Math.max(1, Math.floor(geoVecLength(a, b) / boxsize / 2));
for (var box = 0; box < num; box++) {
var p = geoInterp(a, b, box / num);
var p = geoVecInterp(a, b, box / num);
var x0 = p[0] - boxsize - padding;
var y0 = p[1] - boxsize - padding;
var x1 = p[0] + boxsize + padding;
@@ -532,7 +532,7 @@ export function svgLabels(projection, context) {
for (var i = 0; i < points.length - 1; i++) {
var a = points[i];
var b = points[i + 1];
var current = geoEuclideanDistance(a, b);
var current = geoVecLength(a, b);
var portion;
if (!start && sofar + current >= from) {
portion = (from - sofar) / current;
+7 -7
View File
@@ -7,9 +7,9 @@ import {
import {
geoAngle,
geoEuclideanDistance,
geoInterp,
geoLineIntersection
geoLineIntersection,
geoVecInterp,
geoVecLength
} from '../geo';
@@ -73,8 +73,8 @@ export function svgMidpoints(projection, context) {
if (midpoints[id]) {
midpoints[id].parents.push(entity);
} else {
if (geoEuclideanDistance(projection(a.loc), projection(b.loc)) > 40) {
var point = geoInterp(a.loc, b.loc, 0.5);
if (geoVecLength(projection(a.loc), projection(b.loc)) > 40) {
var point = geoVecInterp(a.loc, b.loc, 0.5);
var loc = null;
if (extent.intersects(point)) {
@@ -83,8 +83,8 @@ export function svgMidpoints(projection, context) {
for (var k = 0; k < 4; k++) {
point = geoLineIntersection([a.loc, b.loc], [poly[k], poly[k + 1]]);
if (point &&
geoEuclideanDistance(projection(a.loc), projection(point)) > 20 &&
geoEuclideanDistance(projection(b.loc), projection(point)) > 20)
geoVecLength(projection(a.loc), projection(point)) > 20 &&
geoVecLength(projection(b.loc), projection(point)) > 20)
{
loc = point;
break;
+1
View File
@@ -74,6 +74,7 @@
<script src='spec/geo/extent.js'></script>
<script src='spec/geo/geo.js'></script>
<script src='spec/geo/vector.js'></script>
<script src='spec/lib/d3.combobox.js'></script>
<script src='spec/lib/d3.keybinding.js'></script>
+4 -4
View File
@@ -5,7 +5,7 @@ describe('iD.actionCircularize', function () {
var points = graph.childNodes(graph.entity(id))
.map(function (n) { return projection(n.loc); }),
centroid = d3.polygonCentroid(points),
radius = iD.geoEuclideanDistance(centroid, points[0]),
radius = iD.geoVecLength(centroid, points[0]),
estArea = Math.PI * radius * radius,
trueArea = Math.abs(d3.polygonArea(points)),
pctDiff = (estArea - trueArea) / estArea;
@@ -31,10 +31,10 @@ describe('iD.actionCircularize', function () {
vector2 = [point2[0] - center[0], point2[1] - center[1]],
distance;
distance = iD.geoEuclideanDistance(vector1, [0, 0]);
distance = iD.geoVecLength(vector1, [0, 0]);
vector1 = [vector1[0] / distance, vector1[1] / distance];
distance = iD.geoEuclideanDistance(vector2, [0, 0]);
distance = iD.geoVecLength(vector2, [0, 0]);
vector2 = [vector2[0] / distance, vector2[1] / distance];
return 180 / Math.PI * Math.acos(vector1[0] * vector2[0] + vector1[1] * vector2[1]);
@@ -106,7 +106,7 @@ describe('iD.actionCircularize', function () {
graph = iD.actionCircularize('-', projection)(graph);
expect(isCircular('-', graph)).to.be.ok;
expect(iD.geoEuclideanDistance(graph.entity('d').loc, [2, -2])).to.be.lt(0.5);
expect(iD.geoVecLength(graph.entity('d').loc, [2, -2])).to.be.lt(0.5);
});
it('creates circle respecting min-angle limit', function() {
-103
View File
@@ -1,108 +1,5 @@
describe('iD.geo', function() {
describe('geoVecEquals', function() {
it('tests vectors for equality', function() {
expect(iD.geoVecEquals([1, 2], [1, 2])).to.be.true;
expect(iD.geoVecEquals([1, 2], [1, 0])).to.be.false;
expect(iD.geoVecEquals([1, 2], [2, 1])).to.be.false;
});
});
describe('geoVecAdd', function() {
it('adds vectors', function() {
expect(iD.geoVecAdd([1, 2], [3, 4])).to.eql([4, 6]);
expect(iD.geoVecAdd([1, 2], [0, 0])).to.eql([1, 2]);
expect(iD.geoVecAdd([1, 2], [-3, -4])).to.eql([-2, -2]);
});
});
describe('geoVecSubtract', function() {
it('subtracts vectors', function() {
expect(iD.geoVecSubtract([1, 2], [3, 4])).to.eql([-2, -2]);
expect(iD.geoVecSubtract([1, 2], [0, 0])).to.eql([1, 2]);
expect(iD.geoVecSubtract([1, 2], [-3, -4])).to.eql([4, 6]);
});
});
describe('geoVecScale', function() {
it('multiplies vectors', function() {
expect(iD.geoVecScale([1, 2], 0)).to.eql([0, 0]);
expect(iD.geoVecScale([1, 2], 1)).to.eql([1, 2]);
expect(iD.geoVecScale([1, 2], 2)).to.eql([2, 4]);
expect(iD.geoVecScale([1, 2], 0.5)).to.eql([0.5, 1]);
});
});
describe('geoVecFloor (was: geoRoundCoordinates)', function() {
it('rounds vectors', function() {
expect(iD.geoVecFloor([0.1, 1])).to.eql([0, 1]);
expect(iD.geoVecFloor([0, 1])).to.eql([0, 1]);
expect(iD.geoVecFloor([0, 1.1])).to.eql([0, 1]);
});
});
describe('geoInterp', function() {
it('interpolates halfway', function() {
var a = [0, 0];
var b = [10, 10];
expect(iD.geoInterp(a, b, 0.5)).to.eql([5, 5]);
});
it('interpolates to one side', function() {
var a = [0, 0];
var b = [10, 10];
expect(iD.geoInterp(a, b, 0)).to.eql([0, 0]);
});
});
describe('geoDot', function() {
it('dot product of right angle is zero', function() {
var a = [1, 0];
var b = [0, 1];
expect(iD.geoDot(a, b)).to.eql(0);
});
it('dot product of same vector multiplies', function() {
var a = [2, 0];
var b = [2, 0];
expect(iD.geoDot(a, b)).to.eql(4);
});
});
describe('geoCross', function() {
it('2D cross product of right hand turn is positive', function() {
var a = [2, 0];
var b = [0, 2];
expect(iD.geoCross(a, b)).to.eql(4);
});
it('2D cross product of left hand turn is negative', function() {
var a = [2, 0];
var b = [0, -2];
expect(iD.geoCross(a, b)).to.eql(-4);
});
it('2D cross product of colinear points is zero', function() {
var a = [-2, 0];
var b = [2, 0];
expect(iD.geoCross(a, b)).to.equal(0);
});
});
describe('geoEuclideanDistance', function() {
it('distance between two same points is zero', function() {
var a = [0, 0];
var b = [0, 0];
expect(iD.geoEuclideanDistance(a, b)).to.eql(0);
});
it('a straight 10 unit line is 10', function() {
var a = [0, 0];
var b = [10, 0];
expect(iD.geoEuclideanDistance(a, b)).to.eql(10);
});
it('a pythagorean triangle is right', function() {
var a = [0, 0];
var b = [4, 3];
expect(iD.geoEuclideanDistance(a, b)).to.eql(5);
});
});
describe('geoLatToMeters', function() {
it('0 degrees latitude is 0 meters', function() {
expect(iD.geoLatToMeters(0)).to.eql(0);
+115
View File
@@ -0,0 +1,115 @@
describe('iD.geo vector', function() {
describe('geoVecEqual', function() {
it('tests vectors for equality', function() {
expect(iD.geoVecEqual([1, 2], [1, 2])).to.be.true;
expect(iD.geoVecEqual([1, 2], [1, 0])).to.be.false;
expect(iD.geoVecEqual([1, 2], [2, 1])).to.be.false;
});
});
describe('geoVecAdd', function() {
it('adds vectors', function() {
expect(iD.geoVecAdd([1, 2], [3, 4])).to.eql([4, 6]);
expect(iD.geoVecAdd([1, 2], [0, 0])).to.eql([1, 2]);
expect(iD.geoVecAdd([1, 2], [-3, -4])).to.eql([-2, -2]);
});
});
describe('geoVecSubtract', function() {
it('subtracts vectors', function() {
expect(iD.geoVecSubtract([1, 2], [3, 4])).to.eql([-2, -2]);
expect(iD.geoVecSubtract([1, 2], [0, 0])).to.eql([1, 2]);
expect(iD.geoVecSubtract([1, 2], [-3, -4])).to.eql([4, 6]);
});
});
describe('geoVecScale', function() {
it('multiplies vectors', function() {
expect(iD.geoVecScale([1, 2], 0)).to.eql([0, 0]);
expect(iD.geoVecScale([1, 2], 1)).to.eql([1, 2]);
expect(iD.geoVecScale([1, 2], 2)).to.eql([2, 4]);
expect(iD.geoVecScale([1, 2], 0.5)).to.eql([0.5, 1]);
});
});
describe('geoVecFloor (was: geoRoundCoordinates)', function() {
it('rounds vectors', function() {
expect(iD.geoVecFloor([0.1, 1])).to.eql([0, 1]);
expect(iD.geoVecFloor([0, 1])).to.eql([0, 1]);
expect(iD.geoVecFloor([0, 1.1])).to.eql([0, 1]);
});
});
describe('geoVecInterp', function() {
it('interpolates halfway', function() {
var a = [0, 0];
var b = [10, 10];
expect(iD.geoVecInterp(a, b, 0.5)).to.eql([5, 5]);
});
it('interpolates to one side', function() {
var a = [0, 0];
var b = [10, 10];
expect(iD.geoVecInterp(a, b, 0)).to.eql([0, 0]);
});
});
describe('geoVecLength (was: geoEuclideanDistance)', function() {
it('distance between two same points is zero', function() {
var a = [0, 0];
var b = [0, 0];
expect(iD.geoVecLength(a, b)).to.eql(0);
});
it('a straight 10 unit line is 10', function() {
var a = [0, 0];
var b = [10, 0];
expect(iD.geoVecLength(a, b)).to.eql(10);
});
it('a pythagorean triangle is right', function() {
var a = [0, 0];
var b = [4, 3];
expect(iD.geoVecLength(a, b)).to.eql(5);
});
});
describe('geoVecAngle', function() {
it('returns angle between a and b', function() {
expect(iD.geoVecAngle([0, 0], [1, 0])).to.be.closeTo(0, 1e-6);
expect(iD.geoVecAngle([0, 0], [0, 1])).to.be.closeTo(Math.PI / 2, 1e-6);
expect(iD.geoVecAngle([0, 0], [-1, 0])).to.be.closeTo(Math.PI, 1e-6);
expect(iD.geoVecAngle([0, 0], [0, -1])).to.be.closeTo(-Math.PI / 2, 1e-6);
});
});
describe('geoVecDot', function() {
it('dot product of right angle is zero', function() {
var a = [1, 0];
var b = [0, 1];
expect(iD.geoVecDot(a, b)).to.eql(0);
});
it('dot product of same vector multiplies', function() {
var a = [2, 0];
var b = [2, 0];
expect(iD.geoVecDot(a, b)).to.eql(4);
});
});
describe('geoVecCross', function() {
it('2D cross product of right hand turn is positive', function() {
var a = [2, 0];
var b = [0, 2];
expect(iD.geoVecCross(a, b)).to.eql(4);
});
it('2D cross product of left hand turn is negative', function() {
var a = [2, 0];
var b = [0, -2];
expect(iD.geoVecCross(a, b)).to.eql(-4);
});
it('2D cross product of colinear points is zero', function() {
var a = [-2, 0];
var b = [2, 0];
expect(iD.geoVecCross(a, b)).to.equal(0);
});
});
});