@snaptrude/plugin-core 0.6.0 → 0.7.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.
- package/CHANGELOG.md +29 -0
- package/api-manifest.json +206 -704
- package/dist/api/core/index.d.ts +5 -0
- package/dist/api/core/index.d.ts.map +1 -1
- package/dist/api/core/proposals/index.d.ts +475 -0
- package/dist/api/core/proposals/index.d.ts.map +1 -0
- package/dist/api/design/create/index.d.ts +209 -0
- package/dist/api/design/create/index.d.ts.map +1 -1
- package/dist/api/design/doors/index.d.ts +159 -0
- package/dist/api/design/doors/index.d.ts.map +1 -1
- package/dist/api/design/furniture/index.d.ts +34 -5
- package/dist/api/design/furniture/index.d.ts.map +1 -1
- package/dist/api/design/index.d.ts +83 -0
- package/dist/api/design/index.d.ts.map +1 -1
- package/dist/api/design/lock.d.ts +26 -0
- package/dist/api/design/lock.d.ts.map +1 -1
- package/dist/api/design/materials/index.d.ts +159 -2
- package/dist/api/design/materials/index.d.ts.map +1 -1
- package/dist/api/design/query/index.d.ts +115 -5
- package/dist/api/design/query/index.d.ts.map +1 -1
- package/dist/api/design/selection/index.d.ts +2 -2
- package/dist/api/design/transform/index.d.ts +83 -2
- package/dist/api/design/transform/index.d.ts.map +1 -1
- package/dist/api/design/update/index.d.ts +168 -0
- package/dist/api/design/update/index.d.ts.map +1 -1
- package/dist/api/design/windows/index.d.ts +113 -2
- package/dist/api/design/windows/index.d.ts.map +1 -1
- package/dist/api/entity/story.d.ts +101 -7
- package/dist/api/entity/story.d.ts.map +1 -1
- package/dist/api/presentation/aiInspiration.d.ts +1967 -7
- package/dist/api/presentation/aiInspiration.d.ts.map +1 -1
- package/dist/api/presentation/import.d.ts +49 -4
- package/dist/api/presentation/import.d.ts.map +1 -1
- package/dist/api/presentation/index.d.ts +2 -3
- package/dist/api/presentation/index.d.ts.map +1 -1
- package/dist/api/presentation/views.d.ts +210 -5
- package/dist/api/presentation/views.d.ts.map +1 -1
- package/dist/api/program/areas.d.ts +42 -12
- package/dist/api/program/areas.d.ts.map +1 -1
- package/dist/api/program/index.d.ts +3 -11
- package/dist/api/program/index.d.ts.map +1 -1
- package/dist/api/program/layout.d.ts +186 -11
- package/dist/api/program/layout.d.ts.map +1 -1
- package/dist/errors/codes.d.ts +34 -0
- package/dist/errors/codes.d.ts.map +1 -0
- package/dist/errors/envelope.d.ts +56 -0
- package/dist/errors/envelope.d.ts.map +1 -0
- package/dist/errors/index.d.ts +6 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/plugin-error.d.ts +69 -0
- package/dist/errors/plugin-error.d.ts.map +1 -0
- package/dist/host-utils.d.ts +4 -0
- package/dist/host-utils.d.ts.map +1 -1
- package/dist/index.cjs +1993 -1033
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1874 -1030
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/scripts/generate-manifest.test.mjs +77 -0
- package/src/api/core/index.ts +5 -0
- package/src/api/core/proposals/index.ts +509 -0
- package/src/api/design/create/index.ts +249 -0
- package/src/api/design/doors/index.ts +168 -0
- package/src/api/design/furniture/index.ts +36 -5
- package/src/api/design/index.ts +87 -0
- package/src/api/design/lock.ts +27 -0
- package/src/api/design/materials/index.ts +182 -2
- package/src/api/design/query/index.ts +81 -0
- package/src/api/design/transform/index.ts +74 -2
- package/src/api/design/update/index.ts +167 -0
- package/src/api/design/windows/index.ts +128 -2
- package/src/api/entity/story.ts +105 -7
- package/src/api/presentation/aiInspiration.ts +761 -7
- package/src/api/presentation/import.ts +45 -4
- package/src/api/presentation/index.ts +2 -3
- package/src/api/presentation/views.ts +221 -5
- package/src/api/program/areas.ts +34 -12
- package/src/api/program/index.ts +3 -11
- package/src/api/program/layout.ts +195 -11
- package/src/errors/codes.ts +136 -0
- package/src/errors/envelope.ts +75 -0
- package/src/errors/index.ts +21 -0
- package/src/errors/plugin-error.ts +134 -0
- package/src/host-utils.ts +4 -0
- package/src/index.ts +1 -0
- package/test/errors.test.mjs +184 -0
- package/api-manifest.full.json +0 -5600
|
@@ -10,8 +10,15 @@ import { ComponentHandle, MaterialHandle } from "../../../handles"
|
|
|
10
10
|
* scene (undoable via `save`, default `true`). `create` mints a material from a
|
|
11
11
|
* {@linkcode PluginMaterialSpec} (idempotent by name).
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
13
|
+
* Per-face assignment — {@linkcode PluginDesignMaterialsApi.applyToFaces} /
|
|
14
|
+
* {@linkcode PluginDesignMaterialsApi.resetFaces} plus per-face reads
|
|
15
|
+
* ({@linkcode PluginDesignMaterialsApi.getByFace} / {@linkcode PluginDesignMaterialsApi.listByFace})
|
|
16
|
+
* — targets **BREP faces by durable face index** and is **BREP-only** (furniture,
|
|
17
|
+
* doors/windows, and Revit imports are rejected) and **visual-only** in v1 (the
|
|
18
|
+
* per-face finish is not carried into BIM schedules / cost layers). A face index
|
|
19
|
+
* is durable only while the component's BREP topology (face count/order) is unchanged.
|
|
20
|
+
*
|
|
21
|
+
* `copy` (needs an undo wrapper) lands in a later pass.
|
|
15
22
|
*/
|
|
16
23
|
export abstract class PluginDesignMaterialsApi {
|
|
17
24
|
constructor() {}
|
|
@@ -112,6 +119,120 @@ export abstract class PluginDesignMaterialsApi {
|
|
|
112
119
|
save?: boolean
|
|
113
120
|
): PluginApiReturn<PluginMaterialResult>
|
|
114
121
|
|
|
122
|
+
/**
|
|
123
|
+
* Paint specific **BREP faces** of a component with a material — an existing
|
|
124
|
+
* project material by handle, or a new one described inline as a
|
|
125
|
+
* {@linkcode PluginMaterialSpec}. Faces are addressed by **durable BREP face
|
|
126
|
+
* index** (their 0-based position in the component's BREP face list).
|
|
127
|
+
* Undoable when `save` is true (the default).
|
|
128
|
+
*
|
|
129
|
+
* Per-face materials are **BREP-only** (walls, floors, slabs, roofs, masses,
|
|
130
|
+
* columns, beams; furniture, doors/windows, and Revit imports are rejected) and
|
|
131
|
+
* **visual-only in v1** — the finish is not carried into BIM schedules / cost
|
|
132
|
+
* layers. A face index is durable only while the BREP topology (face count/order)
|
|
133
|
+
* is unchanged; a face index at or beyond the face count is rejected.
|
|
134
|
+
*
|
|
135
|
+
* @param component The (BREP) component to paint
|
|
136
|
+
* @param faces BREP face indices to paint (nonnegative integers)
|
|
137
|
+
* @param material Material handle, or an inline {@linkcode PluginMaterialSpec}
|
|
138
|
+
* @param save Commit as an undoable command (default `true`)
|
|
139
|
+
* @returns The number of faces painted
|
|
140
|
+
*
|
|
141
|
+
* @examplePrompt Paint the top face of this slab red
|
|
142
|
+
* @examplePrompt Apply the brick material to faces 0 and 2 of this wall
|
|
143
|
+
* @examplePrompt Put a wood finish on just the front face of this column
|
|
144
|
+
* @examplePrompt Colour face 4 of the selected mass with #ff8800
|
|
145
|
+
*
|
|
146
|
+
* # Example
|
|
147
|
+
* ```ts
|
|
148
|
+
* const [wall] = await snaptrude.design.query.listWalls({ storeys: [1] })
|
|
149
|
+
* // faces are addressed by durable 0-based BREP face index
|
|
150
|
+
* const { count } = await snaptrude.design.materials.applyToFaces(wall, [0, 1], { name: "Accent", color: "#b04a3a" })
|
|
151
|
+
* console.log("painted", count, "faces")
|
|
152
|
+
* ```
|
|
153
|
+
*/
|
|
154
|
+
public abstract applyToFaces(
|
|
155
|
+
component: ComponentHandle,
|
|
156
|
+
faces: number[],
|
|
157
|
+
material: MaterialHandle | PluginMaterialSpec,
|
|
158
|
+
save?: boolean
|
|
159
|
+
): PluginApiReturn<PluginMaterialResult>
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Reset specific BREP faces of a component back to the component/type default,
|
|
163
|
+
* removing any per-face finish. The inverse of
|
|
164
|
+
* {@linkcode PluginDesignMaterialsApi.applyToFaces}; undoable when `save` is true
|
|
165
|
+
* (the default). BREP-only (see `applyToFaces`).
|
|
166
|
+
*
|
|
167
|
+
* @param component The (BREP) component
|
|
168
|
+
* @param faces BREP face indices to reset (nonnegative integers)
|
|
169
|
+
* @param save Commit as an undoable command (default `true`)
|
|
170
|
+
* @returns The number of faces reset
|
|
171
|
+
*
|
|
172
|
+
* @examplePrompt Remove the paint from the top face of this slab
|
|
173
|
+
* @examplePrompt Reset faces 0 and 2 of this wall to their default material
|
|
174
|
+
* @examplePrompt Clear the custom finish on the front face of this column
|
|
175
|
+
*
|
|
176
|
+
* # Example
|
|
177
|
+
* ```ts
|
|
178
|
+
* const [wall] = await snaptrude.design.query.listWalls({ storeys: [1] })
|
|
179
|
+
* const { count } = await snaptrude.design.materials.resetFaces(wall, [0, 1])
|
|
180
|
+
* console.log("reset", count, "faces")
|
|
181
|
+
* ```
|
|
182
|
+
*/
|
|
183
|
+
public abstract resetFaces(
|
|
184
|
+
component: ComponentHandle,
|
|
185
|
+
faces: number[],
|
|
186
|
+
save?: boolean
|
|
187
|
+
): PluginApiReturn<PluginMaterialResult>
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Read the material applied to a single BREP face.
|
|
191
|
+
*
|
|
192
|
+
* @param component The (BREP) component
|
|
193
|
+
* @param face The BREP face index (a nonnegative integer)
|
|
194
|
+
* @returns The face's material as a {@linkcode MaterialHandle}, or `null` if the
|
|
195
|
+
* face carries no explicit per-face material (or the component has no BREP)
|
|
196
|
+
*
|
|
197
|
+
* @examplePrompt What material is on the top face of this slab?
|
|
198
|
+
* @examplePrompt Get the finish applied to face 2 of this wall
|
|
199
|
+
* @examplePrompt Which material is painted on the front face of this column?
|
|
200
|
+
*
|
|
201
|
+
* # Example
|
|
202
|
+
* ```ts
|
|
203
|
+
* const [wall] = await snaptrude.design.query.listWalls()
|
|
204
|
+
* const material = await snaptrude.design.materials.getByFace(wall, 0)
|
|
205
|
+
* if (material) console.log(await snaptrude.design.materials.getInfo(material))
|
|
206
|
+
* ```
|
|
207
|
+
*/
|
|
208
|
+
public abstract getByFace(
|
|
209
|
+
component: ComponentHandle,
|
|
210
|
+
face: number
|
|
211
|
+
): PluginApiReturn<MaterialHandle | null>
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* List the per-face materials of a component — one entry per BREP face, in face
|
|
215
|
+
* index order. A face carrying no explicit per-face material reports `null`.
|
|
216
|
+
*
|
|
217
|
+
* @param component The (BREP) component
|
|
218
|
+
* @returns The per-face materials as {@linkcode PluginFaceMaterial}`[]` (`[]` for a
|
|
219
|
+
* component with no BREP)
|
|
220
|
+
*
|
|
221
|
+
* @examplePrompt List the material on every face of this wall
|
|
222
|
+
* @examplePrompt Show me which faces of this slab are painted and with what
|
|
223
|
+
* @examplePrompt Break down the per-face finishes of the selected mass
|
|
224
|
+
*
|
|
225
|
+
* # Example
|
|
226
|
+
* ```ts
|
|
227
|
+
* const [wall] = await snaptrude.design.query.listWalls()
|
|
228
|
+
* const faces = await snaptrude.design.materials.listByFace(wall)
|
|
229
|
+
* for (const f of faces) console.log(f.faceIndex, f.material ?? "(default)")
|
|
230
|
+
* ```
|
|
231
|
+
*/
|
|
232
|
+
public abstract listByFace(
|
|
233
|
+
component: ComponentHandle
|
|
234
|
+
): PluginApiReturn<PluginFaceMaterial[]>
|
|
235
|
+
|
|
115
236
|
/**
|
|
116
237
|
* Create a new material in the project from a spec — a color, texture URL,
|
|
117
238
|
* opacity, and/or name. Idempotent by name: creating with the name of an
|
|
@@ -257,6 +378,20 @@ export type PluginMaterialSpec = z.infer<typeof PluginMaterialSpec>
|
|
|
257
378
|
export const PluginMaterialResult = z.object({ count: z.number() })
|
|
258
379
|
export type PluginMaterialResult = z.infer<typeof PluginMaterialResult>
|
|
259
380
|
|
|
381
|
+
/**
|
|
382
|
+
* A single component face's material assignment (value record — not a handle).
|
|
383
|
+
*
|
|
384
|
+
* | Property | Type | Description |
|
|
385
|
+
* |---|---|---|
|
|
386
|
+
* | `faceIndex` | `number` | Durable BREP face index (0-based position in the face list) |
|
|
387
|
+
* | `material` | {@linkcode MaterialHandle} \| `null` | The applied material, or `null` for the default |
|
|
388
|
+
*/
|
|
389
|
+
export const PluginFaceMaterial = z.object({
|
|
390
|
+
faceIndex: z.number().int().nonnegative(),
|
|
391
|
+
material: MaterialHandle.nullable(),
|
|
392
|
+
})
|
|
393
|
+
export type PluginFaceMaterial = z.infer<typeof PluginFaceMaterial>
|
|
394
|
+
|
|
260
395
|
/** Arguments for {@linkcode PluginDesignMaterialsApi.list}. */
|
|
261
396
|
export const PluginDesignMaterialsListArgs = z.object({
|
|
262
397
|
scope: z.enum(["project", "preset"]).optional(),
|
|
@@ -299,3 +434,48 @@ export type PluginDesignMaterialsResetArgs = z.infer<typeof PluginDesignMaterial
|
|
|
299
434
|
/** Arguments for {@linkcode PluginDesignMaterialsApi.create}. */
|
|
300
435
|
export const PluginDesignMaterialsCreateArgs = z.object({ spec: PluginMaterialSpec })
|
|
301
436
|
export type PluginDesignMaterialsCreateArgs = z.infer<typeof PluginDesignMaterialsCreateArgs>
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Arguments for {@linkcode PluginDesignMaterialsApi.applyToFaces}.
|
|
440
|
+
*
|
|
441
|
+
* | Property | Type | Description |
|
|
442
|
+
* |---|---|---|
|
|
443
|
+
* | `component` | {@linkcode ComponentHandle} | The (BREP) component to paint |
|
|
444
|
+
* | `faces` | `number[]` | BREP face indices to paint (nonnegative integers) |
|
|
445
|
+
* | `material` | {@linkcode MaterialHandle} \| {@linkcode PluginMaterialSpec} | Material handle, or an inline spec |
|
|
446
|
+
* | `save` | `boolean` | Commit as an undoable command (default `true`) |
|
|
447
|
+
*/
|
|
448
|
+
export const PluginDesignMaterialsApplyToFacesArgs = z.object({
|
|
449
|
+
component: ComponentHandle,
|
|
450
|
+
faces: z.array(z.number().int().nonnegative()),
|
|
451
|
+
material: z.union([MaterialHandle, PluginMaterialSpec]),
|
|
452
|
+
save: z.boolean().optional(),
|
|
453
|
+
})
|
|
454
|
+
export type PluginDesignMaterialsApplyToFacesArgs = z.infer<
|
|
455
|
+
typeof PluginDesignMaterialsApplyToFacesArgs
|
|
456
|
+
>
|
|
457
|
+
|
|
458
|
+
/** Arguments for {@linkcode PluginDesignMaterialsApi.resetFaces}. */
|
|
459
|
+
export const PluginDesignMaterialsResetFacesArgs = z.object({
|
|
460
|
+
component: ComponentHandle,
|
|
461
|
+
faces: z.array(z.number().int().nonnegative()),
|
|
462
|
+
save: z.boolean().optional(),
|
|
463
|
+
})
|
|
464
|
+
export type PluginDesignMaterialsResetFacesArgs = z.infer<
|
|
465
|
+
typeof PluginDesignMaterialsResetFacesArgs
|
|
466
|
+
>
|
|
467
|
+
|
|
468
|
+
/** Arguments for {@linkcode PluginDesignMaterialsApi.getByFace}. */
|
|
469
|
+
export const PluginDesignMaterialsGetByFaceArgs = z.object({
|
|
470
|
+
component: ComponentHandle,
|
|
471
|
+
face: z.number().int().nonnegative(),
|
|
472
|
+
})
|
|
473
|
+
export type PluginDesignMaterialsGetByFaceArgs = z.infer<
|
|
474
|
+
typeof PluginDesignMaterialsGetByFaceArgs
|
|
475
|
+
>
|
|
476
|
+
|
|
477
|
+
/** Arguments for {@linkcode PluginDesignMaterialsApi.listByFace} (component only). */
|
|
478
|
+
export const PluginDesignMaterialsListByFaceArgs = PluginDesignMaterialsComponentArgs
|
|
479
|
+
export type PluginDesignMaterialsListByFaceArgs = z.infer<
|
|
480
|
+
typeof PluginDesignMaterialsListByFaceArgs
|
|
481
|
+
>
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
PluginDepartmentId,
|
|
8
8
|
PluginAreaClass,
|
|
9
9
|
} from "../../entity/space"
|
|
10
|
+
import { PluginStaircasePreset } from "../create"
|
|
10
11
|
import { PluginDesignQueryGeometryApi } from "./geometry"
|
|
11
12
|
import { PluginDesignQuerySpacesApi } from "./spaces"
|
|
12
13
|
|
|
@@ -215,6 +216,56 @@ export const PluginEntityMeasurements = z.object({
|
|
|
215
216
|
})
|
|
216
217
|
export type PluginEntityMeasurements = z.infer<typeof PluginEntityMeasurements>
|
|
217
218
|
|
|
219
|
+
/**
|
|
220
|
+
* The parametric properties of a staircase — the same editable fields the
|
|
221
|
+
* staircase properties panel exposes. Returned by
|
|
222
|
+
* {@linkcode PluginDesignQueryApi.getStaircaseParams}; the write side is
|
|
223
|
+
* `design.update.staircase`. Engine internals (block schema, min well size,
|
|
224
|
+
* square-well padding) are deliberately excluded.
|
|
225
|
+
*
|
|
226
|
+
* All dimensions are in **engine units** (the same convention as
|
|
227
|
+
* `design.doors.getWidth`); `steps` is a unitless count. The invariant
|
|
228
|
+
* `staircaseHeight = steps × riser` is governed by the two lock booleans — see
|
|
229
|
+
* `design.update.staircase`. Type-specific fields (`wellSize`, `landingWidth`,
|
|
230
|
+
* `flightStartDistAfterTurn`) are present only when the preset uses them.
|
|
231
|
+
*
|
|
232
|
+
* | Property | Type | Description |
|
|
233
|
+
* |---|---|---|
|
|
234
|
+
* | `staircaseType` | {@linkcode PluginStaircasePreset} | Base staircase type |
|
|
235
|
+
* | `staircasePreset` | {@linkcode PluginStaircasePreset} | Named parametric preset |
|
|
236
|
+
* | `staircaseHeight` | `number` | Total rise (engine units) |
|
|
237
|
+
* | `steps` | `number` | Step count (unitless) |
|
|
238
|
+
* | `riser` | `number` | Riser height (engine units) |
|
|
239
|
+
* | `tread` | `number` | Tread depth (engine units) |
|
|
240
|
+
* | `width` | `number` | Flight width (engine units) |
|
|
241
|
+
* | `depth` | `number` | Slab/waist thickness (engine units) |
|
|
242
|
+
* | `wellSize` | `number`? | Stairwell gap (dogLegged / openWell / square) |
|
|
243
|
+
* | `landingWidth` | `number`? | Landing depth (straight / dogLegged) |
|
|
244
|
+
* | `flightStartDistAfterTurn` | `number`? | Flight offset after a turn (dogLegged / lShaped / square) |
|
|
245
|
+
* | `baseOffset` | `number` | Vertical offset from the storey base (engine units) |
|
|
246
|
+
* | `isStoreyHeightUnlocked` | `boolean` | When `false`, height is tied to the storey height |
|
|
247
|
+
* | `isStaircaseHeightUnlocked` | `boolean` | When `false`, height recomputes from steps × riser |
|
|
248
|
+
* | `storey` | `number` | Storey/floor level the staircase sits on |
|
|
249
|
+
*/
|
|
250
|
+
export const PluginStaircaseParams = z.object({
|
|
251
|
+
staircaseType: PluginStaircasePreset,
|
|
252
|
+
staircasePreset: PluginStaircasePreset,
|
|
253
|
+
staircaseHeight: z.number(),
|
|
254
|
+
steps: z.number(),
|
|
255
|
+
riser: z.number(),
|
|
256
|
+
tread: z.number(),
|
|
257
|
+
width: z.number(),
|
|
258
|
+
depth: z.number(),
|
|
259
|
+
wellSize: z.number().optional(),
|
|
260
|
+
landingWidth: z.number().optional(),
|
|
261
|
+
flightStartDistAfterTurn: z.number().optional(),
|
|
262
|
+
baseOffset: z.number(),
|
|
263
|
+
isStoreyHeightUnlocked: z.boolean(),
|
|
264
|
+
isStaircaseHeightUnlocked: z.boolean(),
|
|
265
|
+
storey: z.number(),
|
|
266
|
+
})
|
|
267
|
+
export type PluginStaircaseParams = z.infer<typeof PluginStaircaseParams>
|
|
268
|
+
|
|
218
269
|
// ---------------------------------------------------------------------------
|
|
219
270
|
// The query API
|
|
220
271
|
// ---------------------------------------------------------------------------
|
|
@@ -902,6 +953,36 @@ export abstract class PluginDesignQueryApi {
|
|
|
902
953
|
component: ComponentHandle,
|
|
903
954
|
): PluginApiReturn<PluginEntityMeasurements | null>
|
|
904
955
|
|
|
956
|
+
/**
|
|
957
|
+
* Read a staircase's parametric properties — its preset, dimensions, step
|
|
958
|
+
* count, base offset, and height-lock state — as a
|
|
959
|
+
* {@linkcode PluginStaircaseParams} record. The paired write is
|
|
960
|
+
* `design.update.staircase`.
|
|
961
|
+
*
|
|
962
|
+
* All dimensions are in **engine units** (the same convention as
|
|
963
|
+
* `design.doors.getWidth`); `steps` is a unitless count. A read — never
|
|
964
|
+
* throws: returns `null` for a handle that is not a staircase or no longer
|
|
965
|
+
* resolves.
|
|
966
|
+
*
|
|
967
|
+
* @param staircase - the staircase's {@linkcode ComponentHandle}
|
|
968
|
+
* @returns the {@linkcode PluginStaircaseParams} record, or `null`
|
|
969
|
+
*
|
|
970
|
+
* @examplePrompt What's the riser height of this staircase?
|
|
971
|
+
* @examplePrompt How many steps does the selected stair have?
|
|
972
|
+
* @examplePrompt Show me the tread and width of this staircase
|
|
973
|
+
* @examplePrompt Read all the parameters of the stairs on level 1
|
|
974
|
+
*
|
|
975
|
+
* # Example
|
|
976
|
+
* ```ts
|
|
977
|
+
* const [stair] = await snaptrude.design.query.listStaircases({ isSelected: true })
|
|
978
|
+
* const params = stair ? await snaptrude.design.query.getStaircaseParams(stair) : null
|
|
979
|
+
* if (params) console.log(params.steps, params.riser, params.tread)
|
|
980
|
+
* ```
|
|
981
|
+
*/
|
|
982
|
+
public abstract getStaircaseParams(
|
|
983
|
+
staircase: ComponentHandle,
|
|
984
|
+
): PluginApiReturn<PluginStaircaseParams | null>
|
|
985
|
+
|
|
905
986
|
/**
|
|
906
987
|
* Get the **union** axis-aligned bounding box enclosing a set of entities.
|
|
907
988
|
*
|
|
@@ -6,8 +6,10 @@ import { PluginDesignChangeResult } from "../lock"
|
|
|
6
6
|
/**
|
|
7
7
|
* `snaptrude.design.transform` — rigid transforms on scene entities.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* `move` / `rotate` migrated from the removed `tools.transform.*`; `align` snaps a
|
|
10
|
+
* set of components' bounding-box edges to each other (or to a reference). All ops
|
|
11
|
+
* are undoable and return {@linkcode PluginDesignChangeResult} `{ affected }`;
|
|
12
|
+
* failures throw (RPC rejects).
|
|
11
13
|
*
|
|
12
14
|
* `mirror` / `getPosition` / `setPosition` (and `scale`, an engine gap) are NOT in
|
|
13
15
|
* this surface yet — they are a later, non-migration pass.
|
|
@@ -68,8 +70,62 @@ export abstract class PluginDesignTransformApi {
|
|
|
68
70
|
angleInDegrees: number,
|
|
69
71
|
options?: { axis?: Vec3Handle; is2D?: boolean },
|
|
70
72
|
): PluginApiReturn<PluginDesignChangeResult>
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Align a set of components by snapping one bounding-box edge to a common value
|
|
76
|
+
* — the classic Left/Center/Right/Top/Middle/Bottom align. Purely **world-axis**
|
|
77
|
+
* (deterministic and headless — the UI's camera-relative remap is bypassed):
|
|
78
|
+
*
|
|
79
|
+
* - `left` / `right` / `center` → world **X** min / max / mid
|
|
80
|
+
* - `top` / `bottom` / `middle` → world **Z** max / min / mid
|
|
81
|
+
*
|
|
82
|
+
* There is **no vertical (Y) alignment** in v1 (the UI only reaches it via a
|
|
83
|
+
* side-elevation camera remap, which this API does not replicate). Doors and
|
|
84
|
+
* windows are **rejected** in v1 — if any component in `components` (or the
|
|
85
|
+
* `reference`) is a door or window, the whole call **throws**; nothing is
|
|
86
|
+
* silently dropped. A group aligns as **one rigid body** (its whole-group
|
|
87
|
+
* bounding box moves together). Undoable.
|
|
88
|
+
*
|
|
89
|
+
* Requires **at least 2 components** unless a `reference` is supplied; with a
|
|
90
|
+
* reference, every component aligns to that reference's edge (the reference does
|
|
91
|
+
* not move) and a single component is allowed. Y (height) is preserved.
|
|
92
|
+
*
|
|
93
|
+
* @param components - Entities to align (≥2 without a `reference`)
|
|
94
|
+
* @param edge - Which bounding-box edge to snap ({@linkcode PluginAlignEdge})
|
|
95
|
+
* @param options - `reference`: a component whose edge is the fixed target
|
|
96
|
+
* @returns {@linkcode PluginDesignChangeResult}
|
|
97
|
+
*
|
|
98
|
+
* @examplePrompt Align the selected rooms to the left
|
|
99
|
+
* @examplePrompt Line up these masses along their top edges
|
|
100
|
+
* @examplePrompt Center these columns horizontally
|
|
101
|
+
* @examplePrompt Align all the walls to the left edge of this reference wall
|
|
102
|
+
* @examplePrompt Snap the bottom edges of these spaces together
|
|
103
|
+
*
|
|
104
|
+
* # Example
|
|
105
|
+
* ```ts
|
|
106
|
+
* const rooms = await snaptrude.design.selection.get()
|
|
107
|
+
* // align every room's left (world -X) edge to a common minimum
|
|
108
|
+
* await snaptrude.design.transform.align(rooms, "left")
|
|
109
|
+
* // …or align them to a fixed reference component's left edge
|
|
110
|
+
* const [anchor] = await snaptrude.design.query.listWalls({ storeys: [1] })
|
|
111
|
+
* await snaptrude.design.transform.align(rooms, "left", { reference: anchor })
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
public abstract align(
|
|
115
|
+
components: ComponentHandle[],
|
|
116
|
+
edge: PluginAlignEdge,
|
|
117
|
+
options?: { reference?: ComponentHandle },
|
|
118
|
+
): PluginApiReturn<PluginDesignChangeResult>
|
|
71
119
|
}
|
|
72
120
|
|
|
121
|
+
/**
|
|
122
|
+
* The bounding-box edge an {@linkcode PluginDesignTransformApi.align} snaps to.
|
|
123
|
+
* Mirrors the engine's align `DirectionType`. World-axis mapping: `left`/`right`/`center`
|
|
124
|
+
* → world X min/max/mid; `top`/`bottom`/`middle` → world Z max/min/mid.
|
|
125
|
+
*/
|
|
126
|
+
export const PluginAlignEdge = z.enum(["left", "right", "center", "top", "bottom", "middle"])
|
|
127
|
+
export type PluginAlignEdge = z.infer<typeof PluginAlignEdge>
|
|
128
|
+
|
|
73
129
|
/**
|
|
74
130
|
* Arguments for {@linkcode PluginDesignTransformApi.move} (options flattened).
|
|
75
131
|
*/
|
|
@@ -91,3 +147,19 @@ export const PluginDesignRotateArgs = z.object({
|
|
|
91
147
|
is2D: z.boolean().optional(),
|
|
92
148
|
})
|
|
93
149
|
export type PluginDesignRotateArgs = z.infer<typeof PluginDesignRotateArgs>
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Arguments for {@linkcode PluginDesignTransformApi.align} (options flattened).
|
|
153
|
+
*
|
|
154
|
+
* | Property | Type | Description |
|
|
155
|
+
* |---|---|---|
|
|
156
|
+
* | `components` | {@linkcode ComponentHandle}`[]` | Entities to align (≥2 unless `reference` is set — host-enforced) |
|
|
157
|
+
* | `edge` | {@linkcode PluginAlignEdge} | Which bounding-box edge to snap |
|
|
158
|
+
* | `reference` | {@linkcode ComponentHandle} | A fixed component whose edge is the target (optional) |
|
|
159
|
+
*/
|
|
160
|
+
export const PluginDesignAlignArgs = z.object({
|
|
161
|
+
components: z.array(ComponentHandle).min(1),
|
|
162
|
+
edge: PluginAlignEdge,
|
|
163
|
+
reference: ComponentHandle.optional(),
|
|
164
|
+
})
|
|
165
|
+
export type PluginDesignAlignArgs = z.infer<typeof PluginDesignAlignArgs>
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
PluginAreaClass,
|
|
9
9
|
PluginSpaceUpdateResult,
|
|
10
10
|
} from "../../entity/space"
|
|
11
|
+
import { PluginDesignChangeResult } from "../lock"
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Sparse property updates for a space. Only provided fields change.
|
|
@@ -44,6 +45,83 @@ export const PluginUpdateSpaceItem = z.object({
|
|
|
44
45
|
})
|
|
45
46
|
export type PluginUpdateSpaceItem = z.infer<typeof PluginUpdateSpaceItem>
|
|
46
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Wire schema for {@linkcode PluginDesignUpdateApi.setLabel}.
|
|
50
|
+
*
|
|
51
|
+
* | Property | Type | Description |
|
|
52
|
+
* |---|---|---|
|
|
53
|
+
* | `component` | `ComponentHandle` | The component to relabel |
|
|
54
|
+
* | `label` | `string` | New label (non-empty after trimming) |
|
|
55
|
+
*/
|
|
56
|
+
export const PluginDesignSetLabelArgs = z.object({
|
|
57
|
+
component: ComponentHandle,
|
|
58
|
+
label: z.string().trim().min(1),
|
|
59
|
+
})
|
|
60
|
+
export type PluginDesignSetLabelArgs = z.infer<typeof PluginDesignSetLabelArgs>
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Sparse parametric updates for a staircase (all fields optional; at least one
|
|
64
|
+
* required). Only the fields you provide change. Consumed by
|
|
65
|
+
* {@linkcode PluginDesignUpdateApi.staircase}; the paired read is
|
|
66
|
+
* `design.query.getStaircaseParams`.
|
|
67
|
+
*
|
|
68
|
+
* All dimensions are in **engine units** (the same convention as
|
|
69
|
+
* `design.doors.setWidth`); `steps` is a unitless integer count. Height, riser,
|
|
70
|
+
* and step count are **coupled** (`staircaseHeight = steps × riser`); the two
|
|
71
|
+
* lock booleans govern which value recomputes — see the method JSDoc.
|
|
72
|
+
*
|
|
73
|
+
* | Property | Type | Description |
|
|
74
|
+
* |---|---|---|
|
|
75
|
+
* | `isStoreyHeightUnlocked` | `boolean`? | Unlock the staircase height from the storey height |
|
|
76
|
+
* | `isStaircaseHeightUnlocked` | `boolean`? | Unlock the height from `steps × riser` |
|
|
77
|
+
* | `storeyHeight` | `number`? | Storey height the stair sits within (engine units) |
|
|
78
|
+
* | `staircaseHeight` | `number`? | Total rise (engine units) |
|
|
79
|
+
* | `steps` | `number`? | Step count (unitless integer) |
|
|
80
|
+
* | `riser` | `number`? | Riser height (engine units) |
|
|
81
|
+
* | `tread` | `number`? | Tread depth (engine units) |
|
|
82
|
+
* | `width` | `number`? | Flight width (engine units) |
|
|
83
|
+
* | `depth` | `number`? | Slab/waist thickness (engine units) |
|
|
84
|
+
* | `wellSize` | `number`? | Stairwell gap (dogLegged / openWell / square) |
|
|
85
|
+
* | `flightStartDistAfterTurn` | `number`? | Flight offset after a turn (dogLegged / lShaped / square) |
|
|
86
|
+
* | `landingWidth` | `number`? | Landing depth (straight / dogLegged) |
|
|
87
|
+
* | `baseOffset` | `number`? | Vertical offset from the storey base (engine units) |
|
|
88
|
+
*/
|
|
89
|
+
export const PluginStaircaseParamUpdates = z
|
|
90
|
+
.object({
|
|
91
|
+
isStoreyHeightUnlocked: z.boolean().optional(),
|
|
92
|
+
isStaircaseHeightUnlocked: z.boolean().optional(),
|
|
93
|
+
storeyHeight: z.number().optional(),
|
|
94
|
+
staircaseHeight: z.number().optional(),
|
|
95
|
+
steps: z.number().int().optional(),
|
|
96
|
+
riser: z.number().optional(),
|
|
97
|
+
tread: z.number().optional(),
|
|
98
|
+
width: z.number().optional(),
|
|
99
|
+
depth: z.number().optional(),
|
|
100
|
+
wellSize: z.number().optional(),
|
|
101
|
+
flightStartDistAfterTurn: z.number().optional(),
|
|
102
|
+
landingWidth: z.number().optional(),
|
|
103
|
+
baseOffset: z.number().optional(),
|
|
104
|
+
})
|
|
105
|
+
.refine((p) => Object.keys(p).length > 0, {
|
|
106
|
+
message:
|
|
107
|
+
"PluginStaircaseParamUpdates requires at least one field to change",
|
|
108
|
+
})
|
|
109
|
+
export type PluginStaircaseParamUpdates = z.infer<typeof PluginStaircaseParamUpdates>
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Arguments for {@linkcode PluginDesignUpdateApi.staircase}.
|
|
113
|
+
*
|
|
114
|
+
* | Property | Type | Description |
|
|
115
|
+
* |---|---|---|
|
|
116
|
+
* | `staircases` | {@linkcode ComponentHandle}`[]` | The staircases to update |
|
|
117
|
+
* | `params` | {@linkcode PluginStaircaseParamUpdates} | Sparse parametric changes (≥ 1 field) |
|
|
118
|
+
*/
|
|
119
|
+
export const PluginDesignUpdateStaircaseArgs = z.object({
|
|
120
|
+
staircases: z.array(ComponentHandle),
|
|
121
|
+
params: PluginStaircaseParamUpdates,
|
|
122
|
+
})
|
|
123
|
+
export type PluginDesignUpdateStaircaseArgs = z.infer<typeof PluginDesignUpdateStaircaseArgs>
|
|
124
|
+
|
|
47
125
|
/**
|
|
48
126
|
* `snaptrude.design.update` — edit existing scene entities (properties + geometry).
|
|
49
127
|
*
|
|
@@ -53,6 +131,46 @@ export type PluginUpdateSpaceItem = z.infer<typeof PluginUpdateSpaceItem>
|
|
|
53
131
|
export abstract class PluginDesignUpdateApi {
|
|
54
132
|
constructor() {}
|
|
55
133
|
|
|
134
|
+
/**
|
|
135
|
+
* Set a component's **Label** — the same editable Label field the properties
|
|
136
|
+
* panel shows (doors, windows, furniture, spaces, and every other labelled
|
|
137
|
+
* component). The paired read is `design.query.getLabel`. Undoable; the
|
|
138
|
+
* canvas label text updates immediately.
|
|
139
|
+
*
|
|
140
|
+
* Strictly **per component**: labelling one door of a placed family never
|
|
141
|
+
* touches its sibling doors (the panel's Label edit, by contrast, applies to
|
|
142
|
+
* the whole family). On **legacy masses** without a space type, panel parity
|
|
143
|
+
* applies — a label matching a room-type preset with a height can also
|
|
144
|
+
* adjust the mass's height and material, exactly like typing it in the panel.
|
|
145
|
+
*
|
|
146
|
+
* Throws when the component is locked, the handle is gone/forged, the label
|
|
147
|
+
* is empty, or the component has no mesh to label. Re-applying the current
|
|
148
|
+
* label is a no-op that returns `true` without touching the undo stack.
|
|
149
|
+
*
|
|
150
|
+
* @param component - The component to relabel
|
|
151
|
+
* @param label - New label text (non-empty; leading/trailing whitespace trimmed)
|
|
152
|
+
* @returns `true` once the label is applied
|
|
153
|
+
*
|
|
154
|
+
* @examplePrompt Rename this door to D-101
|
|
155
|
+
* @examplePrompt Number all doors on storey 2 with a D-2-XX tag
|
|
156
|
+
* @examplePrompt Set the selected window's label to W-04
|
|
157
|
+
* @examplePrompt Tag every door with its fire rating
|
|
158
|
+
*
|
|
159
|
+
* # Example
|
|
160
|
+
* ```ts
|
|
161
|
+
* // Door tag numbering: label every door as D-<storey>-<n>, numbered per storey
|
|
162
|
+
* await snaptrude.design.selection.setByFilter({ types: ["door"] })
|
|
163
|
+
* const doors = await snaptrude.design.selection.get()
|
|
164
|
+
* const counts = {}
|
|
165
|
+
* for (const door of doors) {
|
|
166
|
+
* const { storey = 0 } = await snaptrude.design.query.getProperties(door)
|
|
167
|
+
* counts[storey] = (counts[storey] ?? 0) + 1
|
|
168
|
+
* await snaptrude.design.update.setLabel(door, `D-${storey}-${String(counts[storey]).padStart(2, "0")}`)
|
|
169
|
+
* }
|
|
170
|
+
* ```
|
|
171
|
+
*/
|
|
172
|
+
public abstract setLabel(component: ComponentHandle, label: string): PluginApiReturn<boolean>
|
|
173
|
+
|
|
56
174
|
/**
|
|
57
175
|
* Update a single space (room) — change its properties (room label, space
|
|
58
176
|
* type, mass type, area class, department) and/or replace its geometry by
|
|
@@ -114,4 +232,53 @@ export abstract class PluginDesignUpdateApi {
|
|
|
114
232
|
* ```
|
|
115
233
|
*/
|
|
116
234
|
public abstract spaces(items: PluginUpdateSpaceItem[]): PluginApiReturn<PluginSpaceUpdateResult[]>
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Update the parameters of one or more staircases — riser, tread, width,
|
|
238
|
+
* depth, step count, total height, well size, landing depth, base offset,
|
|
239
|
+
* and the two height locks — in one undoable operation. Sparse: only the
|
|
240
|
+
* fields you provide change. The paired read is
|
|
241
|
+
* `design.query.getStaircaseParams`.
|
|
242
|
+
*
|
|
243
|
+
* All dimensions are in **engine units** (the same convention as
|
|
244
|
+
* `design.doors.setWidth`); `steps` is a unitless integer count. The height,
|
|
245
|
+
* riser, and step count are **coupled** — `staircaseHeight = steps × riser` —
|
|
246
|
+
* and the two lock booleans govern which value recomputes when another
|
|
247
|
+
* changes. With `isStoreyHeightUnlocked` false the staircase height follows
|
|
248
|
+
* the storey height; unlocking it also unlocks the staircase height. Setting
|
|
249
|
+
* `steps` recomputes `riser` (or the height, once the height is unlocked),
|
|
250
|
+
* and setting `riser` recomputes `steps`/height in turn.
|
|
251
|
+
*
|
|
252
|
+
* Presets-only: this edits the parametric fields of a preset staircase; it
|
|
253
|
+
* does not change the staircase's type/preset (there is no in-place re-type).
|
|
254
|
+
* Every edit rebuilds the staircase geometry, so the call is async.
|
|
255
|
+
*
|
|
256
|
+
* Throws when a handle is not a staircase, when a staircase is locked, or
|
|
257
|
+
* when a value falls outside the engine's accepted range (RPC rejects).
|
|
258
|
+
*
|
|
259
|
+
* @param staircases - the staircases to update
|
|
260
|
+
* @param params - sparse {@linkcode PluginStaircaseParamUpdates} (at least one field)
|
|
261
|
+
* @returns the affected staircases
|
|
262
|
+
*
|
|
263
|
+
* @examplePrompt Set the riser of this staircase to 165mm
|
|
264
|
+
* @examplePrompt Change all the stairs to 14 steps
|
|
265
|
+
* @examplePrompt Make the selected staircase 1200 wide with a 280 tread
|
|
266
|
+
* @examplePrompt Widen every staircase on level 1 to 1 metre
|
|
267
|
+
* @examplePrompt Unlock the height and set this stair to 3200 tall
|
|
268
|
+
*
|
|
269
|
+
* # Example
|
|
270
|
+
* ```ts
|
|
271
|
+
* // Give every ground-floor staircase a common riser + tread (engine units)
|
|
272
|
+
* const stairs = await snaptrude.design.query.listStaircases({ storeys: [1] })
|
|
273
|
+
* const { affected } = await snaptrude.design.update.staircase(stairs, {
|
|
274
|
+
* riser: 165,
|
|
275
|
+
* tread: 280,
|
|
276
|
+
* })
|
|
277
|
+
* console.log("updated", affected.length, "staircases")
|
|
278
|
+
* ```
|
|
279
|
+
*/
|
|
280
|
+
public abstract staircase(
|
|
281
|
+
staircases: ComponentHandle[],
|
|
282
|
+
params: PluginStaircaseParamUpdates,
|
|
283
|
+
): PluginApiReturn<PluginDesignChangeResult>
|
|
117
284
|
}
|