Created
April 17, 2021 10:52
-
-
Save steveruizok/d4a2d4edbdb05b51fee3a8686d878353 to your computer and use it in GitHub Desktop.
Given a point and a line (or line segment), get the nearest point on the line.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Get the nearest point on a line with a known unit vector that passes through point A | |
* @param A Any point on the line | |
* @param u The unit vector for the line. | |
* @param P A point not on the line to test. | |
* @returns | |
*/ | |
export function nearestPointOnLineThroughPoint( | |
A: number[], | |
u: number[], | |
P: number[] | |
) { | |
return add(A, mul(u, pry(sub(P, A), u))) | |
} | |
/** | |
* Get the nearest point on a line segment between A and B | |
* @param A The start of the line segment | |
* @param B The end of the line segment | |
* @param P The off-line point | |
* @param clamp Whether to clamp the point between A and B. | |
* @returns | |
*/ | |
export function nearestPointOnLineSegment( | |
A: number[], | |
B: number[], | |
P: number[], | |
clamp = true | |
) { | |
const delta = sub(B, A) | |
const length = len(delta) | |
const u = div(delta, length) | |
const pt = add(A, mul(u, pry(sub(P, A), u))) | |
const da = dist(A, pt) | |
const db = dist(B, pt) | |
if (clamp) { | |
if (db < da && da > length) return B | |
if (da < db && db > length) return A | |
} | |
return pt | |
} | |
/** | |
* Distance between a point and a line with a known unit vector that passes through a point. | |
* @param A Any point on the line | |
* @param u The unit vector for the line. | |
* @param P A point not on the line to test. | |
* @returns | |
*/ | |
export function distanceToLineThroughPoint( | |
A: number[], | |
u: number[], | |
P: number[] | |
) { | |
return dist(P, nearestPointOnLineThroughPoint(A, u, P)) | |
} | |
/** | |
* Distance between a point and the nearest point on a line segment between A and B | |
* @param A The start of the line segment | |
* @param B The end of the line segment | |
* @param P The off-line point | |
* @param clamp Whether to clamp the point between A and B. | |
* @returns | |
*/ | |
export function distanceToLineSegment( | |
A: number[], | |
u: number[], | |
P: number[], | |
clamp = true | |
) { | |
return dist(P, nearestPointOnLineSegment(A, u, P, clamp)) | |
} | |
// Vector Utilities | |
function add(A: number[], B: number[]) { | |
return [A[0] + B[0], A[1] + B[1]] | |
} | |
function sub(A: number[], B: number[]) { | |
return [A[0] - B[0], A[1] - B[1]] | |
} | |
function mul(A: number[], n: number) { | |
return [A[0] * n, A[1] * n] | |
} | |
function dpr(A: number[], B: number[]) { | |
return A[0] * B[0] + A[1] * B[1] | |
} | |
function len(A: number[]) { | |
return Math.hypot(A[0], A[1]) | |
} | |
function dist(A: number[], B: number[]) { | |
return Math.hypot(A[1] - B[1], A[0] - B[0]) | |
} | |
function pry(A: number[], B: number[]) { | |
return dpr(A, B) / len(B) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment