@snaptrude/plugin-core 0.0.0-dev-20260708130115 → 0.0.0-dev-20260827135706

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 (261) hide show
  1. package/CHANGELOG.md +152 -1
  2. package/api-manifest.json +3620 -276
  3. package/dist/api/analysis/daylight.d.ts +603 -0
  4. package/dist/api/analysis/daylight.d.ts.map +1 -0
  5. package/dist/api/analysis/heatmaps.d.ts +658 -0
  6. package/dist/api/analysis/heatmaps.d.ts.map +1 -0
  7. package/dist/api/analysis/illuminance.d.ts +145 -0
  8. package/dist/api/analysis/illuminance.d.ts.map +1 -0
  9. package/dist/api/analysis/index.d.ts +59 -0
  10. package/dist/api/analysis/index.d.ts.map +1 -0
  11. package/dist/api/analysis/shadows.d.ts +165 -0
  12. package/dist/api/analysis/shadows.d.ts.map +1 -0
  13. package/dist/api/analysis/solar.d.ts +249 -0
  14. package/dist/api/analysis/solar.d.ts.map +1 -0
  15. package/dist/api/analysis/sunlightHours.d.ts +208 -0
  16. package/dist/api/analysis/sunlightHours.d.ts.map +1 -0
  17. package/dist/api/analysis/sunpath.d.ts +80 -0
  18. package/dist/api/analysis/sunpath.d.ts.map +1 -0
  19. package/dist/api/analysis/weather.d.ts +193 -0
  20. package/dist/api/analysis/weather.d.ts.map +1 -0
  21. package/dist/api/core/camera/index.d.ts +261 -0
  22. package/dist/api/core/camera/index.d.ts.map +1 -0
  23. package/dist/api/core/comment/index.d.ts +105 -2
  24. package/dist/api/core/comment/index.d.ts.map +1 -1
  25. package/dist/api/core/geom/create/index.d.ts +840 -14
  26. package/dist/api/core/geom/create/index.d.ts.map +1 -1
  27. package/dist/api/core/geom/delete/index.d.ts +8 -2
  28. package/dist/api/core/geom/delete/index.d.ts.map +1 -1
  29. package/dist/api/core/geom/query/arc.d.ts +5 -5
  30. package/dist/api/core/geom/query/brep.d.ts +130 -18
  31. package/dist/api/core/geom/query/brep.d.ts.map +1 -1
  32. package/dist/api/core/geom/query/circle.d.ts +18 -18
  33. package/dist/api/core/geom/query/contour.d.ts +20 -20
  34. package/dist/api/core/geom/query/curve.d.ts +49 -49
  35. package/dist/api/core/geom/query/edge.d.ts +5 -5
  36. package/dist/api/core/geom/query/face.d.ts +16 -16
  37. package/dist/api/core/geom/query/halfedge.d.ts +8 -8
  38. package/dist/api/core/geom/query/profile.d.ts +19 -19
  39. package/dist/api/core/geom/query/vertex.d.ts +8 -8
  40. package/dist/api/core/geom/update/contour.d.ts +14 -14
  41. package/dist/api/core/geom/update/curve.d.ts +7 -7
  42. package/dist/api/core/geom/update/profile.d.ts +16 -16
  43. package/dist/api/core/handles/index.d.ts +210 -0
  44. package/dist/api/core/handles/index.d.ts.map +1 -0
  45. package/dist/api/core/index.d.ts +34 -0
  46. package/dist/api/core/index.d.ts.map +1 -1
  47. package/dist/api/core/io/export/index.d.ts +134 -0
  48. package/dist/api/core/io/export/index.d.ts.map +1 -0
  49. package/dist/api/core/io/import/index.d.ts +453 -0
  50. package/dist/api/core/io/import/index.d.ts.map +1 -0
  51. package/dist/api/core/io/index.d.ts +40 -0
  52. package/dist/api/core/io/index.d.ts.map +1 -0
  53. package/dist/api/core/io/job/index.d.ts +139 -0
  54. package/dist/api/core/io/job/index.d.ts.map +1 -0
  55. package/dist/api/core/io/query/index.d.ts +74 -0
  56. package/dist/api/core/io/query/index.d.ts.map +1 -0
  57. package/dist/api/core/io/terrain/index.d.ts +341 -0
  58. package/dist/api/core/io/terrain/index.d.ts.map +1 -0
  59. package/dist/api/core/io/underlay/index.d.ts +805 -0
  60. package/dist/api/core/io/underlay/index.d.ts.map +1 -0
  61. package/dist/api/core/layers.d.ts +7 -7
  62. package/dist/api/core/mode/index.d.ts +99 -0
  63. package/dist/api/core/mode/index.d.ts.map +1 -0
  64. package/dist/api/core/project/index.d.ts +68 -1
  65. package/dist/api/core/project/index.d.ts.map +1 -1
  66. package/dist/api/core/proposals/index.d.ts +531 -0
  67. package/dist/api/core/proposals/index.d.ts.map +1 -0
  68. package/dist/api/core/storeys/index.d.ts +265 -0
  69. package/dist/api/core/storeys/index.d.ts.map +1 -0
  70. package/dist/api/core/tags.d.ts +24 -0
  71. package/dist/api/core/tags.d.ts.map +1 -1
  72. package/dist/api/core/user.d.ts +44 -0
  73. package/dist/api/core/user.d.ts.map +1 -0
  74. package/dist/api/core/zoom/index.d.ts +4 -0
  75. package/dist/api/core/zoom/index.d.ts.map +1 -1
  76. package/dist/api/design/boolean/index.d.ts +4 -4
  77. package/dist/api/design/create/index.d.ts +568 -45
  78. package/dist/api/design/create/index.d.ts.map +1 -1
  79. package/dist/api/design/delete/index.d.ts +3 -0
  80. package/dist/api/design/delete/index.d.ts.map +1 -1
  81. package/dist/api/design/doors/index.d.ts +195 -0
  82. package/dist/api/design/doors/index.d.ts.map +1 -1
  83. package/dist/api/design/edit/index.d.ts +1 -1
  84. package/dist/api/design/erase/index.d.ts +2 -2
  85. package/dist/api/design/family.d.ts +349 -0
  86. package/dist/api/design/family.d.ts.map +1 -0
  87. package/dist/api/design/furniture/index.d.ts +181 -8
  88. package/dist/api/design/furniture/index.d.ts.map +1 -1
  89. package/dist/api/design/index.d.ts +98 -0
  90. package/dist/api/design/index.d.ts.map +1 -1
  91. package/dist/api/design/lock.d.ts +26 -0
  92. package/dist/api/design/lock.d.ts.map +1 -1
  93. package/dist/api/design/materials/index.d.ts +270 -16
  94. package/dist/api/design/materials/index.d.ts.map +1 -1
  95. package/dist/api/design/query/geometry/index.d.ts +112 -0
  96. package/dist/api/design/query/geometry/index.d.ts.map +1 -1
  97. package/dist/api/design/query/index.d.ts +282 -11
  98. package/dist/api/design/query/index.d.ts.map +1 -1
  99. package/dist/api/design/query/referenceLines.d.ts +45 -0
  100. package/dist/api/design/query/referenceLines.d.ts.map +1 -0
  101. package/dist/api/design/query/spaces.d.ts +181 -8
  102. package/dist/api/design/query/spaces.d.ts.map +1 -1
  103. package/dist/api/design/selection/index.d.ts +144 -0
  104. package/dist/api/design/selection/index.d.ts.map +1 -1
  105. package/dist/api/design/transform/index.d.ts +172 -10
  106. package/dist/api/design/transform/index.d.ts.map +1 -1
  107. package/dist/api/design/types/index.d.ts +181 -0
  108. package/dist/api/design/types/index.d.ts.map +1 -0
  109. package/dist/api/design/update/index.d.ts +553 -2
  110. package/dist/api/design/update/index.d.ts.map +1 -1
  111. package/dist/api/design/visibility.d.ts +126 -0
  112. package/dist/api/design/visibility.d.ts.map +1 -0
  113. package/dist/api/design/windows/index.d.ts +113 -2
  114. package/dist/api/design/windows/index.d.ts.map +1 -1
  115. package/dist/api/entity/buildableEnvelope.d.ts +4 -0
  116. package/dist/api/entity/buildableEnvelope.d.ts.map +1 -1
  117. package/dist/api/entity/referenceLine.d.ts +10 -2
  118. package/dist/api/entity/referenceLine.d.ts.map +1 -1
  119. package/dist/api/entity/space.d.ts +21 -21
  120. package/dist/api/entity/story.d.ts +242 -15
  121. package/dist/api/entity/story.d.ts.map +1 -1
  122. package/dist/api/index.d.ts +10 -0
  123. package/dist/api/index.d.ts.map +1 -1
  124. package/dist/api/presentation/aiInspiration.d.ts +25 -25
  125. package/dist/api/presentation/annotate.d.ts +467 -0
  126. package/dist/api/presentation/annotate.d.ts.map +1 -0
  127. package/dist/api/presentation/diagrams.d.ts +111 -8
  128. package/dist/api/presentation/diagrams.d.ts.map +1 -1
  129. package/dist/api/presentation/export.d.ts +108 -0
  130. package/dist/api/presentation/export.d.ts.map +1 -0
  131. package/dist/api/presentation/import.d.ts +55 -4
  132. package/dist/api/presentation/import.d.ts.map +1 -1
  133. package/dist/api/presentation/index.d.ts +58 -1
  134. package/dist/api/presentation/index.d.ts.map +1 -1
  135. package/dist/api/presentation/placedViews.d.ts +1139 -0
  136. package/dist/api/presentation/placedViews.d.ts.map +1 -0
  137. package/dist/api/presentation/shapes.d.ts +481 -0
  138. package/dist/api/presentation/shapes.d.ts.map +1 -0
  139. package/dist/api/presentation/sheets.d.ts +452 -13
  140. package/dist/api/presentation/sheets.d.ts.map +1 -1
  141. package/dist/api/presentation/slideshow.d.ts +125 -0
  142. package/dist/api/presentation/slideshow.d.ts.map +1 -0
  143. package/dist/api/presentation/tables.d.ts +81 -0
  144. package/dist/api/presentation/tables.d.ts.map +1 -0
  145. package/dist/api/presentation/views.d.ts +367 -7
  146. package/dist/api/presentation/views.d.ts.map +1 -1
  147. package/dist/api/program/areas.d.ts +102 -12
  148. package/dist/api/program/areas.d.ts.map +1 -1
  149. package/dist/api/program/cores.d.ts +3 -99
  150. package/dist/api/program/cores.d.ts.map +1 -1
  151. package/dist/api/program/index.d.ts +7 -15
  152. package/dist/api/program/index.d.ts.map +1 -1
  153. package/dist/api/program/layout.d.ts +346 -11
  154. package/dist/api/program/layout.d.ts.map +1 -1
  155. package/dist/api/program/site.d.ts +469 -13
  156. package/dist/api/program/site.d.ts.map +1 -1
  157. package/dist/api/program/spreadsheet.d.ts +365 -41
  158. package/dist/api/program/spreadsheet.d.ts.map +1 -1
  159. package/dist/api/workspace/index.d.ts +505 -0
  160. package/dist/api/workspace/index.d.ts.map +1 -0
  161. package/dist/errors/codes.d.ts +34 -0
  162. package/dist/errors/codes.d.ts.map +1 -0
  163. package/dist/errors/envelope.d.ts +56 -0
  164. package/dist/errors/envelope.d.ts.map +1 -0
  165. package/dist/errors/index.d.ts +6 -0
  166. package/dist/errors/index.d.ts.map +1 -0
  167. package/dist/errors/plugin-error.d.ts +69 -0
  168. package/dist/errors/plugin-error.d.ts.map +1 -0
  169. package/dist/handles.d.ts +97 -25
  170. package/dist/handles.d.ts.map +1 -1
  171. package/dist/host-utils.d.ts +4 -0
  172. package/dist/host-utils.d.ts.map +1 -1
  173. package/dist/index.cjs +4582 -1422
  174. package/dist/index.cjs.map +1 -1
  175. package/dist/index.d.ts +1 -0
  176. package/dist/index.d.ts.map +1 -1
  177. package/dist/index.js +4171 -1411
  178. package/dist/index.js.map +1 -1
  179. package/package.json +4 -2
  180. package/scripts/generate-manifest.mjs +45 -0
  181. package/scripts/generate-manifest.test.mjs +103 -4
  182. package/src/api/analysis/daylight.ts +470 -0
  183. package/src/api/analysis/heatmaps.ts +683 -0
  184. package/src/api/analysis/illuminance.ts +155 -0
  185. package/src/api/analysis/index.ts +61 -0
  186. package/src/api/analysis/shadows.ts +183 -0
  187. package/src/api/analysis/solar.ts +237 -0
  188. package/src/api/analysis/sunlightHours.ts +211 -0
  189. package/src/api/analysis/sunpath.ts +83 -0
  190. package/src/api/analysis/weather.ts +179 -0
  191. package/src/api/core/camera/index.ts +268 -0
  192. package/src/api/core/comment/index.ts +120 -2
  193. package/src/api/core/geom/create/index.ts +912 -1
  194. package/src/api/core/geom/delete/index.ts +6 -0
  195. package/src/api/core/geom/query/brep.ts +119 -0
  196. package/src/api/core/handles/index.ts +233 -0
  197. package/src/api/core/index.ts +34 -0
  198. package/src/api/core/io/export/index.ts +126 -0
  199. package/src/api/core/io/import/index.ts +496 -0
  200. package/src/api/core/io/index.ts +42 -0
  201. package/src/api/core/io/job/index.ts +140 -0
  202. package/src/api/core/io/query/index.ts +71 -0
  203. package/src/api/core/io/terrain/index.ts +360 -0
  204. package/src/api/core/io/underlay/index.ts +705 -0
  205. package/src/api/core/mode/index.ts +96 -0
  206. package/src/api/core/project/index.ts +62 -1
  207. package/src/api/core/proposals/index.ts +569 -0
  208. package/src/api/core/storeys/index.ts +294 -0
  209. package/src/api/core/tags.ts +27 -0
  210. package/src/api/core/user.ts +46 -0
  211. package/src/api/core/zoom/index.ts +4 -0
  212. package/src/api/design/create/index.ts +670 -30
  213. package/src/api/design/delete/index.ts +3 -0
  214. package/src/api/design/doors/index.ts +208 -0
  215. package/src/api/design/erase/index.ts +1 -1
  216. package/src/api/design/family.ts +388 -0
  217. package/src/api/design/furniture/index.ts +197 -8
  218. package/src/api/design/index.ts +102 -0
  219. package/src/api/design/lock.ts +27 -0
  220. package/src/api/design/materials/index.ts +334 -27
  221. package/src/api/design/query/geometry/index.ts +125 -3
  222. package/src/api/design/query/index.ts +217 -7
  223. package/src/api/design/query/referenceLines.ts +52 -0
  224. package/src/api/design/query/spaces.ts +143 -0
  225. package/src/api/design/selection/index.ts +129 -0
  226. package/src/api/design/transform/index.ts +170 -9
  227. package/src/api/design/types/index.ts +156 -0
  228. package/src/api/design/update/index.ts +631 -3
  229. package/src/api/design/visibility.ts +143 -0
  230. package/src/api/design/windows/index.ts +128 -2
  231. package/src/api/entity/buildableEnvelope.ts +4 -0
  232. package/src/api/entity/referenceLine.ts +8 -0
  233. package/src/api/entity/story.ts +259 -15
  234. package/src/api/index.ts +10 -0
  235. package/src/api/presentation/annotate.ts +385 -0
  236. package/src/api/presentation/diagrams.ts +118 -8
  237. package/src/api/presentation/export.ts +108 -0
  238. package/src/api/presentation/import.ts +51 -4
  239. package/src/api/presentation/index.ts +66 -1
  240. package/src/api/presentation/placedViews.ts +1120 -0
  241. package/src/api/presentation/shapes.ts +274 -0
  242. package/src/api/presentation/sheets.ts +400 -13
  243. package/src/api/presentation/slideshow.ts +134 -0
  244. package/src/api/presentation/tables.ts +84 -0
  245. package/src/api/presentation/views.ts +376 -8
  246. package/src/api/program/areas.ts +88 -15
  247. package/src/api/program/cores.ts +3 -91
  248. package/src/api/program/index.ts +7 -15
  249. package/src/api/program/layout.ts +365 -11
  250. package/src/api/program/site.ts +435 -13
  251. package/src/api/program/spreadsheet.ts +376 -35
  252. package/src/api/workspace/index.ts +563 -0
  253. package/src/errors/codes.ts +136 -0
  254. package/src/errors/envelope.ts +75 -0
  255. package/src/errors/index.ts +21 -0
  256. package/src/errors/plugin-error.ts +134 -0
  257. package/src/handles.ts +123 -13
  258. package/src/host-utils.ts +4 -0
  259. package/src/index.ts +1 -0
  260. package/test/errors.test.mjs +184 -0
  261. package/tsconfig.json +7 -2
@@ -1,6 +1,7 @@
1
1
  import * as z from "zod"
2
2
  import { PluginApiReturn } from "../../../types"
3
3
  import {
4
+ BrepHandle,
4
5
  ComponentHandle,
5
6
  ContourHandle,
6
7
  ProfileHandle,
@@ -11,6 +12,12 @@ import {
11
12
  PluginMassType,
12
13
  PluginDepartmentId,
13
14
  } from "../../entity/space"
15
+ import {
16
+ PluginBuildableEnvelopePolygonVertex,
17
+ PluginBuildableEnvelopeSetbackTier,
18
+ PluginBuildableEnvelopeVerticalCap,
19
+ PluginBuildableEnvelopeCreateResult,
20
+ } from "../../entity/buildableEnvelope"
14
21
 
15
22
  /**
16
23
  * `design.create.*` — author new scene-committed BIM entities.
@@ -24,6 +31,13 @@ import {
24
31
  * Footprint objects take a {@linkcode ContourHandle} (outer profile + holes);
25
32
  * build one with `snaptrude.core.geom.create.contourFromProfile(s)`.
26
33
  *
34
+ * `door`/`window` place a catalog item into a host wall (the world point is
35
+ * projected onto the wall face to locate the opening; the wall is re-cut in one
36
+ * undo entry) and throw if the point projects outside the wall. `smartLayout` is
37
+ * the one creator returning a grouped {@linkcode PluginSmartLayoutResult} rather
38
+ * than a handle — it clones an in-scene template into one or more target spaces,
39
+ * merging every target into a single undo batch (see its JSDoc).
40
+ *
27
41
  * Accessed via `snaptrude.design.create`.
28
42
  */
29
43
  export abstract class PluginDesignCreateApi {
@@ -43,14 +57,25 @@ export abstract class PluginDesignCreateApi {
43
57
  * @param spaceType - Space-type classification (default `Room`)
44
58
  * @param massType - Mass-type classification (default `Room`)
45
59
  * @param departmentId - Department assignment (default department)
60
+ * @param storey - Target storey number to place the space on — the same integer
61
+ * `entity.story` uses (`1` ground, `2` first floor, `-1` basement). Default:
62
+ * the active storey. Folds a `core.storeys.setActive` call into the create,
63
+ * placing the space at that storey's floor elevation (any `position` offset is
64
+ * applied on top). Throws if no storey has that value.
46
65
  * @returns the {@linkcode ComponentHandle} of the created space
47
- * @throws if the contour is invalid or the height is not positive
66
+ * @throws if the contour is invalid, the height is not positive, or the target
67
+ * storey does not exist
48
68
  *
49
69
  * @examplePrompt Create a room called Living Room that's 3m tall from this outline
50
70
  * @examplePrompt Add a new space on the current storey using this footprint
51
71
  * @examplePrompt Make a bedroom here and assign it to the Residential department
52
72
  * @examplePrompt Extrude this closed outline into a 10 foot high room
53
73
  * @examplePrompt Can you create a Kitchen space from the sketch I just drew?
74
+ * @examplePrompt Create a bedroom on the second floor from this outline
75
+ *
76
+ * @performance For MORE THAN ONE space, call `design.create.spaces(items[])` — the whole
77
+ * batch is one host round-trip. Looping this single-space creator is N round-trips (slow,
78
+ * and a large loop can trip the plugin rate limit).
54
79
  *
55
80
  * # Example
56
81
  * ```ts
@@ -63,6 +88,10 @@ export abstract class PluginDesignCreateApi {
63
88
  * ])
64
89
  * const contour = await snaptrude.core.geom.create.contourFromProfile(outer)
65
90
  * const space = await snaptrude.design.create.space(contour, 3, "Living")
91
+ * // …place one directly on the second floor (no setActive dance):
92
+ * const upstairs = await snaptrude.design.create.space(
93
+ * contour, 3, "Bedroom", undefined, undefined, undefined, undefined, 2,
94
+ * )
66
95
  * ```
67
96
  */
68
97
  public abstract space(
@@ -73,6 +102,7 @@ export abstract class PluginDesignCreateApi {
73
102
  spaceType?: PluginSpaceType,
74
103
  massType?: PluginMassType,
75
104
  departmentId?: PluginDepartmentId,
105
+ storey?: number,
76
106
  ): PluginApiReturn<ComponentHandle>
77
107
 
78
108
  /**
@@ -107,6 +137,51 @@ export abstract class PluginDesignCreateApi {
107
137
  massType?: PluginMassType,
108
138
  ): PluginApiReturn<ComponentHandle>
109
139
 
140
+ /**
141
+ * Create a **mass** from a custom solid built with a `core.geom.create` brep
142
+ * constructor (`brepFromFaces`, `brepFromExtrusion`, `brepFromLoft`,
143
+ * `brepFromMesh`, or a brep boolean) — the free-form counterpart of
144
+ * {@linkcode PluginDesignCreateApi.mass}, for solids that are not simple
145
+ * footprint extrusions (pyramids, wedges, faceted volumes).
146
+ *
147
+ * Only accepts breps **minted by a `core.geom.create` brep constructor** —
148
+ * scene-derived breps from `design.query.geometry.getBrep` are read-only and
149
+ * throw. The mass is created as a generic mass (`massType` "Generic mass") at
150
+ * the exact coordinates the solid was authored in, committed as a single
151
+ * undo step, and synced to collaborators like any other creator.
152
+ *
153
+ * @param brep - A solid from a `core.geom.create` brep constructor
154
+ * @param label - Label (maps to `room_type`)
155
+ * @returns the {@linkcode ComponentHandle} of the created mass
156
+ * @throws if the brep handle is invalid, or the brep was not created by a
157
+ * `core.geom.create` brep constructor
158
+ *
159
+ * @examplePrompt Create a mass from these faces
160
+ * @examplePrompt Turn this custom brep into a scene mass
161
+ * @examplePrompt Add a pyramid-shaped mass to the model
162
+ *
163
+ * # Example
164
+ * ```ts
165
+ * const apex = { x: 0, y: 4, z: 0 }
166
+ * const a = { x: -2, y: 0, z: -2 }
167
+ * const b = { x: 2, y: 0, z: -2 }
168
+ * const c = { x: 2, y: 0, z: 2 }
169
+ * const d = { x: -2, y: 0, z: 2 }
170
+ * const brep = await snaptrude.core.geom.create.brepFromFaces([
171
+ * [a, d, c, b],
172
+ * [a, b, apex],
173
+ * [b, c, apex],
174
+ * [c, d, apex],
175
+ * [d, a, apex],
176
+ * ])
177
+ * const pyramid = await snaptrude.design.create.massFromBrep(brep, "Pavilion")
178
+ * ```
179
+ */
180
+ public abstract massFromBrep(
181
+ brep: BrepHandle,
182
+ label?: string,
183
+ ): PluginApiReturn<ComponentHandle>
184
+
110
185
  /**
111
186
  * Create a **slab** by extruding a footprint contour by `thickness`
112
187
  * (default direction: down).
@@ -222,14 +297,17 @@ export abstract class PluginDesignCreateApi {
222
297
 
223
298
  /**
224
299
  * Create a **column** by extruding a cross-section contour upward by `height`
225
- * from a base `position`.
300
+ * from a base `position`. The column is built to EXACTLY `height` — unlike
301
+ * the interactive draw tool, no slab-thickness deduction is applied.
226
302
  *
227
303
  * @param position - Base point
228
304
  * @param crossSection - Column cross-section footprint
229
- * @param height - Column height (> 0)
230
- * @param directionUp - Up axis (default world up)
305
+ * @param height - Column height (> 0), built exactly
306
+ * @param directionUp - Up axis (default world up); normalized by the host, so
307
+ * magnitude does not scale the height
231
308
  * @returns the {@linkcode ComponentHandle} of the created column
232
- * @throws if the cross-section is invalid or the height is not positive
309
+ * @throws if the cross-section is invalid, the height is not positive, or the
310
+ * direction is zero / lies in the cross-section plane (degenerate extrusion)
233
311
  *
234
312
  * @examplePrompt Add a 3m tall column at this point
235
313
  * @examplePrompt Place a column at the grid intersection
@@ -253,13 +331,30 @@ export abstract class PluginDesignCreateApi {
253
331
 
254
332
  /**
255
333
  * Create a **beam** by extruding a cross-section contour along `direction` by
256
- * `length`.
334
+ * `length`. Matches the Snaptrude beam tool's semantics: the beam body hangs
335
+ * BELOW the plane the section is authored on.
336
+ *
337
+ * For a **horizontal** `direction` (the common spanning beam), author the
338
+ * section as an axis-aligned plan rectangle: its X extent is the beam's
339
+ * width, its Z extent is the beam's depth (the vertical drop). The beam runs
340
+ * `length` along `direction` starting at the section's centre, hanging below
341
+ * the authoring plane by its depth — exactly like an interactively drawn
342
+ * beam. Only axis-aligned rectangular sections are supported horizontally.
257
343
  *
258
- * @param section - Beam cross-section footprint
259
- * @param length - Beam length along `direction` (> 0)
260
- * @param direction - Extrusion axis (default world up)
344
+ * For a **vertical** `direction` (the default), the section itself is
345
+ * extruded by `length`, again hanging below the authoring plane. Diagonal
346
+ * directions are rejected.
347
+ *
348
+ * @param section - Beam cross-section, an axis-aligned rectangle in plan
349
+ * (X extent = width, Z extent = depth)
350
+ * @param length - Beam length along `direction` (> 0); direction is
351
+ * normalized by the host, so magnitude does not scale the length
352
+ * @param direction - Beam axis: horizontal for a spanning beam, or vertical
353
+ * (default world up). Diagonal directions throw.
261
354
  * @returns the {@linkcode ComponentHandle} of the created beam
262
- * @throws if the section is invalid or the length is not positive
355
+ * @throws if the section is invalid (horizontal beams: not an axis-aligned
356
+ * plan rectangle), the length is not positive, or the direction is zero or
357
+ * diagonal
263
358
  *
264
359
  * @examplePrompt Add a beam spanning 6 metres between the columns
265
360
  * @examplePrompt Create a horizontal beam along the X axis
@@ -286,15 +381,31 @@ export abstract class PluginDesignCreateApi {
286
381
  *
287
382
  * @param profile - Ordered curve chain (wall centerlines)
288
383
  * @param height - Wall height (default engine default)
289
- * @param thickness - Wall thickness (default engine default)
384
+ * @param thickness - Wall thickness (default: the `wallType`'s total layer
385
+ * thickness when `wallType` is given, else the engine default)
386
+ * @param wallType - Wall type to build with — the same searchable list the
387
+ * Draw tab's Wall Type dropdown offers. Pass a name from
388
+ * `design.types.list("wall")` (the summary's `label`, or its `"wall:…"` id).
389
+ * Drives the wall's construction layers and default material, and — when
390
+ * `thickness` is omitted — its total layer thickness. Default: the engine's
391
+ * generic wall.
392
+ * @param storey - Target storey (integer: `1` ground, `2` first floor, `-1`
393
+ * basement — same convention as `design.create.space`). The walls are
394
+ * assigned to it and lifted to its base elevation, all inside the same
395
+ * single undo entry. The storey must already exist
396
+ * (`core.storeys.create` first). Default: geometry-derived — a profile
397
+ * drawn at y = 0 lands on storey 1; the ACTIVE storey is NOT consulted.
290
398
  * @returns a {@linkcode ComponentHandle}`[]` (one per wall), in profile order
291
- * @throws if the profile is empty or wall creation fails
399
+ * @throws if the profile is empty, `wallType` names no wall type in the
400
+ * project, `storey` does not exist, or wall creation fails
292
401
  *
293
402
  * @examplePrompt Draw walls along this centerline
294
403
  * @examplePrompt Create 3m high, 200mm thick walls from these points
295
404
  * @examplePrompt Build a wall run around the room outline
296
405
  * @examplePrompt Turn this polyline sketch into walls
297
406
  * @examplePrompt Add walls with mitred corners along the path
407
+ * @examplePrompt Draw brick walls with plaster along this outline
408
+ * @examplePrompt Build the perimeter in exposed concrete walls
298
409
  *
299
410
  * # Example
300
411
  * ```ts
@@ -305,12 +416,19 @@ export abstract class PluginDesignCreateApi {
305
416
  * await v.new(8, 0, 6),
306
417
  * ])
307
418
  * const walls = await snaptrude.design.create.walls(centerlines, 3, 0.2)
419
+ * // …built as a specific wall type (layers/material/thickness from the type):
420
+ * const [brick] = await snaptrude.design.types.list("wall")
421
+ * const brickWalls = await snaptrude.design.create.walls(
422
+ * centerlines, 3, undefined, brick.label,
423
+ * )
308
424
  * ```
309
425
  */
310
426
  public abstract walls(
311
427
  profile: ProfileHandle,
312
428
  height?: number,
313
429
  thickness?: number,
430
+ wallType?: string,
431
+ storey?: number,
314
432
  ): PluginApiReturn<ComponentHandle[]>
315
433
 
316
434
  /**
@@ -318,12 +436,17 @@ export abstract class PluginDesignCreateApi {
318
436
  *
319
437
  * Geometry is procedurally generated from the chosen `preset`; there is no
320
438
  * footprint input. Placed on the active structure/story unless overridden.
439
+ * The promise resolves only after the staircase's asynchronous geometry union
440
+ * and creation-command capture settle; a union failure rejects the call.
321
441
  *
322
442
  * @param preset - The parametric preset to generate
323
443
  * @param position - Placement point
324
444
  * @param label - Optional label
325
445
  * @param structureId - Target structure (default active)
326
446
  * @param level - Target level name (default active/`"01"`)
447
+ * @param dimensions - Optional positive dimension overrides in Snaptrude engine
448
+ * units, plus a placement angle in 90-degree increments. Overrides are
449
+ * applied to the staircase parameters before the creation command runs.
327
450
  * @returns the {@linkcode ComponentHandle} of the created staircase
328
451
  * @throws if the preset is unknown or placement fails
329
452
  *
@@ -347,6 +470,7 @@ export abstract class PluginDesignCreateApi {
347
470
  label?: string,
348
471
  structureId?: string,
349
472
  level?: string,
473
+ dimensions?: PluginStaircaseDimensions,
350
474
  ): PluginApiReturn<ComponentHandle>
351
475
 
352
476
  /**
@@ -401,14 +525,23 @@ export abstract class PluginDesignCreateApi {
401
525
  * with {@linkcode PluginDesignFurnitureApi.listCatalog}. Placement is asynchronous
402
526
  * (the source mesh is fetched if not cached) and creates one undo entry.
403
527
  *
404
- * Placed at the item's default rotation; rotate afterward via `design.transform.rotate`
405
- * on the returned handle (planned namespace).
528
+ * Placed at the item's default rotation unless `angleInDegrees` is given, which
529
+ * rotates it about the vertical axis at creation time (baked into the same undo
530
+ * entry). To rotate an existing instance instead, use `design.transform.rotate`.
406
531
  *
407
532
  * @param catalogId - Library id: team `_id` or general `fullName`
408
- * @param position - Absolute world placement point
533
+ * @param position - Absolute world placement point. `position.y` is the REST
534
+ * elevation: the item is grounded so its bounding-box base sits exactly at
535
+ * `position.y` (the same surface-flush contract as interactive drag-drop) —
536
+ * pass the floor/storey elevation to stand furniture on it; never add half
537
+ * the item's height yourself.
409
538
  * @param options - Optional placement options: `label` — instance name
410
539
  * (default auto `${name}Ins${n}`); `createNewSourceMesh` — emit a
411
540
  * source-mesh creation command (default `true`)
541
+ * @param angleInDegrees - Optional signed rotation about the vertical axis, in
542
+ * degrees (same convention as {@linkcode PluginDesignTransformApi.rotate}).
543
+ * Applied at creation time so it is part of the placement's single undo entry.
544
+ * Default: the item's own (unrotated) orientation.
412
545
  * @returns the {@linkcode ComponentHandle} of the placed furniture instance
413
546
  * @throws if the catalog id is unknown, the source mesh fails to load, or placement fails
414
547
  *
@@ -417,6 +550,7 @@ export abstract class PluginDesignCreateApi {
417
550
  * @examplePrompt Drop a desk into the scene at 3, 0, 5
418
551
  * @examplePrompt Insert the uploaded bed model in the bedroom
419
552
  * @examplePrompt Put a table from my project library here and name it Table-01
553
+ * @examplePrompt Place a chair here rotated 90 degrees
420
554
  *
421
555
  * # Example
422
556
  * ```ts
@@ -425,24 +559,235 @@ export abstract class PluginDesignCreateApi {
425
559
  * await snaptrude.core.math.vec3.new(3, 0, 5),
426
560
  * { label: "Chair-01" },
427
561
  * )
562
+ * // …placed already turned 90° about the vertical axis:
563
+ * const turned = await snaptrude.design.create.furniture(
564
+ * "6620f1a…",
565
+ * await snaptrude.core.math.vec3.new(3, 0, 5),
566
+ * undefined,
567
+ * 90,
568
+ * )
428
569
  * ```
429
570
  */
430
571
  public abstract furniture(
431
572
  catalogId: string,
432
573
  position: Vec3Handle,
433
574
  options?: { label?: string; createNewSourceMesh?: boolean },
575
+ angleInDegrees?: number,
576
+ ): PluginApiReturn<ComponentHandle>
577
+
578
+ /**
579
+ * Place a **door** from the catalog into a host wall.
580
+ *
581
+ * Identified by a **catalog (library) id** — the team object's stable `_id`
582
+ * or the general library's `fullName`. Discover ids with
583
+ * {@linkcode PluginDesignDoorsApi.listCatalog}. The world `position` is
584
+ * projected onto the host wall's face to locate the opening; the wall is
585
+ * re-cut and the door hosted in a single undo entry. Placement is
586
+ * asynchronous (the source mesh is fetched if not cached).
587
+ *
588
+ * @param catalogId - Library id: team `_id` or general `fullName`
589
+ * @param hostWall - The wall to host the door
590
+ * @param position - World point projected onto the wall to locate the opening
591
+ * @param options - Optional placement options: `label` — instance name
592
+ * @param facing - World point selecting which side of the wall the door faces
593
+ * (the room it opens into) — the same convention as approaching the wall
594
+ * from that side with the cursor in the interactive tool. Any point clearly
595
+ * on that side works (e.g. the room's center). Default: the engine picks a
596
+ * side (nondeterministic when `position` sits on the wall centerline).
597
+ * @returns the {@linkcode ComponentHandle} of the placed door
598
+ * @throws if the catalog id is unknown, the host is not a wall, the source
599
+ * mesh fails to load, or the projected point falls **outside** the host wall
600
+ *
601
+ * @examplePrompt Add a door to this wall here
602
+ * @examplePrompt Place a sliding door on the selected wall
603
+ * @examplePrompt Put a door from the catalog at this point on the wall
604
+ * @examplePrompt Insert the entrance door into this wall
605
+ * @examplePrompt Add a door to the wall and call it Entry-01
606
+ * @examplePrompt Add a door that opens into the living room
607
+ *
608
+ * # Example
609
+ * ```ts
610
+ * const [wall] = await snaptrude.design.query.listWalls({ isSelected: true })
611
+ * const [entry] = await snaptrude.design.doors.listCatalog()
612
+ * const door = await snaptrude.design.create.door(
613
+ * entry.id,
614
+ * wall,
615
+ * await snaptrude.core.math.vec3.new(3, 0, 5),
616
+ * { label: "Entry-01" },
617
+ * )
618
+ * // …facing a specific side of the wall (pass a point inside that room):
619
+ * const intoKitchen = await snaptrude.design.create.door(
620
+ * entry.id,
621
+ * wall,
622
+ * await snaptrude.core.math.vec3.new(3, 0, 5),
623
+ * undefined,
624
+ * await snaptrude.core.math.vec3.new(3, 0, 9),
625
+ * )
626
+ * ```
627
+ */
628
+ public abstract door(
629
+ catalogId: string,
630
+ hostWall: ComponentHandle,
631
+ position: Vec3Handle,
632
+ options?: { label?: string },
633
+ facing?: Vec3Handle,
634
+ ): PluginApiReturn<ComponentHandle>
635
+
636
+ /**
637
+ * Place a **window** from the catalog into a host wall.
638
+ *
639
+ * Identified by a **catalog (library) id** — the team object's stable `_id`
640
+ * or the general library's `fullName`. Discover ids with
641
+ * {@linkcode PluginDesignWindowsApi.listCatalog}. The world `position` is
642
+ * projected onto the host wall's face to locate the opening; the wall is
643
+ * re-cut and the window hosted in a single undo entry. Placement is
644
+ * asynchronous (the source mesh is fetched if not cached).
645
+ *
646
+ * @param catalogId - Library id: team `_id` or general `fullName`
647
+ * @param hostWall - The wall to host the window
648
+ * @param position - World point projected onto the wall to locate the opening
649
+ * @param options - Optional placement options: `label` — instance name
650
+ * @param facing - World point selecting which side of the wall the window
651
+ * faces (matters for asymmetric windows, e.g. casement swing) — same
652
+ * convention as {@linkcode PluginDesignCreateApi.door}. Default: the engine
653
+ * picks a side (nondeterministic when `position` sits on the centerline).
654
+ * @returns the {@linkcode ComponentHandle} of the placed window
655
+ * @throws if the catalog id is unknown, the host is not a wall, the source
656
+ * mesh fails to load, or the projected point falls **outside** the host wall
657
+ *
658
+ * @examplePrompt Add a window to this wall here
659
+ * @examplePrompt Place a casement window on the selected wall
660
+ * @examplePrompt Put a window from the catalog at this point on the wall
661
+ * @examplePrompt Insert a window into the living room wall
662
+ * @examplePrompt Add a window to the wall and name it Win-01
663
+ * @examplePrompt Add a casement window opening toward the garden side
664
+ *
665
+ * # Example
666
+ * ```ts
667
+ * const [wall] = await snaptrude.design.query.listWalls({ isSelected: true })
668
+ * const [entry] = await snaptrude.design.windows.listCatalog()
669
+ * const window = await snaptrude.design.create.window(
670
+ * entry.id,
671
+ * wall,
672
+ * await snaptrude.core.math.vec3.new(3, 0, 5),
673
+ * { label: "Win-01" },
674
+ * )
675
+ * ```
676
+ */
677
+ public abstract window(
678
+ catalogId: string,
679
+ hostWall: ComponentHandle,
680
+ position: Vec3Handle,
681
+ options?: { label?: string },
682
+ facing?: Vec3Handle,
434
683
  ): PluginApiReturn<ComponentHandle>
435
684
 
685
+ /**
686
+ * Place a catalog **door or window** into a host wall with optional size overrides.
687
+ *
688
+ * The world `position` is projected onto the host wall to locate the opening.
689
+ * All supplied dimensions use Snaptrude engine units. Window `sillHeight` is
690
+ * measured from the host wall base to the bottom of the window, not to
691
+ * its center. Placement is asynchronous and committed as one undoable creation.
692
+ *
693
+ * @param options - Opening kind, catalog and host references, placement, and
694
+ * optional facing, label, and dimensions
695
+ * @returns the {@linkcode ComponentHandle} of the placed door or window
696
+ * @throws if the options are invalid, the catalog id is unknown, the host is
697
+ * not a wall, loading fails, or the opening cannot be placed on the host
698
+ *
699
+ * @examplePrompt Add a 1m wide door to this wall here
700
+ * @examplePrompt Place a window with a 0.9m sill height on the selected wall
701
+ *
702
+ * @performance Single-opening creator — use it for one hosted opening. A plural
703
+ * API is intentionally unavailable until host placement can be atomic.
704
+ *
705
+ * # Example
706
+ * ```ts
707
+ * const opening = await snaptrude.design.create.opening({
708
+ * kind: "window",
709
+ * catalogId: windowType.id,
710
+ * hostWall: wall,
711
+ * position: await snaptrude.core.math.vec3.new(3, 0, 5),
712
+ * width: 1.2,
713
+ * height: 1.5,
714
+ * sillHeight: 0.9,
715
+ * })
716
+ * ```
717
+ */
718
+ public abstract opening(
719
+ options: PluginDesignCreateOpeningOptions,
720
+ ): PluginApiReturn<ComponentHandle>
721
+
722
+ /**
723
+ * Place a **smart layout** — clone an in-scene template cluster (walls,
724
+ * furniture, doors, windows, floors) into one or more target ROOM/DEPARTMENT
725
+ * spaces, adapting orientation and fit to each target.
726
+ *
727
+ * Supply the template as **exactly one** of `templateGroup` (a group **id**
728
+ * from `snaptrude.core.groups.list` — groups are string-id entities, not
729
+ * component handles) or
730
+ * `templateComponents` (an explicit component set); every target in `targets`
731
+ * (≥1) receives its own placement. By default the target spaces are hidden
732
+ * after placement (`hideTargets`, mirroring the product) — pass `false` to keep
733
+ * them visible.
734
+ *
735
+ * The whole operation — every wall/opening/furniture/floor across every target,
736
+ * plus the target-hide — is committed as a **single undo step**. Because all
737
+ * targets merge into one creation batch, the created entities are returned
738
+ * **flat, grouped by kind** ({@linkcode PluginSmartLayoutResult}), NOT attributed
739
+ * per target (per-target attribution would break the single-undo guarantee).
740
+ * `skippedWalls` is the interior-optimizer's best-candidate count of walls it
741
+ * could not place. Synchronous and heavy — many targets can jank the main thread.
742
+ *
743
+ * @param options - {@linkcode PluginSmartLayoutArgs}: template source, targets, and `hideTargets`
744
+ * @returns a {@linkcode PluginSmartLayoutResult} — created entities grouped by kind + `skippedWalls`
745
+ * @throws if smart layout is not enabled, the template group is invalid, there
746
+ * is no template / no targets / no valid mappings, or component creation fails
747
+ *
748
+ * @examplePrompt Copy this room's layout into the selected rooms
749
+ * @examplePrompt Apply this furniture arrangement to the other offices
750
+ * @examplePrompt Replicate this template into these two spaces
751
+ * @examplePrompt Place this desk cluster into every meeting room
752
+ * @examplePrompt Use this room as a template for the rooms I selected but keep them visible
753
+ *
754
+ * # Example
755
+ * ```ts
756
+ * const [templateRoom, ...targets] = await snaptrude.design.query.listSpaces({ isSelected: true })
757
+ * const result = await snaptrude.design.create.smartLayout({
758
+ * templateComponents: [templateRoom],
759
+ * targets,
760
+ * })
761
+ * console.log("placed walls:", result.created.walls.length, "skipped:", result.skippedWalls)
762
+ *
763
+ * // …or clone a saved group by its id (from core.groups.list)
764
+ * const { groups } = await snaptrude.core.groups.list()
765
+ * await snaptrude.design.create.smartLayout({ templateGroup: groups[0].id, targets })
766
+ * ```
767
+ */
768
+ public abstract smartLayout(
769
+ options: PluginSmartLayoutArgs,
770
+ ): PluginApiReturn<PluginSmartLayoutResult>
771
+
436
772
  /**
437
773
  * **Copy** existing entities, offsetting each copy by `displacement * i` (i = 1…count),
438
774
  * source positions preserved. Migrated from the removed `tools.copy`. In `"instance"`
439
775
  * mode copies stay in the source's instance family where possible; `"unique"` makes
440
776
  * independent geometry. The created copies become the active selection. Undoable.
441
777
  *
778
+ * Runs on the engine's array-copy tool, so its semantics carry over: an
779
+ * `"instance"` copy of a source that is not yet an instance **replaces that
780
+ * source** with a fresh instance component — the handle you passed in stops
781
+ * resolving after the call. Re-query (e.g. `design.query.listSpaces`) to pick
782
+ * up the replacement before operating on that entity again; copying the stale
783
+ * handle throws. `"unique"` mode never replaces sources.
784
+ *
442
785
  * @param components - Entities to copy (≥1)
443
786
  * @param displacement - Per-copy offset as a {@linkcode Vec3Handle} (copy `i` at `displacement * i`)
444
787
  * @param options - `count` (copies per component, default 1), `mode` (`"instance"` | `"unique"`, default `"instance"`)
445
788
  * @returns the created copies as {@linkcode ComponentHandle}`[]`
789
+ * @throws if a component handle is unknown or stale (replaced by an earlier
790
+ * `"instance"` copy), `count` is not a positive integer, or the engine fails
446
791
  *
447
792
  * @examplePrompt Copy this room 3 times spaced 6m apart
448
793
  * @examplePrompt Duplicate the selected furniture along the X axis
@@ -450,6 +795,11 @@ export abstract class PluginDesignCreateApi {
450
795
  * @examplePrompt Array these walls with a 3m offset
451
796
  * @examplePrompt Clone the desk as a unique copy, not an instance
452
797
  *
798
+ * @performance To place N repeats of the same geometry, bulk-create the seed(s) once then
799
+ * call this ONCE with `{ count: N - 1 }` — one host round-trip for all copies. Never
800
+ * reproduce geometry by looping `design.create.*` N times. Copy only after the create
801
+ * that mints the seed ids.
802
+ *
453
803
  * # Example
454
804
  * ```ts
455
805
  * const { vec3 } = snaptrude.core.math
@@ -467,15 +817,24 @@ export abstract class PluginDesignCreateApi {
467
817
  * Migrated from the removed `entity.space.bulkCreate`. Each item extrudes a contour
468
818
  * footprint up `height`. Validate-all-or-throw; one command. For a rectangular
469
819
  * (box) space, build the footprint with `core.geom.create.profileRect` →
470
- * `contourFromProfile`, then pass it as an item.
820
+ * `contourFromProfile`, then pass it as an item. Each item may carry its own
821
+ * `storey` (see {@linkcode PluginCreateSpaceItem}) so a single call can populate
822
+ * several floors at once.
471
823
  *
472
824
  * @param items - One {@linkcode PluginCreateSpaceItem} per space to create (≥1)
473
825
  * @returns the created spaces as {@linkcode ComponentHandle}`[]`, in input order
826
+ * @throws if any item's contour is invalid, its height is not positive, or its
827
+ * target `storey` does not exist (all-or-nothing — nothing is created)
474
828
  *
475
829
  * @examplePrompt Create several rooms at once from these footprints
476
830
  * @examplePrompt Generate all the bedrooms in one operation
477
831
  * @examplePrompt Bulk create the spaces for this floor plan
478
832
  * @examplePrompt Add three rooms with different heights in a single undo step
833
+ * @examplePrompt Create one room on the ground floor and one on the first floor
834
+ *
835
+ * @performance Bulk creator — the whole batch is ONE host round-trip. Always prefer this
836
+ * over calling `design.create.space` in a loop: build the full `items[]` array first
837
+ * (all per-item math and geometry up front), then make one call.
479
838
  *
480
839
  * # Example
481
840
  * ```ts
@@ -487,7 +846,61 @@ export abstract class PluginDesignCreateApi {
487
846
  * ])
488
847
  * ```
489
848
  */
490
- public abstract spaces(items: PluginCreateSpaceItem[]): PluginApiReturn<ComponentHandle[]>
849
+ public abstract spaces(
850
+ items: PluginCreateSpaceItem[],
851
+ ): PluginApiReturn<ComponentHandle[]>
852
+
853
+ /**
854
+ * Create a new parametric buildable envelope — the zoning-regulated volume a
855
+ * building may occupy — generated from a site polygon, tiered setbacks, a
856
+ * height or floor-count cap, and optional FAR and lot-coverage limits. The host
857
+ * mints the envelope id and returns it. Canonical home of the create formerly
858
+ * at `entity.buildableEnvelope.create` (now deprecated).
859
+ *
860
+ * @param sitePolygon - Site polygon vertices in `lengthUnit`; minimum 3 vertices.
861
+ * @param lengthUnit - Unit used by all length fields (`"ft" | "m"`).
862
+ * @param setbacks - Setback profile, ground tier first; minimum 1 tier.
863
+ * @param verticalCap - Maximum height or floor count.
864
+ * @param floorToFloor - Required floor-to-floor height in `lengthUnit`.
865
+ * @param farRatio - Optional FAR value, positive when provided.
866
+ * @param lotCoverageMaxPct - Optional lot coverage cap, `0..100`.
867
+ * @returns A {@linkcode PluginBuildableEnvelopeCreateResult} with the
868
+ * `buildableEnvelopeId` of the created envelope.
869
+ * @throws If validation fails or generation produces no renderable geometry.
870
+ *
871
+ * @examplePrompt Create a buildable envelope for my site
872
+ * @examplePrompt Generate the zoning envelope with 10ft front setbacks
873
+ * @examplePrompt Show the maximum buildable volume with a 150ft height limit
874
+ * @examplePrompt Create a zoning envelope capped at 12 floors with FAR 3
875
+ *
876
+ * # Example
877
+ * ```ts
878
+ * const { buildableEnvelopeId } = await snaptrude.design.create.buildableEnvelope(
879
+ * [
880
+ * { x: 0, z: 0 },
881
+ * { x: 100, z: 0 },
882
+ * { x: 100, z: 80 },
883
+ * { x: 0, z: 80 },
884
+ * ],
885
+ * "ft",
886
+ * [
887
+ * { aboveHeight: 0, front: 10, side: 5, rear: 10 },
888
+ * { aboveHeight: 100, front: 20, side: 10, rear: 20 },
889
+ * ],
890
+ * { kind: "max_height", maxHeight: 150 },
891
+ * 12,
892
+ * )
893
+ * ```
894
+ */
895
+ public abstract buildableEnvelope(
896
+ sitePolygon: PluginBuildableEnvelopePolygonVertex[],
897
+ lengthUnit: "ft" | "m",
898
+ setbacks: PluginBuildableEnvelopeSetbackTier[],
899
+ verticalCap: PluginBuildableEnvelopeVerticalCap,
900
+ floorToFloor: number,
901
+ farRatio?: number,
902
+ lotCoverageMaxPct?: number,
903
+ ): PluginApiReturn<PluginBuildableEnvelopeCreateResult>
491
904
  }
492
905
 
493
906
  // ---------------------------------------------------------------------------
@@ -549,6 +962,24 @@ export const PluginStaircasePreset = z.enum([
549
962
  ])
550
963
  export type PluginStaircasePreset = z.infer<typeof PluginStaircasePreset>
551
964
 
965
+ /**
966
+ * Optional staircase dimension overrides. Lengths use Snaptrude engine units
967
+ * and must be positive. `angleInDegrees` controls placement about the vertical
968
+ * axis and must be a multiple of 90 degrees.
969
+ */
970
+ export const PluginStaircaseDimensions = z
971
+ .object({
972
+ width: z.number().finite().positive().optional(),
973
+ tread: z.number().finite().positive().optional(),
974
+ riser: z.number().finite().positive().optional(),
975
+ landingWidth: z.number().finite().positive().optional(),
976
+ angleInDegrees: z.number().finite().multipleOf(90).optional(),
977
+ })
978
+ .strict()
979
+ export type PluginStaircaseDimensions = z.infer<
980
+ typeof PluginStaircaseDimensions
981
+ >
982
+
552
983
  // ---------------------------------------------------------------------------
553
984
  // space
554
985
  // ---------------------------------------------------------------------------
@@ -565,6 +996,7 @@ export type PluginStaircasePreset = z.infer<typeof PluginStaircasePreset>
565
996
  * | `spaceType` | {@linkcode PluginSpaceType}? | Space-type classification (default `Room`) |
566
997
  * | `massType` | {@linkcode PluginMassType}? | Mass-type classification (default `Room`) |
567
998
  * | `departmentId` | {@linkcode PluginDepartmentId}? | Department assignment (default department) |
999
+ * | `storey` | `number`? (int) | Target storey number (default active storey) |
568
1000
  */
569
1001
  export const PluginDesignCreateSpaceArgs = z.object({
570
1002
  contour: ContourHandle,
@@ -574,8 +1006,13 @@ export const PluginDesignCreateSpaceArgs = z.object({
574
1006
  spaceType: PluginSpaceType.optional(),
575
1007
  massType: PluginMassType.optional(),
576
1008
  departmentId: PluginDepartmentId.optional(),
1009
+ storey: z.number().int().optional(),
577
1010
  })
578
- export type PluginDesignCreateSpaceArgs = z.infer<typeof PluginDesignCreateSpaceArgs>
1011
+ export type PluginDesignCreateSpaceArgs = z.infer<
1012
+ typeof PluginDesignCreateSpaceArgs
1013
+ >
1014
+ // TRANSPORT: positional signature shipped — storey is the trailing 8th arg:
1015
+ // space(contour, height, label?, position?, spaceType?, massType?, departmentId?, storey?)
579
1016
 
580
1017
  // ---------------------------------------------------------------------------
581
1018
  // mass
@@ -599,7 +1036,29 @@ export const PluginDesignCreateMassArgs = z.object({
599
1036
  position: Vec3Handle.optional(),
600
1037
  massType: PluginMassType.optional(),
601
1038
  })
602
- export type PluginDesignCreateMassArgs = z.infer<typeof PluginDesignCreateMassArgs>
1039
+ export type PluginDesignCreateMassArgs = z.infer<
1040
+ typeof PluginDesignCreateMassArgs
1041
+ >
1042
+
1043
+ // ---------------------------------------------------------------------------
1044
+ // massFromBrep
1045
+ // ---------------------------------------------------------------------------
1046
+
1047
+ /**
1048
+ * Arguments for {@linkcode PluginDesignCreateApi.massFromBrep}.
1049
+ *
1050
+ * | Property | Type | Description |
1051
+ * |---|---|---|
1052
+ * | `brep` | {@linkcode BrepHandle} | A solid from a `core.geom.create` brep constructor |
1053
+ * | `label` | `string`? | Label (maps to `room_type`) |
1054
+ */
1055
+ export const PluginDesignCreateMassFromBrepArgs = z.object({
1056
+ brep: BrepHandle,
1057
+ label: z.string().optional(),
1058
+ })
1059
+ export type PluginDesignCreateMassFromBrepArgs = z.infer<
1060
+ typeof PluginDesignCreateMassFromBrepArgs
1061
+ >
603
1062
 
604
1063
  // ---------------------------------------------------------------------------
605
1064
  // slab
@@ -621,7 +1080,9 @@ export const PluginDesignCreateSlabArgs = z.object({
621
1080
  direction: z.enum(["up", "down"]).optional(),
622
1081
  slabType: PluginSlabType.optional(),
623
1082
  })
624
- export type PluginDesignCreateSlabArgs = z.infer<typeof PluginDesignCreateSlabArgs>
1083
+ export type PluginDesignCreateSlabArgs = z.infer<
1084
+ typeof PluginDesignCreateSlabArgs
1085
+ >
625
1086
 
626
1087
  // ---------------------------------------------------------------------------
627
1088
  // floor
@@ -641,7 +1102,9 @@ export const PluginDesignCreateFloorArgs = z.object({
641
1102
  thickness: z.number(),
642
1103
  position: Vec3Handle.optional(),
643
1104
  })
644
- export type PluginDesignCreateFloorArgs = z.infer<typeof PluginDesignCreateFloorArgs>
1105
+ export type PluginDesignCreateFloorArgs = z.infer<
1106
+ typeof PluginDesignCreateFloorArgs
1107
+ >
645
1108
 
646
1109
  // ---------------------------------------------------------------------------
647
1110
  // roof
@@ -659,7 +1122,9 @@ export const PluginDesignCreateRoofArgs = z.object({
659
1122
  contour: ContourHandle,
660
1123
  thickness: z.number(),
661
1124
  })
662
- export type PluginDesignCreateRoofArgs = z.infer<typeof PluginDesignCreateRoofArgs>
1125
+ export type PluginDesignCreateRoofArgs = z.infer<
1126
+ typeof PluginDesignCreateRoofArgs
1127
+ >
663
1128
 
664
1129
  // ---------------------------------------------------------------------------
665
1130
  // ceiling
@@ -679,7 +1144,9 @@ export const PluginDesignCreateCeilingArgs = z.object({
679
1144
  thickness: z.number(),
680
1145
  heightFromFloor: z.number().optional(),
681
1146
  })
682
- export type PluginDesignCreateCeilingArgs = z.infer<typeof PluginDesignCreateCeilingArgs>
1147
+ export type PluginDesignCreateCeilingArgs = z.infer<
1148
+ typeof PluginDesignCreateCeilingArgs
1149
+ >
683
1150
 
684
1151
  // ---------------------------------------------------------------------------
685
1152
  // column
@@ -701,7 +1168,9 @@ export const PluginDesignCreateColumnArgs = z.object({
701
1168
  height: z.number(),
702
1169
  directionUp: Vec3Handle.optional(),
703
1170
  })
704
- export type PluginDesignCreateColumnArgs = z.infer<typeof PluginDesignCreateColumnArgs>
1171
+ export type PluginDesignCreateColumnArgs = z.infer<
1172
+ typeof PluginDesignCreateColumnArgs
1173
+ >
705
1174
 
706
1175
  // ---------------------------------------------------------------------------
707
1176
  // beam
@@ -721,7 +1190,9 @@ export const PluginDesignCreateBeamArgs = z.object({
721
1190
  length: z.number(),
722
1191
  direction: Vec3Handle.optional(),
723
1192
  })
724
- export type PluginDesignCreateBeamArgs = z.infer<typeof PluginDesignCreateBeamArgs>
1193
+ export type PluginDesignCreateBeamArgs = z.infer<
1194
+ typeof PluginDesignCreateBeamArgs
1195
+ >
725
1196
 
726
1197
  // ---------------------------------------------------------------------------
727
1198
  // walls
@@ -734,14 +1205,22 @@ export type PluginDesignCreateBeamArgs = z.infer<typeof PluginDesignCreateBeamAr
734
1205
  * |---|---|---|
735
1206
  * | `profile` | {@linkcode ProfileHandle} | Ordered curve chain (wall centerlines) |
736
1207
  * | `height` | `number`? | Wall height (default engine default) |
737
- * | `thickness` | `number`? | Wall thickness (default engine default) |
1208
+ * | `thickness` | `number`? | Wall thickness (default: `wallType`'s total layer thickness when given, else engine default) |
1209
+ * | `wallType` | `string`? | Wall type name from `design.types.list("wall")` — layers/material/thickness defaults (default generic) |
1210
+ * | `storey` | `number`? | Target storey (integer, must exist) — walls assigned + lifted to its base in the same undo entry (default: geometry-derived) |
738
1211
  */
739
1212
  export const PluginDesignCreateWallsArgs = z.object({
740
1213
  profile: ProfileHandle,
741
1214
  height: z.number().optional(),
742
1215
  thickness: z.number().optional(),
1216
+ wallType: z.string().min(1).optional(),
1217
+ storey: z.number().int().optional(),
743
1218
  })
744
- export type PluginDesignCreateWallsArgs = z.infer<typeof PluginDesignCreateWallsArgs>
1219
+ export type PluginDesignCreateWallsArgs = z.infer<
1220
+ typeof PluginDesignCreateWallsArgs
1221
+ >
1222
+ // TRANSPORT: positional signature shipped — storey is the trailing 5th arg:
1223
+ // walls(profile: ProfileHandle, height?: number, thickness?: number, wallType?: string, storey?: number)
745
1224
 
746
1225
  // ---------------------------------------------------------------------------
747
1226
  // staircase
@@ -757,6 +1236,7 @@ export type PluginDesignCreateWallsArgs = z.infer<typeof PluginDesignCreateWalls
757
1236
  * | `label` | `string`? | Optional label |
758
1237
  * | `structureId` | `string`? | Target structure (default active) |
759
1238
  * | `level` | `string`? | Target level name (default active/`"01"`) |
1239
+ * | `dimensions` | {@linkcode PluginStaircaseDimensions}? | Positive dimension overrides in engine units, applied before creation |
760
1240
  */
761
1241
  export const PluginDesignCreateStaircaseArgs = z.object({
762
1242
  preset: PluginStaircasePreset,
@@ -764,6 +1244,7 @@ export const PluginDesignCreateStaircaseArgs = z.object({
764
1244
  label: z.string().optional(),
765
1245
  structureId: z.string().optional(),
766
1246
  level: z.string().optional(),
1247
+ dimensions: PluginStaircaseDimensions.optional(),
767
1248
  })
768
1249
  export type PluginDesignCreateStaircaseArgs = z.infer<
769
1250
  typeof PluginDesignCreateStaircaseArgs
@@ -782,18 +1263,173 @@ export type PluginDesignCreateStaircaseArgs = z.infer<
782
1263
  * | `position` | {@linkcode Vec3Handle} | Absolute world placement point |
783
1264
  * | `label` | `string`? | Instance name (default auto `${name}Ins${n}`) |
784
1265
  * | `createNewSourceMesh` | `boolean`? | Emit a source-mesh creation command (default `true`) |
1266
+ * | `angleInDegrees` | `number`? | Rotation about the vertical axis, in degrees (default unrotated) |
785
1267
  */
786
1268
  export const PluginDesignCreateFurnitureArgs = z.object({
787
1269
  catalogId: z.string().min(1),
788
1270
  position: Vec3Handle,
789
1271
  label: z.string().optional(),
790
1272
  createNewSourceMesh: z.boolean().optional(),
1273
+ angleInDegrees: z.number().optional(),
791
1274
  })
792
1275
  export type PluginDesignCreateFurnitureArgs = z.infer<
793
1276
  typeof PluginDesignCreateFurnitureArgs
794
1277
  >
795
- // TRANSPORT: positional signature shipped —
796
- // furniture(catalogId: string, position: Vec3Handle, options?: { label?, createNewSourceMesh? })
1278
+ // TRANSPORT: positional signature shipped — angleInDegrees is the trailing 4th arg:
1279
+ // furniture(catalogId: string, position: Vec3Handle, options?: { label?, createNewSourceMesh? }, angleInDegrees?: number)
1280
+
1281
+ // ---------------------------------------------------------------------------
1282
+ // door
1283
+ // ---------------------------------------------------------------------------
1284
+
1285
+ /**
1286
+ * Arguments for {@linkcode PluginDesignCreateApi.door}.
1287
+ *
1288
+ * | Property | Type | Description |
1289
+ * |---|---|---|
1290
+ * | `catalogId` | `string` | Library id: team `_id` or general `fullName` |
1291
+ * | `hostWall` | {@linkcode ComponentHandle} | The wall to host the door |
1292
+ * | `position` | {@linkcode Vec3Handle} | World point projected onto the wall |
1293
+ * | `label` | `string`? | Instance name (optional) |
1294
+ * | `facing` | {@linkcode Vec3Handle}? | World point on the side of the wall the door faces (default engine-chosen) |
1295
+ */
1296
+ export const PluginDesignCreateDoorArgs = z.object({
1297
+ catalogId: z.string().min(1),
1298
+ hostWall: ComponentHandle,
1299
+ position: Vec3Handle,
1300
+ label: z.string().optional(),
1301
+ facing: Vec3Handle.optional(),
1302
+ })
1303
+ export type PluginDesignCreateDoorArgs = z.infer<
1304
+ typeof PluginDesignCreateDoorArgs
1305
+ >
1306
+ // TRANSPORT: positional signature shipped — facing is the trailing 5th arg:
1307
+ // door(catalogId: string, hostWall: ComponentHandle, position: Vec3Handle, options?: { label? }, facing?: Vec3Handle)
1308
+
1309
+ // ---------------------------------------------------------------------------
1310
+ // window
1311
+ // ---------------------------------------------------------------------------
1312
+
1313
+ /**
1314
+ * Arguments for {@linkcode PluginDesignCreateApi.window}.
1315
+ *
1316
+ * | Property | Type | Description |
1317
+ * |---|---|---|
1318
+ * | `catalogId` | `string` | Library id: team `_id` or general `fullName` |
1319
+ * | `hostWall` | {@linkcode ComponentHandle} | The wall to host the window |
1320
+ * | `position` | {@linkcode Vec3Handle} | World point projected onto the wall |
1321
+ * | `label` | `string`? | Instance name (optional) |
1322
+ * | `facing` | {@linkcode Vec3Handle}? | World point on the side of the wall the window faces (default engine-chosen) |
1323
+ */
1324
+ export const PluginDesignCreateWindowArgs = z.object({
1325
+ catalogId: z.string().min(1),
1326
+ hostWall: ComponentHandle,
1327
+ position: Vec3Handle,
1328
+ label: z.string().optional(),
1329
+ facing: Vec3Handle.optional(),
1330
+ })
1331
+ export type PluginDesignCreateWindowArgs = z.infer<
1332
+ typeof PluginDesignCreateWindowArgs
1333
+ >
1334
+ // TRANSPORT: positional signature shipped — facing is the trailing 5th arg:
1335
+ // window(catalogId: string, hostWall: ComponentHandle, position: Vec3Handle, options?: { label? }, facing?: Vec3Handle)
1336
+
1337
+ // ---------------------------------------------------------------------------
1338
+ // opening
1339
+ // ---------------------------------------------------------------------------
1340
+
1341
+ const PluginOpeningBaseOptions = {
1342
+ catalogId: z.string().min(1),
1343
+ hostWall: ComponentHandle,
1344
+ position: Vec3Handle,
1345
+ facing: Vec3Handle.optional(),
1346
+ label: z.string().optional(),
1347
+ width: z.number().finite().positive().optional(),
1348
+ height: z.number().finite().positive().optional(),
1349
+ }
1350
+
1351
+ /**
1352
+ * Options for {@linkcode PluginDesignCreateApi.opening}, discriminated by
1353
+ * `kind`. Width and height are positive Snaptrude engine-unit values.
1354
+ * `sillHeight` is available only for windows, is nonnegative, and is precisely
1355
+ * the distance from the host wall base to the bottom of the window.
1356
+ */
1357
+ export const PluginDesignCreateOpeningOptions = z.discriminatedUnion("kind", [
1358
+ z
1359
+ .object({
1360
+ kind: z.literal("door"),
1361
+ ...PluginOpeningBaseOptions,
1362
+ })
1363
+ .strict(),
1364
+ z
1365
+ .object({
1366
+ kind: z.literal("window"),
1367
+ ...PluginOpeningBaseOptions,
1368
+ sillHeight: z.number().finite().nonnegative().optional(),
1369
+ })
1370
+ .strict(),
1371
+ ])
1372
+ export type PluginDesignCreateOpeningOptions = z.infer<
1373
+ typeof PluginDesignCreateOpeningOptions
1374
+ >
1375
+
1376
+ // ---------------------------------------------------------------------------
1377
+ // smartLayout
1378
+ // ---------------------------------------------------------------------------
1379
+
1380
+ /**
1381
+ * Arguments for {@linkcode PluginDesignCreateApi.smartLayout}. Provide **exactly
1382
+ * one** template source: `templateGroup` or a non-empty `templateComponents`.
1383
+ *
1384
+ * | Property | Type | Description |
1385
+ * |---|---|---|
1386
+ * | `templateGroup` | `string`? | Id of the group to clone as the template (a group id from `snaptrude.core.groups.list`, NOT a component handle) |
1387
+ * | `templateComponents` | {@linkcode ComponentHandle}`[]`? | Explicit component set to clone as the template |
1388
+ * | `targets` | {@linkcode ComponentHandle}`[]` | ROOM/DEPARTMENT spaces to place into (≥1) |
1389
+ * | `hideTargets` | `boolean`? | Hide the target spaces after placement (default `true`) |
1390
+ */
1391
+ export const PluginSmartLayoutArgs = z
1392
+ .object({
1393
+ templateGroup: z.string().min(1).optional(),
1394
+ templateComponents: z.array(ComponentHandle).optional(),
1395
+ targets: z.array(ComponentHandle).min(1),
1396
+ hideTargets: z.boolean().optional(),
1397
+ })
1398
+ .refine(
1399
+ (v) =>
1400
+ (v.templateGroup != null) !== (v.templateComponents?.length ?? 0) > 0,
1401
+ {
1402
+ message:
1403
+ "Provide exactly one template source: templateGroup or a non-empty templateComponents",
1404
+ },
1405
+ )
1406
+ export type PluginSmartLayoutArgs = z.infer<typeof PluginSmartLayoutArgs>
1407
+
1408
+ /**
1409
+ * Result of {@linkcode PluginDesignCreateApi.smartLayout} — the entities the run
1410
+ * created, grouped by kind (flat across all targets; see the method JSDoc for why
1411
+ * this is not attributed per target).
1412
+ *
1413
+ * | Property | Type | Description |
1414
+ * |---|---|---|
1415
+ * | `created.walls` | {@linkcode ComponentHandle}`[]` | Created walls |
1416
+ * | `created.furniture` | {@linkcode ComponentHandle}`[]` | Created furniture |
1417
+ * | `created.doors` | {@linkcode ComponentHandle}`[]` | Created doors |
1418
+ * | `created.windows` | {@linkcode ComponentHandle}`[]` | Created windows |
1419
+ * | `created.floors` | {@linkcode ComponentHandle}`[]` | Created floors |
1420
+ * | `skippedWalls` | `number` | Walls the interior optimizer could not place |
1421
+ */
1422
+ export const PluginSmartLayoutResult = z.object({
1423
+ created: z.object({
1424
+ walls: z.array(ComponentHandle),
1425
+ furniture: z.array(ComponentHandle),
1426
+ doors: z.array(ComponentHandle),
1427
+ windows: z.array(ComponentHandle),
1428
+ floors: z.array(ComponentHandle),
1429
+ }),
1430
+ skippedWalls: z.number(),
1431
+ })
1432
+ export type PluginSmartLayoutResult = z.infer<typeof PluginSmartLayoutResult>
797
1433
 
798
1434
  // ---------------------------------------------------------------------------
799
1435
  // referenceLines
@@ -852,7 +1488,9 @@ export const PluginDesignCreateCopyArgs = z.object({
852
1488
  count: z.number().int().positive().optional(),
853
1489
  mode: PluginCopyMode.optional(),
854
1490
  })
855
- export type PluginDesignCreateCopyArgs = z.infer<typeof PluginDesignCreateCopyArgs>
1491
+ export type PluginDesignCreateCopyArgs = z.infer<
1492
+ typeof PluginDesignCreateCopyArgs
1493
+ >
856
1494
  // TRANSPORT: positional signature shipped —
857
1495
  // copy(components: ComponentHandle[], displacement: Vec3Handle, options?: { count?, mode? })
858
1496
 
@@ -873,6 +1511,7 @@ export type PluginDesignCreateCopyArgs = z.infer<typeof PluginDesignCreateCopyAr
873
1511
  * | `spaceType` | `PluginSpaceType`? | Space type |
874
1512
  * | `massType` | `PluginMassType`? | Mass type |
875
1513
  * | `departmentId` | `PluginDepartmentId`? | Department |
1514
+ * | `storey` | `number`? (int) | Target storey number (default active storey) |
876
1515
  */
877
1516
  export const PluginCreateSpaceItem = z.object({
878
1517
  contour: ContourHandle,
@@ -882,5 +1521,6 @@ export const PluginCreateSpaceItem = z.object({
882
1521
  spaceType: PluginSpaceType.optional(),
883
1522
  massType: PluginMassType.optional(),
884
1523
  departmentId: PluginDepartmentId.optional(),
1524
+ storey: z.number().int().optional(),
885
1525
  })
886
1526
  export type PluginCreateSpaceItem = z.infer<typeof PluginCreateSpaceItem>