Skip to content

Commit

Permalink
fix #2964: distance from point to line (#2965)
Browse files Browse the repository at this point in the history
some additional unit tests, types and doc fix for distance function

Co-authored-by: Jos de Jong <wjosdejong@gmail.com>
  • Loading branch information
Kiku-CN and josdejong committed Jun 13, 2023
1 parent 6fa5890 commit 1b06ec4
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 27 deletions.
35 changes: 17 additions & 18 deletions src/function/geometry/distance.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ const dependencies = [
'subtract',
'divideScalar',
'multiplyScalar',
'unaryMinus',
'deepEqual',
'sqrt',
'abs'
]

export const createDistance = /* #__PURE__ */ factory(name, dependencies, ({ typed, addScalar, subtract, multiplyScalar, divideScalar, unaryMinus, sqrt, abs }) => {
export const createDistance = /* #__PURE__ */ factory(name, dependencies, ({ typed, addScalar, subtract, multiplyScalar, divideScalar, deepEqual, sqrt, abs }) => {
/**
* Calculates:
* The eucledian distance between two points in N-dimensional spaces.
Expand All @@ -32,10 +32,10 @@ export const createDistance = /* #__PURE__ */ factory(name, dependencies, ({ typ
* math.distance([x1,y1,z1,a1], [x2,y2,z2,a2])
* math.distance([[x1,y1], [x2,y2], [x3,y3]])
* math.distance([[x1,y1,z1], [x2,y2,z2], [x3,y3,z3]])
* math.distance([x1,y1], [x2,y2,z2])
* math.distance([x1,y1], [x2,y2], [x3,y3])
* math.distance([pointX,pointY], [a,b,c])
* math.distance([pointX,pointY], [lineOnePtX,lineOnePtY], [lineTwoPtX,lineTwoPtY])
* math.distance({pointX, pointY}, {lineOnePtX, lineOnePtY}, {lineTwoPtX, lineTwoPtY})
* math.distance([x1,y1,z1], [x0, y0, z0, a, b, c])
* math.distance([pointX,pointY,pointZ], [x0, y0, z0, a, b, c])
* math.distance({pointX, pointY, pointZ}, {x0, y0, z0, a, b, c})
*
* Examples:
Expand All @@ -51,11 +51,11 @@ export const createDistance = /* #__PURE__ */ factory(name, dependencies, ({ typ
* math.distance([[1, 2], [1, 2], [1, 3]]) // Returns [0, 1, 1]
* math.distance([[1,2,4], [1,2,6], [8,1,3]]) // Returns [2, 7.14142842854285, 7.681145747868608]
* math.distance([10, 10], [8, 1, 3]) // Returns 11.535230316796387
* math.distance([10, 10], [2, 3], [-8, 0]) // Returns 8.759953130362847
* math.distance([0, 0], [3, 0], [0, 4]) // Returns 2.4
* math.distance(
* {pointX: 1, pointY: 4},
* {lineOnePtX: 6, lineOnePtY: 3},
* {lineTwoPtX: 2, lineTwoPtY: 8}) // Returns 2.720549372624744
* {pointX: 0, pointY: 0},
* {lineOnePtX: 3, lineOnePtY: 0},
* {lineTwoPtX: 0, lineTwoPtY: 4}) // Returns 2.4
* math.distance([2, 3, 1], [1, 1, 2, 5, 0, 1]) // Returns 2.3204774044612857
* math.distance(
* {pointX: 2, pointY: 3, pointZ: 1},
Expand All @@ -72,10 +72,10 @@ export const createDistance = /* #__PURE__ */ factory(name, dependencies, ({ typ
if (!_2d(x)) { throw new TypeError('Array with 2 numbers or BigNumbers expected for first argument') }
if (!_2d(y)) { throw new TypeError('Array with 2 numbers or BigNumbers expected for second argument') }
if (!_2d(z)) { throw new TypeError('Array with 2 numbers or BigNumbers expected for third argument') }
const m = divideScalar(subtract(z[1], z[0]), subtract(y[1], y[0]))
const xCoeff = multiplyScalar(multiplyScalar(m, m), y[0])
const yCoeff = unaryMinus(multiplyScalar(m, y[0]))
const constant = x[1]
if (deepEqual(y, z)) { throw new TypeError('LinePoint1 should not be same with LinePoint2') }
const xCoeff = subtract(z[1], y[1])
const yCoeff = subtract(y[0], z[0])
const constant = subtract(multiplyScalar(z[0], y[1]), multiplyScalar(y[0], z[1]))

return _distancePointLine2D(x[0], x[1], xCoeff, yCoeff, constant)
} else {
Expand All @@ -87,13 +87,12 @@ export const createDistance = /* #__PURE__ */ factory(name, dependencies, ({ typ
if (!_2d(x)) { throw new TypeError('Values of pointX and pointY should be numbers or BigNumbers') }
if (!_2d(y)) { throw new TypeError('Values of lineOnePtX and lineOnePtY should be numbers or BigNumbers') }
if (!_2d(z)) { throw new TypeError('Values of lineTwoPtX and lineTwoPtY should be numbers or BigNumbers') }
if (deepEqual(_objectToArray(y), _objectToArray(z))) { throw new TypeError('LinePoint1 should not be same with LinePoint2') }
if ('pointX' in x && 'pointY' in x && 'lineOnePtX' in y &&
'lineOnePtY' in y && 'lineTwoPtX' in z && 'lineTwoPtY' in z) {
const m = divideScalar(subtract(z.lineTwoPtY, z.lineTwoPtX), subtract(y.lineOnePtY, y.lineOnePtX))
const xCoeff = multiplyScalar(multiplyScalar(m, m), y.lineOnePtX)
const yCoeff = unaryMinus(multiplyScalar(m, y.lineOnePtX))
const constant = x.pointX

const xCoeff = subtract(z.lineTwoPtY, y.lineOnePtY)
const yCoeff = subtract(y.lineOnePtX, z.lineTwoPtX)
const constant = subtract(multiplyScalar(z.lineTwoPtX, y.lineOnePtY), multiplyScalar(y.lineOnePtX, z.lineTwoPtY))
return _distancePointLine2D(x.pointX, x.pointY, xCoeff, yCoeff, constant)
} else {
throw new TypeError('Key names do not match')
Expand Down
23 changes: 16 additions & 7 deletions test/unit-tests/function/geometry/distance.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ describe('distance', function () {
})

it('should calculate distance for inputs passed as objects', function () {
assert.deepStrictEqual(math.distance({ pointX: 1, pointY: 4 }, { lineOnePtX: 6, lineOnePtY: 3 }, { lineTwoPtX: 2, lineTwoPtY: 8 }), 2.720549372624744)
assert.deepStrictEqual(math.distance({ pointX: 0, pointY: 0 }, { lineOnePtX: 3, lineOnePtY: 0 }, { lineTwoPtX: 0, lineTwoPtY: 4 }), 2.4)
assert.deepStrictEqual(math.distance({ pointX: 10, pointY: 10 }, { xCoeffLine: 8, yCoeffLine: 1, constant: 3 }), 11.535230316796387)
assert.strictEqual(math.distance({ pointOneX: 0, pointOneY: 0 }, { pointTwoX: 10, pointTwoY: 10 }), 14.142135623730951)
assert.throws(function () { math.distance({ pointX: 1, pointY: 4 }, { lineOnePtX: 'l', lineOnePtY: 3 }, { lineTwoPtX: 2, lineTwoPtY: 8 }) }, TypeError)
Expand All @@ -53,6 +53,9 @@ describe('distance', function () {
assert.throws(function () { math.distance(['2', '3'], math.matrix([1, 2]), [1, 2]) }, TypeError)
assert.throws(function () { math.distance([2, 3], math.matrix(['a', 'b']), [1, 2]) }, TypeError)
assert.throws(function () { math.distance([2, 3], math.matrix([1, 2]), ['a', 'b']) }, TypeError)
assert.throws(function () { math.distance(math.matrix([0, 0]), math.matrix([3, 0]), math.matrix([3, 0])) }, TypeError)
assert.throws(function () { math.distance([0, 0], [3, 0], [3, 0]) }, TypeError)
assert.throws(function () { math.distance({ pointX: 0, pointY: 0 }, { lineOnePtX: 3, lineOnePtY: 0 }, { lineTwoPtX: 3, lineTwoPtY: 0 }) }, TypeError)
assert.throws(function () { math.distance({ pointX: 'l', pointY: 4 }, { lineOnePtX: 1, lineOnePtY: 3 }, { lineTwoPtX: 2, lineTwoPtY: 8 }) }, TypeError)
assert.throws(function () { math.distance({ pointX: 1, pointY: 4 }, { lineOnePtX: 'l', lineOnePtY: 3 }, { lineTwoPtX: 2, lineTwoPtY: 8 }) }, TypeError)
assert.throws(function () { math.distance({ pointX: 1, pointY: 4 }, { lineOnePtX: 1, lineOnePtY: 3 }, { lineTwoPtX: 'l', lineTwoPtY: 8 }) }, TypeError)
Expand Down Expand Up @@ -103,10 +106,16 @@ describe('distance', function () {
})

it('should calculate distance between a point and a line segment given by two points in 2D accurately', function () {
assert.deepStrictEqual(math.distance(math.matrix([10, 10]), math.matrix([2, 3]), math.matrix([-8, 0])), 8.759953130362847)
assert.deepStrictEqual(math.distance([0.23, -0.1240], [-0.232, 13.292], [-0.34, 0.346]), 10.658908662088363)
assert.deepStrictEqual(math.distance([-10, 0.54], [38, 12.8], [94.33, -239]), 10.012171799590002)
assert.deepStrictEqual(math.distance({ pointX: 1, pointY: 4 }, { lineOnePtX: 6, lineOnePtY: 3 }, { lineTwoPtX: 2, lineTwoPtY: 8 }), 2.720549372624744)
assert.deepStrictEqual(math.distance(math.matrix([8, 0]), math.matrix([10, 0]), math.matrix([5, 0])), 0)
assert.deepStrictEqual(math.distance(math.matrix([8, 3]), math.matrix([10, 0]), math.matrix([5, 0])), 3)
assert.deepStrictEqual(math.distance(math.matrix([0, 0]), math.matrix([3, 0]), math.matrix([0, 4])), 2.4)
assert.deepStrictEqual(math.distance([8, 0], [10, 0], [5, 0]), 0)
assert.deepStrictEqual(math.distance([8, 3], [10, 0], [5, 0]), 3)
assert.deepStrictEqual(math.distance([0, 0], [3, 0], [0, 4]), 2.4)
assert.deepStrictEqual(math.distance({ pointX: 8, pointY: 0 }, { lineOnePtX: 10, lineOnePtY: 0 }, { lineTwoPtX: 5, lineTwoPtY: 0 }), 0)
assert.deepStrictEqual(math.distance({ pointX: 8, pointY: 3 }, { lineOnePtX: 10, lineOnePtY: 0 }, { lineTwoPtX: 5, lineTwoPtY: 0 }), 3)
assert.deepStrictEqual(math.distance({ pointX: 0, pointY: 0 }, { lineOnePtX: 3, lineOnePtY: 0 }, { lineTwoPtX: 0, lineTwoPtY: 4 }), 2.4)
assert.deepStrictEqual(math.distance({ pointX: 1, pointY: 4 }, { lineOnePtX: 6, lineOnePtY: 3 }, { lineTwoPtX: 2, lineTwoPtY: 8 }), 3.2796489996607274)
})

it('should calculate distance between point and line segment(with parametric co-ordinates) in 3D accurately', function () {
Expand All @@ -132,9 +141,9 @@ describe('distance', function () {
assert.deepStrictEqual(bigmath.evaluate('distance({pointOneX: 4, pointOneY: 5, pointOneZ: 8}, {pointTwoX: 2, pointTwoY: 7, pointTwoZ: 10})'), bignumber('3.4641016151377545870548926830117'))
assert.deepStrictEqual(bigmath.evaluate('distance([[0,2],[-2,0],[0,2]])'), [bignumber('2.8284271247461900976033774484194'), bignumber('0'), bignumber('2.8284271247461900976033774484194')])
assert.deepStrictEqual(bigmath.evaluate('distance([[1,2,4],[1,2,6],[8,1,3]])'), [bignumber('2'), bignumber('7.1414284285428499979993998113673'), bignumber('7.6811457478686081757696870217314')])
assert.deepStrictEqual(bigmath.evaluate('distance([0.23, -0.1240], [-0.232, 13.292], [-0.34, 0.346])'), bignumber('10.658908662088362142660358292758'))
assert.deepStrictEqual(bigmath.evaluate('distance([0.23, -0.1240], [-0.232, 13.292], [-0.34, 0.346])'), bignumber('0.57390093231864283264935146782154'))
assert.deepStrictEqual(bigmath.evaluate('distance({pointX: 1, pointY: 4}, {lineOnePtX: 6, lineOnePtY: 3}, {lineTwoPtX: 2, lineTwoPtY: 8})'),
bignumber('2.7205493726247441306311612969564'))
bignumber('3.2796489996607273760061602723673'))
assert.deepStrictEqual(bigmath.evaluate('distance([0.1123, -0.242], [0.1316, -0.2421, 0.122135])'), bignumber('0.709482134734344383494644726006'))
assert.deepStrictEqual(bigmath.evaluate('distance({pointX: 10, pointY: 10}, {xCoeffLine: 8, yCoeffLine: 1, constant: 3})'), bignumber('11.535230316796386425693769698742'))
assert.deepStrictEqual(bigmath.evaluate('distance([2, 3, 1], [1, 1, 2, 5, 0, 1])'), bignumber('2.3204774044612855517320588018918'))
Expand Down
6 changes: 4 additions & 2 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1581,12 +1581,14 @@ declare namespace math {
* a 3D line, x0, y0, z0, a, b, c are from: (x−x0, y−y0, z−z0) = t(a, b,
* c)
* @param x Coordinates of the first point
* @param y Coordinates of the second point
* @param y Coordinates of the second point OR coefficients of a line in 3D OR first end-point of a line if the calculation is for distance between point and a line in 2D
* @param z Coordinates of second end-point of a line if the calculation is for distance between point and a line in 2D
* @returns Returns the distance from two/three points
*/
distance(
x: MathCollection | object,
y: MathCollection | object
y: MathCollection | object,
z?: MathCollection | object
): number | BigNumber

/**
Expand Down

0 comments on commit 1b06ec4

Please sign in to comment.