Box round each part and footing
64-bitThe center, axes and half sizes of the box used for the first, coarse test.
c = o + x̂·L ÷ 2 + v̂·voff; h = (L ÷ 2, width ÷ 2, depth ÷ 2)
Halves and sums of stored sizes. A footing's axes come from its rotation in degrees × Math.PI ÷ 180 through Math.cos and Math.sin.
/** A solid to check: { ref, id, kind, phase, frame {o, x, u, v}, L, loops (section, centered), voff, cuts, box (obb) }. */
export function solids(state, built, phases) {
const keep = (ph) => !phases || phases.has(ph || 1);
const out = [];
for (const p of built.parts) {
if (!keep(p.phase)) continue;
const w = p.profile.width, d = p.profile.depth;
const c = add(p.frame.o, add(mul(p.frame.x, p.length / 2), mul(p.frame.v, p.voff)));
out.push({ ref: p.member ? { kind: 'member', id: p.member } : p.stair ? { kind: 'stair', id: p.stair } : { kind: 'conn', id: p.conn }, id: p.id, kind: 'steel', phase: p.phase || 1, conn: p.conn, member: p.member, stair: p.stair || null,
frame: p.frame, L: p.length, loops: p.profile.loops, voff: p.voff, cuts: p.cuts.filter((k) => k.kind === 'cope'), depth: d, lug: !!p.conn && p.id === p.conn + '.lug',
box: { c, ax: [p.frame.x, p.frame.u, p.frame.v], h: [p.length / 2, w / 2, d / 2] } });
}
for (const f of state.footings) {
if (!keep(f.phase)) continue;
const a = (Number(f.rotation) || 0) * Math.PI / 180;
const x = [Math.cos(a), Math.sin(a), 0], u = [-Math.sin(a), Math.cos(a), 0], v = [0, 0, 1];
const o = add([f.x, f.y, f.top - f.H / 2], mul(x, -f.L / 2));
const loop = [[-f.B / 2, -f.H / 2], [f.B / 2, -f.H / 2], [f.B / 2, f.H / 2], [-f.B / 2, f.H / 2]];
// the footing as a "part" along its length: section B × H (u across, v up)
out.push({ ref: { kind: 'footing', id: f.id }, id: 'footing:' + f.id, kind: 'concrete', phase: f.phase || 1, frame: { o, x, u, v }, L: f.L, loops: [loop], voff: 0, cuts: [], depth: f.H,
box: { c: [f.x, f.y, f.top - f.H / 2], ax: [x, u, v], h: [f.L / 2, f.B / 2, f.H / 2] } });
}
return out;
}