Appearance
Design Create
Author new scene-committed BIM entities. Every creator takes geometry handles + scalars and returns a ComponentHandle (or ComponentHandle[] for plural creators) — 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 the plural spaces(items) and copy(components, displacement, options) — the whole batch is one host round-trip. Looping a single-item creator (space, mass, slab, …) is one round-trip per item and can trip the rate limit. See Performance.
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".
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 |
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 entity.story.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
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
- Returns:
ComponentHandle— The created floor - Throws: If the contour is invalid or the thickness is not positive
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?)
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.
- 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")
- Returns:
ComponentHandle[]— One handle per wall, in profile order - Throws: If the profile is empty,
wallTypenames no wall type in the project, 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);staircase(preset, position, label?, structureId?, level?)
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.
- 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 name
- 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)
);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");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 nameoptions.createNewSourceMesh:boolean(optional, defaulttrue) — Emit a source-mesh creation command
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
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
);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 which side of the wall the door faces (the room it opens into) — the same convention as approaching the wall from that side with the cursor in the interactive tool. 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).
- Parameters:
catalogId:string— Door catalog idhostWall:ComponentHandle— The wall to host the doorposition:Vec3Handle— World point (projected onto the wall)options:object(optional) —label?: stringfacing:Vec3Handle(optional, default engine-chosen side) — World point on the side of the wall the door faces
- 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)
);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 (matters for asymmetric windows, e.g. casement swing).
- Parameters:
catalogId:string— Window catalog idhostWall:ComponentHandle— The wall to host the windowposition:Vec3Handle— World point (projected onto the wall)options:object(optional) —label?: stringfacing:Vec3Handle(optional, default engine-chosen side) — World point on the side of the wall the window faces
- Returns:
ComponentHandle— The placed window - Throws: if the host wall has no mesh, the catalog id is unknown, or the hole cut fails
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) |
HANDLE_INVALID | all creators | A contour / profile / position / direction / component handle cannot be resolved | — |
PRECONDITION_FAILED | walls, 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 | 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 |
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 |
spaces validates and resolves every item before touching the scene, so one bad item rejects the whole call without leaving partially-created spaces (all-or-nothing).