Transforms & Composition
Every normalized Elucim element separates what it draws (props) from how it is placed (layout). This keeps static composition inspectable and lets timelines/state machines own motion.
The Prop Model
Section titled “The Prop Model”Every element has three main categories:
| Category | Controls | Examples |
|---|---|---|
| Primitive props | What to draw | cx, cy, r, fill, stroke |
| Layout | Where / how | rotation, scale, translate |
| Timelines | When values change | opacity, translate, scale, rotate, fill, stroke tracks |
These compose on a single normalized element:
{ id: 'orbit', type: 'circle', layout: { rotation: 45, translate: [10, 0] }, props: { type: 'circle', cx: 200, cy: 150, r: 50, stroke: '$accent', fill: 'none' },}Spatial Transforms
Section titled “Spatial Transforms”All normalized elements can use these spatial fields in layout.
rotation
Section titled “rotation”Rotation in degrees. By default the element rotates around its own center.
{ layout: { rotation: 30 }, props: { type: 'rect', x: 100, y: 50, width: 120, height: 80, stroke: '$accent', fill: 'none' } }rotationOrigin
Section titled “rotationOrigin”Override the pivot point with an [x, y] coordinate:
{ layout: { rotation: 45, rotationOrigin: [100, 50] }, props: { type: 'rect', x: 100, y: 50, width: 120, height: 80, stroke: '$accent', fill: 'none' },}A uniform number or an [sx, sy] pair. Values are unitless multipliers (1 = normal size).
{ layout: { scale: 1.5 }, props: { type: 'circle', cx: 200, cy: 150, r: 40, stroke: '$secondary', fill: 'none' } }{ layout: { scale: [2, 0.5] }, props: { type: 'circle', cx: 200, cy: 150, r: 40, stroke: '$success', fill: 'none' } }translate
Section titled “translate”An [dx, dy] pixel offset applied before rotation and scale:
{ layout: { translate: [50, 20] }, props: { type: 'circle', cx: 100, cy: 100, r: 30, stroke: '$warning', fill: 'none' } }Stacking with Object order
Section titled “Stacking with Object order”SVG follows the painter’s model: siblings declared later paint on top of earlier siblings. In normalized documents, that means order in scene.children and each group element’s children array is the stacking order.
scene: { type: 'player', width: 800, height: 600, children: ['card', 'circle'] },elements: { card: { id: 'card', type: 'rect', props: { type: 'rect', x: 100, y: 60, width: 100, height: 80, fill: '$success' } }, circle: { id: 'circle', type: 'circle', props: { type: 'circle', cx: 130, cy: 100, r: 50, fill: '$accent' } },}Key rules:
- Later siblings render later (on top)
- Root stacking is controlled by
scene.children - Group stacking is controlled by that group’s
children - Move Objects up/down in the editor Objects panel, or use agent order commands, to change stacking
Grouping
Section titled “Grouping”group is a container that applies layout transforms to all its children as a single unit.
elements: { card: { id: 'card', type: 'group', layout: { rotation: 15, translate: [50, 0] }, children: ['box', 'dot'], props: { type: 'group' } }, box: { id: 'box', type: 'rect', parentId: 'card', props: { type: 'rect', x: 100, y: 50, width: 200, height: 150, stroke: '$accent', fill: 'none' } }, dot: { id: 'dot', type: 'circle', parentId: 'card', props: { type: 'circle', cx: 200, cy: 125, r: 40, stroke: '$secondary', fill: 'none' } },}What this does:
- Rotate a group → rotates everything inside
- Groups can nest — transforms compose through the hierarchy
- Groups stack their own children by order — each group creates its own stacking context
Static vs Animated Transforms
Section titled “Static vs Animated Transforms”The spatial fields described above are static. For animated transforms, create timeline tracks over translate, scale, or rotate:
timelines: { spin: { id: 'spin', duration: 60, tracks: [ { target: 'orbit', property: 'rotate', keyframes: [{ frame: 0, value: 0 }, { frame: 60, value: 360 }] }, ], },}Use layout for initial positioning and timelines for motion.