Skip to content

Transform

Rigid transforms on scene entities. Accessed via snaptrude.design.transform.

Migrated from the removed tools.transform.*. All operations are undoable and return a PluginDesignChangeResult; failures throw (the RPC rejects).

Performance

Every transform is array-based: pass all targets in one call (one host round-trip). Never loop a transform per entity. See Performance.

getPosition / setPosition (and scale, an engine gap) are not part of this surface yet — they land in a later pass.

Types

PluginDesignChangeResult

The committed-state record shared by scene-mutating design ops.

PropertyTypeDescription
affectedComponentHandle[]The entities the op acted on

PluginAlignEdge

Which bounding-box edge align snaps to a common world-axis value.

ValueAxisSnaps
'left'XMin X (left edges)
'center'XCentre X
'right'XMax X (right edges)
'top'ZMax Z (top/back edges)
'middle'ZCentre Z
'bottom'ZMin Z (bottom/front edges)

PluginTransformMirrorAxis

The horizontal axis mirror flips across (world axes, Y up).

ValueMirrors acrossIn plan (Y up)
'x'World XFront ↔ back flip (reflects Z)
'z'World ZLeft ↔ right flip (reflects X)

Vertical ('y') mirroring is not exposed.

Functions

move(components, displacement, options?)

Translate entities by a relative displacement vector (added to the first component's absolute position as the anchor). Undoable.

  • Parameters:
    • components: ComponentHandle[] — Entities to move (≥1)
    • displacement: Vec3Handle — Relative translation
    • options: object (optional)is2D?: boolean, trackParentChange?: boolean
  • Returns: PluginDesignChangeResult
  • Throws: if any handle is unknown/forged, or the engine move fails
ts
const { vec3 } = snaptrude.core.math;
await snaptrude.design.transform.move(["space-id"], vec3.new(10, 0, 0));

rotate(components, angleInDegrees, options?)

Rotate entities by a signed angle in DEGREES (positive = CCW about +Y). The pivot defaults to the combined bounding-box centre; pass options.pivot to rotate about an explicit world-space point instead. Undoable.

Contract change from tools.transform.rotate

The removed tools.transform.rotate took radians; this canonical form takes degrees. The rotation axis moves into options.axis (defaults to world +Y), and the pivot moves into options.pivot (defaults to the combined bbox centre).

  • Parameters:
    • components: ComponentHandle[] — Entities to rotate (≥1)
    • angleInDegrees: number — Signed rotation angle in degrees
    • options: object (optional)axis?: Vec3Handle (defaults to world +Y), pivot?: Vec3Handle (world-space rotation centre; defaults to the combined bbox centre), is2D?: boolean
  • Returns: PluginDesignChangeResult
  • Throws: if any handle is unknown/forged, or the engine rotate fails
ts
// About the selection's own bbox centre (default)
await snaptrude.design.transform.rotate(["space-id"], 90);

// About an explicit pivot point (here, the world origin)
const { vec3 } = snaptrude.core.math;
await snaptrude.design.transform.rotate(["space-id"], 90, { pivot: vec3.new(0, 0, 0) });

align(components, edge, options?)

Snap a set of components' bounding-box edges to a common world-axis value — the classic Left / Center / Right / Top / Middle / Bottom align. With options.reference the target is that component's edge (it stays put); without it, the components align among themselves. World axes only (the UI's camera-relative remap is bypassed). Doors and windows are rejected in v1 — if any component in components (or the reference) is a door or window, the whole call throws; nothing is silently dropped. A group aligns as one rigid body. Undoable.

  • Parameters:
    • components: ComponentHandle[] — Entities to align (≥1; doors/windows rejected)
    • edge: PluginAlignEdge — Which bounding-box edge to snap
    • options: object (optional)reference?: ComponentHandle — the component whose edge is the fixed target
  • Returns: PluginDesignChangeResult
  • Throws: if any handle is unknown/forged, a door/window is included (in components or as the reference), or the engine align fails
ts
// Left-align three masses to the leftmost one.
await snaptrude.design.transform.align(["m1", "m2", "m3"], "left");

// Align two spaces to a reference's right edge.
await snaptrude.design.transform.align(["s1", "s2"], "right", { reference: "s1" });

mirror(components, axis)

Mirror components in place about a horizontal axis — flip their geometry across that axis through the combined bounding-box centre (world-matrix flip + brep inversion + CAD / dimension-line / parametric handling, committed atomically). Nothing moves off-centre; this is the toolbar "flip", not a translate-and-reflect. axis names the axis the geometry mirrors across: 'x' reflects the Z coord (a front ↔ back flip in plan), 'z' reflects the X coord (a left ↔ right flip). Vertical ('y') mirroring is not exposed. Locked, bucket-locked and ineligible entries (terrain, plinths, reference / dimension lines, neighbourhood buildings) are silently dropped — the call only throws when nothing mirrorable remains. Undoable.

  • Parameters:
  • Returns: PluginDesignChangeResultaffected echoes the supplied handles
  • Throws: if any handle is unknown/forged, or nothing mirrorable remains
ts
const rooms = await snaptrude.design.selection.get();
await snaptrude.design.transform.mirror(rooms, "x");

Errors

Failed calls reject with a typed PluginError — see Error Handling. Conditions specific to this namespace:

CodeThrown byWhendetails
VALIDATIONmove, rotatecomponents is empty — both ops require at least one entity
VALIDATIONalignFewer than 2 components without a reference, or an unknown edge
HANDLE_INVALIDmove, rotateA component handle, the displacement vec3, or rotate's options.axis / options.pivot cannot be resolved
HANDLE_INVALIDalignA component or reference handle cannot be resolved
PRECONDITION_FAILEDalignA component or the reference is a door/window (unsupported in v1), or targets a foreign proposal
OPERATION_FAILEDmove, rotateThe engine move/rotate failsengineCode — the engine's failure code
OPERATION_FAILEDalignThe engine align operation fails
VALIDATIONmirrorcomponents is empty, or axis is not 'x' / 'z' (schema)
HANDLE_INVALIDmirrorA component handle cannot be resolved
PRECONDITION_FAILEDmirrorA component targets a foreign proposal
OPERATION_FAILEDmirrorNothing mirrorable remains (all locked/ineligible) or the engine flip failsengineCode — e.g. NO_COMPONENTS