@snaptrude/plugin-core 0.9.17 → 0.9.18-edgesetbacks-20261021.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. package/AGENTS.md +231 -231
  2. package/CHANGELOG.md +408 -408
  3. package/CLAUDE.md +11 -11
  4. package/api-manifest.json +1428 -712
  5. package/dist/api/analysis/daylight.d.ts +5 -5
  6. package/dist/api/analysis/solar.d.ts +2 -2
  7. package/dist/api/core/camera/index.d.ts +29 -0
  8. package/dist/api/core/camera/index.d.ts.map +1 -1
  9. package/dist/api/core/geom/create/index.d.ts +7 -1
  10. package/dist/api/core/geom/create/index.d.ts.map +1 -1
  11. package/dist/api/core/geom/update/contour.d.ts +8 -2
  12. package/dist/api/core/geom/update/contour.d.ts.map +1 -1
  13. package/dist/api/core/storeys/index.d.ts +5 -2
  14. package/dist/api/core/storeys/index.d.ts.map +1 -1
  15. package/dist/api/core/zoom/index.d.ts +13 -0
  16. package/dist/api/core/zoom/index.d.ts.map +1 -1
  17. package/dist/api/design/bim.d.ts +299 -0
  18. package/dist/api/design/bim.d.ts.map +1 -0
  19. package/dist/api/design/boolean/index.d.ts +2 -0
  20. package/dist/api/design/boolean/index.d.ts.map +1 -1
  21. package/dist/api/design/catalog.d.ts +320 -0
  22. package/dist/api/design/catalog.d.ts.map +1 -0
  23. package/dist/api/design/create/bulk-items.d.ts +65 -25
  24. package/dist/api/design/create/bulk-items.d.ts.map +1 -1
  25. package/dist/api/design/create/index.d.ts +299 -100
  26. package/dist/api/design/create/index.d.ts.map +1 -1
  27. package/dist/api/design/create/opening-fields.d.ts +13 -12
  28. package/dist/api/design/create/opening-fields.d.ts.map +1 -1
  29. package/dist/api/design/delete/index.d.ts +1 -0
  30. package/dist/api/design/delete/index.d.ts.map +1 -1
  31. package/dist/api/design/doors/index.d.ts +18 -24
  32. package/dist/api/design/doors/index.d.ts.map +1 -1
  33. package/dist/api/design/family.d.ts +813 -35
  34. package/dist/api/design/family.d.ts.map +1 -1
  35. package/dist/api/design/index.d.ts +16 -0
  36. package/dist/api/design/index.d.ts.map +1 -1
  37. package/dist/api/design/networks.d.ts +1703 -0
  38. package/dist/api/design/networks.d.ts.map +1 -0
  39. package/dist/api/design/query/grids.d.ts +2 -2
  40. package/dist/api/design/query/index.d.ts +42 -32
  41. package/dist/api/design/query/index.d.ts.map +1 -1
  42. package/dist/api/design/selection/index.d.ts +10 -10
  43. package/dist/api/design/types/index.d.ts +40 -24
  44. package/dist/api/design/types/index.d.ts.map +1 -1
  45. package/dist/api/design/update/index.d.ts +111 -6
  46. package/dist/api/design/update/index.d.ts.map +1 -1
  47. package/dist/api/design/windows/index.d.ts +8 -8
  48. package/dist/api/design/windows/index.d.ts.map +1 -1
  49. package/dist/api/entity/buildableEnvelope.d.ts +84 -2
  50. package/dist/api/entity/buildableEnvelope.d.ts.map +1 -1
  51. package/dist/api/presentation/aiInspiration.d.ts +10 -10
  52. package/dist/api/presentation/annotate.d.ts +38 -22
  53. package/dist/api/presentation/annotate.d.ts.map +1 -1
  54. package/dist/api/presentation/drawingContract.d.ts +32 -0
  55. package/dist/api/presentation/drawingContract.d.ts.map +1 -0
  56. package/dist/api/presentation/drawings.d.ts +3523 -0
  57. package/dist/api/presentation/drawings.d.ts.map +1 -0
  58. package/dist/api/presentation/export.d.ts +23 -5
  59. package/dist/api/presentation/export.d.ts.map +1 -1
  60. package/dist/api/presentation/index.d.ts +32 -12
  61. package/dist/api/presentation/index.d.ts.map +1 -1
  62. package/dist/api/presentation/placedViews.d.ts +106 -6
  63. package/dist/api/presentation/placedViews.d.ts.map +1 -1
  64. package/dist/api/presentation/shapes.d.ts +2 -2
  65. package/dist/api/presentation/sheets.d.ts +175 -19
  66. package/dist/api/presentation/sheets.d.ts.map +1 -1
  67. package/dist/api/presentation/viewPresets.d.ts +222 -0
  68. package/dist/api/presentation/viewPresets.d.ts.map +1 -0
  69. package/dist/api/presentation/views.d.ts +141 -8
  70. package/dist/api/presentation/views.d.ts.map +1 -1
  71. package/dist/api/presentation/walkthroughs.d.ts +6 -6
  72. package/dist/errors/codes.d.ts +3 -1
  73. package/dist/errors/codes.d.ts.map +1 -1
  74. package/dist/index.cjs +3831 -2444
  75. package/dist/index.cjs.map +1 -1
  76. package/dist/index.js +3705 -2444
  77. package/dist/index.js.map +1 -1
  78. package/package.json +1 -1
  79. package/scripts/check-dist-fresh.mjs +22 -22
  80. package/scripts/generate-manifest.mjs +243 -238
  81. package/scripts/generate-manifest.test.mjs +303 -303
  82. package/src/api/analysis/daylight.ts +470 -470
  83. package/src/api/analysis/heatmaps.ts +683 -683
  84. package/src/api/analysis/illuminance.ts +155 -155
  85. package/src/api/analysis/index.ts +61 -61
  86. package/src/api/analysis/shadows.ts +183 -183
  87. package/src/api/analysis/solar.ts +237 -237
  88. package/src/api/analysis/sunlightHours.ts +211 -211
  89. package/src/api/analysis/sunpath.ts +83 -83
  90. package/src/api/analysis/weather.ts +179 -179
  91. package/src/api/core/buildings.ts +485 -485
  92. package/src/api/core/camera/index.ts +696 -667
  93. package/src/api/core/comment/index.ts +376 -376
  94. package/src/api/core/geom/create/index.ts +2139 -2133
  95. package/src/api/core/geom/delete/index.ts +84 -84
  96. package/src/api/core/geom/index.ts +35 -35
  97. package/src/api/core/geom/query/arc.ts +169 -169
  98. package/src/api/core/geom/query/brep.ts +621 -621
  99. package/src/api/core/geom/query/circle.ts +404 -404
  100. package/src/api/core/geom/query/contour.ts +540 -540
  101. package/src/api/core/geom/query/curve.ts +1102 -1102
  102. package/src/api/core/geom/query/edge.ts +130 -130
  103. package/src/api/core/geom/query/face.ts +508 -508
  104. package/src/api/core/geom/query/halfedge.ts +210 -210
  105. package/src/api/core/geom/query/index.ts +63 -63
  106. package/src/api/core/geom/query/profile.ts +473 -473
  107. package/src/api/core/geom/query/vertex.ts +205 -205
  108. package/src/api/core/geom/update/contour.ts +323 -317
  109. package/src/api/core/geom/update/curve.ts +209 -209
  110. package/src/api/core/geom/update/index.ts +29 -29
  111. package/src/api/core/geom/update/profile.ts +379 -379
  112. package/src/api/core/groups.ts +521 -521
  113. package/src/api/core/handles/index.ts +234 -234
  114. package/src/api/core/history/index.ts +84 -84
  115. package/src/api/core/index.ts +99 -99
  116. package/src/api/core/io/export/index.ts +126 -126
  117. package/src/api/core/io/import/index.ts +504 -504
  118. package/src/api/core/io/index.ts +42 -42
  119. package/src/api/core/io/job/index.ts +140 -140
  120. package/src/api/core/io/query/index.ts +71 -71
  121. package/src/api/core/io/terrain/index.ts +360 -360
  122. package/src/api/core/io/underlay/index.ts +705 -705
  123. package/src/api/core/layers.ts +540 -540
  124. package/src/api/core/math/index.ts +20 -20
  125. package/src/api/core/math/quat.ts +172 -172
  126. package/src/api/core/math/vec3.ts +179 -179
  127. package/src/api/core/mode/index.ts +108 -108
  128. package/src/api/core/project/index.ts +479 -479
  129. package/src/api/core/proposals/index.ts +570 -570
  130. package/src/api/core/storeys/index.ts +311 -308
  131. package/src/api/core/storeys/layers.ts +339 -339
  132. package/src/api/core/tags.ts +911 -911
  133. package/src/api/core/units/index.ts +164 -164
  134. package/src/api/core/user.ts +46 -46
  135. package/src/api/core/zoom/index.ts +64 -51
  136. package/src/api/design/bim.ts +210 -0
  137. package/src/api/design/boolean/index.ts +181 -179
  138. package/src/api/design/catalog.ts +203 -0
  139. package/src/api/design/create/bulk-items.ts +294 -254
  140. package/src/api/design/create/grids.ts +137 -137
  141. package/src/api/design/create/index.ts +2178 -1980
  142. package/src/api/design/create/opening-fields.ts +40 -37
  143. package/src/api/design/delete/index.ts +75 -74
  144. package/src/api/design/dimensions.ts +453 -453
  145. package/src/api/design/doors/index.ts +516 -500
  146. package/src/api/design/edit/index.ts +54 -54
  147. package/src/api/design/erase/index.ts +151 -151
  148. package/src/api/design/family.ts +1342 -704
  149. package/src/api/design/furniture/index.ts +319 -319
  150. package/src/api/design/grids.ts +237 -237
  151. package/src/api/design/index.ts +304 -274
  152. package/src/api/design/lock.ts +69 -69
  153. package/src/api/design/materials/index.ts +608 -608
  154. package/src/api/design/networks.ts +607 -0
  155. package/src/api/design/query/geometry/index.ts +319 -319
  156. package/src/api/design/query/grids.ts +197 -197
  157. package/src/api/design/query/index.ts +1236 -1226
  158. package/src/api/design/query/referenceLines.ts +52 -52
  159. package/src/api/design/query/spaces.ts +275 -275
  160. package/src/api/design/selection/index.ts +273 -273
  161. package/src/api/design/shared.ts +40 -40
  162. package/src/api/design/transform/index.ts +254 -254
  163. package/src/api/design/types/index.ts +163 -156
  164. package/src/api/design/update/index.ts +1096 -983
  165. package/src/api/design/visibility.ts +143 -143
  166. package/src/api/design/windows/index.ts +351 -333
  167. package/src/api/entity/buildableEnvelope.ts +397 -322
  168. package/src/api/entity/index.ts +30 -30
  169. package/src/api/entity/referenceLine.ts +235 -235
  170. package/src/api/entity/space.ts +612 -612
  171. package/src/api/entity/story.ts +517 -517
  172. package/src/api/index.ts +47 -47
  173. package/src/api/presentation/aiInspiration.ts +1116 -1116
  174. package/src/api/presentation/annotate.ts +417 -385
  175. package/src/api/presentation/camera.ts +289 -289
  176. package/src/api/presentation/diagrams.ts +194 -194
  177. package/src/api/presentation/drawingContract.ts +70 -0
  178. package/src/api/presentation/drawings.ts +1179 -0
  179. package/src/api/presentation/export.ts +134 -108
  180. package/src/api/presentation/import.ts +159 -159
  181. package/src/api/presentation/index.ts +160 -135
  182. package/src/api/presentation/placedViews.ts +1211 -1120
  183. package/src/api/presentation/sections.ts +420 -420
  184. package/src/api/presentation/shapes.ts +274 -274
  185. package/src/api/presentation/sheets.ts +746 -585
  186. package/src/api/presentation/slideshow.ts +134 -134
  187. package/src/api/presentation/tables.ts +84 -84
  188. package/src/api/presentation/viewPresets.ts +241 -0
  189. package/src/api/presentation/views.ts +742 -673
  190. package/src/api/presentation/walkthroughs.ts +1201 -1201
  191. package/src/api/program/adjacency.ts +164 -164
  192. package/src/api/program/areas.ts +408 -408
  193. package/src/api/program/classification.ts +138 -138
  194. package/src/api/program/cores.ts +83 -83
  195. package/src/api/program/departments.ts +517 -517
  196. package/src/api/program/index.ts +73 -73
  197. package/src/api/program/labels.ts +332 -332
  198. package/src/api/program/layout.ts +422 -422
  199. package/src/api/program/metadata.ts +364 -364
  200. package/src/api/program/metrics.ts +120 -120
  201. package/src/api/program/site.ts +748 -748
  202. package/src/api/program/spreadsheet.ts +2698 -2698
  203. package/src/api/workspace/index.ts +612 -612
  204. package/src/errors/codes.ts +143 -136
  205. package/src/errors/envelope.ts +75 -75
  206. package/src/errors/index.ts +21 -21
  207. package/src/errors/plugin-error.ts +134 -134
  208. package/src/handles.ts +297 -297
  209. package/src/host-utils.ts +67 -67
  210. package/src/index.ts +7 -7
  211. package/src/massParameters.ops.ts +246 -246
  212. package/src/massParameters.ts +427 -427
  213. package/src/types.ts +16 -16
  214. package/test/drawingContract.test.mjs +288 -0
  215. package/test/errors.test.mjs +185 -184
  216. package/test/massParameters.test.mjs +952 -952
  217. package/test/presentationViewSettings.test.mjs +85 -0
  218. package/tsconfig.json +22 -22
  219. package/tsup.config.ts +12 -12
@@ -1,1980 +1,2178 @@
1
- import * as z from "zod"
2
- import { PluginApiReturn } from "../../../types"
3
- import {
4
- BrepHandle,
5
- ComponentHandle,
6
- ContourHandle,
7
- CurveHandle,
8
- ProfileHandle,
9
- Vec3Handle,
10
- } from "../../../handles"
11
- import { PluginGridStyleOptions } from "./grids"
12
- import {
13
- PluginSpaceType,
14
- PluginMassType,
15
- PluginDepartmentId,
16
- } from "../../entity/space"
17
- import {
18
- PluginBuildableEnvelopePolygonVertex,
19
- PluginBuildableEnvelopeSetbackTier,
20
- PluginBuildableEnvelopeVerticalCap,
21
- PluginBuildableEnvelopeCreateResult,
22
- } from "../../entity/buildableEnvelope"
23
- import { PluginOpeningBaseOptions } from "./opening-fields"
24
- import type {
25
- PluginCreateDoorItem,
26
- PluginCreateFloorItem,
27
- PluginCreateFurnitureItem,
28
- PluginCreateWallRunItem,
29
- PluginCreateWindowItem,
30
- } from "./bulk-items"
31
- import { PluginWallTopProfile } from "./bulk-items"
32
-
33
- /**
34
- * `design.create.*` — author new scene-committed BIM entities.
35
- *
36
- * Every creator takes geometry handles + scalars and returns a
37
- * {@linkcode ComponentHandle} (or `ComponentHandle[]` for plural creators;
38
- * `wallRuns` returns one `ComponentHandle[]` per run) — the
39
- * `Component.id` of the created entity, resolvable across the rest of the
40
- * `design.*` surface. Creation is an undoable host call; it **throws** on failure
41
- * (no `Result` wrapper — consistent with `core.geom.create.*`).
42
- *
43
- * Footprint objects take a {@linkcode ContourHandle} (outer profile + holes);
44
- * build one with `snaptrude.core.geom.create.contourFromProfile(s)`.
45
- *
46
- * `door`/`window` place a catalog item into a host wall (the world point is
47
- * projected onto the wall face to locate the opening; the wall is re-cut in one
48
- * undo entry) and throw if the point projects outside the wall. `smartLayout` is
49
- * the one creator returning a grouped {@linkcode PluginSmartLayoutResult} rather
50
- * than a handle — it clones an in-scene template into one or more target spaces,
51
- * merging every target into a single undo batch (see its JSDoc).
52
- *
53
- * Accessed via `snaptrude.design.create`.
54
- */
55
- export abstract class PluginDesignCreateApi {
56
- constructor() {}
57
-
58
- /**
59
- * Create a **space** (a room / mass) by extruding a footprint contour upward.
60
- *
61
- * The `contour` is the bottom footprint (outer profile + optional holes); it
62
- * is extruded by `height` along +Y. The space is placed on the active story
63
- * and assigned a default department unless overridden.
64
- *
65
- * @param contour - Bottom footprint (outer + holes)
66
- * @param height - Extrusion height (Snaptrude units, > 0)
67
- * @param label - Room label (maps to the space's `room_type`)
68
- * @param position - Offset from origin (default origin)
69
- * @param spaceType - Space-type classification (default `Room`)
70
- * @param massType - Mass-type classification (default `Room`)
71
- * @param departmentId - Department assignment (default department)
72
- * @param storey - Target storey number to place the space on — the same integer
73
- * `entity.story` uses (`1` ground, `2` first floor, `-1` basement). Default:
74
- * the active storey. Folds a `core.storeys.setActive` call into the create,
75
- * placing the space at that storey's floor elevation (any `position` offset is
76
- * applied on top). Throws if no storey has that value.
77
- * @returns the {@linkcode ComponentHandle} of the created space
78
- * @throws if the contour is invalid, the height is not positive, or the target
79
- * storey does not exist
80
- *
81
- * @examplePrompt Create a room called Living Room that's 3m tall from this outline
82
- * @examplePrompt Add a new space on the current storey using this footprint
83
- * @examplePrompt Make a bedroom here and assign it to the Residential department
84
- * @examplePrompt Extrude this closed outline into a 10 foot high room
85
- * @examplePrompt Can you create a Kitchen space from the sketch I just drew?
86
- * @examplePrompt Create a bedroom on the second floor from this outline
87
- *
88
- * @performance For MORE THAN ONE space, call `design.create.spaces(items[])` — the whole
89
- * batch is one host round-trip. Looping this single-space creator is N round-trips (slow,
90
- * and a large loop can trip the plugin rate limit).
91
- *
92
- * # Example
93
- * ```ts
94
- * const v = snaptrude.core.math.vec3
95
- * const outer = await snaptrude.core.geom.create.profileFromLinePoints([
96
- * await v.new(0, 0, 0),
97
- * await v.new(10, 0, 0),
98
- * await v.new(10, 0, 8),
99
- * await v.new(0, 0, 8),
100
- * ])
101
- * const contour = await snaptrude.core.geom.create.contourFromProfile(outer)
102
- * const space = await snaptrude.design.create.space(contour, 3, "Living")
103
- * // …place one directly on the second floor (no setActive dance):
104
- * const upstairs = await snaptrude.design.create.space(
105
- * contour, 3, "Bedroom", undefined, undefined, undefined, undefined, 2,
106
- * )
107
- * ```
108
- */
109
- public abstract space(
110
- contour: ContourHandle,
111
- height: number,
112
- label?: string,
113
- position?: Vec3Handle,
114
- spaceType?: PluginSpaceType,
115
- massType?: PluginMassType,
116
- departmentId?: PluginDepartmentId,
117
- storey?: number,
118
- ): PluginApiReturn<ComponentHandle>
119
-
120
- /**
121
- * Create a **mass** — a generic extruded prism (default `massType` "Generic mass").
122
- * Same footprint-extrude path as {@linkcode PluginDesignCreateApi.space}.
123
- *
124
- * @param contour - Footprint (outer + holes)
125
- * @param height - Extrusion height (> 0)
126
- * @param label - Label (maps to `room_type`)
127
- * @param position - Offset from origin
128
- * @param massType - Mass type (default `Generic mass`)
129
- * @returns the {@linkcode ComponentHandle} of the created mass
130
- * @throws if the contour is invalid or the height is not positive
131
- *
132
- * @examplePrompt Create a generic mass 30m tall from this footprint
133
- * @examplePrompt Block out a conceptual massing volume for the site
134
- * @examplePrompt Extrude this shape into a building mass
135
- * @examplePrompt Add a simple study-model volume at the origin
136
- *
137
- * # Example
138
- * ```ts
139
- * const rect = await snaptrude.core.geom.create.profileRect(20, 15)
140
- * const contour = await snaptrude.core.geom.create.contourFromProfile(rect)
141
- * const tower = await snaptrude.design.create.mass(contour, 30, "Tower A", undefined, "Building")
142
- * ```
143
- */
144
- public abstract mass(
145
- contour: ContourHandle,
146
- height: number,
147
- label?: string,
148
- position?: Vec3Handle,
149
- massType?: PluginMassType,
150
- ): PluginApiReturn<ComponentHandle>
151
-
152
- /**
153
- * Create a **mass** from a custom solid built with a `core.geom.create` brep
154
- * constructor (`brepFromFaces`, `brepFromExtrusion`, `brepFromLoft`,
155
- * `brepFromMesh`, or a brep boolean) — the free-form counterpart of
156
- * {@linkcode PluginDesignCreateApi.mass}, for solids that are not simple
157
- * footprint extrusions (pyramids, wedges, faceted volumes).
158
- *
159
- * Only accepts breps **minted by a `core.geom.create` brep constructor** —
160
- * scene-derived breps from `design.query.geometry.getBrep` are read-only and
161
- * throw. The mass is created as a generic mass (`massType` "Generic mass") at
162
- * the exact coordinates the solid was authored in, committed as a single
163
- * undo step, and synced to collaborators like any other creator.
164
- *
165
- * @param brep - A solid from a `core.geom.create` brep constructor
166
- * @param label - Label (maps to `room_type`)
167
- * @returns the {@linkcode ComponentHandle} of the created mass
168
- * @throws if the brep handle is invalid, or the brep was not created by a
169
- * `core.geom.create` brep constructor
170
- *
171
- * @examplePrompt Create a mass from these faces
172
- * @examplePrompt Turn this custom brep into a scene mass
173
- * @examplePrompt Add a pyramid-shaped mass to the model
174
- *
175
- * # Example
176
- * ```ts
177
- * const apex = { x: 0, y: 4, z: 0 }
178
- * const a = { x: -2, y: 0, z: -2 }
179
- * const b = { x: 2, y: 0, z: -2 }
180
- * const c = { x: 2, y: 0, z: 2 }
181
- * const d = { x: -2, y: 0, z: 2 }
182
- * const brep = await snaptrude.core.geom.create.brepFromFaces([
183
- * [a, d, c, b],
184
- * [a, b, apex],
185
- * [b, c, apex],
186
- * [c, d, apex],
187
- * [d, a, apex],
188
- * ])
189
- * const pyramid = await snaptrude.design.create.massFromBrep(brep, "Pavilion")
190
- * ```
191
- */
192
- public abstract massFromBrep(
193
- brep: BrepHandle,
194
- label?: string,
195
- ): PluginApiReturn<ComponentHandle>
196
-
197
- /**
198
- * Create a **slab** by extruding a footprint contour by `thickness`
199
- * (default direction: down).
200
- *
201
- * @param contour - Footprint (outer + holes)
202
- * @param thickness - Slab thickness (> 0)
203
- * @param direction - Extrusion direction (default `"down"`)
204
- * @param slabType - Slab-type classification
205
- * @returns the {@linkcode ComponentHandle} of the created slab
206
- * @throws if the contour is invalid or the thickness is not positive
207
- *
208
- * @examplePrompt Add a 150mm thick slab under this footprint
209
- * @examplePrompt Create a floor slab from this outline
210
- * @examplePrompt Pour a plinth slab over the building footprint
211
- * @examplePrompt Make a basement slab that extrudes downward
212
- * @examplePrompt Give this room outline a 6 inch slab
213
- *
214
- * # Example
215
- * ```ts
216
- * const rect = await snaptrude.core.geom.create.profileRect(12, 10)
217
- * const contour = await snaptrude.core.geom.create.contourFromProfile(rect)
218
- * const slab = await snaptrude.design.create.slab(contour, 0.15, "down", "Plinth")
219
- * ```
220
- */
221
- public abstract slab(
222
- contour: ContourHandle,
223
- thickness: number,
224
- direction?: "up" | "down",
225
- slabType?: PluginSlabType,
226
- ): PluginApiReturn<ComponentHandle>
227
-
228
- /**
229
- * Create a **floor** by extruding a footprint contour by `thickness`
230
- * (extruded upward).
231
- *
232
- * @param contour - Footprint (outer + holes)
233
- * @param thickness - Floor thickness (> 0)
234
- * @param position - Offset from origin
235
- * @returns the {@linkcode ComponentHandle} of the created floor
236
- * @throws if the contour is invalid or the thickness is not positive
237
- *
238
- * @examplePrompt Create a floor from this room outline
239
- * @examplePrompt Add a 100mm thick floor over the footprint
240
- * @examplePrompt Lay flooring across the selected outline
241
- * @examplePrompt Put a floor at this position with 150mm thickness
242
- *
243
- * # Example
244
- * ```ts
245
- * const rect = await snaptrude.core.geom.create.profileRect(5, 4)
246
- * const contour = await snaptrude.core.geom.create.contourFromProfile(rect)
247
- * const floor = await snaptrude.design.create.floor(contour, 0.1)
248
- * ```
249
- *
250
- * @performance For MORE THAN ONE floor, call `design.create.floors(items[])` — one
251
- * host round-trip and ONE undo entry for the whole batch. Looping this single-floor
252
- * creator is N round-trips and N undo entries.
253
- */
254
- public abstract floor(
255
- contour: ContourHandle,
256
- thickness: number,
257
- position?: Vec3Handle,
258
- ): PluginApiReturn<ComponentHandle>
259
-
260
- /**
261
- * Create **many floors** in one undoable operation (bulk plural of
262
- * {@linkcode floor}). Each item extrudes its own footprint contour (outer
263
- * profile + holes) upward by its own `thickness`, at its own optional
264
- * position offset. Validate-all-or-throw; one command.
265
- *
266
- * @param items - One {@linkcode PluginCreateFloorItem} per floor to create
267
- * (≥1, ≤1000)
268
- * @returns the created floors as {@linkcode ComponentHandle}`[]`, in input order
269
- * @throws `VALIDATION` if the items fail the schema (empty array, more than
270
- * 1000 items, a non-positive thickness) or a contour is degenerate;
271
- * `HANDLE_INVALID` if a `contour` or `position` handle is unknown or
272
- * released; `OPERATION_FAILED` if extrusion fails (nothing is created —
273
- * `details.itemIndex` is the failing item); `METHOD_NOT_PERMITTED` if the
274
- * plugin may not write.
275
- *
276
- * @examplePrompt Create floors for all these room outlines at once
277
- * @examplePrompt Add a floor to every space in one operation
278
- * @examplePrompt Bulk create the floor plates for this building
279
- * @examplePrompt Lay 100mm floors across these five footprints in one undo step
280
- * @examplePrompt Create the ground and first floor slabs together
281
- *
282
- * @performance Bulk creator — the whole batch is ONE host round-trip and ONE undo
283
- * entry. Always prefer this over calling `design.create.floor` in a loop: build the
284
- * full `items[]` array first (all contours and offsets up front), then make one call.
285
- *
286
- * # Example
287
- * ```ts
288
- * const rect = await snaptrude.core.geom.create.profileRect(5, 4)
289
- * const contour = await snaptrude.core.geom.create.contourFromProfile(rect)
290
- * const [ground, upper] = await snaptrude.design.create.floors([
291
- * { contour, thickness: 0.1 },
292
- * { contour, thickness: 0.1, position: await snaptrude.core.math.vec3.new(0, 3, 0) },
293
- * ])
294
- * ```
295
- */
296
- public abstract floors(
297
- items: PluginCreateFloorItem[],
298
- ): PluginApiReturn<ComponentHandle[]>
299
-
300
- /**
301
- * Create a **roof** by extruding a footprint contour by `thickness`. Created
302
- * flat (extruded downward); pitch/slope is a separate post-creation edit.
303
- *
304
- * @param contour - Footprint (outer + holes)
305
- * @param thickness - Roof-slab thickness (> 0)
306
- * @returns the {@linkcode ComponentHandle} of the created roof
307
- * @throws if the contour is invalid or the thickness is not positive
308
- *
309
- * @examplePrompt Add a flat roof over the building footprint
310
- * @examplePrompt Create a roof 200mm thick from this outline
311
- * @examplePrompt Cover the top storey with a roof slab
312
- * @examplePrompt Put a roof on this building
313
- *
314
- * # Example
315
- * ```ts
316
- * const rect = await snaptrude.core.geom.create.profileRect(12, 10)
317
- * const contour = await snaptrude.core.geom.create.contourFromProfile(rect)
318
- * const roof = await snaptrude.design.create.roof(contour, 0.2)
319
- * ```
320
- */
321
- public abstract roof(
322
- contour: ContourHandle,
323
- thickness: number,
324
- ): PluginApiReturn<ComponentHandle>
325
-
326
- /**
327
- * Create a **ceiling** by extruding a footprint contour by `thickness`, lifted
328
- * `heightFromFloor` above the floor.
329
- *
330
- * @param contour - Footprint (outer + holes)
331
- * @param thickness - Ceiling thickness (> 0)
332
- * @param heightFromFloor - Lift above the floor (default 0)
333
- * @returns the {@linkcode ComponentHandle} of the created ceiling
334
- * @throws if the contour is invalid or the thickness is not positive
335
- *
336
- * @examplePrompt Add a false ceiling 2.7m above the floor
337
- * @examplePrompt Create a ceiling over this room footprint
338
- * @examplePrompt Put a dropped ceiling in the living room at 3 metres
339
- * @examplePrompt Add a 50mm thick ceiling lifted 2800mm from floor level
340
- *
341
- * # Example
342
- * ```ts
343
- * const rect = await snaptrude.core.geom.create.profileRect(5, 4)
344
- * const contour = await snaptrude.core.geom.create.contourFromProfile(rect)
345
- * const ceiling = await snaptrude.design.create.ceiling(contour, 0.05, 2.8)
346
- * ```
347
- */
348
- public abstract ceiling(
349
- contour: ContourHandle,
350
- thickness: number,
351
- heightFromFloor?: number,
352
- ): PluginApiReturn<ComponentHandle>
353
-
354
- /**
355
- * Create a **column** by extruding a cross-section contour upward by `height`
356
- * from a base `position`. The column is built to EXACTLY `height` — unlike
357
- * the interactive draw tool, no slab-thickness deduction is applied.
358
- *
359
- * @param position - Base point
360
- * @param crossSection - Column cross-section footprint
361
- * @param height - Column height (> 0), built exactly
362
- * @param directionUp - Up axis (default world up); normalized by the host, so
363
- * magnitude does not scale the height
364
- * @returns the {@linkcode ComponentHandle} of the created column
365
- * @throws if the cross-section is invalid, the height is not positive, or the
366
- * direction is zero / lies in the cross-section plane (degenerate extrusion)
367
- *
368
- * @examplePrompt Add a 3m tall column at this point
369
- * @examplePrompt Place a column at the grid intersection
370
- * @examplePrompt Create a 450mm square concrete column 4 metres high
371
- * @examplePrompt Stand a column up from this base point
372
- *
373
- * # Example
374
- * ```ts
375
- * const rect = await snaptrude.core.geom.create.profileRect(0.45, 0.45)
376
- * const crossSection = await snaptrude.core.geom.create.contourFromProfile(rect)
377
- * const base = await snaptrude.core.math.vec3.new(4, 0, 4)
378
- * const column = await snaptrude.design.create.column(base, crossSection, 3)
379
- * ```
380
- */
381
- public abstract column(
382
- position: Vec3Handle,
383
- crossSection: ContourHandle,
384
- height: number,
385
- directionUp?: Vec3Handle,
386
- ): PluginApiReturn<ComponentHandle>
387
-
388
- /**
389
- * Create a **beam** by extruding a cross-section contour along `direction` by
390
- * `length`. Matches the Snaptrude beam tool's semantics: the beam body hangs
391
- * BELOW the plane the section is authored on.
392
- *
393
- * For a **horizontal** `direction` (the common spanning beam), author the
394
- * section as an axis-aligned plan rectangle: its X extent is the beam's
395
- * width, its Z extent is the beam's depth (the vertical drop). The beam runs
396
- * `length` along `direction` starting at the section's centre, hanging below
397
- * the authoring plane by its depth — exactly like an interactively drawn
398
- * beam. Only axis-aligned rectangular sections are supported horizontally.
399
- *
400
- * For a **vertical** `direction` (the default), the section itself is
401
- * extruded by `length`, again hanging below the authoring plane. Diagonal
402
- * directions are rejected.
403
- *
404
- * @param section - Beam cross-section, an axis-aligned rectangle in plan
405
- * (X extent = width, Z extent = depth)
406
- * @param length - Beam length along `direction` (> 0); direction is
407
- * normalized by the host, so magnitude does not scale the length
408
- * @param direction - Beam axis: horizontal for a spanning beam, or vertical
409
- * (default world up). Diagonal directions throw.
410
- * @returns the {@linkcode ComponentHandle} of the created beam
411
- * @throws if the section is invalid (horizontal beams: not an axis-aligned
412
- * plan rectangle), the length is not positive, or the direction is zero or
413
- * diagonal
414
- *
415
- * @examplePrompt Add a beam spanning 6 metres between the columns
416
- * @examplePrompt Create a horizontal beam along the X axis
417
- * @examplePrompt Run a 300x600 beam across the room
418
- * @examplePrompt Extrude this cross-section into a 5m long beam
419
- *
420
- * # Example
421
- * ```ts
422
- * const rect = await snaptrude.core.geom.create.profileRect(0.3, 0.6)
423
- * const section = await snaptrude.core.geom.create.contourFromProfile(rect)
424
- * const alongX = await snaptrude.core.math.vec3.new(1, 0, 0)
425
- * const beam = await snaptrude.design.create.beam(section, 6, alongX)
426
- * ```
427
- */
428
- public abstract beam(
429
- section: ContourHandle,
430
- length: number,
431
- direction?: Vec3Handle,
432
- ): PluginApiReturn<ComponentHandle>
433
-
434
- /**
435
- * Create a connected run of **walls** from a profile — one wall per curve in
436
- * the profile's chain, with mitred corners at shared endpoints.
437
- *
438
- * @param profile - Ordered curve chain (wall centerlines)
439
- * @param height - Wall height (default engine default)
440
- * @param thickness - Wall thickness (default: the `wallType`'s total layer
441
- * thickness when `wallType` is given, else the engine default)
442
- * @param wallType - Wall type to build with — the same searchable list the
443
- * Draw tab's Wall Type dropdown offers. Pass a name from
444
- * `design.types.list("wall")` (the summary's `label`, or its `"wall:…"` id).
445
- * Drives the wall's construction layers and default material, and — when
446
- * `thickness` is omitted — its total layer thickness. Default: the engine's
447
- * generic wall.
448
- * @param storey - Target storey (integer: `1` ground, `2` first floor, `-1`
449
- * basement — same convention as `design.create.space`). The walls are
450
- * assigned to it and lifted to its base elevation, all inside the same
451
- * single undo entry. The storey must already exist
452
- * (`core.storeys.create` first). Default: geometry-derived — a profile
453
- * drawn at y = 0 lands on storey 1; the ACTIVE storey is NOT consulted.
454
- * @param topProfiles - Optional top edge per curve of `profile` (same order):
455
- * a {@linkcode PluginWallTopProfile} makes that wall raked / gabled; `null`
456
- * or a missing entry keeps it flat at `height`. Straight curves only. Such
457
- * walls are non-parametric (see {@linkcode PluginWallTopProfile}).
458
- * @returns a {@linkcode ComponentHandle}`[]` (one per wall), in profile order
459
- * @throws if the profile is empty, `wallType` names no wall type in the
460
- * project, `storey` does not exist, `topProfiles` has more entries than the
461
- * profile has curves or names an arc curve, a top profile drops to the wall
462
- * base at a mitred corner, or wall creation fails
463
- *
464
- * @examplePrompt Draw walls along this centerline
465
- * @examplePrompt Create 3m high, 200mm thick walls from these points
466
- * @examplePrompt Build a wall run around the room outline
467
- * @examplePrompt Turn this polyline sketch into walls
468
- * @examplePrompt Add walls with mitred corners along the path
469
- * @examplePrompt Draw brick walls with plaster along this outline
470
- * @examplePrompt Build the perimeter in exposed concrete walls
471
- * @examplePrompt Create a raked wall that slopes from 3m to 5m
472
- * @examplePrompt Build a gable end wall with the ridge in the middle
473
- * @examplePrompt Draw a wall with a sloped top edge
474
- *
475
- * # Example
476
- * ```ts
477
- * const v = snaptrude.core.math.vec3
478
- * const centerlines = await snaptrude.core.geom.create.profileFromLinePoints([
479
- * await v.new(0, 0, 0),
480
- * await v.new(8, 0, 0),
481
- * await v.new(8, 0, 6),
482
- * ])
483
- * const walls = await snaptrude.design.create.walls(centerlines, 3, 0.2)
484
- * // …built as a specific wall type (layers/material/thickness from the type):
485
- * const [brick] = await snaptrude.design.types.list("wall")
486
- * const brickWalls = await snaptrude.design.create.walls(
487
- * centerlines, 3, undefined, brick.label,
488
- * )
489
- * // …a raked wall (3m at the start, 5m at the end) on a single centerline:
490
- * const run = await snaptrude.core.geom.create.profileFromCurves([
491
- * await snaptrude.core.geom.create.line(await v.new(0, 0, 10), await v.new(8, 0, 10)),
492
- * ])
493
- * const [raked] = await snaptrude.design.create.walls(run, undefined, 0.2, undefined, undefined, [
494
- * [{ along: 0, height: 3 }, { along: 1, height: 5 }],
495
- * ])
496
- * ```
497
- *
498
- * @performance For MORE THAN ONE run, call `design.create.wallRuns(items[])` — one
499
- * host round-trip and ONE undo entry for every run in the batch. Looping this
500
- * single-run creator is N round-trips and N undo entries.
501
- */
502
- public abstract walls(
503
- profile: ProfileHandle,
504
- height?: number,
505
- thickness?: number,
506
- wallType?: string,
507
- storey?: number,
508
- topProfiles?: (PluginWallTopProfile | null)[],
509
- ): PluginApiReturn<ComponentHandle[]>
510
-
511
- /**
512
- * Create **many wall runs** in one undoable operation (bulk plural of
513
- * {@linkcode walls}). Each item is one full run — a wall per curve in that
514
- * item's profile chain, mitred at shared endpoints. Junctions resolve within a
515
- * run, not between runs. Each item carries its own
516
- * dimensions, `wallType` and `storey`, so a single call can build a whole
517
- * floor plate or several storeys at once. Per-curve `topProfiles` make raked
518
- * or gabled walls (see {@linkcode PluginWallTopProfile}). Validate-all-or-throw;
519
- * one command.
520
- *
521
- * @param items - One {@linkcode PluginCreateWallRunItem} per run to create
522
- * (≥1, ≤1000)
523
- * @returns one {@linkcode ComponentHandle}`[]` per item, in input order —
524
- * each inner array holding that run's walls in profile-curve order
525
- * @throws `VALIDATION` if the items fail the schema (empty array, more than
526
- * 1000 items, a non-positive height or thickness, a zero-length curve);
527
- * `HANDLE_INVALID` if a `profile` handle is unknown or released;
528
- * `PRECONDITION_FAILED` if a profile has no curves, `wallType` names no
529
- * wall type in the project, or `storey` does not exist;
530
- * `OPERATION_FAILED` if wall creation fails (nothing is created —
531
- * `details.itemIndex` is the failing item); `METHOD_NOT_PERMITTED` if the
532
- * plugin may not write.
533
- *
534
- * @examplePrompt Draw all the walls of this floor plan at once
535
- * @examplePrompt Build every room's perimeter in one operation
536
- * @examplePrompt Bulk create wall runs from these centerlines
537
- * @examplePrompt Create the walls for both storeys in a single undo step
538
- * @examplePrompt Turn these polylines into 200mm brick walls together
539
- * @examplePrompt Import these raked walls with their top profiles in one go
540
- *
541
- * @performance Bulk creator — the whole batch is ONE host round-trip and ONE undo
542
- * entry. Always prefer this over calling `design.create.walls` in a loop: build the
543
- * full `items[]` array first (all profiles up front), then make one call.
544
- *
545
- * # Example
546
- * ```ts
547
- * const v = snaptrude.core.math.vec3
548
- * const ground = await snaptrude.core.geom.create.profileFromLinePoints([
549
- * await v.new(0, 0, 0),
550
- * await v.new(8, 0, 0),
551
- * await v.new(8, 0, 6),
552
- * ])
553
- * const upper = await snaptrude.core.geom.create.profileFromLinePoints([
554
- * await v.new(0, 0, 0),
555
- * await v.new(8, 0, 0),
556
- * ])
557
- * const [groundWalls, upperWalls] = await snaptrude.design.create.wallRuns([
558
- * { profile: ground, height: 3, thickness: 0.2 },
559
- * { profile: upper, height: 3, storey: 2 },
560
- * // one gable wall: 3m at both ends, 5m ridge in the middle
561
- * { profile: upper, thickness: 0.2, storey: 3,
562
- * topProfiles: [[{ along: 0, height: 3 }, { along: 0.5, height: 5 }, { along: 1, height: 3 }]] },
563
- * ])
564
- * console.log(groundWalls.length, "walls on the ground floor")
565
- * ```
566
- */
567
- public abstract wallRuns(
568
- items: PluginCreateWallRunItem[],
569
- ): PluginApiReturn<ComponentHandle[][]>
570
-
571
- /**
572
- * Create a **staircase** from a parametric preset, placed at a point.
573
- *
574
- * Geometry is procedurally generated from the chosen `preset`; there is no
575
- * footprint input. Placed on the active structure/story unless overridden.
576
- * The promise resolves only after the staircase's asynchronous geometry union
577
- * and creation-command capture settle; a union failure rejects the call.
578
- *
579
- * @param preset - The parametric preset to generate
580
- * @param position - Placement point
581
- * @param label - Optional label
582
- * @param structureId - Target structure (default active)
583
- * @param level - Target level name (default active/`"01"`)
584
- * @param dimensions - Optional positive dimension overrides in Snaptrude engine
585
- * units, plus a placement angle in 90-degree increments. Overrides are
586
- * applied to the staircase parameters before the creation command runs.
587
- * @returns the {@linkcode ComponentHandle} of the created staircase
588
- * @throws if the preset is unknown or placement fails
589
- *
590
- * @examplePrompt Add a dog-legged staircase at this location
591
- * @examplePrompt Place a straight stair on level 01
592
- * @examplePrompt Create an L-shaped staircase in the lobby
593
- * @examplePrompt Insert a staircase near the entrance
594
- * @examplePrompt Put amphitheatre steps at these coordinates
595
- *
596
- * # Example
597
- * ```ts
598
- * const stair = await snaptrude.design.create.staircase(
599
- * "dogLegged",
600
- * await snaptrude.core.math.vec3.new(5, 0, 5),
601
- * )
602
- * ```
603
- */
604
- public abstract staircase(
605
- preset: PluginStaircasePreset,
606
- position: Vec3Handle,
607
- label?: string,
608
- structureId?: string,
609
- level?: string,
610
- dimensions?: PluginStaircaseDimensions,
611
- ): PluginApiReturn<ComponentHandle>
612
-
613
- /**
614
- * Create **reference lines** from a profile — one reference line per curve in
615
- * the profile's ordered chain. Returns a handle per created line.
616
- *
617
- * Optional style (`color`, `thickness`, `style`, `gridTag`) applies to
618
- * every created line.
619
- *
620
- * @param profile - Ordered curve chain (centerlines)
621
- * @param color - Hex color `"#RRGGBB"` (default white)
622
- * @param thickness - Line thickness (default 1)
623
- * @param style - Line style (default `SOLID`)
624
- * @param gridTag - Grid tag label (default `"A"`)
625
- * @returns a {@linkcode ComponentHandle}`[]` (one per curve), in profile order
626
- * @throws if the profile is empty or creation fails
627
- *
628
- * @examplePrompt Draw a reference line between these two points
629
- * @examplePrompt Add dashed guide lines along this path
630
- * @examplePrompt Create grid lines tagged A through the plan
631
- * @examplePrompt Set up construction lines for the column grid
632
- * @examplePrompt Mark a red reference line across the site
633
- *
634
- * # Example
635
- * ```ts
636
- * const v = snaptrude.core.math.vec3
637
- * const profile = await snaptrude.core.geom.create.profileFromLinePoints([
638
- * await v.new(0, 0, 0),
639
- * await v.new(10, 0, 0),
640
- * ])
641
- * const lines = await snaptrude.design.create.referenceLines(
642
- * profile,
643
- * undefined,
644
- * undefined,
645
- * "DASHED",
646
- * )
647
- * ```
648
- */
649
- public abstract referenceLines(
650
- profile: ProfileHandle,
651
- color?: string,
652
- thickness?: number,
653
- style?: PluginReferenceLineStyle,
654
- gridTag?: string,
655
- ): PluginApiReturn<ComponentHandle[]>
656
-
657
- /**
658
- * Create a free rectangular **structural grid** — one grid group with two
659
- * perpendicular line families (`linesX` lines spaced `spacingX` apart, and
660
- * `linesY` lines spaced `spacingY` apart) on the active storey. One undoable
661
- * creation.
662
- *
663
- * Units: spacings in Snaptrude units; `angle` in degrees (CCW about +Y,
664
- * default 0). Without `center` the grid is centred on the current viewport
665
- * (the native tool's behavior) — pass `center` for deterministic placement.
666
- *
667
- * @param linesX - Line count of the first family (integer ≥ 1)
668
- * @param linesY - Line count of the second family (integer ≥ 1)
669
- * @param spacingX - First-family spacing (Snaptrude units, > 0)
670
- * @param spacingY - Second-family spacing (Snaptrude units, > 0)
671
- * @param options - `angle`, `center`, and {@linkcode PluginGridStyleOptions}
672
- * @returns the created grid group's {@linkcode ComponentHandle}
673
- *
674
- * @examplePrompt Create a 5 by 8 structural grid with 6 metre spacing
675
- * @examplePrompt Add a column grid rotated 30 degrees
676
- * @examplePrompt Set up a structural grid at the site origin
677
- *
678
- * # Example
679
- * ```ts
680
- * const centre = await snaptrude.core.math.vec3.new(0, 0, 0)
681
- * const grid = await snaptrude.design.create.grid(5, 8, 6, 6, { center: centre })
682
- * ```
683
- */
684
- public abstract grid(
685
- linesX: number,
686
- linesY: number,
687
- spacingX: number,
688
- spacingY: number,
689
- options?: {
690
- angle?: number
691
- center?: Vec3Handle
692
- } & PluginGridStyleOptions,
693
- ): PluginApiReturn<ComponentHandle>
694
-
695
- /**
696
- * Create a **structural grid from a single curve** — one grid group holding
697
- * one wing with one line (the draw tool's output). The curve may be a line
698
- * or an arc; circles are rejected with `VALIDATION`. Undoable.
699
- *
700
- * @param curve - The grid line's geometry ({@linkcode CurveHandle}, world space)
701
- * @param options - `tag` (defaults to the draw tool's next tag) and
702
- * {@linkcode PluginGridStyleOptions}
703
- * @returns the created grid group's {@linkcode ComponentHandle}
704
- *
705
- * @examplePrompt Draw a grid line from A to B
706
- * @examplePrompt Add a curved grid line through these points
707
- * @examplePrompt Create grid line "C" along this wall
708
- *
709
- * # Example
710
- * ```ts
711
- * const { vec3 } = snaptrude.core.math
712
- * const from = await vec3.new(0, 0, 0)
713
- * const to = await vec3.new(30, 0, 0)
714
- * const curve = await snaptrude.core.geom.create.line(from, to)
715
- * const grid = await snaptrude.design.create.gridFromCurve(curve, { tag: "A" })
716
- * ```
717
- */
718
- public abstract gridFromCurve(
719
- curve: CurveHandle,
720
- options?: {
721
- tag?: string
722
- } & PluginGridStyleOptions,
723
- ): PluginApiReturn<ComponentHandle>
724
-
725
- /**
726
- * Generate **structural grids fitted to mass footprints** — one grid group
727
- * per footprint cluster, with counts and orientation derived from each
728
- * footprint's geometry and the given spacings. All created groups commit as
729
- * ONE undoable unit.
730
- *
731
- * Lenient like the native tool: masses without an extractable footprint
732
- * (no B-rep, degenerate top face) are skipped; throws `VALIDATION` only when
733
- * NONE of the given masses yields a footprint.
734
- *
735
- * @param masses - Masses whose footprints seat the grids
736
- * @param spacingX - First-family spacing (Snaptrude units, > 0)
737
- * @param spacingY - Second-family spacing (Snaptrude units, > 0)
738
- * @param options - {@linkcode PluginGridStyleOptions}
739
- * @returns the created grid groups' {@linkcode ComponentHandle}`[]`
740
- *
741
- * @examplePrompt Generate grids for the selected masses
742
- * @examplePrompt Auto-generate a 6m structural grid on this building
743
- * @examplePrompt Fit column grids to every mass on this storey
744
- *
745
- * # Example
746
- * ```ts
747
- * const masses = await snaptrude.design.query.listMasses({ storeys: [1] })
748
- * const grids = await snaptrude.design.create.gridsFromMasses(masses, 6, 6)
749
- * ```
750
- */
751
- public abstract gridsFromMasses(
752
- masses: ComponentHandle[],
753
- spacingX: number,
754
- spacingY: number,
755
- options?: PluginGridStyleOptions,
756
- ): PluginApiReturn<ComponentHandle[]>
757
-
758
- /**
759
- * Create **columns at every grid-line intersection** of the given grid
760
- * groups, sized `width` × `depth` and oriented to the grid axes, on the
761
- * grids' storey. All columns commit as ONE undoable unit.
762
- *
763
- * @param grids - Grid groups whose intersections seat the columns
764
- * @param width - Column width (Snaptrude units, > 0)
765
- * @param depth - Column depth (Snaptrude units, > 0)
766
- * @returns the created columns' {@linkcode ComponentHandle}`[]`
767
- * @throws `VALIDATION` when an id does not resolve to a grid group;
768
- * `OPERATION_FAILED` when the grids have no line intersections
769
- *
770
- * @examplePrompt Generate columns on this grid
771
- * @examplePrompt Place 450mm square columns at every grid intersection
772
- *
773
- * # Example
774
- * ```ts
775
- * const grids = await snaptrude.design.query.listGrids()
776
- * const columns = await snaptrude.design.create.columnsFromGrids(grids, 0.45, 0.45)
777
- * ```
778
- */
779
- public abstract columnsFromGrids(
780
- grids: ComponentHandle[],
781
- width: number,
782
- depth: number,
783
- ): PluginApiReturn<ComponentHandle[]>
784
-
785
- /**
786
- * Place a **furniture** item from the project library at a world position.
787
- *
788
- * Identified by a **catalog (library) id** — the team object's stable `_id` for
789
- * project uploads, or the item's `fullName` for the general library. Discover ids
790
- * with {@linkcode PluginDesignFurnitureApi.listCatalog}. Placement is asynchronous
791
- * (the source mesh is fetched if not cached) and creates one undo entry.
792
- *
793
- * Placed at the item's default rotation unless `angleInDegrees` is given, which
794
- * rotates it about the vertical axis at creation time (baked into the same undo
795
- * entry). To rotate an existing instance instead, use `design.transform.rotate`.
796
- *
797
- * @param catalogId - Library id: team `_id` or general `fullName`
798
- * @param position - Absolute world placement point. `position.y` is the REST
799
- * elevation: the item is grounded so its bounding-box base sits exactly at
800
- * `position.y` (the same surface-flush contract as interactive drag-drop) —
801
- * pass the floor/storey elevation to stand furniture on it; never add half
802
- * the item's height yourself.
803
- * @param options - Optional placement options: `label` — instance name and
804
- * readable label (`design.query.getLabel`; default auto `${name}Ins${n}`);
805
- * `createNewSourceMesh` — legacy flag, kept for compatibility. The host owns
806
- * source-mesh persistence: a catalog source is recorded exactly once, by the
807
- * first placement that brings it in, and this flag cannot skip that record.
808
- * @param angleInDegrees - Optional signed rotation about the vertical axis, in
809
- * degrees (same convention as {@linkcode PluginDesignTransformApi.rotate}).
810
- * Applied at creation time so it is part of the placement's single undo entry.
811
- * Default: the item's own (unrotated) orientation.
812
- * @returns the {@linkcode ComponentHandle} of the placed furniture instance
813
- * @throws if the catalog id is unknown, the source mesh fails to load, or placement fails;
814
- * `PRECONDITION_FAILED` (`details.engineCode: "TOOL_ACTIVE"`) while the interactive
815
- * furniture tool is active — finish or cancel it first
816
- *
817
- * @examplePrompt Place a chair from the library at this spot
818
- * @examplePrompt Add a sofa from the furniture catalog to the living room
819
- * @examplePrompt Drop a desk into the scene at 3, 0, 5
820
- * @examplePrompt Insert the uploaded bed model in the bedroom
821
- * @examplePrompt Put a table from my project library here and name it Table-01
822
- * @examplePrompt Place a chair here rotated 90 degrees
823
- *
824
- * # Example
825
- * ```ts
826
- * const chair = await snaptrude.design.create.furniture(
827
- * "6620f1a…", // from design.furniture.listCatalog()
828
- * await snaptrude.core.math.vec3.new(3, 0, 5),
829
- * { label: "Chair-01" },
830
- * )
831
- * // …placed already turned 90° about the vertical axis:
832
- * const turned = await snaptrude.design.create.furniture(
833
- * "6620f1a…",
834
- * await snaptrude.core.math.vec3.new(3, 0, 5),
835
- * undefined,
836
- * 90,
837
- * )
838
- * ```
839
- *
840
- * @performance For MORE THAN ONE item, call `design.create.furnitureItems(items[])` —
841
- * one host round-trip and ONE undo entry, and a catalog source shared by several
842
- * items is fetched once. Looping this single-item creator is N round-trips.
843
- */
844
- public abstract furniture(
845
- catalogId: string,
846
- position: Vec3Handle,
847
- options?: { label?: string; createNewSourceMesh?: boolean },
848
- angleInDegrees?: number,
849
- ): PluginApiReturn<ComponentHandle>
850
-
851
- /**
852
- * Place **many furniture items** in one undoable operation (bulk plural of
853
- * {@linkcode furniture}). Each item names a catalog id and an absolute world
854
- * position (`position.y` is the REST elevation — the same grounding contract
855
- * as the singular creator), with optional label and rotation. A catalog
856
- * source shared by several items is fetched and recorded once for the whole
857
- * batch, so the host — not the caller — owns source persistence (there is no
858
- * per-item `createNewSourceMesh`). Validate-all-or-throw; one command.
859
- *
860
- * @param items - One {@linkcode PluginCreateFurnitureItem} per instance to
861
- * place (≥1, ≤1000)
862
- * @returns the placed instances as {@linkcode ComponentHandle}`[]`, in input order
863
- * @throws `VALIDATION` if the items fail the schema (empty array, more than
864
- * 1000 items); `HANDLE_INVALID` if a `position` handle is unknown or
865
- * released; `PRECONDITION_FAILED` if a catalog id is unknown, a furniture
866
- * tool is active, or a parametric group is active (exit it first, or place
867
- * items one at a time with `furniture`); `OPERATION_FAILED` if a source
868
- * mesh fails to load or placement fails (nothing is created —
869
- * `details.itemIndex` is the failing item); `METHOD_NOT_PERMITTED` if the plugin may not write.
870
- *
871
- * @examplePrompt Place all the desks for this office at once
872
- * @examplePrompt Furnish every bedroom in one operation
873
- * @examplePrompt Bulk place these chairs around the table
874
- * @examplePrompt Add the whole furniture layout in a single undo step
875
- * @examplePrompt Drop twenty copies of this chair at these positions
876
- *
877
- * @performance Bulk creator — the whole batch is ONE host round-trip and ONE undo
878
- * entry, and each distinct catalog source loads once. Always prefer this over
879
- * calling `design.create.furniture` in a loop: build the full `items[]` array
880
- * first (all positions and angles up front), then make one call.
881
- *
882
- * # Example
883
- * ```ts
884
- * const v = snaptrude.core.math.vec3
885
- * const [chair] = await snaptrude.design.furniture.listCatalog()
886
- * const placed = await snaptrude.design.create.furnitureItems([
887
- * { catalogId: chair.id, position: await v.new(3, 0, 5), label: "Chair-01" },
888
- * { catalogId: chair.id, position: await v.new(4, 0, 5), angleInDegrees: 90 },
889
- * { catalogId: chair.id, position: await v.new(5, 0, 5), angleInDegrees: 180 },
890
- * ])
891
- * ```
892
- */
893
- public abstract furnitureItems(
894
- items: PluginCreateFurnitureItem[],
895
- ): PluginApiReturn<ComponentHandle[]>
896
-
897
- /**
898
- * Place a **door** from the catalog into a host wall.
899
- *
900
- * Identified by a **catalog (library) id** — the team object's stable `_id`
901
- * or the general library's `fullName`. Discover ids with
902
- * {@linkcode PluginDesignDoorsApi.listCatalog}. The world `position` is
903
- * projected onto the host wall's face to locate the opening; the wall is
904
- * re-cut and the door hosted in a single undo entry. Placement is
905
- * asynchronous (the source mesh is fetched if not cached).
906
- *
907
- * @param catalogId - Library id: team `_id` or general `fullName`
908
- * @param hostWall - The wall to host the door
909
- * @param position - World point projected onto the wall to locate the opening
910
- * @param options - Optional placement options: `label` — instance name;
911
- * `hinge` — world point near the jamb the door is hinged on (the door is
912
- * reflected along the wall so its hinged jamb is the one nearer this
913
- * point; default the catalog item's authored hinge side)
914
- * @param facing - World point on the side of the wall the door swings open
915
- * to (the room it opens into — where the plan symbol draws the swing arc).
916
- * Any point clearly on that side works (e.g. the room's center). Default:
917
- * the engine picks a side (nondeterministic when `position` sits on the
918
- * wall centerline).
919
- * @returns the {@linkcode ComponentHandle} of the placed door
920
- * @throws if the catalog id is unknown, the host is not a wall, the source
921
- * mesh fails to load, or the projected point falls **outside** the host wall
922
- *
923
- * @examplePrompt Add a door to this wall here
924
- * @examplePrompt Place a sliding door on the selected wall
925
- * @examplePrompt Put a door from the catalog at this point on the wall
926
- * @examplePrompt Insert the entrance door into this wall
927
- * @examplePrompt Add a door to the wall and call it Entry-01
928
- * @examplePrompt Add a door that opens into the living room
929
- * @examplePrompt Put a door here hinged on the north jamb
930
- *
931
- * # Example
932
- * ```ts
933
- * const [wall] = await snaptrude.design.query.listWalls({ isSelected: true })
934
- * const [entry] = await snaptrude.design.doors.listCatalog()
935
- * const door = await snaptrude.design.create.door(
936
- * entry.id,
937
- * wall,
938
- * await snaptrude.core.math.vec3.new(3, 0, 5),
939
- * { label: "Entry-01" },
940
- * )
941
- * // …facing a specific side of the wall (pass a point inside that room):
942
- * const intoKitchen = await snaptrude.design.create.door(
943
- * entry.id,
944
- * wall,
945
- * await snaptrude.core.math.vec3.new(3, 0, 5),
946
- * undefined,
947
- * await snaptrude.core.math.vec3.new(3, 0, 9),
948
- * )
949
- * // …and hinged on the jamb nearest a point (here the +x end of the opening):
950
- * const hingedRight = await snaptrude.design.create.door(
951
- * entry.id,
952
- * wall,
953
- * await snaptrude.core.math.vec3.new(3, 0, 5),
954
- * { hinge: await snaptrude.core.math.vec3.new(3.5, 0, 5) },
955
- * await snaptrude.core.math.vec3.new(3, 0, 9),
956
- * )
957
- * ```
958
- *
959
- * @performance For MORE THAN ONE door, call `design.create.doors(items[])` — one host
960
- * round-trip and ONE undo entry, with each host wall re-cut once per opening inside
961
- * that single call. Looping this single-door creator is N round-trips.
962
- */
963
- public abstract door(
964
- catalogId: string,
965
- hostWall: ComponentHandle,
966
- position: Vec3Handle,
967
- options?: { label?: string; hinge?: Vec3Handle },
968
- facing?: Vec3Handle,
969
- ): PluginApiReturn<ComponentHandle>
970
-
971
- /**
972
- * Place **many doors** into host walls in one undoable operation (bulk plural
973
- * of {@linkcode door}). Each item carries its own catalog id, host wall,
974
- * world position, and optional `facing`, `label`, `width` and `height` — the
975
- * same fields as a `"door"` {@linkcode PluginDesignCreateOpeningOptions}.
976
- * Validate-all-or-throw; one command.
977
- *
978
- * As with the singular creator, placing an opening **re-cuts its host wall**:
979
- * the wall you passed as `hostWall` stops resolving once the call returns.
980
- * Several items MAY name the same original wall handle in one call — the host
981
- * chains the re-cuts internally — but after the call recover the surviving
982
- * wall with `design.query.getHost(opening)`, never by reusing the handle you
983
- * passed in.
984
- *
985
- * @param items - One {@linkcode PluginCreateDoorItem} per door to place
986
- * (≥1, ≤1000)
987
- * @returns the placed doors as {@linkcode ComponentHandle}`[]`, in input order
988
- * @throws `VALIDATION` if the items fail the schema (empty array, more than
989
- * 1000 items, unknown fields, a non-positive `width`/`height`);
990
- * `HANDLE_INVALID` if a `position`, `facing` or `hinge` handle is unknown
991
- * or released; `PRECONDITION_FAILED` if a catalog id is unknown, a `hostWall`
992
- * is not a wall / is locked / is not in the active proposal, the projected
993
- * point falls outside its host wall, or a door tool is active;
994
- * `OPERATION_FAILED` if a source mesh fails to load or placement fails
995
- * (nothing is created — `details.itemIndex` is the failing item);
996
- * `METHOD_NOT_PERMITTED` if the plugin may not write.
997
- *
998
- * @examplePrompt Add doors to all of these walls at once
999
- * @examplePrompt Place a door in every room in one operation
1000
- * @examplePrompt Bulk add the entrance doors from this schedule
1001
- * @examplePrompt Put three doors on this wall in a single undo step
1002
- * @examplePrompt Add all the doors for this floor plan together
1003
- *
1004
- * @performance Bulk creator — the whole batch is ONE host round-trip and ONE undo
1005
- * entry, and each distinct catalog source loads once. Always prefer this over
1006
- * calling `design.create.door` in a loop: build the full `items[]` array first
1007
- * (all host walls and points up front), then make one call.
1008
- *
1009
- * # Example
1010
- * ```ts
1011
- * const v = snaptrude.core.math.vec3
1012
- * const [wall] = await snaptrude.design.query.listWalls({ isSelected: true })
1013
- * const [entry] = await snaptrude.design.doors.listCatalog()
1014
- * const [front, side] = await snaptrude.design.create.doors([
1015
- * { catalogId: entry.id, hostWall: wall, position: await v.new(2, 0, 5), label: "D-01" },
1016
- * { catalogId: entry.id, hostWall: wall, position: await v.new(6, 0, 5), width: 1.2 },
1017
- * ])
1018
- * // `wall` was re-cut twice and no longer resolves — ask the opening for its host:
1019
- * const currentWall = await snaptrude.design.query.getHost(front)
1020
- * ```
1021
- */
1022
- public abstract doors(
1023
- items: PluginCreateDoorItem[],
1024
- ): PluginApiReturn<ComponentHandle[]>
1025
-
1026
- /**
1027
- * Place a **window** from the catalog into a host wall.
1028
- *
1029
- * Identified by a **catalog (library) id** — the team object's stable `_id`
1030
- * or the general library's `fullName`. Discover ids with
1031
- * {@linkcode PluginDesignWindowsApi.listCatalog}. The world `position` is
1032
- * projected onto the host wall's face to locate the opening; the wall is
1033
- * re-cut and the window hosted in a single undo entry. Placement is
1034
- * asynchronous (the source mesh is fetched if not cached).
1035
- *
1036
- * @param catalogId - Library id: team `_id` or general `fullName`
1037
- * @param hostWall - The wall to host the window
1038
- * @param position - World point projected onto the wall to locate the opening
1039
- * @param options - Optional placement options: `label` — instance name;
1040
- * `hinge` — world point near the jamb the window is hinged on (same rule
1041
- * as {@linkcode PluginDesignCreateApi.door}; default the catalog item's
1042
- * authored side)
1043
- * @param facing - World point on the side of the wall the window opens to
1044
- * (matters for asymmetric windows, e.g. casement swing) — same convention
1045
- * as {@linkcode PluginDesignCreateApi.door}. Default: the engine picks a
1046
- * side (nondeterministic when `position` sits on the centerline).
1047
- * @returns the {@linkcode ComponentHandle} of the placed window
1048
- * @throws if the catalog id is unknown, the host is not a wall, the source
1049
- * mesh fails to load, or the projected point falls **outside** the host wall
1050
- *
1051
- * @examplePrompt Add a window to this wall here
1052
- * @examplePrompt Place a casement window on the selected wall
1053
- * @examplePrompt Put a window from the catalog at this point on the wall
1054
- * @examplePrompt Insert a window into the living room wall
1055
- * @examplePrompt Add a window to the wall and name it Win-01
1056
- * @examplePrompt Add a casement window opening toward the garden side
1057
- *
1058
- * # Example
1059
- * ```ts
1060
- * const [wall] = await snaptrude.design.query.listWalls({ isSelected: true })
1061
- * const [entry] = await snaptrude.design.windows.listCatalog()
1062
- * const window = await snaptrude.design.create.window(
1063
- * entry.id,
1064
- * wall,
1065
- * await snaptrude.core.math.vec3.new(3, 0, 5),
1066
- * { label: "Win-01" },
1067
- * )
1068
- * ```
1069
- *
1070
- * @performance For MORE THAN ONE window, call `design.create.windows(items[])` — one
1071
- * host round-trip and ONE undo entry, with each host wall re-cut once per opening
1072
- * inside that single call. Looping this single-window creator is N round-trips.
1073
- */
1074
- public abstract window(
1075
- catalogId: string,
1076
- hostWall: ComponentHandle,
1077
- position: Vec3Handle,
1078
- options?: { label?: string; hinge?: Vec3Handle },
1079
- facing?: Vec3Handle,
1080
- ): PluginApiReturn<ComponentHandle>
1081
-
1082
- /**
1083
- * Place **many windows** into host walls in one undoable operation (bulk
1084
- * plural of {@linkcode window}). Each item carries its own catalog id, host
1085
- * wall, world position, and optional `facing`, `label`, `width`, `height` and
1086
- * `sillHeight` — the same fields as a `"window"`
1087
- * {@linkcode PluginDesignCreateOpeningOptions}. `sillHeight` is measured from
1088
- * the host wall base to the BOTTOM of the window. Validate-all-or-throw; one
1089
- * command.
1090
- *
1091
- * As with the singular creator, placing an opening **re-cuts its host wall**:
1092
- * the wall you passed as `hostWall` stops resolving once the call returns.
1093
- * Several items MAY name the same original wall handle in one call — the host
1094
- * chains the re-cuts internally — but after the call recover the surviving
1095
- * wall with `design.query.getHost(opening)`, never by reusing the handle you
1096
- * passed in.
1097
- *
1098
- * @param items - One {@linkcode PluginCreateWindowItem} per window to place
1099
- * (≥1, ≤1000)
1100
- * @returns the placed windows as {@linkcode ComponentHandle}`[]`, in input order
1101
- * @throws `VALIDATION` if the items fail the schema (empty array, more than
1102
- * 1000 items, unknown fields, a non-positive `width`/`height`, a negative
1103
- * `sillHeight`); `HANDLE_INVALID` if a `position`, `facing` or `hinge`
1104
- * handle is unknown or released; `PRECONDITION_FAILED` if a catalog id is unknown, a
1105
- * `hostWall` is not a wall / is locked / is not in the active proposal, the
1106
- * projected point falls outside its host wall, or a window tool is active;
1107
- * `OPERATION_FAILED` if a source mesh fails to load or placement fails
1108
- * (nothing is created — `details.itemIndex` is the failing item);
1109
- * `METHOD_NOT_PERMITTED` if the plugin may not write.
1110
- *
1111
- * @examplePrompt Add windows along this whole facade at once
1112
- * @examplePrompt Place a window in every bedroom in one operation
1113
- * @examplePrompt Bulk add the windows from this schedule
1114
- * @examplePrompt Put four windows on this wall in a single undo step
1115
- * @examplePrompt Add all the windows with a 0.9m sill height together
1116
- *
1117
- * @performance Bulk creator — the whole batch is ONE host round-trip and ONE undo
1118
- * entry, and each distinct catalog source loads once. Always prefer this over
1119
- * calling `design.create.window` in a loop: build the full `items[]` array first
1120
- * (all host walls and points up front), then make one call.
1121
- *
1122
- * # Example
1123
- * ```ts
1124
- * const v = snaptrude.core.math.vec3
1125
- * const [wall] = await snaptrude.design.query.listWalls({ isSelected: true })
1126
- * const [casement] = await snaptrude.design.windows.listCatalog()
1127
- * const placed = await snaptrude.design.create.windows([
1128
- * { catalogId: casement.id, hostWall: wall, position: await v.new(2, 0, 5), sillHeight: 0.9 },
1129
- * { catalogId: casement.id, hostWall: wall, position: await v.new(5, 0, 5), sillHeight: 0.9 },
1130
- * ])
1131
- * const currentWall = await snaptrude.design.query.getHost(placed[0])
1132
- * ```
1133
- */
1134
- public abstract windows(
1135
- items: PluginCreateWindowItem[],
1136
- ): PluginApiReturn<ComponentHandle[]>
1137
-
1138
- /**
1139
- * Place a catalog **door or window** into a host wall with optional size overrides.
1140
- *
1141
- * The world `position` is projected onto the host wall to locate the opening.
1142
- * All supplied dimensions use Snaptrude engine units. Window `sillHeight` is
1143
- * measured from the host wall base to the bottom of the window, not to
1144
- * its center. Placement is asynchronous and committed as one undoable creation.
1145
- *
1146
- * @param options - Opening kind, catalog and host references, placement, and
1147
- * optional facing (swing side), hinge (hinged jamb), label, and dimensions
1148
- * @returns the {@linkcode ComponentHandle} of the placed door or window
1149
- * @throws if the options are invalid, the catalog id is unknown, the host is
1150
- * not a wall, loading fails, or the opening cannot be placed on the host
1151
- *
1152
- * @examplePrompt Add a 1m wide door to this wall here
1153
- * @examplePrompt Place a window with a 0.9m sill height on the selected wall
1154
- *
1155
- * @performance Single-opening creator — use it for one hosted opening. For MORE
1156
- * THAN ONE, call `design.create.doors(items[])` / `design.create.windows(items[])`:
1157
- * the same per-item fields, one host round-trip, ONE undo entry.
1158
- *
1159
- * # Example
1160
- * ```ts
1161
- * const opening = await snaptrude.design.create.opening({
1162
- * kind: "window",
1163
- * catalogId: windowType.id,
1164
- * hostWall: wall,
1165
- * position: await snaptrude.core.math.vec3.new(3, 0, 5),
1166
- * width: 1.2,
1167
- * height: 1.5,
1168
- * sillHeight: 0.9,
1169
- * })
1170
- * ```
1171
- */
1172
- public abstract opening(
1173
- options: PluginDesignCreateOpeningOptions,
1174
- ): PluginApiReturn<ComponentHandle>
1175
-
1176
- /**
1177
- * Place a **smart layout** — clone an in-scene template cluster (walls,
1178
- * furniture, doors, windows, floors) into one or more target ROOM/DEPARTMENT
1179
- * spaces, adapting orientation and fit to each target.
1180
- *
1181
- * Supply the template as **exactly one** of `templateGroup` (a group **id**
1182
- * from `snaptrude.core.groups.list` — groups are string-id entities, not
1183
- * component handles) or
1184
- * `templateComponents` (an explicit component set); every target in `targets`
1185
- * (≥1) receives its own placement. By default the target spaces are hidden
1186
- * after placement (`hideTargets`, mirroring the product) — pass `false` to keep
1187
- * them visible.
1188
- *
1189
- * The whole operation — every wall/opening/furniture/floor across every target,
1190
- * plus the target-hide — is committed as a **single undo step**. Because all
1191
- * targets merge into one creation batch, the created entities are returned
1192
- * **flat, grouped by kind** ({@linkcode PluginSmartLayoutResult}), NOT attributed
1193
- * per target (per-target attribution would break the single-undo guarantee).
1194
- * `skippedWalls` is the interior-optimizer's best-candidate count of walls it
1195
- * could not place. Synchronous and heavy — many targets can jank the main thread.
1196
- *
1197
- * @param options - {@linkcode PluginSmartLayoutArgs}: template source, targets, and `hideTargets`
1198
- * @returns a {@linkcode PluginSmartLayoutResult} — created entities grouped by kind + `skippedWalls`
1199
- * @throws if smart layout is not enabled, the template group is invalid, there
1200
- * is no template / no targets / no valid mappings, or component creation fails
1201
- *
1202
- * @examplePrompt Copy this room's layout into the selected rooms
1203
- * @examplePrompt Apply this furniture arrangement to the other offices
1204
- * @examplePrompt Replicate this template into these two spaces
1205
- * @examplePrompt Place this desk cluster into every meeting room
1206
- * @examplePrompt Use this room as a template for the rooms I selected but keep them visible
1207
- *
1208
- * # Example
1209
- * ```ts
1210
- * const [templateRoom, ...targets] = await snaptrude.design.query.listSpaces({ isSelected: true })
1211
- * const result = await snaptrude.design.create.smartLayout({
1212
- * templateComponents: [templateRoom],
1213
- * targets,
1214
- * })
1215
- * console.log("placed walls:", result.created.walls.length, "skipped:", result.skippedWalls)
1216
- *
1217
- * // …or clone a saved group by its id (from core.groups.list)
1218
- * const { groups } = await snaptrude.core.groups.list()
1219
- * await snaptrude.design.create.smartLayout({ templateGroup: groups[0].id, targets })
1220
- * ```
1221
- */
1222
- public abstract smartLayout(
1223
- options: PluginSmartLayoutArgs,
1224
- ): PluginApiReturn<PluginSmartLayoutResult>
1225
-
1226
- /**
1227
- * **Copy** existing entities, offsetting each copy by `displacement * i` (i = 1…count),
1228
- * source positions preserved. Migrated from the removed `tools.copy`. In `"instance"`
1229
- * mode copies stay in the source's instance family where possible; `"unique"` makes
1230
- * independent geometry. The created copies become the active selection. Undoable.
1231
- *
1232
- * Runs on the engine's array-copy tool, so its semantics carry over: an
1233
- * `"instance"` copy of a source that is not yet an instance **replaces that
1234
- * source** with a fresh instance component — the handle you passed in stops
1235
- * resolving after the call. Re-query (e.g. `design.query.listSpaces`) to pick
1236
- * up the replacement before operating on that entity again; copying the stale
1237
- * handle throws. `"unique"` mode never replaces sources.
1238
- *
1239
- * @param components - Entities to copy (≥1)
1240
- * @param displacement - Per-copy offset as a {@linkcode Vec3Handle} (copy `i` at `displacement * i`)
1241
- * @param options - `count` (copies per component, default 1), `mode` (`"instance"` | `"unique"`, default `"instance"`)
1242
- * @returns the created copies as {@linkcode ComponentHandle}`[]`
1243
- * @throws if a component handle is unknown or stale (replaced by an earlier
1244
- * `"instance"` copy), `count` is not a positive integer, or the engine fails
1245
- *
1246
- * @examplePrompt Copy this room 3 times spaced 6m apart
1247
- * @examplePrompt Duplicate the selected furniture along the X axis
1248
- * @examplePrompt Make 5 copies of this column at 4 metre intervals
1249
- * @examplePrompt Array these walls with a 3m offset
1250
- * @examplePrompt Clone the desk as a unique copy, not an instance
1251
- *
1252
- * @performance To place N repeats of the same geometry, bulk-create the seed(s) once then
1253
- * call this ONCE with `{ count: N - 1 }` — one host round-trip for all copies. Never
1254
- * reproduce geometry by looping `design.create.*` N times. Copy only after the create
1255
- * that mints the seed ids.
1256
- *
1257
- * # Example
1258
- * ```ts
1259
- * const { vec3 } = snaptrude.core.math
1260
- * const copies = await snaptrude.design.create.copy(["space-id"], vec3.new(6, 0, 0), { count: 3 })
1261
- * ```
1262
- */
1263
- public abstract copy(
1264
- components: ComponentHandle[],
1265
- displacement: Vec3Handle,
1266
- options?: { count?: number; mode?: PluginCopyMode },
1267
- ): PluginApiReturn<ComponentHandle[]>
1268
-
1269
- /**
1270
- * Create **many spaces** in one undoable operation (bulk plural of {@linkcode space}).
1271
- * Migrated from the removed `entity.space.bulkCreate`. Each item extrudes a contour
1272
- * footprint up `height`. Validate-all-or-throw; one command. For a rectangular
1273
- * (box) space, build the footprint with `core.geom.create.profileRect` →
1274
- * `contourFromProfile`, then pass it as an item. Each item may carry its own
1275
- * `storey` (see {@linkcode PluginCreateSpaceItem}) so a single call can populate
1276
- * several floors at once.
1277
- *
1278
- * @param items - One {@linkcode PluginCreateSpaceItem} per space to create (≥1)
1279
- * @returns the created spaces as {@linkcode ComponentHandle}`[]`, in input order
1280
- * @throws if any item's contour is invalid, its height is not positive, or its
1281
- * target `storey` does not exist (all-or-nothing — nothing is created)
1282
- *
1283
- * @examplePrompt Create several rooms at once from these footprints
1284
- * @examplePrompt Generate all the bedrooms in one operation
1285
- * @examplePrompt Bulk create the spaces for this floor plan
1286
- * @examplePrompt Add three rooms with different heights in a single undo step
1287
- * @examplePrompt Create one room on the ground floor and one on the first floor
1288
- *
1289
- * @performance Bulk creator — the whole batch is ONE host round-trip. Always prefer this
1290
- * over calling `design.create.space` in a loop: build the full `items[]` array first
1291
- * (all per-item math and geometry up front), then make one call.
1292
- *
1293
- * # Example
1294
- * ```ts
1295
- * const rect = await snaptrude.core.geom.create.profileRect(4, 3)
1296
- * const contour = await snaptrude.core.geom.create.contourFromProfile(rect)
1297
- * const [a, b] = await snaptrude.design.create.spaces([
1298
- * { contour, height: 3, label: "R1" },
1299
- * { contour, height: 4, label: "R2", position: await vec3.new(10, 0, 0) },
1300
- * ])
1301
- * ```
1302
- */
1303
- public abstract spaces(
1304
- items: PluginCreateSpaceItem[],
1305
- ): PluginApiReturn<ComponentHandle[]>
1306
-
1307
- /**
1308
- * Create a new parametric buildable envelope — the zoning-regulated volume a
1309
- * building may occupy — generated from a site polygon, tiered setbacks, a
1310
- * height or floor-count cap, and optional FAR and lot-coverage limits. The host
1311
- * mints the envelope id and returns it. Canonical home of the create formerly
1312
- * at `entity.buildableEnvelope.create` (now deprecated).
1313
- *
1314
- * @param sitePolygon - Site polygon vertices in `lengthUnit`; minimum 3 vertices.
1315
- * @param lengthUnit - Unit used by all length fields (`"ft" | "m"`).
1316
- * @param setbacks - Setback profile, ground tier first; minimum 1 tier.
1317
- * @param verticalCap - Maximum height or floor count.
1318
- * @param floorToFloor - Required floor-to-floor height in `lengthUnit`.
1319
- * @param farRatio - Optional FAR value, positive when provided.
1320
- * @param lotCoverageMaxPct - Optional lot coverage cap, `0..100`.
1321
- * @returns A {@linkcode PluginBuildableEnvelopeCreateResult} with the
1322
- * `buildableEnvelopeId` of the created envelope.
1323
- * @throws If validation fails or generation produces no renderable geometry.
1324
- *
1325
- * @examplePrompt Create a buildable envelope for my site
1326
- * @examplePrompt Generate the zoning envelope with 10ft front setbacks
1327
- * @examplePrompt Show the maximum buildable volume with a 150ft height limit
1328
- * @examplePrompt Create a zoning envelope capped at 12 floors with FAR 3
1329
- *
1330
- * # Example
1331
- * ```ts
1332
- * const { buildableEnvelopeId } = await snaptrude.design.create.buildableEnvelope(
1333
- * [
1334
- * { x: 0, z: 0 },
1335
- * { x: 100, z: 0 },
1336
- * { x: 100, z: 80 },
1337
- * { x: 0, z: 80 },
1338
- * ],
1339
- * "ft",
1340
- * [
1341
- * { aboveHeight: 0, front: 10, side: 5, rear: 10 },
1342
- * { aboveHeight: 100, front: 20, side: 10, rear: 20 },
1343
- * ],
1344
- * { kind: "max_height", maxHeight: 150 },
1345
- * 12,
1346
- * )
1347
- * ```
1348
- */
1349
- public abstract buildableEnvelope(
1350
- sitePolygon: PluginBuildableEnvelopePolygonVertex[],
1351
- lengthUnit: "ft" | "m",
1352
- setbacks: PluginBuildableEnvelopeSetbackTier[],
1353
- verticalCap: PluginBuildableEnvelopeVerticalCap,
1354
- floorToFloor: number,
1355
- farRatio?: number,
1356
- lotCoverageMaxPct?: number,
1357
- ): PluginApiReturn<PluginBuildableEnvelopeCreateResult>
1358
- }
1359
-
1360
- // ---------------------------------------------------------------------------
1361
- // Enums
1362
- // ---------------------------------------------------------------------------
1363
-
1364
- /**
1365
- * Line style of a reference line (mirrors the engine `ReferenceLineMode` verbatim).
1366
- *
1367
- * | Value | Description |
1368
- * |---|---|
1369
- * | `"SOLID"` | Solid line (default) |
1370
- * | `"DASHED"` | Dashed line |
1371
- * | `"DOT_DASH"` | Dot-dash line |
1372
- */
1373
- export const PluginReferenceLineStyle = z.enum(["SOLID", "DASHED", "DOT_DASH"])
1374
- export type PluginReferenceLineStyle = z.infer<typeof PluginReferenceLineStyle>
1375
-
1376
- /**
1377
- * Slab type (mirrors the engine `SLAB_TYPES` values verbatim).
1378
- *
1379
- * | Value | Description |
1380
- * |---|---|
1381
- * | `"Intermediate Slab"` | Intermediate floor slab (default) |
1382
- * | `"Basement Slab"` | Basement slab |
1383
- * | `"Plinth"` | Plinth slab |
1384
- * | `"Roof"` | Roof slab |
1385
- */
1386
- export const PluginSlabType = z.enum([
1387
- "Intermediate Slab",
1388
- "Basement Slab",
1389
- "Plinth",
1390
- "Roof",
1391
- ])
1392
- export type PluginSlabType = z.infer<typeof PluginSlabType>
1393
-
1394
- /**
1395
- * A staircase preset — a base type or a named parametric preset. Mirrors the
1396
- * engine `STAIRCASE_PRESETS` / `StaircaseType`. Use the value that matches the
1397
- * shape you want (e.g. `"straight"`, `"dogLegged"`, `"lShaped"`).
1398
- */
1399
- export const PluginStaircasePreset = z.enum([
1400
- "straight",
1401
- "straightWoLanding",
1402
- "straightFlightWoMidLanding",
1403
- "dogLegged",
1404
- "openWell",
1405
- "lShaped",
1406
- "3StepLShaped",
1407
- "square",
1408
- "singleStep",
1409
- "plinthLevel",
1410
- "amphitheatre",
1411
- "connectingStepsToAmphitheatre",
1412
- "mezzanineFloorStraight",
1413
- "mezzanineFloorDogLegged",
1414
- "mezzanineFloorLShaped",
1415
- "custom",
1416
- ])
1417
- export type PluginStaircasePreset = z.infer<typeof PluginStaircasePreset>
1418
-
1419
- /**
1420
- * Optional staircase dimension overrides. Lengths use Snaptrude engine units
1421
- * and must be positive. `angleInDegrees` controls placement about the vertical
1422
- * axis and must be a multiple of 90 degrees.
1423
- */
1424
- export const PluginStaircaseDimensions = z
1425
- .object({
1426
- width: z.number().finite().positive().optional(),
1427
- tread: z.number().finite().positive().optional(),
1428
- riser: z.number().finite().positive().optional(),
1429
- landingWidth: z.number().finite().positive().optional(),
1430
- angleInDegrees: z.number().finite().multipleOf(90).optional(),
1431
- })
1432
- .strict()
1433
- export type PluginStaircaseDimensions = z.infer<
1434
- typeof PluginStaircaseDimensions
1435
- >
1436
-
1437
- // ---------------------------------------------------------------------------
1438
- // space
1439
- // ---------------------------------------------------------------------------
1440
-
1441
- /**
1442
- * Arguments for {@linkcode PluginDesignCreateApi.space}.
1443
- *
1444
- * | Property | Type | Description |
1445
- * |---|---|---|
1446
- * | `contour` | {@linkcode ContourHandle} | Bottom footprint (outer + holes) |
1447
- * | `height` | `number` | Extrusion height (Snaptrude units, > 0) |
1448
- * | `label` | `string`? | Room label (maps to the space's `room_type`) |
1449
- * | `position` | {@linkcode Vec3Handle}? | Offset from origin (default origin) |
1450
- * | `spaceType` | {@linkcode PluginSpaceType}? | Space-type classification (default `Room`) |
1451
- * | `massType` | {@linkcode PluginMassType}? | Mass-type classification (default `Room`) |
1452
- * | `departmentId` | {@linkcode PluginDepartmentId}? | Department assignment (default department) |
1453
- * | `storey` | `number`? (int) | Target storey number (default active storey) |
1454
- */
1455
- export const PluginDesignCreateSpaceArgs = z.object({
1456
- contour: ContourHandle,
1457
- height: z.number(),
1458
- label: z.string().optional(),
1459
- position: Vec3Handle.optional(),
1460
- spaceType: PluginSpaceType.optional(),
1461
- massType: PluginMassType.optional(),
1462
- departmentId: PluginDepartmentId.optional(),
1463
- storey: z.number().int().optional(),
1464
- })
1465
- export type PluginDesignCreateSpaceArgs = z.infer<
1466
- typeof PluginDesignCreateSpaceArgs
1467
- >
1468
- // TRANSPORT: positional signature shipped — storey is the trailing 8th arg:
1469
- // space(contour, height, label?, position?, spaceType?, massType?, departmentId?, storey?)
1470
-
1471
- // ---------------------------------------------------------------------------
1472
- // mass
1473
- // ---------------------------------------------------------------------------
1474
-
1475
- /**
1476
- * Arguments for {@linkcode PluginDesignCreateApi.mass}.
1477
- *
1478
- * | Property | Type | Description |
1479
- * |---|---|---|
1480
- * | `contour` | {@linkcode ContourHandle} | Footprint (outer + holes) |
1481
- * | `height` | `number` | Extrusion height (> 0) |
1482
- * | `label` | `string`? | Label (maps to `room_type`) |
1483
- * | `position` | {@linkcode Vec3Handle}? | Offset from origin |
1484
- * | `massType` | {@linkcode PluginMassType}? | Mass type (default `Generic mass`) |
1485
- */
1486
- export const PluginDesignCreateMassArgs = z.object({
1487
- contour: ContourHandle,
1488
- height: z.number(),
1489
- label: z.string().optional(),
1490
- position: Vec3Handle.optional(),
1491
- massType: PluginMassType.optional(),
1492
- })
1493
- export type PluginDesignCreateMassArgs = z.infer<
1494
- typeof PluginDesignCreateMassArgs
1495
- >
1496
-
1497
- // ---------------------------------------------------------------------------
1498
- // massFromBrep
1499
- // ---------------------------------------------------------------------------
1500
-
1501
- /**
1502
- * Arguments for {@linkcode PluginDesignCreateApi.massFromBrep}.
1503
- *
1504
- * | Property | Type | Description |
1505
- * |---|---|---|
1506
- * | `brep` | {@linkcode BrepHandle} | A solid from a `core.geom.create` brep constructor |
1507
- * | `label` | `string`? | Label (maps to `room_type`) |
1508
- */
1509
- export const PluginDesignCreateMassFromBrepArgs = z.object({
1510
- brep: BrepHandle,
1511
- label: z.string().optional(),
1512
- })
1513
- export type PluginDesignCreateMassFromBrepArgs = z.infer<
1514
- typeof PluginDesignCreateMassFromBrepArgs
1515
- >
1516
-
1517
- // ---------------------------------------------------------------------------
1518
- // slab
1519
- // ---------------------------------------------------------------------------
1520
-
1521
- /**
1522
- * Arguments for {@linkcode PluginDesignCreateApi.slab}.
1523
- *
1524
- * | Property | Type | Description |
1525
- * |---|---|---|
1526
- * | `contour` | {@linkcode ContourHandle} | Footprint (outer + holes) |
1527
- * | `thickness` | `number` | Slab thickness (> 0) |
1528
- * | `direction` | `"up" \| "down"`? | Extrusion direction (default `"down"`) |
1529
- * | `slabType` | {@linkcode PluginSlabType}? | Slab-type classification |
1530
- */
1531
- export const PluginDesignCreateSlabArgs = z.object({
1532
- contour: ContourHandle,
1533
- thickness: z.number(),
1534
- direction: z.enum(["up", "down"]).optional(),
1535
- slabType: PluginSlabType.optional(),
1536
- })
1537
- export type PluginDesignCreateSlabArgs = z.infer<
1538
- typeof PluginDesignCreateSlabArgs
1539
- >
1540
-
1541
- // ---------------------------------------------------------------------------
1542
- // floor
1543
- // ---------------------------------------------------------------------------
1544
-
1545
- /**
1546
- * Arguments for {@linkcode PluginDesignCreateApi.floor}.
1547
- *
1548
- * | Property | Type | Description |
1549
- * |---|---|---|
1550
- * | `contour` | {@linkcode ContourHandle} | Footprint (outer + holes) |
1551
- * | `thickness` | `number` | Floor thickness (> 0) |
1552
- * | `position` | {@linkcode Vec3Handle}? | Offset from origin |
1553
- */
1554
- export const PluginDesignCreateFloorArgs = z.object({
1555
- contour: ContourHandle,
1556
- thickness: z.number(),
1557
- position: Vec3Handle.optional(),
1558
- })
1559
- export type PluginDesignCreateFloorArgs = z.infer<
1560
- typeof PluginDesignCreateFloorArgs
1561
- >
1562
-
1563
- // ---------------------------------------------------------------------------
1564
- // roof
1565
- // ---------------------------------------------------------------------------
1566
-
1567
- /**
1568
- * Arguments for {@linkcode PluginDesignCreateApi.roof}.
1569
- *
1570
- * | Property | Type | Description |
1571
- * |---|---|---|
1572
- * | `contour` | {@linkcode ContourHandle} | Footprint (outer + holes) |
1573
- * | `thickness` | `number` | Roof-slab thickness (> 0) |
1574
- */
1575
- export const PluginDesignCreateRoofArgs = z.object({
1576
- contour: ContourHandle,
1577
- thickness: z.number(),
1578
- })
1579
- export type PluginDesignCreateRoofArgs = z.infer<
1580
- typeof PluginDesignCreateRoofArgs
1581
- >
1582
-
1583
- // ---------------------------------------------------------------------------
1584
- // ceiling
1585
- // ---------------------------------------------------------------------------
1586
-
1587
- /**
1588
- * Arguments for {@linkcode PluginDesignCreateApi.ceiling}.
1589
- *
1590
- * | Property | Type | Description |
1591
- * |---|---|---|
1592
- * | `contour` | {@linkcode ContourHandle} | Footprint (outer + holes) |
1593
- * | `thickness` | `number` | Ceiling thickness (> 0) |
1594
- * | `heightFromFloor` | `number`? | Lift above the floor (default 0) |
1595
- */
1596
- export const PluginDesignCreateCeilingArgs = z.object({
1597
- contour: ContourHandle,
1598
- thickness: z.number(),
1599
- heightFromFloor: z.number().optional(),
1600
- })
1601
- export type PluginDesignCreateCeilingArgs = z.infer<
1602
- typeof PluginDesignCreateCeilingArgs
1603
- >
1604
-
1605
- // ---------------------------------------------------------------------------
1606
- // column
1607
- // ---------------------------------------------------------------------------
1608
-
1609
- /**
1610
- * Arguments for {@linkcode PluginDesignCreateApi.column}.
1611
- *
1612
- * | Property | Type | Description |
1613
- * |---|---|---|
1614
- * | `position` | {@linkcode Vec3Handle} | Base point |
1615
- * | `crossSection` | {@linkcode ContourHandle} | Column cross-section footprint |
1616
- * | `height` | `number` | Column height (> 0) |
1617
- * | `directionUp` | {@linkcode Vec3Handle}? | Up axis (default world up) |
1618
- */
1619
- export const PluginDesignCreateColumnArgs = z.object({
1620
- position: Vec3Handle,
1621
- crossSection: ContourHandle,
1622
- height: z.number(),
1623
- directionUp: Vec3Handle.optional(),
1624
- })
1625
- export type PluginDesignCreateColumnArgs = z.infer<
1626
- typeof PluginDesignCreateColumnArgs
1627
- >
1628
-
1629
- // ---------------------------------------------------------------------------
1630
- // beam
1631
- // ---------------------------------------------------------------------------
1632
-
1633
- /**
1634
- * Arguments for {@linkcode PluginDesignCreateApi.beam}.
1635
- *
1636
- * | Property | Type | Description |
1637
- * |---|---|---|
1638
- * | `section` | {@linkcode ContourHandle} | Beam cross-section footprint |
1639
- * | `length` | `number` | Beam length along `direction` (> 0) |
1640
- * | `direction` | {@linkcode Vec3Handle}? | Extrusion axis (default world up) |
1641
- */
1642
- export const PluginDesignCreateBeamArgs = z.object({
1643
- section: ContourHandle,
1644
- length: z.number(),
1645
- direction: Vec3Handle.optional(),
1646
- })
1647
- export type PluginDesignCreateBeamArgs = z.infer<
1648
- typeof PluginDesignCreateBeamArgs
1649
- >
1650
-
1651
- // ---------------------------------------------------------------------------
1652
- // walls
1653
- // ---------------------------------------------------------------------------
1654
-
1655
- /**
1656
- * Arguments for {@linkcode PluginDesignCreateApi.walls}.
1657
- *
1658
- * | Property | Type | Description |
1659
- * |---|---|---|
1660
- * | `profile` | {@linkcode ProfileHandle} | Ordered curve chain (wall centerlines) |
1661
- * | `height` | `number`? | Wall height (default engine default) |
1662
- * | `thickness` | `number`? | Wall thickness (default: `wallType`'s total layer thickness when given, else engine default) |
1663
- * | `wallType` | `string`? | Wall type name from `design.types.list("wall")` — layers/material/thickness defaults (default generic) |
1664
- * | `storey` | `number`? | Target storey (integer, must exist) — walls assigned + lifted to its base in the same undo entry (default: geometry-derived) |
1665
- * | `topProfiles` | ({@linkcode PluginWallTopProfile} \| `null`)[]? | Top edge per curve (same order); `null`/missing = flat at `height`. Straight curves only |
1666
- */
1667
- export const PluginDesignCreateWallsArgs = z.object({
1668
- profile: ProfileHandle,
1669
- height: z.number().optional(),
1670
- thickness: z.number().optional(),
1671
- wallType: z.string().min(1).optional(),
1672
- storey: z.number().int().optional(),
1673
- topProfiles: z.array(PluginWallTopProfile.nullable()).optional(),
1674
- })
1675
- export type PluginDesignCreateWallsArgs = z.infer<
1676
- typeof PluginDesignCreateWallsArgs
1677
- >
1678
- // TRANSPORT: positional signature shipped — storey is the 5th arg, topProfiles the trailing 6th:
1679
- // walls(profile: ProfileHandle, height?: number, thickness?: number, wallType?: string, storey?: number, topProfiles?: (PluginWallTopProfile | null)[])
1680
-
1681
- // ---------------------------------------------------------------------------
1682
- // staircase
1683
- // ---------------------------------------------------------------------------
1684
-
1685
- /**
1686
- * Arguments for {@linkcode PluginDesignCreateApi.staircase}.
1687
- *
1688
- * | Property | Type | Description |
1689
- * |---|---|---|
1690
- * | `preset` | {@linkcode PluginStaircasePreset} | The parametric preset to generate |
1691
- * | `position` | {@linkcode Vec3Handle} | Placement point |
1692
- * | `label` | `string`? | Optional label |
1693
- * | `structureId` | `string`? | Target structure (default active) |
1694
- * | `level` | `string`? | Target level name (default active/`"01"`) |
1695
- * | `dimensions` | {@linkcode PluginStaircaseDimensions}? | Positive dimension overrides in engine units, applied before creation |
1696
- */
1697
- export const PluginDesignCreateStaircaseArgs = z.object({
1698
- preset: PluginStaircasePreset,
1699
- position: Vec3Handle,
1700
- label: z.string().optional(),
1701
- structureId: z.string().optional(),
1702
- level: z.string().optional(),
1703
- dimensions: PluginStaircaseDimensions.optional(),
1704
- })
1705
- export type PluginDesignCreateStaircaseArgs = z.infer<
1706
- typeof PluginDesignCreateStaircaseArgs
1707
- >
1708
-
1709
- // ---------------------------------------------------------------------------
1710
- // furniture
1711
- // ---------------------------------------------------------------------------
1712
-
1713
- /**
1714
- * Arguments for {@linkcode PluginDesignCreateApi.furniture}.
1715
- *
1716
- * | Property | Type | Description |
1717
- * |---|---|---|
1718
- * | `catalogId` | `string` | Library id: team `_id` or general `fullName` |
1719
- * | `position` | {@linkcode Vec3Handle} | Absolute world placement point |
1720
- * | `label` | `string`? | Instance name (default auto `${name}Ins${n}`) |
1721
- * | `createNewSourceMesh` | `boolean`? | Legacy, kept for compatibility; the host records a catalog source exactly once and this flag cannot skip it |
1722
- * | `angleInDegrees` | `number`? | Rotation about the vertical axis, in degrees (default unrotated) |
1723
- */
1724
- export const PluginDesignCreateFurnitureArgs = z.object({
1725
- catalogId: z.string().min(1),
1726
- position: Vec3Handle,
1727
- label: z.string().optional(),
1728
- createNewSourceMesh: z.boolean().optional(),
1729
- angleInDegrees: z.number().optional(),
1730
- })
1731
- export type PluginDesignCreateFurnitureArgs = z.infer<
1732
- typeof PluginDesignCreateFurnitureArgs
1733
- >
1734
- // TRANSPORT: positional signature shipped — angleInDegrees is the trailing 4th arg:
1735
- // furniture(catalogId: string, position: Vec3Handle, options?: { label?, createNewSourceMesh? }, angleInDegrees?: number)
1736
-
1737
- // ---------------------------------------------------------------------------
1738
- // door
1739
- // ---------------------------------------------------------------------------
1740
-
1741
- /**
1742
- * Arguments for {@linkcode PluginDesignCreateApi.door}.
1743
- *
1744
- * | Property | Type | Description |
1745
- * |---|---|---|
1746
- * | `catalogId` | `string` | Library id: team `_id` or general `fullName` |
1747
- * | `hostWall` | {@linkcode ComponentHandle} | The wall to host the door |
1748
- * | `position` | {@linkcode Vec3Handle} | World point projected onto the wall |
1749
- * | `label` | `string`? | Instance name (optional) |
1750
- * | `facing` | {@linkcode Vec3Handle}? | World point on the side of the wall the door swings open to (default engine-chosen) |
1751
- * | `hinge` | {@linkcode Vec3Handle}? | World point near the jamb the door is hinged on (default the catalog item's authored side) |
1752
- */
1753
- export const PluginDesignCreateDoorArgs = z.object({
1754
- catalogId: z.string().min(1),
1755
- hostWall: ComponentHandle,
1756
- position: Vec3Handle,
1757
- label: z.string().optional(),
1758
- facing: Vec3Handle.optional(),
1759
- hinge: Vec3Handle.optional(),
1760
- })
1761
- export type PluginDesignCreateDoorArgs = z.infer<
1762
- typeof PluginDesignCreateDoorArgs
1763
- >
1764
- // TRANSPORT: positional signature shipped — facing is the trailing 5th arg, hinge rides in options:
1765
- // door(catalogId: string, hostWall: ComponentHandle, position: Vec3Handle, options?: { label?, hinge? }, facing?: Vec3Handle)
1766
-
1767
- // ---------------------------------------------------------------------------
1768
- // window
1769
- // ---------------------------------------------------------------------------
1770
-
1771
- /**
1772
- * Arguments for {@linkcode PluginDesignCreateApi.window}.
1773
- *
1774
- * | Property | Type | Description |
1775
- * |---|---|---|
1776
- * | `catalogId` | `string` | Library id: team `_id` or general `fullName` |
1777
- * | `hostWall` | {@linkcode ComponentHandle} | The wall to host the window |
1778
- * | `position` | {@linkcode Vec3Handle} | World point projected onto the wall |
1779
- * | `label` | `string`? | Instance name (optional) |
1780
- * | `facing` | {@linkcode Vec3Handle}? | World point on the side of the wall the window opens to (default engine-chosen) |
1781
- * | `hinge` | {@linkcode Vec3Handle}? | World point near the jamb the window is hinged on (default the catalog item's authored side) |
1782
- */
1783
- export const PluginDesignCreateWindowArgs = z.object({
1784
- catalogId: z.string().min(1),
1785
- hostWall: ComponentHandle,
1786
- position: Vec3Handle,
1787
- label: z.string().optional(),
1788
- facing: Vec3Handle.optional(),
1789
- hinge: Vec3Handle.optional(),
1790
- })
1791
- export type PluginDesignCreateWindowArgs = z.infer<
1792
- typeof PluginDesignCreateWindowArgs
1793
- >
1794
- // TRANSPORT: positional signature shipped — facing is the trailing 5th arg, hinge rides in options:
1795
- // window(catalogId: string, hostWall: ComponentHandle, position: Vec3Handle, options?: { label?, hinge? }, facing?: Vec3Handle)
1796
-
1797
- // ---------------------------------------------------------------------------
1798
- // opening
1799
- // ---------------------------------------------------------------------------
1800
-
1801
- /**
1802
- * Options for {@linkcode PluginDesignCreateApi.opening}, discriminated by
1803
- * `kind`. Width and height are positive Snaptrude engine-unit values.
1804
- * `sillHeight` is available only for windows, is nonnegative, and is precisely
1805
- * the distance from the host wall base to the bottom of the window.
1806
- */
1807
- export const PluginDesignCreateOpeningOptions = z.discriminatedUnion("kind", [
1808
- z
1809
- .object({
1810
- kind: z.literal("door"),
1811
- ...PluginOpeningBaseOptions,
1812
- })
1813
- .strict(),
1814
- z
1815
- .object({
1816
- kind: z.literal("window"),
1817
- ...PluginOpeningBaseOptions,
1818
- sillHeight: z.number().finite().nonnegative().optional(),
1819
- })
1820
- .strict(),
1821
- ])
1822
- export type PluginDesignCreateOpeningOptions = z.infer<
1823
- typeof PluginDesignCreateOpeningOptions
1824
- >
1825
-
1826
- // ---------------------------------------------------------------------------
1827
- // smartLayout
1828
- // ---------------------------------------------------------------------------
1829
-
1830
- /**
1831
- * Arguments for {@linkcode PluginDesignCreateApi.smartLayout}. Provide **exactly
1832
- * one** template source: `templateGroup` or a non-empty `templateComponents`.
1833
- *
1834
- * | Property | Type | Description |
1835
- * |---|---|---|
1836
- * | `templateGroup` | `string`? | Id of the group to clone as the template (a group id from `snaptrude.core.groups.list`, NOT a component handle) |
1837
- * | `templateComponents` | {@linkcode ComponentHandle}`[]`? | Explicit component set to clone as the template |
1838
- * | `targets` | {@linkcode ComponentHandle}`[]` | ROOM/DEPARTMENT spaces to place into (≥1) |
1839
- * | `hideTargets` | `boolean`? | Hide the target spaces after placement (default `true`) |
1840
- */
1841
- export const PluginSmartLayoutArgs = z
1842
- .object({
1843
- templateGroup: z.string().min(1).optional(),
1844
- templateComponents: z.array(ComponentHandle).optional(),
1845
- targets: z.array(ComponentHandle).min(1),
1846
- hideTargets: z.boolean().optional(),
1847
- })
1848
- .refine(
1849
- (v) =>
1850
- (v.templateGroup != null) !== (v.templateComponents?.length ?? 0) > 0,
1851
- {
1852
- message:
1853
- "Provide exactly one template source: templateGroup or a non-empty templateComponents",
1854
- },
1855
- )
1856
- export type PluginSmartLayoutArgs = z.infer<typeof PluginSmartLayoutArgs>
1857
-
1858
- /**
1859
- * Result of {@linkcode PluginDesignCreateApi.smartLayout} — the entities the run
1860
- * created, grouped by kind (flat across all targets; see the method JSDoc for why
1861
- * this is not attributed per target).
1862
- *
1863
- * | Property | Type | Description |
1864
- * |---|---|---|
1865
- * | `created.walls` | {@linkcode ComponentHandle}`[]` | Created walls |
1866
- * | `created.furniture` | {@linkcode ComponentHandle}`[]` | Created furniture |
1867
- * | `created.doors` | {@linkcode ComponentHandle}`[]` | Created doors |
1868
- * | `created.windows` | {@linkcode ComponentHandle}`[]` | Created windows |
1869
- * | `created.floors` | {@linkcode ComponentHandle}`[]` | Created floors |
1870
- * | `skippedWalls` | `number` | Walls the interior optimizer could not place |
1871
- */
1872
- export const PluginSmartLayoutResult = z.object({
1873
- created: z.object({
1874
- walls: z.array(ComponentHandle),
1875
- furniture: z.array(ComponentHandle),
1876
- doors: z.array(ComponentHandle),
1877
- windows: z.array(ComponentHandle),
1878
- floors: z.array(ComponentHandle),
1879
- }),
1880
- skippedWalls: z.number(),
1881
- })
1882
- export type PluginSmartLayoutResult = z.infer<typeof PluginSmartLayoutResult>
1883
-
1884
- // ---------------------------------------------------------------------------
1885
- // referenceLines
1886
- // ---------------------------------------------------------------------------
1887
-
1888
- /**
1889
- * Arguments for {@linkcode PluginDesignCreateApi.referenceLines}.
1890
- *
1891
- * | Property | Type | Description |
1892
- * |---|---|---|
1893
- * | `profile` | {@linkcode ProfileHandle} | Ordered curve chain (centerlines) |
1894
- * | `color` | `string`? | Hex color `"#RRGGBB"` (default white) |
1895
- * | `thickness` | `number`? | Line thickness (default 1) |
1896
- * | `style` | {@linkcode PluginReferenceLineStyle}? | Line style (default `SOLID`) |
1897
- * | `gridTag` | `string`? | Grid tag label (default `"A"`) |
1898
- */
1899
- export const PluginDesignCreateReferenceLinesArgs = z.object({
1900
- profile: ProfileHandle,
1901
- color: z.string().optional(),
1902
- thickness: z.number().optional(),
1903
- style: PluginReferenceLineStyle.optional(),
1904
- gridTag: z.string().optional(),
1905
- })
1906
- export type PluginDesignCreateReferenceLinesArgs = z.infer<
1907
- typeof PluginDesignCreateReferenceLinesArgs
1908
- >
1909
-
1910
- // ---------------------------------------------------------------------------
1911
- // copy
1912
- // ---------------------------------------------------------------------------
1913
-
1914
- /**
1915
- * Copy mode for {@linkcode PluginDesignCreateApi.copy}.
1916
- *
1917
- * | Value | Description |
1918
- * |---|---|
1919
- * | `"instance"` | Keep copies in the source's instance family where possible |
1920
- * | `"unique"` | Independent geometry copies |
1921
- */
1922
- export const PluginCopyMode = z.enum(["instance", "unique"])
1923
- export type PluginCopyMode = z.infer<typeof PluginCopyMode>
1924
-
1925
- /**
1926
- * Arguments for {@linkcode PluginDesignCreateApi.copy} (options flattened).
1927
- *
1928
- * | Property | Type | Description |
1929
- * |---|---|---|
1930
- * | `components` | {@linkcode ComponentHandle}`[]` | Entities to copy (≥1) |
1931
- * | `displacement` | {@linkcode Vec3Handle} | Per-copy offset (copy `i` at `displacement * i`) |
1932
- * | `count` | `number`? | Copies per component (default 1) |
1933
- * | `mode` | {@linkcode PluginCopyMode}? | `"instance"` (default) or `"unique"` |
1934
- */
1935
- export const PluginDesignCreateCopyArgs = z.object({
1936
- components: z.array(ComponentHandle).min(1),
1937
- displacement: Vec3Handle,
1938
- count: z.number().int().positive().optional(),
1939
- mode: PluginCopyMode.optional(),
1940
- })
1941
- export type PluginDesignCreateCopyArgs = z.infer<
1942
- typeof PluginDesignCreateCopyArgs
1943
- >
1944
- // TRANSPORT: positional signature shipped —
1945
- // copy(components: ComponentHandle[], displacement: Vec3Handle, options?: { count?, mode? })
1946
-
1947
- // ---------------------------------------------------------------------------
1948
- // spaces (bulk)
1949
- // ---------------------------------------------------------------------------
1950
-
1951
- /**
1952
- * One space to create via {@linkcode PluginDesignCreateApi.spaces} — the same
1953
- * footprint-extrude inputs as {@linkcode PluginDesignCreateApi.space}.
1954
- *
1955
- * | Property | Type | Description |
1956
- * |---|---|---|
1957
- * | `contour` | {@linkcode ContourHandle} | Footprint (outer profile + holes) |
1958
- * | `height` | `number` | Extrude height |
1959
- * | `label` | `string`? | Room label |
1960
- * | `position` | {@linkcode Vec3Handle}? | Placement offset |
1961
- * | `spaceType` | `PluginSpaceType`? | Space type |
1962
- * | `massType` | `PluginMassType`? | Mass type |
1963
- * | `departmentId` | `PluginDepartmentId`? | Department |
1964
- * | `storey` | `number`? (int) | Target storey number (default active storey) |
1965
- */
1966
- export const PluginCreateSpaceItem = z.object({
1967
- contour: ContourHandle,
1968
- height: z.number(),
1969
- label: z.string().optional(),
1970
- position: Vec3Handle.optional(),
1971
- spaceType: PluginSpaceType.optional(),
1972
- massType: PluginMassType.optional(),
1973
- departmentId: PluginDepartmentId.optional(),
1974
- storey: z.number().int().optional(),
1975
- })
1976
- export type PluginCreateSpaceItem = z.infer<typeof PluginCreateSpaceItem>
1977
-
1978
- export * from "./opening-fields"
1979
- export * from "./bulk-items"
1980
- export * from "./grids"
1
+ import * as z from "zod"
2
+ import { PluginApiReturn } from "../../../types"
3
+ import {
4
+ BrepHandle,
5
+ ComponentHandle,
6
+ ContourHandle,
7
+ LineHandle,
8
+ CurveHandle,
9
+ ProfileHandle,
10
+ Vec3Handle,
11
+ } from "../../../handles"
12
+ import { PluginGridStyleOptions } from "./grids"
13
+ import {
14
+ PluginSpaceType,
15
+ PluginMassType,
16
+ PluginDepartmentId,
17
+ } from "../../entity/space"
18
+ import {
19
+ PluginBuildableEnvelopePolygonVertex,
20
+ PluginBuildableEnvelopeSetbackTier,
21
+ PluginBuildableEnvelopeVerticalCap,
22
+ PluginBuildableEnvelopeCreateResult,
23
+ PluginDesignCreateBuildableEnvelopeFromSiteResult,
24
+ } from "../../entity/buildableEnvelope"
25
+ import { PluginOpeningBaseOptions } from "./opening-fields"
26
+ import type {
27
+ PluginCreateDoorItem,
28
+ PluginCreateFloorItem,
29
+ PluginCreateFurnitureItem,
30
+ PluginCreateVoidItem,
31
+ PluginCreateWallRunItem,
32
+ PluginCreateWindowItem,
33
+ } from "./bulk-items"
34
+ import { PluginWallTopProfile } from "./bulk-items"
35
+ import { PluginWallType } from "../types"
36
+ import {
37
+ PluginFamilyPlacement,
38
+ PluginFamilyFloorOptions,
39
+ PluginFamilySlabOptions,
40
+ PluginFamilyCeilingOptions,
41
+ PluginFamilyValues,
42
+ } from "../catalog"
43
+
44
+ /**
45
+ * `design.create.*` — author new scene-committed BIM entities.
46
+ *
47
+ * Every creator takes geometry handles + scalars and returns a
48
+ * {@linkcode ComponentHandle} (or `ComponentHandle[]` for plural creators;
49
+ * `wallRuns` returns one `ComponentHandle[]` per run) — the
50
+ * `Component.id` of the created entity, resolvable across the rest of the
51
+ * `design.*` surface. Creation is an undoable host call; it **throws** on failure
52
+ * (no `Result` wrapper — consistent with `core.geom.create.*`).
53
+ *
54
+ * Footprint objects take a {@linkcode ContourHandle} (outer profile + holes);
55
+ * build one with `snaptrude.core.geom.create.contourFromProfile(s)`.
56
+ *
57
+ * Family overloads take `{ type, values? }` from `design.catalog`. The family supplies
58
+ * construction; the contour or axis supplies instance geometry. Geometry uses SU,
59
+ * while family length overrides use mm and angles use degrees. Omitted parameters
60
+ * retain family defaults; shared/type, formula and geometry-bound overrides are refused.
61
+ * Placement never publishes a definition. Unsupported references or geometry fail before mutation.
62
+ *
63
+ * `door`/`window` place a parametric family into a host wall (the world point is
64
+ * projected onto the wall face to locate the opening; the wall is re-cut in one
65
+ * undo entry) and throw if the point projects outside the wall. Voids
66
+ * (`void`/`voids`) are the third hosted child: holes through slabs, floors,
67
+ * roofs and ceilings. `smartLayout` is
68
+ * the one creator returning a grouped {@linkcode PluginSmartLayoutResult} rather
69
+ * than a handle — it clones an in-scene template into one or more target spaces,
70
+ * merging every target into a single undo batch (see its JSDoc).
71
+ *
72
+ * Accessed via `snaptrude.design.create`.
73
+ */
74
+ export abstract class PluginDesignCreateApi {
75
+ constructor() {}
76
+
77
+ /**
78
+ * Create a **space** (a room / mass) by extruding a footprint contour upward.
79
+ *
80
+ * The `contour` is the bottom footprint (outer profile + optional holes); it
81
+ * is extruded by `height` along +Y. The space is placed on the active story
82
+ * and assigned a default department unless overridden.
83
+ *
84
+ * @param contour - Bottom footprint (outer + holes)
85
+ * @param height - Extrusion height (Snaptrude units, > 0)
86
+ * @param label - Room label (maps to the space's `room_type`)
87
+ * @param position - Offset from origin (default origin)
88
+ * @param spaceType - Space-type classification (default `Room`)
89
+ * @param massType - Mass-type classification (default `Room`)
90
+ * @param departmentId - Department assignment (default department)
91
+ * @param storey - Target storey number to place the space on — the same integer
92
+ * `entity.story` uses (`1` ground, `2` first floor, `-1` basement). Default:
93
+ * the active storey. Folds a `core.storeys.setActive` call into the create,
94
+ * placing the space at that storey's floor elevation (any `position` offset is
95
+ * applied on top). Throws if no storey has that value.
96
+ * @returns the {@linkcode ComponentHandle} of the created space
97
+ * @throws if the contour is invalid, the height is not positive, or the target
98
+ * storey does not exist
99
+ *
100
+ * @examplePrompt Create a room called Living Room that's 3m tall from this outline
101
+ * @examplePrompt Add a new space on the current storey using this footprint
102
+ * @examplePrompt Make a bedroom here and assign it to the Residential department
103
+ * @examplePrompt Extrude this closed outline into a 10 foot high room
104
+ * @examplePrompt Can you create a Kitchen space from the sketch I just drew?
105
+ * @examplePrompt Create a bedroom on the second floor from this outline
106
+ *
107
+ * @performance For MORE THAN ONE space, call `design.create.spaces(items[])` — the whole
108
+ * batch is one host round-trip. Looping this single-space creator is N round-trips (slow,
109
+ * and a large loop can trip the plugin rate limit).
110
+ *
111
+ * # Example
112
+ * ```ts
113
+ * const v = snaptrude.core.math.vec3
114
+ * const outer = await snaptrude.core.geom.create.profileFromLinePoints([
115
+ * await v.new(0, 0, 0),
116
+ * await v.new(10, 0, 0),
117
+ * await v.new(10, 0, 8),
118
+ * await v.new(0, 0, 8),
119
+ * ])
120
+ * const contour = await snaptrude.core.geom.create.contourFromProfile(outer)
121
+ * const space = await snaptrude.design.create.space(contour, 3, "Living")
122
+ * // …place one directly on the second floor (no setActive dance):
123
+ * const upstairs = await snaptrude.design.create.space(
124
+ * contour, 3, "Bedroom", undefined, undefined, undefined, undefined, 2,
125
+ * )
126
+ * ```
127
+ */
128
+ public abstract space(
129
+ contour: ContourHandle,
130
+ height: number,
131
+ label?: string,
132
+ position?: Vec3Handle,
133
+ spaceType?: PluginSpaceType,
134
+ massType?: PluginMassType,
135
+ departmentId?: PluginDepartmentId,
136
+ storey?: number,
137
+ ): PluginApiReturn<ComponentHandle>
138
+
139
+ /**
140
+ * Create a **mass** — a generic extruded prism (default `massType` "Generic mass").
141
+ * Same footprint-extrude path as {@linkcode PluginDesignCreateApi.space}.
142
+ *
143
+ * @param contour - Footprint (outer + holes)
144
+ * @param height - Extrusion height (> 0)
145
+ * @param label - Label (maps to `room_type`)
146
+ * @param position - Offset from origin
147
+ * @param massType - Mass type (default `Generic mass`)
148
+ * @returns the {@linkcode ComponentHandle} of the created mass
149
+ * @throws if the contour is invalid or the height is not positive
150
+ *
151
+ * @examplePrompt Create a generic mass 30m tall from this footprint
152
+ * @examplePrompt Block out a conceptual massing volume for the site
153
+ * @examplePrompt Extrude this shape into a building mass
154
+ * @examplePrompt Add a simple study-model volume at the origin
155
+ *
156
+ * # Example
157
+ * ```ts
158
+ * const rect = await snaptrude.core.geom.create.profileRect(20, 15)
159
+ * const contour = await snaptrude.core.geom.create.contourFromProfile(rect)
160
+ * const tower = await snaptrude.design.create.mass(contour, 30, "Tower A", undefined, "Building")
161
+ * ```
162
+ */
163
+ public abstract mass(
164
+ contour: ContourHandle,
165
+ height: number,
166
+ label?: string,
167
+ position?: Vec3Handle,
168
+ massType?: PluginMassType,
169
+ ): PluginApiReturn<ComponentHandle>
170
+
171
+ /**
172
+ * Create a **mass** from a custom solid built with a `core.geom.create` brep
173
+ * constructor (`brepFromFaces`, `brepFromExtrusion`, `brepFromLoft`,
174
+ * `brepFromMesh`, or a brep boolean) — the free-form counterpart of
175
+ * {@linkcode PluginDesignCreateApi.mass}, for solids that are not simple
176
+ * footprint extrusions (pyramids, wedges, faceted volumes).
177
+ *
178
+ * Only accepts breps **minted by a `core.geom.create` brep constructor** —
179
+ * scene-derived breps from `design.query.geometry.getBrep` are read-only and
180
+ * throw. The mass is created as a generic mass (`massType` "Generic mass") at
181
+ * the exact coordinates the solid was authored in, committed as a single
182
+ * undo step, and synced to collaborators like any other creator.
183
+ *
184
+ * @param brep - A solid from a `core.geom.create` brep constructor
185
+ * @param label - Label (maps to `room_type`)
186
+ * @returns the {@linkcode ComponentHandle} of the created mass
187
+ * @throws if the brep handle is invalid, or the brep was not created by a
188
+ * `core.geom.create` brep constructor
189
+ *
190
+ * @examplePrompt Create a mass from these faces
191
+ * @examplePrompt Turn this custom brep into a scene mass
192
+ * @examplePrompt Add a pyramid-shaped mass to the model
193
+ *
194
+ * # Example
195
+ * ```ts
196
+ * const apex = { x: 0, y: 4, z: 0 }
197
+ * const a = { x: -2, y: 0, z: -2 }
198
+ * const b = { x: 2, y: 0, z: -2 }
199
+ * const c = { x: 2, y: 0, z: 2 }
200
+ * const d = { x: -2, y: 0, z: 2 }
201
+ * const brep = await snaptrude.core.geom.create.brepFromFaces([
202
+ * [a, d, c, b],
203
+ * [a, b, apex],
204
+ * [b, c, apex],
205
+ * [c, d, apex],
206
+ * [d, a, apex],
207
+ * ])
208
+ * const pyramid = await snaptrude.design.create.massFromBrep(brep, "Pavilion")
209
+ * ```
210
+ */
211
+ public abstract massFromBrep(
212
+ brep: BrepHandle,
213
+ label?: string,
214
+ ): PluginApiReturn<ComponentHandle>
215
+
216
+ /**
217
+ * Create a **slab** by extruding a footprint contour by `thickness`
218
+ * (default direction: down).
219
+ *
220
+ * To cut a hole in an EXISTING one, use `design.create.void`; a contour hole
221
+ * here is baked into the footprint.
222
+ *
223
+ * @param contour - Footprint (outer + holes)
224
+ * @param thickness - Slab thickness (> 0)
225
+ * @param direction - Extrusion direction (default `"down"`)
226
+ * @param slabType - Slab-type classification
227
+ * @returns the {@linkcode ComponentHandle} of the created slab
228
+ * @throws if the contour is invalid or the thickness is not positive
229
+ *
230
+ * @examplePrompt Add a 150mm thick slab under this footprint
231
+ * @examplePrompt Create a floor slab from this outline
232
+ * @examplePrompt Pour a plinth slab over the building footprint
233
+ * @examplePrompt Make a basement slab that extrudes downward
234
+ * @examplePrompt Give this room outline a 6 inch slab
235
+ *
236
+ * # Example
237
+ * ```ts
238
+ * const rect = await snaptrude.core.geom.create.profileRect(12, 10)
239
+ * const contour = await snaptrude.core.geom.create.contourFromProfile(rect)
240
+ * const slab = await snaptrude.design.create.slab(contour, 0.15, "down", "Plinth")
241
+ * ```
242
+ */
243
+ public abstract slab(
244
+ ...args:
245
+ | [
246
+ contour: ContourHandle,
247
+ thickness: number,
248
+ direction?: "up" | "down",
249
+ slabType?: PluginSlabType,
250
+ ]
251
+ | [contour: ContourHandle, options: PluginFamilySlabOptions]
252
+ ): PluginApiReturn<ComponentHandle>
253
+
254
+ /**
255
+ * Create a **floor** by extruding a footprint contour by `thickness`
256
+ * (extruded upward).
257
+ *
258
+ * The native family overload takes `{ type, values?, position? }`. Contour
259
+ * geometry uses SU; family values use mm. The family supplies layer thickness.
260
+ * PG2 can split or merge the footprint, so both overloads return an array.
261
+ * A merge into an existing floor can return no newly created handles.
262
+ *
263
+ * To cut a hole in an EXISTING one, use `design.create.void`; a contour hole
264
+ * here is baked into the footprint.
265
+ *
266
+ * @param contour - Footprint (outer + holes)
267
+ * @param thickness - Floor thickness (> 0)
268
+ * @param position - Offset from origin
269
+ * @returns Newly created {@linkcode ComponentHandle}s; an empty array after a merge
270
+ * @throws if the contour is invalid or the thickness is not positive
271
+ *
272
+ * @examplePrompt Create a floor from this room outline
273
+ * @examplePrompt Add a 100mm thick floor over the footprint
274
+ * @examplePrompt Lay flooring across the selected outline
275
+ * @examplePrompt Put a floor at this position with 150mm thickness
276
+ *
277
+ * # Example
278
+ * ```ts
279
+ * const rect = await snaptrude.core.geom.create.profileRect(5, 4)
280
+ * const contour = await snaptrude.core.geom.create.contourFromProfile(rect)
281
+ * const floor = await snaptrude.design.create.floor(contour, 0.1)
282
+ * ```
283
+ *
284
+ * @performance For dimensional floors, `design.create.floors(items[])` uses one
285
+ * host round-trip and one undo entry. That bulk method does not accept native
286
+ * family options; use this method for a family-backed floor.
287
+ */
288
+ public abstract floor(
289
+ ...args:
290
+ | [contour: ContourHandle, thickness: number, position?: Vec3Handle]
291
+ | [contour: ContourHandle, options: PluginFamilyFloorOptions]
292
+ ): PluginApiReturn<ComponentHandle[]>
293
+
294
+ /**
295
+ * Create **many floors** in one undoable operation (bulk plural of
296
+ * {@linkcode floor}). Each item extrudes its own footprint contour (outer
297
+ * profile + holes) upward by its own `thickness`, at its own optional
298
+ * position offset. Validate-all-or-throw; one command.
299
+ *
300
+ * @param items - One {@linkcode PluginCreateFloorItem} per floor to create
301
+ * (≥1, ≤1000)
302
+ * @returns the created floors as {@linkcode ComponentHandle}`[]`, in input order
303
+ * @throws `VALIDATION` if the items fail the schema (empty array, more than
304
+ * 1000 items, a non-positive thickness) or a contour is degenerate;
305
+ * `HANDLE_INVALID` if a `contour` or `position` handle is unknown or
306
+ * released; `OPERATION_FAILED` if extrusion fails (nothing is created —
307
+ * `details.itemIndex` is the failing item); `METHOD_NOT_PERMITTED` if the
308
+ * plugin may not write.
309
+ *
310
+ * @examplePrompt Create floors for all these room outlines at once
311
+ * @examplePrompt Add a floor to every space in one operation
312
+ * @examplePrompt Bulk create the floor plates for this building
313
+ * @examplePrompt Lay 100mm floors across these five footprints in one undo step
314
+ * @examplePrompt Create the ground and first floor slabs together
315
+ *
316
+ * @performance Bulk creator — the whole batch is ONE host round-trip and ONE undo
317
+ * entry. Always prefer this over calling `design.create.floor` in a loop: build the
318
+ * full `items[]` array first (all contours and offsets up front), then make one call.
319
+ *
320
+ * # Example
321
+ * ```ts
322
+ * const rect = await snaptrude.core.geom.create.profileRect(5, 4)
323
+ * const contour = await snaptrude.core.geom.create.contourFromProfile(rect)
324
+ * const [ground, upper] = await snaptrude.design.create.floors([
325
+ * { contour, thickness: 0.1 },
326
+ * { contour, thickness: 0.1, position: await snaptrude.core.math.vec3.new(0, 3, 0) },
327
+ * ])
328
+ * ```
329
+ */
330
+ public abstract floors(
331
+ items: PluginCreateFloorItem[],
332
+ ): PluginApiReturn<ComponentHandle[]>
333
+
334
+ /**
335
+ * Create a **roof** by extruding a footprint contour by `thickness`. Created
336
+ * flat (extruded downward); pitch/slope is a separate post-creation edit.
337
+ *
338
+ * To cut a hole in an EXISTING one, use `design.create.void`; a contour hole
339
+ * here is baked into the footprint.
340
+ *
341
+ * @param contour - Footprint (outer + holes)
342
+ * @param thickness - Roof-slab thickness (> 0)
343
+ * @returns the {@linkcode ComponentHandle} of the created roof
344
+ * @throws if the contour is invalid or the thickness is not positive
345
+ *
346
+ * @examplePrompt Add a flat roof over the building footprint
347
+ * @examplePrompt Create a roof 200mm thick from this outline
348
+ * @examplePrompt Cover the top storey with a roof slab
349
+ * @examplePrompt Put a roof on this building
350
+ *
351
+ * # Example
352
+ * ```ts
353
+ * const rect = await snaptrude.core.geom.create.profileRect(12, 10)
354
+ * const contour = await snaptrude.core.geom.create.contourFromProfile(rect)
355
+ * const roof = await snaptrude.design.create.roof(contour, 0.2)
356
+ * ```
357
+ */
358
+ public abstract roof(
359
+ contour: ContourHandle,
360
+ thickness: number | PluginFamilyPlacement,
361
+ ): PluginApiReturn<ComponentHandle>
362
+
363
+ /**
364
+ * Create a **ceiling** by extruding a footprint contour by `thickness`, lifted
365
+ * `heightFromFloor` above the floor.
366
+ *
367
+ * To cut a hole in an EXISTING one, use `design.create.void`; a contour hole
368
+ * here is baked into the footprint.
369
+ *
370
+ * @param contour - Footprint (outer + holes)
371
+ * @param thickness - Ceiling thickness (> 0)
372
+ * @param heightFromFloor - Lift above the floor (default 0)
373
+ * @returns the {@linkcode ComponentHandle} of the created ceiling
374
+ * @throws if the contour is invalid or the thickness is not positive
375
+ *
376
+ * @examplePrompt Add a false ceiling 2.7m above the floor
377
+ * @examplePrompt Create a ceiling over this room footprint
378
+ * @examplePrompt Put a dropped ceiling in the living room at 3 metres
379
+ * @examplePrompt Add a 50mm thick ceiling lifted 2800mm from floor level
380
+ *
381
+ * # Example
382
+ * ```ts
383
+ * const rect = await snaptrude.core.geom.create.profileRect(5, 4)
384
+ * const contour = await snaptrude.core.geom.create.contourFromProfile(rect)
385
+ * const ceiling = await snaptrude.design.create.ceiling(contour, 0.05, 2.8)
386
+ * ```
387
+ */
388
+ public abstract ceiling(
389
+ ...args:
390
+ | [contour: ContourHandle, thickness: number, heightFromFloor?: number]
391
+ | [contour: ContourHandle, options: PluginFamilyCeilingOptions]
392
+ ): PluginApiReturn<ComponentHandle>
393
+
394
+ /**
395
+ * Create a **column** by extruding a cross-section contour upward by `height`
396
+ * from a base `position`. The column is built to EXACTLY `height` — unlike
397
+ * the interactive draw tool, no slab-thickness deduction is applied.
398
+ *
399
+ * @param position - Base point
400
+ * @param crossSection - Column cross-section footprint
401
+ * @param height - Column height (> 0), built exactly
402
+ * @param directionUp - Up axis (default world up); normalized by the host, so
403
+ * magnitude does not scale the height
404
+ * @returns the {@linkcode ComponentHandle} of the created column
405
+ * @throws if the cross-section is invalid, the height is not positive, or the
406
+ * direction is zero / lies in the cross-section plane (degenerate extrusion)
407
+ *
408
+ * @examplePrompt Add a 3m tall column at this point
409
+ * @examplePrompt Place a column at the grid intersection
410
+ * @examplePrompt Create a 450mm square concrete column 4 metres high
411
+ * @examplePrompt Stand a column up from this base point
412
+ *
413
+ * # Example
414
+ * ```ts
415
+ * const rect = await snaptrude.core.geom.create.profileRect(0.45, 0.45)
416
+ * const crossSection = await snaptrude.core.geom.create.contourFromProfile(rect)
417
+ * const base = await snaptrude.core.math.vec3.new(4, 0, 4)
418
+ * const column = await snaptrude.design.create.column(base, crossSection, 3)
419
+ * ```
420
+ */
421
+ public abstract column(
422
+ ...args:
423
+ | [
424
+ position: Vec3Handle,
425
+ crossSection: ContourHandle,
426
+ height: number,
427
+ directionUp?: Vec3Handle,
428
+ ]
429
+ | [axis: LineHandle, options: PluginFamilyPlacement]
430
+ ): PluginApiReturn<ComponentHandle>
431
+
432
+ /**
433
+ * Create a **beam** by extruding a cross-section contour along `direction` by
434
+ * `length`. Matches the Snaptrude beam tool's semantics: the beam body hangs
435
+ * BELOW the plane the section is authored on.
436
+ *
437
+ * For a **horizontal** `direction` (the common spanning beam), author the
438
+ * section as an axis-aligned plan rectangle: its X extent is the beam's
439
+ * width, its Z extent is the beam's depth (the vertical drop). The beam runs
440
+ * `length` along `direction` starting at the section's centre, hanging below
441
+ * the authoring plane by its depth — exactly like an interactively drawn
442
+ * beam. Only axis-aligned rectangular sections are supported horizontally.
443
+ *
444
+ * For a **vertical** `direction` (the default), the section itself is
445
+ * extruded by `length`, again hanging below the authoring plane. Diagonal
446
+ * directions are rejected.
447
+ *
448
+ * @param section - Beam cross-section, an axis-aligned rectangle in plan
449
+ * (X extent = width, Z extent = depth)
450
+ * @param length - Beam length along `direction` (> 0); direction is
451
+ * normalized by the host, so magnitude does not scale the length
452
+ * @param direction - Beam axis: horizontal for a spanning beam, or vertical
453
+ * (default world up). Diagonal directions throw.
454
+ * @returns the {@linkcode ComponentHandle} of the created beam
455
+ * @throws if the section is invalid (horizontal beams: not an axis-aligned
456
+ * plan rectangle), the length is not positive, or the direction is zero or
457
+ * diagonal
458
+ *
459
+ * @examplePrompt Add a beam spanning 6 metres between the columns
460
+ * @examplePrompt Create a horizontal beam along the X axis
461
+ * @examplePrompt Run a 300x600 beam across the room
462
+ * @examplePrompt Extrude this cross-section into a 5m long beam
463
+ *
464
+ * # Example
465
+ * ```ts
466
+ * const rect = await snaptrude.core.geom.create.profileRect(0.3, 0.6)
467
+ * const section = await snaptrude.core.geom.create.contourFromProfile(rect)
468
+ * const alongX = await snaptrude.core.math.vec3.new(1, 0, 0)
469
+ * const beam = await snaptrude.design.create.beam(section, 6, alongX)
470
+ * ```
471
+ */
472
+ public abstract beam(
473
+ ...args:
474
+ | [section: ContourHandle, length: number, direction?: Vec3Handle]
475
+ | [axis: LineHandle, options: PluginFamilyPlacement]
476
+ ): PluginApiReturn<ComponentHandle>
477
+
478
+ /**
479
+ * Create a connected run of **walls** from a profile — one wall per curve in
480
+ * the profile's chain, with mitred corners at shared endpoints.
481
+ *
482
+ * @param profile - Ordered curve chain (wall centerlines)
483
+ * @param height - Wall height (default engine default)
484
+ * @param thickness - Wall thickness (default: the `wallType`'s total layer
485
+ * thickness when `wallType` is given, else the engine default)
486
+ * @param wallType - Legacy label/`wall:` id, or the exact `nativeFamily`
487
+ * `{ id, version }` pin from `design.types.list("wall")`. Native pins are
488
+ * authorized on every use; selecting a family in Draw is not registration.
489
+ * Native pins support straight horizontal centerlines and flat tops only.
490
+ * Omit thickness for native pins; a conflicting explicit thickness rejects.
491
+ * Default: the engine's generic wall.
492
+ * @param storey - Target storey (integer: `1` ground, `2` first floor, `-1`
493
+ * basement — same convention as `design.create.space`). The walls are
494
+ * assigned to it and lifted to its base elevation, all inside the same
495
+ * single undo entry. The storey must already exist
496
+ * (`core.storeys.create` first). Default: geometry-derived — a profile
497
+ * drawn at y = 0 lands on storey 1; the ACTIVE storey is NOT consulted.
498
+ * @param topProfiles - Optional top edge per curve of `profile` (same order):
499
+ * a {@linkcode PluginWallTopProfile} makes that wall raked / gabled; `null`
500
+ * or a missing entry keeps it flat at `height`. Straight curves only. Such
501
+ * walls are non-parametric (see {@linkcode PluginWallTopProfile}).
502
+ * @returns a {@linkcode ComponentHandle}`[]` (one per wall), in profile order
503
+ * @throws if the profile is empty, `wallType` names no wall type in the
504
+ * project, `storey` does not exist, `topProfiles` has more entries than the
505
+ * profile has curves or names an arc curve, a top profile drops to the wall
506
+ * base at a mitred corner, or wall creation fails
507
+ *
508
+ * @examplePrompt Draw walls along this centerline
509
+ * @examplePrompt Create 3m high, 200mm thick walls from these points
510
+ * @examplePrompt Build a wall run around the room outline
511
+ * @examplePrompt Turn this polyline sketch into walls
512
+ * @examplePrompt Add walls with mitred corners along the path
513
+ * @examplePrompt Draw brick walls with plaster along this outline
514
+ * @examplePrompt Build the perimeter in exposed concrete walls
515
+ * @examplePrompt Create a raked wall that slopes from 3m to 5m
516
+ * @examplePrompt Build a gable end wall with the ridge in the middle
517
+ * @examplePrompt Draw a wall with a sloped top edge
518
+ *
519
+ * # Example
520
+ * ```ts
521
+ * const v = snaptrude.core.math.vec3
522
+ * const centerlines = await snaptrude.core.geom.create.profileFromLinePoints([
523
+ * await v.new(0, 0, 0),
524
+ * await v.new(8, 0, 0),
525
+ * await v.new(8, 0, 6),
526
+ * ])
527
+ * const walls = await snaptrude.design.create.walls(centerlines, 3, 0.2)
528
+ * // …built as a specific wall type (layers/material/thickness from the type):
529
+ * const [brick] = await snaptrude.design.types.list("wall")
530
+ * const brickWalls = await snaptrude.design.create.walls(
531
+ * centerlines, 3, undefined, brick.label,
532
+ * )
533
+ * // …a raked wall (3m at the start, 5m at the end) on a single centerline:
534
+ * const run = await snaptrude.core.geom.create.profileFromCurves([
535
+ * await snaptrude.core.geom.create.line(await v.new(0, 0, 10), await v.new(8, 0, 10)),
536
+ * ])
537
+ * const [raked] = await snaptrude.design.create.walls(run, undefined, 0.2, undefined, undefined, [
538
+ * [{ along: 0, height: 3 }, { along: 1, height: 5 }],
539
+ * ])
540
+ * ```
541
+ *
542
+ * @performance For MORE THAN ONE run, call `design.create.wallRuns(items[])` — one
543
+ * host round-trip and ONE undo entry for every run in the batch. Looping this
544
+ * single-run creator is N round-trips and N undo entries.
545
+ */
546
+ public abstract walls(
547
+ profile: ProfileHandle,
548
+ height?: number,
549
+ thickness?: number,
550
+ wallType?: PluginWallType,
551
+ storey?: number,
552
+ topProfiles?: (PluginWallTopProfile | null)[],
553
+ values?: PluginFamilyValues,
554
+ ): PluginApiReturn<ComponentHandle[]>
555
+
556
+ /**
557
+ * Create **many wall runs** in one undoable operation (bulk plural of
558
+ * {@linkcode walls}). Each item is one full run — a wall per curve in that
559
+ * item's profile chain, mitred at shared endpoints. Junctions resolve within a
560
+ * run, not between runs. Each item carries its own
561
+ * dimensions, `wallType` and `storey`, so a single call can build a whole
562
+ * floor plate or several storeys at once. Per-curve `topProfiles` make raked
563
+ * or gabled walls (see {@linkcode PluginWallTopProfile}). Validate-all-or-throw;
564
+ * one command.
565
+ *
566
+ * @param items - One {@linkcode PluginCreateWallRunItem} per run to create
567
+ * (≥1, ≤1000)
568
+ * @returns one {@linkcode ComponentHandle}`[]` per item, in input order —
569
+ * each inner array holding that run's walls in profile-curve order
570
+ * @throws `VALIDATION` if the items fail the schema (empty array, more than
571
+ * 1000 items, a non-positive height or thickness, a zero-length curve);
572
+ * `HANDLE_INVALID` if a `profile` handle is unknown or released;
573
+ * `PRECONDITION_FAILED` if a profile has no curves, `wallType` names no
574
+ * wall type in the project, or `storey` does not exist;
575
+ * `OPERATION_FAILED` if wall creation fails (nothing is created —
576
+ * `details.itemIndex` is the failing item); `METHOD_NOT_PERMITTED` if the
577
+ * plugin may not write.
578
+ *
579
+ * @examplePrompt Draw all the walls of this floor plan at once
580
+ * @examplePrompt Build every room's perimeter in one operation
581
+ * @examplePrompt Bulk create wall runs from these centerlines
582
+ * @examplePrompt Create the walls for both storeys in a single undo step
583
+ * @examplePrompt Turn these polylines into 200mm brick walls together
584
+ * @examplePrompt Import these raked walls with their top profiles in one go
585
+ *
586
+ * @performance Bulk creator — the whole batch is ONE host round-trip and ONE undo
587
+ * entry. Always prefer this over calling `design.create.walls` in a loop: build the
588
+ * full `items[]` array first (all profiles up front), then make one call.
589
+ *
590
+ * # Example
591
+ * ```ts
592
+ * const v = snaptrude.core.math.vec3
593
+ * const ground = await snaptrude.core.geom.create.profileFromLinePoints([
594
+ * await v.new(0, 0, 0),
595
+ * await v.new(8, 0, 0),
596
+ * await v.new(8, 0, 6),
597
+ * ])
598
+ * const upper = await snaptrude.core.geom.create.profileFromLinePoints([
599
+ * await v.new(0, 0, 0),
600
+ * await v.new(8, 0, 0),
601
+ * ])
602
+ * const [groundWalls, upperWalls] = await snaptrude.design.create.wallRuns([
603
+ * { profile: ground, height: 3, thickness: 0.2 },
604
+ * { profile: upper, height: 3, storey: 2 },
605
+ * // one gable wall: 3m at both ends, 5m ridge in the middle
606
+ * { profile: upper, thickness: 0.2, storey: 3,
607
+ * topProfiles: [[{ along: 0, height: 3 }, { along: 0.5, height: 5 }, { along: 1, height: 3 }]] },
608
+ * ])
609
+ * console.log(groundWalls.length, "walls on the ground floor")
610
+ * ```
611
+ */
612
+ public abstract wallRuns(
613
+ items: PluginCreateWallRunItem[],
614
+ ): PluginApiReturn<ComponentHandle[][]>
615
+
616
+ /**
617
+ * Create a **staircase** from a parametric preset, placed at a point.
618
+ *
619
+ * Geometry is procedurally generated from the chosen `preset`; there is no
620
+ * footprint input. Placed on the active structure/story unless overridden.
621
+ * The promise resolves only after the staircase's asynchronous geometry union
622
+ * and creation-command capture settle; a union failure rejects the call.
623
+ *
624
+ * @param preset - The parametric preset to generate
625
+ * @param position - Placement point
626
+ * @param label - Optional label
627
+ * @param structureId - Target structure (default active)
628
+ * @param level - Target level name (default active/`"01"`)
629
+ * @param dimensions - Optional positive dimension overrides in Snaptrude engine
630
+ * units, plus a placement angle in 90-degree increments. Overrides are
631
+ * applied to the staircase parameters before the creation command runs.
632
+ * @returns the {@linkcode ComponentHandle} of the created staircase
633
+ * @throws if the preset is unknown or placement fails
634
+ *
635
+ * @examplePrompt Add a dog-legged staircase at this location
636
+ * @examplePrompt Place a straight stair on level 01
637
+ * @examplePrompt Create an L-shaped staircase in the lobby
638
+ * @examplePrompt Insert a staircase near the entrance
639
+ * @examplePrompt Put amphitheatre steps at these coordinates
640
+ *
641
+ * # Example
642
+ * ```ts
643
+ * const stair = await snaptrude.design.create.staircase(
644
+ * "dogLegged",
645
+ * await snaptrude.core.math.vec3.new(5, 0, 5),
646
+ * )
647
+ * ```
648
+ */
649
+ public abstract staircase(
650
+ preset: PluginStaircasePreset,
651
+ position: Vec3Handle,
652
+ label?: string,
653
+ structureId?: string,
654
+ level?: string,
655
+ dimensions?: PluginStaircaseDimensions,
656
+ ): PluginApiReturn<ComponentHandle>
657
+
658
+ /**
659
+ * Create **reference lines** from a profile — one reference line per curve in
660
+ * the profile's ordered chain. Returns a handle per created line.
661
+ *
662
+ * Optional style (`color`, `thickness`, `style`, `gridTag`) applies to
663
+ * every created line.
664
+ *
665
+ * @param profile - Ordered curve chain (centerlines)
666
+ * @param color - Hex color `"#RRGGBB"` (default white)
667
+ * @param thickness - Line thickness (default 1)
668
+ * @param style - Line style (default `SOLID`)
669
+ * @param gridTag - Grid tag label (default `"A"`)
670
+ * @returns a {@linkcode ComponentHandle}`[]` (one per curve), in profile order
671
+ * @throws if the profile is empty or creation fails
672
+ *
673
+ * @examplePrompt Draw a reference line between these two points
674
+ * @examplePrompt Add dashed guide lines along this path
675
+ * @examplePrompt Create grid lines tagged A through the plan
676
+ * @examplePrompt Set up construction lines for the column grid
677
+ * @examplePrompt Mark a red reference line across the site
678
+ *
679
+ * # Example
680
+ * ```ts
681
+ * const v = snaptrude.core.math.vec3
682
+ * const profile = await snaptrude.core.geom.create.profileFromLinePoints([
683
+ * await v.new(0, 0, 0),
684
+ * await v.new(10, 0, 0),
685
+ * ])
686
+ * const lines = await snaptrude.design.create.referenceLines(
687
+ * profile,
688
+ * undefined,
689
+ * undefined,
690
+ * "DASHED",
691
+ * )
692
+ * ```
693
+ */
694
+ public abstract referenceLines(
695
+ profile: ProfileHandle,
696
+ color?: string,
697
+ thickness?: number,
698
+ style?: PluginReferenceLineStyle,
699
+ gridTag?: string,
700
+ ): PluginApiReturn<ComponentHandle[]>
701
+
702
+ /**
703
+ * Create a free rectangular **structural grid** — one grid group with two
704
+ * perpendicular line families (`linesX` lines spaced `spacingX` apart, and
705
+ * `linesY` lines spaced `spacingY` apart) on the active storey. One undoable
706
+ * creation.
707
+ *
708
+ * Units: spacings in Snaptrude units; `angle` in degrees (CCW about +Y,
709
+ * default 0). Without `center` the grid is centred on the current viewport
710
+ * (the native tool's behavior) — pass `center` for deterministic placement.
711
+ *
712
+ * @param linesX - Line count of the first family (integer ≥ 1)
713
+ * @param linesY - Line count of the second family (integer ≥ 1)
714
+ * @param spacingX - First-family spacing (Snaptrude units, > 0)
715
+ * @param spacingY - Second-family spacing (Snaptrude units, > 0)
716
+ * @param options - `angle`, `center`, and {@linkcode PluginGridStyleOptions}
717
+ * @returns the created grid group's {@linkcode ComponentHandle}
718
+ *
719
+ * @examplePrompt Create a 5 by 8 structural grid with 6 metre spacing
720
+ * @examplePrompt Add a column grid rotated 30 degrees
721
+ * @examplePrompt Set up a structural grid at the site origin
722
+ *
723
+ * # Example
724
+ * ```ts
725
+ * const centre = await snaptrude.core.math.vec3.new(0, 0, 0)
726
+ * const grid = await snaptrude.design.create.grid(5, 8, 6, 6, { center: centre })
727
+ * ```
728
+ */
729
+ public abstract grid(
730
+ linesX: number,
731
+ linesY: number,
732
+ spacingX: number,
733
+ spacingY: number,
734
+ options?: {
735
+ angle?: number
736
+ center?: Vec3Handle
737
+ } & PluginGridStyleOptions,
738
+ ): PluginApiReturn<ComponentHandle>
739
+
740
+ /**
741
+ * Create a **structural grid from a single curve** — one grid group holding
742
+ * one wing with one line (the draw tool's output). The curve may be a line
743
+ * or an arc; circles are rejected with `VALIDATION`. Undoable.
744
+ *
745
+ * @param curve - The grid line's geometry ({@linkcode CurveHandle}, world space)
746
+ * @param options - `tag` (defaults to the draw tool's next tag) and
747
+ * {@linkcode PluginGridStyleOptions}
748
+ * @returns the created grid group's {@linkcode ComponentHandle}
749
+ *
750
+ * @examplePrompt Draw a grid line from A to B
751
+ * @examplePrompt Add a curved grid line through these points
752
+ * @examplePrompt Create grid line "C" along this wall
753
+ *
754
+ * # Example
755
+ * ```ts
756
+ * const { vec3 } = snaptrude.core.math
757
+ * const from = await vec3.new(0, 0, 0)
758
+ * const to = await vec3.new(30, 0, 0)
759
+ * const curve = await snaptrude.core.geom.create.line(from, to)
760
+ * const grid = await snaptrude.design.create.gridFromCurve(curve, { tag: "A" })
761
+ * ```
762
+ */
763
+ public abstract gridFromCurve(
764
+ curve: CurveHandle,
765
+ options?: {
766
+ tag?: string
767
+ } & PluginGridStyleOptions,
768
+ ): PluginApiReturn<ComponentHandle>
769
+
770
+ /**
771
+ * Generate **structural grids fitted to mass footprints** — one grid group
772
+ * per footprint cluster, with counts and orientation derived from each
773
+ * footprint's geometry and the given spacings. All created groups commit as
774
+ * ONE undoable unit.
775
+ *
776
+ * Lenient like the native tool: masses without an extractable footprint
777
+ * (no B-rep, degenerate top face) are skipped; throws `VALIDATION` only when
778
+ * NONE of the given masses yields a footprint.
779
+ *
780
+ * @param masses - Masses whose footprints seat the grids
781
+ * @param spacingX - First-family spacing (Snaptrude units, > 0)
782
+ * @param spacingY - Second-family spacing (Snaptrude units, > 0)
783
+ * @param options - {@linkcode PluginGridStyleOptions}
784
+ * @returns the created grid groups' {@linkcode ComponentHandle}`[]`
785
+ *
786
+ * @examplePrompt Generate grids for the selected masses
787
+ * @examplePrompt Auto-generate a 6m structural grid on this building
788
+ * @examplePrompt Fit column grids to every mass on this storey
789
+ *
790
+ * # Example
791
+ * ```ts
792
+ * const masses = await snaptrude.design.query.listMasses({ storeys: [1] })
793
+ * const grids = await snaptrude.design.create.gridsFromMasses(masses, 6, 6)
794
+ * ```
795
+ */
796
+ public abstract gridsFromMasses(
797
+ masses: ComponentHandle[],
798
+ spacingX: number,
799
+ spacingY: number,
800
+ options?: PluginGridStyleOptions,
801
+ ): PluginApiReturn<ComponentHandle[]>
802
+
803
+ /**
804
+ * Create **columns at every grid-line intersection** of the given grid
805
+ * groups, sized `width` × `depth` and oriented to the grid axes, on the
806
+ * grids' storey. All columns commit as ONE undoable unit.
807
+ *
808
+ * @param grids - Grid groups whose intersections seat the columns
809
+ * @param width - Column width (Snaptrude units, > 0)
810
+ * @param depth - Column depth (Snaptrude units, > 0)
811
+ * @returns the created columns' {@linkcode ComponentHandle}`[]`
812
+ * @throws `VALIDATION` when an id does not resolve to a grid group;
813
+ * `OPERATION_FAILED` when the grids have no line intersections
814
+ *
815
+ * @examplePrompt Generate columns on this grid
816
+ * @examplePrompt Place 450mm square columns at every grid intersection
817
+ *
818
+ * # Example
819
+ * ```ts
820
+ * const grids = await snaptrude.design.query.listGrids()
821
+ * const columns = await snaptrude.design.create.columnsFromGrids(grids, 0.45, 0.45)
822
+ * ```
823
+ */
824
+ public abstract columnsFromGrids(
825
+ grids: ComponentHandle[],
826
+ width: number,
827
+ depth: number,
828
+ ): PluginApiReturn<ComponentHandle[]>
829
+
830
+ /**
831
+ * Place a **furniture** item from the project library at a world position.
832
+ *
833
+ * Identified by a **catalog (library) id** — the team object's stable `_id` for
834
+ * project uploads, or the item's `fullName` for the general library. Discover ids
835
+ * with {@linkcode PluginDesignFurnitureApi.listCatalog}. Placement is asynchronous
836
+ * (the source mesh is fetched if not cached) and creates one undo entry.
837
+ *
838
+ * Placed at the item's default rotation unless `angleInDegrees` is given, which
839
+ * rotates it about the vertical axis at creation time (baked into the same undo
840
+ * entry). To rotate an existing instance instead, use `design.transform.rotate`.
841
+ *
842
+ * @param catalogId - Library id: team `_id` or general `fullName`
843
+ * @param position - Absolute world placement point. `position.y` is the REST
844
+ * elevation: the item is grounded so its bounding-box base sits exactly at
845
+ * `position.y` (the same surface-flush contract as interactive drag-drop) —
846
+ * pass the floor/storey elevation to stand furniture on it; never add half
847
+ * the item's height yourself.
848
+ * @param options - Optional placement options: `label` — instance name and
849
+ * readable label (`design.query.getLabel`; default auto `${name}Ins${n}`);
850
+ * `createNewSourceMesh` — legacy flag, kept for compatibility. The host owns
851
+ * source-mesh persistence: a catalog source is recorded exactly once, by the
852
+ * first placement that brings it in, and this flag cannot skip that record.
853
+ * @param angleInDegrees - Optional signed rotation about the vertical axis, in
854
+ * degrees (same convention as {@linkcode PluginDesignTransformApi.rotate}).
855
+ * Applied at creation time so it is part of the placement's single undo entry.
856
+ * Default: the item's own (unrotated) orientation.
857
+ * @returns the {@linkcode ComponentHandle} of the placed furniture instance
858
+ * @throws if the catalog id is unknown, the source mesh fails to load, or placement fails;
859
+ * `PRECONDITION_FAILED` (`details.engineCode: "TOOL_ACTIVE"`) while the interactive
860
+ * furniture tool is active — finish or cancel it first
861
+ *
862
+ * @examplePrompt Place a chair from the library at this spot
863
+ * @examplePrompt Add a sofa from the furniture catalog to the living room
864
+ * @examplePrompt Drop a desk into the scene at 3, 0, 5
865
+ * @examplePrompt Insert the uploaded bed model in the bedroom
866
+ * @examplePrompt Put a table from my project library here and name it Table-01
867
+ * @examplePrompt Place a chair here rotated 90 degrees
868
+ *
869
+ * # Example
870
+ * ```ts
871
+ * const chair = await snaptrude.design.create.furniture(
872
+ * "6620f1a…", // from design.furniture.listCatalog()
873
+ * await snaptrude.core.math.vec3.new(3, 0, 5),
874
+ * { label: "Chair-01" },
875
+ * )
876
+ * // …placed already turned 90° about the vertical axis:
877
+ * const turned = await snaptrude.design.create.furniture(
878
+ * "6620f1a…",
879
+ * await snaptrude.core.math.vec3.new(3, 0, 5),
880
+ * undefined,
881
+ * 90,
882
+ * )
883
+ * ```
884
+ *
885
+ * @performance For MORE THAN ONE item, call `design.create.furnitureItems(items[])` —
886
+ * one host round-trip and ONE undo entry, and a catalog source shared by several
887
+ * items is fetched once. Looping this single-item creator is N round-trips.
888
+ */
889
+ public abstract furniture(
890
+ catalogId: string,
891
+ position: Vec3Handle,
892
+ options?: { label?: string; createNewSourceMesh?: boolean },
893
+ angleInDegrees?: number,
894
+ ): PluginApiReturn<ComponentHandle>
895
+
896
+ /**
897
+ * Place **many furniture items** in one undoable operation (bulk plural of
898
+ * {@linkcode furniture}). Each item names a catalog id and an absolute world
899
+ * position (`position.y` is the REST elevation — the same grounding contract
900
+ * as the singular creator), with optional label and rotation. A catalog
901
+ * source shared by several items is fetched and recorded once for the whole
902
+ * batch, so the host — not the caller — owns source persistence (there is no
903
+ * per-item `createNewSourceMesh`). Validate-all-or-throw; one command.
904
+ *
905
+ * @param items - One {@linkcode PluginCreateFurnitureItem} per instance to
906
+ * place (≥1, ≤1000)
907
+ * @returns the placed instances as {@linkcode ComponentHandle}`[]`, in input order
908
+ * @throws `VALIDATION` if the items fail the schema (empty array, more than
909
+ * 1000 items); `HANDLE_INVALID` if a `position` handle is unknown or
910
+ * released; `PRECONDITION_FAILED` if a catalog id is unknown, a furniture
911
+ * tool is active, or a parametric group is active (exit it first, or place
912
+ * items one at a time with `furniture`); `OPERATION_FAILED` if a source
913
+ * mesh fails to load or placement fails (nothing is created —
914
+ * `details.itemIndex` is the failing item); `METHOD_NOT_PERMITTED` if the plugin may not write.
915
+ *
916
+ * @examplePrompt Place all the desks for this office at once
917
+ * @examplePrompt Furnish every bedroom in one operation
918
+ * @examplePrompt Bulk place these chairs around the table
919
+ * @examplePrompt Add the whole furniture layout in a single undo step
920
+ * @examplePrompt Drop twenty copies of this chair at these positions
921
+ *
922
+ * @performance Bulk creator — the whole batch is ONE host round-trip and ONE undo
923
+ * entry, and each distinct catalog source loads once. Always prefer this over
924
+ * calling `design.create.furniture` in a loop: build the full `items[]` array
925
+ * first (all positions and angles up front), then make one call.
926
+ *
927
+ * # Example
928
+ * ```ts
929
+ * const v = snaptrude.core.math.vec3
930
+ * const [chair] = await snaptrude.design.furniture.listCatalog()
931
+ * const placed = await snaptrude.design.create.furnitureItems([
932
+ * { catalogId: chair.id, position: await v.new(3, 0, 5), label: "Chair-01" },
933
+ * { catalogId: chair.id, position: await v.new(4, 0, 5), angleInDegrees: 90 },
934
+ * { catalogId: chair.id, position: await v.new(5, 0, 5), angleInDegrees: 180 },
935
+ * ])
936
+ * ```
937
+ */
938
+ public abstract furnitureItems(
939
+ items: PluginCreateFurnitureItem[],
940
+ ): PluginApiReturn<ComponentHandle[]>
941
+
942
+ /**
943
+ * Place a parametric **door** family into a host wall.
944
+ *
945
+ * Identified by a saved **family definition id**. Discover ids with
946
+ * {@linkcode PluginDesignDoorsApi.listCatalog}. The world `position` is
947
+ * projected onto the host wall's face to locate the opening; the wall is
948
+ * re-cut and the door hosted in a single undo entry. Placement is
949
+ * asynchronous. Values and host bindings are evaluated before the first cut.
950
+ *
951
+ * @param familyId - Hosted door definition ID; legacy mesh catalog IDs are not accepted
952
+ * @param hostWall - The wall to host the door
953
+ * @param position - World point projected onto the wall to locate the opening
954
+ * @param options - `version` pins the definition. `values` sets authored parameters
955
+ * (numeric lengths in SU, or unit strings). `label` names the instance.
956
+ * `hinge` selects the declared `hand` enum: local -X is left, +X is right.
957
+ * @param facing - World point selecting local +Z; omitted uses the picked face
958
+ * @returns the {@linkcode ComponentHandle} of the placed door
959
+ * @throws if the family/category/parameters are invalid, or placement or cutting fails
960
+ *
961
+ * @examplePrompt Add a door to this wall here
962
+ * @examplePrompt Place a sliding door on the selected wall
963
+ * @examplePrompt Put a door from the catalog at this point on the wall
964
+ * @examplePrompt Insert the entrance door into this wall
965
+ * @examplePrompt Add a door to the wall and call it Entry-01
966
+ * @examplePrompt Add a door that opens into the living room
967
+ * @examplePrompt Put a door here hinged on the north jamb
968
+ *
969
+ * # Example
970
+ * ```ts
971
+ * const [wall] = await snaptrude.design.query.listWalls({ isSelected: true })
972
+ * const [entry] = await snaptrude.design.doors.listCatalog()
973
+ * const door = await snaptrude.design.create.door(
974
+ * entry.id,
975
+ * wall,
976
+ * await snaptrude.core.math.vec3.new(3, 0, 5),
977
+ * { label: "Entry-01" },
978
+ * )
979
+ * // …facing a specific side of the wall (pass a point inside that room):
980
+ * const intoKitchen = await snaptrude.design.create.door(
981
+ * entry.id,
982
+ * wall,
983
+ * await snaptrude.core.math.vec3.new(3, 0, 5),
984
+ * undefined,
985
+ * await snaptrude.core.math.vec3.new(3, 0, 9),
986
+ * )
987
+ * // …and hinged on the jamb nearest a point (here the +x end of the opening):
988
+ * const hingedRight = await snaptrude.design.create.door(
989
+ * entry.id,
990
+ * wall,
991
+ * await snaptrude.core.math.vec3.new(3, 0, 5),
992
+ * { hinge: await snaptrude.core.math.vec3.new(3.5, 0, 5) },
993
+ * await snaptrude.core.math.vec3.new(3, 0, 9),
994
+ * )
995
+ * ```
996
+ *
997
+ * @performance For MORE THAN ONE door, call `design.create.doors(items[])` — one host
998
+ * round-trip and ONE undo entry, with each host wall re-cut once per opening inside
999
+ * that single call. Looping this single-door creator is N round-trips.
1000
+ */
1001
+ public abstract door(
1002
+ familyId: string,
1003
+ hostWall: ComponentHandle,
1004
+ position: Vec3Handle,
1005
+ options?: {
1006
+ label?: string
1007
+ hinge?: Vec3Handle
1008
+ version?: number
1009
+ values?: Record<string, number | string>
1010
+ },
1011
+ facing?: Vec3Handle,
1012
+ ): PluginApiReturn<ComponentHandle>
1013
+
1014
+ /**
1015
+ * Place **many doors** into host walls in one undoable operation (bulk plural
1016
+ * of {@linkcode door}). Each item carries its own family id, host wall,
1017
+ * world position, and optional `facing`, `label`, `width` and `height` — the
1018
+ * same fields as a `"door"` {@linkcode PluginDesignCreateOpeningOptions}.
1019
+ * Validate-all-or-throw; one command.
1020
+ *
1021
+ * The wall keeps its identity. Several items may use the same host.
1022
+ * Width and height set named recipe parameters, not mesh scale. The recipe's
1023
+ * evaluated hole can include frame allowances. A failed item rolls back the batch.
1024
+ *
1025
+ * @param items - One {@linkcode PluginCreateDoorItem} per door to place
1026
+ * (≥1, ≤1000)
1027
+ * @returns the placed doors as {@linkcode ComponentHandle}`[]`, in input order
1028
+ * @throws `VALIDATION` if the items fail the schema (empty array, more than
1029
+ * 1000 items, unknown fields, a non-positive `width`/`height`);
1030
+ * `HANDLE_INVALID` if a `position`, `facing` or `hinge` handle is unknown
1031
+ * or released; `PRECONDITION_FAILED` if the family cannot be resolved or saved,
1032
+ * or a host is locked / is not in the active proposal;
1033
+ * `OPERATION_FAILED` if placement, containment, overlap checks or cutting fail
1034
+ * (nothing is created — `details.itemIndex` is the failing item);
1035
+ * `METHOD_NOT_PERMITTED` if the plugin may not write.
1036
+ *
1037
+ * @examplePrompt Add doors to all of these walls at once
1038
+ * @examplePrompt Place a door in every room in one operation
1039
+ * @examplePrompt Bulk add the entrance doors from this schedule
1040
+ * @examplePrompt Put three doors on this wall in a single undo step
1041
+ * @examplePrompt Add all the doors for this floor plan together
1042
+ *
1043
+ * @performance Bulk creator — the whole batch is ONE host round-trip and ONE undo
1044
+ * entry. Always prefer this over
1045
+ * calling `design.create.door` in a loop: build the full `items[]` array first
1046
+ * (all host walls and points up front), then make one call.
1047
+ *
1048
+ * # Example
1049
+ * ```ts
1050
+ * const v = snaptrude.core.math.vec3
1051
+ * const [wall] = await snaptrude.design.query.listWalls({ isSelected: true })
1052
+ * const [entry] = await snaptrude.design.doors.listCatalog()
1053
+ * const [front, side] = await snaptrude.design.create.doors([
1054
+ * { familyId: entry.id, hostWall: wall, position: await v.new(2, 0, 5), label: "D-01" },
1055
+ * { familyId: entry.id, hostWall: wall, position: await v.new(6, 0, 5), values: { width: "1200mm" } },
1056
+ * ])
1057
+ * // The host still resolves after both cuts:
1058
+ * const currentWall = await snaptrude.design.query.getHost(front)
1059
+ * ```
1060
+ */
1061
+ public abstract doors(
1062
+ items: PluginCreateDoorItem[],
1063
+ ): PluginApiReturn<ComponentHandle[]>
1064
+
1065
+ /**
1066
+ * Place a parametric **window** family into a host wall.
1067
+ *
1068
+ * Identified by a saved **family definition id**. Discover ids with
1069
+ * {@linkcode PluginDesignWindowsApi.listCatalog}. The world `position` is
1070
+ * projected onto the host wall's face to locate the opening; the wall is
1071
+ * re-cut and the window hosted in a single undo entry. Placement is
1072
+ * asynchronous. The sill parameter determines elevation; position Y is ignored.
1073
+ *
1074
+ * @param familyId - Hosted window definition ID; legacy mesh catalog IDs are not accepted
1075
+ * @param hostWall - The wall to host the window
1076
+ * @param position - World point projected onto the wall to locate the opening
1077
+ * @param options - `version`, `values`, `label`, and parameter-driven `hinge`, as for door
1078
+ * @param facing - World point selecting local +Z; omitted uses the picked face
1079
+ * @returns the {@linkcode ComponentHandle} of the placed window
1080
+ * @throws if the family/category/parameters are invalid, or placement or cutting fails
1081
+ *
1082
+ * @examplePrompt Add a window to this wall here
1083
+ * @examplePrompt Place a casement window on the selected wall
1084
+ * @examplePrompt Put a window from the catalog at this point on the wall
1085
+ * @examplePrompt Insert a window into the living room wall
1086
+ * @examplePrompt Add a window to the wall and name it Win-01
1087
+ * @examplePrompt Add a casement window opening toward the garden side
1088
+ *
1089
+ * # Example
1090
+ * ```ts
1091
+ * const [wall] = await snaptrude.design.query.listWalls({ isSelected: true })
1092
+ * const [entry] = await snaptrude.design.windows.listCatalog()
1093
+ * const window = await snaptrude.design.create.window(
1094
+ * entry.id,
1095
+ * wall,
1096
+ * await snaptrude.core.math.vec3.new(3, 0, 5),
1097
+ * { label: "Win-01" },
1098
+ * )
1099
+ * ```
1100
+ *
1101
+ * @performance For MORE THAN ONE window, call `design.create.windows(items[])` — one
1102
+ * host round-trip and ONE undo entry, with each host wall re-cut once per opening
1103
+ * inside that single call. Looping this single-window creator is N round-trips.
1104
+ */
1105
+ public abstract window(
1106
+ familyId: string,
1107
+ hostWall: ComponentHandle,
1108
+ position: Vec3Handle,
1109
+ options?: {
1110
+ label?: string
1111
+ hinge?: Vec3Handle
1112
+ version?: number
1113
+ values?: Record<string, number | string>
1114
+ },
1115
+ facing?: Vec3Handle,
1116
+ ): PluginApiReturn<ComponentHandle>
1117
+
1118
+ /**
1119
+ * Place **many windows** into host walls in one undoable operation (bulk
1120
+ * plural of {@linkcode window}). Each item carries its own family id, host
1121
+ * wall, world position, and optional `facing`, `label`, `width`, `height` and
1122
+ * `sillHeight` — the same fields as a `"window"`
1123
+ * {@linkcode PluginDesignCreateOpeningOptions}. `sillHeight` is measured from
1124
+ * the host wall base to the BOTTOM of the window. Validate-all-or-throw; one
1125
+ * command.
1126
+ *
1127
+ * Hosts keep their identity. Width, height and sillHeight set the declared
1128
+ * width, height and sill parameters before the first cut. No mesh scaling occurs.
1129
+ *
1130
+ * @param items - One {@linkcode PluginCreateWindowItem} per window to place
1131
+ * (≥1, ≤1000)
1132
+ * @returns the placed windows as {@linkcode ComponentHandle}`[]`, in input order
1133
+ * @throws `VALIDATION` if the items fail the schema (empty array, more than
1134
+ * 1000 items, unknown fields, a non-positive `width`/`height`, a negative
1135
+ * `sillHeight`); `HANDLE_INVALID` if a `position`, `facing` or `hinge`
1136
+ * handle is unknown or released; `PRECONDITION_FAILED` if the family cannot be
1137
+ * resolved or saved, or a host is locked / is not in the active proposal;
1138
+ * `OPERATION_FAILED` if placement, containment, overlap checks or cutting fail
1139
+ * (nothing is created — `details.itemIndex` is the failing item);
1140
+ * `METHOD_NOT_PERMITTED` if the plugin may not write.
1141
+ *
1142
+ * @examplePrompt Add windows along this whole facade at once
1143
+ * @examplePrompt Place a window in every bedroom in one operation
1144
+ * @examplePrompt Bulk add the windows from this schedule
1145
+ * @examplePrompt Put four windows on this wall in a single undo step
1146
+ * @examplePrompt Add all the windows with a 0.9m sill height together
1147
+ *
1148
+ * @performance Bulk creator — the whole batch is ONE host round-trip and ONE undo
1149
+ * entry. Always prefer this over
1150
+ * calling `design.create.window` in a loop: build the full `items[]` array first
1151
+ * (all host walls and points up front), then make one call.
1152
+ *
1153
+ * # Example
1154
+ * ```ts
1155
+ * const v = snaptrude.core.math.vec3
1156
+ * const [wall] = await snaptrude.design.query.listWalls({ isSelected: true })
1157
+ * const [casement] = await snaptrude.design.windows.listCatalog()
1158
+ * const placed = await snaptrude.design.create.windows([
1159
+ * { familyId: casement.id, hostWall: wall, position: await v.new(2, 0, 5), values: { sill: "900mm" } },
1160
+ * { familyId: casement.id, hostWall: wall, position: await v.new(5, 0, 5), values: { sill: "900mm" } },
1161
+ * ])
1162
+ * const currentWall = await snaptrude.design.query.getHost(placed[0])
1163
+ * ```
1164
+ */
1165
+ public abstract windows(
1166
+ items: PluginCreateWindowItem[],
1167
+ ): PluginApiReturn<ComponentHandle[]>
1168
+
1169
+ /**
1170
+ * Cut a **void** — a hole, opening or shaft — through an existing FLAT slab,
1171
+ * floor, roof or ceiling. The void is a selectable child object of its host
1172
+ * (the "Void" of the Draw Void tool). It always cuts the full host thickness.
1173
+ * It can be moved with `design.transform.move` (a move that would leave the
1174
+ * host is shortened without a report) and removed with
1175
+ * `design.delete.entities`; to resize, delete it and cut a new one
1176
+ * (`design.update.space` refuses a void).
1177
+ *
1178
+ * Give the outline **in plan** (all points at one height): its height is
1179
+ * ignored, and it is dropped straight onto the host's top face, so one outline
1180
+ * works on every storey. Keep it at least 25.4 mm (0.1 SU) inside the host
1181
+ * edge and away from other voids; merge overlapping holes into one outline.
1182
+ * Straight segments and arcs only (no splines).
1183
+ *
1184
+ * **A room has a floor finish AND a structural slab that overlap in plan; a
1185
+ * void in one does not cut the other, so a through-opening needs one void in
1186
+ * each.**
1187
+ *
1188
+ * `getEntityType` reports a void as `"mass"`, and `listMasses` /
1189
+ * `listByType("mass")` include voids: a mass whose `design.query.getHost` is a
1190
+ * slab, floor, roof or ceiling is a void. After its host is copied, a void can
1191
+ * report `null` and be missing from those lists; `listChildren(host)` still
1192
+ * finds it.
1193
+ *
1194
+ * Call it as `snaptrude.design.create.void(...)`; `void` is a reserved word and
1195
+ * cannot be destructured. Voids in walls, on sloped, generated or multi-plane
1196
+ * roofs, and on instanced hosts are not available yet; for a door or window
1197
+ * use `design.create.door` / `window`. To make a NEW slab that already has a
1198
+ * fixed hole, pass a contour with holes to `design.create.slab`; that hole is
1199
+ * part of the footprint, not an object. For a contour's outer loop, pass
1200
+ * `core.geom.query.contour.getOuterProfile(contour)`.
1201
+ *
1202
+ * # Example
1203
+ * ```ts
1204
+ * const m = 1000 / 254 // metres → Snaptrude units (SU)
1205
+ * const [slab] = await snaptrude.design.query.listByType("slab", { isSelected: true })
1206
+ * const center = await snaptrude.core.math.vec3.new(10 * m, 0, 5 * m) // plan point; y is ignored
1207
+ * const outline = await snaptrude.core.geom.create.profileRect(2 * m, 2 * m, center) // 2 m x 2 m
1208
+ * const hole = await snaptrude.design.create.void(slab, outline)
1209
+ * ```
1210
+ *
1211
+ * A round hole is two arcs (a circle handle is not a profile):
1212
+ * ```ts
1213
+ * const m = 1000 / 254
1214
+ * const { vec3 } = snaptrude.core.math
1215
+ * const g = snaptrude.core.geom.create
1216
+ * const r = 0.5 * m // 1 m diameter
1217
+ * const centre = await vec3.new(10 * m, 0, 5 * m)
1218
+ * const axis = await vec3.new(0, -1, 0)
1219
+ * const east = await vec3.new(10 * m + r, 0, 5 * m)
1220
+ * const west = await vec3.new(10 * m - r, 0, 5 * m)
1221
+ * const round = await g.profileFromCurves([await g.arc(east, west, centre, axis), await g.arc(west, east, centre, axis)])
1222
+ * await snaptrude.design.create.void(slab, round) // never destructure `void`
1223
+ * ```
1224
+ *
1225
+ * @param host - The slab, floor, roof or ceiling to cut through
1226
+ * @param outline - Closed, flat plan outline (world SU); its height is ignored
1227
+ * @returns the {@linkcode ComponentHandle} of the new void
1228
+ * @throws `VALIDATION` if the outline is open, crosses itself, is not flat or
1229
+ * has a spline, or the arguments are the wrong handle kinds or swapped;
1230
+ * `HANDLE_INVALID` if a handle is unknown or released or the host was
1231
+ * deleted; `HANDLE_STALE` if the host was rebuilt (re-query it);
1232
+ * `PRECONDITION_FAILED` if the
1233
+ * host is a wall or not a slab/floor/roof/ceiling, is sloped, instanced, in
1234
+ * another building, locked, outside the active proposal, a generated roof or
1235
+ * not a single plate, or there is no active structure (`engineCode`
1236
+ * `NO_ACTIVE_STRUCTURE`); `OPERATION_FAILED` with `engineCode`
1237
+ * `VOID_DOES_NOT_FIT` when the outline crosses the host edge, overlaps a void
1238
+ * or hole, is closer than 25.4 mm to either, or the fit could not be checked,
1239
+ * or another `engineCode` when the cut failed (nothing is created, except
1240
+ * `PUBLISH_FAILED` with a hint that the voids are in the scene: undo, then
1241
+ * retry); `METHOD_NOT_PERMITTED` if the plugin may not write.
1242
+ *
1243
+ * @examplePrompt Cut a hole in this slab
1244
+ * @examplePrompt Add a 1.2m by 2.4m stair opening in the first-floor slab
1245
+ * @examplePrompt Make a 600 x 600 service penetration in the floor for the riser
1246
+ * @examplePrompt Put a skylight opening through the roof
1247
+ * @examplePrompt Cut a round 1m duct hole in the slab
1248
+ * @examplePrompt Make a cut-out in the ceiling for the light well
1249
+ * @examplePrompt Add a double-height atrium void in the level 2 floor
1250
+ *
1251
+ * @performance Single-void creator. For MORE THAN ONE void, call
1252
+ * `design.create.voids(items[])`: one host round-trip, ONE undo entry, one cut
1253
+ * per host.
1254
+ */
1255
+ public abstract void(
1256
+ host: ComponentHandle,
1257
+ outline: ProfileHandle,
1258
+ ): PluginApiReturn<ComponentHandle>
1259
+
1260
+ /**
1261
+ * Cut **many voids** (holes, openings, shafts) through slabs, floors, roofs
1262
+ * and ceilings in one undoable operation (bulk plural of {@linkcode void}).
1263
+ * Items may share a host or use different hosts — one shaft outline on every
1264
+ * storey's slab AND floor is one call. Validate-all-or-throw: one item whose
1265
+ * host does not contain the outline rejects the whole batch, so pass only the
1266
+ * hosts that contain it; `details.itemIndex` names the failing item (for two
1267
+ * overlapping items of the batch, the earlier one). At most 50 voids per host
1268
+ * and 200 in all per call. Same outline rules as {@linkcode void}: plan outline, 25.4 mm
1269
+ * (0.1 SU) clearance, floor AND slab are separate hosts. Call it as
1270
+ * `snaptrude.design.create.voids(...)`.
1271
+ *
1272
+ * # Example
1273
+ * ```ts
1274
+ * const m = 1000 / 254
1275
+ * // The user selects the slabs AND floors the shaft passes through; every one must contain the outline.
1276
+ * const hosts = [
1277
+ * ...(await snaptrude.design.query.listByType("slab", { isSelected: true })),
1278
+ * ...(await snaptrude.design.query.listByType("floor", { isSelected: true })),
1279
+ * ]
1280
+ * const centre = await snaptrude.core.math.vec3.new(12 * m, 0, -6 * m)
1281
+ * const shaft = await snaptrude.core.geom.create.profileRect(2 * m, 2 * m, centre)
1282
+ * const holes = await snaptrude.design.create.voids(hosts.map((host) => ({ host, outline: shaft })))
1283
+ * ```
1284
+ *
1285
+ * @param items - One {@linkcode PluginCreateVoidItem} per void (≥1, ≤200; ≤50 per host)
1286
+ * @returns the new voids as {@linkcode ComponentHandle}`[]`, in input order
1287
+ * @throws `VALIDATION` if the items fail the schema (empty array, more than
1288
+ * 1000 items, unknown fields), an outline is invalid (as for
1289
+ * {@linkcode void}), more than 50 items share one host, or there are more
1290
+ * than 200 items; plus every code {@linkcode void} throws, each with
1291
+ * `details.itemIndex`. Nothing is created on any failure, except
1292
+ * `PUBLISH_FAILED` (as for {@linkcode void}).
1293
+ *
1294
+ * @examplePrompt Cut a lift shaft through every floor slab of the core
1295
+ * @examplePrompt Cut a 2m x 2m shaft through all the slabs at this location
1296
+ * @examplePrompt Make stair openings in all the floors above the staircases
1297
+ * @examplePrompt Make holes in the roof for these three skylights
1298
+ * @examplePrompt Cut openings in each storey's slab matching this outline
1299
+ * @examplePrompt Punch the same riser penetration through every floor at once
1300
+ *
1301
+ * @performance ONE host round-trip, ONE undo entry, ONE cut per host. Never call
1302
+ * `void` in a loop. Runs synchronously on the main thread: 50 voids on one
1303
+ * host take about 1 s, and the time grows with the number of hosts (caps:
1304
+ * 50 per host, 200 per call).
1305
+ */
1306
+ public abstract voids(
1307
+ items: PluginCreateVoidItem[],
1308
+ ): PluginApiReturn<ComponentHandle[]>
1309
+
1310
+ /**
1311
+ * Place a parametric **door or window** into a host wall with parameter overrides.
1312
+ *
1313
+ * The world `position` is projected onto the host wall to locate the opening.
1314
+ * All supplied dimensions use Snaptrude engine units. Window `sillHeight` is
1315
+ * measured from the host wall base to the bottom of the window, not to
1316
+ * its center. Placement is asynchronous and committed as one undoable creation.
1317
+ *
1318
+ * Wall only. For a hole in a slab, floor or roof use `design.create.void`.
1319
+ *
1320
+ * @param options - Opening kind, family and host references, placement, and
1321
+ * optional facing (swing side), hinge (hinged jamb), label, and dimensions
1322
+ * @returns the {@linkcode ComponentHandle} of the placed door or window
1323
+ * @throws if the options are invalid, the family id is unknown, the host is
1324
+ * not a wall, loading fails, or the opening cannot be placed on the host
1325
+ *
1326
+ * @examplePrompt Add a 1m wide door to this wall here
1327
+ * @examplePrompt Place a window with a 0.9m sill height on the selected wall
1328
+ *
1329
+ * @performance Single-opening creator — use it for one hosted opening. For MORE
1330
+ * THAN ONE, call `design.create.doors(items[])` / `design.create.windows(items[])`:
1331
+ * the same per-item fields, one host round-trip, ONE undo entry.
1332
+ *
1333
+ * # Example
1334
+ * ```ts
1335
+ * const opening = await snaptrude.design.create.opening({
1336
+ * kind: "window",
1337
+ * familyId: windowType.id,
1338
+ * hostWall: wall,
1339
+ * position: await snaptrude.core.math.vec3.new(3, 0, 5),
1340
+ * width: 1.2,
1341
+ * height: 1.5,
1342
+ * sillHeight: 0.9,
1343
+ * })
1344
+ * ```
1345
+ */
1346
+ public abstract opening(
1347
+ options: PluginDesignCreateOpeningOptions,
1348
+ ): PluginApiReturn<ComponentHandle>
1349
+
1350
+ /**
1351
+ * Place a **smart layout** — clone an in-scene template cluster (walls,
1352
+ * furniture, doors, windows, floors) into one or more target ROOM/DEPARTMENT
1353
+ * spaces, adapting orientation and fit to each target.
1354
+ *
1355
+ * Supply the template as **exactly one** of `templateGroup` (a group **id**
1356
+ * from `snaptrude.core.groups.list` — groups are string-id entities, not
1357
+ * component handles) or
1358
+ * `templateComponents` (an explicit component set); every target in `targets`
1359
+ * (≥1) receives its own placement. By default the target spaces are hidden
1360
+ * after placement (`hideTargets`, mirroring the product) — pass `false` to keep
1361
+ * them visible.
1362
+ *
1363
+ * The whole operation — every wall/opening/furniture/floor across every target,
1364
+ * plus the target-hide — is committed as a **single undo step**. Because all
1365
+ * targets merge into one creation batch, the created entities are returned
1366
+ * **flat, grouped by kind** ({@linkcode PluginSmartLayoutResult}), NOT attributed
1367
+ * per target (per-target attribution would break the single-undo guarantee).
1368
+ * `skippedWalls` is the interior-optimizer's best-candidate count of walls it
1369
+ * could not place. Synchronous and heavy — many targets can jank the main thread.
1370
+ *
1371
+ * @param options - {@linkcode PluginSmartLayoutArgs}: template source, targets, and `hideTargets`
1372
+ * @returns a {@linkcode PluginSmartLayoutResult} — created entities grouped by kind + `skippedWalls`
1373
+ * @throws if smart layout is not enabled, the template group is invalid, there
1374
+ * is no template / no targets / no valid mappings, or component creation fails
1375
+ *
1376
+ * @examplePrompt Copy this room's layout into the selected rooms
1377
+ * @examplePrompt Apply this furniture arrangement to the other offices
1378
+ * @examplePrompt Replicate this template into these two spaces
1379
+ * @examplePrompt Place this desk cluster into every meeting room
1380
+ * @examplePrompt Use this room as a template for the rooms I selected but keep them visible
1381
+ *
1382
+ * # Example
1383
+ * ```ts
1384
+ * const [templateRoom, ...targets] = await snaptrude.design.query.listSpaces({ isSelected: true })
1385
+ * const result = await snaptrude.design.create.smartLayout({
1386
+ * templateComponents: [templateRoom],
1387
+ * targets,
1388
+ * })
1389
+ * console.log("placed walls:", result.created.walls.length, "skipped:", result.skippedWalls)
1390
+ *
1391
+ * // …or clone a saved group by its id (from core.groups.list)
1392
+ * const { groups } = await snaptrude.core.groups.list()
1393
+ * await snaptrude.design.create.smartLayout({ templateGroup: groups[0].id, targets })
1394
+ * ```
1395
+ */
1396
+ public abstract smartLayout(
1397
+ options: PluginSmartLayoutArgs,
1398
+ ): PluginApiReturn<PluginSmartLayoutResult>
1399
+
1400
+ /**
1401
+ * **Copy** existing entities, offsetting each copy by `displacement * i` (i = 1…count),
1402
+ * source positions preserved. Migrated from the removed `tools.copy`. In `"instance"`
1403
+ * mode copies stay in the source's instance family where possible; `"unique"` makes
1404
+ * independent geometry. The created copies become the active selection. Undoable.
1405
+ *
1406
+ * Runs on the engine's array-copy tool, so its semantics carry over: an
1407
+ * `"instance"` copy of a source that is not yet an instance **replaces that
1408
+ * source** with a fresh instance component — the handle you passed in stops
1409
+ * resolving after the call. Re-query (e.g. `design.query.listSpaces`) to pick
1410
+ * up the replacement before operating on that entity again; copying the stale
1411
+ * handle throws. `"unique"` mode never replaces sources.
1412
+ *
1413
+ * @param components - Entities to copy (≥1)
1414
+ * @param displacement - Per-copy offset as a {@linkcode Vec3Handle} (copy `i` at `displacement * i`)
1415
+ * @param options - `count` (copies per component, default 1), `mode` (`"instance"` | `"unique"`, default `"instance"`)
1416
+ * @returns the created copies as {@linkcode ComponentHandle}`[]`
1417
+ * @throws if a component handle is unknown or stale (replaced by an earlier
1418
+ * `"instance"` copy), `count` is not a positive integer, or the engine fails
1419
+ *
1420
+ * @examplePrompt Copy this room 3 times spaced 6m apart
1421
+ * @examplePrompt Duplicate the selected furniture along the X axis
1422
+ * @examplePrompt Make 5 copies of this column at 4 metre intervals
1423
+ * @examplePrompt Array these walls with a 3m offset
1424
+ * @examplePrompt Clone the desk as a unique copy, not an instance
1425
+ *
1426
+ * @performance To place N repeats of the same geometry, bulk-create the seed(s) once then
1427
+ * call this ONCE with `{ count: N - 1 }` — one host round-trip for all copies. Never
1428
+ * reproduce geometry by looping `design.create.*` N times. Copy only after the create
1429
+ * that mints the seed ids.
1430
+ *
1431
+ * # Example
1432
+ * ```ts
1433
+ * const { vec3 } = snaptrude.core.math
1434
+ * const copies = await snaptrude.design.create.copy(["space-id"], vec3.new(6, 0, 0), { count: 3 })
1435
+ * ```
1436
+ */
1437
+ public abstract copy(
1438
+ components: ComponentHandle[],
1439
+ displacement: Vec3Handle,
1440
+ options?: { count?: number; mode?: PluginCopyMode },
1441
+ ): PluginApiReturn<ComponentHandle[]>
1442
+
1443
+ /**
1444
+ * Create **many spaces** in one undoable operation (bulk plural of {@linkcode space}).
1445
+ * Migrated from the removed `entity.space.bulkCreate`. Each item extrudes a contour
1446
+ * footprint up `height`. Validate-all-or-throw; one command. For a rectangular
1447
+ * (box) space, build the footprint with `core.geom.create.profileRect` →
1448
+ * `contourFromProfile`, then pass it as an item. Each item may carry its own
1449
+ * `storey` (see {@linkcode PluginCreateSpaceItem}) so a single call can populate
1450
+ * several floors at once.
1451
+ *
1452
+ * @param items - One {@linkcode PluginCreateSpaceItem} per space to create (≥1)
1453
+ * @returns the created spaces as {@linkcode ComponentHandle}`[]`, in input order
1454
+ * @throws if any item's contour is invalid, its height is not positive, or its
1455
+ * target `storey` does not exist (all-or-nothing — nothing is created)
1456
+ *
1457
+ * @examplePrompt Create several rooms at once from these footprints
1458
+ * @examplePrompt Generate all the bedrooms in one operation
1459
+ * @examplePrompt Bulk create the spaces for this floor plan
1460
+ * @examplePrompt Add three rooms with different heights in a single undo step
1461
+ * @examplePrompt Create one room on the ground floor and one on the first floor
1462
+ *
1463
+ * @performance Bulk creator — the whole batch is ONE host round-trip. Always prefer this
1464
+ * over calling `design.create.space` in a loop: build the full `items[]` array first
1465
+ * (all per-item math and geometry up front), then make one call.
1466
+ *
1467
+ * # Example
1468
+ * ```ts
1469
+ * const rect = await snaptrude.core.geom.create.profileRect(4, 3)
1470
+ * const contour = await snaptrude.core.geom.create.contourFromProfile(rect)
1471
+ * const [a, b] = await snaptrude.design.create.spaces([
1472
+ * { contour, height: 3, label: "R1" },
1473
+ * { contour, height: 4, label: "R2", position: await vec3.new(10, 0, 0) },
1474
+ * ])
1475
+ * ```
1476
+ */
1477
+ public abstract spaces(
1478
+ items: PluginCreateSpaceItem[],
1479
+ ): PluginApiReturn<ComponentHandle[]>
1480
+
1481
+ /**
1482
+ * Create a new parametric buildable envelope — the zoning-regulated volume a
1483
+ * building may occupy — generated from a site polygon, tiered setbacks, a
1484
+ * height or floor-count cap, and optional FAR and lot-coverage limits. The host
1485
+ * mints the envelope id and returns it. Canonical home of the create formerly
1486
+ * at `entity.buildableEnvelope.create` (now deprecated).
1487
+ *
1488
+ * @param sitePolygon - Site polygon vertices in `lengthUnit`; minimum 3 vertices.
1489
+ * @param lengthUnit - Unit used by all length fields (`"ft" | "m"`).
1490
+ * @param setbacks - Setback profile, ground tier first; minimum 1 tier.
1491
+ * @param verticalCap - Maximum height or floor count.
1492
+ * @param floorToFloor - Required floor-to-floor height in `lengthUnit`.
1493
+ * @param farRatio - Optional FAR value, positive when provided.
1494
+ * @param lotCoverageMaxPct - Optional lot coverage cap, `0..100`.
1495
+ * @returns A {@linkcode PluginBuildableEnvelopeCreateResult} with the
1496
+ * `buildableEnvelopeId` of the created envelope.
1497
+ * @throws If validation fails or generation produces no renderable geometry.
1498
+ *
1499
+ * @examplePrompt Create a buildable envelope for my site
1500
+ * @examplePrompt Generate the zoning envelope with 10ft front setbacks
1501
+ * @examplePrompt Show the maximum buildable volume with a 150ft height limit
1502
+ * @examplePrompt Create a zoning envelope capped at 12 floors with FAR 3
1503
+ *
1504
+ * # Example
1505
+ * ```ts
1506
+ * const { buildableEnvelopeId } = await snaptrude.design.create.buildableEnvelope(
1507
+ * [
1508
+ * { x: 0, z: 0 },
1509
+ * { x: 100, z: 0 },
1510
+ * { x: 100, z: 80 },
1511
+ * { x: 0, z: 80 },
1512
+ * ],
1513
+ * "ft",
1514
+ * [
1515
+ * { aboveHeight: 0, front: 10, side: 5, rear: 10 },
1516
+ * { aboveHeight: 100, front: 20, side: 10, rear: 20 },
1517
+ * ],
1518
+ * { kind: "max_height", maxHeight: 150 },
1519
+ * 12,
1520
+ * )
1521
+ * ```
1522
+ */
1523
+ public abstract buildableEnvelope(
1524
+ sitePolygon: PluginBuildableEnvelopePolygonVertex[],
1525
+ lengthUnit: "ft" | "m",
1526
+ setbacks: PluginBuildableEnvelopeSetbackTier[],
1527
+ verticalCap: PluginBuildableEnvelopeVerticalCap,
1528
+ floorToFloor: number,
1529
+ farRatio?: number,
1530
+ lotCoverageMaxPct?: number,
1531
+ ): PluginApiReturn<PluginBuildableEnvelopeCreateResult>
1532
+
1533
+ /**
1534
+ * Create a default buildable envelope for an existing site — the same as the
1535
+ * right panel's "Create Buildable Envelope" button. Front, side and rear
1536
+ * distances are seeded from Site Analysis or the Program sheet, with a 10 ft
1537
+ * fallback, and the envelope is linked to the site. Call it only after the
1538
+ * user agrees (for example when `design.update.siteEdge` throws
1539
+ * `NO_BUILDABLE_ENVELOPE`).
1540
+ *
1541
+ * @param siteId - Site component id, from `program.site.listEdges()`.
1542
+ * @returns A {@linkcode PluginDesignCreateBuildableEnvelopeFromSiteResult}
1543
+ * with the `buildableEnvelopeId` of the created envelope.
1544
+ * @throws `HANDLE_INVALID` for an unknown `siteId`. `PRECONDITION_FAILED`
1545
+ * (`details.reason: "SITE_HAS_ENVELOPE"`) when the site already has an
1546
+ * envelope.
1547
+ *
1548
+ * @examplePrompt Create a buildable envelope for this site with default setbacks
1549
+ * @examplePrompt Add the zoning envelope to my site
1550
+ *
1551
+ * # Example
1552
+ * ```ts
1553
+ * const [{ siteId }] = (await snaptrude.program.site.listEdges()).sites
1554
+ * const { buildableEnvelopeId } =
1555
+ * await snaptrude.design.create.buildableEnvelopeFromSite(siteId)
1556
+ * ```
1557
+ */
1558
+ public abstract buildableEnvelopeFromSite(
1559
+ siteId: string,
1560
+ ): PluginApiReturn<PluginDesignCreateBuildableEnvelopeFromSiteResult>
1561
+ }
1562
+
1563
+ // ---------------------------------------------------------------------------
1564
+ // Enums
1565
+ // ---------------------------------------------------------------------------
1566
+
1567
+ /**
1568
+ * Line style of a reference line (mirrors the engine `ReferenceLineMode` verbatim).
1569
+ *
1570
+ * | Value | Description |
1571
+ * |---|---|
1572
+ * | `"SOLID"` | Solid line (default) |
1573
+ * | `"DASHED"` | Dashed line |
1574
+ * | `"DOT_DASH"` | Dot-dash line |
1575
+ */
1576
+ export const PluginReferenceLineStyle = z.enum(["SOLID", "DASHED", "DOT_DASH"])
1577
+ export type PluginReferenceLineStyle = z.infer<typeof PluginReferenceLineStyle>
1578
+
1579
+ /**
1580
+ * Slab type (mirrors the engine `SLAB_TYPES` values verbatim).
1581
+ *
1582
+ * | Value | Description |
1583
+ * |---|---|
1584
+ * | `"Intermediate Slab"` | Intermediate floor slab (default) |
1585
+ * | `"Basement Slab"` | Basement slab |
1586
+ * | `"Plinth"` | Plinth slab |
1587
+ * | `"Roof"` | Roof slab |
1588
+ */
1589
+ export const PluginSlabType = z.enum([
1590
+ "Intermediate Slab",
1591
+ "Basement Slab",
1592
+ "Plinth",
1593
+ "Roof",
1594
+ ])
1595
+ export type PluginSlabType = z.infer<typeof PluginSlabType>
1596
+
1597
+ /**
1598
+ * A staircase preset — a base type or a named parametric preset. Mirrors the
1599
+ * engine `STAIRCASE_PRESETS` / `StaircaseType`. Use the value that matches the
1600
+ * shape you want (e.g. `"straight"`, `"dogLegged"`, `"lShaped"`).
1601
+ */
1602
+ export const PluginStaircasePreset = z.enum([
1603
+ "straight",
1604
+ "straightWoLanding",
1605
+ "straightFlightWoMidLanding",
1606
+ "dogLegged",
1607
+ "openWell",
1608
+ "lShaped",
1609
+ "3StepLShaped",
1610
+ "square",
1611
+ "singleStep",
1612
+ "plinthLevel",
1613
+ "amphitheatre",
1614
+ "connectingStepsToAmphitheatre",
1615
+ "mezzanineFloorStraight",
1616
+ "mezzanineFloorDogLegged",
1617
+ "mezzanineFloorLShaped",
1618
+ "custom",
1619
+ ])
1620
+ export type PluginStaircasePreset = z.infer<typeof PluginStaircasePreset>
1621
+
1622
+ /**
1623
+ * Optional staircase dimension overrides. Lengths use Snaptrude engine units
1624
+ * and must be positive. `angleInDegrees` controls placement about the vertical
1625
+ * axis and must be a multiple of 90 degrees.
1626
+ */
1627
+ export const PluginStaircaseDimensions = z
1628
+ .object({
1629
+ width: z.number().finite().positive().optional(),
1630
+ tread: z.number().finite().positive().optional(),
1631
+ riser: z.number().finite().positive().optional(),
1632
+ landingWidth: z.number().finite().positive().optional(),
1633
+ angleInDegrees: z.number().finite().multipleOf(90).optional(),
1634
+ })
1635
+ .strict()
1636
+ export type PluginStaircaseDimensions = z.infer<
1637
+ typeof PluginStaircaseDimensions
1638
+ >
1639
+
1640
+ // ---------------------------------------------------------------------------
1641
+ // space
1642
+ // ---------------------------------------------------------------------------
1643
+
1644
+ /**
1645
+ * Arguments for {@linkcode PluginDesignCreateApi.space}.
1646
+ *
1647
+ * | Property | Type | Description |
1648
+ * |---|---|---|
1649
+ * | `contour` | {@linkcode ContourHandle} | Bottom footprint (outer + holes) |
1650
+ * | `height` | `number` | Extrusion height (Snaptrude units, > 0) |
1651
+ * | `label` | `string`? | Room label (maps to the space's `room_type`) |
1652
+ * | `position` | {@linkcode Vec3Handle}? | Offset from origin (default origin) |
1653
+ * | `spaceType` | {@linkcode PluginSpaceType}? | Space-type classification (default `Room`) |
1654
+ * | `massType` | {@linkcode PluginMassType}? | Mass-type classification (default `Room`) |
1655
+ * | `departmentId` | {@linkcode PluginDepartmentId}? | Department assignment (default department) |
1656
+ * | `storey` | `number`? (int) | Target storey number (default active storey) |
1657
+ */
1658
+ export const PluginDesignCreateSpaceArgs = z.object({
1659
+ contour: ContourHandle,
1660
+ height: z.number(),
1661
+ label: z.string().optional(),
1662
+ position: Vec3Handle.optional(),
1663
+ spaceType: PluginSpaceType.optional(),
1664
+ massType: PluginMassType.optional(),
1665
+ departmentId: PluginDepartmentId.optional(),
1666
+ storey: z.number().int().optional(),
1667
+ })
1668
+ export type PluginDesignCreateSpaceArgs = z.infer<
1669
+ typeof PluginDesignCreateSpaceArgs
1670
+ >
1671
+ // TRANSPORT: positional signature shipped — storey is the trailing 8th arg:
1672
+ // space(contour, height, label?, position?, spaceType?, massType?, departmentId?, storey?)
1673
+
1674
+ // ---------------------------------------------------------------------------
1675
+ // mass
1676
+ // ---------------------------------------------------------------------------
1677
+
1678
+ /**
1679
+ * Arguments for {@linkcode PluginDesignCreateApi.mass}.
1680
+ *
1681
+ * | Property | Type | Description |
1682
+ * |---|---|---|
1683
+ * | `contour` | {@linkcode ContourHandle} | Footprint (outer + holes) |
1684
+ * | `height` | `number` | Extrusion height (> 0) |
1685
+ * | `label` | `string`? | Label (maps to `room_type`) |
1686
+ * | `position` | {@linkcode Vec3Handle}? | Offset from origin |
1687
+ * | `massType` | {@linkcode PluginMassType}? | Mass type (default `Generic mass`) |
1688
+ */
1689
+ export const PluginDesignCreateMassArgs = z.object({
1690
+ contour: ContourHandle,
1691
+ height: z.number(),
1692
+ label: z.string().optional(),
1693
+ position: Vec3Handle.optional(),
1694
+ massType: PluginMassType.optional(),
1695
+ })
1696
+ export type PluginDesignCreateMassArgs = z.infer<
1697
+ typeof PluginDesignCreateMassArgs
1698
+ >
1699
+
1700
+ // ---------------------------------------------------------------------------
1701
+ // massFromBrep
1702
+ // ---------------------------------------------------------------------------
1703
+
1704
+ /**
1705
+ * Arguments for {@linkcode PluginDesignCreateApi.massFromBrep}.
1706
+ *
1707
+ * | Property | Type | Description |
1708
+ * |---|---|---|
1709
+ * | `brep` | {@linkcode BrepHandle} | A solid from a `core.geom.create` brep constructor |
1710
+ * | `label` | `string`? | Label (maps to `room_type`) |
1711
+ */
1712
+ export const PluginDesignCreateMassFromBrepArgs = z.object({
1713
+ brep: BrepHandle,
1714
+ label: z.string().optional(),
1715
+ })
1716
+ export type PluginDesignCreateMassFromBrepArgs = z.infer<
1717
+ typeof PluginDesignCreateMassFromBrepArgs
1718
+ >
1719
+
1720
+ // ---------------------------------------------------------------------------
1721
+ // slab
1722
+ // ---------------------------------------------------------------------------
1723
+
1724
+ /**
1725
+ * Arguments for {@linkcode PluginDesignCreateApi.slab}.
1726
+ *
1727
+ * | Property | Type | Description |
1728
+ * |---|---|---|
1729
+ * | `contour` | {@linkcode ContourHandle} | Footprint (outer + holes) |
1730
+ * | `thickness` | `number` | Slab thickness (> 0) |
1731
+ * | `direction` | `"up" \| "down"`? | Extrusion direction (default `"down"`) |
1732
+ * | `slabType` | {@linkcode PluginSlabType}? | Slab-type classification |
1733
+ */
1734
+ export const PluginDesignCreateSlabArgs = z.object({
1735
+ contour: ContourHandle,
1736
+ thickness: z.number(),
1737
+ direction: z.enum(["up", "down"]).optional(),
1738
+ slabType: PluginSlabType.optional(),
1739
+ })
1740
+ export type PluginDesignCreateSlabArgs = z.infer<
1741
+ typeof PluginDesignCreateSlabArgs
1742
+ >
1743
+
1744
+ // ---------------------------------------------------------------------------
1745
+ // floor
1746
+ // ---------------------------------------------------------------------------
1747
+
1748
+ /**
1749
+ * Arguments for {@linkcode PluginDesignCreateApi.floor}.
1750
+ *
1751
+ * | Property | Type | Description |
1752
+ * |---|---|---|
1753
+ * | `contour` | {@linkcode ContourHandle} | Footprint (outer + holes) |
1754
+ * | `thickness` | `number` | Floor thickness (> 0) |
1755
+ * | `position` | {@linkcode Vec3Handle}? | Offset from origin |
1756
+ */
1757
+ export const PluginDesignCreateFloorArgs = z.object({
1758
+ contour: ContourHandle,
1759
+ thickness: z.number(),
1760
+ position: Vec3Handle.optional(),
1761
+ })
1762
+ export type PluginDesignCreateFloorArgs = z.infer<
1763
+ typeof PluginDesignCreateFloorArgs
1764
+ >
1765
+
1766
+ // ---------------------------------------------------------------------------
1767
+ // roof
1768
+ // ---------------------------------------------------------------------------
1769
+
1770
+ /**
1771
+ * Arguments for {@linkcode PluginDesignCreateApi.roof}.
1772
+ *
1773
+ * | Property | Type | Description |
1774
+ * |---|---|---|
1775
+ * | `contour` | {@linkcode ContourHandle} | Footprint (outer + holes) |
1776
+ * | `thickness` | `number` | Roof-slab thickness (> 0) |
1777
+ */
1778
+ export const PluginDesignCreateRoofArgs = z.object({
1779
+ contour: ContourHandle,
1780
+ thickness: z.number(),
1781
+ })
1782
+ export type PluginDesignCreateRoofArgs = z.infer<
1783
+ typeof PluginDesignCreateRoofArgs
1784
+ >
1785
+
1786
+ // ---------------------------------------------------------------------------
1787
+ // ceiling
1788
+ // ---------------------------------------------------------------------------
1789
+
1790
+ /**
1791
+ * Arguments for {@linkcode PluginDesignCreateApi.ceiling}.
1792
+ *
1793
+ * | Property | Type | Description |
1794
+ * |---|---|---|
1795
+ * | `contour` | {@linkcode ContourHandle} | Footprint (outer + holes) |
1796
+ * | `thickness` | `number` | Ceiling thickness (> 0) |
1797
+ * | `heightFromFloor` | `number`? | Lift above the floor (default 0) |
1798
+ */
1799
+ export const PluginDesignCreateCeilingArgs = z.object({
1800
+ contour: ContourHandle,
1801
+ thickness: z.number(),
1802
+ heightFromFloor: z.number().optional(),
1803
+ })
1804
+ export type PluginDesignCreateCeilingArgs = z.infer<
1805
+ typeof PluginDesignCreateCeilingArgs
1806
+ >
1807
+
1808
+ // ---------------------------------------------------------------------------
1809
+ // column
1810
+ // ---------------------------------------------------------------------------
1811
+
1812
+ /**
1813
+ * Arguments for {@linkcode PluginDesignCreateApi.column}.
1814
+ *
1815
+ * | Property | Type | Description |
1816
+ * |---|---|---|
1817
+ * | `position` | {@linkcode Vec3Handle} | Base point |
1818
+ * | `crossSection` | {@linkcode ContourHandle} | Column cross-section footprint |
1819
+ * | `height` | `number` | Column height (> 0) |
1820
+ * | `directionUp` | {@linkcode Vec3Handle}? | Up axis (default world up) |
1821
+ */
1822
+ export const PluginDesignCreateColumnArgs = z.object({
1823
+ position: Vec3Handle,
1824
+ crossSection: ContourHandle,
1825
+ height: z.number(),
1826
+ directionUp: Vec3Handle.optional(),
1827
+ })
1828
+ export type PluginDesignCreateColumnArgs = z.infer<
1829
+ typeof PluginDesignCreateColumnArgs
1830
+ >
1831
+
1832
+ // ---------------------------------------------------------------------------
1833
+ // beam
1834
+ // ---------------------------------------------------------------------------
1835
+
1836
+ /**
1837
+ * Arguments for {@linkcode PluginDesignCreateApi.beam}.
1838
+ *
1839
+ * | Property | Type | Description |
1840
+ * |---|---|---|
1841
+ * | `section` | {@linkcode ContourHandle} | Beam cross-section footprint |
1842
+ * | `length` | `number` | Beam length along `direction` (> 0) |
1843
+ * | `direction` | {@linkcode Vec3Handle}? | Extrusion axis (default world up) |
1844
+ */
1845
+ export const PluginDesignCreateBeamArgs = z.object({
1846
+ section: ContourHandle,
1847
+ length: z.number(),
1848
+ direction: Vec3Handle.optional(),
1849
+ })
1850
+ export type PluginDesignCreateBeamArgs = z.infer<
1851
+ typeof PluginDesignCreateBeamArgs
1852
+ >
1853
+
1854
+ // ---------------------------------------------------------------------------
1855
+ // walls
1856
+ // ---------------------------------------------------------------------------
1857
+
1858
+ /**
1859
+ * Arguments for {@linkcode PluginDesignCreateApi.walls}.
1860
+ *
1861
+ * | Property | Type | Description |
1862
+ * |---|---|---|
1863
+ * | `profile` | {@linkcode ProfileHandle} | Ordered curve chain (wall centerlines) |
1864
+ * | `height` | `number`? | Wall height (default engine default) |
1865
+ * | `thickness` | `number`? | Wall thickness (default: `wallType`'s total layer thickness when given, else engine default) |
1866
+ * | `wallType` | {@linkcode PluginWallType}? | Legacy name/id or exact native `{ id, version }` pin from `design.types.list("wall")` (default generic) |
1867
+ * | `storey` | `number`? | Target storey (integer, must exist) — walls assigned + lifted to its base in the same undo entry (default: geometry-derived) |
1868
+ * | `topProfiles` | ({@linkcode PluginWallTopProfile} \| `null`)[]? | Top edge per curve (same order); `null`/missing = flat at `height`. Straight curves only |
1869
+ */
1870
+ export const PluginDesignCreateWallsArgs = z.object({
1871
+ profile: ProfileHandle,
1872
+ height: z.number().optional(),
1873
+ thickness: z.number().optional(),
1874
+ wallType: PluginWallType.optional(),
1875
+ storey: z.number().int().optional(),
1876
+ topProfiles: z.array(PluginWallTopProfile.nullable()).optional(),
1877
+ values: PluginFamilyValues.optional(),
1878
+ })
1879
+ export type PluginDesignCreateWallsArgs = z.infer<
1880
+ typeof PluginDesignCreateWallsArgs
1881
+ >
1882
+ // TRANSPORT: positional signature shipped — storey is the 5th arg, topProfiles the trailing 6th:
1883
+ // walls(profile: ProfileHandle, height?: number, thickness?: number, wallType?: string, storey?: number, topProfiles?: (PluginWallTopProfile | null)[])
1884
+
1885
+ // ---------------------------------------------------------------------------
1886
+ // staircase
1887
+ // ---------------------------------------------------------------------------
1888
+
1889
+ /**
1890
+ * Arguments for {@linkcode PluginDesignCreateApi.staircase}.
1891
+ *
1892
+ * | Property | Type | Description |
1893
+ * |---|---|---|
1894
+ * | `preset` | {@linkcode PluginStaircasePreset} | The parametric preset to generate |
1895
+ * | `position` | {@linkcode Vec3Handle} | Placement point |
1896
+ * | `label` | `string`? | Optional label |
1897
+ * | `structureId` | `string`? | Target structure (default active) |
1898
+ * | `level` | `string`? | Target level name (default active/`"01"`) |
1899
+ * | `dimensions` | {@linkcode PluginStaircaseDimensions}? | Positive dimension overrides in engine units, applied before creation |
1900
+ */
1901
+ export const PluginDesignCreateStaircaseArgs = z.object({
1902
+ preset: PluginStaircasePreset,
1903
+ position: Vec3Handle,
1904
+ label: z.string().optional(),
1905
+ structureId: z.string().optional(),
1906
+ level: z.string().optional(),
1907
+ dimensions: PluginStaircaseDimensions.optional(),
1908
+ })
1909
+ export type PluginDesignCreateStaircaseArgs = z.infer<
1910
+ typeof PluginDesignCreateStaircaseArgs
1911
+ >
1912
+
1913
+ // ---------------------------------------------------------------------------
1914
+ // furniture
1915
+ // ---------------------------------------------------------------------------
1916
+
1917
+ /**
1918
+ * Arguments for {@linkcode PluginDesignCreateApi.furniture}.
1919
+ *
1920
+ * | Property | Type | Description |
1921
+ * |---|---|---|
1922
+ * | `catalogId` | `string` | Library id: team `_id` or general `fullName` |
1923
+ * | `position` | {@linkcode Vec3Handle} | Absolute world placement point |
1924
+ * | `label` | `string`? | Instance name (default auto `${name}Ins${n}`) |
1925
+ * | `createNewSourceMesh` | `boolean`? | Legacy, kept for compatibility; the host records a catalog source exactly once and this flag cannot skip it |
1926
+ * | `angleInDegrees` | `number`? | Rotation about the vertical axis, in degrees (default unrotated) |
1927
+ */
1928
+ export const PluginDesignCreateFurnitureArgs = z.object({
1929
+ catalogId: z.string().min(1),
1930
+ position: Vec3Handle,
1931
+ label: z.string().optional(),
1932
+ createNewSourceMesh: z.boolean().optional(),
1933
+ angleInDegrees: z.number().optional(),
1934
+ })
1935
+ export type PluginDesignCreateFurnitureArgs = z.infer<
1936
+ typeof PluginDesignCreateFurnitureArgs
1937
+ >
1938
+ // TRANSPORT: positional signature shipped — angleInDegrees is the trailing 4th arg:
1939
+ // furniture(catalogId: string, position: Vec3Handle, options?: { label?, createNewSourceMesh? }, angleInDegrees?: number)
1940
+
1941
+ // ---------------------------------------------------------------------------
1942
+ // door
1943
+ // ---------------------------------------------------------------------------
1944
+
1945
+ /**
1946
+ * Arguments for {@linkcode PluginDesignCreateApi.door}.
1947
+ *
1948
+ * | Property | Type | Description |
1949
+ * |---|---|---|
1950
+ * | `familyId` | `string` | Hosted door family definition ID |
1951
+ * | `version` | `number`? | Exact version; newest known by default |
1952
+ * | `values` | `Record<string, number \| string>`? | Parameters; numeric lengths use SU |
1953
+ * | `hostWall` | {@linkcode ComponentHandle} | The wall to host the door |
1954
+ * | `position` | {@linkcode Vec3Handle} | World point projected onto the wall |
1955
+ * | `label` | `string`? | Instance name (optional) |
1956
+ * | `facing` | {@linkcode Vec3Handle}? | World point on the side of the wall the door swings open to (default engine-chosen) |
1957
+ * | `hinge` | {@linkcode Vec3Handle}? | World point near the hinged jamb; requires hand control |
1958
+ */
1959
+ export const PluginDesignCreateDoorArgs = z.object({
1960
+ ...PluginOpeningBaseOptions,
1961
+ })
1962
+ export type PluginDesignCreateDoorArgs = z.infer<
1963
+ typeof PluginDesignCreateDoorArgs
1964
+ >
1965
+ // TRANSPORT: positional signature shipped — facing is the trailing 5th arg, hinge rides in options:
1966
+ // door(familyId, hostWall, position, options?: { label?, hinge?, version?, values? }, facing?)
1967
+
1968
+ // ---------------------------------------------------------------------------
1969
+ // window
1970
+ // ---------------------------------------------------------------------------
1971
+
1972
+ /**
1973
+ * Arguments for {@linkcode PluginDesignCreateApi.window}.
1974
+ *
1975
+ * | Property | Type | Description |
1976
+ * |---|---|---|
1977
+ * | `familyId` | `string` | Hosted window family definition ID |
1978
+ * | `version` | `number`? | Exact version; newest known by default |
1979
+ * | `values` | `Record<string, number \| string>`? | Parameters; numeric lengths use SU |
1980
+ * | `hostWall` | {@linkcode ComponentHandle} | The wall to host the window |
1981
+ * | `position` | {@linkcode Vec3Handle} | World point projected onto the wall |
1982
+ * | `label` | `string`? | Instance name (optional) |
1983
+ * | `facing` | {@linkcode Vec3Handle}? | World point on the side of the wall the window opens to (default engine-chosen) |
1984
+ * | `hinge` | {@linkcode Vec3Handle}? | World point near the hinged jamb; requires hand control |
1985
+ */
1986
+ export const PluginDesignCreateWindowArgs = z.object({
1987
+ ...PluginOpeningBaseOptions,
1988
+ })
1989
+ export type PluginDesignCreateWindowArgs = z.infer<
1990
+ typeof PluginDesignCreateWindowArgs
1991
+ >
1992
+ // TRANSPORT: positional signature shipped — facing is the trailing 5th arg, hinge rides in options:
1993
+ // window(familyId, hostWall, position, options?: { label?, hinge?, version?, values? }, facing?)
1994
+
1995
+ // ---------------------------------------------------------------------------
1996
+ // opening
1997
+ // ---------------------------------------------------------------------------
1998
+
1999
+ /**
2000
+ * Options for {@linkcode PluginDesignCreateApi.opening}, discriminated by
2001
+ * `kind`. Width and height are positive Snaptrude engine-unit values.
2002
+ * `sillHeight` is available only for windows, is nonnegative, and is precisely
2003
+ * the distance from the host wall base to the bottom of the window.
2004
+ */
2005
+ export const PluginDesignCreateOpeningOptions = z.discriminatedUnion("kind", [
2006
+ z
2007
+ .object({
2008
+ kind: z.literal("door"),
2009
+ ...PluginOpeningBaseOptions,
2010
+ })
2011
+ .strict(),
2012
+ z
2013
+ .object({
2014
+ kind: z.literal("window"),
2015
+ ...PluginOpeningBaseOptions,
2016
+ sillHeight: z.number().finite().nonnegative().optional(),
2017
+ })
2018
+ .strict(),
2019
+ ])
2020
+ export type PluginDesignCreateOpeningOptions = z.infer<
2021
+ typeof PluginDesignCreateOpeningOptions
2022
+ >
2023
+
2024
+ // ---------------------------------------------------------------------------
2025
+ // smartLayout
2026
+ // ---------------------------------------------------------------------------
2027
+
2028
+ /**
2029
+ * Arguments for {@linkcode PluginDesignCreateApi.smartLayout}. Provide **exactly
2030
+ * one** template source: `templateGroup` or a non-empty `templateComponents`.
2031
+ *
2032
+ * | Property | Type | Description |
2033
+ * |---|---|---|
2034
+ * | `templateGroup` | `string`? | Id of the group to clone as the template (a group id from `snaptrude.core.groups.list`, NOT a component handle) |
2035
+ * | `templateComponents` | {@linkcode ComponentHandle}`[]`? | Explicit component set to clone as the template |
2036
+ * | `targets` | {@linkcode ComponentHandle}`[]` | ROOM/DEPARTMENT spaces to place into (≥1) |
2037
+ * | `hideTargets` | `boolean`? | Hide the target spaces after placement (default `true`) |
2038
+ */
2039
+ export const PluginSmartLayoutArgs = z
2040
+ .object({
2041
+ templateGroup: z.string().min(1).optional(),
2042
+ templateComponents: z.array(ComponentHandle).optional(),
2043
+ targets: z.array(ComponentHandle).min(1),
2044
+ hideTargets: z.boolean().optional(),
2045
+ })
2046
+ .refine(
2047
+ (v) =>
2048
+ (v.templateGroup != null) !== (v.templateComponents?.length ?? 0) > 0,
2049
+ {
2050
+ message:
2051
+ "Provide exactly one template source: templateGroup or a non-empty templateComponents",
2052
+ },
2053
+ )
2054
+ export type PluginSmartLayoutArgs = z.infer<typeof PluginSmartLayoutArgs>
2055
+
2056
+ /**
2057
+ * Result of {@linkcode PluginDesignCreateApi.smartLayout} — the entities the run
2058
+ * created, grouped by kind (flat across all targets; see the method JSDoc for why
2059
+ * this is not attributed per target).
2060
+ *
2061
+ * | Property | Type | Description |
2062
+ * |---|---|---|
2063
+ * | `created.walls` | {@linkcode ComponentHandle}`[]` | Created walls |
2064
+ * | `created.furniture` | {@linkcode ComponentHandle}`[]` | Created furniture |
2065
+ * | `created.doors` | {@linkcode ComponentHandle}`[]` | Created doors |
2066
+ * | `created.windows` | {@linkcode ComponentHandle}`[]` | Created windows |
2067
+ * | `created.floors` | {@linkcode ComponentHandle}`[]` | Created floors |
2068
+ * | `skippedWalls` | `number` | Walls the interior optimizer could not place |
2069
+ */
2070
+ export const PluginSmartLayoutResult = z.object({
2071
+ created: z.object({
2072
+ walls: z.array(ComponentHandle),
2073
+ furniture: z.array(ComponentHandle),
2074
+ doors: z.array(ComponentHandle),
2075
+ windows: z.array(ComponentHandle),
2076
+ floors: z.array(ComponentHandle),
2077
+ }),
2078
+ skippedWalls: z.number(),
2079
+ })
2080
+ export type PluginSmartLayoutResult = z.infer<typeof PluginSmartLayoutResult>
2081
+
2082
+ // ---------------------------------------------------------------------------
2083
+ // referenceLines
2084
+ // ---------------------------------------------------------------------------
2085
+
2086
+ /**
2087
+ * Arguments for {@linkcode PluginDesignCreateApi.referenceLines}.
2088
+ *
2089
+ * | Property | Type | Description |
2090
+ * |---|---|---|
2091
+ * | `profile` | {@linkcode ProfileHandle} | Ordered curve chain (centerlines) |
2092
+ * | `color` | `string`? | Hex color `"#RRGGBB"` (default white) |
2093
+ * | `thickness` | `number`? | Line thickness (default 1) |
2094
+ * | `style` | {@linkcode PluginReferenceLineStyle}? | Line style (default `SOLID`) |
2095
+ * | `gridTag` | `string`? | Grid tag label (default `"A"`) |
2096
+ */
2097
+ export const PluginDesignCreateReferenceLinesArgs = z.object({
2098
+ profile: ProfileHandle,
2099
+ color: z.string().optional(),
2100
+ thickness: z.number().optional(),
2101
+ style: PluginReferenceLineStyle.optional(),
2102
+ gridTag: z.string().optional(),
2103
+ })
2104
+ export type PluginDesignCreateReferenceLinesArgs = z.infer<
2105
+ typeof PluginDesignCreateReferenceLinesArgs
2106
+ >
2107
+
2108
+ // ---------------------------------------------------------------------------
2109
+ // copy
2110
+ // ---------------------------------------------------------------------------
2111
+
2112
+ /**
2113
+ * Copy mode for {@linkcode PluginDesignCreateApi.copy}.
2114
+ *
2115
+ * | Value | Description |
2116
+ * |---|---|
2117
+ * | `"instance"` | Keep copies in the source's instance family where possible |
2118
+ * | `"unique"` | Independent geometry copies |
2119
+ */
2120
+ export const PluginCopyMode = z.enum(["instance", "unique"])
2121
+ export type PluginCopyMode = z.infer<typeof PluginCopyMode>
2122
+
2123
+ /**
2124
+ * Arguments for {@linkcode PluginDesignCreateApi.copy} (options flattened).
2125
+ *
2126
+ * | Property | Type | Description |
2127
+ * |---|---|---|
2128
+ * | `components` | {@linkcode ComponentHandle}`[]` | Entities to copy (≥1) |
2129
+ * | `displacement` | {@linkcode Vec3Handle} | Per-copy offset (copy `i` at `displacement * i`) |
2130
+ * | `count` | `number`? | Copies per component (default 1) |
2131
+ * | `mode` | {@linkcode PluginCopyMode}? | `"instance"` (default) or `"unique"` |
2132
+ */
2133
+ export const PluginDesignCreateCopyArgs = z.object({
2134
+ components: z.array(ComponentHandle).min(1),
2135
+ displacement: Vec3Handle,
2136
+ count: z.number().int().positive().optional(),
2137
+ mode: PluginCopyMode.optional(),
2138
+ })
2139
+ export type PluginDesignCreateCopyArgs = z.infer<
2140
+ typeof PluginDesignCreateCopyArgs
2141
+ >
2142
+ // TRANSPORT: positional signature shipped —
2143
+ // copy(components: ComponentHandle[], displacement: Vec3Handle, options?: { count?, mode? })
2144
+
2145
+ // ---------------------------------------------------------------------------
2146
+ // spaces (bulk)
2147
+ // ---------------------------------------------------------------------------
2148
+
2149
+ /**
2150
+ * One space to create via {@linkcode PluginDesignCreateApi.spaces} — the same
2151
+ * footprint-extrude inputs as {@linkcode PluginDesignCreateApi.space}.
2152
+ *
2153
+ * | Property | Type | Description |
2154
+ * |---|---|---|
2155
+ * | `contour` | {@linkcode ContourHandle} | Footprint (outer profile + holes) |
2156
+ * | `height` | `number` | Extrude height |
2157
+ * | `label` | `string`? | Room label |
2158
+ * | `position` | {@linkcode Vec3Handle}? | Placement offset |
2159
+ * | `spaceType` | `PluginSpaceType`? | Space type |
2160
+ * | `massType` | `PluginMassType`? | Mass type |
2161
+ * | `departmentId` | `PluginDepartmentId`? | Department |
2162
+ * | `storey` | `number`? (int) | Target storey number (default active storey) |
2163
+ */
2164
+ export const PluginCreateSpaceItem = z.object({
2165
+ contour: ContourHandle,
2166
+ height: z.number(),
2167
+ label: z.string().optional(),
2168
+ position: Vec3Handle.optional(),
2169
+ spaceType: PluginSpaceType.optional(),
2170
+ massType: PluginMassType.optional(),
2171
+ departmentId: PluginDepartmentId.optional(),
2172
+ storey: z.number().int().optional(),
2173
+ })
2174
+ export type PluginCreateSpaceItem = z.infer<typeof PluginCreateSpaceItem>
2175
+
2176
+ export * from "./opening-fields"
2177
+ export * from "./bulk-items"
2178
+ export * from "./grids"