tightenAabb: Fix = ...

Auto-fix for GEOMETRY_AABB_NOT_TIGHT — re-quantises positionsCompressed into a tight AABB so every u16 step resolves a smaller world-space increment. World positions are preserved up to a tiny rounding error introduced by the re-quantisation.

Algebra:

  • Old encoding: w = oldMin + oldRange · u_old / 65535.
  • True world bounds: walk positionsCompressed, find u16 [minU, maxU] per axis, then tightMin = oldMin + oldRange · minU / 65535, tightMax = oldMin + oldRange · maxU / 65535.
  • New encoding: w = tightMin + tightRange · u_new / 65535. Substituting and solving: u_new = (u_old - minU) · 65535 / (maxU - minU). The arithmetic stays in u16 space — no float decompression needed.

Quality gain scales with how loose the original AABB was. A 1% fill becomes 100% — quantisation step shrinks 100×.

Edge case: a collapsed axis (minU === maxU) means every vertex sits at the same coordinate on that axis. The fix encodes them all as 0 and the new AABB collapses too, which is consistent and decodes losslessly.

Idempotent: re-running on a tight geometry computes minU = 0, maxU = 65535, scaling factor 1 → no-op. Returns {fixed: false} when the geometry is already tight on every axis.

Mutates geom.positionsCompressed (Uint16Array) and geom.aabb (Float32Array) via the typed-cast pattern shared across the geometry-fix family.