Skip to content

Instantly share code, notes, and snippets.

@steveruizok
Created March 31, 2021 19:28
Show Gist options
  • Save steveruizok/f04188866e751e6d5dc74ff20a635772 to your computer and use it in GitHub Desktop.
Save steveruizok/f04188866e751e6d5dc74ff20a635772 to your computer and use it in GitHub Desktop.
From an off-path point, get the closest on-path point. (With SVG paths).
/**
* Find the closest point on a path to an off-path point.
* @param pathNode
* @param point
* @returns
*/
export function getClosestPointOnPath(
pathNode: SVGPathElement,
point: number[]
) {
var pathLen = pathNode.getTotalLength(),
p = 8,
best: DOMPoint,
bestLen: number,
bestDist = Infinity
// linear scan for coarse approximation
for (
var scan: DOMPoint, scanLen = 0, scanDist: number;
scanLen <= pathLen;
scanLen += p
) {
if (
(scanDist = distance2(
(scan = pathNode.getPointAtLength(scanLen)),
point
)) < bestDist
) {
;(best = scan), (bestLen = scanLen), (bestDist = scanDist)
}
}
// binary search for precise estimate
p /= 2
while (p > 0.5) {
var before: DOMPoint,
after: DOMPoint,
bl: number,
al: number,
bd: number,
ad: number
if (
(bl = bestLen - p) >= 0 &&
(bd = distance2((before = pathNode.getPointAtLength(bl)), point)) <
bestDist
) {
;(best = before), (bestLen = bl), (bestDist = bd)
} else if (
(al = bestLen + p) <= pathLen &&
(ad = distance2((after = pathNode.getPointAtLength(al)), point)) <
bestDist
) {
;(best = after), (bestLen = al), (bestDist = ad)
} else {
p /= 2
}
}
return {
point: [best.x, best.y],
distance: bestDist,
length: (bl + al) / 2,
t: (bl + al) / 2 / pathLen,
}
}
function distance2(p: DOMPoint, point: number[]) {
var dx = p.x - point[0],
dy = p.y - point[1]
return dx * dx + dy * dy
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment