@snaptrude/plugin-core 0.9.2 → 0.9.4
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 +13 -0
- package/api-manifest.full.json +25 -4
- package/api-manifest.json +25 -4
- package/dist/api/core/geom/create/index.d.ts +141 -27
- package/dist/api/core/geom/create/index.d.ts.map +1 -1
- package/dist/api/core/geom/query/brep.d.ts +68 -0
- package/dist/api/core/geom/query/brep.d.ts.map +1 -1
- package/dist/api/design/create/index.d.ts +103 -4
- package/dist/api/design/create/index.d.ts.map +1 -1
- package/dist/api/design/query/index.d.ts +10 -10
- package/dist/api/design/selection/index.d.ts +4 -4
- package/dist/api/program/index.d.ts +2 -2
- package/dist/api/program/index.d.ts.map +1 -1
- package/dist/api/program/site.d.ts +118 -7
- package/dist/api/program/site.d.ts.map +1 -1
- package/dist/handles.d.ts +2 -2
- package/dist/index.cjs +104 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +97 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/api/core/geom/create/index.ts +162 -29
- package/src/api/core/geom/query/brep.ts +74 -0
- package/src/api/design/create/index.ts +119 -4
- package/src/api/program/index.ts +2 -2
- package/src/api/program/site.ts +131 -8
- package/src/handles.ts +2 -2
package/package.json
CHANGED
|
@@ -264,28 +264,52 @@ export abstract class PluginGeomCreateApi {
|
|
|
264
264
|
/**
|
|
265
265
|
* Create a closed solid **B-rep** by lofting between a bottom and a top
|
|
266
266
|
* contour, optionally through intermediate cross-sections. Host API call —
|
|
267
|
-
* returns a {@linkcode BrepHandle}.
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
*
|
|
273
|
-
*
|
|
274
|
-
*
|
|
267
|
+
* returns a {@linkcode BrepHandle}. Sections with different edge counts are
|
|
268
|
+
* auto-matched: vertices are deterministically inserted on the smaller
|
|
269
|
+
* sections at the larger section's perimeter positions, so the same input
|
|
270
|
+
* always yields the same solid. For straight-edge (all-line) sections the
|
|
271
|
+
* seam is auto-aligned too — winding and start-vertex correspondence are
|
|
272
|
+
* chosen to avoid a twist, so outlines authored from any starting corner
|
|
273
|
+
* loft cleanly; arc-bearing sections keep the authored correspondence
|
|
274
|
+
* unchanged. A pair whose side faces would be non-planar under every
|
|
275
|
+
* correspondence still builds for hole-free straight-edged pairs — planar
|
|
276
|
+
* side quads stay quads and warped ones split into planar triangles (the
|
|
277
|
+
* triangulated lane); arc-bearing or holed twisted pairs are rejected, as is
|
|
278
|
+
* auto-matching that would pair an arc with a straight edge. Matching hole
|
|
279
|
+
* counts are allowed in the two-section form. With intermediates the loft
|
|
280
|
+
* is a chain of ruled segments folded into one solid: sections must be
|
|
281
|
+
* planar and hole-free, ordered bottom → intermediates → top. The contours
|
|
282
|
+
* are copied — the input handles are never mutated.
|
|
275
283
|
*
|
|
276
284
|
* Inspect the result via `core.geom.query.brep.*`, or commit it to the scene
|
|
277
285
|
* with `design.create.massFromBrep`.
|
|
278
286
|
*
|
|
279
287
|
* @param bottomContour The bottom cross-section
|
|
280
|
-
* @param topContour The top cross-section (
|
|
288
|
+
* @param topContour The top cross-section (edge count may differ from the
|
|
289
|
+
* bottom — sections are auto-matched)
|
|
281
290
|
* @param intermediateContours Optional in-between cross-sections, ordered
|
|
282
|
-
* bottom to top (hole-free
|
|
291
|
+
* bottom to top (hole-free; edge counts may differ — every section is
|
|
292
|
+
* normalized to the largest)
|
|
293
|
+
* @param options Optional behavior switches. `compatibility` — `"auto"`
|
|
294
|
+
* (default) inserts vertices to match differing edge counts; `"strict"`
|
|
295
|
+
* skips normalization and throws VALIDATION when outer edge counts (or,
|
|
296
|
+
* in the two-section form, hole-pair counts) differ. `seamAlignment` —
|
|
297
|
+
* `"auto"` (default) searches seam rotations on straight-edge sections to
|
|
298
|
+
* avoid a twist; `"authored"` keeps the authored start-vertex
|
|
299
|
+
* correspondence unchanged (a twisted correspondence then builds through
|
|
300
|
+
* the triangulated lane for hole-free straight-edged pairs).
|
|
301
|
+
* `{ compatibility: "strict", seamAlignment: "authored" }` reproduces
|
|
302
|
+
* pre-0.9.4 lofting exactly.
|
|
283
303
|
* @returns The new solid as a {@linkcode BrepHandle}
|
|
284
|
-
* @throws VALIDATION if the contours are coincident, their
|
|
285
|
-
*
|
|
304
|
+
* @throws VALIDATION if the contours are coincident, their hole counts
|
|
305
|
+
* differ, any section has holes when intermediates are present
|
|
286
306
|
* (multi-section lofts take hole-free sections), a section is non-planar,
|
|
287
|
-
*
|
|
288
|
-
*
|
|
307
|
+
* sections are too dissimilar to auto-match, auto-matching would pair an
|
|
308
|
+
* arc with a straight edge, or — for arc-bearing or holed pairs only — no vertex correspondence avoids a twist (a
|
|
309
|
+
* side face between corresponding edges would be non-planar — the error
|
|
310
|
+
* names the offending sections); with `compatibility: "strict"`, if
|
|
311
|
+
* section edge counts (or hole-pair counts) differ — the error names the
|
|
312
|
+
* sections and counts and points at `compatibility: "auto"`
|
|
289
313
|
* @throws OPERATION_FAILED if the lofted segments cannot be joined into a
|
|
290
314
|
* valid solid
|
|
291
315
|
*
|
|
@@ -293,6 +317,10 @@ export abstract class PluginGeomCreateApi {
|
|
|
293
317
|
* @examplePrompt Loft between a large base and a smaller top
|
|
294
318
|
* @examplePrompt Create a frustum from two squares
|
|
295
319
|
* @examplePrompt Loft the tower through these three floor outlines
|
|
320
|
+
* @examplePrompt Loft a four-sided base to a five-sided top
|
|
321
|
+
* @examplePrompt Loft these outlines even though their corners don't line up
|
|
322
|
+
* @examplePrompt Loft these outlines exactly as authored, corner to corner
|
|
323
|
+
* @examplePrompt Fail the loft if my sections don't have matching edge counts
|
|
296
324
|
*
|
|
297
325
|
* # Example
|
|
298
326
|
* ```ts
|
|
@@ -310,6 +338,7 @@ export abstract class PluginGeomCreateApi {
|
|
|
310
338
|
bottomContour: ContourHandle,
|
|
311
339
|
topContour: ContourHandle,
|
|
312
340
|
intermediateContours?: ContourHandle[],
|
|
341
|
+
options?: { compatibility?: "strict" | "auto"; seamAlignment?: "authored" | "auto" },
|
|
313
342
|
): PluginApiReturn<BrepHandle>
|
|
314
343
|
|
|
315
344
|
/**
|
|
@@ -636,24 +665,59 @@ export abstract class PluginGeomCreateApi {
|
|
|
636
665
|
): PluginApiReturn<BrepHandle>
|
|
637
666
|
|
|
638
667
|
/**
|
|
639
|
-
* Sweeps a planar profile along
|
|
640
|
-
*
|
|
668
|
+
* Sweeps a planar profile along a polyline path, with mitred corners at
|
|
669
|
+
* each bend. Host API call — returns a new {@linkcode BrepHandle}; the
|
|
641
670
|
* input contour is read-only. The profile must be hole-free and must not
|
|
642
|
-
* lie in a plane containing the first path segment's direction.
|
|
643
|
-
*
|
|
644
|
-
*
|
|
645
|
-
*
|
|
671
|
+
* lie in a plane containing the first path segment's direction. Repeating
|
|
672
|
+
* the first point as the last closes the path into a ring solid (a picture
|
|
673
|
+
* frame): closed paths must be flat and straight-segmented and take a
|
|
674
|
+
* straight-edged (arc-free) profile — the ring's cross-section is the
|
|
675
|
+
* authored profile projected along the first leg onto the first mitre
|
|
676
|
+
* plane, so a profile not perpendicular to the first leg appears sheared.
|
|
677
|
+
* An optional scale law tapers the sweep: `startScale`/`endScale`
|
|
678
|
+
* interpolate linearly by arc length, or `scales` gives one factor per path
|
|
679
|
+
* point (scaling is about the profile centroid). A uniform scale (all
|
|
680
|
+
* factors equal) works on any path today's sweep accepts; a varying scale
|
|
681
|
+
* takes straight (collinear) open paths with straight-edged profiles —
|
|
682
|
+
* tapering across a bend or along an arc edge would create faces Snaptrude
|
|
683
|
+
* cannot represent and throws (sweep each straight run separately and union
|
|
684
|
+
* the results instead). An optional `transition` picks the corner
|
|
685
|
+
* treatment: `"miter"` (default) is today's sharp mitred bend;
|
|
686
|
+
* `{ bevel: b }` chamfers each turning corner by cutting `b` back along
|
|
687
|
+
* both adjacent legs (works on open and closed paths — each leg must be
|
|
688
|
+
* long enough for its cuts). On a corner-less path `transition` has no effect, and the
|
|
689
|
+
* varying-scale lane takes straight paths, so it never sees a corner.
|
|
690
|
+
*
|
|
691
|
+
* Open paths run on the OpenCascade kernel (the first kernel call loads a
|
|
692
|
+
* wasm of tens of MB — expect a pause of seconds); closed paths are
|
|
693
|
+
* computed host-side and never load the kernel.
|
|
646
694
|
*
|
|
647
695
|
* Inspect the result via `core.geom.query.brep.*`, or commit it to the scene
|
|
648
696
|
* with `design.create.massFromBrep`.
|
|
649
697
|
*
|
|
650
698
|
* @param profile The cross-section to sweep (hole-free contour)
|
|
651
|
-
* @param path The polyline path as plain `{x, y, z}` points (≥2
|
|
699
|
+
* @param path The polyline path as plain `{x, y, z}` points (≥2; repeat the
|
|
700
|
+
* first point as the last to close the path into a ring)
|
|
701
|
+
* @param options Optional scale law and corner transition. Scale law:
|
|
702
|
+
* `startScale`/`endScale` (linear taper by arc length) or `scales` (one
|
|
703
|
+
* factor per path point; mutually exclusive with
|
|
704
|
+
* `startScale`/`endScale`); every factor must be finite and within
|
|
705
|
+
* 1e-3..1e3. `transition`: `"miter"` (default, sharp corners) or
|
|
706
|
+
* `{ bevel: b }` (chamfered corners, `b` finite within 1e-3..1e3; open
|
|
707
|
+
* and closed paths)
|
|
652
708
|
* @returns The swept solid as a new {@linkcode BrepHandle}
|
|
653
709
|
* @throws VALIDATION if the profile has holes, the path has fewer than 2
|
|
654
|
-
* points, consecutive path points coincide,
|
|
655
|
-
*
|
|
656
|
-
*
|
|
710
|
+
* points, consecutive path points coincide, a coordinate is not finite,
|
|
711
|
+
* or the profile plane contains the first path segment's direction
|
|
712
|
+
* (degenerate sweep); for closed paths, if the ring has fewer than 3
|
|
713
|
+
* distinct corners, the path is non-planar, a corner doubles back, the
|
|
714
|
+
* profile has an arc edge, or the profile is too large for a mitred
|
|
715
|
+
* corner; for scale options, if a factor is outside 1e-3..1e3, `scales`
|
|
716
|
+
* is combined with `startScale`/`endScale`, `scales` does not match the
|
|
717
|
+
* path length, or a varying scale is used on a bent or closed path or
|
|
718
|
+
* with an arc-edged profile; for `{ bevel }`, if a leg is too short for
|
|
719
|
+
* the cuts its beveled corners take from it or a corner nearly doubles
|
|
720
|
+
* back so its chamfer collapses (the errors name the corner)
|
|
657
721
|
* @throws OPERATION_FAILED if the kernel cannot sweep the profile into a
|
|
658
722
|
* valid solid or a transition patch is a curved surface Snaptrude cannot
|
|
659
723
|
* represent
|
|
@@ -661,6 +725,10 @@ export abstract class PluginGeomCreateApi {
|
|
|
661
725
|
* @examplePrompt Sweep this profile along the corridor path
|
|
662
726
|
* @examplePrompt Extrude the railing section along this route
|
|
663
727
|
* @examplePrompt Run a duct profile through these points
|
|
728
|
+
* @examplePrompt Sweep a frame profile around a closed rectangular loop
|
|
729
|
+
* @examplePrompt Taper this duct from 1x1 to 2x2 along its run
|
|
730
|
+
* @examplePrompt Sweep a column section that narrows toward the top
|
|
731
|
+
* @examplePrompt Chamfer the corners of this swept frame by 200mm
|
|
664
732
|
*
|
|
665
733
|
* # Example
|
|
666
734
|
* ```ts
|
|
@@ -673,11 +741,34 @@ export abstract class PluginGeomCreateApi {
|
|
|
673
741
|
* { x: 0, y: 2.8, z: 0 },
|
|
674
742
|
* { x: 10, y: 2.8, z: 0 },
|
|
675
743
|
* ])
|
|
744
|
+
* // A closed ring (picture frame): repeat the first point as the last
|
|
745
|
+
* const frame = await snaptrude.core.geom.create.brepFromSweep(profile, [
|
|
746
|
+
* { x: 0, y: 0, z: 0 },
|
|
747
|
+
* { x: 0, y: 3, z: 0 },
|
|
748
|
+
* { x: 4, y: 3, z: 0 },
|
|
749
|
+
* { x: 4, y: 0, z: 0 },
|
|
750
|
+
* { x: 0, y: 0, z: 0 },
|
|
751
|
+
* ])
|
|
752
|
+
* // A tapered straight run: 0.4x0.4 at the start, 0.8x0.8 at the end
|
|
753
|
+
* const flue = await snaptrude.core.geom.create.brepFromSweep(
|
|
754
|
+
* profile,
|
|
755
|
+
* [
|
|
756
|
+
* { x: 0, y: 0, z: 0 },
|
|
757
|
+
* { x: 0, y: 10, z: 0 },
|
|
758
|
+
* ],
|
|
759
|
+
* { startScale: 1, endScale: 2 },
|
|
760
|
+
* )
|
|
676
761
|
* ```
|
|
677
762
|
*/
|
|
678
763
|
public abstract brepFromSweep(
|
|
679
764
|
profile: ContourHandle,
|
|
680
765
|
path: Vec3Components[],
|
|
766
|
+
options?: {
|
|
767
|
+
startScale?: number
|
|
768
|
+
endScale?: number
|
|
769
|
+
scales?: number[]
|
|
770
|
+
transition?: "miter" | { bevel: number }
|
|
771
|
+
},
|
|
681
772
|
): PluginApiReturn<BrepHandle>
|
|
682
773
|
|
|
683
774
|
/**
|
|
@@ -897,11 +988,18 @@ export type PluginGeomCreateBrepFromExtrusionArgs = z.infer<
|
|
|
897
988
|
* | `bottomContour` | {@linkcode ContourHandle} | The bottom cross-section |
|
|
898
989
|
* | `topContour` | {@linkcode ContourHandle} | The top cross-section |
|
|
899
990
|
* | `intermediateContours` | {@linkcode ContourHandle}`[]`? | Optional in-between cross-sections, ordered bottom to top |
|
|
991
|
+
* | `options` | `object`? | Optional behavior switches: `compatibility` (`"strict"` throws on mismatched edge counts instead of auto-matching; default `"auto"`) and `seamAlignment` (`"authored"` keeps the authored correspondence instead of searching seam rotations; default `"auto"`) |
|
|
900
992
|
*/
|
|
901
993
|
export const PluginGeomCreateBrepFromLoftArgs = z.object({
|
|
902
994
|
bottomContour: ContourHandle,
|
|
903
995
|
topContour: ContourHandle,
|
|
904
996
|
intermediateContours: z.array(ContourHandle).optional(),
|
|
997
|
+
options: z
|
|
998
|
+
.object({
|
|
999
|
+
compatibility: z.enum(["strict", "auto"]).optional(),
|
|
1000
|
+
seamAlignment: z.enum(["authored", "auto"]).optional(),
|
|
1001
|
+
})
|
|
1002
|
+
.optional(),
|
|
905
1003
|
})
|
|
906
1004
|
|
|
907
1005
|
export type PluginGeomCreateBrepFromLoftArgs = z.infer<typeof PluginGeomCreateBrepFromLoftArgs>
|
|
@@ -1034,18 +1132,53 @@ export type PluginGeomCreateBrepFromChamferArgs = z.infer<
|
|
|
1034
1132
|
typeof PluginGeomCreateBrepFromChamferArgs
|
|
1035
1133
|
>
|
|
1036
1134
|
|
|
1135
|
+
// Taper scale factor — bounded so a near-zero scale cannot pinch a section
|
|
1136
|
+
// into a degenerate sliver the downstream planarity gates would miss.
|
|
1137
|
+
const SweepScaleFactor = z.number().finite().min(1e-3).max(1e3)
|
|
1138
|
+
|
|
1037
1139
|
/**
|
|
1038
1140
|
* Arguments for {@linkcode PluginGeomCreateApi.brepFromSweep}.
|
|
1039
1141
|
*
|
|
1040
1142
|
* | Property | Type | Description |
|
|
1041
1143
|
* |---|---|---|
|
|
1042
1144
|
* | `profile` | {@linkcode ContourHandle} | The cross-section to sweep (hole-free) |
|
|
1043
|
-
* | `path` | {@linkcode Vec3Components}`[]` | The
|
|
1145
|
+
* | `path` | {@linkcode Vec3Components}`[]` | The polyline path (≥2 points, finite components; repeating the first point as the last closes it into a ring — closed paths must be flat and straight-segmented and take a straight-edged profile) |
|
|
1146
|
+
* | `options` | `object`? | Optional scale law: `startScale`/`endScale` (linear taper by arc length) or per-point `scales` (mutually exclusive; factors finite, 1e-3..1e3; `scales.length` must equal `path.length`). Optional corner `transition`: `"miter"` (default) or `{ bevel }` (chamfered corners; finite, 1e-3..1e3) |
|
|
1044
1147
|
*/
|
|
1045
|
-
export const PluginGeomCreateBrepFromSweepArgs = z
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1148
|
+
export const PluginGeomCreateBrepFromSweepArgs = z
|
|
1149
|
+
.object({
|
|
1150
|
+
profile: ContourHandle,
|
|
1151
|
+
path: z.array(FiniteVec3Components).min(2),
|
|
1152
|
+
options: z
|
|
1153
|
+
.object({
|
|
1154
|
+
startScale: SweepScaleFactor.optional(),
|
|
1155
|
+
endScale: SweepScaleFactor.optional(),
|
|
1156
|
+
scales: z.array(SweepScaleFactor).optional(),
|
|
1157
|
+
transition: z
|
|
1158
|
+
.union([
|
|
1159
|
+
z.literal("miter"),
|
|
1160
|
+
z.object({ bevel: z.number().finite().min(1e-3).max(1e3) }),
|
|
1161
|
+
])
|
|
1162
|
+
.optional(),
|
|
1163
|
+
})
|
|
1164
|
+
.optional(),
|
|
1165
|
+
})
|
|
1166
|
+
.superRefine((a, ctx) => {
|
|
1167
|
+
const options = a.options
|
|
1168
|
+
if (!options || options.scales === undefined) return
|
|
1169
|
+
if (options.startScale !== undefined || options.endScale !== undefined) {
|
|
1170
|
+
ctx.addIssue({
|
|
1171
|
+
code: z.ZodIssueCode.custom,
|
|
1172
|
+
message: "scales is mutually exclusive with startScale/endScale",
|
|
1173
|
+
})
|
|
1174
|
+
}
|
|
1175
|
+
if (options.scales.length !== a.path.length) {
|
|
1176
|
+
ctx.addIssue({
|
|
1177
|
+
code: z.ZodIssueCode.custom,
|
|
1178
|
+
message: "scales must have exactly one entry per path point",
|
|
1179
|
+
})
|
|
1180
|
+
}
|
|
1181
|
+
})
|
|
1049
1182
|
|
|
1050
1183
|
export type PluginGeomCreateBrepFromSweepArgs = z.infer<typeof PluginGeomCreateBrepFromSweepArgs>
|
|
1051
1184
|
|
|
@@ -163,6 +163,64 @@ export abstract class PluginGeomQueryBrepApi {
|
|
|
163
163
|
vertexIndexB: number
|
|
164
164
|
): PluginApiReturn<EdgeHandle[]>
|
|
165
165
|
|
|
166
|
+
/**
|
|
167
|
+
* Read the exact curve of a brep edge as plain values — a discriminated
|
|
168
|
+
* union on `type`. Pure read: no geometry kernel is loaded. Points and
|
|
169
|
+
* directions are {@linkcode Vec3Components}; lengths and radii are raw
|
|
170
|
+
* Babylon units.
|
|
171
|
+
*
|
|
172
|
+
* | `type` | Fields |
|
|
173
|
+
* |---|---|
|
|
174
|
+
* | `"line"` | `startPoint`, `endPoint`, `length` |
|
|
175
|
+
* | `"arc"` | `startPoint`, `endPoint`, `centre`, `axis`, `radius`, `length` |
|
|
176
|
+
* | `"circle"` | `centre`, `axis`, `radius`, `length` — a full-circle edge (e.g. a cylinder cap rim) |
|
|
177
|
+
*
|
|
178
|
+
* @param brep The brep the edge belongs to
|
|
179
|
+
* @param edge The edge whose curve to read
|
|
180
|
+
* @returns The curve data as a discriminated union on `type` (see table above)
|
|
181
|
+
* @throws VALIDATION if the edge is not on `brep`
|
|
182
|
+
*
|
|
183
|
+
* @examplePrompt Read the radius of this curved edge
|
|
184
|
+
* @examplePrompt Is this edge straight or an arc?
|
|
185
|
+
* @examplePrompt Get the exact start and end points of this edge
|
|
186
|
+
* @examplePrompt Measure the length of each edge of this mass
|
|
187
|
+
*
|
|
188
|
+
* # Example
|
|
189
|
+
* ```ts
|
|
190
|
+
* const [mass] = await snaptrude.design.query.listMasses()
|
|
191
|
+
* const brep = await snaptrude.design.query.geometry.getBrep(mass)
|
|
192
|
+
* if (brep) {
|
|
193
|
+
* const edges = await snaptrude.core.geom.query.brep.listEdges(brep)
|
|
194
|
+
* for (const edge of edges) {
|
|
195
|
+
* const curve = await snaptrude.core.geom.query.brep.getEdgeCurve(brep, edge)
|
|
196
|
+
* if (curve.type === "arc") console.log("arc radius:", curve.radius)
|
|
197
|
+
* }
|
|
198
|
+
* }
|
|
199
|
+
* ```
|
|
200
|
+
*/
|
|
201
|
+
public abstract getEdgeCurve(
|
|
202
|
+
brep: BrepHandle,
|
|
203
|
+
edge: EdgeHandle
|
|
204
|
+
): PluginApiReturn<
|
|
205
|
+
| { type: "line"; startPoint: Vec3Components; endPoint: Vec3Components; length: number }
|
|
206
|
+
| {
|
|
207
|
+
type: "arc"
|
|
208
|
+
startPoint: Vec3Components
|
|
209
|
+
endPoint: Vec3Components
|
|
210
|
+
centre: Vec3Components
|
|
211
|
+
axis: Vec3Components
|
|
212
|
+
radius: number
|
|
213
|
+
length: number
|
|
214
|
+
}
|
|
215
|
+
| {
|
|
216
|
+
type: "circle"
|
|
217
|
+
centre: Vec3Components
|
|
218
|
+
axis: Vec3Components
|
|
219
|
+
radius: number
|
|
220
|
+
length: number
|
|
221
|
+
}
|
|
222
|
+
>
|
|
223
|
+
|
|
166
224
|
/**
|
|
167
225
|
* Get the position of a vertex within a brep.
|
|
168
226
|
* @param brep The brep to query
|
|
@@ -408,6 +466,22 @@ export type PluginGeomQueryBrepListEdgesBetweenArgs = z.infer<
|
|
|
408
466
|
typeof PluginGeomQueryBrepListEdgesBetweenArgs
|
|
409
467
|
>
|
|
410
468
|
|
|
469
|
+
/**
|
|
470
|
+
* Arguments for {@linkcode PluginGeomQueryBrepApi.getEdgeCurve}.
|
|
471
|
+
*
|
|
472
|
+
* | Property | Type | Description |
|
|
473
|
+
* |---|---|---|
|
|
474
|
+
* | `brep` | {@linkcode BrepHandle} | The brep the edge belongs to |
|
|
475
|
+
* | `edge` | {@linkcode EdgeHandle} | The edge whose curve to read |
|
|
476
|
+
*/
|
|
477
|
+
export const PluginGeomQueryBrepGetEdgeCurveArgs = z.object({
|
|
478
|
+
brep: BrepHandle,
|
|
479
|
+
edge: EdgeHandle,
|
|
480
|
+
})
|
|
481
|
+
export type PluginGeomQueryBrepGetEdgeCurveArgs = z.infer<
|
|
482
|
+
typeof PluginGeomQueryBrepGetEdgeCurveArgs
|
|
483
|
+
>
|
|
484
|
+
|
|
411
485
|
/**
|
|
412
486
|
* Arguments for {@linkcode PluginGeomQueryBrepApi.getVertexPosition}.
|
|
413
487
|
*
|
|
@@ -389,9 +389,15 @@ export abstract class PluginDesignCreateApi {
|
|
|
389
389
|
* Drives the wall's construction layers and default material, and — when
|
|
390
390
|
* `thickness` is omitted — its total layer thickness. Default: the engine's
|
|
391
391
|
* generic wall.
|
|
392
|
+
* @param storey - Target storey (integer: `1` ground, `2` first floor, `-1`
|
|
393
|
+
* basement — same convention as `design.create.space`). The walls are
|
|
394
|
+
* assigned to it and lifted to its base elevation, all inside the same
|
|
395
|
+
* single undo entry. The storey must already exist
|
|
396
|
+
* (`core.storeys.create` first). Default: geometry-derived — a profile
|
|
397
|
+
* drawn at y = 0 lands on storey 1; the ACTIVE storey is NOT consulted.
|
|
392
398
|
* @returns a {@linkcode ComponentHandle}`[]` (one per wall), in profile order
|
|
393
399
|
* @throws if the profile is empty, `wallType` names no wall type in the
|
|
394
|
-
* project, or wall creation fails
|
|
400
|
+
* project, `storey` does not exist, or wall creation fails
|
|
395
401
|
*
|
|
396
402
|
* @examplePrompt Draw walls along this centerline
|
|
397
403
|
* @examplePrompt Create 3m high, 200mm thick walls from these points
|
|
@@ -422,6 +428,7 @@ export abstract class PluginDesignCreateApi {
|
|
|
422
428
|
height?: number,
|
|
423
429
|
thickness?: number,
|
|
424
430
|
wallType?: string,
|
|
431
|
+
storey?: number,
|
|
425
432
|
): PluginApiReturn<ComponentHandle[]>
|
|
426
433
|
|
|
427
434
|
/**
|
|
@@ -429,12 +436,17 @@ export abstract class PluginDesignCreateApi {
|
|
|
429
436
|
*
|
|
430
437
|
* Geometry is procedurally generated from the chosen `preset`; there is no
|
|
431
438
|
* footprint input. Placed on the active structure/story unless overridden.
|
|
439
|
+
* The promise resolves only after the staircase's asynchronous geometry union
|
|
440
|
+
* and creation-command capture settle; a union failure rejects the call.
|
|
432
441
|
*
|
|
433
442
|
* @param preset - The parametric preset to generate
|
|
434
443
|
* @param position - Placement point
|
|
435
444
|
* @param label - Optional label
|
|
436
445
|
* @param structureId - Target structure (default active)
|
|
437
446
|
* @param level - Target level name (default active/`"01"`)
|
|
447
|
+
* @param dimensions - Optional positive dimension overrides in Snaptrude engine
|
|
448
|
+
* units, plus a placement angle in 90-degree increments. Overrides are
|
|
449
|
+
* applied to the staircase parameters before the creation command runs.
|
|
438
450
|
* @returns the {@linkcode ComponentHandle} of the created staircase
|
|
439
451
|
* @throws if the preset is unknown or placement fails
|
|
440
452
|
*
|
|
@@ -458,6 +470,7 @@ export abstract class PluginDesignCreateApi {
|
|
|
458
470
|
label?: string,
|
|
459
471
|
structureId?: string,
|
|
460
472
|
level?: string,
|
|
473
|
+
dimensions?: PluginStaircaseDimensions,
|
|
461
474
|
): PluginApiReturn<ComponentHandle>
|
|
462
475
|
|
|
463
476
|
/**
|
|
@@ -517,7 +530,11 @@ export abstract class PluginDesignCreateApi {
|
|
|
517
530
|
* entry). To rotate an existing instance instead, use `design.transform.rotate`.
|
|
518
531
|
*
|
|
519
532
|
* @param catalogId - Library id: team `_id` or general `fullName`
|
|
520
|
-
* @param position - Absolute world placement point
|
|
533
|
+
* @param position - Absolute world placement point. `position.y` is the REST
|
|
534
|
+
* elevation: the item is grounded so its bounding-box base sits exactly at
|
|
535
|
+
* `position.y` (the same surface-flush contract as interactive drag-drop) —
|
|
536
|
+
* pass the floor/storey elevation to stand furniture on it; never add half
|
|
537
|
+
* the item's height yourself.
|
|
521
538
|
* @param options - Optional placement options: `label` — instance name
|
|
522
539
|
* (default auto `${name}Ins${n}`); `createNewSourceMesh` — emit a
|
|
523
540
|
* source-mesh creation command (default `true`)
|
|
@@ -665,6 +682,43 @@ export abstract class PluginDesignCreateApi {
|
|
|
665
682
|
facing?: Vec3Handle,
|
|
666
683
|
): PluginApiReturn<ComponentHandle>
|
|
667
684
|
|
|
685
|
+
/**
|
|
686
|
+
* Place a catalog **door or window** into a host wall with optional size overrides.
|
|
687
|
+
*
|
|
688
|
+
* The world `position` is projected onto the host wall to locate the opening.
|
|
689
|
+
* All supplied dimensions use Snaptrude engine units. Window `sillHeight` is
|
|
690
|
+
* measured from the host wall base to the bottom of the window, not to
|
|
691
|
+
* its center. Placement is asynchronous and committed as one undoable creation.
|
|
692
|
+
*
|
|
693
|
+
* @param options - Opening kind, catalog and host references, placement, and
|
|
694
|
+
* optional facing, label, and dimensions
|
|
695
|
+
* @returns the {@linkcode ComponentHandle} of the placed door or window
|
|
696
|
+
* @throws if the options are invalid, the catalog id is unknown, the host is
|
|
697
|
+
* not a wall, loading fails, or the opening cannot be placed on the host
|
|
698
|
+
*
|
|
699
|
+
* @examplePrompt Add a 1m wide door to this wall here
|
|
700
|
+
* @examplePrompt Place a window with a 0.9m sill height on the selected wall
|
|
701
|
+
*
|
|
702
|
+
* @performance Single-opening creator — use it for one hosted opening. A plural
|
|
703
|
+
* API is intentionally unavailable until host placement can be atomic.
|
|
704
|
+
*
|
|
705
|
+
* # Example
|
|
706
|
+
* ```ts
|
|
707
|
+
* const opening = await snaptrude.design.create.opening({
|
|
708
|
+
* kind: "window",
|
|
709
|
+
* catalogId: windowType.id,
|
|
710
|
+
* hostWall: wall,
|
|
711
|
+
* position: await snaptrude.core.math.vec3.new(3, 0, 5),
|
|
712
|
+
* width: 1.2,
|
|
713
|
+
* height: 1.5,
|
|
714
|
+
* sillHeight: 0.9,
|
|
715
|
+
* })
|
|
716
|
+
* ```
|
|
717
|
+
*/
|
|
718
|
+
public abstract opening(
|
|
719
|
+
options: PluginDesignCreateOpeningOptions,
|
|
720
|
+
): PluginApiReturn<ComponentHandle>
|
|
721
|
+
|
|
668
722
|
/**
|
|
669
723
|
* Place a **smart layout** — clone an in-scene template cluster (walls,
|
|
670
724
|
* furniture, doors, windows, floors) into one or more target ROOM/DEPARTMENT
|
|
@@ -908,6 +962,24 @@ export const PluginStaircasePreset = z.enum([
|
|
|
908
962
|
])
|
|
909
963
|
export type PluginStaircasePreset = z.infer<typeof PluginStaircasePreset>
|
|
910
964
|
|
|
965
|
+
/**
|
|
966
|
+
* Optional staircase dimension overrides. Lengths use Snaptrude engine units
|
|
967
|
+
* and must be positive. `angleInDegrees` controls placement about the vertical
|
|
968
|
+
* axis and must be a multiple of 90 degrees.
|
|
969
|
+
*/
|
|
970
|
+
export const PluginStaircaseDimensions = z
|
|
971
|
+
.object({
|
|
972
|
+
width: z.number().finite().positive().optional(),
|
|
973
|
+
tread: z.number().finite().positive().optional(),
|
|
974
|
+
riser: z.number().finite().positive().optional(),
|
|
975
|
+
landingWidth: z.number().finite().positive().optional(),
|
|
976
|
+
angleInDegrees: z.number().finite().multipleOf(90).optional(),
|
|
977
|
+
})
|
|
978
|
+
.strict()
|
|
979
|
+
export type PluginStaircaseDimensions = z.infer<
|
|
980
|
+
typeof PluginStaircaseDimensions
|
|
981
|
+
>
|
|
982
|
+
|
|
911
983
|
// ---------------------------------------------------------------------------
|
|
912
984
|
// space
|
|
913
985
|
// ---------------------------------------------------------------------------
|
|
@@ -1135,18 +1207,20 @@ export type PluginDesignCreateBeamArgs = z.infer<
|
|
|
1135
1207
|
* | `height` | `number`? | Wall height (default engine default) |
|
|
1136
1208
|
* | `thickness` | `number`? | Wall thickness (default: `wallType`'s total layer thickness when given, else engine default) |
|
|
1137
1209
|
* | `wallType` | `string`? | Wall type name from `design.types.list("wall")` — layers/material/thickness defaults (default generic) |
|
|
1210
|
+
* | `storey` | `number`? | Target storey (integer, must exist) — walls assigned + lifted to its base in the same undo entry (default: geometry-derived) |
|
|
1138
1211
|
*/
|
|
1139
1212
|
export const PluginDesignCreateWallsArgs = z.object({
|
|
1140
1213
|
profile: ProfileHandle,
|
|
1141
1214
|
height: z.number().optional(),
|
|
1142
1215
|
thickness: z.number().optional(),
|
|
1143
1216
|
wallType: z.string().min(1).optional(),
|
|
1217
|
+
storey: z.number().int().optional(),
|
|
1144
1218
|
})
|
|
1145
1219
|
export type PluginDesignCreateWallsArgs = z.infer<
|
|
1146
1220
|
typeof PluginDesignCreateWallsArgs
|
|
1147
1221
|
>
|
|
1148
|
-
// TRANSPORT: positional signature shipped —
|
|
1149
|
-
// walls(profile: ProfileHandle, height?: number, thickness?: number, wallType?: string)
|
|
1222
|
+
// TRANSPORT: positional signature shipped — storey is the trailing 5th arg:
|
|
1223
|
+
// walls(profile: ProfileHandle, height?: number, thickness?: number, wallType?: string, storey?: number)
|
|
1150
1224
|
|
|
1151
1225
|
// ---------------------------------------------------------------------------
|
|
1152
1226
|
// staircase
|
|
@@ -1162,6 +1236,7 @@ export type PluginDesignCreateWallsArgs = z.infer<
|
|
|
1162
1236
|
* | `label` | `string`? | Optional label |
|
|
1163
1237
|
* | `structureId` | `string`? | Target structure (default active) |
|
|
1164
1238
|
* | `level` | `string`? | Target level name (default active/`"01"`) |
|
|
1239
|
+
* | `dimensions` | {@linkcode PluginStaircaseDimensions}? | Positive dimension overrides in engine units, applied before creation |
|
|
1165
1240
|
*/
|
|
1166
1241
|
export const PluginDesignCreateStaircaseArgs = z.object({
|
|
1167
1242
|
preset: PluginStaircasePreset,
|
|
@@ -1169,6 +1244,7 @@ export const PluginDesignCreateStaircaseArgs = z.object({
|
|
|
1169
1244
|
label: z.string().optional(),
|
|
1170
1245
|
structureId: z.string().optional(),
|
|
1171
1246
|
level: z.string().optional(),
|
|
1247
|
+
dimensions: PluginStaircaseDimensions.optional(),
|
|
1172
1248
|
})
|
|
1173
1249
|
export type PluginDesignCreateStaircaseArgs = z.infer<
|
|
1174
1250
|
typeof PluginDesignCreateStaircaseArgs
|
|
@@ -1258,6 +1334,45 @@ export type PluginDesignCreateWindowArgs = z.infer<
|
|
|
1258
1334
|
// TRANSPORT: positional signature shipped — facing is the trailing 5th arg:
|
|
1259
1335
|
// window(catalogId: string, hostWall: ComponentHandle, position: Vec3Handle, options?: { label? }, facing?: Vec3Handle)
|
|
1260
1336
|
|
|
1337
|
+
// ---------------------------------------------------------------------------
|
|
1338
|
+
// opening
|
|
1339
|
+
// ---------------------------------------------------------------------------
|
|
1340
|
+
|
|
1341
|
+
const PluginOpeningBaseOptions = {
|
|
1342
|
+
catalogId: z.string().min(1),
|
|
1343
|
+
hostWall: ComponentHandle,
|
|
1344
|
+
position: Vec3Handle,
|
|
1345
|
+
facing: Vec3Handle.optional(),
|
|
1346
|
+
label: z.string().optional(),
|
|
1347
|
+
width: z.number().finite().positive().optional(),
|
|
1348
|
+
height: z.number().finite().positive().optional(),
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
/**
|
|
1352
|
+
* Options for {@linkcode PluginDesignCreateApi.opening}, discriminated by
|
|
1353
|
+
* `kind`. Width and height are positive Snaptrude engine-unit values.
|
|
1354
|
+
* `sillHeight` is available only for windows, is nonnegative, and is precisely
|
|
1355
|
+
* the distance from the host wall base to the bottom of the window.
|
|
1356
|
+
*/
|
|
1357
|
+
export const PluginDesignCreateOpeningOptions = z.discriminatedUnion("kind", [
|
|
1358
|
+
z
|
|
1359
|
+
.object({
|
|
1360
|
+
kind: z.literal("door"),
|
|
1361
|
+
...PluginOpeningBaseOptions,
|
|
1362
|
+
})
|
|
1363
|
+
.strict(),
|
|
1364
|
+
z
|
|
1365
|
+
.object({
|
|
1366
|
+
kind: z.literal("window"),
|
|
1367
|
+
...PluginOpeningBaseOptions,
|
|
1368
|
+
sillHeight: z.number().finite().nonnegative().optional(),
|
|
1369
|
+
})
|
|
1370
|
+
.strict(),
|
|
1371
|
+
])
|
|
1372
|
+
export type PluginDesignCreateOpeningOptions = z.infer<
|
|
1373
|
+
typeof PluginDesignCreateOpeningOptions
|
|
1374
|
+
>
|
|
1375
|
+
|
|
1261
1376
|
// ---------------------------------------------------------------------------
|
|
1262
1377
|
// smartLayout
|
|
1263
1378
|
// ---------------------------------------------------------------------------
|
package/src/api/program/index.ts
CHANGED
|
@@ -23,7 +23,7 @@ import { PluginProgramAreasApi } from "./areas"
|
|
|
23
23
|
* - {@linkcode PluginProgramApi.metrics} — Read the area-program summary
|
|
24
24
|
* - {@linkcode PluginProgramApi.areas} — FAR / built-up-area rollup and groupings
|
|
25
25
|
* - {@linkcode PluginProgramApi.spreadsheet} — Render program data to sheets and read it back
|
|
26
|
-
* - {@linkcode PluginProgramApi.site} — Read
|
|
26
|
+
* - {@linkcode PluginProgramApi.site} — Read site/plot context and read/update Site Analysis
|
|
27
27
|
* - {@linkcode PluginProgramApi.cores} — Read & predicate vertical-circulation cores (reads only)
|
|
28
28
|
* - {@linkcode PluginProgramApi.classification} — Classification catalog + display tags
|
|
29
29
|
*
|
|
@@ -42,7 +42,7 @@ export abstract class PluginProgramApi {
|
|
|
42
42
|
public abstract areas: PluginProgramAreasApi
|
|
43
43
|
/** Spreadsheet reports — render tables to sheets, export, list, read back. See {@linkcode PluginProgramSpreadsheetApi}. */
|
|
44
44
|
public abstract spreadsheet: PluginProgramSpreadsheetApi
|
|
45
|
-
/** Site/plot
|
|
45
|
+
/** Site/plot context and persisted Site Analysis. See {@linkcode PluginProgramSiteApi}. */
|
|
46
46
|
public abstract site: PluginProgramSiteApi
|
|
47
47
|
/** Vertical-circulation cores — read & predicate (reads only). See {@linkcode PluginProgramCoresApi}. */
|
|
48
48
|
public abstract cores: PluginProgramCoresApi
|