Closest point
The nearest point on a mesh to a point, or to another mesh, and what lies at a hit.
const closest = tree.closestPointToPoint({ x: 0, y: 2, z: 0 }); // { point, distance, faceIndex } or null
// minThreshold: stop below this distance; maxThreshold: look no farther.
const near = tree.closestPointToPoint({ x: 0, y: 2, z: 0 }, {}, 0, 1);
const onTree = {};
const onOther = {};
const between = tree.closestPointToGeometry(other, otherToTree, onTree, onOther); // onTree is between too
closestPointToPoint(point, target, minThreshold, maxThreshold)is three-mesh-bvh's shapecast step for step: the nearer box first, boxes culled by the best distance so far and bymaxThreshold, an early stop belowminThreshold. It fillstargetwithpoint,distanceandfaceIndex(the original triangle), and returns it, or null withtargetuntouched. As in three-mesh-bvh,maxThresholdculls boxes, not triangles. One departure: on a sliver (the squared sine of its angle ataat most 1e-7) the nearer of three's point and the nearest point of its three edges is kept, as on the GPU, because three'sTriangle.closestPointToPointcancels there.closestPointToGeometry(other, geometryToBvh, target1, target2, minThreshold, maxThreshold)runs the same casts between two meshes.target1is on this tree,target2(null skips it) on the other, in its own space. BothfaceIndexes are original triangles. A scalinggeometryToBvhskews the culling, as in three-mesh-bvh.
Against three-mesh-bvh 0.9.18 (bench/closest-point.ts --rounds 10, Node 22 on the Apple M5, the 269,764-triangle
room, 2,000 points per row, 200 placements; 2026-11-13, after the CPU query pass, under load; indicative):
What lies at a hit
getTriangleHitPointInfo(point, triangle, { indices, attributes }) gives the barycentric weights of the three
corners at a point, the corners, the face normal, and any per-vertex attributes interpolated there:
const hit = tree.raycastFirst({ origin: [0, 0, 5], direction: [0, 0, -1] });
if (hit) {
const info = tree.getTriangleHitPointInfo(hit.point, hit.triangle, {
attributes: { normal: { array: normals, itemSize: 3 }, uv: { array: uvs, itemSize: 2 } },
});
console.log(info.barycoord, info.faceNormal, info.attributes.uv);
}
The tree's triangles hold positions only. Normals, UVs and colours come from your own arrays.
See also
- Raycast: the same tree, for rays.
- Shapecast and bvhcast: boxes, spheres and your own shapes against a tree.
- Spatial queries on the GPU: closest point in WGSL, over a whole scene.
- Signed distance fields: the distance to every point of a grid, on the GPU.