viewer/explode.js
1.5 KB · 33 lines · javascript · Download from the repository page
// Shared explosion math. Pure functions so validate.mjs (Node) and app.js (browser) agree exactly.// position(t) = assembled + ease(t) * offset — computed from the immutable assembled transform every// update; nothing accumulates frame to frame. export function easeInOutCubic(t) { t = Math.min(1, Math.max(0, t)); return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;} // Document frame [dx, dy(down), dz(rear)] → renderer frame [x, y(up), z(toward front)]export function docVecToScene(v) { return [v[0], -v[1], -v[2]]; } /** * Offset (renderer frame) that a component should have at explosion amount t. * @param {object} comp component record (needs .parent, .explode) * @param {number} t 0..1 slider amount * @param {object} opts { scope: componentId|null, expandChildren: bool, parentOf: (id)=>comp|undefined } * Rules (skill §8): top-level components move in a whole-object explosion; a child's own offset is * applied only when its parent is the scope, or when expandChildren is on. Scoped explosion * (scope !== null) moves only the scope's direct children; everything else stays assembled. */export function explodedOffset(comp, t, opts) { const e = easeInOutCubic(t); const off = comp.explode || [0, 0, 0]; const scaled = docVecToScene(off).map((c) => c * e); const zero = [0, 0, 0]; if (opts.scope) { return comp.parent === opts.scope ? scaled : zero; } if (comp.parent === null) return scaled; return opts.expandChildren ? scaled : zero;}