From 6f0f42040ce765ad63b420ae81dff402ac9f807f Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Mon, 20 Jan 2014 09:24:41 +0100 Subject: [PATCH] Linearly interpolate extra dimensions in ol.geom.flat.closestPoint --- src/ol/geom/closestgeom.js | 30 ++++++++++-------- src/ol/geom/flatgeom.js | 45 +++++++++++++++++---------- test/spec/ol/geom/closestgeom.test.js | 27 ++++++++++++++++ 3 files changed, 72 insertions(+), 30 deletions(-) diff --git a/src/ol/geom/closestgeom.js b/src/ol/geom/closestgeom.js index 5fa7712359..0084a0d6f6 100644 --- a/src/ol/geom/closestgeom.js +++ b/src/ol/geom/closestgeom.js @@ -95,14 +95,16 @@ ol.geom.closest.getClosestPoint = function(flatCoordinates, offset, end, stride, if (offset == end) { return minSquaredDistance; } - var squaredDistance; + var i, squaredDistance; if (maxDelta === 0) { // All points are identical, so just test the first point. squaredDistance = ol.geom.flat.squaredDistance( x, y, flatCoordinates[offset], flatCoordinates[offset + 1]); if (squaredDistance < minSquaredDistance) { - closestPoint[0] = flatCoordinates[offset]; - closestPoint[1] = flatCoordinates[offset + 1]; + for (i = 0; i < stride; ++i) { + closestPoint[i] = flatCoordinates[offset + i]; + } + closestPoint.length = stride; return squaredDistance; } else { return minSquaredDistance; @@ -112,15 +114,16 @@ ol.geom.closest.getClosestPoint = function(flatCoordinates, offset, end, stride, var tmpPoint = goog.isDef(opt_tmpPoint) ? opt_tmpPoint : [NaN, NaN]; var index = offset + stride; while (index < end) { - ol.geom.flat.closestPoint(x, y, - flatCoordinates[index - stride], flatCoordinates[index - stride + 1], - flatCoordinates[index], flatCoordinates[index + 1], tmpPoint); + ol.geom.flat.closestPoint( + flatCoordinates, index - stride, index, stride, x, y, tmpPoint); squaredDistance = ol.geom.flat.squaredDistance( x, y, tmpPoint[0], tmpPoint[1]); if (squaredDistance < minSquaredDistance) { minSquaredDistance = squaredDistance; - closestPoint[0] = tmpPoint[0]; - closestPoint[1] = tmpPoint[1]; + for (i = 0; i < stride; ++i) { + closestPoint[i] = tmpPoint[i]; + } + closestPoint.length = stride; index += stride; } else { // Skip ahead multiple points, because we know that all the skipped @@ -140,15 +143,16 @@ ol.geom.closest.getClosestPoint = function(flatCoordinates, offset, end, stride, } if (isRing) { // Check the closing segment. - ol.geom.flat.closestPoint(x, y, - flatCoordinates[end - stride], flatCoordinates[end - stride + 1], - flatCoordinates[offset], flatCoordinates[offset + 1], tmpPoint); + ol.geom.flat.closestPoint( + flatCoordinates, end - stride, offset, stride, x, y, tmpPoint); squaredDistance = ol.geom.flat.squaredDistance( x, y, tmpPoint[0], tmpPoint[1]); if (squaredDistance < minSquaredDistance) { minSquaredDistance = squaredDistance; - closestPoint[0] = tmpPoint[0]; - closestPoint[1] = tmpPoint[1]; + for (i = 0; i < stride; ++i) { + closestPoint[i] = tmpPoint[i]; + } + closestPoint.length = stride; } } return minSquaredDistance; diff --git a/src/ol/geom/flatgeom.js b/src/ol/geom/flatgeom.js index d6ba281505..88635b9100 100644 --- a/src/ol/geom/flatgeom.js +++ b/src/ol/geom/flatgeom.js @@ -2,39 +2,50 @@ goog.provide('ol.geom.flat'); goog.require('goog.array'); goog.require('goog.asserts'); +goog.require('goog.math'); goog.require('goog.vec.Mat4'); /** - * Returns the point on the line segment (x1, y1) to (x2, y2) that is closest to - * the point (x, y). + * Returns the point on the 2D line segment flatCoordinates[offset1] to + * flatCoordinates[offset2] that is closest to the point (x, y). Extra + * dimensions are linearly interpolated. + * @param {Array.} flatCoordinates Flat coordinates. + * @param {number} offset1 Offset 1. + * @param {number} offset2 Offset 2. + * @param {number} stride Stride. * @param {number} x X. * @param {number} y Y. - * @param {number} x1 X1. - * @param {number} y1 Y1. - * @param {number} x2 X2. - * @param {number} y2 Y2. * @param {Array.} closestPoint Closest point. */ -ol.geom.flat.closestPoint = function(x, y, x1, y1, x2, y2, closestPoint) { - var dx = x2 - x1; - var dy = y2 - y1; +ol.geom.flat.closestPoint = + function(flatCoordinates, offset1, offset2, stride, x, y, closestPoint) { + var x1 = flatCoordinates[offset1]; + var y1 = flatCoordinates[offset1 + 1]; + var dx = flatCoordinates[offset2] - x1; + var dy = flatCoordinates[offset2 + 1] - y1; + var i, offset; if (dx === 0 && dy === 0) { - closestPoint[0] = x1; - closestPoint[1] = y1; + offset = offset1; } else { var t = ((x - x1) * dx + (y - y1) * dy) / (dx * dx + dy * dy); if (t > 1) { - closestPoint[0] = x2; - closestPoint[1] = y2; + offset = offset2; } else if (t > 0) { - closestPoint[0] = x1 + dx * t; - closestPoint[1] = y1 + dy * t; + for (i = 0; i < stride; ++i) { + closestPoint[i] = goog.math.lerp(flatCoordinates[offset1 + i], + flatCoordinates[offset2 + i], t); + } + closestPoint.length = stride; + return; } else { - closestPoint[0] = x1; - closestPoint[1] = y1; + offset = offset1; } } + for (i = 0; i < stride; ++i) { + closestPoint[i] = flatCoordinates[offset + i]; + } + closestPoint.length = stride; }; diff --git a/test/spec/ol/geom/closestgeom.test.js b/test/spec/ol/geom/closestgeom.test.js index 102f924e4e..124b5afde6 100644 --- a/test/spec/ol/geom/closestgeom.test.js +++ b/test/spec/ol/geom/closestgeom.test.js @@ -113,6 +113,33 @@ describe('ol.geom.closest', function() { }); + describe('with multi-dimensional data', function() { + + var flatCoordinates = [0, 0, 10, -10, 2, 2, 30, -20]; + var stride = 4; + + describe('ol.geom.closest.getClosestPoint', function() { + + it('interpolates M coordinates', function() { + var maxDelta = Math.sqrt(ol.geom.closest.getMaxSquaredDelta( + flatCoordinates, 0, flatCoordinates.length, stride, 0)); + expect(maxDelta).to.roughlyEqual(Math.sqrt(8), 1e-9); + var closestPoint = [NaN, NaN]; + expect(ol.geom.closest.getClosestPoint( + flatCoordinates, 0, flatCoordinates.length, stride, + maxDelta, false, 1, 1, closestPoint, Infinity)). + to.roughlyEqual(0, 1e-9); + expect(closestPoint).to.have.length(stride); + expect(closestPoint[0]).to.be(1); + expect(closestPoint[1]).to.be(1); + expect(closestPoint[2]).to.be(20); + expect(closestPoint[3]).to.be(-15); + }); + + }); + + }); + });