Last active
June 20, 2018 11:02
-
-
Save peeke/7f282fbc1b03802ad9fb8762d48bac65 to your computer and use it in GitHub Desktop.
Quickly find the nearest neighbor of a point
This file contains 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
class KdTree { | |
constructor(points, axes = ['x', 'y']) { | |
this._axes = axes | |
this._tree = KdTree.build(points, axes) | |
} | |
static build(points, axes, depth = 0) { | |
if (!points.length) return null | |
const median = Math.floor((points.length - 1) / 2) | |
const axis = axes[depth % axes.length] | |
// Sort and divide based on axis | |
points.sort((a, b) => a[axis] - b[axis]) | |
return { | |
location: points[median], | |
left: KdTree.build(points.slice(0, median), axes, depth + 1), | |
right: KdTree.build(points.slice(median + 1), axes, depth + 1) | |
} | |
} | |
static nodeIsLeaf(node) { | |
return !node.left && !node.right | |
} | |
static nearestNeighbor(node, axes, point) { | |
const traverse = (node, depth = 0, best = { x: Infinity, y: Infinity, distanceSq: Infinity }) => { | |
if (!node) return best | |
const axis = axes[depth % axes.length] | |
const plane = node.location[axis] | |
const nextBranch = point[axis] < plane ? 'left' : 'right' | |
best = traverse(node[nextBranch], depth + 1, best) | |
// Check if points on the other side of the hyperplane might qualify as new best | |
// If they do, traverse | |
const distanceSqToPlane = (point[axis] - plane) ** 2 | |
if (distanceSqToPlane < best.distanceSq) { | |
const oppositeBranch = nextBranch === 'left' ? 'right' : 'left' | |
best = traverse(node[oppositeBranch], depth + 1, best) | |
} | |
const distanceSq = (node.location.x - point.x) ** 2 + (node.location.y - point.y) ** 2 | |
const isNewBest = distanceSq < best.distanceSq | |
return isNewBest | |
? { x: node.location.x, y: node.location.y, distanceSq } | |
: best | |
} | |
return traverse(node) | |
} | |
nearestNeighbor(point) { | |
return KdTree.nearestNeighbor(this._tree, this._axes, point) | |
} | |
} |
Author
peeke
commented
Jun 20, 2018
•
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment