Zum Inhalt

viewer/tests/unit/voxelize.test.mjs

import { test } from 'node:test';
import assert from 'node:assert/strict';
import { voxelize, smoothDensity, defaultIso } from '../../js/collision/voxelize.js';

const bounds10 = { min: { x: 0, y: 0, z: 0 }, max: { x: 10, y: 10, z: 10 } };

function flat(...pts) {
  const f = new Float32Array(pts.length);
  for (let i = 0; i < pts.length; i++) f[i] = pts[i];
  return f;
}

test('voxelize: empty positions → all-zero grid', () => {
  const { density, resolution } = voxelize(new Float32Array(0), bounds10, 8);
  assert.equal(resolution, 8);
  assert.equal(density.length, 8 ** 3);
  for (const v of density) assert.equal(v, 0);
});

test('voxelize: single splat at centre → exactly one non-zero cell', () => {
  const { density } = voxelize(flat(5, 5, 5), bounds10, 8);
  let nonZero = 0;
  for (const v of density) if (v > 0) nonZero++;
  assert.equal(nonZero, 1);
  const idx = 4 * 64 + 4 * 8 + 4;
  assert.equal(density[idx], 1);
});

test('voxelize: out-of-bounds splats are rejected', () => {
  const { density } = voxelize(flat(-1, -1, -1, 11, 11, 11), bounds10, 8);
  for (const v of density) assert.equal(v, 0);
});

test('voxelize: degenerate bounds (zero extent) → all-zero', () => {
  const bad = { min: { x: 0, y: 0, z: 0 }, max: { x: 0, y: 0, z: 0 } };
  const { density } = voxelize(flat(0, 0, 0), bad, 4);
  for (const v of density) assert.equal(v, 0);
});

test('smoothDensity: single non-zero cell spreads to 3x3x3 neighbourhood', () => {
  const res = 5;
  const density = new Float32Array(res ** 3);
  density[2 * 25 + 2 * 5 + 2] = 27;
  const out = smoothDensity(density, res);
  assert.equal(out[2 * 25 + 2 * 5 + 2], 1);
  assert.equal(out[2 * 25 + 2 * 5 + 3], 1);
  assert.equal(out[0], 0);
});

test('smoothDensity: empty grid stays empty', () => {
  const out = smoothDensity(new Float32Array(64), 4);
  for (const v of out) assert.equal(v, 0);
});

test('defaultIso: empty grid → 1.5 fallback', () => {
  assert.equal(defaultIso(new Float32Array(64)), 1.5);
});

test('defaultIso: 50th-percentile of non-zero × 0.5, floored at 1.5', () => {
  const density = new Float32Array(100);
  for (let i = 0; i < 10; i++) density[i] = i + 1;
  // median of [1..10] sorted = nz[Math.floor(9/2)] = nz[4] = 5 → * 0.5 = 2.5
  assert.equal(defaultIso(density), 2.5);
});

test('defaultIso: small values → falls back to 1.5', () => {
  const density = new Float32Array(10);
  density[0] = 1; density[1] = 2;
  // median = nz[Math.floor(1/2)] = nz[0] = 1, * 0.5 = 0.5 → max(1.5, 0.5) = 1.5
  assert.equal(defaultIso(density), 1.5);
});