World triangles and box of each element
32-bit in partEach element's triangles in the scene's coordinates and the box round them.
p' = M p
Each corner is read from the batch's positions, moved by the model's matrix in 64-bit and stored in a Float32Array of world triangles. The box is taken from the 64-bit values before that store. The batch positions are themselves a Float32Array in ifc-model.js, so both carry about 7 digits.
/** World triangles and bounds of each element (keys), through its model's placement (matrixOf(batch) → column-major 4 × 4 | null). */
export function elementsOf(batches, keys, matrixOf = () => null) {
const want = keys instanceof Set ? keys : new Set(keys);
const map = new Map();
for (const b of batches) {
const E = b.elems, P = b.positions, I = b.indices, M = matrixOf(b);
for (let r = 0; r < E.ids.length; r++) {
const key = E.ids[r];
if (!want.has(key)) continue;
let el = map.get(key);
if (!el) { el = { key, parts: [], box: [Infinity, Infinity, Infinity, -Infinity, -Infinity, -Infinity] }; map.set(key, el); }
const t = new Float32Array(E.tn[r] * 9);
for (let k = 0, tt = E.t0[r] * 3; k < E.tn[r] * 3; k++, tt++) {
const v = I[tt] * 3;
let x = P[v], y = P[v + 1], z = P[v + 2];
if (M) { const X = M[0] * x + M[4] * y + M[8] * z + M[12], Y = M[1] * x + M[5] * y + M[9] * z + M[13], Z = M[2] * x + M[6] * y + M[10] * z + M[14]; x = X; y = Y; z = Z; }
t[k * 3] = x; t[k * 3 + 1] = y; t[k * 3 + 2] = z;
const bx = el.box;
if (x < bx[0]) bx[0] = x; if (y < bx[1]) bx[1] = y; if (z < bx[2]) bx[2] = z;
if (x > bx[3]) bx[3] = x; if (y > bx[4]) bx[4] = y; if (z > bx[5]) bx[5] = z;
}
el.parts.push(t);
}
}
return [...map.values()].map((e) => {
const n = e.parts.reduce((a, p) => a + p.length, 0), tris = new Float32Array(n);
let at = 0; for (const p of e.parts) { tris.set(p, at); at += p.length; }
return { key: e.key, box: e.box, tris };
});
}