@snaptrude/plugin-core 0.9.16 → 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 (221) hide show
  1. package/AGENTS.md +231 -231
  2. package/CHANGELOG.md +408 -402
  3. package/CLAUDE.md +11 -11
  4. package/api-manifest.json +1510 -701
  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 +41 -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/sections.d.ts +461 -0
  65. package/dist/api/presentation/sections.d.ts.map +1 -0
  66. package/dist/api/presentation/shapes.d.ts +2 -2
  67. package/dist/api/presentation/sheets.d.ts +175 -19
  68. package/dist/api/presentation/sheets.d.ts.map +1 -1
  69. package/dist/api/presentation/viewPresets.d.ts +222 -0
  70. package/dist/api/presentation/viewPresets.d.ts.map +1 -0
  71. package/dist/api/presentation/views.d.ts +141 -8
  72. package/dist/api/presentation/views.d.ts.map +1 -1
  73. package/dist/api/presentation/walkthroughs.d.ts +6 -6
  74. package/dist/errors/codes.d.ts +3 -1
  75. package/dist/errors/codes.d.ts.map +1 -1
  76. package/dist/index.cjs +3883 -2385
  77. package/dist/index.cjs.map +1 -1
  78. package/dist/index.js +3744 -2385
  79. package/dist/index.js.map +1 -1
  80. package/package.json +1 -1
  81. package/scripts/check-dist-fresh.mjs +22 -22
  82. package/scripts/generate-manifest.mjs +243 -238
  83. package/scripts/generate-manifest.test.mjs +303 -303
  84. package/src/api/analysis/daylight.ts +470 -470
  85. package/src/api/analysis/heatmaps.ts +683 -683
  86. package/src/api/analysis/illuminance.ts +155 -155
  87. package/src/api/analysis/index.ts +61 -61
  88. package/src/api/analysis/shadows.ts +183 -183
  89. package/src/api/analysis/solar.ts +237 -237
  90. package/src/api/analysis/sunlightHours.ts +211 -211
  91. package/src/api/analysis/sunpath.ts +83 -83
  92. package/src/api/analysis/weather.ts +179 -179
  93. package/src/api/core/buildings.ts +485 -485
  94. package/src/api/core/camera/index.ts +696 -667
  95. package/src/api/core/comment/index.ts +376 -376
  96. package/src/api/core/geom/create/index.ts +2139 -2133
  97. package/src/api/core/geom/delete/index.ts +84 -84
  98. package/src/api/core/geom/index.ts +35 -35
  99. package/src/api/core/geom/query/arc.ts +169 -169
  100. package/src/api/core/geom/query/brep.ts +621 -621
  101. package/src/api/core/geom/query/circle.ts +404 -404
  102. package/src/api/core/geom/query/contour.ts +540 -540
  103. package/src/api/core/geom/query/curve.ts +1102 -1102
  104. package/src/api/core/geom/query/edge.ts +130 -130
  105. package/src/api/core/geom/query/face.ts +508 -508
  106. package/src/api/core/geom/query/halfedge.ts +210 -210
  107. package/src/api/core/geom/query/index.ts +63 -63
  108. package/src/api/core/geom/query/profile.ts +473 -473
  109. package/src/api/core/geom/query/vertex.ts +205 -205
  110. package/src/api/core/geom/update/contour.ts +323 -317
  111. package/src/api/core/geom/update/curve.ts +209 -209
  112. package/src/api/core/geom/update/index.ts +29 -29
  113. package/src/api/core/geom/update/profile.ts +379 -379
  114. package/src/api/core/groups.ts +521 -521
  115. package/src/api/core/handles/index.ts +234 -234
  116. package/src/api/core/history/index.ts +84 -84
  117. package/src/api/core/index.ts +99 -99
  118. package/src/api/core/io/export/index.ts +126 -126
  119. package/src/api/core/io/import/index.ts +504 -504
  120. package/src/api/core/io/index.ts +42 -42
  121. package/src/api/core/io/job/index.ts +140 -140
  122. package/src/api/core/io/query/index.ts +71 -71
  123. package/src/api/core/io/terrain/index.ts +360 -360
  124. package/src/api/core/io/underlay/index.ts +705 -705
  125. package/src/api/core/layers.ts +540 -540
  126. package/src/api/core/math/index.ts +20 -20
  127. package/src/api/core/math/quat.ts +172 -172
  128. package/src/api/core/math/vec3.ts +179 -179
  129. package/src/api/core/mode/index.ts +108 -108
  130. package/src/api/core/project/index.ts +479 -479
  131. package/src/api/core/proposals/index.ts +570 -570
  132. package/src/api/core/storeys/index.ts +311 -308
  133. package/src/api/core/storeys/layers.ts +339 -339
  134. package/src/api/core/tags.ts +911 -911
  135. package/src/api/core/units/index.ts +164 -164
  136. package/src/api/core/user.ts +46 -46
  137. package/src/api/core/zoom/index.ts +64 -51
  138. package/src/api/design/bim.ts +210 -0
  139. package/src/api/design/boolean/index.ts +181 -179
  140. package/src/api/design/catalog.ts +203 -0
  141. package/src/api/design/create/bulk-items.ts +294 -254
  142. package/src/api/design/create/grids.ts +137 -137
  143. package/src/api/design/create/index.ts +2178 -1980
  144. package/src/api/design/create/opening-fields.ts +40 -37
  145. package/src/api/design/delete/index.ts +75 -74
  146. package/src/api/design/dimensions.ts +453 -453
  147. package/src/api/design/doors/index.ts +516 -500
  148. package/src/api/design/edit/index.ts +54 -54
  149. package/src/api/design/erase/index.ts +151 -151
  150. package/src/api/design/family.ts +1342 -704
  151. package/src/api/design/furniture/index.ts +319 -319
  152. package/src/api/design/grids.ts +237 -237
  153. package/src/api/design/index.ts +304 -274
  154. package/src/api/design/lock.ts +69 -69
  155. package/src/api/design/materials/index.ts +608 -608
  156. package/src/api/design/networks.ts +607 -0
  157. package/src/api/design/query/geometry/index.ts +319 -319
  158. package/src/api/design/query/grids.ts +197 -197
  159. package/src/api/design/query/index.ts +1236 -1226
  160. package/src/api/design/query/referenceLines.ts +52 -52
  161. package/src/api/design/query/spaces.ts +275 -275
  162. package/src/api/design/selection/index.ts +273 -273
  163. package/src/api/design/shared.ts +40 -40
  164. package/src/api/design/transform/index.ts +254 -254
  165. package/src/api/design/types/index.ts +163 -156
  166. package/src/api/design/update/index.ts +1096 -983
  167. package/src/api/design/visibility.ts +143 -143
  168. package/src/api/design/windows/index.ts +351 -333
  169. package/src/api/entity/buildableEnvelope.ts +397 -322
  170. package/src/api/entity/index.ts +30 -30
  171. package/src/api/entity/referenceLine.ts +235 -235
  172. package/src/api/entity/space.ts +612 -612
  173. package/src/api/entity/story.ts +517 -517
  174. package/src/api/index.ts +47 -47
  175. package/src/api/presentation/aiInspiration.ts +1116 -1116
  176. package/src/api/presentation/annotate.ts +417 -385
  177. package/src/api/presentation/camera.ts +289 -289
  178. package/src/api/presentation/diagrams.ts +194 -194
  179. package/src/api/presentation/drawingContract.ts +70 -0
  180. package/src/api/presentation/drawings.ts +1179 -0
  181. package/src/api/presentation/export.ts +134 -108
  182. package/src/api/presentation/import.ts +159 -159
  183. package/src/api/presentation/index.ts +160 -126
  184. package/src/api/presentation/placedViews.ts +1211 -1120
  185. package/src/api/presentation/sections.ts +420 -0
  186. package/src/api/presentation/shapes.ts +274 -274
  187. package/src/api/presentation/sheets.ts +746 -585
  188. package/src/api/presentation/slideshow.ts +134 -134
  189. package/src/api/presentation/tables.ts +84 -84
  190. package/src/api/presentation/viewPresets.ts +241 -0
  191. package/src/api/presentation/views.ts +742 -673
  192. package/src/api/presentation/walkthroughs.ts +1201 -1201
  193. package/src/api/program/adjacency.ts +164 -164
  194. package/src/api/program/areas.ts +408 -408
  195. package/src/api/program/classification.ts +138 -138
  196. package/src/api/program/cores.ts +83 -83
  197. package/src/api/program/departments.ts +517 -517
  198. package/src/api/program/index.ts +73 -73
  199. package/src/api/program/labels.ts +332 -332
  200. package/src/api/program/layout.ts +422 -422
  201. package/src/api/program/metadata.ts +364 -364
  202. package/src/api/program/metrics.ts +120 -120
  203. package/src/api/program/site.ts +748 -748
  204. package/src/api/program/spreadsheet.ts +2698 -2698
  205. package/src/api/workspace/index.ts +612 -612
  206. package/src/errors/codes.ts +143 -136
  207. package/src/errors/envelope.ts +75 -75
  208. package/src/errors/index.ts +21 -21
  209. package/src/errors/plugin-error.ts +134 -134
  210. package/src/handles.ts +297 -297
  211. package/src/host-utils.ts +67 -67
  212. package/src/index.ts +7 -7
  213. package/src/massParameters.ops.ts +246 -246
  214. package/src/massParameters.ts +427 -427
  215. package/src/types.ts +16 -16
  216. package/test/drawingContract.test.mjs +288 -0
  217. package/test/errors.test.mjs +185 -184
  218. package/test/massParameters.test.mjs +952 -952
  219. package/test/presentationViewSettings.test.mjs +85 -0
  220. package/tsconfig.json +22 -22
  221. package/tsup.config.ts +12 -12
@@ -1,1226 +1,1236 @@
1
- import * as z from "zod"
2
- import { PluginApiReturn } from "../../../types"
3
- import { ComponentHandle, BBoxComponents } from "../../../handles"
4
- import type { MassParametersRecord } from "../../../massParameters"
5
- import {
6
- PluginSpaceType,
7
- PluginMassType,
8
- PluginDepartmentId,
9
- PluginAreaClass,
10
- } from "../../entity/space"
11
- import { PluginStaircasePreset } from "../create"
12
- import { PluginDesignQueryGeometryApi } from "./geometry"
13
- import { PluginDesignQuerySpacesApi } from "./spaces"
14
- import { PluginDesignQueryReferenceLinesApi } from "./referenceLines"
15
- import { PluginDesignQueryGridsApi } from "./grids"
16
-
17
- // ---------------------------------------------------------------------------
18
- // Entity type vocabulary
19
- // ---------------------------------------------------------------------------
20
-
21
- /**
22
- * The kind of a BIM entity — the discovery/filter vocabulary for the whole
23
- * `design.query.*` surface. Values are clean `lowerCamel` tokens; the host maps
24
- * each to the engine's internal type (whose raw strings are case-inconsistent —
25
- * `"roof"`/`"slab"` lowercase, `"Wall"`/`"Door"` PascalCase — never surfaced here).
26
- *
27
- * | Value | Entity |
28
- * |---|---|
29
- * | `"wall"` | Wall |
30
- * | `"space"` | Space (a room mass) |
31
- * | `"mass"` | Generic mass (non-room) |
32
- * | `"door"` | Door |
33
- * | `"window"` | Window |
34
- * | `"floor"` | Floor |
35
- * | `"slab"` | Slab |
36
- * | `"roof"` | Roof |
37
- * | `"column"` | Column |
38
- * | `"beam"` | Beam |
39
- * | `"ceiling"` | Ceiling |
40
- * | `"staircase"` | Staircase |
41
- * | `"furniture"` | Furniture |
42
- * | `"referenceLine"` | Reference line |
43
- * | `"curtainWall"` | Parametric curtain wall |
44
- * | `"mullion"` | Curtain-wall mullion |
45
- * | `"panel"` | Curtain-wall panel |
46
- * | `"parametricComponent"` | A placed parametric family instance (native or Revit-imported). Its geometry is rebuilt from `design.family` parameters, so read and drive it through `design.family.getParameters` / `setParameters` rather than editing its meshes. |
47
- * | `"grid"` | Structural grid group |
48
- */
49
- export const PluginEntityType = z.enum([
50
- "wall",
51
- "space",
52
- "mass",
53
- "door",
54
- "window",
55
- "floor",
56
- "slab",
57
- "roof",
58
- "column",
59
- "beam",
60
- "ceiling",
61
- "staircase",
62
- "furniture",
63
- "referenceLine",
64
- "curtainWall",
65
- "mullion",
66
- "panel",
67
- "parametricComponent",
68
- "grid",
69
- ])
70
- export type PluginEntityType = z.infer<typeof PluginEntityType>
71
-
72
- // ---------------------------------------------------------------------------
73
- // The shared filter — one options object, every list* method
74
- // ---------------------------------------------------------------------------
75
-
76
- /**
77
- * `EntityFilterOptions` — the **single filter** shared by every `design.query.list*`
78
- * method, so filtering is identical across all BIM object types. Every field is
79
- * optional and **AND-combined**: a component must satisfy all provided fields.
80
- * Plural fields are arrays (match any of); singular fields are scalars. A field
81
- * that names a type-specific dimension (`massTypes`/`spaceTypes`/`departmentId`)
82
- * excludes components that lack that dimension.
83
- *
84
- * | Field | Type | Keeps components where |
85
- * |---|---|---|
86
- * | `types` | {@linkcode PluginEntityType}`[]` | entity kind ∈ `types` (ignored by per-type `list*` — the method fixes the kind) |
87
- * | `storeys` | `number[]` | storey/floor level ∈ `storeys` (negative = basement) |
88
- * | `buildingIds` | `string[]` | building ∈ `buildingIds` |
89
- * | `structureIds` | `string[]` | structure ∈ `structureIds` |
90
- * | `label` | `string` | label (`room_type` ?? mesh label) === `label` |
91
- * | `massTypes` | {@linkcode PluginMassType}`[]` | mass-type ∈ (masses only) |
92
- * | `spaceTypes` | {@linkcode PluginSpaceType}`[]` | space-type ∈ (spaces only) |
93
- * | `departmentId` | {@linkcode PluginDepartmentId} | department === |
94
- * | `isSelected` | `boolean` | current selection state === |
95
- * | `isLocked` | `boolean` | lock flag === |
96
- * | `isHidden` | `boolean` | user-hidden flag === |
97
- * | `within` | {@linkcode BBoxComponents} | the entity's bounding box intersects the region |
98
- * | `overlaps` | {@linkcode ComponentHandle} | the entity's plan footprint overlaps the given space's bottom-face footprint — non-zero intersection area, so fully-inside, boundary-nested, and partially-overhanging entities all match; edge-touching with zero interior overlap does not. Exact polygon intersection (holes respected). Tested in plan — storey height is ignored; compose with `storeys` to scope a floor. Hidden entities are excluded by default — pass `isHidden` explicitly to override. The reference space itself is never returned. Throws on an unknown id. Entities without a brep footprint use their bounding-box plan rectangle. |
99
- * | `ids` | {@linkcode ComponentHandle}`[]` | id ∈ `ids` (restrict to a known set) |
100
- */
101
- export const PluginEntityFilter = z.object({
102
- types: z.array(PluginEntityType).optional(),
103
- storeys: z.array(z.number()).optional(),
104
- buildingIds: z.array(z.string()).optional(),
105
- structureIds: z.array(z.string()).optional(),
106
- label: z.string().optional(),
107
- massTypes: z.array(PluginMassType).optional(),
108
- spaceTypes: z.array(PluginSpaceType).optional(),
109
- departmentId: PluginDepartmentId.optional(),
110
- isSelected: z.boolean().optional(),
111
- isLocked: z.boolean().optional(),
112
- isHidden: z.boolean().optional(),
113
- within: BBoxComponents.optional(),
114
- overlaps: ComponentHandle.optional(),
115
- ids: z.array(ComponentHandle).optional(),
116
- })
117
- export type PluginEntityFilter = z.infer<typeof PluginEntityFilter>
118
-
119
- /**
120
- * Arguments for {@linkcode PluginDesignQueryApi.listByType} — a required entity
121
- * `type` plus every {@linkcode PluginEntityFilter} field.
122
- */
123
- export const PluginDesignQueryListByTypeArgs = PluginEntityFilter.extend({
124
- type: PluginEntityType,
125
- })
126
- export type PluginDesignQueryListByTypeArgs = z.infer<
127
- typeof PluginDesignQueryListByTypeArgs
128
- >
129
-
130
- // ---------------------------------------------------------------------------
131
- // Single-entity argument shapes
132
- // ---------------------------------------------------------------------------
133
-
134
- /** Arguments for methods that read one entity by its {@linkcode ComponentHandle}. */
135
- export const PluginDesignQueryComponentArgs = z.object({
136
- component: ComponentHandle,
137
- })
138
- export type PluginDesignQueryComponentArgs = z.infer<
139
- typeof PluginDesignQueryComponentArgs
140
- >
141
-
142
- /** Arguments for methods that look an entity up by id. */
143
- export const PluginDesignQueryIdArgs = z.object({
144
- id: ComponentHandle,
145
- })
146
- export type PluginDesignQueryIdArgs = z.infer<typeof PluginDesignQueryIdArgs>
147
-
148
- /** Arguments for methods over a set of entities (e.g. the union bounding box). */
149
- export const PluginDesignQueryComponentsArgs = z.object({
150
- components: z.array(ComponentHandle),
151
- })
152
- export type PluginDesignQueryComponentsArgs = z.infer<
153
- typeof PluginDesignQueryComponentsArgs
154
- >
155
-
156
- // ---------------------------------------------------------------------------
157
- // Read records
158
- // ---------------------------------------------------------------------------
159
-
160
- /**
161
- * The common property record returned by {@linkcode PluginDesignQueryApi.getProperties} —
162
- * a **partial** object (a field is present only when it applies to the entity).
163
- * Type-specific and geometry reads live on {@linkcode PluginDesignQueryApi.measure}
164
- * and `design.query.geometry.*`, keeping this record generic across every type.
165
- *
166
- * | Property | Type | Description |
167
- * |---|---|---|
168
- * | `id` | `string` | The entity id |
169
- * | `type` | {@linkcode PluginEntityType} | Entity kind |
170
- * | `label` | `string` | `room_type` ?? mesh label ?? `""` |
171
- * | `storey` | `number` | Storey/floor level |
172
- * | `buildingId` | `string` | Owning building |
173
- * | `structureId` | `string` | Owning structure |
174
- * | `massType` | `string` | Mass-type (masses only) |
175
- * | `spaceType` | {@linkcode PluginSpaceType} | Space-type (spaces only) |
176
- * | `areaClass` | {@linkcode PluginAreaClass} | Area classification (spaces only) |
177
- * | `departmentId` | `string` | Department (masses only) |
178
- * | `buildingType` | `string` | Assigned building type NAME (walls / slabs / floors / roofs / ceilings only) — the Object Properties panel's Wall/Slab/Floor/Roof/Ceiling Type dropdown value, matching the names from `design.types.list(kind)` |
179
- * | `isLocked` | `boolean` | Lock flag |
180
- * | `isHidden` | `boolean` | User-hidden flag |
181
- * | `isSelected` | `boolean` | Current selection state |
182
- * | `boundingBox` | {@linkcode BBoxComponents} | World-space AABB |
183
- * | `adjacency` | `{ spaceId: string; value: number }[]` | Adjacency relationships (spaces/masses only). `value`: `2` = Direct (required), `1` = Indirect or physical contact, `-1` = Restricted; `0` entries are filtered out. `[]` when adjacency has never been computed — see `program.adjacency.compute`. |
184
- */
185
- export const PluginEntityProperties = z
186
- .object({
187
- id: z.string(),
188
- type: PluginEntityType,
189
- label: z.string(),
190
- storey: z.number(),
191
- buildingId: z.string(),
192
- structureId: z.string(),
193
- massType: z.string(),
194
- spaceType: PluginSpaceType,
195
- areaClass: PluginAreaClass,
196
- departmentId: z.string(),
197
- buildingType: z.string(),
198
- isLocked: z.boolean(),
199
- isHidden: z.boolean(),
200
- isSelected: z.boolean(),
201
- boundingBox: BBoxComponents,
202
- adjacency: z.array(z.object({ spaceId: z.string(), value: z.number() })),
203
- })
204
- .partial()
205
- export type PluginEntityProperties = z.infer<typeof PluginEntityProperties>
206
-
207
- /**
208
- * The multi-metric bundle returned by {@linkcode PluginDesignQueryApi.measure}.
209
- * `boundingBox` is always present; the numeric metrics appear only when the
210
- * entity's type computes them (area: floors/masses/slabs/roofs/beams/columns;
211
- * volume: all; length: walls/beams).
212
- *
213
- * `thickness` and `height` are the Object Properties panel's dimension reads
214
- * (the same engine source `design.update.wall` / `design.update.slab` etc.
215
- * write through), in engine units. `thickness`: walls (the plan thickness —
216
- * present only for straight orthogonal walls, exactly when the panel shows it)
217
- * and slabs/floors/roofs/ceilings (the vertical extent the panel calls
218
- * Thickness). `height`: walls only.
219
- *
220
- * | Property | Type | Description |
221
- * |---|---|---|
222
- * | `area` | `number`? | Bottom-face area |
223
- * | `volume` | `number`? | Solid volume |
224
- * | `length` | `number`? | Running length (linear elements) |
225
- * | `thickness` | `number`? | Panel Thickness — walls (straight orthogonal only) and the slab family |
226
- * | `height` | `number`? | Panel Height — walls only |
227
- * | `boundingBox` | {@linkcode BBoxComponents} | World-space AABB (always present) |
228
- */
229
- export const PluginEntityMeasurements = z.object({
230
- area: z.number().optional(),
231
- volume: z.number().optional(),
232
- length: z.number().optional(),
233
- thickness: z.number().optional(),
234
- height: z.number().optional(),
235
- boundingBox: BBoxComponents,
236
- })
237
- export type PluginEntityMeasurements = z.infer<typeof PluginEntityMeasurements>
238
-
239
- /**
240
- * The parametric properties of a staircase — the same editable fields the
241
- * staircase properties panel exposes. Returned by
242
- * {@linkcode PluginDesignQueryApi.getStaircaseParams}; the write side is
243
- * `design.update.staircase`. Engine internals (block schema, min well size,
244
- * square-well padding) are deliberately excluded.
245
- *
246
- * All dimensions are in **engine units** (the same convention as
247
- * `design.doors.getWidth`); `steps` is a unitless count. The invariant
248
- * `staircaseHeight = steps × riser` is governed by the two lock booleans — see
249
- * `design.update.staircase`. Type-specific fields (`wellSize`, `landingWidth`,
250
- * `flightStartDistAfterTurn`) are present only when the preset uses them.
251
- *
252
- * | Property | Type | Description |
253
- * |---|---|---|
254
- * | `staircaseType` | {@linkcode PluginStaircasePreset} | Base staircase type |
255
- * | `staircasePreset` | {@linkcode PluginStaircasePreset} | Named parametric preset |
256
- * | `staircaseHeight` | `number` | Total rise (engine units) |
257
- * | `steps` | `number` | Step count (unitless) |
258
- * | `riser` | `number` | Riser height (engine units) |
259
- * | `tread` | `number` | Tread depth (engine units) |
260
- * | `width` | `number` | Flight width (engine units) |
261
- * | `depth` | `number` | Slab/waist thickness (engine units) |
262
- * | `wellSize` | `number`? | Stairwell gap (dogLegged / openWell / square) |
263
- * | `landingWidth` | `number`? | Landing depth (straight / dogLegged) |
264
- * | `flightStartDistAfterTurn` | `number`? | Flight offset after a turn (dogLegged / lShaped / square) |
265
- * | `baseOffset` | `number` | Vertical offset from the storey base (engine units) |
266
- * | `isStoreyHeightUnlocked` | `boolean` | When `false`, height is tied to the storey height |
267
- * | `isStaircaseHeightUnlocked` | `boolean` | When `false`, height recomputes from steps × riser |
268
- * | `storey` | `number` | Storey/floor level the staircase sits on |
269
- */
270
- export const PluginStaircaseParams = z.object({
271
- staircaseType: PluginStaircasePreset,
272
- staircasePreset: PluginStaircasePreset,
273
- staircaseHeight: z.number(),
274
- steps: z.number(),
275
- riser: z.number(),
276
- tread: z.number(),
277
- width: z.number(),
278
- depth: z.number(),
279
- wellSize: z.number().optional(),
280
- landingWidth: z.number().optional(),
281
- flightStartDistAfterTurn: z.number().optional(),
282
- baseOffset: z.number(),
283
- isStoreyHeightUnlocked: z.boolean(),
284
- isStaircaseHeightUnlocked: z.boolean(),
285
- storey: z.number(),
286
- })
287
- export type PluginStaircaseParams = z.infer<typeof PluginStaircaseParams>
288
-
289
- // ---------------------------------------------------------------------------
290
- // The query API
291
- // ---------------------------------------------------------------------------
292
-
293
- /**
294
- * `design.query.*` — read-only enumeration + inspection of scene-committed BIM
295
- * entities (all-handle model, §11). No query mutates the scene.
296
- *
297
- * Every `list*` returns a snapshot {@linkcode ComponentHandle}`[]` (`[]`, never
298
- * `null`) filtered by the shared {@linkcode PluginEntityFilter}; per-entity reads
299
- * take a {@linkcode ComponentHandle} and return primitive records / child handles
300
- * or `null` when the entity is gone. Resolve details of a returned handle with
301
- * {@linkcode PluginDesignQueryApi.getProperties} / {@linkcode PluginDesignQueryApi.measure}
302
- * / `design.query.geometry.*`.
303
- *
304
- * Accessed via `snaptrude.design.query`.
305
- */
306
- export abstract class PluginDesignQueryApi {
307
- /** Entity-geometry reads (brep, bottom contour). See {@linkcode PluginDesignQueryGeometryApi}. */
308
- public abstract geometry: PluginDesignQueryGeometryApi
309
- /** Space-specific reads (footprint, …). See {@linkcode PluginDesignQuerySpacesApi}. */
310
- public abstract spaces: PluginDesignQuerySpacesApi
311
- /** Reference-line-specific reads. See {@linkcode PluginDesignQueryReferenceLinesApi}. */
312
- public abstract referenceLines: PluginDesignQueryReferenceLinesApi
313
- /** Structural-grid-specific reads. See {@linkcode PluginDesignQueryGridsApi}. */
314
- public abstract grids: PluginDesignQueryGridsApi
315
-
316
- constructor() {}
317
-
318
- // ── enumeration ───────────────────────────────────────────────────────────
319
-
320
- /**
321
- * List **all** BIM entities in the project matching the filter.
322
- *
323
- * @param filter - an optional {@linkcode PluginEntityFilter}
324
- * @returns matching entities as {@linkcode ComponentHandle}`[]` (`[]` if none)
325
- *
326
- * @examplePrompt Show me everything in the model
327
- * @examplePrompt How many objects are on the ground floor?
328
- * @examplePrompt List all the doors and windows on level 2
329
- * @examplePrompt What elements are currently selected?
330
- * @examplePrompt Find every hidden object in the project
331
- *
332
- * # Example
333
- * ```ts
334
- * // Doors and windows on the ground storey (fields AND-combine)
335
- * const doorsAndWindows = await snaptrude.design.query.listEntities({
336
- * types: ["door", "window"],
337
- * storeys: [1],
338
- * })
339
- *
340
- * // Everything the user currently has selected
341
- * const selected = await snaptrude.design.query.listEntities({ isSelected: true })
342
- *
343
- * // Everything inside a world-space region (axis-aligned box, Snaptrude units)
344
- * const inRegion = await snaptrude.design.query.listEntities({
345
- * within: { min: { x: 0, y: 0, z: 0 }, max: { x: 10, y: 4, z: 8 } },
346
- * })
347
- * ```
348
- */
349
- public abstract listEntities(
350
- filter?: PluginEntityFilter,
351
- ): PluginApiReturn<ComponentHandle[]>
352
-
353
- /**
354
- * List all entities of one BIM object type — wall, door, slab, panel, and so
355
- * on — optionally narrowed by the shared filter.
356
- *
357
- * @param type - the {@linkcode PluginEntityType} to list
358
- * @param filter - an optional {@linkcode PluginEntityFilter}
359
- * @returns matching entities as {@linkcode ComponentHandle}`[]`
360
- *
361
- * @examplePrompt List all the curtain walls in the project
362
- * @examplePrompt Show every curtain wall panel on this storey
363
- * @examplePrompt Get all the elements of a single type, like columns
364
- * @examplePrompt Find all the locked slabs in the model
365
- *
366
- * # Example
367
- * ```ts
368
- * const lockedSlabs = await snaptrude.design.query.listByType("slab", { isLocked: true })
369
- *
370
- * // Curtain-wall panels on storeys 2-3 of one building
371
- * const panels = await snaptrude.design.query.listByType("panel", {
372
- * storeys: [2, 3],
373
- * buildingIds: ["bld_1"],
374
- * })
375
- * ```
376
- */
377
- public abstract listByType(
378
- type: PluginEntityType,
379
- filter?: PluginEntityFilter,
380
- ): PluginApiReturn<ComponentHandle[]>
381
-
382
- /**
383
- * List all the walls in the project, optionally filtered by storey, building,
384
- * label, selection state, or region.
385
- *
386
- * The `types` field of the filter is ignored (this method fixes the kind).
387
- *
388
- * @param filter - an optional {@linkcode PluginEntityFilter}
389
- * @returns wall {@linkcode ComponentHandle}`[]`
390
- *
391
- * @examplePrompt How many walls are in the project?
392
- * @examplePrompt List all the walls on the ground floor
393
- * @examplePrompt Which walls are currently selected?
394
- * @examplePrompt Find the walls on level 2 of building A
395
- * @examplePrompt Show me every hidden wall
396
- *
397
- * # Example
398
- * ```ts
399
- * const groundFloorWalls = await snaptrude.design.query.listWalls({ storeys: [1] })
400
- * ```
401
- */
402
- public abstract listWalls(
403
- filter?: PluginEntityFilter,
404
- ): PluginApiReturn<ComponentHandle[]>
405
-
406
- /**
407
- * List all the spaces (rooms) in the project, optionally filtered by storey,
408
- * department, space type, label, selection state, or region.
409
- *
410
- * @param filter - an optional {@linkcode PluginEntityFilter}
411
- * @returns space {@linkcode ComponentHandle}`[]`
412
- *
413
- * @examplePrompt How many rooms are in this project?
414
- * @examplePrompt List all the spaces on the second storey
415
- * @examplePrompt Which rooms are in the Bedrooms department?
416
- * @examplePrompt Find every room labelled Kitchen
417
- * @examplePrompt Show the spaces that are currently selected
418
- *
419
- * # Example
420
- * ```ts
421
- * // Balconies and decks on storey 2 (`label` is an exact match on room_type)
422
- * const outdoor = await snaptrude.design.query.listSpaces({
423
- * storeys: [2],
424
- * spaceTypes: ["Balcony", "Deck"],
425
- * })
426
- *
427
- * // Rooms in a department: resolve its id first, then filter and total the area
428
- * const { departments } = await snaptrude.program.departments.list()
429
- * const bedrooms = departments.find((d) => d.name === "Bedrooms")
430
- * if (bedrooms) {
431
- * const rooms = await snaptrude.design.query.listSpaces({ departmentId: bedrooms.id })
432
- * let total = 0
433
- * for (const room of rooms) {
434
- * const m = await snaptrude.design.query.measure(room)
435
- * total += m?.area ?? 0
436
- * }
437
- * }
438
- * ```
439
- */
440
- public abstract listSpaces(
441
- filter?: PluginEntityFilter,
442
- ): PluginApiReturn<ComponentHandle[]>
443
-
444
- /**
445
- * List all the generic masses (non-room massing volumes) in the project,
446
- * optionally filtered by storey, building, label, mass type, or region.
447
- *
448
- * @param filter - an optional {@linkcode PluginEntityFilter}
449
- * @returns mass {@linkcode ComponentHandle}`[]`
450
- *
451
- * @examplePrompt List all the masses in the model
452
- * @examplePrompt How many massing blocks are on the site?
453
- * @examplePrompt Show the generic masses on level 1
454
- * @examplePrompt Find every mass that isn't a room
455
- *
456
- * # Example
457
- * ```ts
458
- * // Only certain mass kinds — massTypes matches any of the given values
459
- * const massing = await snaptrude.design.query.listMasses({
460
- * massTypes: ["Generic mass", "Plinth"],
461
- * })
462
- * ```
463
- */
464
- public abstract listMasses(
465
- filter?: PluginEntityFilter,
466
- ): PluginApiReturn<ComponentHandle[]>
467
-
468
- /**
469
- * List all the doors in the project, optionally filtered by storey, building,
470
- * selection state, or region.
471
- *
472
- * @param filter - an optional {@linkcode PluginEntityFilter}
473
- * @returns door {@linkcode ComponentHandle}`[]`
474
- *
475
- * @examplePrompt How many doors are in the project?
476
- * @examplePrompt List all the doors on the ground floor
477
- * @examplePrompt Which doors are currently selected?
478
- * @examplePrompt Find every door in building B
479
- *
480
- * # Example
481
- * ```ts
482
- * // Ground-floor doors, then read each one's width via design.doors.*
483
- * const doors = await snaptrude.design.query.listDoors({ storeys: [1] })
484
- * for (const door of doors) {
485
- * const width = await snaptrude.design.doors.getWidth(door)
486
- * }
487
- * ```
488
- */
489
- public abstract listDoors(
490
- filter?: PluginEntityFilter,
491
- ): PluginApiReturn<ComponentHandle[]>
492
-
493
- /**
494
- * List all the windows in the project, optionally filtered by storey,
495
- * building, selection state, or region.
496
- *
497
- * @param filter - an optional {@linkcode PluginEntityFilter}
498
- * @returns window {@linkcode ComponentHandle}`[]`
499
- *
500
- * @examplePrompt How many windows does this building have?
501
- * @examplePrompt List every window on level 3
502
- * @examplePrompt Which windows are on the ground floor?
503
- * @examplePrompt Show all the windows that are currently selected
504
- *
505
- * # Example
506
- * ```ts
507
- * const level3Windows = await snaptrude.design.query.listWindows({ storeys: [3] })
508
- * ```
509
- */
510
- public abstract listWindows(
511
- filter?: PluginEntityFilter,
512
- ): PluginApiReturn<ComponentHandle[]>
513
-
514
- /**
515
- * List all the floor elements (floor plates / finishes) in the project,
516
- * optionally filtered by storey, building, lock state, or region.
517
- *
518
- * @param filter - an optional {@linkcode PluginEntityFilter}
519
- * @returns floor {@linkcode ComponentHandle}`[]`
520
- *
521
- * @examplePrompt List all the floor elements in the model
522
- * @examplePrompt How many floor plates are on the second storey?
523
- * @examplePrompt Which floors are locked?
524
- * @examplePrompt Show the floor finishes on level 0
525
- *
526
- * # Example
527
- * ```ts
528
- * const lockedFloors = await snaptrude.design.query.listFloors({ isLocked: true })
529
- * ```
530
- */
531
- public abstract listFloors(
532
- filter?: PluginEntityFilter,
533
- ): PluginApiReturn<ComponentHandle[]>
534
-
535
- /**
536
- * List all the slabs in the project, optionally filtered by storey, building,
537
- * lock state, or region.
538
- *
539
- * @param filter - an optional {@linkcode PluginEntityFilter}
540
- * @returns slab {@linkcode ComponentHandle}`[]`
541
- *
542
- * @examplePrompt List all the slabs in the project
543
- * @examplePrompt How many slabs are on level 2?
544
- * @examplePrompt Which slabs are locked?
545
- * @examplePrompt Show the slabs in the selected building
546
- *
547
- * # Example
548
- * ```ts
549
- * const storey2Slabs = await snaptrude.design.query.listSlabs({ storeys: [2] })
550
- * ```
551
- */
552
- public abstract listSlabs(
553
- filter?: PluginEntityFilter,
554
- ): PluginApiReturn<ComponentHandle[]>
555
-
556
- /**
557
- * List all the roofs in the project, optionally filtered by storey, building,
558
- * visibility, or region.
559
- *
560
- * @param filter - an optional {@linkcode PluginEntityFilter}
561
- * @returns roof {@linkcode ComponentHandle}`[]`
562
- *
563
- * @examplePrompt How many roofs does the model have?
564
- * @examplePrompt List all the roof elements
565
- * @examplePrompt Show the roofs on the top storey
566
- * @examplePrompt Find every hidden roof
567
- *
568
- * # Example
569
- * ```ts
570
- * const hiddenRoofs = await snaptrude.design.query.listRoofs({ isHidden: true })
571
- * ```
572
- */
573
- public abstract listRoofs(
574
- filter?: PluginEntityFilter,
575
- ): PluginApiReturn<ComponentHandle[]>
576
-
577
- /**
578
- * List all the columns in the project, optionally filtered by storey,
579
- * building, selection state, or region.
580
- *
581
- * @param filter - an optional {@linkcode PluginEntityFilter}
582
- * @returns column {@linkcode ComponentHandle}`[]`
583
- *
584
- * @examplePrompt How many columns are on the ground floor?
585
- * @examplePrompt List all the columns in building A
586
- * @examplePrompt Show every column on level 3
587
- * @examplePrompt Which columns are currently selected?
588
- *
589
- * # Example
590
- * ```ts
591
- * const groundFloorColumns = await snaptrude.design.query.listColumns({ storeys: [1] })
592
- * ```
593
- */
594
- public abstract listColumns(
595
- filter?: PluginEntityFilter,
596
- ): PluginApiReturn<ComponentHandle[]>
597
-
598
- /**
599
- * List all the beams in the project, optionally filtered by storey, building,
600
- * lock state, or region.
601
- *
602
- * @param filter - an optional {@linkcode PluginEntityFilter}
603
- * @returns beam {@linkcode ComponentHandle}`[]`
604
- *
605
- * @examplePrompt List all the beams in the project
606
- * @examplePrompt How many beams are on the second storey?
607
- * @examplePrompt Show the beams in this structure
608
- * @examplePrompt Find every locked beam
609
- *
610
- * # Example
611
- * ```ts
612
- * const storey2Beams = await snaptrude.design.query.listBeams({ storeys: [2] })
613
- * ```
614
- */
615
- public abstract listBeams(
616
- filter?: PluginEntityFilter,
617
- ): PluginApiReturn<ComponentHandle[]>
618
-
619
- /**
620
- * List all the ceilings in the project, optionally filtered by storey,
621
- * building, visibility, or region.
622
- *
623
- * @param filter - an optional {@linkcode PluginEntityFilter}
624
- * @returns ceiling {@linkcode ComponentHandle}`[]`
625
- *
626
- * @examplePrompt List all the ceilings in the model
627
- * @examplePrompt How many ceilings are on level 1?
628
- * @examplePrompt Which ceilings are hidden?
629
- * @examplePrompt Show the ceilings in building B
630
- *
631
- * # Example
632
- * ```ts
633
- * const hiddenCeilings = await snaptrude.design.query.listCeilings({ isHidden: true })
634
- * ```
635
- */
636
- public abstract listCeilings(
637
- filter?: PluginEntityFilter,
638
- ): PluginApiReturn<ComponentHandle[]>
639
-
640
- /**
641
- * List all the staircases in the project, optionally filtered by storey,
642
- * building, selection state, or region.
643
- *
644
- * @param filter - an optional {@linkcode PluginEntityFilter}
645
- * @returns staircase {@linkcode ComponentHandle}`[]`
646
- *
647
- * @examplePrompt How many staircases are in the building?
648
- * @examplePrompt List all the stairs in the project
649
- * @examplePrompt Show the staircases on the ground floor
650
- * @examplePrompt Which stairs are currently selected?
651
- *
652
- * # Example
653
- * ```ts
654
- * const groundFloorStairs = await snaptrude.design.query.listStaircases({ storeys: [1] })
655
- * ```
656
- */
657
- public abstract listStaircases(
658
- filter?: PluginEntityFilter,
659
- ): PluginApiReturn<ComponentHandle[]>
660
-
661
- /**
662
- * List all the furniture in the project, optionally filtered by storey,
663
- * building, label, selection state, or region.
664
- *
665
- * @param filter - an optional {@linkcode PluginEntityFilter}
666
- * @returns furniture {@linkcode ComponentHandle}`[]`
667
- *
668
- * @examplePrompt List all the furniture in the project
669
- * @examplePrompt How many furniture items are on this floor?
670
- * @examplePrompt Show the furniture inside the selected room
671
- * @examplePrompt Which furniture is on storey 2?
672
- *
673
- * # Example
674
- * ```ts
675
- * // Furniture whose plan footprint overlaps a room (`overlaps` is plan-only —
676
- * // scope the storey too, or furniture stacked on other floors matches)
677
- * const [kitchen] = await snaptrude.design.query.listSpaces({ label: "Kitchen" })
678
- * if (kitchen) {
679
- * const inKitchen = await snaptrude.design.query.listFurniture({
680
- * overlaps: kitchen,
681
- * storeys: [1],
682
- * })
683
- * }
684
- * ```
685
- */
686
- public abstract listFurniture(
687
- filter?: PluginEntityFilter,
688
- ): PluginApiReturn<ComponentHandle[]>
689
-
690
- /**
691
- * List all the reference lines in the project, optionally filtered by storey,
692
- * building, or region.
693
- *
694
- * @param filter - an optional {@linkcode PluginEntityFilter}
695
- * @returns reference-line {@linkcode ComponentHandle}`[]`
696
- *
697
- * @examplePrompt List all the reference lines in the model
698
- * @examplePrompt Show the guide lines on the ground floor
699
- * @examplePrompt How many reference lines are on this storey?
700
- * @examplePrompt Find the setting-out lines in building A
701
- *
702
- * # Example
703
- * ```ts
704
- * const groundFloorRefLines = await snaptrude.design.query.listReferenceLines({ storeys: [1] })
705
- * ```
706
- */
707
- public abstract listReferenceLines(
708
- filter?: PluginEntityFilter,
709
- ): PluginApiReturn<ComponentHandle[]>
710
-
711
- /**
712
- * List all the curtain-wall mullions in the project, optionally filtered by
713
- * storey, building, selection state, or region.
714
- *
715
- * @param filter - an optional {@linkcode PluginEntityFilter}
716
- * @returns mullion {@linkcode ComponentHandle}`[]`
717
- *
718
- * @examplePrompt List all the mullions in the curtain wall
719
- * @examplePrompt How many mullions are on the facade?
720
- * @examplePrompt Show every curtain wall mullion on level 5
721
- * @examplePrompt Which mullions are currently selected?
722
- *
723
- * # Example
724
- * ```ts
725
- * const level5Mullions = await snaptrude.design.query.listMullions({ storeys: [5] })
726
- * ```
727
- */
728
- public abstract listMullions(
729
- filter?: PluginEntityFilter,
730
- ): PluginApiReturn<ComponentHandle[]>
731
-
732
- /**
733
- * List all the structural grids (grid groups) in the project, optionally
734
- * filtered by storey, building, selection state, or region.
735
- *
736
- * @param filter - an optional {@linkcode PluginEntityFilter}
737
- * @returns grid-group {@linkcode ComponentHandle}`[]`
738
- *
739
- * @examplePrompt List all the structural grids in the model
740
- * @examplePrompt How many grids are on the ground floor?
741
- * @examplePrompt Show the grids on this storey
742
- * @examplePrompt Which grids are currently selected?
743
- *
744
- * # Example
745
- * ```ts
746
- * const groundFloorGrids = await snaptrude.design.query.listGrids({ storeys: [1] })
747
- * ```
748
- */
749
- public abstract listGrids(
750
- filter?: PluginEntityFilter,
751
- ): PluginApiReturn<ComponentHandle[]>
752
-
753
- // ── single-entity access & identity ─────────────────────────────────────────
754
-
755
- /**
756
- * Look an entity up by id — returns the same handle if it still resolves live,
757
- * else `null`. A cheap validity probe (distinct from {@linkcode PluginDesignQueryApi.exists}).
758
- *
759
- * @param id - the entity id ({@linkcode ComponentHandle}) to look up
760
- * @returns the {@linkcode ComponentHandle}, or `null` if it no longer resolves
761
- *
762
- * @examplePrompt Look up an object by its id
763
- * @examplePrompt Does id cmp_42 still point to something in the model?
764
- * @examplePrompt Fetch the element with this id
765
- * @examplePrompt Check if this saved element id is still valid
766
- *
767
- * # Example
768
- * ```ts
769
- * // Persistence round-trip: save a ref now, revalidate it in a later run
770
- * const [wall] = await snaptrude.design.query.listWalls()
771
- * const savedRef = await snaptrude.design.query.getEntityRef(wall)
772
- *
773
- * // ...later (savedRef restored from plugin storage)...
774
- * const stillThere = await snaptrude.design.query.getById(savedRef)
775
- * if (stillThere) {
776
- * const props = await snaptrude.design.query.getProperties(stillThere)
777
- * }
778
- * ```
779
- */
780
- public abstract getById(
781
- id: ComponentHandle,
782
- ): PluginApiReturn<ComponentHandle | null>
783
-
784
- /**
785
- * Test whether an entity id resolves to a live scene entity.
786
- * @param id - the entity id ({@linkcode ComponentHandle}) to look up
787
- * @returns `true` if it exists, otherwise `false`
788
- *
789
- * @examplePrompt Does this element still exist in the scene?
790
- * @examplePrompt Check whether the wall I stored earlier is still there
791
- * @examplePrompt Is id cmp_42 still valid?
792
- * @examplePrompt Verify the object wasn't deleted
793
- *
794
- * # Example
795
- * ```ts
796
- * // Guard before acting on a handle that may have been deleted or undone
797
- * if (await snaptrude.design.query.exists(wall)) {
798
- * const dx = await snaptrude.core.math.vec3.new(5, 0, 0)
799
- * await snaptrude.design.transform.move([wall], dx)
800
- * }
801
- * ```
802
- */
803
- public abstract exists(id: ComponentHandle): PluginApiReturn<boolean>
804
-
805
- /**
806
- * Get the kind of BIM entity a handle refers to — wall, door, space, slab,
807
- * and so on (its {@linkcode PluginEntityType}).
808
- * @param component - the entity's {@linkcode ComponentHandle}
809
- * @returns the kind, or `null` if the entity is gone / its type is unknown
810
- *
811
- * @examplePrompt What kind of element is this?
812
- * @examplePrompt Is the selected object a wall or a mass?
813
- * @examplePrompt Tell me the type of this component
814
- * @examplePrompt What am I looking at — a door, a window, or a panel?
815
- *
816
- * # Example
817
- * ```ts
818
- * // Dispatch on what the user selected
819
- * const selection = await snaptrude.design.selection.get()
820
- * for (const component of selection) {
821
- * const kind = await snaptrude.design.query.getEntityType(component)
822
- * if (kind === "wall") {
823
- * // wall-specific handling
824
- * } else if (kind === "space") {
825
- * // room-specific handling
826
- * }
827
- * }
828
- * ```
829
- */
830
- public abstract getEntityType(
831
- component: ComponentHandle,
832
- ): PluginApiReturn<PluginEntityType | null>
833
-
834
- /**
835
- * Get an entity's human label (`room_type` for spaces/masses, else the mesh
836
- * label; `""` when unlabelled). The write side is `design.update` (`setLabel`).
837
- * @param component - the entity's {@linkcode ComponentHandle}
838
- * @returns the label string
839
- *
840
- * @examplePrompt What is this room called?
841
- * @examplePrompt Get the name of the selected object
842
- * @examplePrompt Show the label on this wall
843
- * @examplePrompt What's the room type of this space?
844
- *
845
- * # Example
846
- * ```ts
847
- * // Fuzzy label search (the `label` filter field is exact-match; for
848
- * // case-insensitive or partial matches, read labels and compare yourself)
849
- * const spaces = await snaptrude.design.query.listSpaces()
850
- * const kitchens: typeof spaces = []
851
- * for (const space of spaces) {
852
- * const label = await snaptrude.design.query.getLabel(space)
853
- * if (label.toLowerCase().includes("kitchen")) kitchens.push(space)
854
- * }
855
- * ```
856
- */
857
- public abstract getLabel(component: ComponentHandle): PluginApiReturn<string>
858
-
859
- /**
860
- * Get a serializable, persistable reference to an entity. Under the all-handle
861
- * model a {@linkcode ComponentHandle} already IS the ref (raw `Component.id`),
862
- * so this is the identity passthrough — provided for API completeness.
863
- * @param component - the entity's {@linkcode ComponentHandle}
864
- * @returns the entity's {@linkcode ComponentHandle}
865
- *
866
- * @examplePrompt Give me a reference I can save for this element
867
- * @examplePrompt Get a persistent id for the selected wall
868
- * @examplePrompt Store a handle to this room so I can find it later
869
- *
870
- * # Example
871
- * ```ts
872
- * const [wall] = await snaptrude.design.query.listWalls()
873
- * const savedRef = await snaptrude.design.query.getEntityRef(wall)
874
- * // savedRef is a plain string — persist it, then revalidate in a later run
875
- * // with snaptrude.design.query.getById(savedRef)
876
- * ```
877
- */
878
- public abstract getEntityRef(
879
- component: ComponentHandle,
880
- ): PluginApiReturn<ComponentHandle>
881
-
882
- /**
883
- * List an entity's direct child entities — for example the doors and windows
884
- * hosted in a wall.
885
- * @param component - the entity's {@linkcode ComponentHandle}
886
- * @returns child {@linkcode ComponentHandle}`[]` (`[]` if none)
887
- *
888
- * @examplePrompt What's inside this wall — list its doors and windows
889
- * @examplePrompt Show the child elements of the selected object
890
- * @examplePrompt Which openings belong to this wall?
891
- * @examplePrompt List everything nested under this mass
892
- *
893
- * # Example
894
- * ```ts
895
- * // Count the openings hosted in a wall, split by kind
896
- * const children = await snaptrude.design.query.listChildren(wall)
897
- * let doors = 0
898
- * let windows = 0
899
- * for (const child of children) {
900
- * const kind = await snaptrude.design.query.getEntityType(child)
901
- * if (kind === "door") doors++
902
- * if (kind === "window") windows++
903
- * }
904
- * ```
905
- */
906
- public abstract listChildren(
907
- component: ComponentHandle,
908
- ): PluginApiReturn<ComponentHandle[]>
909
-
910
- /**
911
- * Get an entity's host/parent entity (e.g. the wall a door is cut into).
912
- * @param component - the entity's {@linkcode ComponentHandle}
913
- * @returns the host {@linkcode ComponentHandle}, or `null` if top-level
914
- *
915
- * @examplePrompt Which wall is this door in?
916
- * @examplePrompt Find the parent of the selected window
917
- * @examplePrompt What is this element attached to?
918
- * @examplePrompt Which wall hosts this window?
919
- *
920
- * # Example
921
- * ```ts
922
- * // From a door up to its host wall, then measure the wall
923
- * const [door] = await snaptrude.design.query.listDoors({ isSelected: true })
924
- * const hostWall = door ? await snaptrude.design.query.getHost(door) : null
925
- * if (hostWall) {
926
- * const m = await snaptrude.design.query.measure(hostWall)
927
- * console.log("wall length:", m?.length)
928
- * }
929
- * ```
930
- */
931
- public abstract getHost(
932
- component: ComponentHandle,
933
- ): PluginApiReturn<ComponentHandle | null>
934
-
935
- // ── property & metric reads ─────────────────────────────────────────────────
936
-
937
- /**
938
- * Read an entity's common properties as a {@linkcode PluginEntityProperties}
939
- * record (only applicable fields are present). For spaces/masses this includes
940
- * `adjacency` — the per-entity adjacency read (bulk form:
941
- * `program.adjacency.getMatrix`). For walls and the slab family (slab / floor
942
- * / roof / ceiling) this includes `buildingType` — the assigned building type
943
- * NAME the panel's type dropdown shows, matching `design.types.list(kind)`.
944
- *
945
- * @param component - the entity's {@linkcode ComponentHandle}
946
- * @returns the property record
947
- *
948
- * @examplePrompt Show me all the details of the selected object
949
- * @examplePrompt What storey is this wall on?
950
- * @examplePrompt Which wall type is assigned to this wall?
951
- * @examplePrompt Which department does this room belong to?
952
- * @examplePrompt Is this element locked or hidden?
953
- * @examplePrompt Get the adjacencies of this space
954
- *
955
- * # Example
956
- * ```ts
957
- * const [wall] = await snaptrude.design.query.listWalls()
958
- * const props = await snaptrude.design.query.getProperties(wall)
959
- * console.log(props.type, props.storey, props.boundingBox)
960
- *
961
- * // The record is partial — type-specific fields exist only where they apply
962
- * const [room] = await snaptrude.design.query.listSpaces()
963
- * const p = await snaptrude.design.query.getProperties(room)
964
- * if (p.spaceType === "Room" && p.areaClass === "NET") {
965
- * for (const adj of p.adjacency ?? []) {
966
- * // adj.value: 2 = direct, 1 = indirect/contact, -1 = restricted
967
- * console.log(adj.spaceId, adj.value)
968
- * }
969
- * }
970
- * ```
971
- */
972
- public abstract getProperties(
973
- component: ComponentHandle,
974
- ): PluginApiReturn<PluginEntityProperties>
975
-
976
- /**
977
- * Measure an entity — read its area, volume, and/or running length (whichever
978
- * its type supports) together with its world-space bounding box in one call.
979
- *
980
- * For walls and the slab family this also carries the Object Properties
981
- * panel's dimension reads in engine units: `thickness` (walls — straight
982
- * orthogonal only, exactly when the panel shows it; slabs / floors / roofs /
983
- * ceilings — the vertical extent the panel calls Thickness) and `height`
984
- * (walls). These are the read side of `design.update.wall` /
985
- * `design.update.slab` / `.floor` / `.roof` / `.ceiling`.
986
- *
987
- * @param component - the entity's {@linkcode ComponentHandle}
988
- * @returns a {@linkcode PluginEntityMeasurements}, or `null` if the entity is gone
989
- *
990
- * @examplePrompt What's the area of this room?
991
- * @examplePrompt How long is the selected wall?
992
- * @examplePrompt How thick is this wall?
993
- * @examplePrompt Get the thickness of the slab on level 2
994
- * @examplePrompt Get the volume of this mass
995
- * @examplePrompt Give me the dimensions of the selected element
996
- * @examplePrompt What's the floor area of the kitchen?
997
- *
998
- * # Example
999
- * ```ts
1000
- * const [space] = await snaptrude.design.query.listSpaces()
1001
- * const m = await snaptrude.design.query.measure(space)
1002
- * console.log(m?.area, m?.volume)
1003
- *
1004
- * // wall dimensions — the panel's Thickness / Height, in engine units
1005
- * const [wall] = await snaptrude.design.query.listWalls()
1006
- * const wm = await snaptrude.design.query.measure(wall)
1007
- * console.log(wm?.thickness, wm?.height)
1008
- * ```
1009
- */
1010
- public abstract measure(
1011
- component: ComponentHandle,
1012
- ): PluginApiReturn<PluginEntityMeasurements | null>
1013
-
1014
- /**
1015
- * Read a staircase's parametric properties — its preset, dimensions, step
1016
- * count, base offset, and height-lock state — as a
1017
- * {@linkcode PluginStaircaseParams} record. The paired write is
1018
- * `design.update.staircase`.
1019
- *
1020
- * All dimensions are in **engine units** (the same convention as
1021
- * `design.doors.getWidth`); `steps` is a unitless count. A read — never
1022
- * throws: returns `null` for a handle that is not a staircase or no longer
1023
- * resolves.
1024
- *
1025
- * @param staircase - the staircase's {@linkcode ComponentHandle}
1026
- * @returns the {@linkcode PluginStaircaseParams} record, or `null`
1027
- *
1028
- * @examplePrompt What's the riser height of this staircase?
1029
- * @examplePrompt How many steps does the selected stair have?
1030
- * @examplePrompt Show me the tread and width of this staircase
1031
- * @examplePrompt Read all the parameters of the stairs on level 1
1032
- *
1033
- * # Example
1034
- * ```ts
1035
- * const [stair] = await snaptrude.design.query.listStaircases({ isSelected: true })
1036
- * const params = stair ? await snaptrude.design.query.getStaircaseParams(stair) : null
1037
- * if (params) console.log(params.steps, params.riser, params.tread)
1038
- * ```
1039
- */
1040
- public abstract getStaircaseParams(
1041
- staircase: ComponentHandle,
1042
- ): PluginApiReturn<PluginStaircaseParams | null>
1043
-
1044
- /**
1045
- * Read a mass's creation-parameter record — the recipe it was built from
1046
- * (`kind` plus the values that constructor was called with). Present on
1047
- * masses committed from a `core.geom.create` brep constructor that carries a
1048
- * recipe: {@linkcode MassParametersRecord} covers the four primitives
1049
- * (sphere, cylinder, cone, torus), extrusions, revolutions, lofts and sweeps.
1050
- *
1051
- * A fillet, chamfer, shell or offset of a recorded solid keeps the record
1052
- * too: `kind` stays the base constructor and the operation is appended to the
1053
- * record's `operations` list. Edit any of it with `design.update.parameters`.
1054
- *
1055
- * `null` for everything else — drawn masses, imported geometry, booleans,
1056
- * split parts, `brepFromFaces` / `brepFromMesh`, and results built on a
1057
- * source that carried no record — and for a mass whose record no longer
1058
- * matches what is on
1059
- * screen (a later free-form edit dropped the recipe, or the mass carries a
1060
- * baked scale). `null` too when the recipe was not recordable: the record
1061
- * schema has its own caps (a sweep path of at most 512 points, a loft of at
1062
- * most 32 sections, at most 8 chained `operations`, at most 256 curves in one
1063
- * profile loop and 64 holes in one contour), and a mass built past one of
1064
- * them — through a route that does not enforce the matching creator cap —
1065
- * commits as ordinary geometry carrying no record. The `operations` cap is
1066
- * the one an agent can walk into by accident: a 9th chained
1067
- * fillet/chamfer/shell/offset drops the record silently, so the result
1068
- * commits as ordinary geometry and reads back `null` here. A record is never
1069
- * reported unless it is still the truth.
1070
- *
1071
- * Points and axes come back in **world** coordinates. Pure read — nothing is
1072
- * created or modified.
1073
- *
1074
- * @param component - the mass's {@linkcode ComponentHandle}
1075
- * @returns the {@linkcode MassParametersRecord}, or `null`
1076
- *
1077
- * @examplePrompt What radius is this sphere?
1078
- * @examplePrompt Is this mass parametric?
1079
- * @examplePrompt Read back the dome's creation parameters
1080
- * @examplePrompt How tall was this cylinder made?
1081
- *
1082
- * # Example
1083
- * ```ts
1084
- * const [mass] = await snaptrude.design.query.listMasses({ isSelected: true })
1085
- * const params = await snaptrude.design.query.getParameters(mass)
1086
- * if (params?.kind === "sphere") console.log("radius:", params.values.radius)
1087
- * ```
1088
- */
1089
- public abstract getParameters(
1090
- component: ComponentHandle,
1091
- ): PluginApiReturn<MassParametersRecord | null>
1092
-
1093
- /**
1094
- * Get the **union** axis-aligned bounding box enclosing a set of entities.
1095
- *
1096
- * @param components - the set of entity {@linkcode ComponentHandle}s
1097
- * @returns the union {@linkcode BBoxComponents}, or `null` if the set is empty /
1098
- * none resolve
1099
- *
1100
- * @examplePrompt Get the bounding box around the selected objects
1101
- * @examplePrompt What is the overall extent of these walls?
1102
- * @examplePrompt How big is the region these elements cover together?
1103
- * @examplePrompt Find the combined extents of everything on level 2
1104
- *
1105
- * # Example
1106
- * ```ts
1107
- * const walls = await snaptrude.design.query.listWalls()
1108
- * const box = await snaptrude.design.query.getBoundingBox(walls)
1109
- *
1110
- * // Compose with the `within` filter: everything in the selection's extents
1111
- * const selection = await snaptrude.design.selection.get()
1112
- * const extents = await snaptrude.design.query.getBoundingBox(selection)
1113
- * if (extents) {
1114
- * const nearby = await snaptrude.design.query.listEntities({ within: extents })
1115
- * const width = extents.max.x - extents.min.x
1116
- * }
1117
- * ```
1118
- */
1119
- public abstract getBoundingBox(
1120
- components: ComponentHandle[],
1121
- ): PluginApiReturn<BBoxComponents | null>
1122
-
1123
- /**
1124
- * Compute the plan **outline of what is built on a storey** — the union of
1125
- * the storey's wall (by default) footprints as polygons-with-holes. This is
1126
- * the footprint-from-the-built-model query: by the time slabs are needed the
1127
- * walls exist, and the engine's own solids (snapped, joined and mitred at
1128
- * creation) are the cleanest wall network available — no CAD re-tracing.
1129
- *
1130
- * Semantics: the union of wall solids is the wall *material* — a ring-shaped
1131
- * polygon. The **outer ring is the OUTSIDE wall face** (the slab boundary);
1132
- * every enclosed region — rooms AND courtyards — appears as a hole. When the
1133
- * largest hole is ~zero the walls don't enclose anything: the footprint
1134
- * reports `enclosed: false` (an open wall network), never a garbage outline.
1135
- * Detached buildings come back as separate footprints.
1136
- *
1137
- * A read — nothing is created. Feed a footprint's `outline` to
1138
- * `core.geom.create.profileFromLinePoints` → `design.create.slab` to build
1139
- * the floor/roof plate. Coordinates are world-space plan (x, z) in Snaptrude
1140
- * internal units; `areaSq` values are in squared internal units.
1141
- *
1142
- * @param options - storey / included kinds / debris filter; see
1143
- * {@linkcode PluginStoreyOutlineOptions}.
1144
- * @returns the storey's footprints (possibly several for detached buildings)
1145
- * plus pieces discarded by `minArea` (`footprints: []` when nothing matches).
1146
- *
1147
- * @examplePrompt What is the building footprint on the ground floor?
1148
- * @examplePrompt Create a floor slab covering the whole storey
1149
- * @examplePrompt Get the outline of the walls on storey 1
1150
- * @examplePrompt How much area do the ground-floor walls enclose?
1151
- *
1152
- * # Example
1153
- * ```ts
1154
- * const r = await snaptrude.design.query.storeyOutline({ storey: 1 })
1155
- * const main = r.footprints.find((f) => f.enclosed)
1156
- * if (main) {
1157
- * const profile = await snaptrude.core.geom.create.profileFromLinePoints(
1158
- * main.outline.map((p) => ({ x: p.x, y: 0, z: p.z })),
1159
- * )
1160
- * // → design.create.slab with the profile's contour
1161
- * }
1162
- * ```
1163
- */
1164
- public abstract storeyOutline(
1165
- options?: PluginStoreyOutlineOptions,
1166
- ): PluginApiReturn<PluginStoreyOutlineResult>
1167
- }
1168
-
1169
-
1170
- /** A point in the world-space plan (x, z) projection, Snaptrude internal units. */
1171
- export const PluginOutlinePlanPoint = z.object({ x: z.number(), z: z.number() })
1172
- export type PluginOutlinePlanPoint = z.infer<typeof PluginOutlinePlanPoint>
1173
-
1174
- /**
1175
- * Options for {@link PluginDesignQueryApi.storeyOutline}.
1176
- *
1177
- * | Property | Type | Description |
1178
- * |---|---|---|
1179
- * | `storey` | `number`? | Only components on this storey (default: all storeys) |
1180
- * | `include` | {@link PluginEntityType}`[]`? | Component kinds to union (default `["wall"]`) |
1181
- * | `minArea` | `number`? | Drop union pieces below this plan area, squared internal units (default `1`) |
1182
- */
1183
- export const PluginStoreyOutlineOptions = z.object({
1184
- storey: z.number().int().optional(),
1185
- include: z.array(PluginEntityType).nonempty().default(["wall"]),
1186
- minArea: z.number().nonnegative().default(1),
1187
- })
1188
- export type PluginStoreyOutlineOptions = z.input<
1189
- typeof PluginStoreyOutlineOptions
1190
- >
1191
-
1192
- /**
1193
- * One connected footprint from {@link PluginDesignQueryApi.storeyOutline}.
1194
- * `outline` traces the OUTSIDE wall face; `holes` are the enclosed regions
1195
- * (rooms and courtyards). `enclosed` is `false` when the wall network does not
1196
- * close around any region (open C-shape) — don't slab an unenclosed outline
1197
- * without checking it.
1198
- */
1199
- export const PluginStoreyFootprint = z.object({
1200
- outline: z.array(PluginOutlinePlanPoint),
1201
- holes: z.array(
1202
- z.object({
1203
- ring: z.array(PluginOutlinePlanPoint),
1204
- areaSq: z.number().nonnegative(),
1205
- }),
1206
- ),
1207
- areaSq: z.number().nonnegative(),
1208
- enclosed: z.boolean(),
1209
- })
1210
- export type PluginStoreyFootprint = z.infer<typeof PluginStoreyFootprint>
1211
-
1212
- /** Result of {@link PluginDesignQueryApi.storeyOutline}. */
1213
- export const PluginStoreyOutlineResult = z.object({
1214
- footprints: z.array(PluginStoreyFootprint),
1215
- discarded: z.array(
1216
- z.object({ areaSq: z.number().nonnegative(), reason: z.string() }),
1217
- ),
1218
- })
1219
- export type PluginStoreyOutlineResult = z.infer<
1220
- typeof PluginStoreyOutlineResult
1221
- >
1222
-
1223
- export * from "./geometry"
1224
- export * from "./spaces"
1225
- export * from "./referenceLines"
1226
- export * from "./grids"
1
+ import * as z from "zod"
2
+ import { PluginApiReturn } from "../../../types"
3
+ import { ComponentHandle, BBoxComponents } from "../../../handles"
4
+ import type { MassParametersRecord } from "../../../massParameters"
5
+ import {
6
+ PluginSpaceType,
7
+ PluginMassType,
8
+ PluginDepartmentId,
9
+ PluginAreaClass,
10
+ } from "../../entity/space"
11
+ import { PluginStaircasePreset } from "../create"
12
+ import { PluginDesignQueryGeometryApi } from "./geometry"
13
+ import { PluginDesignQuerySpacesApi } from "./spaces"
14
+ import { PluginDesignQueryReferenceLinesApi } from "./referenceLines"
15
+ import { PluginDesignQueryGridsApi } from "./grids"
16
+
17
+ // ---------------------------------------------------------------------------
18
+ // Entity type vocabulary
19
+ // ---------------------------------------------------------------------------
20
+
21
+ /**
22
+ * The kind of a BIM entity — the discovery/filter vocabulary for the whole
23
+ * `design.query.*` surface. Values are clean `lowerCamel` tokens; the host maps
24
+ * each to the engine's internal type (whose raw strings are case-inconsistent —
25
+ * `"roof"`/`"slab"` lowercase, `"Wall"`/`"Door"` PascalCase — never surfaced here).
26
+ *
27
+ * | Value | Entity |
28
+ * |---|---|
29
+ * | `"wall"` | Wall |
30
+ * | `"space"` | Space (a room mass) |
31
+ * | `"mass"` | Generic mass (non-room) |
32
+ * | `"door"` | Door |
33
+ * | `"window"` | Window |
34
+ * | `"floor"` | Floor |
35
+ * | `"slab"` | Slab |
36
+ * | `"roof"` | Roof |
37
+ * | `"column"` | Column |
38
+ * | `"beam"` | Beam |
39
+ * | `"ceiling"` | Ceiling |
40
+ * | `"staircase"` | Staircase |
41
+ * | `"furniture"` | Furniture |
42
+ * | `"referenceLine"` | Reference line |
43
+ * | `"curtainWall"` | Parametric curtain wall |
44
+ * | `"mullion"` | Curtain-wall mullion |
45
+ * | `"panel"` | Curtain-wall panel |
46
+ * | `"parametricComponent"` | A placed parametric family instance (native or Revit-imported). Its geometry is rebuilt from `design.family` parameters, so read and drive it through `design.family.getParameters` / `setParameters` rather than editing its meshes. |
47
+ * | `"grid"` | Structural grid group |
48
+ */
49
+ export const PluginEntityType = z.enum([
50
+ "wall",
51
+ "space",
52
+ "mass",
53
+ "door",
54
+ "window",
55
+ "floor",
56
+ "slab",
57
+ "roof",
58
+ "column",
59
+ "beam",
60
+ "ceiling",
61
+ "staircase",
62
+ "furniture",
63
+ "referenceLine",
64
+ "curtainWall",
65
+ "mullion",
66
+ "panel",
67
+ "parametricComponent",
68
+ "grid",
69
+ ])
70
+ export type PluginEntityType = z.infer<typeof PluginEntityType>
71
+
72
+ // ---------------------------------------------------------------------------
73
+ // The shared filter — one options object, every list* method
74
+ // ---------------------------------------------------------------------------
75
+
76
+ /**
77
+ * `EntityFilterOptions` — the **single filter** shared by every `design.query.list*`
78
+ * method, so filtering is identical across all BIM object types. Every field is
79
+ * optional and **AND-combined**: a component must satisfy all provided fields.
80
+ * Plural fields are arrays (match any of); singular fields are scalars. A field
81
+ * that names a type-specific dimension (`massTypes`/`spaceTypes`/`departmentId`)
82
+ * excludes components that lack that dimension.
83
+ *
84
+ * | Field | Type | Keeps components where |
85
+ * |---|---|---|
86
+ * | `types` | {@linkcode PluginEntityType}`[]` | entity kind ∈ `types` (ignored by per-type `list*` — the method fixes the kind) |
87
+ * | `storeys` | `number[]` | storey/floor level ∈ `storeys` (negative = basement) |
88
+ * | `buildingIds` | `string[]` | building ∈ `buildingIds` |
89
+ * | `structureIds` | `string[]` | structure ∈ `structureIds` |
90
+ * | `label` | `string` | label (`room_type` ?? mesh label) === `label` |
91
+ * | `massTypes` | {@linkcode PluginMassType}`[]` | mass-type ∈ (masses only) |
92
+ * | `spaceTypes` | {@linkcode PluginSpaceType}`[]` | space-type ∈ (spaces only) |
93
+ * | `departmentId` | {@linkcode PluginDepartmentId} | department === |
94
+ * | `isSelected` | `boolean` | current selection state === |
95
+ * | `isLocked` | `boolean` | lock flag === |
96
+ * | `isHidden` | `boolean` | user-hidden flag === |
97
+ * | `within` | {@linkcode BBoxComponents} | the entity's bounding box intersects the region |
98
+ * | `overlaps` | {@linkcode ComponentHandle} | the entity's plan footprint overlaps the given space's bottom-face footprint — non-zero intersection area, so fully-inside, boundary-nested, and partially-overhanging entities all match; edge-touching with zero interior overlap does not. Exact polygon intersection (holes respected). Tested in plan — storey height is ignored; compose with `storeys` to scope a floor. Hidden entities are excluded by default — pass `isHidden` explicitly to override. The reference space itself is never returned. Throws on an unknown id. Entities without a brep footprint use their bounding-box plan rectangle. |
99
+ * | `ids` | {@linkcode ComponentHandle}`[]` | id ∈ `ids` (restrict to a known set) |
100
+ */
101
+ export const PluginEntityFilter = z.object({
102
+ types: z.array(PluginEntityType).optional(),
103
+ storeys: z.array(z.number()).optional(),
104
+ buildingIds: z.array(z.string()).optional(),
105
+ structureIds: z.array(z.string()).optional(),
106
+ label: z.string().optional(),
107
+ massTypes: z.array(PluginMassType).optional(),
108
+ spaceTypes: z.array(PluginSpaceType).optional(),
109
+ departmentId: PluginDepartmentId.optional(),
110
+ isSelected: z.boolean().optional(),
111
+ isLocked: z.boolean().optional(),
112
+ isHidden: z.boolean().optional(),
113
+ within: BBoxComponents.optional(),
114
+ overlaps: ComponentHandle.optional(),
115
+ ids: z.array(ComponentHandle).optional(),
116
+ })
117
+ export type PluginEntityFilter = z.infer<typeof PluginEntityFilter>
118
+
119
+ /**
120
+ * Arguments for {@linkcode PluginDesignQueryApi.listByType} — a required entity
121
+ * `type` plus every {@linkcode PluginEntityFilter} field.
122
+ */
123
+ export const PluginDesignQueryListByTypeArgs = PluginEntityFilter.extend({
124
+ type: PluginEntityType,
125
+ })
126
+ export type PluginDesignQueryListByTypeArgs = z.infer<
127
+ typeof PluginDesignQueryListByTypeArgs
128
+ >
129
+
130
+ // ---------------------------------------------------------------------------
131
+ // Single-entity argument shapes
132
+ // ---------------------------------------------------------------------------
133
+
134
+ /** Arguments for methods that read one entity by its {@linkcode ComponentHandle}. */
135
+ export const PluginDesignQueryComponentArgs = z.object({
136
+ component: ComponentHandle,
137
+ })
138
+ export type PluginDesignQueryComponentArgs = z.infer<
139
+ typeof PluginDesignQueryComponentArgs
140
+ >
141
+
142
+ /** Arguments for methods that look an entity up by id. */
143
+ export const PluginDesignQueryIdArgs = z.object({
144
+ id: ComponentHandle,
145
+ })
146
+ export type PluginDesignQueryIdArgs = z.infer<typeof PluginDesignQueryIdArgs>
147
+
148
+ /** Arguments for methods over a set of entities (e.g. the union bounding box). */
149
+ export const PluginDesignQueryComponentsArgs = z.object({
150
+ components: z.array(ComponentHandle),
151
+ })
152
+ export type PluginDesignQueryComponentsArgs = z.infer<
153
+ typeof PluginDesignQueryComponentsArgs
154
+ >
155
+
156
+ // ---------------------------------------------------------------------------
157
+ // Read records
158
+ // ---------------------------------------------------------------------------
159
+
160
+ /**
161
+ * The common property record returned by {@linkcode PluginDesignQueryApi.getProperties} —
162
+ * a **partial** object (a field is present only when it applies to the entity).
163
+ * Type-specific and geometry reads live on {@linkcode PluginDesignQueryApi.measure}
164
+ * and `design.query.geometry.*`, keeping this record generic across every type.
165
+ *
166
+ * | Property | Type | Description |
167
+ * |---|---|---|
168
+ * | `id` | `string` | The entity id |
169
+ * | `type` | {@linkcode PluginEntityType} | Entity kind |
170
+ * | `label` | `string` | `room_type` ?? mesh label ?? `""` |
171
+ * | `storey` | `number` | Storey/floor level |
172
+ * | `buildingId` | `string` | Owning building |
173
+ * | `structureId` | `string` | Owning structure |
174
+ * | `massType` | `string` | Mass-type (masses only) |
175
+ * | `spaceType` | {@linkcode PluginSpaceType} | Space-type (spaces only) |
176
+ * | `areaClass` | {@linkcode PluginAreaClass} | Area classification (spaces only) |
177
+ * | `departmentId` | `string` | Department (masses only) |
178
+ * | `buildingType` | `string` | Assigned building type NAME (walls / slabs / floors / roofs / ceilings only) — the Object Properties panel's Wall/Slab/Floor/Roof/Ceiling Type dropdown value, matching the names from `design.types.list(kind)` |
179
+ * | `isLocked` | `boolean` | Lock flag |
180
+ * | `isHidden` | `boolean` | User-hidden flag |
181
+ * | `isSelected` | `boolean` | Current selection state |
182
+ * | `boundingBox` | {@linkcode BBoxComponents} | World-space AABB |
183
+ * | `adjacency` | `{ spaceId: string; value: number }[]` | Adjacency relationships (spaces/masses only). `value`: `2` = Direct (required), `1` = Indirect or physical contact, `-1` = Restricted; `0` entries are filtered out. `[]` when adjacency has never been computed — see `program.adjacency.compute`. |
184
+ */
185
+ export const PluginEntityProperties = z
186
+ .object({
187
+ id: z.string(),
188
+ type: PluginEntityType,
189
+ label: z.string(),
190
+ storey: z.number(),
191
+ buildingId: z.string(),
192
+ structureId: z.string(),
193
+ massType: z.string(),
194
+ spaceType: PluginSpaceType,
195
+ areaClass: PluginAreaClass,
196
+ departmentId: z.string(),
197
+ buildingType: z.string(),
198
+ isLocked: z.boolean(),
199
+ isHidden: z.boolean(),
200
+ isSelected: z.boolean(),
201
+ boundingBox: BBoxComponents,
202
+ adjacency: z.array(z.object({ spaceId: z.string(), value: z.number() })),
203
+ })
204
+ .partial()
205
+ export type PluginEntityProperties = z.infer<typeof PluginEntityProperties>
206
+
207
+ /**
208
+ * The multi-metric bundle returned by {@linkcode PluginDesignQueryApi.measure}.
209
+ * `boundingBox` is always present; the numeric metrics appear only when the
210
+ * entity's type computes them (area: floors/masses/slabs/roofs/beams/columns;
211
+ * volume: all; length: walls/beams).
212
+ *
213
+ * `thickness` and `height` are the Object Properties panel's dimension reads
214
+ * (the same engine source `design.update.wall` / `design.update.slab` etc.
215
+ * write through), in engine units. `thickness`: walls (the plan thickness —
216
+ * present only for straight orthogonal walls, exactly when the panel shows it)
217
+ * and slabs/floors/roofs/ceilings (the vertical extent the panel calls
218
+ * Thickness). `height`: walls only.
219
+ *
220
+ * | Property | Type | Description |
221
+ * |---|---|---|
222
+ * | `area` | `number`? | Bottom-face area |
223
+ * | `volume` | `number`? | Solid volume |
224
+ * | `length` | `number`? | Running length (linear elements) |
225
+ * | `thickness` | `number`? | Panel Thickness — walls (straight orthogonal only) and the slab family |
226
+ * | `height` | `number`? | Panel Height — walls only |
227
+ * | `boundingBox` | {@linkcode BBoxComponents} | World-space AABB (always present) |
228
+ */
229
+ export const PluginEntityMeasurements = z.object({
230
+ area: z.number().optional(),
231
+ volume: z.number().optional(),
232
+ length: z.number().optional(),
233
+ thickness: z.number().optional(),
234
+ height: z.number().optional(),
235
+ boundingBox: BBoxComponents,
236
+ })
237
+ export type PluginEntityMeasurements = z.infer<typeof PluginEntityMeasurements>
238
+
239
+ /**
240
+ * The parametric properties of a staircase — the same editable fields the
241
+ * staircase properties panel exposes. Returned by
242
+ * {@linkcode PluginDesignQueryApi.getStaircaseParams}; the write side is
243
+ * `design.update.staircase`. Engine internals (block schema, min well size,
244
+ * square-well padding) are deliberately excluded.
245
+ *
246
+ * All dimensions are in **engine units** (the same convention as
247
+ * `design.doors.getWidth`); `steps` is a unitless count. The invariant
248
+ * `staircaseHeight = steps × riser` is governed by the two lock booleans — see
249
+ * `design.update.staircase`. Type-specific fields (`wellSize`, `landingWidth`,
250
+ * `flightStartDistAfterTurn`) are present only when the preset uses them.
251
+ *
252
+ * | Property | Type | Description |
253
+ * |---|---|---|
254
+ * | `staircaseType` | {@linkcode PluginStaircasePreset} | Base staircase type |
255
+ * | `staircasePreset` | {@linkcode PluginStaircasePreset} | Named parametric preset |
256
+ * | `staircaseHeight` | `number` | Total rise (engine units) |
257
+ * | `steps` | `number` | Step count (unitless) |
258
+ * | `riser` | `number` | Riser height (engine units) |
259
+ * | `tread` | `number` | Tread depth (engine units) |
260
+ * | `width` | `number` | Flight width (engine units) |
261
+ * | `depth` | `number` | Slab/waist thickness (engine units) |
262
+ * | `wellSize` | `number`? | Stairwell gap (dogLegged / openWell / square) |
263
+ * | `landingWidth` | `number`? | Landing depth (straight / dogLegged) |
264
+ * | `flightStartDistAfterTurn` | `number`? | Flight offset after a turn (dogLegged / lShaped / square) |
265
+ * | `baseOffset` | `number` | Vertical offset from the storey base (engine units) |
266
+ * | `isStoreyHeightUnlocked` | `boolean` | When `false`, height is tied to the storey height |
267
+ * | `isStaircaseHeightUnlocked` | `boolean` | When `false`, height recomputes from steps × riser |
268
+ * | `storey` | `number` | Storey/floor level the staircase sits on |
269
+ */
270
+ export const PluginStaircaseParams = z.object({
271
+ staircaseType: PluginStaircasePreset,
272
+ staircasePreset: PluginStaircasePreset,
273
+ staircaseHeight: z.number(),
274
+ steps: z.number(),
275
+ riser: z.number(),
276
+ tread: z.number(),
277
+ width: z.number(),
278
+ depth: z.number(),
279
+ wellSize: z.number().optional(),
280
+ landingWidth: z.number().optional(),
281
+ flightStartDistAfterTurn: z.number().optional(),
282
+ baseOffset: z.number(),
283
+ isStoreyHeightUnlocked: z.boolean(),
284
+ isStaircaseHeightUnlocked: z.boolean(),
285
+ storey: z.number(),
286
+ })
287
+ export type PluginStaircaseParams = z.infer<typeof PluginStaircaseParams>
288
+
289
+ // ---------------------------------------------------------------------------
290
+ // The query API
291
+ // ---------------------------------------------------------------------------
292
+
293
+ /**
294
+ * `design.query.*` — read-only enumeration + inspection of scene-committed BIM
295
+ * entities (all-handle model, §11). No query mutates the scene.
296
+ *
297
+ * Every `list*` returns a snapshot {@linkcode ComponentHandle}`[]` (`[]`, never
298
+ * `null`) filtered by the shared {@linkcode PluginEntityFilter}; per-entity reads
299
+ * take a {@linkcode ComponentHandle} and return primitive records / child handles
300
+ * or `null` when the entity is gone. Resolve details of a returned handle with
301
+ * {@linkcode PluginDesignQueryApi.getProperties} / {@linkcode PluginDesignQueryApi.measure}
302
+ * / `design.query.geometry.*`.
303
+ *
304
+ * Accessed via `snaptrude.design.query`.
305
+ */
306
+ export abstract class PluginDesignQueryApi {
307
+ /** Entity-geometry reads (brep, bottom contour). See {@linkcode PluginDesignQueryGeometryApi}. */
308
+ public abstract geometry: PluginDesignQueryGeometryApi
309
+ /** Space-specific reads (footprint, …). See {@linkcode PluginDesignQuerySpacesApi}. */
310
+ public abstract spaces: PluginDesignQuerySpacesApi
311
+ /** Reference-line-specific reads. See {@linkcode PluginDesignQueryReferenceLinesApi}. */
312
+ public abstract referenceLines: PluginDesignQueryReferenceLinesApi
313
+ /** Structural-grid-specific reads. See {@linkcode PluginDesignQueryGridsApi}. */
314
+ public abstract grids: PluginDesignQueryGridsApi
315
+
316
+ constructor() {}
317
+
318
+ // ── enumeration ───────────────────────────────────────────────────────────
319
+
320
+ /**
321
+ * List **all** BIM entities in the project matching the filter.
322
+ *
323
+ * @param filter - an optional {@linkcode PluginEntityFilter}
324
+ * @returns matching entities as {@linkcode ComponentHandle}`[]` (`[]` if none)
325
+ *
326
+ * @examplePrompt Show me everything in the model
327
+ * @examplePrompt How many objects are on the ground floor?
328
+ * @examplePrompt List all the doors and windows on level 2
329
+ * @examplePrompt What elements are currently selected?
330
+ * @examplePrompt Find every hidden object in the project
331
+ *
332
+ * # Example
333
+ * ```ts
334
+ * // Doors and windows on the ground storey (fields AND-combine)
335
+ * const doorsAndWindows = await snaptrude.design.query.listEntities({
336
+ * types: ["door", "window"],
337
+ * storeys: [1],
338
+ * })
339
+ *
340
+ * // Everything the user currently has selected
341
+ * const selected = await snaptrude.design.query.listEntities({ isSelected: true })
342
+ *
343
+ * // Everything inside a world-space region (axis-aligned box, Snaptrude units)
344
+ * const inRegion = await snaptrude.design.query.listEntities({
345
+ * within: { min: { x: 0, y: 0, z: 0 }, max: { x: 10, y: 4, z: 8 } },
346
+ * })
347
+ * ```
348
+ */
349
+ public abstract listEntities(
350
+ filter?: PluginEntityFilter,
351
+ ): PluginApiReturn<ComponentHandle[]>
352
+
353
+ /**
354
+ * List all entities of one BIM object type — wall, door, slab, panel, and so
355
+ * on — optionally narrowed by the shared filter.
356
+ *
357
+ * `"mass"` includes voids; a mass whose `design.query.getHost` is a slab,
358
+ * floor, roof or ceiling is a void (made by `design.create.void`).
359
+ *
360
+ * @param type - the {@linkcode PluginEntityType} to list
361
+ * @param filter - an optional {@linkcode PluginEntityFilter}
362
+ * @returns matching entities as {@linkcode ComponentHandle}`[]`
363
+ *
364
+ * @examplePrompt List all the curtain walls in the project
365
+ * @examplePrompt Show every curtain wall panel on this storey
366
+ * @examplePrompt Get all the elements of a single type, like columns
367
+ * @examplePrompt Find all the locked slabs in the model
368
+ *
369
+ * # Example
370
+ * ```ts
371
+ * const lockedSlabs = await snaptrude.design.query.listByType("slab", { isLocked: true })
372
+ *
373
+ * // Curtain-wall panels on storeys 2-3 of one building
374
+ * const panels = await snaptrude.design.query.listByType("panel", {
375
+ * storeys: [2, 3],
376
+ * buildingIds: ["bld_1"],
377
+ * })
378
+ * ```
379
+ */
380
+ public abstract listByType(
381
+ type: PluginEntityType,
382
+ filter?: PluginEntityFilter,
383
+ ): PluginApiReturn<ComponentHandle[]>
384
+
385
+ /**
386
+ * List all the walls in the project, optionally filtered by storey, building,
387
+ * label, selection state, or region.
388
+ *
389
+ * The `types` field of the filter is ignored (this method fixes the kind).
390
+ *
391
+ * @param filter - an optional {@linkcode PluginEntityFilter}
392
+ * @returns wall {@linkcode ComponentHandle}`[]`
393
+ *
394
+ * @examplePrompt How many walls are in the project?
395
+ * @examplePrompt List all the walls on the ground floor
396
+ * @examplePrompt Which walls are currently selected?
397
+ * @examplePrompt Find the walls on level 2 of building A
398
+ * @examplePrompt Show me every hidden wall
399
+ *
400
+ * # Example
401
+ * ```ts
402
+ * const groundFloorWalls = await snaptrude.design.query.listWalls({ storeys: [1] })
403
+ * ```
404
+ */
405
+ public abstract listWalls(
406
+ filter?: PluginEntityFilter,
407
+ ): PluginApiReturn<ComponentHandle[]>
408
+
409
+ /**
410
+ * List all the spaces (rooms) in the project, optionally filtered by storey,
411
+ * department, space type, label, selection state, or region.
412
+ *
413
+ * @param filter - an optional {@linkcode PluginEntityFilter}
414
+ * @returns space {@linkcode ComponentHandle}`[]`
415
+ *
416
+ * @examplePrompt How many rooms are in this project?
417
+ * @examplePrompt List all the spaces on the second storey
418
+ * @examplePrompt Which rooms are in the Bedrooms department?
419
+ * @examplePrompt Find every room labelled Kitchen
420
+ * @examplePrompt Show the spaces that are currently selected
421
+ *
422
+ * # Example
423
+ * ```ts
424
+ * // Balconies and decks on storey 2 (`label` is an exact match on room_type)
425
+ * const outdoor = await snaptrude.design.query.listSpaces({
426
+ * storeys: [2],
427
+ * spaceTypes: ["Balcony", "Deck"],
428
+ * })
429
+ *
430
+ * // Rooms in a department: resolve its id first, then filter and total the area
431
+ * const { departments } = await snaptrude.program.departments.list()
432
+ * const bedrooms = departments.find((d) => d.name === "Bedrooms")
433
+ * if (bedrooms) {
434
+ * const rooms = await snaptrude.design.query.listSpaces({ departmentId: bedrooms.id })
435
+ * let total = 0
436
+ * for (const room of rooms) {
437
+ * const m = await snaptrude.design.query.measure(room)
438
+ * total += m?.area ?? 0
439
+ * }
440
+ * }
441
+ * ```
442
+ */
443
+ public abstract listSpaces(
444
+ filter?: PluginEntityFilter,
445
+ ): PluginApiReturn<ComponentHandle[]>
446
+
447
+ /**
448
+ * List all the generic masses (non-room massing volumes) in the project,
449
+ * optionally filtered by storey, building, label, mass type, or region.
450
+ *
451
+ * Includes voids; a mass whose `design.query.getHost` is a slab, floor, roof
452
+ * or ceiling is a void (made by `design.create.void`).
453
+ *
454
+ * @param filter - an optional {@linkcode PluginEntityFilter}
455
+ * @returns mass {@linkcode ComponentHandle}`[]`
456
+ *
457
+ * @examplePrompt List all the masses in the model
458
+ * @examplePrompt How many massing blocks are on the site?
459
+ * @examplePrompt Show the generic masses on level 1
460
+ * @examplePrompt Find every mass that isn't a room
461
+ *
462
+ * # Example
463
+ * ```ts
464
+ * // Only certain mass kinds — massTypes matches any of the given values
465
+ * const massing = await snaptrude.design.query.listMasses({
466
+ * massTypes: ["Generic mass", "Plinth"],
467
+ * })
468
+ * ```
469
+ */
470
+ public abstract listMasses(
471
+ filter?: PluginEntityFilter,
472
+ ): PluginApiReturn<ComponentHandle[]>
473
+
474
+ /**
475
+ * List all the doors in the project, optionally filtered by storey, building,
476
+ * selection state, or region.
477
+ *
478
+ * @param filter - an optional {@linkcode PluginEntityFilter}
479
+ * @returns door {@linkcode ComponentHandle}`[]`
480
+ *
481
+ * @examplePrompt How many doors are in the project?
482
+ * @examplePrompt List all the doors on the ground floor
483
+ * @examplePrompt Which doors are currently selected?
484
+ * @examplePrompt Find every door in building B
485
+ *
486
+ * # Example
487
+ * ```ts
488
+ * // Ground-floor doors, then read each one's width via design.doors.*
489
+ * const doors = await snaptrude.design.query.listDoors({ storeys: [1] })
490
+ * for (const door of doors) {
491
+ * const width = await snaptrude.design.doors.getWidth(door)
492
+ * }
493
+ * ```
494
+ */
495
+ public abstract listDoors(
496
+ filter?: PluginEntityFilter,
497
+ ): PluginApiReturn<ComponentHandle[]>
498
+
499
+ /**
500
+ * List all the windows in the project, optionally filtered by storey,
501
+ * building, selection state, or region.
502
+ *
503
+ * @param filter - an optional {@linkcode PluginEntityFilter}
504
+ * @returns window {@linkcode ComponentHandle}`[]`
505
+ *
506
+ * @examplePrompt How many windows does this building have?
507
+ * @examplePrompt List every window on level 3
508
+ * @examplePrompt Which windows are on the ground floor?
509
+ * @examplePrompt Show all the windows that are currently selected
510
+ *
511
+ * # Example
512
+ * ```ts
513
+ * const level3Windows = await snaptrude.design.query.listWindows({ storeys: [3] })
514
+ * ```
515
+ */
516
+ public abstract listWindows(
517
+ filter?: PluginEntityFilter,
518
+ ): PluginApiReturn<ComponentHandle[]>
519
+
520
+ /**
521
+ * List all the floor elements (floor plates / finishes) in the project,
522
+ * optionally filtered by storey, building, lock state, or region.
523
+ *
524
+ * @param filter - an optional {@linkcode PluginEntityFilter}
525
+ * @returns floor {@linkcode ComponentHandle}`[]`
526
+ *
527
+ * @examplePrompt List all the floor elements in the model
528
+ * @examplePrompt How many floor plates are on the second storey?
529
+ * @examplePrompt Which floors are locked?
530
+ * @examplePrompt Show the floor finishes on level 0
531
+ *
532
+ * # Example
533
+ * ```ts
534
+ * const lockedFloors = await snaptrude.design.query.listFloors({ isLocked: true })
535
+ * ```
536
+ */
537
+ public abstract listFloors(
538
+ filter?: PluginEntityFilter,
539
+ ): PluginApiReturn<ComponentHandle[]>
540
+
541
+ /**
542
+ * List all the slabs in the project, optionally filtered by storey, building,
543
+ * lock state, or region.
544
+ *
545
+ * @param filter - an optional {@linkcode PluginEntityFilter}
546
+ * @returns slab {@linkcode ComponentHandle}`[]`
547
+ *
548
+ * @examplePrompt List all the slabs in the project
549
+ * @examplePrompt How many slabs are on level 2?
550
+ * @examplePrompt Which slabs are locked?
551
+ * @examplePrompt Show the slabs in the selected building
552
+ *
553
+ * # Example
554
+ * ```ts
555
+ * const storey2Slabs = await snaptrude.design.query.listSlabs({ storeys: [2] })
556
+ * ```
557
+ */
558
+ public abstract listSlabs(
559
+ filter?: PluginEntityFilter,
560
+ ): PluginApiReturn<ComponentHandle[]>
561
+
562
+ /**
563
+ * List all the roofs in the project, optionally filtered by storey, building,
564
+ * visibility, or region.
565
+ *
566
+ * @param filter - an optional {@linkcode PluginEntityFilter}
567
+ * @returns roof {@linkcode ComponentHandle}`[]`
568
+ *
569
+ * @examplePrompt How many roofs does the model have?
570
+ * @examplePrompt List all the roof elements
571
+ * @examplePrompt Show the roofs on the top storey
572
+ * @examplePrompt Find every hidden roof
573
+ *
574
+ * # Example
575
+ * ```ts
576
+ * const hiddenRoofs = await snaptrude.design.query.listRoofs({ isHidden: true })
577
+ * ```
578
+ */
579
+ public abstract listRoofs(
580
+ filter?: PluginEntityFilter,
581
+ ): PluginApiReturn<ComponentHandle[]>
582
+
583
+ /**
584
+ * List all the columns in the project, optionally filtered by storey,
585
+ * building, selection state, or region.
586
+ *
587
+ * @param filter - an optional {@linkcode PluginEntityFilter}
588
+ * @returns column {@linkcode ComponentHandle}`[]`
589
+ *
590
+ * @examplePrompt How many columns are on the ground floor?
591
+ * @examplePrompt List all the columns in building A
592
+ * @examplePrompt Show every column on level 3
593
+ * @examplePrompt Which columns are currently selected?
594
+ *
595
+ * # Example
596
+ * ```ts
597
+ * const groundFloorColumns = await snaptrude.design.query.listColumns({ storeys: [1] })
598
+ * ```
599
+ */
600
+ public abstract listColumns(
601
+ filter?: PluginEntityFilter,
602
+ ): PluginApiReturn<ComponentHandle[]>
603
+
604
+ /**
605
+ * List all the beams in the project, optionally filtered by storey, building,
606
+ * lock state, or region.
607
+ *
608
+ * @param filter - an optional {@linkcode PluginEntityFilter}
609
+ * @returns beam {@linkcode ComponentHandle}`[]`
610
+ *
611
+ * @examplePrompt List all the beams in the project
612
+ * @examplePrompt How many beams are on the second storey?
613
+ * @examplePrompt Show the beams in this structure
614
+ * @examplePrompt Find every locked beam
615
+ *
616
+ * # Example
617
+ * ```ts
618
+ * const storey2Beams = await snaptrude.design.query.listBeams({ storeys: [2] })
619
+ * ```
620
+ */
621
+ public abstract listBeams(
622
+ filter?: PluginEntityFilter,
623
+ ): PluginApiReturn<ComponentHandle[]>
624
+
625
+ /**
626
+ * List all the ceilings in the project, optionally filtered by storey,
627
+ * building, visibility, or region.
628
+ *
629
+ * @param filter - an optional {@linkcode PluginEntityFilter}
630
+ * @returns ceiling {@linkcode ComponentHandle}`[]`
631
+ *
632
+ * @examplePrompt List all the ceilings in the model
633
+ * @examplePrompt How many ceilings are on level 1?
634
+ * @examplePrompt Which ceilings are hidden?
635
+ * @examplePrompt Show the ceilings in building B
636
+ *
637
+ * # Example
638
+ * ```ts
639
+ * const hiddenCeilings = await snaptrude.design.query.listCeilings({ isHidden: true })
640
+ * ```
641
+ */
642
+ public abstract listCeilings(
643
+ filter?: PluginEntityFilter,
644
+ ): PluginApiReturn<ComponentHandle[]>
645
+
646
+ /**
647
+ * List all the staircases in the project, optionally filtered by storey,
648
+ * building, selection state, or region.
649
+ *
650
+ * @param filter - an optional {@linkcode PluginEntityFilter}
651
+ * @returns staircase {@linkcode ComponentHandle}`[]`
652
+ *
653
+ * @examplePrompt How many staircases are in the building?
654
+ * @examplePrompt List all the stairs in the project
655
+ * @examplePrompt Show the staircases on the ground floor
656
+ * @examplePrompt Which stairs are currently selected?
657
+ *
658
+ * # Example
659
+ * ```ts
660
+ * const groundFloorStairs = await snaptrude.design.query.listStaircases({ storeys: [1] })
661
+ * ```
662
+ */
663
+ public abstract listStaircases(
664
+ filter?: PluginEntityFilter,
665
+ ): PluginApiReturn<ComponentHandle[]>
666
+
667
+ /**
668
+ * List all the furniture in the project, optionally filtered by storey,
669
+ * building, label, selection state, or region.
670
+ *
671
+ * @param filter - an optional {@linkcode PluginEntityFilter}
672
+ * @returns furniture {@linkcode ComponentHandle}`[]`
673
+ *
674
+ * @examplePrompt List all the furniture in the project
675
+ * @examplePrompt How many furniture items are on this floor?
676
+ * @examplePrompt Show the furniture inside the selected room
677
+ * @examplePrompt Which furniture is on storey 2?
678
+ *
679
+ * # Example
680
+ * ```ts
681
+ * // Furniture whose plan footprint overlaps a room (`overlaps` is plan-only —
682
+ * // scope the storey too, or furniture stacked on other floors matches)
683
+ * const [kitchen] = await snaptrude.design.query.listSpaces({ label: "Kitchen" })
684
+ * if (kitchen) {
685
+ * const inKitchen = await snaptrude.design.query.listFurniture({
686
+ * overlaps: kitchen,
687
+ * storeys: [1],
688
+ * })
689
+ * }
690
+ * ```
691
+ */
692
+ public abstract listFurniture(
693
+ filter?: PluginEntityFilter,
694
+ ): PluginApiReturn<ComponentHandle[]>
695
+
696
+ /**
697
+ * List all the reference lines in the project, optionally filtered by storey,
698
+ * building, or region.
699
+ *
700
+ * @param filter - an optional {@linkcode PluginEntityFilter}
701
+ * @returns reference-line {@linkcode ComponentHandle}`[]`
702
+ *
703
+ * @examplePrompt List all the reference lines in the model
704
+ * @examplePrompt Show the guide lines on the ground floor
705
+ * @examplePrompt How many reference lines are on this storey?
706
+ * @examplePrompt Find the setting-out lines in building A
707
+ *
708
+ * # Example
709
+ * ```ts
710
+ * const groundFloorRefLines = await snaptrude.design.query.listReferenceLines({ storeys: [1] })
711
+ * ```
712
+ */
713
+ public abstract listReferenceLines(
714
+ filter?: PluginEntityFilter,
715
+ ): PluginApiReturn<ComponentHandle[]>
716
+
717
+ /**
718
+ * List all the curtain-wall mullions in the project, optionally filtered by
719
+ * storey, building, selection state, or region.
720
+ *
721
+ * @param filter - an optional {@linkcode PluginEntityFilter}
722
+ * @returns mullion {@linkcode ComponentHandle}`[]`
723
+ *
724
+ * @examplePrompt List all the mullions in the curtain wall
725
+ * @examplePrompt How many mullions are on the facade?
726
+ * @examplePrompt Show every curtain wall mullion on level 5
727
+ * @examplePrompt Which mullions are currently selected?
728
+ *
729
+ * # Example
730
+ * ```ts
731
+ * const level5Mullions = await snaptrude.design.query.listMullions({ storeys: [5] })
732
+ * ```
733
+ */
734
+ public abstract listMullions(
735
+ filter?: PluginEntityFilter,
736
+ ): PluginApiReturn<ComponentHandle[]>
737
+
738
+ /**
739
+ * List all the structural grids (grid groups) in the project, optionally
740
+ * filtered by storey, building, selection state, or region.
741
+ *
742
+ * @param filter - an optional {@linkcode PluginEntityFilter}
743
+ * @returns grid-group {@linkcode ComponentHandle}`[]`
744
+ *
745
+ * @examplePrompt List all the structural grids in the model
746
+ * @examplePrompt How many grids are on the ground floor?
747
+ * @examplePrompt Show the grids on this storey
748
+ * @examplePrompt Which grids are currently selected?
749
+ *
750
+ * # Example
751
+ * ```ts
752
+ * const groundFloorGrids = await snaptrude.design.query.listGrids({ storeys: [1] })
753
+ * ```
754
+ */
755
+ public abstract listGrids(
756
+ filter?: PluginEntityFilter,
757
+ ): PluginApiReturn<ComponentHandle[]>
758
+
759
+ // ── single-entity access & identity ─────────────────────────────────────────
760
+
761
+ /**
762
+ * Look an entity up by id — returns the same handle if it still resolves live,
763
+ * else `null`. A cheap validity probe (distinct from {@linkcode PluginDesignQueryApi.exists}).
764
+ *
765
+ * @param id - the entity id ({@linkcode ComponentHandle}) to look up
766
+ * @returns the {@linkcode ComponentHandle}, or `null` if it no longer resolves
767
+ *
768
+ * @examplePrompt Look up an object by its id
769
+ * @examplePrompt Does id cmp_42 still point to something in the model?
770
+ * @examplePrompt Fetch the element with this id
771
+ * @examplePrompt Check if this saved element id is still valid
772
+ *
773
+ * # Example
774
+ * ```ts
775
+ * // Persistence round-trip: save a ref now, revalidate it in a later run
776
+ * const [wall] = await snaptrude.design.query.listWalls()
777
+ * const savedRef = await snaptrude.design.query.getEntityRef(wall)
778
+ *
779
+ * // ...later (savedRef restored from plugin storage)...
780
+ * const stillThere = await snaptrude.design.query.getById(savedRef)
781
+ * if (stillThere) {
782
+ * const props = await snaptrude.design.query.getProperties(stillThere)
783
+ * }
784
+ * ```
785
+ */
786
+ public abstract getById(
787
+ id: ComponentHandle,
788
+ ): PluginApiReturn<ComponentHandle | null>
789
+
790
+ /**
791
+ * Test whether an entity id resolves to a live scene entity.
792
+ * @param id - the entity id ({@linkcode ComponentHandle}) to look up
793
+ * @returns `true` if it exists, otherwise `false`
794
+ *
795
+ * @examplePrompt Does this element still exist in the scene?
796
+ * @examplePrompt Check whether the wall I stored earlier is still there
797
+ * @examplePrompt Is id cmp_42 still valid?
798
+ * @examplePrompt Verify the object wasn't deleted
799
+ *
800
+ * # Example
801
+ * ```ts
802
+ * // Guard before acting on a handle that may have been deleted or undone
803
+ * if (await snaptrude.design.query.exists(wall)) {
804
+ * const dx = await snaptrude.core.math.vec3.new(5, 0, 0)
805
+ * await snaptrude.design.transform.move([wall], dx)
806
+ * }
807
+ * ```
808
+ */
809
+ public abstract exists(id: ComponentHandle): PluginApiReturn<boolean>
810
+
811
+ /**
812
+ * Get the kind of BIM entity a handle refers to — wall, door, space, slab,
813
+ * and so on (its {@linkcode PluginEntityType}).
814
+ * @param component - the entity's {@linkcode ComponentHandle}
815
+ * @returns the kind, or `null` if the entity is gone / its type is unknown
816
+ *
817
+ * @examplePrompt What kind of element is this?
818
+ * @examplePrompt Is the selected object a wall or a mass?
819
+ * @examplePrompt Tell me the type of this component
820
+ * @examplePrompt What am I looking at — a door, a window, or a panel?
821
+ *
822
+ * # Example
823
+ * ```ts
824
+ * // Dispatch on what the user selected
825
+ * const selection = await snaptrude.design.selection.get()
826
+ * for (const component of selection) {
827
+ * const kind = await snaptrude.design.query.getEntityType(component)
828
+ * if (kind === "wall") {
829
+ * // wall-specific handling
830
+ * } else if (kind === "space") {
831
+ * // room-specific handling
832
+ * }
833
+ * }
834
+ * ```
835
+ */
836
+ public abstract getEntityType(
837
+ component: ComponentHandle,
838
+ ): PluginApiReturn<PluginEntityType | null>
839
+
840
+ /**
841
+ * Get an entity's human label (`room_type` for spaces/masses, else the mesh
842
+ * label; `""` when unlabelled). The write side is `design.update` (`setLabel`).
843
+ * @param component - the entity's {@linkcode ComponentHandle}
844
+ * @returns the label string
845
+ *
846
+ * @examplePrompt What is this room called?
847
+ * @examplePrompt Get the name of the selected object
848
+ * @examplePrompt Show the label on this wall
849
+ * @examplePrompt What's the room type of this space?
850
+ *
851
+ * # Example
852
+ * ```ts
853
+ * // Fuzzy label search (the `label` filter field is exact-match; for
854
+ * // case-insensitive or partial matches, read labels and compare yourself)
855
+ * const spaces = await snaptrude.design.query.listSpaces()
856
+ * const kitchens: typeof spaces = []
857
+ * for (const space of spaces) {
858
+ * const label = await snaptrude.design.query.getLabel(space)
859
+ * if (label.toLowerCase().includes("kitchen")) kitchens.push(space)
860
+ * }
861
+ * ```
862
+ */
863
+ public abstract getLabel(component: ComponentHandle): PluginApiReturn<string>
864
+
865
+ /**
866
+ * Get a serializable, persistable reference to an entity. Under the all-handle
867
+ * model a {@linkcode ComponentHandle} already IS the ref (raw `Component.id`),
868
+ * so this is the identity passthrough — provided for API completeness.
869
+ * @param component - the entity's {@linkcode ComponentHandle}
870
+ * @returns the entity's {@linkcode ComponentHandle}
871
+ *
872
+ * @examplePrompt Give me a reference I can save for this element
873
+ * @examplePrompt Get a persistent id for the selected wall
874
+ * @examplePrompt Store a handle to this room so I can find it later
875
+ *
876
+ * # Example
877
+ * ```ts
878
+ * const [wall] = await snaptrude.design.query.listWalls()
879
+ * const savedRef = await snaptrude.design.query.getEntityRef(wall)
880
+ * // savedRef is a plain string — persist it, then revalidate in a later run
881
+ * // with snaptrude.design.query.getById(savedRef)
882
+ * ```
883
+ */
884
+ public abstract getEntityRef(
885
+ component: ComponentHandle,
886
+ ): PluginApiReturn<ComponentHandle>
887
+
888
+ /**
889
+ * List an entity's direct child entities — for example the doors and windows
890
+ * hosted in a wall, or the voids (holes, shafts, openings) cut in a slab,
891
+ * floor, roof or ceiling by `design.create.void`.
892
+ * @param component - the entity's {@linkcode ComponentHandle}
893
+ * @returns child {@linkcode ComponentHandle}`[]` (`[]` if none)
894
+ *
895
+ * @examplePrompt What's inside this wall — list its doors and windows
896
+ * @examplePrompt Show the child elements of the selected object
897
+ * @examplePrompt Which openings belong to this wall?
898
+ * @examplePrompt List everything nested under this mass
899
+ * @examplePrompt List all holes in this slab
900
+ *
901
+ * # Example
902
+ * ```ts
903
+ * // Count the openings hosted in a wall, split by kind
904
+ * const children = await snaptrude.design.query.listChildren(wall)
905
+ * let doors = 0
906
+ * let windows = 0
907
+ * for (const child of children) {
908
+ * const kind = await snaptrude.design.query.getEntityType(child)
909
+ * if (kind === "door") doors++
910
+ * if (kind === "window") windows++
911
+ * }
912
+ * ```
913
+ */
914
+ public abstract listChildren(
915
+ component: ComponentHandle,
916
+ ): PluginApiReturn<ComponentHandle[]>
917
+
918
+ /**
919
+ * Get an entity's host/parent entity (e.g. the wall a door is cut into, or
920
+ * the slab, floor, roof or ceiling a void is cut through).
921
+ * @param component - the entity's {@linkcode ComponentHandle}
922
+ * @returns the host {@linkcode ComponentHandle}, or `null` if top-level
923
+ *
924
+ * @examplePrompt Which wall is this door in?
925
+ * @examplePrompt Find the parent of the selected window
926
+ * @examplePrompt What is this element attached to?
927
+ * @examplePrompt Which wall hosts this window?
928
+ * @examplePrompt Which slab is this shaft in?
929
+ *
930
+ * # Example
931
+ * ```ts
932
+ * // From a door up to its host wall, then measure the wall
933
+ * const [door] = await snaptrude.design.query.listDoors({ isSelected: true })
934
+ * const hostWall = door ? await snaptrude.design.query.getHost(door) : null
935
+ * if (hostWall) {
936
+ * const m = await snaptrude.design.query.measure(hostWall)
937
+ * console.log("wall length:", m?.length)
938
+ * }
939
+ * ```
940
+ */
941
+ public abstract getHost(
942
+ component: ComponentHandle,
943
+ ): PluginApiReturn<ComponentHandle | null>
944
+
945
+ // ── property & metric reads ─────────────────────────────────────────────────
946
+
947
+ /**
948
+ * Read an entity's common properties as a {@linkcode PluginEntityProperties}
949
+ * record (only applicable fields are present). For spaces/masses this includes
950
+ * `adjacency` — the per-entity adjacency read (bulk form:
951
+ * `program.adjacency.getMatrix`). For walls and the slab family (slab / floor
952
+ * / roof / ceiling) this includes `buildingType` — the assigned building type
953
+ * NAME the panel's type dropdown shows, matching `design.types.list(kind)`.
954
+ *
955
+ * @param component - the entity's {@linkcode ComponentHandle}
956
+ * @returns the property record
957
+ *
958
+ * @examplePrompt Show me all the details of the selected object
959
+ * @examplePrompt What storey is this wall on?
960
+ * @examplePrompt Which wall type is assigned to this wall?
961
+ * @examplePrompt Which department does this room belong to?
962
+ * @examplePrompt Is this element locked or hidden?
963
+ * @examplePrompt Get the adjacencies of this space
964
+ *
965
+ * # Example
966
+ * ```ts
967
+ * const [wall] = await snaptrude.design.query.listWalls()
968
+ * const props = await snaptrude.design.query.getProperties(wall)
969
+ * console.log(props.type, props.storey, props.boundingBox)
970
+ *
971
+ * // The record is partial — type-specific fields exist only where they apply
972
+ * const [room] = await snaptrude.design.query.listSpaces()
973
+ * const p = await snaptrude.design.query.getProperties(room)
974
+ * if (p.spaceType === "Room" && p.areaClass === "NET") {
975
+ * for (const adj of p.adjacency ?? []) {
976
+ * // adj.value: 2 = direct, 1 = indirect/contact, -1 = restricted
977
+ * console.log(adj.spaceId, adj.value)
978
+ * }
979
+ * }
980
+ * ```
981
+ */
982
+ public abstract getProperties(
983
+ component: ComponentHandle,
984
+ ): PluginApiReturn<PluginEntityProperties>
985
+
986
+ /**
987
+ * Measure an entity — read its area, volume, and/or running length (whichever
988
+ * its type supports) together with its world-space bounding box in one call.
989
+ *
990
+ * For walls and the slab family this also carries the Object Properties
991
+ * panel's dimension reads in engine units: `thickness` (walls — straight
992
+ * orthogonal only, exactly when the panel shows it; slabs / floors / roofs /
993
+ * ceilings — the vertical extent the panel calls Thickness) and `height`
994
+ * (walls). These are the read side of `design.update.wall` /
995
+ * `design.update.slab` / `.floor` / `.roof` / `.ceiling`.
996
+ *
997
+ * @param component - the entity's {@linkcode ComponentHandle}
998
+ * @returns a {@linkcode PluginEntityMeasurements}, or `null` if the entity is gone
999
+ *
1000
+ * @examplePrompt What's the area of this room?
1001
+ * @examplePrompt How long is the selected wall?
1002
+ * @examplePrompt How thick is this wall?
1003
+ * @examplePrompt Get the thickness of the slab on level 2
1004
+ * @examplePrompt Get the volume of this mass
1005
+ * @examplePrompt Give me the dimensions of the selected element
1006
+ * @examplePrompt What's the floor area of the kitchen?
1007
+ *
1008
+ * # Example
1009
+ * ```ts
1010
+ * const [space] = await snaptrude.design.query.listSpaces()
1011
+ * const m = await snaptrude.design.query.measure(space)
1012
+ * console.log(m?.area, m?.volume)
1013
+ *
1014
+ * // wall dimensions — the panel's Thickness / Height, in engine units
1015
+ * const [wall] = await snaptrude.design.query.listWalls()
1016
+ * const wm = await snaptrude.design.query.measure(wall)
1017
+ * console.log(wm?.thickness, wm?.height)
1018
+ * ```
1019
+ */
1020
+ public abstract measure(
1021
+ component: ComponentHandle,
1022
+ ): PluginApiReturn<PluginEntityMeasurements | null>
1023
+
1024
+ /**
1025
+ * Read a staircase's parametric properties — its preset, dimensions, step
1026
+ * count, base offset, and height-lock state — as a
1027
+ * {@linkcode PluginStaircaseParams} record. The paired write is
1028
+ * `design.update.staircase`.
1029
+ *
1030
+ * All dimensions are in **engine units** (the same convention as
1031
+ * `design.doors.getWidth`); `steps` is a unitless count. A read — never
1032
+ * throws: returns `null` for a handle that is not a staircase or no longer
1033
+ * resolves.
1034
+ *
1035
+ * @param staircase - the staircase's {@linkcode ComponentHandle}
1036
+ * @returns the {@linkcode PluginStaircaseParams} record, or `null`
1037
+ *
1038
+ * @examplePrompt What's the riser height of this staircase?
1039
+ * @examplePrompt How many steps does the selected stair have?
1040
+ * @examplePrompt Show me the tread and width of this staircase
1041
+ * @examplePrompt Read all the parameters of the stairs on level 1
1042
+ *
1043
+ * # Example
1044
+ * ```ts
1045
+ * const [stair] = await snaptrude.design.query.listStaircases({ isSelected: true })
1046
+ * const params = stair ? await snaptrude.design.query.getStaircaseParams(stair) : null
1047
+ * if (params) console.log(params.steps, params.riser, params.tread)
1048
+ * ```
1049
+ */
1050
+ public abstract getStaircaseParams(
1051
+ staircase: ComponentHandle,
1052
+ ): PluginApiReturn<PluginStaircaseParams | null>
1053
+
1054
+ /**
1055
+ * Read a mass's creation-parameter record — the recipe it was built from
1056
+ * (`kind` plus the values that constructor was called with). Present on
1057
+ * masses committed from a `core.geom.create` brep constructor that carries a
1058
+ * recipe: {@linkcode MassParametersRecord} covers the four primitives
1059
+ * (sphere, cylinder, cone, torus), extrusions, revolutions, lofts and sweeps.
1060
+ *
1061
+ * A fillet, chamfer, shell or offset of a recorded solid keeps the record
1062
+ * too: `kind` stays the base constructor and the operation is appended to the
1063
+ * record's `operations` list. Edit any of it with `design.update.parameters`.
1064
+ *
1065
+ * `null` for everything else — drawn masses, imported geometry, booleans,
1066
+ * split parts, `brepFromFaces` / `brepFromMesh`, and results built on a
1067
+ * source that carried no record — and for a mass whose record no longer
1068
+ * matches what is on
1069
+ * screen (a later free-form edit dropped the recipe, or the mass carries a
1070
+ * baked scale). `null` too when the recipe was not recordable: the record
1071
+ * schema has its own caps (a sweep path of at most 512 points, a loft of at
1072
+ * most 32 sections, at most 8 chained `operations`, at most 256 curves in one
1073
+ * profile loop and 64 holes in one contour), and a mass built past one of
1074
+ * them — through a route that does not enforce the matching creator cap —
1075
+ * commits as ordinary geometry carrying no record. The `operations` cap is
1076
+ * the one an agent can walk into by accident: a 9th chained
1077
+ * fillet/chamfer/shell/offset drops the record silently, so the result
1078
+ * commits as ordinary geometry and reads back `null` here. A record is never
1079
+ * reported unless it is still the truth.
1080
+ *
1081
+ * Points and axes come back in **world** coordinates. Pure read — nothing is
1082
+ * created or modified.
1083
+ *
1084
+ * @param component - the mass's {@linkcode ComponentHandle}
1085
+ * @returns the {@linkcode MassParametersRecord}, or `null`
1086
+ *
1087
+ * @examplePrompt What radius is this sphere?
1088
+ * @examplePrompt Is this mass parametric?
1089
+ * @examplePrompt Read back the dome's creation parameters
1090
+ * @examplePrompt How tall was this cylinder made?
1091
+ *
1092
+ * # Example
1093
+ * ```ts
1094
+ * const [mass] = await snaptrude.design.query.listMasses({ isSelected: true })
1095
+ * const params = await snaptrude.design.query.getParameters(mass)
1096
+ * if (params?.kind === "sphere") console.log("radius:", params.values.radius)
1097
+ * ```
1098
+ */
1099
+ public abstract getParameters(
1100
+ component: ComponentHandle,
1101
+ ): PluginApiReturn<MassParametersRecord | null>
1102
+
1103
+ /**
1104
+ * Get the **union** axis-aligned bounding box enclosing a set of entities.
1105
+ *
1106
+ * @param components - the set of entity {@linkcode ComponentHandle}s
1107
+ * @returns the union {@linkcode BBoxComponents}, or `null` if the set is empty /
1108
+ * none resolve
1109
+ *
1110
+ * @examplePrompt Get the bounding box around the selected objects
1111
+ * @examplePrompt What is the overall extent of these walls?
1112
+ * @examplePrompt How big is the region these elements cover together?
1113
+ * @examplePrompt Find the combined extents of everything on level 2
1114
+ *
1115
+ * # Example
1116
+ * ```ts
1117
+ * const walls = await snaptrude.design.query.listWalls()
1118
+ * const box = await snaptrude.design.query.getBoundingBox(walls)
1119
+ *
1120
+ * // Compose with the `within` filter: everything in the selection's extents
1121
+ * const selection = await snaptrude.design.selection.get()
1122
+ * const extents = await snaptrude.design.query.getBoundingBox(selection)
1123
+ * if (extents) {
1124
+ * const nearby = await snaptrude.design.query.listEntities({ within: extents })
1125
+ * const width = extents.max.x - extents.min.x
1126
+ * }
1127
+ * ```
1128
+ */
1129
+ public abstract getBoundingBox(
1130
+ components: ComponentHandle[],
1131
+ ): PluginApiReturn<BBoxComponents | null>
1132
+
1133
+ /**
1134
+ * Compute the plan **outline of what is built on a storey** — the union of
1135
+ * the storey's wall (by default) footprints as polygons-with-holes. This is
1136
+ * the footprint-from-the-built-model query: by the time slabs are needed the
1137
+ * walls exist, and the engine's own solids (snapped, joined and mitred at
1138
+ * creation) are the cleanest wall network available — no CAD re-tracing.
1139
+ *
1140
+ * Semantics: the union of wall solids is the wall *material* — a ring-shaped
1141
+ * polygon. The **outer ring is the OUTSIDE wall face** (the slab boundary);
1142
+ * every enclosed region — rooms AND courtyards — appears as a hole. When the
1143
+ * largest hole is ~zero the walls don't enclose anything: the footprint
1144
+ * reports `enclosed: false` (an open wall network), never a garbage outline.
1145
+ * Detached buildings come back as separate footprints.
1146
+ *
1147
+ * A read — nothing is created. Feed a footprint's `outline` to
1148
+ * `core.geom.create.profileFromLinePoints` → `design.create.slab` to build
1149
+ * the floor/roof plate. Coordinates are world-space plan (x, z) in Snaptrude
1150
+ * internal units; `areaSq` values are in squared internal units.
1151
+ *
1152
+ * @param options - storey / included kinds / debris filter; see
1153
+ * {@linkcode PluginStoreyOutlineOptions}.
1154
+ * @returns the storey's footprints (possibly several for detached buildings)
1155
+ * plus pieces discarded by `minArea` (`footprints: []` when nothing matches).
1156
+ *
1157
+ * @examplePrompt What is the building footprint on the ground floor?
1158
+ * @examplePrompt Create a floor slab covering the whole storey
1159
+ * @examplePrompt Get the outline of the walls on storey 1
1160
+ * @examplePrompt How much area do the ground-floor walls enclose?
1161
+ *
1162
+ * # Example
1163
+ * ```ts
1164
+ * const r = await snaptrude.design.query.storeyOutline({ storey: 1 })
1165
+ * const main = r.footprints.find((f) => f.enclosed)
1166
+ * if (main) {
1167
+ * const profile = await snaptrude.core.geom.create.profileFromLinePoints(
1168
+ * main.outline.map((p) => ({ x: p.x, y: 0, z: p.z })),
1169
+ * )
1170
+ * // → design.create.slab with the profile's contour
1171
+ * }
1172
+ * ```
1173
+ */
1174
+ public abstract storeyOutline(
1175
+ options?: PluginStoreyOutlineOptions,
1176
+ ): PluginApiReturn<PluginStoreyOutlineResult>
1177
+ }
1178
+
1179
+
1180
+ /** A point in the world-space plan (x, z) projection, Snaptrude internal units. */
1181
+ export const PluginOutlinePlanPoint = z.object({ x: z.number(), z: z.number() })
1182
+ export type PluginOutlinePlanPoint = z.infer<typeof PluginOutlinePlanPoint>
1183
+
1184
+ /**
1185
+ * Options for {@link PluginDesignQueryApi.storeyOutline}.
1186
+ *
1187
+ * | Property | Type | Description |
1188
+ * |---|---|---|
1189
+ * | `storey` | `number`? | Only components on this storey (default: all storeys) |
1190
+ * | `include` | {@link PluginEntityType}`[]`? | Component kinds to union (default `["wall"]`) |
1191
+ * | `minArea` | `number`? | Drop union pieces below this plan area, squared internal units (default `1`) |
1192
+ */
1193
+ export const PluginStoreyOutlineOptions = z.object({
1194
+ storey: z.number().int().optional(),
1195
+ include: z.array(PluginEntityType).nonempty().default(["wall"]),
1196
+ minArea: z.number().nonnegative().default(1),
1197
+ })
1198
+ export type PluginStoreyOutlineOptions = z.input<
1199
+ typeof PluginStoreyOutlineOptions
1200
+ >
1201
+
1202
+ /**
1203
+ * One connected footprint from {@link PluginDesignQueryApi.storeyOutline}.
1204
+ * `outline` traces the OUTSIDE wall face; `holes` are the enclosed regions
1205
+ * (rooms and courtyards). `enclosed` is `false` when the wall network does not
1206
+ * close around any region (open C-shape) — don't slab an unenclosed outline
1207
+ * without checking it.
1208
+ */
1209
+ export const PluginStoreyFootprint = z.object({
1210
+ outline: z.array(PluginOutlinePlanPoint),
1211
+ holes: z.array(
1212
+ z.object({
1213
+ ring: z.array(PluginOutlinePlanPoint),
1214
+ areaSq: z.number().nonnegative(),
1215
+ }),
1216
+ ),
1217
+ areaSq: z.number().nonnegative(),
1218
+ enclosed: z.boolean(),
1219
+ })
1220
+ export type PluginStoreyFootprint = z.infer<typeof PluginStoreyFootprint>
1221
+
1222
+ /** Result of {@link PluginDesignQueryApi.storeyOutline}. */
1223
+ export const PluginStoreyOutlineResult = z.object({
1224
+ footprints: z.array(PluginStoreyFootprint),
1225
+ discarded: z.array(
1226
+ z.object({ areaSq: z.number().nonnegative(), reason: z.string() }),
1227
+ ),
1228
+ })
1229
+ export type PluginStoreyOutlineResult = z.infer<
1230
+ typeof PluginStoreyOutlineResult
1231
+ >
1232
+
1233
+ export * from "./geometry"
1234
+ export * from "./spaces"
1235
+ export * from "./referenceLines"
1236
+ export * from "./grids"