From c3ac083b321d8c106a10c4985bd483f15b88b9cd Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Mon, 25 Feb 2019 21:15:20 -0500 Subject: [PATCH] Reuse math code, add geoVecNormalize, code cleanups --- modules/actions/orthogonalize.js | 104 +++++++++++++------------------ modules/geo/index.js | 1 + modules/geo/vector.js | 10 +++ modules/ui/intro/helper.js | 32 +++------- 4 files changed, 62 insertions(+), 85 deletions(-) diff --git a/modules/actions/orthogonalize.js b/modules/actions/orthogonalize.js index b9d64b205..60300896e 100644 --- a/modules/actions/orthogonalize.js +++ b/modules/actions/orthogonalize.js @@ -2,33 +2,41 @@ import _clone from 'lodash-es/clone'; import _uniq from 'lodash-es/uniq'; import { actionDeleteNode } from './delete_node'; -import { geoVecInterp, geoVecLength } from '../geo'; +import { + geoVecAdd, + geoVecDot, + geoVecInterp, + geoVecLength, + geoVecNormalize, + geoVecScale, + geoVecSubtract +} from '../geo'; /* * Based on https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/potlatch2/tools/Quadrilateralise.as */ export function actionOrthogonalize(wayId, projection) { - var threshold = 12, // degrees within right or straight to alter - lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180), - upperThreshold = Math.cos(threshold * Math.PI / 180); + var threshold = 12; // degrees within right or straight to alter + var lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180); + var upperThreshold = Math.cos(threshold * Math.PI / 180); 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, - node, loc, score, motions, i, j; + var way = graph.entity(wayId); + var nodes = graph.childNodes(way); + var points = _uniq(nodes).map(function(n) { return projection(n.loc); }); + var corner = {i: 0, dotp: 1}; + var epsilon = 1e-4; + var node, loc, score, motions, i, j; 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] = geoVecAdd(points[corner.i], motions[corner.i]); score = corner.dotp; if (score < epsilon) { break; @@ -40,14 +48,14 @@ export function actionOrthogonalize(wayId, projection) { graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t))); } else { - var best, - originalPoints = _clone(points); + var best; + var originalPoints = _clone(points); score = Infinity; for (i = 0; i < 1000; i++) { motions = points.map(calcMotion); for (j = 0; j < motions.length; j++) { - points[j] = addPoints(points[j],motions[j]); + points[j] = geoVecAdd(points[j],motions[j]); } var newScore = squareness(points); if (newScore < score) { @@ -91,15 +99,15 @@ export function actionOrthogonalize(wayId, projection) { function calcMotion(b, i, array) { - var a = array[(i - 1 + array.length) % array.length], - c = array[(i + 1) % array.length], - p = subtractPoints(a, b), - q = subtractPoints(c, b), - scale, dotp; + var a = array[(i - 1 + array.length) % array.length]; + var c = array[(i + 1) % array.length]; + var p = geoVecSubtract(a, b); + var q = geoVecSubtract(c, b); + var scale, dotp; - scale = 2 * Math.min(geoVecLength(p, [0, 0]), geoVecLength(q, [0, 0])); - p = normalizePoint(p, 1.0); - q = normalizePoint(q, 1.0); + scale = 2 * Math.min(geoVecLength(p), geoVecLength(q)); + p = geoVecNormalize(p); + q = geoVecNormalize(q); dotp = filterDotProduct(p[0] * q[0] + p[1] * q[1]); @@ -113,7 +121,9 @@ export function actionOrthogonalize(wayId, projection) { corner.dotp = Math.abs(dotp); } - return normalizePoint(addPoints(p, q), 0.1 * dotp * scale); + var vec = geoVecNormalize(geoVecAdd(p, q)); + return geoVecScale(vec, 0.1 * dotp * scale); + } }; @@ -121,7 +131,6 @@ export function actionOrthogonalize(wayId, projection) { function squareness(points) { return points.reduce(function(sum, val, i, array) { var dotp = normalizedDotProduct(i, array); - dotp = filterDotProduct(dotp); return sum + 2.0 * Math.min(Math.abs(dotp - 1.0), Math.min(Math.abs(dotp), Math.abs(dotp + 1))); }, 0); @@ -129,41 +138,12 @@ export function actionOrthogonalize(wayId, projection) { function normalizedDotProduct(i, points) { - var a = points[(i - 1 + points.length) % points.length], - b = points[i], - c = points[(i + 1) % points.length], - p = subtractPoints(a, b), - q = subtractPoints(c, b); - - p = normalizePoint(p, 1.0); - q = normalizePoint(q, 1.0); - - return p[0] * q[0] + p[1] * q[1]; - } - - - function subtractPoints(a, b) { - return [a[0] - b[0], a[1] - b[1]]; - } - - - function addPoints(a, b) { - return [a[0] + b[0], a[1] + b[1]]; - } - - - function normalizePoint(point, scale) { - var vector = [0, 0]; - var length = Math.sqrt(point[0] * point[0] + point[1] * point[1]); - if (length !== 0) { - vector[0] = point[0] / length; - vector[1] = point[1] / length; - } - - vector[0] *= scale; - vector[1] *= scale; - - return vector; + var a = points[(i - 1 + points.length) % points.length]; + var b = points[i]; + var c = points[(i + 1) % points.length]; + var p = geoVecNormalize(geoVecSubtract(a, b)); + var q = geoVecNormalize(geoVecSubtract(c, b)); + return geoVecDot(p, q); } @@ -177,9 +157,9 @@ export function actionOrthogonalize(wayId, projection) { action.disabled = function(graph) { - var way = graph.entity(wayId), - nodes = graph.childNodes(way), - points = _uniq(nodes).map(function(n) { return projection(n.loc); }); + var way = graph.entity(wayId); + var nodes = graph.childNodes(way); + var points = _uniq(nodes).map(function(n) { return projection(n.loc); }); if (squareness(points)) { return false; diff --git a/modules/geo/index.js b/modules/geo/index.js index e27f00cfc..04794254c 100644 --- a/modules/geo/index.js +++ b/modules/geo/index.js @@ -36,5 +36,6 @@ export { geoVecEqual } from './vector.js'; export { geoVecFloor } from './vector.js'; export { geoVecInterp } from './vector.js'; export { geoVecLength } from './vector.js'; +export { geoVecNormalize } from './vector.js'; export { geoVecSubtract } from './vector.js'; export { geoVecScale } from './vector.js'; diff --git a/modules/geo/vector.js b/modules/geo/vector.js index e67a6d176..cbdefab6c 100644 --- a/modules/geo/vector.js +++ b/modules/geo/vector.js @@ -37,11 +37,21 @@ export function geoVecInterp(a, b, t) { // http://jsperf.com/id-dist-optimization export function geoVecLength(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)); } +// get a unit vector +export function geoVecNormalize(a) { + var length = Math.sqrt((a[0] * a[0]) + (a[1] * a[1])); + if (length !== 0) { + return geoVecScale(a, 1 / length); + } + return [0, 0]; +} + // 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) { diff --git a/modules/ui/intro/helper.js b/modules/ui/intro/helper.js index de261ea1a..14f3f843f 100644 --- a/modules/ui/intro/helper.js +++ b/modules/ui/intro/helper.js @@ -1,7 +1,12 @@ import { select as d3_select } from 'd3-selection'; import { t } from '../../util/locale'; -import { geoSphericalDistance } from '../../geo'; +import { + geoSphericalDistance, + geoVecDot, + geoVecNormalize, + geoVecSubtract +} from '../../geo'; export function pointBox(loc, context) { @@ -133,28 +138,9 @@ export function isMostlySquare(points) { var a = points[(i - 1 + points.length) % points.length]; var b = points[i]; var c = points[(i + 1) % points.length]; - var p = subtractPoints(a, b); - var q = subtractPoints(c, b); - - p = normalizePoint(p); - q = normalizePoint(q); - - return p[0] * q[0] + p[1] * q[1]; - - - function subtractPoints(a, b) { - return [a[0] - b[0], a[1] - b[1]]; - } - - function normalizePoint(point) { - var vector = [0, 0]; - var length = Math.sqrt(point[0] * point[0] + point[1] * point[1]); - if (length !== 0) { - vector[0] = point[0] / length; - vector[1] = point[1] / length; - } - return vector; - } + var p = geoVecNormalize(geoVecSubtract(a, b)); + var q = geoVecNormalize(geoVecSubtract(c, b)); + return geoVecDot(p, q); } }