|
@@ -43,29 +43,31 @@ interface SpatialHash {
|
|
|
size: number[],
|
|
|
min: number[],
|
|
|
grid: Uint32Array,
|
|
|
- divisor: number[],
|
|
|
+ delta: number[],
|
|
|
bucketOffset: Uint32Array,
|
|
|
bucketCounts: Int32Array,
|
|
|
bucketArray: Int32Array,
|
|
|
data: InputData,
|
|
|
maxRadius: number,
|
|
|
+ expandedBox: Box3D,
|
|
|
boundingBox: Box3D,
|
|
|
boundingSphere: Sphere3D
|
|
|
}
|
|
|
|
|
|
interface BuildState {
|
|
|
size: number[],
|
|
|
- divisor: number[],
|
|
|
+ delta: number[],
|
|
|
data: InputData,
|
|
|
+ expandedBox: Box3D,
|
|
|
boundingBox: Box3D,
|
|
|
boundingSphere: Sphere3D,
|
|
|
count: number
|
|
|
}
|
|
|
|
|
|
function _build(state: BuildState): SpatialHash {
|
|
|
- const { boundingBox, size: [sX, sY, sZ], data: { x: px, y: py, z: pz, radius, indices }, count, divisor } = state;
|
|
|
+ const { expandedBox, size: [sX, sY, sZ], data: { x: px, y: py, z: pz, radius, indices }, count, delta } = state;
|
|
|
const n = sX * sY * sZ;
|
|
|
- const { min: [minX, minY, minZ] } = boundingBox;
|
|
|
+ const { min: [minX, minY, minZ] } = expandedBox;
|
|
|
|
|
|
let maxRadius = 0;
|
|
|
let bucketCount = 0;
|
|
@@ -73,10 +75,11 @@ function _build(state: BuildState): SpatialHash {
|
|
|
const bucketIndex = new Int32Array(count);
|
|
|
for (let t = 0, _t = indices.length; t < _t; t++) {
|
|
|
const i = indices[t];
|
|
|
- const x = Math.floor((px[i] - minX) / divisor[0]);
|
|
|
- const y = Math.floor((py[i] - minY) / divisor[1]);
|
|
|
- const z = Math.floor((pz[i] - minZ) / divisor[2]);
|
|
|
+ const x = Math.floor((px[i] - minX) / delta[0]);
|
|
|
+ const y = Math.floor((py[i] - minY) / delta[1]);
|
|
|
+ const z = Math.floor((pz[i] - minZ) / delta[2]);
|
|
|
const idx = (((x * sY) + y) * sZ) + z;
|
|
|
+
|
|
|
if ((grid[idx] += 1) === 1) {
|
|
|
bucketCount += 1
|
|
|
}
|
|
@@ -122,10 +125,11 @@ function _build(state: BuildState): SpatialHash {
|
|
|
bucketCounts,
|
|
|
bucketOffset,
|
|
|
grid,
|
|
|
- divisor,
|
|
|
- min: state.boundingBox.min,
|
|
|
+ delta,
|
|
|
+ min: state.expandedBox.min,
|
|
|
data: state.data,
|
|
|
maxRadius,
|
|
|
+ expandedBox: state.expandedBox,
|
|
|
boundingBox: state.boundingBox,
|
|
|
boundingSphere: state.boundingSphere
|
|
|
}
|
|
@@ -133,6 +137,7 @@ function _build(state: BuildState): SpatialHash {
|
|
|
|
|
|
export function build(data: PositionData) {
|
|
|
const boundingBox = Box3D.computeBounding(data);
|
|
|
+ const expandedBox = Box3D.expand(boundingBox, Vec3.create(0.5, 0.5, 0.5));
|
|
|
const boundingSphere = Sphere3D.computeBounding(data);
|
|
|
|
|
|
// TODO: specialized code that does not require the indices annotation?
|
|
@@ -141,17 +146,20 @@ export function build(data: PositionData) {
|
|
|
for (let i = 0, _i = data.x.length; i < _i; i++) indices[i] = i;
|
|
|
}
|
|
|
|
|
|
- // size of the box
|
|
|
- const S = Vec3.sub(Vec3.zero(), boundingBox.max, boundingBox.min);
|
|
|
- // required "grid volume" so that each cell contains on average 32 elements.
|
|
|
- const V = Math.ceil(indices.length / 32);
|
|
|
- const f = Math.pow(V / (S[0] * S[1] * S[2]), 1 / 3);
|
|
|
-
|
|
|
- const divisor = [Math.ceil(S[0] * f), Math.ceil(S[1] * f), Math.ceil(S[2] * f)];
|
|
|
-
|
|
|
- const size = [Math.floor(S[0] / divisor[0]) + 1, Math.floor(S[1] / divisor[1]) + 1, Math.floor(S[2] / divisor[2]) + 1];
|
|
|
-
|
|
|
- console.log({ divisor, size })
|
|
|
+ const S = Vec3.sub(Vec3.zero(), expandedBox.max, expandedBox.min);
|
|
|
+ let delta, size;
|
|
|
+
|
|
|
+ if (indices.length > 0) {
|
|
|
+ // size of the box
|
|
|
+ // required "grid volume" so that each cell contains on average 32 elements.
|
|
|
+ const V = Math.ceil(indices.length / 32);
|
|
|
+ const f = Math.pow(V / (S[0] * S[1] * S[2]), 1 / 3);
|
|
|
+ size = [Math.ceil(S[0] * f), Math.ceil(S[1] * f), Math.ceil(S[2] * f)];
|
|
|
+ delta = [S[0] / size[0], S[1] / size[1], S[2] / size[2]];
|
|
|
+ } else {
|
|
|
+ delta = S;
|
|
|
+ size = [1, 1, 1];
|
|
|
+ }
|
|
|
|
|
|
const inputData: InputData = {
|
|
|
x: data.x,
|
|
@@ -164,53 +172,55 @@ export function build(data: PositionData) {
|
|
|
const state: BuildState = {
|
|
|
size,
|
|
|
data: inputData,
|
|
|
+ expandedBox,
|
|
|
boundingBox,
|
|
|
boundingSphere,
|
|
|
count: indices.length,
|
|
|
- divisor
|
|
|
+ delta
|
|
|
}
|
|
|
|
|
|
return _build(state);
|
|
|
}
|
|
|
|
|
|
export function inSphere(structure: SpatialHash, x: number, y: number, z: number, radius: number): Result {
|
|
|
- const { min, size: [sX, sY, sZ], bucketOffset, bucketCounts, bucketArray, grid, data: { x: px, y: py, z: pz, indices }, divisor } = structure;
|
|
|
+ const { min, size: [sX, sY, sZ], bucketOffset, bucketCounts, bucketArray, grid, data: { x: px, y: py, z: pz, indices }, delta } = structure;
|
|
|
//const { radius: r, radiusSq: rSq, pivot: [x, y, z], isCheck, mask } = ctx;
|
|
|
const r = radius, rSq = r * r;
|
|
|
|
|
|
const result = Result.create();
|
|
|
|
|
|
- const loX = Math.max(0, Math.floor((x - r - min[0]) / divisor[0]));
|
|
|
- const loY = Math.max(0, Math.floor((y - r - min[1]) / divisor[1]));
|
|
|
- const loZ = Math.max(0, Math.floor((z - r - min[2]) / divisor[2]));
|
|
|
+ const loX = Math.max(0, Math.floor((x - r - min[0]) / delta[0]));
|
|
|
+ const loY = Math.max(0, Math.floor((y - r - min[1]) / delta[1]));
|
|
|
+ const loZ = Math.max(0, Math.floor((z - r - min[2]) / delta[2]));
|
|
|
+
|
|
|
+ const hiX = Math.min(sX - 1, Math.floor((x + r - min[0]) / delta[0]));
|
|
|
+ const hiY = Math.min(sY - 1, Math.floor((y + r - min[1]) / delta[1]));
|
|
|
+ const hiZ = Math.min(sZ - 1, Math.floor((z + r - min[2]) / delta[2]));
|
|
|
|
|
|
- const hiX = Math.min(sX, Math.floor((x + r - min[0]) / divisor[0]));
|
|
|
- const hiY = Math.min(sY, Math.floor((y + r - min[1]) / divisor[1]));
|
|
|
- const hiZ = Math.min(sZ, Math.floor((z + r - min[2]) / divisor[0]));
|
|
|
+ if (loX > hiX || loY > hiY || loZ > hiZ) return result;
|
|
|
|
|
|
for (let ix = loX; ix <= hiX; ix++) {
|
|
|
for (let iy = loY; iy <= hiY; iy++) {
|
|
|
for (let iz = loZ; iz <= hiZ; iz++) {
|
|
|
const bucketIdx = grid[(((ix * sY) + iy) * sZ) + iz];
|
|
|
+ if (bucketIdx === 0) continue;
|
|
|
|
|
|
- if (bucketIdx > 0) {
|
|
|
- const k = bucketIdx - 1;
|
|
|
- const offset = bucketOffset[k];
|
|
|
- const count = bucketCounts[k];
|
|
|
- const end = offset + count;
|
|
|
+ const k = bucketIdx - 1;
|
|
|
+ const offset = bucketOffset[k];
|
|
|
+ const count = bucketCounts[k];
|
|
|
+ const end = offset + count;
|
|
|
|
|
|
- for (let i = offset; i < end; i++) {
|
|
|
- const idx = indices[bucketArray[i]];
|
|
|
+ for (let i = offset; i < end; i++) {
|
|
|
+ const idx = indices[bucketArray[i]];
|
|
|
|
|
|
- const dx = px[idx] - x;
|
|
|
- const dy = py[idx] - y;
|
|
|
- const dz = pz[idx] - z;
|
|
|
- const distSq = dx * dx + dy * dy + dz * dz;
|
|
|
+ const dx = px[idx] - x;
|
|
|
+ const dy = py[idx] - y;
|
|
|
+ const dz = pz[idx] - z;
|
|
|
+ const distSq = dx * dx + dy * dy + dz * dz;
|
|
|
|
|
|
- if (distSq <= rSq) {
|
|
|
- //if (isCheck) return true;
|
|
|
- Result.add(result, idx, distSq);
|
|
|
- }
|
|
|
+ if (distSq <= rSq) {
|
|
|
+ // if (isCheck) return true;
|
|
|
+ Result.add(result, bucketArray[i], distSq);
|
|
|
}
|
|
|
}
|
|
|
}
|