Appearance
Design Create
Author new scene-committed BIM entities. Every creator takes geometry handles + scalars and returns a ComponentHandle (or ComponentHandle[] for plural creators; wallRuns returns one ComponentHandle[] per run) — the Component.id of the created entity, resolvable across the rest of the design.* surface. Creation is an undoable host call; it throws on failure (no Result wrapper — consistent with core.geom.create.*). Accessed via snaptrude.design.create.
Footprint objects take a ContourHandle (outer profile + holes); build one with snaptrude.core.geom.create.contourFromProfile(s). Passing an invalid, stale, or foreign handle rejects with HandleInvalidError (code HANDLE_INVALID).
All lengths (heights, thicknesses, positions) are in Snaptrude units — convert with snaptrude.core.units.convert(value, from, to).
Performance
Creating many entities? Use a bulk creator — spaces, wallRuns, floors, doors, windows, furnitureItems — or copy(components, displacement, options) for repeats of the same geometry. The whole batch is one host round-trip. Looping a single-item creator (mass, slab, column, …) is one round-trip per item and can trip the rate limit. See Performance.
Bulk creators
Six creators take an items[] array: spaces, wallRuns, floors, doors, windows and furnitureItems. Each item mirrors its singular sibling's arguments field for field, with the same validation — anything the singular accepts, the plural accepts per item.
A plural returns the singular's return lifted into an array: space returns a ComponentHandle, so spaces returns ComponentHandle[]; walls already returns a ComponentHandle[] (one wall per profile curve), so wallRuns returns ComponentHandle[][] — one inner array per run.
The five BIM creators — wallRuns, floors, doors, windows, furnitureItems — take 1 to 1000 items per call (spaces has no upper cap). Every batch commits as one host round-trip and one undo entry: one undo removes the whole batch, one redo restores it. Split a larger import into separate awaited batches, each with its own undo entry. A doors, windows or furnitureItems batch whose catalog assets take longer than about 50 seconds to load fails with OPERATION_FAILED (engineCode: "LOAD_BUDGET_EXCEEDED") before anything is placed — retry with fewer distinct catalog items.
Bulk creation is all-or-nothing. Every item is validated and every handle resolved before anything is created, and a failure while building geometry rolls the batch's scene changes back before publication — a rejected call creates nothing. Item-specific errors carry the failing item's zero-based details.itemIndex; schema errors carry dot-separated paths whose leading segment is the zero-based item index, such as details.issues[0].path: "2.width" for width on the third item. A returned result confirms the local operation; it is not a persistence-service acknowledgement.
Results preserve input order even when the host groups items internally (by thickness, catalog source or host wall). Geometry handles stay reusable — the same contour or profile may appear in several items.
The host owns catalog source-mesh persistence, including across calls that overlap in time: whichever placement first builds the source's creation command records it, and every later call — batch or singular, yours or another plugin's — simply instances it. You never need to sequence your calls to keep a source alive.
Types
PluginReferenceLineStyle
Line style of a reference line (mirrors the engine ReferenceLineMode verbatim).
| Value | Description |
|---|---|
"SOLID" | Solid line (default) |
"DASHED" | Dashed line |
"DOT_DASH" | Dot-dash line |
PluginSlabType
Slab type (mirrors the engine SLAB_TYPES values verbatim).
| Value | Description |
|---|---|
"Intermediate Slab" | Intermediate floor slab (default) |
"Basement Slab" | Basement slab |
"Plinth" | Plinth slab |
"Roof" | Roof slab |
PluginStaircasePreset
A staircase preset — a base type or a named parametric preset. Mirrors the engine STAIRCASE_PRESETS / StaircaseType. Use the value that matches the shape you want (e.g. "straight", "dogLegged", "lShaped").
Values: "straight", "straightWoLanding", "straightFlightWoMidLanding", "dogLegged", "openWell", "lShaped", "3StepLShaped", "square", "singleStep", "plinthLevel", "amphitheatre", "connectingStepsToAmphitheatre", "mezzanineFloorStraight", "mezzanineFloorDogLegged", "mezzanineFloorLShaped", "custom".
PluginStaircaseDimensions
Optional parametric overrides applied before staircase creation. Lengths are in Snaptrude units.
| Property | Type | Description |
|---|---|---|
width | number? | Flight width; positive |
tread | number? | Tread depth; positive |
riser | number? | Target riser height; positive and normalized to the storey height |
landingWidth | number? | Landing depth; positive and not less than the tread |
angleInDegrees | number? | Rotation about the vertical axis; must be a multiple of 90° |
PluginDesignCreateOpeningOptions
Configured door/window placement options. Width, height, and sill height are in Snaptrude units.
| Property | Type | Description |
|---|---|---|
kind | "door" | "window" | Opening type |
catalogId | string | Catalog id from design.doors/windows.listCatalog() |
hostWall | ComponentHandle | Wall receiving the opening |
position | Vec3Handle | Approximate world point projected onto the wall |
facing | Vec3Handle? | World point on the side the opening swings open to |
hinge | Vec3Handle? | World point near the jamb the opening is hinged on |
label | string? | Instance label |
width | number? | Final opening width; positive |
height | number? | Final opening height; positive |
sillHeight | number? | Window only: distance from host-wall base to opening bottom; nonnegative |
PluginCreateWallRunItem
One wall run for wallRuns — the same inputs as walls, one run per item.
| Property | Type | Description |
|---|---|---|
profile | ProfileHandle | Ordered curve chain (wall centerlines) |
height | number? | Wall height (> 0); default the engine default |
thickness | number? | Wall thickness (> 0); default the wallType's total layer thickness when given, else the engine default |
wallType | string? | Wall type name (or "wall:…" id) from design.types.list("wall") — layers/material/thickness; default generic wall |
storey | number? | Target storey (integer, must already exist) — the run is assigned and lifted to its base in the same undo entry; default geometry-derived |
PluginCreateFloorItem
One floor for floors — the same footprint-extrude inputs as floor.
| Property | Type | Description |
|---|---|---|
contour | ContourHandle | Footprint (outer profile + holes) |
thickness | number | Floor thickness (> 0) |
position | Vec3Handle? | Offset from origin; default the origin |
PluginCreateDoorItem
One door for doors — the same fields as a "door" PluginDesignCreateOpeningOptions, so the size overrides singular door lacks are available per item. Unknown fields are rejected.
| Property | Type | Description |
|---|---|---|
catalogId | string | Library id: team _id or general fullName |
hostWall | ComponentHandle | The wall to host the door; several items may name the same wall |
position | Vec3Handle | World point projected onto the wall |
facing | Vec3Handle? | World POINT on the side the door opens into, not a direction; default engine-chosen |
hinge | Vec3Handle? | World POINT near the jamb the door is hinged on; default the catalog item's authored side |
label | string? | Instance name |
width | number? | Width override (> 0); default the catalog item's |
height | number? | Height override (> 0); default the catalog item's |
PluginCreateWindowItem
One window for windows — the PluginCreateDoorItem fields plus sillHeight. Unknown fields are rejected.
| Property | Type | Description |
|---|---|---|
catalogId | string | Library id: team _id or general fullName |
hostWall | ComponentHandle | The wall to host the window; several items may name the same wall |
position | Vec3Handle | World point projected onto the wall |
facing | Vec3Handle? | World POINT on the side the window opens to, not a direction; default engine-chosen |
hinge | Vec3Handle? | World POINT near the jamb the window is hinged on; default the catalog item's authored side |
label | string? | Instance name |
width | number? | Width override (> 0); default the catalog item's |
height | number? | Height override (> 0); default the catalog item's |
sillHeight | number? | Wall base to window BOTTOM (≥ 0), not to its center; default the catalog item's |
PluginCreateFurnitureItem
One furniture instance for furnitureItems — the same inputs as furniture minus createNewSourceMesh: in a batch the host owns source-mesh persistence (a source is recorded once per call, however many items reuse it).
position.y is the REST elevation — the item is grounded so its bounding-box base sits exactly at position.y. Pass the floor/storey elevation to stand furniture on it; never add half the item's height yourself.
| Property | Type | Description |
|---|---|---|
catalogId | string | Library id: team _id or general fullName |
position | Vec3Handle | Absolute world placement point (y = rest elevation) |
label | string? | Instance name and readable label; default auto ${name}Ins${n} |
angleInDegrees | number? | Rotation about the vertical axis, in degrees; default unrotated |
PluginSpaceType
Supported space type values (mirrors the internal SpaceType enum).
| Value | Description |
|---|---|
"Room" | Standard room |
"Program Block" | Program/department block |
"Balcony" | Balcony space |
"Road" | Road |
"Garden" | Garden area |
"Deck" | Deck |
"Pool" | Pool |
"Walkway" | Walkway |
"Envelope" | Envelope |
"Parking" | Parking area |
PluginAreaClass
Area-class classification of a space (mirrors the internal AreaClass enum). Controls which area total a space's footprint contributes to — it does not change the space's own footprint area; it only decides which project-level total that footprint is summed into.
| Value | Description |
|---|---|
"NET" | Net (usable) area |
"GROSS" | Gross area |
"EXCLUDED" | Excluded from area totals |
PluginMassType
Supported mass type values (mirrors the internal MASS_TYPES enum).
| Value | Description |
|---|---|
"Plinth" | Plinth mass |
"Void" | Void/cut-out |
"Pergola" | Pergola structure |
"Furniture" | Furniture element |
"Facade element" | Facade element |
"Generic mass" | Generic mass |
"Room" | Room (default for spaces) |
"Department" | Department block |
"Building" | Building envelope |
"Revit Import" | Imported from Revit |
"Mass" | Generic mass type |
"Site" | Site object |
PluginDepartmentId
Accepted department ID values — either a well-known built-in ID (PluginWellKnownDepartmentId) or a UUID string for custom (user-created) departments.
| Value | Department |
|---|---|
"DEFAULT" | Default department |
"SITE" | Site department |
"ENVELOPE" | Envelope department |
"CORE" | Core department |
| UUID string | Custom (user-created) department |
PluginSmartLayoutArgs
Arguments for smartLayout. Provide a template as EITHER a templateGroup id OR an explicit templateComponents set (not both).
| Property | Type | Description |
|---|---|---|
templateGroup | string? | Id of the group whose supported components form the template — a group id from snaptrude.core.groups.list (groups are string-id entities, NOT component handles) |
templateComponents | ComponentHandle[]? | An explicit template component set |
targets | ComponentHandle[] | The spaces/masses to lay the template into |
hideTargets | boolean? (default true) | Hide the target masses after placement |
PluginSmartLayoutResult
Result of smartLayout — the created entities grouped by kind (flat across all targets; not attributed per target, so the run stays one undo batch).
| Property | Type | Description |
|---|---|---|
created.walls | ComponentHandle[] | Created walls |
created.furniture | ComponentHandle[] | Created furniture |
created.doors | ComponentHandle[] | Created doors |
created.windows | ComponentHandle[] | Created windows |
created.floors | ComponentHandle[] | Created floors |
skippedWalls | number | Walls the solver could not place |
PluginBuildableEnvelopePolygonVertex
Site polygon vertex for buildableEnvelope, in the request lengthUnit. Vertices may be passed as { x, z } objects or [x, z] tuples. Numeric values must be finite (no NaN or ±Infinity).
| Shape | Type | Description |
|---|---|---|
| Object | { x: number, z: number } | Plan coordinate in the X/Z plane |
| Tuple | [number, number] | Equivalent [x, z] plan coordinate |
PluginBuildableEnvelopeVerticalCap
Vertical cap for a buildable envelope. Use exactly one shape. Floor-to-floor height is not nested here; it is always supplied as the top-level floorToFloor argument.
| Shape | Type | Description |
|---|---|---|
| Max height | { kind: "max_height", maxHeight: number } | Cap the envelope by total height; maxHeight > 0 |
| Max floors | { kind: "max_floors", maxFloors: number } | Cap the envelope by floor count; positive integer |
PluginBuildableEnvelopeSetbackTier
One tier of a buildable envelope's setback profile.
Tiers are ordered by aboveHeight. The first tier (index 0) must have aboveHeight: 0 and describes the ground footprint; each subsequent tier describes the complete setback at a strictly greater height. Each tier carries its own front, side, and rear values — there is no inheritance from earlier tiers. All numeric values must be finite and nonnegative, in the request lengthUnit.
| Property | Type | Description |
|---|---|---|
aboveHeight | number | Height at or above which this tier applies; the ground tier uses 0 |
front | number | Front setback for this tier |
side | number | Side setback for this tier |
rear | number | Rear setback for this tier |
Functions
space(contour, height, label?, position?, spaceType?, massType?, departmentId?, storey?)
Create a space (a room / mass) by extruding a footprint contour upward. The contour is the bottom footprint (outer profile + optional holes); it is extruded by height along +Y. The space is placed on the active story and assigned a default department unless overridden.
- Parameters:
contour:ContourHandle— Bottom footprint (outer + holes)height:number— Extrusion height in Snaptrude units (> 0)label:string(optional) — Room label (maps to the space'sroom_type)position:Vec3Handle(optional) — Offset from origin (default origin)spaceType:PluginSpaceType(optional) — Space-type classification (defaultRoom)massType:PluginMassType(optional) — Mass-type classification (defaultRoom)departmentId:PluginDepartmentId(optional) — Department assignment (default department)storey:number(optional) — Target storey number — the same integerentity.storyuses (1ground,2first floor,-1basement). Default: the active storey. Folds anentity.story.setActivecall into the create, placing the space at that storey's floor elevation; anypositionoffset is applied on top.
- Returns:
ComponentHandle— The created space - Throws: If the contour is invalid, the height is not positive, or no storey has the given
storeyvalue
ts
const vec3 = snaptrude.core.math.vec3;
const points = [
await vec3.new(0, 0, 0),
await vec3.new(10, 0, 0),
await vec3.new(10, 0, 8),
await vec3.new(0, 0, 8)
];
const outer = await snaptrude.core.geom.create.profileFromLinePoints(points);
const contour = await snaptrude.core.geom.create.contourFromProfile(outer);
const space = await snaptrude.design.create.space(contour, 3, "Living");
// Place a room directly on the second floor — no core.storeys.setActive dance:
const upstairs = await snaptrude.design.create.space(
contour,
3,
"Bedroom",
undefined,
undefined,
undefined,
undefined,
2
);mass(contour, height, label?, position?, massType?)
Create a mass — a generic extruded prism (default massType "Generic mass"). Same footprint-extrude path as space.
- Parameters:
contour:ContourHandle— Footprint (outer + holes)height:number— Extrusion height (> 0)label:string(optional) — Label (maps toroom_type)position:Vec3Handle(optional) — Offset from originmassType:PluginMassType(optional) — Mass type (defaultGeneric mass)
- Returns:
ComponentHandle— The created mass - Throws: If the contour is invalid or the height is not positive
massFromBrep(brep, label?)
Create a mass from a custom solid built with a core.geom.create brep constructor — brepFromFaces, brepFromExtrusion, brepFromLoft, brepFromMesh, or a brep boolean — the free-form counterpart of mass, for solids that are not simple footprint extrusions (pyramids, wedges, faceted volumes).
Only accepts breps minted by a core.geom.create brep constructor — scene-derived breps from design.query.geometry.getBrep are read-only and throw PRECONDITION_FAILED. The mass is created as a generic mass (massType "Generic mass") at the exact coordinates the solid was authored in, committed as a single undo step, and synced to collaborators like any other creator.
- Parameters:
brep:BrepHandle— A solid from acore.geom.createbrep constructorlabel:string(optional) — Label (maps toroom_type)
- Returns:
ComponentHandle— The created mass - Throws: If the brep handle is invalid, or the brep was not created by a
core.geom.createbrep constructor
ts
// A pyramid: square base + 4 triangular sides
const apex = { x: 0, y: 4, z: 0 };
const a = { x: -2, y: 0, z: -2 };
const b = { x: 2, y: 0, z: -2 };
const c = { x: 2, y: 0, z: 2 };
const d = { x: -2, y: 0, z: 2 };
const brep = await snaptrude.core.geom.create.brepFromFaces([
[a, d, c, b],
[a, b, apex],
[b, c, apex],
[c, d, apex],
[d, a, apex]
]);
const pyramid = await snaptrude.design.create.massFromBrep(brep, "Pavilion");slab(contour, thickness, direction?, slabType?)
Create a slab by extruding a footprint contour by thickness (default direction: down).
- Parameters:
contour:ContourHandle— Footprint (outer + holes)thickness:number— Slab thickness (> 0)direction:"up" | "down"(optional, default"down") — Extrusion directionslabType:PluginSlabType(optional) — Slab-type classification
- Returns:
ComponentHandle— The created slab - Throws: If the contour is invalid or the thickness is not positive
floor(contour, thickness, position?)
Create a floor by extruding a footprint contour by thickness (extruded upward).
- Parameters:
contour:ContourHandle— Footprint (outer + holes)thickness:number— Floor thickness (> 0)position:Vec3Handle(optional) — Offset from origin, applied to the footprint before the floor is built, so the storey is derived from the offset elevation and the offset survives undo, redo and reload
- Returns:
ComponentHandle— The created floor - Throws: If the contour is invalid or the thickness is not positive
floors(items)
Create many floors in one undoable operation (bulk plural of floor). Each item extrudes its own footprint contour (outer profile + holes) upward by its own thickness, at its own optional position offset. See Bulk creators for the batch limit and all-or-nothing semantics.
- Parameters:
items:PluginCreateFloorItem[]— one per floor to create (1–1000)
- Returns:
ComponentHandle[]— the created floors, in input order - Throws:
VALIDATIONfor a schema failure (empty array, more than 1000 items, a non-positive thickness) or a degenerate contour;HANDLE_INVALIDfor an unknowncontour/position;OPERATION_FAILEDif extrusion fails (details.itemIndexnames the failing item);METHOD_NOT_PERMITTEDif the plugin may not write
ts
const rect = await snaptrude.core.geom.create.profileRect(5, 4);
const contour = await snaptrude.core.geom.create.contourFromProfile(rect);
const [ground, upper] = await snaptrude.design.create.floors([
{ contour, thickness: 0.1 },
{ contour, thickness: 0.1, position: await snaptrude.core.math.vec3.new(0, 3, 0) }
]);roof(contour, thickness)
Create a roof by extruding a footprint contour by thickness. Created flat (extruded downward); pitch/slope is a separate post-creation edit.
- Parameters:
contour:ContourHandle— Footprint (outer + holes)thickness:number— Roof-slab thickness (> 0)
- Returns:
ComponentHandle— The created roof - Throws: If the contour is invalid or the thickness is not positive
ceiling(contour, thickness, heightFromFloor?)
Create a ceiling by extruding a footprint contour by thickness, lifted heightFromFloor above the floor.
- Parameters:
contour:ContourHandle— Footprint (outer + holes)thickness:number— Ceiling thickness (> 0)heightFromFloor:number(optional, default0) — Lift above the floor
- Returns:
ComponentHandle— The created ceiling - Throws: If the contour is invalid or the thickness is not positive
column(position, crossSection, height, directionUp?)
Create a column by extruding a cross-section contour upward by height from a base position. The column is built to exactly height — unlike the interactive draw tool, no slab-thickness deduction is applied.
- Parameters:
position:Vec3Handle— Base pointcrossSection:ContourHandle— Column cross-section footprintheight:number— Column height (> 0), built exactlydirectionUp:Vec3Handle(optional, default world up) — Up axis; normalized, so magnitude does not scale the height
- Returns:
ComponentHandle— The created column - Throws: If the cross-section is invalid, the height is not positive, or the direction is zero / lies in the cross-section plane (degenerate extrusion)
beam(section, length, direction?)
Create a beam running length along direction. Matches the Snaptrude beam tool's semantics: the beam body hangs below the plane the section is authored on.
For a horizontal direction (the common spanning beam), author the section as an axis-aligned plan rectangle: its X extent is the beam's width and its Z extent is its depth (the vertical drop). The beam starts at the section's centre, runs length along direction, and hangs below the authoring plane by its depth — exactly like an interactively drawn beam. Only axis-aligned rectangular sections are supported horizontally.
For a vertical direction (the default), the section itself is extruded by length, again hanging below the authoring plane. Diagonal directions are rejected.
- Parameters:
section:ContourHandle— Cross-section as an axis-aligned plan rectangle (X extent = width, Z extent = depth)length:number— Beam length alongdirection(> 0)direction:Vec3Handle(optional, default world up) — Beam axis, horizontal or vertical; normalized, so magnitude does not scale the length
- Returns:
ComponentHandle— The created beam - Throws: If the section is invalid (horizontal beams: not an axis-aligned plan rectangle), the length is not positive, or the direction is zero or diagonal
ts
// A 300x600 beam spanning 6 units along X (section: X extent = width 0.3, Z extent = depth 0.6):
const rect = await snaptrude.core.geom.create.profileRect(0.3, 0.6);
const section = await snaptrude.core.geom.create.contourFromProfile(rect);
const alongX = await snaptrude.core.math.vec3.new(1, 0, 0);
const beam = await snaptrude.design.create.beam(section, 6, alongX);walls(profile, height?, thickness?, wallType?, storey?)
Create a connected run of walls from a profile — one wall per curve in the profile's chain, with mitred corners at shared endpoints.
wallType builds the run as a specific wall type — the same searchable list the Draw tab's Wall Type dropdown offers. Pass a name from design.types.list("wall") (the summary's label, or its "wall:…" id). The type is stamped on each wall (driving its construction layers, library data and default material) and, when thickness is omitted, its total layer thickness becomes the thickness — mirroring the dropdown's on-select defaults.
storey targets a floor: the walls are assigned to it and lifted to its base elevation inside the same single undo entry (the Story-dropdown seam setStorey uses). Without it, the storey is geometry-derived — a profile drawn at y = 0 lands on storey 1; the active storey is not consulted.
- Parameters:
profile:ProfileHandle— Ordered curve chain (wall centerlines)height:number(optional, default engine default) — Wall heightthickness:number(optional) — Wall thickness; defaults to thewallType's total layer thickness whenwallTypeis given, else the engine defaultwallType:string(optional, default the engine's generic wall) — Wall type name (or"wall:…"id) fromdesign.types.list("wall")storey:number(optional, default geometry-derived) — Target storey (integer:1ground,2first floor,-1basement — thedesign.create.spaceconvention). Must already exist (entity.story.create/core.storeys.createfirst)
- Returns:
ComponentHandle[]— One handle per wall, in profile order - Throws: If the profile is empty,
wallTypenames no wall type in the project,storeydoes not exist, or wall creation fails
ts
// …built as a specific wall type (layers/material/thickness from the type):
const [brick] = await snaptrude.design.types.list("wall");
const brickWalls = await snaptrude.design.create.walls(centerlines, 3, undefined, brick.label);wallRuns(items)
Create many wall runs in one undoable operation (bulk plural of walls). Each item is one full run — a wall per curve in that item's profile chain, mitred at shared endpoints. Junctions resolve within a run, not between runs: two items meeting at a shared endpoint butt against each other, so keep a continuous chain in one item. Each item carries its own dimensions, wallType and storey, so a single call can build a whole floor plate or several storeys at once. See Bulk creators for the batch limit and all-or-nothing semantics.
The name is wallRuns, not walls: the singular walls already creates one run of walls, so the plural is a plural of runs.
- Parameters:
items:PluginCreateWallRunItem[]— one per run to create (1–1000)
- Returns:
ComponentHandle[][]— one inner array per item, in input order, each holding that run's walls in profile-curve order - Throws:
VALIDATIONfor a schema failure (empty array, more than 1000 items, a non-positive height or thickness, a zero-length curve);HANDLE_INVALIDfor an unknownprofile;PRECONDITION_FAILEDif a profile has no curves,wallTypenames no wall type in the project, orstoreydoes not exist;OPERATION_FAILEDif wall creation fails (details.itemIndexnames the failing item);METHOD_NOT_PERMITTEDif the plugin may not write
ts
const v = snaptrude.core.math.vec3;
const ground = await snaptrude.core.geom.create.profileFromLinePoints([
await v.new(0, 0, 0),
await v.new(8, 0, 0),
await v.new(8, 0, 6)
]);
const upper = await snaptrude.core.geom.create.profileFromLinePoints([
await v.new(0, 0, 0),
await v.new(8, 0, 0)
]);
const [groundWalls, upperWalls] = await snaptrude.design.create.wallRuns([
{ profile: ground, height: 3, thickness: 0.2 },
{ profile: upper, height: 3, storey: 2 }
]);
console.log(groundWalls.length, "walls on the ground floor");staircase(preset, position, label?, structureId?, level?, dimensions?)
Create a staircase from a parametric preset, placed at a point. Geometry is procedurally generated from the chosen preset; there is no footprint input. Placed on the active structure/story unless overridden. The promise resolves only after the geometry union and creation-command capture settle.
- Parameters:
preset:PluginStaircasePreset— The parametric preset to generateposition:Vec3Handle— Placement pointlabel:string(optional) — Optional labelstructureId:string(optional, default active) — Target structurelevel:string(optional, default active/"01") — Target level namedimensions:PluginStaircaseDimensions(optional) — Dimension and quarter-turn rotation overrides applied before creation
- Returns:
ComponentHandle— The created staircase - Throws: If the preset is unknown or placement fails
ts
const stair = await snaptrude.design.create.staircase(
"dogLegged",
await snaptrude.core.math.vec3.new(5, 0, 5),
"Main Stair",
undefined,
undefined,
{ width: 1.2, tread: 0.28, riser: 0.175, angleInDegrees: 90 }
);referenceLines(profile, color?, thickness?, style?, gridTag?)
Create reference lines from a profile — one reference line per curve in the profile's ordered chain. Returns a handle per created line. Optional style (color, thickness, style, gridTag) applies to every created line.
- Parameters:
profile:ProfileHandle— Ordered curve chain (centerlines)color:string(optional, default white) — Hex color"#RRGGBB"thickness:number(optional, default1) — Line thicknessstyle:PluginReferenceLineStyle(optional, default"SOLID") — Line stylegridTag:string(optional, default"A") — Grid tag label
- Returns:
ComponentHandle[]— One handle per curve, in profile order - Throws: If the profile is empty or creation fails
ts
const vec3 = snaptrude.core.math.vec3;
const points = [await vec3.new(0, 0, 0), await vec3.new(10, 0, 0)];
const profile = await snaptrude.core.geom.create.profileFromLinePoints(points);
// skip color and thickness (undefined) to set only the style
const lines = await snaptrude.design.create.referenceLines(profile, undefined, undefined, "DASHED");buildableEnvelope(sitePolygon, lengthUnit, setbacks, verticalCap, floorToFloor, farRatio?, lotCoverageMaxPct?)
Create a new parametric buildable envelope — the regulated volume inside which a building may be massed. The host owns envelope identity: this call mints a buildableEnvelopeId and returns it; pass that id to design.update.buildableEnvelope to modify it later. This is the canonical home of entity.buildableEnvelope.create (now deprecated).
- Parameters:
sitePolygon:PluginBuildableEnvelopePolygonVertex[]— Site polygon vertices inlengthUnit; minimum 3 verticeslengthUnit:"ft" | "m"— Unit used by all length fieldssetbacks:PluginBuildableEnvelopeSetbackTier[]— Setback profile, ground tier first; minimum 1 tierverticalCap:PluginBuildableEnvelopeVerticalCap— Maximum height or floor countfloorToFloor:number— Required floor-to-floor height inlengthUnitfarRatio:number(optional) — Optional FAR value, positive when providedlotCoverageMaxPct:number(optional) — Optional lot coverage cap,0..100
- Returns:
{ buildableEnvelopeId: string }— The id of the created envelope - Throws: If validation fails or generation produces no renderable geometry
ts
const { buildableEnvelopeId } = await snaptrude.design.create.buildableEnvelope(
[
{ x: 0, z: 0 },
{ x: 100, z: 0 },
{ x: 100, z: 80 },
{ x: 0, z: 80 }
],
"ft",
[
{ aboveHeight: 0, front: 10, side: 5, rear: 10 },
{ aboveHeight: 100, front: 20, side: 10, rear: 20 }
],
{ kind: "max_height", maxHeight: 150 },
12
);furniture(catalogId, position, options?, angleInDegrees?)
Place a furniture item from the project library at a world position. Identified by a catalog (library) id — the team object's stable _id for project uploads, or the item's fullName for the general library. Discover ids with design.furniture.listCatalog. Placement is asynchronous (the source mesh is fetched if not cached) and creates one undo entry.
The item is placed at its default rotation unless angleInDegrees is given, which rotates it about the vertical axis at creation time (baked into the same undo entry). To rotate an existing instance instead, use design.transform.rotate.
- Parameters:
catalogId:string— Library id: team_idor generalfullNameposition:Vec3Handle— Absolute world placement pointoptions:object(optional) — Placement options:options.label:string(optional, default auto${name}Ins${n}) — Instance name and readable label (whatdesign.query.getLabelreturns)options.createNewSourceMesh:boolean(optional, defaulttrue) — Legacy flag; it cannot skip recording a source this call brought into the scene (the host owns source persistence, see Bulk creators). Leave it unset.
angleInDegrees:number(optional) — Signed rotation about the vertical axis, in degrees (same convention asdesign.transform.rotate). Applied at creation time. Default: the item's own (unrotated) orientation.
- Returns:
ComponentHandle— The placed furniture instance - Throws: If the catalog id is unknown, the source mesh fails to load, or placement fails;
PRECONDITION_FAILED(details.engineCode: "TOOL_ACTIVE") while the interactive furniture tool is active — finish or cancel it first
ts
const chair = await snaptrude.design.create.furniture(
"6620f1a…", // from design.furniture.listCatalog()
await snaptrude.core.math.vec3.new(3, 0, 5),
{ label: "Chair-01" }
);
// …placed already turned 90° about the vertical axis:
const turned = await snaptrude.design.create.furniture(
"6620f1a…",
await snaptrude.core.math.vec3.new(3, 0, 5),
undefined,
90
);furnitureItems(items)
Place many furniture items in one undoable operation (bulk plural of furniture). Each item names a catalog id and an absolute world position (position.y is the REST elevation — the same grounding contract as the singular creator), with optional label and rotation. A catalog source shared by several items is fetched and recorded once for the whole batch, so the host — not the caller — owns source persistence; there is no per-item createNewSourceMesh. See Bulk creators for the batch limit and all-or-nothing semantics.
The leaf is furnitureItems, not furnitures: furniture is uncountable, so the plural names the countable thing being placed.
- Parameters:
items:PluginCreateFurnitureItem[]— one per instance to place (1–1000)
- Returns:
ComponentHandle[]— the placed instances, in input order - Throws:
VALIDATIONfor a schema failure (empty array, more than 1000 items);HANDLE_INVALIDfor an unknownposition;PRECONDITION_FAILEDif a catalog id is unknown, a furniture tool is active, or a parametric group is active (exit it first, or place items one at a time withfurniture);OPERATION_FAILEDif a source mesh fails to load or placement fails (details.itemIndexnames the failing item);METHOD_NOT_PERMITTEDif the plugin may not write
ts
const v = snaptrude.core.math.vec3;
const [chair] = await snaptrude.design.furniture.listCatalog();
const placed = await snaptrude.design.create.furnitureItems([
{ catalogId: chair.id, position: await v.new(3, 0, 5), label: "Chair-01" },
{ catalogId: chair.id, position: await v.new(4, 0, 5), angleInDegrees: 90 },
{ catalogId: chair.id, position: await v.new(5, 0, 5), angleInDegrees: 180 }
]);door(catalogId, hostWall, position, options?, facing?)
Place a door from the catalog onto a host wall at a world position. Discover catalog ids with design.doors.listCatalog. The point is projected onto the wall; the wall is re-cut as one undo batch.
facing selects the side of the wall the door swings open to (the room it opens into — where the plan symbol draws the swing arc). Any world point clearly on that side works (e.g. the room's center). Without it the engine picks a side nondeterministically (the projected position sits on the wall centreline). Note the interactive tool works the other way round: a door dropped with the cursor on one side swings AWAY from the cursor.
options.hinge selects the jamb the door is hinged on: a world point near that jamb (the end of the opening, a corner of the room — anything nearer one jamb than the other). The door is reflected along the wall when the catalog item's authored hinge side would land on the other jamb. Without it the authored side is kept. facing and hinge are independent: one picks the side of the wall, the other the end of the opening. Symmetric items (sliders) show no visible change. Read the result back with design.doors.getSwingDirection.
The host wall is replaced. Cutting the opening rebuilds the wall as a new entity, so the handle you passed as hostWall stops resolving once the call returns. Recover the surviving wall with design.query.getHost(door); never reuse the handle you passed in.
- Parameters:
catalogId:string— Door catalog idhostWall:ComponentHandle— The wall to host the doorposition:Vec3Handle— World point (projected onto the wall)options:object(optional) —label?: string,hinge?: Vec3Handle(world point near the hinged jamb; default the catalog item's authored side)facing:Vec3Handle(optional, default engine-chosen side) — World point on the side of the wall the door swings open to
- Returns:
ComponentHandle— The placed door - Throws: if the host wall has no mesh, the catalog id is unknown, or the hole cut fails
ts
const [wall] = await snaptrude.design.query.listWalls();
const [item] = await snaptrude.design.doors.listCatalog();
const door = await snaptrude.design.create.door(item.id, wall, position);
// …opening into a specific room (pass a point inside that room):
const intoKitchen = await snaptrude.design.create.door(
item.id,
wall,
position,
undefined,
await snaptrude.core.math.vec3.new(3, 0, 9)
);
// …and hinged on a chosen jamb (a point near that end of the opening):
const hingedEast = await snaptrude.design.create.door(
item.id,
wall,
position,
{ hinge: await snaptrude.core.math.vec3.new(3.5, 0, 5) },
await snaptrude.core.math.vec3.new(3, 0, 9)
);doors(items)
Place many doors into host walls in one undoable operation (bulk plural of door). Each item carries its own catalog id, host wall, world position, and optional facing, hinge, label, width and height — the same fields as a "door" PluginDesignCreateOpeningOptions. See Bulk creators for the batch limit and all-or-nothing semantics.
Host walls are replaced. As with the singular creator, placing an opening re-cuts its host wall: the wall you passed as hostWall stops resolving once the call returns. Several items MAY name the same original wall handle in one call — the host chains the re-cuts internally — but after the call recover the surviving wall with design.query.getHost(door), never by reusing the handle you passed in.
- Parameters:
items:PluginCreateDoorItem[]— one per door to place (1–1000)
- Returns:
ComponentHandle[]— the placed doors, in input order - Throws:
VALIDATIONfor a schema failure (empty array, more than 1000 items, unknown fields, a non-positivewidth/height);HANDLE_INVALIDfor an unknownposition,facingorhinge;PRECONDITION_FAILEDif a catalog id is unknown, ahostWallis not a wall / is locked / is not in the active proposal, the projected point falls outside its host wall, or a door tool is active;OPERATION_FAILEDif a source mesh fails to load or placement fails (details.itemIndexnames the failing item);METHOD_NOT_PERMITTEDif the plugin may not write
ts
const v = snaptrude.core.math.vec3;
const [wall] = await snaptrude.design.query.listWalls({ isSelected: true });
const [entry] = await snaptrude.design.doors.listCatalog();
const [front, side] = await snaptrude.design.create.doors([
{ catalogId: entry.id, hostWall: wall, position: await v.new(2, 0, 5), label: "D-01" },
{ catalogId: entry.id, hostWall: wall, position: await v.new(6, 0, 5), width: 1.2 }
]);
// `wall` was re-cut twice and no longer resolves — ask the opening for its host:
const currentWall = await snaptrude.design.query.getHost(front);window(catalogId, hostWall, position, options?, facing?)
Place a window from the catalog onto a host wall at a world position. Discover catalog ids with design.windows.listCatalog. Same host-wall + hole-cut behaviour as door, including the optional facing side selector and options.hinge jamb selector (both matter for asymmetric windows, e.g. casement swing).
The host wall is replaced. Cutting the opening rebuilds the wall as a new entity, so the handle you passed as hostWall stops resolving once the call returns. Recover the surviving wall with design.query.getHost(window); never reuse the handle you passed in.
- Parameters:
catalogId:string— Window catalog idhostWall:ComponentHandle— The wall to host the windowposition:Vec3Handle— World point (projected onto the wall)options:object(optional) —label?: string,hinge?: Vec3Handle(world point near the hinged jamb; default the catalog item's authored side)facing:Vec3Handle(optional, default engine-chosen side) — World point on the side of the wall the window opens to
- Returns:
ComponentHandle— The placed window - Throws: if the host wall has no mesh, the catalog id is unknown, or the hole cut fails
windows(items)
Place many windows into host walls in one undoable operation (bulk plural of window). Each item carries its own catalog id, host wall, world position, and optional facing, hinge, label, width, height and sillHeight — the same fields as a "window" PluginDesignCreateOpeningOptions. sillHeight is measured from the host wall base to the BOTTOM of the window. See Bulk creators for the batch limit and all-or-nothing semantics.
Host walls are replaced. As with the singular creator, placing an opening re-cuts its host wall: the wall you passed as hostWall stops resolving once the call returns. Several items MAY name the same original wall handle in one call — the host chains the re-cuts internally — but after the call recover the surviving wall with design.query.getHost(window), never by reusing the handle you passed in.
- Parameters:
items:PluginCreateWindowItem[]— one per window to place (1–1000)
- Returns:
ComponentHandle[]— the placed windows, in input order - Throws:
VALIDATIONfor a schema failure (empty array, more than 1000 items, unknown fields, a non-positivewidth/height, a negativesillHeight);HANDLE_INVALIDfor an unknownposition,facingorhinge;PRECONDITION_FAILEDif a catalog id is unknown, ahostWallis not a wall / is locked / is not in the active proposal, the projected point falls outside its host wall, or a window tool is active;OPERATION_FAILEDif a source mesh fails to load or placement fails (details.itemIndexnames the failing item);METHOD_NOT_PERMITTEDif the plugin may not write
ts
const v = snaptrude.core.math.vec3;
const [wall] = await snaptrude.design.query.listWalls({ isSelected: true });
const [casement] = await snaptrude.design.windows.listCatalog();
const placed = await snaptrude.design.create.windows([
{ catalogId: casement.id, hostWall: wall, position: await v.new(2, 0, 5), sillHeight: 0.9 },
{ catalogId: casement.id, hostWall: wall, position: await v.new(5, 0, 5), sillHeight: 0.9 }
]);
const currentWall = await snaptrude.design.query.getHost(placed[0]);opening(options)
Place a configured door or window in one operation. Optional width and height are applied before the instance and wall cut are created, so placement and dimensioning remain one undo entry. For windows, sillHeight positions the opening bottom relative to the host wall base.
For MORE THAN ONE opening, use doors / windows — the same per-item fields, one host round-trip, one undo entry.
- Parameters:
options:PluginDesignCreateOpeningOptions— Kind, catalog, host, placement, and optional dimensions
- Returns:
ComponentHandle— The placed opening - Throws: for invalid dimensions, an unknown catalog item, an invalid or locked host wall, a point outside the wall, source-load failure, or hole-cut failure
ts
const [wall] = await snaptrude.design.query.listWalls({ isSelected: true });
const [item] = await snaptrude.design.windows.listCatalog();
const window = await snaptrude.design.create.opening({
kind: "window",
catalogId: item.id,
hostWall: wall,
position: await snaptrude.core.math.vec3.new(3, 1.5, 5),
width: 1.2,
height: 1.5,
sillHeight: 0.9
});smartLayout(options)
Run the Smart Layout solver — replicate a template (a furnished room graph) into one or more target spaces. Split-gated behind the place_template treatment; Arrange auto-commits headless (no interactive review over the plugin boundary). Every target merges into one undo batch.
- Parameters:
options:PluginSmartLayoutArgs— Template source (templateGroupXORtemplateComponents),targets, andhideTargets?
- Returns:
PluginSmartLayoutResult— The created entities grouped by kind - Throws: when the feature is disabled, the template is missing/invalid, or there are no valid targets
ts
const result = await snaptrude.design.create.smartLayout({
templateComponents: ["furnished-room-mass"],
targets: ["target-1", "target-2"]
});
console.log(result.created.furniture.length, result.skippedWalls);copy(components, displacement, options?)
Copy existing entities, offsetting copy i by displacement * i (i = 1…count), source positions preserved. To place N repeats, bulk-create the seed once then call this once with { count: N - 1 } — one host round-trip for every copy; never re-create the same geometry in a loop. Migrated from the removed tools.copy. In "instance" mode copies stay in the source's instance family where possible; "unique" makes independent geometry. The created copies become the active selection. Undoable.
- Parameters:
components:ComponentHandle[]— Entities to copy (≥1)displacement:Vec3Handle— Per-copy offset (copyiatdisplacement * i)options:object(optional) — Copy options:options.count:number(optional, default1) — Copies per component (positive integer)options.mode:'instance' | 'unique'(optional, default'instance') — Copy mode
- Returns:
ComponentHandle[]— The created copies - Throws: for an empty
componentsarray, a non-positivecount, an unknown/forged handle, or an engine copy failure
ts
const { vec3 } = snaptrude.core.math;
const copies = await snaptrude.design.create.copy(["space-id"], vec3.new(6, 0, 0), {
count: 3,
mode: "unique"
});spaces(items)
Create many spaces in one host round-trip (bulk plural of space) — always prefer this over calling space in a loop; it also commits as a single undoable operation. Migrated from the removed entity.space.bulkCreate. Each item extrudes a contour footprint. For a rectangular (box) space, build the footprint with core.geom.create.profileRect → contourFromProfile. Each item may carry its own storey, so one call can populate several floors at once.
- Parameters:
items:PluginCreateSpaceItem[]— one per space:{ contour: ContourHandle; height: number; label?: string; position?: Vec3Handle; spaceType?: PluginSpaceType; massType?: PluginMassType; departmentId?: PluginDepartmentId; storey?: number }.storeyis the target storey number (default active storey) — seespace.
- Returns:
ComponentHandle[]— the created spaces, in input order
ts
const rect = await snaptrude.core.geom.create.profileRect(4, 3);
const contour = await snaptrude.core.geom.create.contourFromProfile(rect);
// One room on the ground floor, one on the first floor — a single undo step.
const [a, b] = await snaptrude.design.create.spaces([
{ contour, height: 3, label: "R1", storey: 1 },
{ contour, height: 4, label: "R2", storey: 2 }
]);Errors
Failed calls reject with a typed PluginError — see Error Handling. Conditions specific to this namespace:
| Code | Thrown by | When | details |
|---|---|---|---|
VALIDATION | all creators | A dimension that must be > 0 (height / thickness / length) is zero or negative; copy count is not a positive integer; spaces/copy given an empty array | The offending field and value (e.g. extrudeHeight, count) |
VALIDATION | the bulk BIM creators | items is empty or holds more than 1000 items, or an item fails its schema (an unknown field on a doors/windows item, a bad type) | issues — the Zod issues, each with a dot-separated path such as 2.width (item index first) |
HANDLE_INVALID | all creators | A contour / profile / position / direction / component handle cannot be resolved | — |
PRECONDITION_FAILED | walls, wallRuns, referenceLines | The profile has no curves to build from | handles — the profile id |
PRECONDITION_FAILED | staircase | No active structure to place into | engineCode: "NO_ACTIVE_STRUCTURE" |
PRECONDITION_FAILED | furniture, furnitureItems | The catalog id matches no library item | engineCode: "FURNITURE_NOT_FOUND", catalogId |
PRECONDITION_FAILED | space, spaces | A storey target was given but no storey has that value | storey — the requested storey number |
PRECONDITION_FAILED | copy | A source component was replaced by an earlier "instance" copy and is no longer live — re-query and copy the replacement | handles — the replaced source ids |
PRECONDITION_FAILED | massFromBrep | The brep was not minted by a core.geom.create brep constructor (scene-derived breps from design.query.geometry.getBrep are read-only) | handles — the brep id |
VALIDATION | buildableEnvelope | verticalCap + floorToFloor resolve to a non-finite or non-positive height (e.g. an extreme maxFloors × floorToFloor) | verticalCap, floorToFloor |
OPERATION_FAILED | buildableEnvelope | Envelope generation failed — typically the setbacks leave no buildable footprint on the site polygon | warnings — the engine's reasons |
OPERATION_FAILED | all creators | The engine could not build or commit the entity — e.g. the contour is not a valid closed footprint, the created entity got no id, or zero copies were created | Footprint failures carry handles — the contour id; engine Result failures preserve the engine's code in engineCode |
OPERATION_FAILED | the bulk BIM creators | The engine failed part-way through the batch — the batch's scene changes are rolled back, so nothing is created | itemIndex — the zero-based index of the failing item; plus the engine's engineCode where it has one |
Every bulk creator validates and resolves every item before touching the scene, so one bad item rejects the whole call without leaving partially-created entities (all-or-nothing).