Placements from the modifier stack
64-bitThe list of placement matrices for the source, first the original.
stepₖ = (hiₖ − loₖ) × relₖ + absₖ; Mᵢ = translate(i × step) × M
The size is taken from the eight box corners of the source under every placement so far, by min and max. Copy i uses i × step in one multiplication, so the last copy is as exact as the first. The soundness check compares the diagonal product with 1e-12, an absolute number.
/** The placements of the source for these items: matrices (column-major 4×4), first = the original. */
export function groupMatrices(ops, box) {
let ms = [ident()];
for (const op of ops) {
if (!op.on) continue;
const p = op.params;
if (op.type === 'scale') {
const S = ident(); [S[0], S[5], S[10]] = p.factors;
ms = ms.map((m) => mmul(S, m));
} else if (op.type === 'array') {
if (ms.length * p.count > MAX_COPIES) bad();
// the step is a fraction of the size of everything so far (the mesh stack's rule), plus the extra step
let lo = [Infinity, Infinity, Infinity], hi = [-Infinity, -Infinity, -Infinity];
if (box) for (const m of ms) for (const c of cornersOf(box)) { const q = apply(m, c); for (let i = 0; i < 3; i++) { lo[i] = Math.min(lo[i], q[i]); hi[i] = Math.max(hi[i], q[i]); } }
else { lo = [0, 0, 0]; hi = [0, 0, 0]; }
const step = [0, 1, 2].map((i) => (hi[i] - lo[i]) * p.rel[i] + p.abs[i]);
const next = [];
for (let i = 0; i < p.count; i++) for (const m of ms) next.push(mmul(translation(step.map((x) => x * i)), m));
ms = next;
} else bad();
if (ms.some((m) => !m.every(ok) || m[0] * m[5] * m[10] < 1e-12)) bad();
}
return ms;
}