@snaptrude/plugin-core 0.9.6 → 0.9.7
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.
- package/CHANGELOG.md +14 -0
- package/api-manifest.full.json +8442 -0
- package/api-manifest.json +162 -6
- package/dist/api/core/io/import/index.d.ts +3 -1
- package/dist/api/core/io/import/index.d.ts.map +1 -1
- package/dist/api/design/create/bulk-items.d.ts +177 -0
- package/dist/api/design/create/bulk-items.d.ts.map +1 -0
- package/dist/api/design/create/index.d.ts +273 -8
- package/dist/api/design/create/index.d.ts.map +1 -1
- package/dist/api/design/create/opening-fields.d.ts +29 -0
- package/dist/api/design/create/opening-fields.d.ts.map +1 -0
- package/dist/api/design/delete/index.d.ts +4 -0
- package/dist/api/design/delete/index.d.ts.map +1 -1
- package/dist/api/design/dimensions.d.ts +427 -0
- package/dist/api/design/dimensions.d.ts.map +1 -0
- package/dist/api/design/index.d.ts +5 -0
- package/dist/api/design/index.d.ts.map +1 -1
- package/dist/api/presentation/annotate.d.ts +2 -2
- package/dist/api/presentation/shapes.d.ts +2 -2
- package/dist/handles.d.ts +19 -0
- package/dist/handles.d.ts.map +1 -1
- package/dist/index.cjs +2006 -1867
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1983 -1867
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/api/core/io/import/index.ts +11 -3
- package/src/api/design/create/bulk-items.ts +182 -0
- package/src/api/design/create/index.ts +296 -18
- package/src/api/design/create/opening-fields.ts +29 -0
- package/src/api/design/delete/index.ts +4 -0
- package/src/api/design/dimensions.ts +453 -0
- package/src/api/design/index.ts +5 -0
- package/src/handles.ts +24 -0
package/package.json
CHANGED
|
@@ -183,7 +183,10 @@ export abstract class PluginCoreIoImportApi {
|
|
|
183
183
|
* if (cad) console.log("DWG imported as CAD underlay:", cad)
|
|
184
184
|
* ```
|
|
185
185
|
*/
|
|
186
|
-
public abstract dwg(
|
|
186
|
+
public abstract dwg(
|
|
187
|
+
source: string,
|
|
188
|
+
storey?: number,
|
|
189
|
+
): PluginApiReturn<ImportJobHandle>
|
|
187
190
|
|
|
188
191
|
/**
|
|
189
192
|
* Sketch a CAD underlay from **already-parsed CAD JSON** — synchronous, with no
|
|
@@ -214,7 +217,10 @@ export abstract class PluginCoreIoImportApi {
|
|
|
214
217
|
* )
|
|
215
218
|
* ```
|
|
216
219
|
*/
|
|
217
|
-
public abstract cadJson(
|
|
220
|
+
public abstract cadJson(
|
|
221
|
+
cad: CadJsonInput,
|
|
222
|
+
storey?: number,
|
|
223
|
+
): PluginApiReturn<UnderlayHandle>
|
|
218
224
|
|
|
219
225
|
/**
|
|
220
226
|
* Import a **3D model** file and place it in the scene as a component.
|
|
@@ -239,7 +245,9 @@ export abstract class PluginCoreIoImportApi {
|
|
|
239
245
|
* @returns the placed {@linkcode ComponentHandle}.
|
|
240
246
|
* @throws if writes are disabled, the source can't be loaded, the format is
|
|
241
247
|
* unsupported, conversion/placement fails, or the converted model cannot be
|
|
242
|
-
* found in the library after upload
|
|
248
|
+
* found in the library after upload; `PRECONDITION_FAILED`
|
|
249
|
+
* (`details.engineCode: "TOOL_ACTIVE"`) while the interactive furniture tool is
|
|
250
|
+
* active — finish or cancel it first.
|
|
243
251
|
*
|
|
244
252
|
* @examplePrompt Import this SketchUp model onto storey 1
|
|
245
253
|
* @examplePrompt Bring in this OBJ file and place it at the origin
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import * as z from "zod"
|
|
2
|
+
import { ContourHandle, ProfileHandle, Vec3Handle } from "../../../handles"
|
|
3
|
+
import { PluginOpeningBaseOptions } from "./opening-fields"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Maximum items per bulk `design.create.*` call. A bounded request keeps
|
|
7
|
+
* validation, command capture and rollback work finite; split a larger import
|
|
8
|
+
* into separate awaited batches, each with its own undo entry.
|
|
9
|
+
*/
|
|
10
|
+
export const PLUGIN_CREATE_BATCH_LIMIT = 1000
|
|
11
|
+
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// wallRuns
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* One wall run to create via {@linkcode PluginDesignCreateApi.wallRuns} — the
|
|
18
|
+
* same inputs as {@linkcode PluginDesignCreateApi.walls}, one run per item.
|
|
19
|
+
*
|
|
20
|
+
* | Property | Type | Description |
|
|
21
|
+
* |---|---|---|
|
|
22
|
+
* | `profile` | {@linkcode ProfileHandle} | Ordered curve chain (wall centerlines) |
|
|
23
|
+
* | `height` | `number`? | Wall height (Snaptrude units, > 0; default engine default) |
|
|
24
|
+
* | `thickness` | `number`? | Wall thickness (Snaptrude units, > 0; default: `wallType`'s total layer thickness when given, else engine default) |
|
|
25
|
+
* | `wallType` | `string`? | Wall type name from `design.types.list("wall")` — layers/material/thickness defaults (default generic) |
|
|
26
|
+
* | `storey` | `number`? (int) | Target storey (must exist) — the run's walls are assigned + lifted to its base in the same undo entry (default: geometry-derived) |
|
|
27
|
+
*/
|
|
28
|
+
export const PluginCreateWallRunItem = z.object({
|
|
29
|
+
profile: ProfileHandle,
|
|
30
|
+
height: z.number().optional(),
|
|
31
|
+
thickness: z.number().optional(),
|
|
32
|
+
wallType: z.string().min(1).optional(),
|
|
33
|
+
storey: z.number().int().optional(),
|
|
34
|
+
})
|
|
35
|
+
export type PluginCreateWallRunItem = z.infer<typeof PluginCreateWallRunItem>
|
|
36
|
+
|
|
37
|
+
/** The `items` array of {@linkcode PluginDesignCreateApi.wallRuns} (1…1000). */
|
|
38
|
+
export const PluginCreateWallRunItems = z
|
|
39
|
+
.array(PluginCreateWallRunItem)
|
|
40
|
+
.min(1)
|
|
41
|
+
.max(PLUGIN_CREATE_BATCH_LIMIT)
|
|
42
|
+
export type PluginCreateWallRunItems = z.infer<typeof PluginCreateWallRunItems>
|
|
43
|
+
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// floors
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* One floor to create via {@linkcode PluginDesignCreateApi.floors} — the same
|
|
50
|
+
* footprint-extrude inputs as {@linkcode PluginDesignCreateApi.floor}.
|
|
51
|
+
*
|
|
52
|
+
* | Property | Type | Description |
|
|
53
|
+
* |---|---|---|
|
|
54
|
+
* | `contour` | {@linkcode ContourHandle} | Footprint (outer profile + holes) |
|
|
55
|
+
* | `thickness` | `number` | Floor thickness (Snaptrude units, > 0) |
|
|
56
|
+
* | `position` | {@linkcode Vec3Handle}? | Offset from origin (default origin) |
|
|
57
|
+
*/
|
|
58
|
+
export const PluginCreateFloorItem = z.object({
|
|
59
|
+
contour: ContourHandle,
|
|
60
|
+
thickness: z.number(),
|
|
61
|
+
position: Vec3Handle.optional(),
|
|
62
|
+
})
|
|
63
|
+
export type PluginCreateFloorItem = z.infer<typeof PluginCreateFloorItem>
|
|
64
|
+
|
|
65
|
+
/** The `items` array of {@linkcode PluginDesignCreateApi.floors} (1…1000). */
|
|
66
|
+
export const PluginCreateFloorItems = z
|
|
67
|
+
.array(PluginCreateFloorItem)
|
|
68
|
+
.min(1)
|
|
69
|
+
.max(PLUGIN_CREATE_BATCH_LIMIT)
|
|
70
|
+
export type PluginCreateFloorItems = z.infer<typeof PluginCreateFloorItems>
|
|
71
|
+
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
// doors / windows
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* One door to place via {@linkcode PluginDesignCreateApi.doors} — the same
|
|
78
|
+
* fields as a `"door"` {@linkcode PluginDesignCreateOpeningOptions}, so the
|
|
79
|
+
* size overrides singular {@linkcode PluginDesignCreateApi.door} lacks are
|
|
80
|
+
* available per item. `facing` is a WORLD POINT on the side of the wall the
|
|
81
|
+
* door opens into, not a direction. Rejects unknown fields.
|
|
82
|
+
*
|
|
83
|
+
* | Property | Type | Description |
|
|
84
|
+
* |---|---|---|
|
|
85
|
+
* | `catalogId` | `string` | Library id: team `_id` or general `fullName` |
|
|
86
|
+
* | `hostWall` | {@linkcode ComponentHandle} | The wall to host the door |
|
|
87
|
+
* | `position` | {@linkcode Vec3Handle} | World point projected onto the wall |
|
|
88
|
+
* | `facing` | {@linkcode Vec3Handle}? | World point on the side the door faces (default engine-chosen) |
|
|
89
|
+
* | `label` | `string`? | Instance name (optional) |
|
|
90
|
+
* | `width` | `number`? | Width override (Snaptrude units, > 0; default the catalog item's) |
|
|
91
|
+
* | `height` | `number`? | Height override (Snaptrude units, > 0; default the catalog item's) |
|
|
92
|
+
*/
|
|
93
|
+
export const PluginCreateDoorItem = z
|
|
94
|
+
.object({ ...PluginOpeningBaseOptions })
|
|
95
|
+
.strict()
|
|
96
|
+
export type PluginCreateDoorItem = z.infer<typeof PluginCreateDoorItem>
|
|
97
|
+
|
|
98
|
+
/** The `items` array of {@linkcode PluginDesignCreateApi.doors} (1…1000). */
|
|
99
|
+
export const PluginCreateDoorItems = z
|
|
100
|
+
.array(PluginCreateDoorItem)
|
|
101
|
+
.min(1)
|
|
102
|
+
.max(PLUGIN_CREATE_BATCH_LIMIT)
|
|
103
|
+
export type PluginCreateDoorItems = z.infer<typeof PluginCreateDoorItems>
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* One window to place via {@linkcode PluginDesignCreateApi.windows} — the same
|
|
107
|
+
* fields as a `"window"` {@linkcode PluginDesignCreateOpeningOptions}.
|
|
108
|
+
* `sillHeight` is a nonnegative Snaptrude-unit distance from the host wall's
|
|
109
|
+
* base to the BOTTOM of the window, not to its center. `facing` is a WORLD
|
|
110
|
+
* POINT on the desired side of the wall, not a direction. Rejects unknown
|
|
111
|
+
* fields.
|
|
112
|
+
*
|
|
113
|
+
* | Property | Type | Description |
|
|
114
|
+
* |---|---|---|
|
|
115
|
+
* | `catalogId` | `string` | Library id: team `_id` or general `fullName` |
|
|
116
|
+
* | `hostWall` | {@linkcode ComponentHandle} | The wall to host the window |
|
|
117
|
+
* | `position` | {@linkcode Vec3Handle} | World point projected onto the wall |
|
|
118
|
+
* | `facing` | {@linkcode Vec3Handle}? | World point on the side the window faces (default engine-chosen) |
|
|
119
|
+
* | `label` | `string`? | Instance name (optional) |
|
|
120
|
+
* | `width` | `number`? | Width override (Snaptrude units, > 0; default the catalog item's) |
|
|
121
|
+
* | `height` | `number`? | Height override (Snaptrude units, > 0; default the catalog item's) |
|
|
122
|
+
* | `sillHeight` | `number`? | Wall base to window bottom (Snaptrude units, ≥ 0; default the catalog item's) |
|
|
123
|
+
*/
|
|
124
|
+
export const PluginCreateWindowItem = z
|
|
125
|
+
.object({
|
|
126
|
+
...PluginOpeningBaseOptions,
|
|
127
|
+
sillHeight: z.number().finite().nonnegative().optional(),
|
|
128
|
+
})
|
|
129
|
+
.strict()
|
|
130
|
+
export type PluginCreateWindowItem = z.infer<typeof PluginCreateWindowItem>
|
|
131
|
+
|
|
132
|
+
/** The `items` array of {@linkcode PluginDesignCreateApi.windows} (1…1000). */
|
|
133
|
+
export const PluginCreateWindowItems = z
|
|
134
|
+
.array(PluginCreateWindowItem)
|
|
135
|
+
.min(1)
|
|
136
|
+
.max(PLUGIN_CREATE_BATCH_LIMIT)
|
|
137
|
+
export type PluginCreateWindowItems = z.infer<typeof PluginCreateWindowItems>
|
|
138
|
+
|
|
139
|
+
// ---------------------------------------------------------------------------
|
|
140
|
+
// furnitureItems
|
|
141
|
+
// ---------------------------------------------------------------------------
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* One furniture instance to place via
|
|
145
|
+
* {@linkcode PluginDesignCreateApi.furnitureItems} — the same inputs as
|
|
146
|
+
* {@linkcode PluginDesignCreateApi.furniture} minus `createNewSourceMesh`: in a
|
|
147
|
+
* batch the host owns source-mesh persistence (a source is recorded once per
|
|
148
|
+
* call, however many items reuse it).
|
|
149
|
+
*
|
|
150
|
+
* `position.y` is the REST elevation: the item is grounded so its bounding-box
|
|
151
|
+
* base sits exactly at `position.y` (the same surface-flush contract as
|
|
152
|
+
* interactive drag-drop) — pass the floor/storey elevation to stand furniture
|
|
153
|
+
* on it; never add half the item's height yourself.
|
|
154
|
+
*
|
|
155
|
+
* | Property | Type | Description |
|
|
156
|
+
* |---|---|---|
|
|
157
|
+
* | `catalogId` | `string` | Library id: team `_id` or general `fullName` |
|
|
158
|
+
* | `position` | {@linkcode Vec3Handle} | Absolute world placement point (`y` = rest elevation) |
|
|
159
|
+
* | `label` | `string`? | Instance name and readable label (default auto `${name}Ins${n}`) |
|
|
160
|
+
* | `angleInDegrees` | `number`? | Rotation about the vertical axis, in degrees (default unrotated) |
|
|
161
|
+
*/
|
|
162
|
+
export const PluginCreateFurnitureItem = z.object({
|
|
163
|
+
catalogId: z.string().min(1),
|
|
164
|
+
position: Vec3Handle,
|
|
165
|
+
label: z.string().optional(),
|
|
166
|
+
angleInDegrees: z.number().optional(),
|
|
167
|
+
})
|
|
168
|
+
export type PluginCreateFurnitureItem = z.infer<
|
|
169
|
+
typeof PluginCreateFurnitureItem
|
|
170
|
+
>
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* The `items` array of {@linkcode PluginDesignCreateApi.furnitureItems}
|
|
174
|
+
* (1…1000).
|
|
175
|
+
*/
|
|
176
|
+
export const PluginCreateFurnitureItems = z
|
|
177
|
+
.array(PluginCreateFurnitureItem)
|
|
178
|
+
.min(1)
|
|
179
|
+
.max(PLUGIN_CREATE_BATCH_LIMIT)
|
|
180
|
+
export type PluginCreateFurnitureItems = z.infer<
|
|
181
|
+
typeof PluginCreateFurnitureItems
|
|
182
|
+
>
|
|
@@ -18,12 +18,21 @@ import {
|
|
|
18
18
|
PluginBuildableEnvelopeVerticalCap,
|
|
19
19
|
PluginBuildableEnvelopeCreateResult,
|
|
20
20
|
} from "../../entity/buildableEnvelope"
|
|
21
|
+
import { PluginOpeningBaseOptions } from "./opening-fields"
|
|
22
|
+
import type {
|
|
23
|
+
PluginCreateDoorItem,
|
|
24
|
+
PluginCreateFloorItem,
|
|
25
|
+
PluginCreateFurnitureItem,
|
|
26
|
+
PluginCreateWallRunItem,
|
|
27
|
+
PluginCreateWindowItem,
|
|
28
|
+
} from "./bulk-items"
|
|
21
29
|
|
|
22
30
|
/**
|
|
23
31
|
* `design.create.*` — author new scene-committed BIM entities.
|
|
24
32
|
*
|
|
25
33
|
* Every creator takes geometry handles + scalars and returns a
|
|
26
|
-
* {@linkcode ComponentHandle} (or `ComponentHandle[]` for plural creators
|
|
34
|
+
* {@linkcode ComponentHandle} (or `ComponentHandle[]` for plural creators;
|
|
35
|
+
* `wallRuns` returns one `ComponentHandle[]` per run) — the
|
|
27
36
|
* `Component.id` of the created entity, resolvable across the rest of the
|
|
28
37
|
* `design.*` surface. Creation is an undoable host call; it **throws** on failure
|
|
29
38
|
* (no `Result` wrapper — consistent with `core.geom.create.*`).
|
|
@@ -234,6 +243,10 @@ export abstract class PluginDesignCreateApi {
|
|
|
234
243
|
* const contour = await snaptrude.core.geom.create.contourFromProfile(rect)
|
|
235
244
|
* const floor = await snaptrude.design.create.floor(contour, 0.1)
|
|
236
245
|
* ```
|
|
246
|
+
*
|
|
247
|
+
* @performance For MORE THAN ONE floor, call `design.create.floors(items[])` — one
|
|
248
|
+
* host round-trip and ONE undo entry for the whole batch. Looping this single-floor
|
|
249
|
+
* creator is N round-trips and N undo entries.
|
|
237
250
|
*/
|
|
238
251
|
public abstract floor(
|
|
239
252
|
contour: ContourHandle,
|
|
@@ -241,6 +254,46 @@ export abstract class PluginDesignCreateApi {
|
|
|
241
254
|
position?: Vec3Handle,
|
|
242
255
|
): PluginApiReturn<ComponentHandle>
|
|
243
256
|
|
|
257
|
+
/**
|
|
258
|
+
* Create **many floors** in one undoable operation (bulk plural of
|
|
259
|
+
* {@linkcode floor}). Each item extrudes its own footprint contour (outer
|
|
260
|
+
* profile + holes) upward by its own `thickness`, at its own optional
|
|
261
|
+
* position offset. Validate-all-or-throw; one command.
|
|
262
|
+
*
|
|
263
|
+
* @param items - One {@linkcode PluginCreateFloorItem} per floor to create
|
|
264
|
+
* (≥1, ≤1000)
|
|
265
|
+
* @returns the created floors as {@linkcode ComponentHandle}`[]`, in input order
|
|
266
|
+
* @throws `VALIDATION` if the items fail the schema (empty array, more than
|
|
267
|
+
* 1000 items, a non-positive thickness) or a contour is degenerate;
|
|
268
|
+
* `HANDLE_INVALID` if a `contour` or `position` handle is unknown or
|
|
269
|
+
* released; `OPERATION_FAILED` if extrusion fails (nothing is created —
|
|
270
|
+
* `details.itemIndex` is the failing item); `METHOD_NOT_PERMITTED` if the
|
|
271
|
+
* plugin may not write.
|
|
272
|
+
*
|
|
273
|
+
* @examplePrompt Create floors for all these room outlines at once
|
|
274
|
+
* @examplePrompt Add a floor to every space in one operation
|
|
275
|
+
* @examplePrompt Bulk create the floor plates for this building
|
|
276
|
+
* @examplePrompt Lay 100mm floors across these five footprints in one undo step
|
|
277
|
+
* @examplePrompt Create the ground and first floor slabs together
|
|
278
|
+
*
|
|
279
|
+
* @performance Bulk creator — the whole batch is ONE host round-trip and ONE undo
|
|
280
|
+
* entry. Always prefer this over calling `design.create.floor` in a loop: build the
|
|
281
|
+
* full `items[]` array first (all contours and offsets up front), then make one call.
|
|
282
|
+
*
|
|
283
|
+
* # Example
|
|
284
|
+
* ```ts
|
|
285
|
+
* const rect = await snaptrude.core.geom.create.profileRect(5, 4)
|
|
286
|
+
* const contour = await snaptrude.core.geom.create.contourFromProfile(rect)
|
|
287
|
+
* const [ground, upper] = await snaptrude.design.create.floors([
|
|
288
|
+
* { contour, thickness: 0.1 },
|
|
289
|
+
* { contour, thickness: 0.1, position: await snaptrude.core.math.vec3.new(0, 3, 0) },
|
|
290
|
+
* ])
|
|
291
|
+
* ```
|
|
292
|
+
*/
|
|
293
|
+
public abstract floors(
|
|
294
|
+
items: PluginCreateFloorItem[],
|
|
295
|
+
): PluginApiReturn<ComponentHandle[]>
|
|
296
|
+
|
|
244
297
|
/**
|
|
245
298
|
* Create a **roof** by extruding a footprint contour by `thickness`. Created
|
|
246
299
|
* flat (extruded downward); pitch/slope is a separate post-creation edit.
|
|
@@ -422,6 +475,10 @@ export abstract class PluginDesignCreateApi {
|
|
|
422
475
|
* centerlines, 3, undefined, brick.label,
|
|
423
476
|
* )
|
|
424
477
|
* ```
|
|
478
|
+
*
|
|
479
|
+
* @performance For MORE THAN ONE run, call `design.create.wallRuns(items[])` — one
|
|
480
|
+
* host round-trip and ONE undo entry for every run in the batch. Looping this
|
|
481
|
+
* single-run creator is N round-trips and N undo entries.
|
|
425
482
|
*/
|
|
426
483
|
public abstract walls(
|
|
427
484
|
profile: ProfileHandle,
|
|
@@ -431,6 +488,60 @@ export abstract class PluginDesignCreateApi {
|
|
|
431
488
|
storey?: number,
|
|
432
489
|
): PluginApiReturn<ComponentHandle[]>
|
|
433
490
|
|
|
491
|
+
/**
|
|
492
|
+
* Create **many wall runs** in one undoable operation (bulk plural of
|
|
493
|
+
* {@linkcode walls}). Each item is one full run — a wall per curve in that
|
|
494
|
+
* item's profile chain, mitred at shared endpoints. Junctions resolve within a
|
|
495
|
+
* run, not between runs. Each item carries its own
|
|
496
|
+
* dimensions, `wallType` and `storey`, so a single call can build a whole
|
|
497
|
+
* floor plate or several storeys at once. Validate-all-or-throw; one command.
|
|
498
|
+
*
|
|
499
|
+
* @param items - One {@linkcode PluginCreateWallRunItem} per run to create
|
|
500
|
+
* (≥1, ≤1000)
|
|
501
|
+
* @returns one {@linkcode ComponentHandle}`[]` per item, in input order —
|
|
502
|
+
* each inner array holding that run's walls in profile-curve order
|
|
503
|
+
* @throws `VALIDATION` if the items fail the schema (empty array, more than
|
|
504
|
+
* 1000 items, a non-positive height or thickness, a zero-length curve);
|
|
505
|
+
* `HANDLE_INVALID` if a `profile` handle is unknown or released;
|
|
506
|
+
* `PRECONDITION_FAILED` if a profile has no curves, `wallType` names no
|
|
507
|
+
* wall type in the project, or `storey` does not exist;
|
|
508
|
+
* `OPERATION_FAILED` if wall creation fails (nothing is created —
|
|
509
|
+
* `details.itemIndex` is the failing item); `METHOD_NOT_PERMITTED` if the
|
|
510
|
+
* plugin may not write.
|
|
511
|
+
*
|
|
512
|
+
* @examplePrompt Draw all the walls of this floor plan at once
|
|
513
|
+
* @examplePrompt Build every room's perimeter in one operation
|
|
514
|
+
* @examplePrompt Bulk create wall runs from these centerlines
|
|
515
|
+
* @examplePrompt Create the walls for both storeys in a single undo step
|
|
516
|
+
* @examplePrompt Turn these polylines into 200mm brick walls together
|
|
517
|
+
*
|
|
518
|
+
* @performance Bulk creator — the whole batch is ONE host round-trip and ONE undo
|
|
519
|
+
* entry. Always prefer this over calling `design.create.walls` in a loop: build the
|
|
520
|
+
* full `items[]` array first (all profiles up front), then make one call.
|
|
521
|
+
*
|
|
522
|
+
* # Example
|
|
523
|
+
* ```ts
|
|
524
|
+
* const v = snaptrude.core.math.vec3
|
|
525
|
+
* const ground = await snaptrude.core.geom.create.profileFromLinePoints([
|
|
526
|
+
* await v.new(0, 0, 0),
|
|
527
|
+
* await v.new(8, 0, 0),
|
|
528
|
+
* await v.new(8, 0, 6),
|
|
529
|
+
* ])
|
|
530
|
+
* const upper = await snaptrude.core.geom.create.profileFromLinePoints([
|
|
531
|
+
* await v.new(0, 0, 0),
|
|
532
|
+
* await v.new(8, 0, 0),
|
|
533
|
+
* ])
|
|
534
|
+
* const [groundWalls, upperWalls] = await snaptrude.design.create.wallRuns([
|
|
535
|
+
* { profile: ground, height: 3, thickness: 0.2 },
|
|
536
|
+
* { profile: upper, height: 3, storey: 2 },
|
|
537
|
+
* ])
|
|
538
|
+
* console.log(groundWalls.length, "walls on the ground floor")
|
|
539
|
+
* ```
|
|
540
|
+
*/
|
|
541
|
+
public abstract wallRuns(
|
|
542
|
+
items: PluginCreateWallRunItem[],
|
|
543
|
+
): PluginApiReturn<ComponentHandle[][]>
|
|
544
|
+
|
|
434
545
|
/**
|
|
435
546
|
* Create a **staircase** from a parametric preset, placed at a point.
|
|
436
547
|
*
|
|
@@ -535,15 +646,19 @@ export abstract class PluginDesignCreateApi {
|
|
|
535
646
|
* `position.y` (the same surface-flush contract as interactive drag-drop) —
|
|
536
647
|
* pass the floor/storey elevation to stand furniture on it; never add half
|
|
537
648
|
* the item's height yourself.
|
|
538
|
-
* @param options - Optional placement options: `label` — instance name
|
|
539
|
-
* (default auto `${name}Ins${n}`);
|
|
540
|
-
*
|
|
649
|
+
* @param options - Optional placement options: `label` — instance name and
|
|
650
|
+
* readable label (`design.query.getLabel`; default auto `${name}Ins${n}`);
|
|
651
|
+
* `createNewSourceMesh` — legacy flag, kept for compatibility. The host owns
|
|
652
|
+
* source-mesh persistence: a catalog source is recorded exactly once, by the
|
|
653
|
+
* first placement that brings it in, and this flag cannot skip that record.
|
|
541
654
|
* @param angleInDegrees - Optional signed rotation about the vertical axis, in
|
|
542
655
|
* degrees (same convention as {@linkcode PluginDesignTransformApi.rotate}).
|
|
543
656
|
* Applied at creation time so it is part of the placement's single undo entry.
|
|
544
657
|
* Default: the item's own (unrotated) orientation.
|
|
545
658
|
* @returns the {@linkcode ComponentHandle} of the placed furniture instance
|
|
546
|
-
* @throws if the catalog id is unknown, the source mesh fails to load, or placement fails
|
|
659
|
+
* @throws if the catalog id is unknown, the source mesh fails to load, or placement fails;
|
|
660
|
+
* `PRECONDITION_FAILED` (`details.engineCode: "TOOL_ACTIVE"`) while the interactive
|
|
661
|
+
* furniture tool is active — finish or cancel it first
|
|
547
662
|
*
|
|
548
663
|
* @examplePrompt Place a chair from the library at this spot
|
|
549
664
|
* @examplePrompt Add a sofa from the furniture catalog to the living room
|
|
@@ -567,6 +682,10 @@ export abstract class PluginDesignCreateApi {
|
|
|
567
682
|
* 90,
|
|
568
683
|
* )
|
|
569
684
|
* ```
|
|
685
|
+
*
|
|
686
|
+
* @performance For MORE THAN ONE item, call `design.create.furnitureItems(items[])` —
|
|
687
|
+
* one host round-trip and ONE undo entry, and a catalog source shared by several
|
|
688
|
+
* items is fetched once. Looping this single-item creator is N round-trips.
|
|
570
689
|
*/
|
|
571
690
|
public abstract furniture(
|
|
572
691
|
catalogId: string,
|
|
@@ -575,6 +694,52 @@ export abstract class PluginDesignCreateApi {
|
|
|
575
694
|
angleInDegrees?: number,
|
|
576
695
|
): PluginApiReturn<ComponentHandle>
|
|
577
696
|
|
|
697
|
+
/**
|
|
698
|
+
* Place **many furniture items** in one undoable operation (bulk plural of
|
|
699
|
+
* {@linkcode furniture}). Each item names a catalog id and an absolute world
|
|
700
|
+
* position (`position.y` is the REST elevation — the same grounding contract
|
|
701
|
+
* as the singular creator), with optional label and rotation. A catalog
|
|
702
|
+
* source shared by several items is fetched and recorded once for the whole
|
|
703
|
+
* batch, so the host — not the caller — owns source persistence (there is no
|
|
704
|
+
* per-item `createNewSourceMesh`). Validate-all-or-throw; one command.
|
|
705
|
+
*
|
|
706
|
+
* @param items - One {@linkcode PluginCreateFurnitureItem} per instance to
|
|
707
|
+
* place (≥1, ≤1000)
|
|
708
|
+
* @returns the placed instances as {@linkcode ComponentHandle}`[]`, in input order
|
|
709
|
+
* @throws `VALIDATION` if the items fail the schema (empty array, more than
|
|
710
|
+
* 1000 items); `HANDLE_INVALID` if a `position` handle is unknown or
|
|
711
|
+
* released; `PRECONDITION_FAILED` if a catalog id is unknown, a furniture
|
|
712
|
+
* tool is active, or a parametric group is active (exit it first, or place
|
|
713
|
+
* items one at a time with `furniture`); `OPERATION_FAILED` if a source
|
|
714
|
+
* mesh fails to load or placement fails (nothing is created —
|
|
715
|
+
* `details.itemIndex` is the failing item); `METHOD_NOT_PERMITTED` if the plugin may not write.
|
|
716
|
+
*
|
|
717
|
+
* @examplePrompt Place all the desks for this office at once
|
|
718
|
+
* @examplePrompt Furnish every bedroom in one operation
|
|
719
|
+
* @examplePrompt Bulk place these chairs around the table
|
|
720
|
+
* @examplePrompt Add the whole furniture layout in a single undo step
|
|
721
|
+
* @examplePrompt Drop twenty copies of this chair at these positions
|
|
722
|
+
*
|
|
723
|
+
* @performance Bulk creator — the whole batch is ONE host round-trip and ONE undo
|
|
724
|
+
* entry, and each distinct catalog source loads once. Always prefer this over
|
|
725
|
+
* calling `design.create.furniture` in a loop: build the full `items[]` array
|
|
726
|
+
* first (all positions and angles up front), then make one call.
|
|
727
|
+
*
|
|
728
|
+
* # Example
|
|
729
|
+
* ```ts
|
|
730
|
+
* const v = snaptrude.core.math.vec3
|
|
731
|
+
* const [chair] = await snaptrude.design.furniture.listCatalog()
|
|
732
|
+
* const placed = await snaptrude.design.create.furnitureItems([
|
|
733
|
+
* { catalogId: chair.id, position: await v.new(3, 0, 5), label: "Chair-01" },
|
|
734
|
+
* { catalogId: chair.id, position: await v.new(4, 0, 5), angleInDegrees: 90 },
|
|
735
|
+
* { catalogId: chair.id, position: await v.new(5, 0, 5), angleInDegrees: 180 },
|
|
736
|
+
* ])
|
|
737
|
+
* ```
|
|
738
|
+
*/
|
|
739
|
+
public abstract furnitureItems(
|
|
740
|
+
items: PluginCreateFurnitureItem[],
|
|
741
|
+
): PluginApiReturn<ComponentHandle[]>
|
|
742
|
+
|
|
578
743
|
/**
|
|
579
744
|
* Place a **door** from the catalog into a host wall.
|
|
580
745
|
*
|
|
@@ -624,6 +789,10 @@ export abstract class PluginDesignCreateApi {
|
|
|
624
789
|
* await snaptrude.core.math.vec3.new(3, 0, 9),
|
|
625
790
|
* )
|
|
626
791
|
* ```
|
|
792
|
+
*
|
|
793
|
+
* @performance For MORE THAN ONE door, call `design.create.doors(items[])` — one host
|
|
794
|
+
* round-trip and ONE undo entry, with each host wall re-cut once per opening inside
|
|
795
|
+
* that single call. Looping this single-door creator is N round-trips.
|
|
627
796
|
*/
|
|
628
797
|
public abstract door(
|
|
629
798
|
catalogId: string,
|
|
@@ -633,6 +802,61 @@ export abstract class PluginDesignCreateApi {
|
|
|
633
802
|
facing?: Vec3Handle,
|
|
634
803
|
): PluginApiReturn<ComponentHandle>
|
|
635
804
|
|
|
805
|
+
/**
|
|
806
|
+
* Place **many doors** into host walls in one undoable operation (bulk plural
|
|
807
|
+
* of {@linkcode door}). Each item carries its own catalog id, host wall,
|
|
808
|
+
* world position, and optional `facing`, `label`, `width` and `height` — the
|
|
809
|
+
* same fields as a `"door"` {@linkcode PluginDesignCreateOpeningOptions}.
|
|
810
|
+
* Validate-all-or-throw; one command.
|
|
811
|
+
*
|
|
812
|
+
* As with the singular creator, placing an opening **re-cuts its host wall**:
|
|
813
|
+
* the wall you passed as `hostWall` stops resolving once the call returns.
|
|
814
|
+
* Several items MAY name the same original wall handle in one call — the host
|
|
815
|
+
* chains the re-cuts internally — but after the call recover the surviving
|
|
816
|
+
* wall with `design.query.getHost(opening)`, never by reusing the handle you
|
|
817
|
+
* passed in.
|
|
818
|
+
*
|
|
819
|
+
* @param items - One {@linkcode PluginCreateDoorItem} per door to place
|
|
820
|
+
* (≥1, ≤1000)
|
|
821
|
+
* @returns the placed doors as {@linkcode ComponentHandle}`[]`, in input order
|
|
822
|
+
* @throws `VALIDATION` if the items fail the schema (empty array, more than
|
|
823
|
+
* 1000 items, unknown fields, a non-positive `width`/`height`);
|
|
824
|
+
* `HANDLE_INVALID` if a `position` or `facing` handle is unknown or
|
|
825
|
+
* released; `PRECONDITION_FAILED` if a catalog id is unknown, a `hostWall`
|
|
826
|
+
* is not a wall / is locked / is not in the active proposal, the projected
|
|
827
|
+
* point falls outside its host wall, or a door tool is active;
|
|
828
|
+
* `OPERATION_FAILED` if a source mesh fails to load or placement fails
|
|
829
|
+
* (nothing is created — `details.itemIndex` is the failing item);
|
|
830
|
+
* `METHOD_NOT_PERMITTED` if the plugin may not write.
|
|
831
|
+
*
|
|
832
|
+
* @examplePrompt Add doors to all of these walls at once
|
|
833
|
+
* @examplePrompt Place a door in every room in one operation
|
|
834
|
+
* @examplePrompt Bulk add the entrance doors from this schedule
|
|
835
|
+
* @examplePrompt Put three doors on this wall in a single undo step
|
|
836
|
+
* @examplePrompt Add all the doors for this floor plan together
|
|
837
|
+
*
|
|
838
|
+
* @performance Bulk creator — the whole batch is ONE host round-trip and ONE undo
|
|
839
|
+
* entry, and each distinct catalog source loads once. Always prefer this over
|
|
840
|
+
* calling `design.create.door` in a loop: build the full `items[]` array first
|
|
841
|
+
* (all host walls and points up front), then make one call.
|
|
842
|
+
*
|
|
843
|
+
* # Example
|
|
844
|
+
* ```ts
|
|
845
|
+
* const v = snaptrude.core.math.vec3
|
|
846
|
+
* const [wall] = await snaptrude.design.query.listWalls({ isSelected: true })
|
|
847
|
+
* const [entry] = await snaptrude.design.doors.listCatalog()
|
|
848
|
+
* const [front, side] = await snaptrude.design.create.doors([
|
|
849
|
+
* { catalogId: entry.id, hostWall: wall, position: await v.new(2, 0, 5), label: "D-01" },
|
|
850
|
+
* { catalogId: entry.id, hostWall: wall, position: await v.new(6, 0, 5), width: 1.2 },
|
|
851
|
+
* ])
|
|
852
|
+
* // `wall` was re-cut twice and no longer resolves — ask the opening for its host:
|
|
853
|
+
* const currentWall = await snaptrude.design.query.getHost(front)
|
|
854
|
+
* ```
|
|
855
|
+
*/
|
|
856
|
+
public abstract doors(
|
|
857
|
+
items: PluginCreateDoorItem[],
|
|
858
|
+
): PluginApiReturn<ComponentHandle[]>
|
|
859
|
+
|
|
636
860
|
/**
|
|
637
861
|
* Place a **window** from the catalog into a host wall.
|
|
638
862
|
*
|
|
@@ -673,6 +897,10 @@ export abstract class PluginDesignCreateApi {
|
|
|
673
897
|
* { label: "Win-01" },
|
|
674
898
|
* )
|
|
675
899
|
* ```
|
|
900
|
+
*
|
|
901
|
+
* @performance For MORE THAN ONE window, call `design.create.windows(items[])` — one
|
|
902
|
+
* host round-trip and ONE undo entry, with each host wall re-cut once per opening
|
|
903
|
+
* inside that single call. Looping this single-window creator is N round-trips.
|
|
676
904
|
*/
|
|
677
905
|
public abstract window(
|
|
678
906
|
catalogId: string,
|
|
@@ -682,6 +910,62 @@ export abstract class PluginDesignCreateApi {
|
|
|
682
910
|
facing?: Vec3Handle,
|
|
683
911
|
): PluginApiReturn<ComponentHandle>
|
|
684
912
|
|
|
913
|
+
/**
|
|
914
|
+
* Place **many windows** into host walls in one undoable operation (bulk
|
|
915
|
+
* plural of {@linkcode window}). Each item carries its own catalog id, host
|
|
916
|
+
* wall, world position, and optional `facing`, `label`, `width`, `height` and
|
|
917
|
+
* `sillHeight` — the same fields as a `"window"`
|
|
918
|
+
* {@linkcode PluginDesignCreateOpeningOptions}. `sillHeight` is measured from
|
|
919
|
+
* the host wall base to the BOTTOM of the window. Validate-all-or-throw; one
|
|
920
|
+
* command.
|
|
921
|
+
*
|
|
922
|
+
* As with the singular creator, placing an opening **re-cuts its host wall**:
|
|
923
|
+
* the wall you passed as `hostWall` stops resolving once the call returns.
|
|
924
|
+
* Several items MAY name the same original wall handle in one call — the host
|
|
925
|
+
* chains the re-cuts internally — but after the call recover the surviving
|
|
926
|
+
* wall with `design.query.getHost(opening)`, never by reusing the handle you
|
|
927
|
+
* passed in.
|
|
928
|
+
*
|
|
929
|
+
* @param items - One {@linkcode PluginCreateWindowItem} per window to place
|
|
930
|
+
* (≥1, ≤1000)
|
|
931
|
+
* @returns the placed windows as {@linkcode ComponentHandle}`[]`, in input order
|
|
932
|
+
* @throws `VALIDATION` if the items fail the schema (empty array, more than
|
|
933
|
+
* 1000 items, unknown fields, a non-positive `width`/`height`, a negative
|
|
934
|
+
* `sillHeight`); `HANDLE_INVALID` if a `position` or `facing` handle is
|
|
935
|
+
* unknown or released; `PRECONDITION_FAILED` if a catalog id is unknown, a
|
|
936
|
+
* `hostWall` is not a wall / is locked / is not in the active proposal, the
|
|
937
|
+
* projected point falls outside its host wall, or a window tool is active;
|
|
938
|
+
* `OPERATION_FAILED` if a source mesh fails to load or placement fails
|
|
939
|
+
* (nothing is created — `details.itemIndex` is the failing item);
|
|
940
|
+
* `METHOD_NOT_PERMITTED` if the plugin may not write.
|
|
941
|
+
*
|
|
942
|
+
* @examplePrompt Add windows along this whole facade at once
|
|
943
|
+
* @examplePrompt Place a window in every bedroom in one operation
|
|
944
|
+
* @examplePrompt Bulk add the windows from this schedule
|
|
945
|
+
* @examplePrompt Put four windows on this wall in a single undo step
|
|
946
|
+
* @examplePrompt Add all the windows with a 0.9m sill height together
|
|
947
|
+
*
|
|
948
|
+
* @performance Bulk creator — the whole batch is ONE host round-trip and ONE undo
|
|
949
|
+
* entry, and each distinct catalog source loads once. Always prefer this over
|
|
950
|
+
* calling `design.create.window` in a loop: build the full `items[]` array first
|
|
951
|
+
* (all host walls and points up front), then make one call.
|
|
952
|
+
*
|
|
953
|
+
* # Example
|
|
954
|
+
* ```ts
|
|
955
|
+
* const v = snaptrude.core.math.vec3
|
|
956
|
+
* const [wall] = await snaptrude.design.query.listWalls({ isSelected: true })
|
|
957
|
+
* const [casement] = await snaptrude.design.windows.listCatalog()
|
|
958
|
+
* const placed = await snaptrude.design.create.windows([
|
|
959
|
+
* { catalogId: casement.id, hostWall: wall, position: await v.new(2, 0, 5), sillHeight: 0.9 },
|
|
960
|
+
* { catalogId: casement.id, hostWall: wall, position: await v.new(5, 0, 5), sillHeight: 0.9 },
|
|
961
|
+
* ])
|
|
962
|
+
* const currentWall = await snaptrude.design.query.getHost(placed[0])
|
|
963
|
+
* ```
|
|
964
|
+
*/
|
|
965
|
+
public abstract windows(
|
|
966
|
+
items: PluginCreateWindowItem[],
|
|
967
|
+
): PluginApiReturn<ComponentHandle[]>
|
|
968
|
+
|
|
685
969
|
/**
|
|
686
970
|
* Place a catalog **door or window** into a host wall with optional size overrides.
|
|
687
971
|
*
|
|
@@ -699,8 +983,9 @@ export abstract class PluginDesignCreateApi {
|
|
|
699
983
|
* @examplePrompt Add a 1m wide door to this wall here
|
|
700
984
|
* @examplePrompt Place a window with a 0.9m sill height on the selected wall
|
|
701
985
|
*
|
|
702
|
-
* @performance Single-opening creator — use it for one hosted opening.
|
|
703
|
-
*
|
|
986
|
+
* @performance Single-opening creator — use it for one hosted opening. For MORE
|
|
987
|
+
* THAN ONE, call `design.create.doors(items[])` / `design.create.windows(items[])`:
|
|
988
|
+
* the same per-item fields, one host round-trip, ONE undo entry.
|
|
704
989
|
*
|
|
705
990
|
* # Example
|
|
706
991
|
* ```ts
|
|
@@ -1262,7 +1547,7 @@ export type PluginDesignCreateStaircaseArgs = z.infer<
|
|
|
1262
1547
|
* | `catalogId` | `string` | Library id: team `_id` or general `fullName` |
|
|
1263
1548
|
* | `position` | {@linkcode Vec3Handle} | Absolute world placement point |
|
|
1264
1549
|
* | `label` | `string`? | Instance name (default auto `${name}Ins${n}`) |
|
|
1265
|
-
* | `createNewSourceMesh` | `boolean`? |
|
|
1550
|
+
* | `createNewSourceMesh` | `boolean`? | Legacy, kept for compatibility; the host records a catalog source exactly once and this flag cannot skip it |
|
|
1266
1551
|
* | `angleInDegrees` | `number`? | Rotation about the vertical axis, in degrees (default unrotated) |
|
|
1267
1552
|
*/
|
|
1268
1553
|
export const PluginDesignCreateFurnitureArgs = z.object({
|
|
@@ -1338,16 +1623,6 @@ export type PluginDesignCreateWindowArgs = z.infer<
|
|
|
1338
1623
|
// opening
|
|
1339
1624
|
// ---------------------------------------------------------------------------
|
|
1340
1625
|
|
|
1341
|
-
const PluginOpeningBaseOptions = {
|
|
1342
|
-
catalogId: z.string().min(1),
|
|
1343
|
-
hostWall: ComponentHandle,
|
|
1344
|
-
position: Vec3Handle,
|
|
1345
|
-
facing: Vec3Handle.optional(),
|
|
1346
|
-
label: z.string().optional(),
|
|
1347
|
-
width: z.number().finite().positive().optional(),
|
|
1348
|
-
height: z.number().finite().positive().optional(),
|
|
1349
|
-
}
|
|
1350
|
-
|
|
1351
1626
|
/**
|
|
1352
1627
|
* Options for {@linkcode PluginDesignCreateApi.opening}, discriminated by
|
|
1353
1628
|
* `kind`. Width and height are positive Snaptrude engine-unit values.
|
|
@@ -1524,3 +1799,6 @@ export const PluginCreateSpaceItem = z.object({
|
|
|
1524
1799
|
storey: z.number().int().optional(),
|
|
1525
1800
|
})
|
|
1526
1801
|
export type PluginCreateSpaceItem = z.infer<typeof PluginCreateSpaceItem>
|
|
1802
|
+
|
|
1803
|
+
export * from "./opening-fields"
|
|
1804
|
+
export * from "./bulk-items"
|