| 24 | * Grid policy note: callers pass their own `quant`. The app currently uses |
| 25 | * 1e4 (0.1 µm... 100 µm cells) — export grid, masking, validation, repair |
| 26 | * 1e5 (10 µm cells) — subdivision, regularize, displacement |
| 27 | * 1e6 (1 µm cells) — decimation (own packed-BigInt welder) |
| 28 | * Keep a call site's grid unchanged unless you intend to change behaviour. |
| 29 | */ |
| 30 | |
| 31 | export class QuantizedPointMap { |
| 32 | /** |
| 33 | * @param {number} quant – grid multiplier (e.g. 1e5 → 10 µm cells) |
| 34 | * @param {number} expected – expected number of unique points (sizing hint) |
| 35 | */ |
| 36 | constructor(quant, expected = 256) { |
| 37 | this.quant = quant; |
| 38 | /** true when the last getOrSet() inserted a new key */ |
| 39 | this.inserted = false; |
| 40 | this._size = 0; |
| 41 | let cap = 16; |
| 42 | const target = Math.max(16, Math.ceil(expected / 0.6)); |
| 43 | while (cap < target) cap *= 2; |
| 44 | this._alloc(cap); |
| 45 | } |
| 46 | |
| 47 | get size() { return this._size; } |
| 48 | |
| 49 | _alloc(cap) { |
| 50 | this._cap = cap; |
| 51 | this._mask = cap - 1; |
| 52 | this._qx = new Float64Array(cap); |
| 53 | this._qy = new Float64Array(cap); |
| 54 | this._qz = new Float64Array(cap); |
| 55 | this._val = new Int32Array(cap).fill(-1); |
| 56 | } |
| 57 | |
| 58 | _slot(qx, qy, qz) { |
| 59 | // Mix the (wrapped-to-32-bit) quantised components; equality is checked |
| 60 | // against the exact f64-stored values, so hash wrapping is harmless. |
| 61 | let h = Math.imul(qx | 0, 0x9E3779B1) ^ Math.imul(qy | 0, 0x85EBCA77) ^ Math.imul(qz | 0, 0xC2B2AE3D); |
| 62 | h ^= h >>> 15; |
| 63 | let i = h & this._mask; |
| 64 | const qxA = this._qx, qyA = this._qy, qzA = this._qz, val = this._val, mask = this._mask; |
| 65 | while (val[i] !== -1) { |
| 66 | if (qxA[i] === qx && qyA[i] === qy && qzA[i] === qz) return i; |
| 67 | i = (i + 1) & mask; |
| 68 | } |
| 69 | return i; |
| 70 | } |
| 71 | |
| 72 | _grow() { |
| 73 | const oqx = this._qx, oqy = this._qy, oqz = this._qz, oval = this._val, ocap = this._cap; |
| 74 | this._alloc(ocap * 2); |
| 75 | for (let i = 0; i < ocap; i++) { |
| 76 | if (oval[i] === -1) continue; |
| 77 | const s = this._slot(oqx[i], oqy[i], oqz[i]); |
| 78 | this._qx[s] = oqx[i]; this._qy[s] = oqy[i]; this._qz[s] = oqz[i]; |
| 79 | this._val[s] = oval[i]; |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | /** Value stored for (x,y,z)'s grid cell, or -1 if absent. */ |
nothing calls this directly
no outgoing calls
no test coverage detected