Skip to content

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).

ValueDescription
"SOLID"Solid line (default)
"DASHED"Dashed line
"DOT_DASH"Dot-dash line

PluginSlabType

Slab type (mirrors the engine SLAB_TYPES values verbatim).

ValueDescription
"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.

PropertyTypeDescription
widthnumber?Flight width; positive
treadnumber?Tread depth; positive
risernumber?Target riser height; positive and normalized to the storey height
landingWidthnumber?Landing depth; positive and not less than the tread
angleInDegreesnumber?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.

PropertyTypeDescription
kind"door" | "window"Opening type
catalogIdstringCatalog id from design.doors/windows.listCatalog()
hostWallComponentHandleWall receiving the opening
positionVec3HandleApproximate world point projected onto the wall
facingVec3Handle?World point on the side the opening swings open to
hingeVec3Handle?World point near the jamb the opening is hinged on
labelstring?Instance label
widthnumber?Final opening width; positive
heightnumber?Final opening height; positive
sillHeightnumber?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.

PropertyTypeDescription
profileProfileHandleOrdered curve chain (wall centerlines)
heightnumber?Wall height (> 0); default the engine default
thicknessnumber?Wall thickness (> 0); default the wallType's total layer thickness when given, else the engine default
wallTypestring?Wall type name (or "wall:…" id) from design.types.list("wall") — layers/material/thickness; default generic wall
storeynumber?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.

PropertyTypeDescription
contourContourHandleFootprint (outer profile + holes)
thicknessnumberFloor thickness (> 0)
positionVec3Handle?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.

PropertyTypeDescription
catalogIdstringLibrary id: team _id or general fullName
hostWallComponentHandleThe wall to host the door; several items may name the same wall
positionVec3HandleWorld point projected onto the wall
facingVec3Handle?World POINT on the side the door opens into, not a direction; default engine-chosen
hingeVec3Handle?World POINT near the jamb the door is hinged on; default the catalog item's authored side
labelstring?Instance name
widthnumber?Width override (> 0); default the catalog item's
heightnumber?Height override (> 0); default the catalog item's

PluginCreateWindowItem

One window for windows — the PluginCreateDoorItem fields plus sillHeight. Unknown fields are rejected.

PropertyTypeDescription
catalogIdstringLibrary id: team _id or general fullName
hostWallComponentHandleThe wall to host the window; several items may name the same wall
positionVec3HandleWorld point projected onto the wall
facingVec3Handle?World POINT on the side the window opens to, not a direction; default engine-chosen
hingeVec3Handle?World POINT near the jamb the window is hinged on; default the catalog item's authored side
labelstring?Instance name
widthnumber?Width override (> 0); default the catalog item's
heightnumber?Height override (> 0); default the catalog item's
sillHeightnumber?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.

PropertyTypeDescription
catalogIdstringLibrary id: team _id or general fullName
positionVec3HandleAbsolute world placement point (y = rest elevation)
labelstring?Instance name and readable label; default auto ${name}Ins${n}
angleInDegreesnumber?Rotation about the vertical axis, in degrees; default unrotated

PluginSpaceType

Supported space type values (mirrors the internal SpaceType enum).

ValueDescription
"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.

ValueDescription
"NET"Net (usable) area
"GROSS"Gross area
"EXCLUDED"Excluded from area totals

PluginMassType

Supported mass type values (mirrors the internal MASS_TYPES enum).

ValueDescription
"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.

ValueDepartment
"DEFAULT"Default department
"SITE"Site department
"ENVELOPE"Envelope department
"CORE"Core department
UUID stringCustom (user-created) department

PluginSmartLayoutArgs

Arguments for smartLayout. Provide a template as EITHER a templateGroup id OR an explicit templateComponents set (not both).

PropertyTypeDescription
templateGroupstring?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)
templateComponentsComponentHandle[]?An explicit template component set
targetsComponentHandle[]The spaces/masses to lay the template into
hideTargetsboolean? (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).

PropertyTypeDescription
created.wallsComponentHandle[]Created walls
created.furnitureComponentHandle[]Created furniture
created.doorsComponentHandle[]Created doors
created.windowsComponentHandle[]Created windows
created.floorsComponentHandle[]Created floors
skippedWallsnumberWalls 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).

ShapeTypeDescription
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.

ShapeTypeDescription
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.

PropertyTypeDescription
aboveHeightnumberHeight at or above which this tier applies; the ground tier uses 0
frontnumberFront setback for this tier
sidenumberSide setback for this tier
rearnumberRear 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's room_type)
    • position: Vec3Handle (optional) — Offset from origin (default origin)
    • spaceType: PluginSpaceType (optional) — Space-type classification (default Room)
    • massType: PluginMassType (optional) — Mass-type classification (default Room)
    • departmentId: PluginDepartmentId (optional) — Department assignment (default department)
    • storey: number (optional) — Target storey number — the same integer entity.story uses (1 ground, 2 first floor, -1 basement). Default: the active storey. Folds an entity.story.setActive call into the create, placing the space at that storey's floor elevation; any position offset 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 storey value
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 to room_type)
    • position: Vec3Handle (optional) — Offset from origin
    • massType: PluginMassType (optional) — Mass type (default Generic 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 a core.geom.create brep constructor
    • label: string (optional) — Label (maps to room_type)
  • Returns: ComponentHandle — The created mass
  • Throws: If the brep handle is invalid, or the brep was not created by a core.geom.create brep 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 direction
    • slabType: 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:
  • Returns: ComponentHandle[] — the created floors, in input order
  • Throws: VALIDATION for a schema failure (empty array, more than 1000 items, a non-positive thickness) or a degenerate contour; HANDLE_INVALID for an unknown contour / position; OPERATION_FAILED if extrusion fails (details.itemIndex names the failing item); METHOD_NOT_PERMITTED if 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, default 0) — 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 point
    • crossSection: ContourHandle — Column cross-section footprint
    • height: number — Column height (> 0), built exactly
    • directionUp: 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 along direction (> 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 height
    • thickness: number (optional) — Wall thickness; defaults to the wallType's total layer thickness when wallType is given, else the engine default
    • wallType: string (optional, default the engine's generic wall) — Wall type name (or "wall:…" id) from design.types.list("wall")
    • storey: number (optional, default geometry-derived) — Target storey (integer: 1 ground, 2 first floor, -1 basement — the design.create.space convention). Must already exist (entity.story.create / core.storeys.create first)
  • Returns: ComponentHandle[] — One handle per wall, in profile order
  • Throws: If the profile is empty, wallType names no wall type in the project, storey does 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:
  • Returns: ComponentHandle[][] — one inner array per item, in input order, each holding that run's walls in profile-curve order
  • Throws: VALIDATION for a schema failure (empty array, more than 1000 items, a non-positive height or thickness, a zero-length curve); HANDLE_INVALID for an unknown profile; PRECONDITION_FAILED if a profile has no curves, wallType names no wall type in the project, or storey does not exist; OPERATION_FAILED if wall creation fails (details.itemIndex names the failing item); METHOD_NOT_PERMITTED if 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 generate
    • position: Vec3Handle — Placement point
    • label: string (optional) — Optional label
    • structureId: string (optional, default active) — Target structure
    • level: string (optional, default active/"01") — Target level name
    • dimensions: 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, default 1) — Line thickness
    • style: PluginReferenceLineStyle (optional, default "SOLID") — Line style
    • gridTag: 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 in lengthUnit; minimum 3 vertices
    • lengthUnit: "ft" | "m" — Unit used by all length fields
    • setbacks: PluginBuildableEnvelopeSetbackTier[] — Setback profile, ground tier first; minimum 1 tier
    • verticalCap: PluginBuildableEnvelopeVerticalCap — Maximum height or floor count
    • floorToFloor: number — Required floor-to-floor height in lengthUnit
    • farRatio: number (optional) — Optional FAR value, positive when provided
    • lotCoverageMaxPct: 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 _id or general fullName
    • position: Vec3Handle — Absolute world placement point
    • options: object (optional) — Placement options:
      • options.label: string (optional, default auto ${name}Ins${n}) — Instance name and readable label (what design.query.getLabel returns)
      • options.createNewSourceMesh: boolean (optional, default true) — 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 as design.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:
  • Returns: ComponentHandle[] — the placed instances, in input order
  • Throws: VALIDATION for a schema failure (empty array, more than 1000 items); HANDLE_INVALID for an unknown position; PRECONDITION_FAILED if 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 with furniture); OPERATION_FAILED if a source mesh fails to load or placement fails (details.itemIndex names the failing item); METHOD_NOT_PERMITTED if 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 id
    • hostWall: ComponentHandle — The wall to host the door
    • position: 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:
  • Returns: ComponentHandle[] — the placed doors, in input order
  • Throws: VALIDATION for a schema failure (empty array, more than 1000 items, unknown fields, a non-positive width/height); HANDLE_INVALID for an unknown position, facing or hinge; PRECONDITION_FAILED if a catalog id is unknown, a hostWall is 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_FAILED if a source mesh fails to load or placement fails (details.itemIndex names the failing item); METHOD_NOT_PERMITTED if 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 id
    • hostWall: ComponentHandle — The wall to host the window
    • position: 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:
  • Returns: ComponentHandle[] — the placed windows, in input order
  • Throws: VALIDATION for a schema failure (empty array, more than 1000 items, unknown fields, a non-positive width/height, a negative sillHeight); HANDLE_INVALID for an unknown position, facing or hinge; PRECONDITION_FAILED if a catalog id is unknown, a hostWall is 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_FAILED if a source mesh fails to load or placement fails (details.itemIndex names the failing item); METHOD_NOT_PERMITTED if 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:
  • 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 (templateGroup XOR templateComponents), targets, and hideTargets?
  • 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 (copy i at displacement * i)
    • options: object (optional) — Copy options:
      • options.count: number (optional, default 1) — Copies per component (positive integer)
      • options.mode: 'instance' | 'unique' (optional, default 'instance') — Copy mode
  • Returns: ComponentHandle[] — The created copies
  • Throws: for an empty components array, a non-positive count, 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.profileRectcontourFromProfile. 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 }. storey is the target storey number (default active storey) — see space.
  • 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:

CodeThrown byWhendetails
VALIDATIONall creatorsA dimension that must be > 0 (height / thickness / length) is zero or negative; copy count is not a positive integer; spaces/copy given an empty arrayThe offending field and value (e.g. extrudeHeight, count)
VALIDATIONthe bulk BIM creatorsitems 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_INVALIDall creatorsA contour / profile / position / direction / component handle cannot be resolved
PRECONDITION_FAILEDwalls, wallRuns, referenceLinesThe profile has no curves to build fromhandles — the profile id
PRECONDITION_FAILEDstaircaseNo active structure to place intoengineCode: "NO_ACTIVE_STRUCTURE"
PRECONDITION_FAILEDfurniture, furnitureItemsThe catalog id matches no library itemengineCode: "FURNITURE_NOT_FOUND", catalogId
PRECONDITION_FAILEDspace, spacesA storey target was given but no storey has that valuestorey — the requested storey number
PRECONDITION_FAILEDcopyA source component was replaced by an earlier "instance" copy and is no longer live — re-query and copy the replacementhandles — the replaced source ids
PRECONDITION_FAILEDmassFromBrepThe 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
VALIDATIONbuildableEnvelopeverticalCap + floorToFloor resolve to a non-finite or non-positive height (e.g. an extreme maxFloors × floorToFloor)verticalCap, floorToFloor
OPERATION_FAILEDbuildableEnvelopeEnvelope generation failed — typically the setbacks leave no buildable footprint on the site polygonwarnings — the engine's reasons
OPERATION_FAILEDall creatorsThe 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 createdFootprint failures carry handles — the contour id; engine Result failures preserve the engine's code in engineCode
OPERATION_FAILEDthe bulk BIM creatorsThe engine failed part-way through the batch — the batch's scene changes are rolled back, so nothing is createditemIndex — 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).