diff --git a/example/testToRemove.html b/example/testToRemove.html
new file mode 100644
index 000000000..49a724dd1
--- /dev/null
+++ b/example/testToRemove.html
@@ -0,0 +1,23 @@
+
+
+
+ three-mesh-bvh - Complex Geometry Raycasting
+
+
+
+
+
+
+
+
diff --git a/example/testToRemove.js b/example/testToRemove.js
new file mode 100644
index 000000000..91cbbefff
--- /dev/null
+++ b/example/testToRemove.js
@@ -0,0 +1,133 @@
+import * as THREE from 'three';
+import { computeBoundsTree, SAH } from '../src';
+
+THREE.BufferGeometry.prototype.computeBoundsTree = computeBoundsTree;
+
+const spawnPointRadius = 20;
+const radius = 10; // if radius 100 and tube 0.1 and spawnRadius 100, sort works really good.
+const tube = 0.1;
+const segmentsMultiplier = 32;
+const maxDepthSorted = 4;
+const maxLeafTris = 10;
+const strategy = SAH;
+
+const tries = 1000;
+const seed = 20000;
+
+// const geometry = new THREE.SphereGeometry( radius, 8 * segmentsMultiplier, 4 * segmentsMultiplier );
+const geometry = new THREE.TorusKnotGeometry( radius, tube, 64 * segmentsMultiplier, 8 * segmentsMultiplier );
+
+geometry.computeBoundsTree( { maxLeafTris, strategy } );
+
+export class PRNG {
+
+ constructor( seed ) {
+
+ this._seed = seed;
+
+ }
+
+ next() {
+
+ let t = ( this._seed += 0x6d2b79f5 );
+ t = Math.imul( t ^ ( t >>> 15 ), t | 1 );
+ t ^= t + Math.imul( t ^ ( t >>> 7 ), t | 61 );
+ return ( ( t ^ ( t >>> 14 ) ) >>> 0 ) / 4294967296;
+
+ }
+
+ range( min, max ) {
+
+ return min + ( max - min ) * this.next();
+
+ }
+
+}
+
+const bvh = geometry.boundsTree;
+const target = {};
+
+const r = new PRNG( seed );
+const points = new Array( tries );
+
+function generatePoints() {
+
+ for ( let i = 0; i < tries; i ++ ) {
+
+ points[ i ] = new THREE.Vector3( r.range( - spawnPointRadius, spawnPointRadius ), r.range( - spawnPointRadius, spawnPointRadius ), r.range( - spawnPointRadius, spawnPointRadius ) );
+
+ }
+
+}
+
+
+// // TEST EQUALS RESULTS
+
+// generatePoints();
+// const target2 = {};
+// for ( let i = 0; i < tries; i ++ ) {
+
+// bvh.closestPointToPoint( points[ i ], target );
+// bvh.closestPointToPointHybrid( points[ i ], target2 );
+
+// if ( target.distance !== target2.distance ) {
+
+// const diff = target.distance - target2.distance;
+// console.error( "error: " + ( diff / target2.distance * 100 ) + "%" );
+
+// }
+
+// }
+
+// TEST PERFORMANCE
+
+function benchmark() {
+
+ generatePoints();
+
+ const startOld = performance.now();
+
+ for ( let i = 0; i < tries; i ++ ) {
+
+ bvh.closestPointToPointOld( points[ i ], target );
+
+ }
+
+ const endOld = performance.now() - startOld;
+ const startNew = performance.now();
+
+ for ( let i = 0; i < tries; i ++ ) {
+
+ bvh.closestPointToPoint( points[ i ], target );
+
+ }
+
+ const endNew = performance.now() - startNew;
+ const startSort = performance.now();
+
+ for ( let i = 0; i < tries; i ++ ) {
+
+ bvh.closestPointToPointSort( points[ i ], target );
+
+ }
+
+ const endSort = performance.now() - startSort;
+ const startHybrid = performance.now();
+
+ for ( let i = 0; i < tries; i ++ ) {
+
+ bvh.closestPointToPointHybrid( points[ i ], target, maxDepthSorted );
+
+ }
+
+ const endHybrid = performance.now() - startHybrid;
+
+ const bestEnd = Math.min( endSort, endNew, endHybrid );
+ const best = bestEnd === endSort ? "Sorted" : ( bestEnd === endNew ? "New" : "Hybrid" );
+
+ console.log( `New: ${endNew.toFixed( 1 )}ms / Sorted: ${endSort.toFixed( 1 )}ms / Hybrid: ${endHybrid.toFixed( 1 )}ms / Old: ${endOld.toFixed( 1 )}ms / Diff: ${( ( 1 - ( endOld / bestEnd ) ) * 100 ).toFixed( 2 )} % / Best: ${best}` );
+
+}
+
+benchmark();
+setInterval( () => benchmark(), 1000 );
diff --git a/src/core/MeshBVH.js b/src/core/MeshBVH.js
index 95ac647c5..14bab17b9 100644
--- a/src/core/MeshBVH.js
+++ b/src/core/MeshBVH.js
@@ -5,7 +5,13 @@ import { OrientedBox } from '../math/OrientedBox.js';
import { arrayToBox } from '../utils/ArrayBoxUtilities.js';
import { ExtendedTrianglePool } from '../utils/ExtendedTrianglePool.js';
import { shapecast } from './cast/shapecast.js';
-import { closestPointToPoint } from './cast/closestPointToPoint.js';
+import { closestPointToPoint } from './cast/closestPointToPoint.generated.js';
+import { closestPointToPoint_indirect } from './cast/closestPointToPoint_indirect.generated.js';
+import { closestPointToPointSort } from './cast/closestPointToPointSort.generated.js';
+import { closestPointToPointSort_indirect } from './cast/closestPointToPointSort_indirect.generated.js';
+import { closestPointToPointHybrid } from './cast/closestPointToPointHybrid.generated.js';
+import { closestPointToPointHybrid_indirect } from './cast/closestPointToPointHybrid_indirect.generated.js';
+import { closestPointToPointOld } from './cast/closestPointToPoint.js'; // REMOVE AFTER TEST
import { iterateOverTriangles } from './utils/iterationUtils.generated.js';
import { refit } from './cast/refit.generated.js';
@@ -519,7 +525,90 @@ export class MeshBVH {
closestPointToPoint( point, target = { }, minThreshold = 0, maxThreshold = Infinity ) {
- return closestPointToPoint(
+ const closestPointToPointFunc = this.indirect ? closestPointToPoint_indirect : closestPointToPoint;
+ const roots = this._roots;
+ let result = null;
+
+ for ( let i = 0, l = roots.length; i < l; i ++ ) {
+
+ result = closestPointToPointFunc(
+ this,
+ i,
+ point,
+ target,
+ minThreshold,
+ maxThreshold,
+ );
+
+ // fix here, check old result and new
+
+ if ( result && result.distance <= minThreshold ) break;
+
+ }
+
+ return result;
+
+ }
+
+ closestPointToPointSort( point, target = { }, minThreshold = 0, maxThreshold = Infinity ) {
+
+ const closestPointToPointFunc = this.indirect ? closestPointToPointSort_indirect : closestPointToPointSort;
+ const roots = this._roots;
+ let result = null;
+
+ for ( let i = 0, l = roots.length; i < l; i ++ ) {
+
+ result = closestPointToPointFunc(
+ this,
+ i,
+ point,
+ target,
+ minThreshold,
+ maxThreshold,
+ );
+
+ // fix here, check old result and new
+
+ if ( result && result.distance <= minThreshold ) break;
+
+ }
+
+ return result;
+
+ }
+
+ closestPointToPointHybrid( point, target = { }, sortedListMaxCount = 16, minThreshold = 0, maxThreshold = Infinity ) {
+
+ const closestPointToPointFunc = this.indirect ? closestPointToPointHybrid_indirect : closestPointToPointHybrid;
+ const roots = this._roots;
+ let result = null;
+
+ for ( let i = 0, l = roots.length; i < l; i ++ ) {
+
+ result = closestPointToPointFunc(
+ this,
+ i,
+ point,
+ target,
+ sortedListMaxCount,
+ minThreshold,
+ maxThreshold,
+ );
+
+ // fix here, check old result and new
+
+ if ( result && result.distance <= minThreshold ) break;
+
+ }
+
+ return result;
+
+ }
+
+ // REMOVE AFTER TEST
+ closestPointToPointOld( point, target = { }, minThreshold = 0, maxThreshold = Infinity ) {
+
+ return closestPointToPointOld(
this,
point,
target,
diff --git a/src/core/cast/closestPointToPoint.js b/src/core/cast/closestPointToPoint.js
index c5ae1a462..dd039d8fc 100644
--- a/src/core/cast/closestPointToPoint.js
+++ b/src/core/cast/closestPointToPoint.js
@@ -1,9 +1,11 @@
import { Vector3 } from 'three';
+// DELETE THIS AFTER TEST
+
const temp = /* @__PURE__ */ new Vector3();
const temp1 = /* @__PURE__ */ new Vector3();
-export function closestPointToPoint(
+export function closestPointToPointOld(
bvh,
point,
target = { },
diff --git a/src/core/cast/closestPointToPoint.template.js b/src/core/cast/closestPointToPoint.template.js
new file mode 100644
index 000000000..7fb935f62
--- /dev/null
+++ b/src/core/cast/closestPointToPoint.template.js
@@ -0,0 +1,117 @@
+import { Vector3 } from 'three';
+import { COUNT, OFFSET, LEFT_NODE, RIGHT_NODE, IS_LEAF } from '../utils/nodeBufferUtils.js';
+import { BufferStack } from '../utils/BufferStack.js';
+import { ExtendedTrianglePool } from '../../utils/ExtendedTrianglePool.js';
+import { setTriangle } from '../../utils/TriangleUtilities.js';
+import { closestDistanceSquaredPointToBox } from '../utils/distanceUtils.js';
+
+const temp = /* @__PURE__ */ new Vector3();
+const temp1 = /* @__PURE__ */ new Vector3();
+
+export function closestPointToPoint/* @echo INDIRECT_STRING */(
+ bvh,
+ root,
+ point,
+ target,
+ minThreshold,
+ maxThreshold
+) {
+
+ const minThresholdSq = minThreshold * minThreshold;
+ const maxThresholdSq = maxThreshold * maxThreshold;
+ let closestDistanceSq = Infinity;
+ let closestDistanceTriIndex = null;
+
+ const { geometry } = bvh;
+ const { index } = geometry;
+ const pos = geometry.attributes.position;
+ const triangle = ExtendedTrianglePool.getPrimitive();
+
+ BufferStack.setBuffer( bvh._roots[ root ] );
+ const { float32Array, uint16Array, uint32Array } = BufferStack;
+ _closestPointToPoint( root );
+ BufferStack.clearBuffer();
+
+ if ( closestDistanceSq === Infinity ) return null;
+
+ const closestDistance = Math.sqrt( closestDistanceSq );
+
+ if ( ! target.point ) target.point = temp1.clone();
+ else target.point.copy( temp1 );
+ target.distance = closestDistance;
+ target.faceIndex = closestDistanceTriIndex;
+
+ return target;
+
+
+ // early out if under minThreshold
+ // skip checking if over maxThreshold
+ // set minThreshold = maxThreshold to quickly check if a point is within a threshold
+ // returns Infinity if no value found
+ function _closestPointToPoint( nodeIndex32 ) {
+
+ const nodeIndex16 = nodeIndex32 * 2;
+ const isLeaf = IS_LEAF( nodeIndex16, uint16Array );
+ if ( isLeaf ) {
+
+ const offset = OFFSET( nodeIndex32, uint32Array );
+ const count = COUNT( nodeIndex16, uint16Array );
+
+ for ( let i = offset, l = count + offset; i < l; i ++ ) {
+
+ /* @if INDIRECT */
+
+ const ti = bvh.resolveTriangleIndex( i );
+ setTriangle( triangle, 3 * ti, index, pos );
+
+ /* @else */
+
+ setTriangle( triangle, i * 3, index, pos );
+
+ /* @endif */
+
+ triangle.needsUpdate = true;
+
+ triangle.closestPointToPoint( point, temp );
+ const distSq = point.distanceToSquared( temp );
+ if ( distSq < closestDistanceSq ) {
+
+ temp1.copy( temp );
+ closestDistanceSq = distSq;
+ closestDistanceTriIndex = i;
+
+ if ( distSq < minThresholdSq ) return true;
+
+ }
+
+ }
+
+ return;
+
+ }
+
+ const leftIndex = LEFT_NODE( nodeIndex32 );
+ const rightIndex = RIGHT_NODE( nodeIndex32, uint32Array );
+
+ const leftDistance = closestDistanceSquaredPointToBox( leftIndex, float32Array, point );
+ const rightDistance = closestDistanceSquaredPointToBox( rightIndex, float32Array, point );
+
+ if ( leftDistance <= rightDistance ) {
+
+ if ( leftDistance < closestDistanceSq && leftDistance < maxThresholdSq ) {
+
+ if ( _closestPointToPoint( leftIndex ) ) return true;
+ if ( rightDistance < closestDistanceSq ) return _closestPointToPoint( rightIndex );
+
+ }
+
+ } else if ( rightDistance < closestDistanceSq && rightDistance < maxThresholdSq ) {
+
+ if ( _closestPointToPoint( rightIndex ) ) return true;
+ if ( leftDistance < closestDistanceSq ) return _closestPointToPoint( leftIndex );
+
+ }
+
+ }
+
+}
diff --git a/src/core/cast/closestPointToPointHybrid.template.js b/src/core/cast/closestPointToPointHybrid.template.js
new file mode 100644
index 000000000..5271954e8
--- /dev/null
+++ b/src/core/cast/closestPointToPointHybrid.template.js
@@ -0,0 +1,181 @@
+import { Vector3 } from 'three';
+import { COUNT, OFFSET, LEFT_NODE, RIGHT_NODE, IS_LEAF } from '../utils/nodeBufferUtils.js';
+import { BufferStack } from '../utils/BufferStack.js';
+import { ExtendedTrianglePool } from '../../utils/ExtendedTrianglePool.js';
+import { setTriangle } from '../../utils/TriangleUtilities.js';
+import { closestDistanceSquaredPointToBox } from '../utils/distanceUtils.js';
+import { SortedListDesc } from '../utils/SortedListDesc.js';
+
+const temp = /* @__PURE__ */ new Vector3();
+const temp1 = /* @__PURE__ */ new Vector3();
+const sortedList = new SortedListDesc();
+
+export function closestPointToPointHybrid/* @echo INDIRECT_STRING */(
+ bvh,
+ root,
+ point,
+ target,
+ maxDepthSorted,
+ minThreshold,
+ maxThreshold
+) {
+
+ const minThresholdSq = minThreshold * minThreshold;
+ const maxThresholdSq = maxThreshold * maxThreshold;
+ let closestDistanceSq = Infinity;
+ let closestDistanceTriIndex = null;
+ BufferStack.setBuffer( bvh._roots[ root ] );
+
+ const { geometry } = bvh;
+ const { index } = geometry;
+ const pos = geometry.attributes.position;
+ const triangle = ExtendedTrianglePool.getPrimitive();
+
+ const { float32Array, uint16Array, uint32Array } = BufferStack;
+
+ sortedList.clear();
+
+ if ( maxDepthSorted > 0 ) {
+
+ _fillSortedList( root, 0 );
+
+ } else {
+
+ sortedList.push( { nodeIndex32: root, distance: closestDistanceSquaredPointToBox( root, float32Array, point ) } );
+
+ }
+
+ const nodes = sortedList.array;
+ for ( let i = nodes.length - 1; i >= 0; i -- ) {
+
+ const { distance, nodeIndex32 } = nodes[ i ];
+
+ if ( distance >= closestDistanceSq ) break;
+
+ _closestPointToPoint( nodeIndex32 );
+
+ }
+
+ BufferStack.clearBuffer();
+
+ if ( closestDistanceSq === Infinity ) return null;
+
+ const closestDistance = Math.sqrt( closestDistanceSq );
+
+ if ( ! target.point ) target.point = temp1.clone();
+ else target.point.copy( temp1 );
+ target.distance = closestDistance;
+ target.faceIndex = closestDistanceTriIndex;
+
+ return target;
+
+
+ function _fillSortedList( nodeIndex32, depth ) {
+
+ const nodeIndex16 = nodeIndex32 * 2;
+ const isLeaf = IS_LEAF( nodeIndex16, uint16Array );
+ if ( isLeaf ) {
+
+ sortedList.push( { nodeIndex32, distance: closestDistanceSquaredPointToBox( nodeIndex32, float32Array, point ) } );
+
+ return;
+
+ }
+
+ const leftIndex = LEFT_NODE( nodeIndex32 );
+ const rightIndex = RIGHT_NODE( nodeIndex32, uint32Array );
+
+ if ( depth === maxDepthSorted ) {
+
+ const leftDistance = closestDistanceSquaredPointToBox( leftIndex, float32Array, point );
+ const rightDistance = closestDistanceSquaredPointToBox( rightIndex, float32Array, point );
+
+ if ( leftDistance > rightDistance ) { // leftDistance < maxThresholdSq - consider this?
+
+ sortedList.push( { nodeIndex32: leftIndex, distance: leftDistance } );
+ sortedList.push( { nodeIndex32: rightIndex, distance: rightDistance } );
+
+ } else {
+
+ sortedList.push( { nodeIndex32: rightIndex, distance: rightDistance } );
+ sortedList.push( { nodeIndex32: leftIndex, distance: leftDistance } );
+
+ }
+
+ return;
+
+ }
+
+ _fillSortedList( leftIndex, depth + 1 );
+ _fillSortedList( rightIndex, depth + 1 );
+
+ }
+
+
+ function _closestPointToPoint( nodeIndex32 ) {
+
+ const nodeIndex16 = nodeIndex32 * 2;
+ const isLeaf = IS_LEAF( nodeIndex16, uint16Array );
+ if ( isLeaf ) {
+
+ const offset = OFFSET( nodeIndex32, uint32Array );
+ const count = COUNT( nodeIndex16, uint16Array );
+
+ for ( let i = offset, l = count + offset; i < l; i ++ ) {
+
+ /* @if INDIRECT */
+
+ const ti = bvh.resolveTriangleIndex( i );
+ setTriangle( triangle, 3 * ti, index, pos );
+
+ /* @else */
+
+ setTriangle( triangle, i * 3, index, pos );
+
+ /* @endif */
+
+ triangle.needsUpdate = true;
+
+ triangle.closestPointToPoint( point, temp );
+ const distSq = point.distanceToSquared( temp );
+ if ( distSq < closestDistanceSq ) {
+
+ temp1.copy( temp );
+ closestDistanceSq = distSq;
+ closestDistanceTriIndex = i;
+
+ if ( distSq < minThresholdSq ) return true;
+
+ }
+
+ }
+
+ return;
+
+ }
+
+ const leftIndex = LEFT_NODE( nodeIndex32 );
+ const rightIndex = RIGHT_NODE( nodeIndex32, uint32Array );
+
+ const leftDistance = closestDistanceSquaredPointToBox( leftIndex, float32Array, point );
+ const rightDistance = closestDistanceSquaredPointToBox( rightIndex, float32Array, point );
+
+ if ( leftDistance <= rightDistance ) {
+
+ if ( leftDistance < closestDistanceSq && leftDistance < maxThresholdSq ) {
+
+ if ( _closestPointToPoint( leftIndex ) ) return true;
+ if ( rightDistance < closestDistanceSq ) return _closestPointToPoint( rightIndex );
+
+ }
+
+ } else if ( rightDistance < closestDistanceSq && rightDistance < maxThresholdSq ) {
+
+ if ( _closestPointToPoint( rightIndex ) ) return true;
+ if ( leftDistance < closestDistanceSq ) return _closestPointToPoint( leftIndex );
+
+ }
+
+ }
+
+}
diff --git a/src/core/cast/closestPointToPointSort.template.js b/src/core/cast/closestPointToPointSort.template.js
new file mode 100644
index 000000000..69ca73bca
--- /dev/null
+++ b/src/core/cast/closestPointToPointSort.template.js
@@ -0,0 +1,123 @@
+import { Vector3 } from 'three';
+import { COUNT, OFFSET, LEFT_NODE, RIGHT_NODE, IS_LEAF } from '../utils/nodeBufferUtils.js';
+import { BufferStack } from '../utils/BufferStack.js';
+import { ExtendedTrianglePool } from '../../utils/ExtendedTrianglePool.js';
+import { setTriangle } from '../../utils/TriangleUtilities.js';
+import { closestDistanceSquaredPointToBox } from '../utils/distanceUtils.js';
+import { SortedListDesc } from '../utils/SortedListDesc.js';
+
+const temp = /* @__PURE__ */ new Vector3();
+const temp1 = /* @__PURE__ */ new Vector3();
+const sortedList = new SortedListDesc();
+
+export function closestPointToPointSort/* @echo INDIRECT_STRING */(
+ bvh,
+ root,
+ point,
+ target,
+ minThreshold,
+ maxThreshold
+) {
+
+ const minThresholdSq = minThreshold * minThreshold;
+ const maxThresholdSq = maxThreshold * maxThreshold;
+ let closestDistanceSq = Infinity;
+ let closestDistanceTriIndex = null;
+ BufferStack.setBuffer( bvh._roots[ root ] );
+
+ _closestPointToPoint();
+
+ BufferStack.clearBuffer();
+
+ if ( closestDistanceSq === Infinity ) return null;
+
+ const closestDistance = Math.sqrt( closestDistanceSq );
+
+ if ( ! target.point ) target.point = temp1.clone();
+ else target.point.copy( temp1 );
+ target.distance = closestDistance;
+ target.faceIndex = closestDistanceTriIndex;
+
+ return target;
+
+
+ function _closestPointToPoint() {
+
+ const { geometry } = bvh;
+ const { index } = geometry;
+ const pos = geometry.attributes.position;
+ const triangle = ExtendedTrianglePool.getPrimitive();
+ const { float32Array, uint16Array, uint32Array } = BufferStack;
+ sortedList.clear();
+
+ let node = { nodeIndex32: 0, distance: closestDistanceSquaredPointToBox( 0, float32Array, point ) };
+
+ do {
+
+ const { distance, nodeIndex32 } = node;
+
+ if ( distance >= closestDistanceSq ) return;
+
+ const nodeIndex16 = nodeIndex32 * 2;
+ const isLeaf = IS_LEAF( nodeIndex16, uint16Array );
+ if ( isLeaf ) {
+
+ const offset = OFFSET( nodeIndex32, uint32Array );
+ const count = COUNT( nodeIndex16, uint16Array );
+
+ for ( let i = offset, l = count + offset; i < l; i ++ ) {
+
+ /* @if INDIRECT */
+
+ const ti = bvh.resolveTriangleIndex( i );
+ setTriangle( triangle, 3 * ti, index, pos );
+
+ /* @else */
+
+ setTriangle( triangle, i * 3, index, pos );
+
+ /* @endif */
+
+ triangle.needsUpdate = true;
+
+ triangle.closestPointToPoint( point, temp );
+ const distSq = point.distanceToSquared( temp );
+ if ( distSq < closestDistanceSq ) {
+
+ temp1.copy( temp );
+ closestDistanceSq = distSq;
+ closestDistanceTriIndex = i;
+
+ if ( distSq < minThresholdSq ) return;
+
+ }
+
+ }
+
+ continue;
+
+ }
+
+ const leftIndex = LEFT_NODE( nodeIndex32 );
+ const rightIndex = RIGHT_NODE( nodeIndex32, uint32Array );
+
+ const leftDistance = closestDistanceSquaredPointToBox( leftIndex, float32Array, point );
+ const rightDistance = closestDistanceSquaredPointToBox( rightIndex, float32Array, point );
+
+ if ( leftDistance < closestDistanceSq && leftDistance < maxThresholdSq ) {
+
+ sortedList.push( { nodeIndex32: leftIndex, distance: leftDistance } );
+
+ }
+
+ if ( rightDistance < closestDistanceSq && rightDistance < maxThresholdSq ) {
+
+ sortedList.push( { nodeIndex32: rightIndex, distance: rightDistance } );
+
+ }
+
+ } while ( node = sortedList.pop() );
+
+ }
+
+}
diff --git a/src/core/utils/SortedListDesc.js b/src/core/utils/SortedListDesc.js
new file mode 100644
index 000000000..5ce845a89
--- /dev/null
+++ b/src/core/utils/SortedListDesc.js
@@ -0,0 +1,47 @@
+export class SortedListDesc {
+
+ constructor() {
+
+ this.array = [];
+
+ }
+
+ clear() {
+
+ this.array.length = 0;
+
+ }
+
+
+ push( node ) {
+
+ const index = this.binarySearch( node.distance );
+ this.array.splice( index, 0, node );
+
+ }
+
+ pop() {
+
+ return this.array.pop();
+
+ }
+
+ binarySearch( value ) {
+
+ const array = this.array;
+
+ let low = 0, high = array.length;
+
+ while ( low < high ) {
+
+ const mid = ( low + high ) >>> 1;
+ if ( array[ mid ].distance > value ) low = mid + 1;
+ else high = mid;
+
+ }
+
+ return low;
+
+ }
+
+}
diff --git a/src/core/utils/distanceUtils.js b/src/core/utils/distanceUtils.js
new file mode 100644
index 000000000..7b9e8ef7d
--- /dev/null
+++ b/src/core/utils/distanceUtils.js
@@ -0,0 +1,20 @@
+export function closestDistanceSquaredPointToBox( nodeIndex32, array, point ) {
+
+ const xMin = array[ nodeIndex32 + 0 ] - point.x;
+ const xMax = point.x - array[ nodeIndex32 + 3 ];
+ let dx = xMin > xMax ? xMin : xMax;
+ dx = dx > 0 ? dx : 0;
+
+ const yMin = array[ nodeIndex32 + 1 ] - point.y;
+ const yMax = point.y - array[ nodeIndex32 + 4 ];
+ let dy = yMin > yMax ? yMin : yMax;
+ dy = dy > 0 ? dy : 0;
+
+ const zMin = array[ nodeIndex32 + 2 ] - point.z;
+ const zMax = point.z - array[ nodeIndex32 + 5 ];
+ let dz = zMin > zMax ? zMin : zMax;
+ dz = dz > 0 ? dz : 0;
+
+ return dx * dx + dy * dy + dz * dz;
+
+}