class LookupGridRn {
constructor(points, neighborhoodRadius) {
this.r = neighborhoodRadius;
this.rDouble = 2 * neighborhoodRadius;
this.nDims = points[0]?.length ?? 0;
const keyExtractors = Array.from(
{ length: this.nDims },
(_,iDim) => point => this.computeKey(point[iDim] - this.r)
);
this.tree = d3.group(
points,
...keyExtractors
)
}
getPointsInAdjacentCells(point) {
const keys = this.computeKeys(point);
const powers = keys.map((x, i) => Math.pow(2, i)).reverse();
const adjacentKeySets = Array.from(
{ length: Math.pow(2, this.nDims) },
() => keys.slice()
).map((arr, i) => arr.map((x, j) => x - (Math.floor(i / powers[j]) % 2)))
return adjacentKeySets.map(this.getPointsInCell, this).flat();
}
getNeighbors(point, norm = point => Math.hypot(...point)) {
const pointOnRadius = Array.from(
{length: this.nDims},
(_, i) => i === 0 ? this.r : 0
);
const maxNorm = norm(pointOnRadius);
return this.getPointsInAdjacentCells(point)
.filter(candidate => norm(candidate.map((coordinate, i) => coordinate - point[i])) <= maxNorm)
}
getManhattanNeighbors(point) {
return this.getNeighbors(point, point => point.reduce((s, x) => s + Math.abs(x), 0));
}
getPointsInCell(keys) {
let subtree = this.tree;
let i = 0;
while (subtree && i < keys.length) {
const key = keys[i++];
subtree = subtree.get(key);
}
return subtree instanceof Array
? subtree
: [];
}
computeKeys(point) {
return point.map(coordinate => this.computeKey(coordinate));
}
computeKey(coordinate) {
return Math.round(coordinate / this.rDouble);
}
}