forgecad 0.9.5 → 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/dist/assets/{AdminPage-uTtcSXtn.js → AdminPage-DX0mpSZT.js} +1 -1
- package/dist/assets/{BlogPage-DYJMjWx3.js → BlogPage-CI_P0_Pf.js} +1 -1
- package/dist/assets/{DocsPage-C58f0K5v.js → DocsPage-DLhIIZyJ.js} +3 -3
- package/dist/assets/EditorApp-BujZvuwX.js +12874 -0
- package/dist/assets/{EditorApp-DS0AIUrZ.css → EditorApp-DfFT2Dn8.css} +1 -0
- package/dist/assets/{EmbedViewer-CMXWA2LX.js → EmbedViewer-0S0qXKog.js} +2 -2
- package/dist/assets/{LandingPageProofDriven-CAu2OZFn.js → LandingPageProofDriven-O_yMtAri.js} +1 -1
- package/dist/assets/{PricingPage-BIgW7m3X.js → PricingPage-DGkX3Ahr.js} +1 -1
- package/dist/assets/{SettingsPage-N1l1tMXO.js → SettingsPage-DBsqTB_y.js} +82 -22
- package/dist/assets/{app-CFy7g5WP.js → app-BE2nD6Yz.js} +1246 -191
- package/dist/assets/cli/{render-BrVVdj_T.js → render-iP9qh475.js} +841 -586
- package/dist/assets/{evalWorker-c_SB9gg3.js → evalWorker-Ds5U4xtN.js} +2732 -112
- package/dist/assets/inspectWorker-Dll4eVyD.js +12620 -0
- package/dist/assets/{manifold-Dp6pvFr6.js → manifold-Bk26ViCr.js} +1 -1
- package/dist/assets/{manifold-CRoBhJKH.js → manifold-DjYsd7A_.js} +2 -2
- package/dist/assets/{manifold-Cjk7WhRs.js → manifold-sJ-axdXM.js} +1 -1
- package/dist/assets/{renderSceneState-3DfsSASX.js → renderSceneState-Bngp5MrQ.js} +1 -1
- package/dist/assets/{reportWorker-BLkuIoS8.js → reportWorker-CU8RZ4O0.js} +2715 -112
- package/dist/assets/{sectionPlaneMath-CykEnkvQ.js → sectionPlaneMath-BdTjyVfs.js} +3213 -252
- package/dist/cli/render.html +1 -1
- package/dist/docs/index.html +1 -1
- package/dist/docs-raw/AI/usage.md +7 -2
- package/dist/docs-raw/CLI.md +82 -53
- package/dist/docs-raw/beta-operations.md +9 -0
- package/dist/docs-raw/coding.md +1 -1
- package/dist/docs-raw/deployment.md +38 -23
- package/dist/docs-raw/generated/concepts.md +141 -7
- package/dist/docs-raw/generated/core.md +206 -1
- package/dist/docs-raw/generated/curves.md +97 -5
- package/dist/docs-raw/generated/lib.md +17 -1
- package/dist/docs-raw/generated/sketch.md +9 -1
- package/dist/docs-raw/generated/viewport.md +1 -1
- package/dist/docs-raw/guides/inspection-bundles.md +45 -16
- package/dist/docs-raw/platform/auth.md +2 -0
- package/dist/docs-raw/platform/google-oauth-setup.md +4 -0
- package/dist/docs-raw/runbook.md +3 -3
- package/dist/docs-raw/skills/forgecad-make-a-model.md +87 -8
- package/dist/docs-raw/skills/forgecad-prepare-prompt.md +14 -6
- package/dist/docs-raw/skills/forgecad-render-inspect.md +1 -1
- package/dist/docs-raw/skills/index.md +2 -2
- package/dist/index.html +1 -1
- package/dist/sitemap.xml +6 -6
- package/dist-cli/forgecad.js +8725 -4747
- package/dist-cli/forgecad.js.map +1 -1
- package/dist-skill/CONTEXT.md +375 -25
- package/dist-skill/docs/CLI.md +82 -53
- package/dist-skill/docs/generated/core.md +206 -1
- package/dist-skill/docs/generated/curves.md +97 -5
- package/dist-skill/docs/generated/lib.md +17 -1
- package/dist-skill/docs/generated/sketch.md +9 -1
- package/dist-skill/docs/generated/viewport.md +1 -1
- package/dist-skill/docs/guides/inspection-bundles.md +45 -16
- package/dist-skill/docs-dev/CLI.md +82 -53
- package/dist-skill/docs-dev/coding.md +1 -1
- package/dist-skill/docs-dev/generated/core.md +206 -1
- package/dist-skill/docs-dev/generated/curves.md +97 -5
- package/dist-skill/docs-dev/generated/lib.md +17 -1
- package/dist-skill/docs-dev/generated/sketch.md +9 -1
- package/dist-skill/docs-dev/generated/viewport.md +1 -1
- package/dist-skill/docs-dev/guides/inspection-bundles.md +45 -16
- package/dist-skill/library/forgecad-make-a-model/SKILL.md +87 -8
- package/dist-skill/library/forgecad-prepare-prompt/SKILL.md +14 -6
- package/dist-skill/library/forgecad-prepare-prompt/references/default-profiles.md +5 -3
- package/dist-skill/library/forgecad-prepare-prompt/references/master-prompt.md +7 -5
- package/dist-skill/library/forgecad-render-inspect/SKILL.md +1 -1
- package/examples/api/bolted-service-cover.forge.js +17 -0
- package/examples/api/cable-gland-anchor.forge.js +14 -0
- package/examples/api/captured-cartridge-guide.forge.js +14 -0
- package/examples/api/captured-linear-slide.forge.js +13 -0
- package/examples/api/clevis-pin-joint.forge.js +13 -0
- package/examples/api/datum-enclosure.forge.js +16 -0
- package/examples/api/guided-loft-olive-oil-bottle.forge.js +135 -0
- package/examples/api/hose-barb-port.forge.js +14 -0
- package/examples/api/intentional-overlap-overmold.forge.js +16 -0
- package/examples/api/knuckled-hinge-assembly.forge.js +15 -0
- package/examples/api/living-hinge-cover.forge.js +14 -0
- package/examples/api/pcb-terminal-block.forge.js +22 -0
- package/examples/api/pinned-lever-pivot-stack.forge.js +14 -0
- package/examples/api/retained-shaft-knob-stack.forge.js +15 -0
- package/examples/api/routed-tube-clip.forge.js +15 -0
- package/examples/api/seated-bearing-stack.forge.js +30 -0
- package/examples/api/snap-latch-cover.forge.js +14 -0
- package/examples/api/static-assembly-connectors.forge.js +14 -16
- package/examples/api/thumb-screw-clamp.forge.js +15 -0
- package/package.json +20 -2
- package/dist/assets/EditorApp-DNH1TEz1.js +0 -12729
|
@@ -7,7 +7,7 @@ Every public API function belongs to one of 16 fundamental concepts. This docume
|
|
|
7
7
|
- **[C1: Primitive Construction](#c1-primitive-construction)** — Create geometry from parameters — no input geometry required. *(76 functions)*
|
|
8
8
|
- **[C2: Boolean Combination](#c2-boolean-combination)** — Combine same-dimension geometry using CSG set operations. *(6 functions)*
|
|
9
9
|
- **[C3: Rigid Transform](#c3-rigid-transform)** — Reposition or reorient geometry without changing its shape. *(3 functions)*
|
|
10
|
-
- **[C4: Dimensional Promotion](#c4-dimensional-promotion)** — Convert a 2D profile into a 3D solid (extrude, revolve, loft, sweep). *(
|
|
10
|
+
- **[C4: Dimensional Promotion](#c4-dimensional-promotion)** — Convert a 2D profile into a 3D solid (extrude, revolve, loft, sweep). *(40 functions)*
|
|
11
11
|
- **[C5: Topology Query](#c5-topology-query)** — Select or inspect named faces and edges on a shape. *(3 functions)*
|
|
12
12
|
- **[C6: Edge Feature](#c6-edge-feature)** — Modify edges of a solid — fillets, chamfers, draft, offset. *(7 functions)*
|
|
13
13
|
- **[C7: Pattern Replication](#c7-pattern-replication)** — Duplicate geometry in regular arrangements (linear, circular, mirror). *(6 functions)*
|
|
@@ -17,7 +17,7 @@ Every public API function belongs to one of 16 fundamental concepts. This docume
|
|
|
17
17
|
- **[C11: Parameterization & UI](#c11-parameterization-ui)** — Declare user-facing controls that drive model geometry. *(7 functions)*
|
|
18
18
|
- **[C12: Dimensional Demotion](#c12-dimensional-demotion)** — Extract 2D geometry from a 3D solid (section, projection). *(3 functions)*
|
|
19
19
|
- **[C13: Export & Output](#c13-export-output)** — Convert geometry to external formats (STL, 3MF, SVG, DXF, G-code, PDF). *(5 functions)*
|
|
20
|
-
- **[C14: Visual & Debugging](#c14-visual-debugging)** — Control viewport appearance and debugging aids. *(
|
|
20
|
+
- **[C14: Visual & Debugging](#c14-visual-debugging)** — Control viewport appearance and debugging aids. *(35 functions)*
|
|
21
21
|
- **[C15: Import & Composition](#c15-import-composition)** — Bring external geometry or other ForgeCAD modules into the current script. *(1 functions)*
|
|
22
22
|
- **[C16: Part Library](#c16-part-library)** — Pre-built parametric parts accessible via `lib.*`. *(4 functions)*
|
|
23
23
|
|
|
@@ -1002,6 +1002,87 @@ composeChain(...steps: TransformInput[]): Transform
|
|
|
1002
1002
|
|
|
1003
1003
|
Convert a 2D profile into a 3D solid (extrude, revolve, loft, sweep).
|
|
1004
1004
|
|
|
1005
|
+
#### `Loft.station()` — Create a loft station from a 2D profile and an axis position.
|
|
1006
|
+
|
|
1007
|
+
```ts
|
|
1008
|
+
Loft.station(profile: Sketch, position: number): LoftStation
|
|
1009
|
+
```
|
|
1010
|
+
|
|
1011
|
+
`LoftStation`: `{ profile: Sketch, position: number }`
|
|
1012
|
+
|
|
1013
|
+
#### `Loft.leftRail()` — Create a guide rail that constrains the section-local negative-X side.
|
|
1014
|
+
|
|
1015
|
+
```ts
|
|
1016
|
+
Loft.leftRail(path: LoftGuideRailPath): LoftGuideRail
|
|
1017
|
+
```
|
|
1018
|
+
|
|
1019
|
+
`LoftGuideRail`: `{ side: LoftGuideRailSide, path: LoftGuideRailPath }`
|
|
1020
|
+
|
|
1021
|
+
#### `Loft.rightRail()` — Create a guide rail that constrains the section-local positive-X side.
|
|
1022
|
+
|
|
1023
|
+
```ts
|
|
1024
|
+
Loft.rightRail(path: LoftGuideRailPath): LoftGuideRail
|
|
1025
|
+
```
|
|
1026
|
+
|
|
1027
|
+
#### `Loft.frontRail()` — Create a guide rail that constrains the section-local positive-Y side.
|
|
1028
|
+
|
|
1029
|
+
```ts
|
|
1030
|
+
Loft.frontRail(path: LoftGuideRailPath): LoftGuideRail
|
|
1031
|
+
```
|
|
1032
|
+
|
|
1033
|
+
#### `Loft.backRail()` — Create a guide rail that constrains the section-local negative-Y side.
|
|
1034
|
+
|
|
1035
|
+
```ts
|
|
1036
|
+
Loft.backRail(path: LoftGuideRailPath): LoftGuideRail
|
|
1037
|
+
```
|
|
1038
|
+
|
|
1039
|
+
#### `Loft.centerRail()` — Create a guide rail that moves section centers along the loft.
|
|
1040
|
+
|
|
1041
|
+
```ts
|
|
1042
|
+
Loft.centerRail(path: LoftGuideRailPath): LoftGuideRail
|
|
1043
|
+
```
|
|
1044
|
+
|
|
1045
|
+
#### `Loft.pathOnXz()` — Place a 2D guide path onto the XZ plane.
|
|
1046
|
+
|
|
1047
|
+
The path's first coordinate becomes X and its second coordinate becomes Z. Use this for left/right silhouette rails authored with `path()` or `constrainedSketch()`.
|
|
1048
|
+
|
|
1049
|
+
```ts
|
|
1050
|
+
Loft.pathOnXz(path: LoftPath2D, y?: number): Vec3[]
|
|
1051
|
+
```
|
|
1052
|
+
|
|
1053
|
+
#### `Loft.pathOnYz()` — Place a 2D guide path onto the YZ plane.
|
|
1054
|
+
|
|
1055
|
+
The path's first coordinate becomes Y and its second coordinate becomes Z. Use this for front/back crown rails authored with `path()` or `constrainedSketch()`.
|
|
1056
|
+
|
|
1057
|
+
```ts
|
|
1058
|
+
Loft.pathOnYz(path: LoftPath2D, x?: number): Vec3[]
|
|
1059
|
+
```
|
|
1060
|
+
|
|
1061
|
+
#### `Loft.pathOnXy()` — Place a 2D guide path onto the XY plane.
|
|
1062
|
+
|
|
1063
|
+
The path's first coordinate becomes X and its second coordinate becomes Y. Use this when lofting along X or Y and a rail lives in a horizontal sketch plane.
|
|
1064
|
+
|
|
1065
|
+
```ts
|
|
1066
|
+
Loft.pathOnXy(path: LoftPath2D, z?: number): Vec3[]
|
|
1067
|
+
```
|
|
1068
|
+
|
|
1069
|
+
#### `Loft.withGuideRails()` — Loft through profile stations while forcing generated sections to follow guide rails.
|
|
1070
|
+
|
|
1071
|
+
Stations define the cross-section family. Guide rails define the side or center paths the loft must pass through. With opposite side rails, the section is scaled to touch both rails. With one side rail, the section keeps its interpolated size unless a center rail is also present.
|
|
1072
|
+
|
|
1073
|
+
```ts
|
|
1074
|
+
Loft.withGuideRails(stations: LoftStation[], rails: LoftGuideRail[], options?: LoftWithGuideRailsOptions): Shape
|
|
1075
|
+
```
|
|
1076
|
+
|
|
1077
|
+
**`LoftOptions`**
|
|
1078
|
+
- `edgeLength?: number` — Marching-grid edge length for level-set meshing. Smaller = finer.
|
|
1079
|
+
- `boundsPadding?: number` — Optional extra bounds padding.
|
|
1080
|
+
|
|
1081
|
+
**`LoftWithGuideRailsOptions`** extends LoftOptions
|
|
1082
|
+
- `axis?: LoftAxis` — Primary station axis. Default Z.
|
|
1083
|
+
- `samples?: number` — Number of generated loft stations including ends. Default scales with station count.
|
|
1084
|
+
- `railSamples?: number` — Number of points sampled from curve-backed rails before axis interpolation. Default 64.
|
|
1085
|
+
|
|
1005
1086
|
#### `Analysis.EdgeContinuity()`
|
|
1006
1087
|
|
|
1007
1088
|
```ts
|
|
@@ -1312,10 +1393,6 @@ Performance note: loft is significantly heavier than primitive/extrude/revolve.
|
|
|
1312
1393
|
loft(profiles: Sketch[], heights: number[], options?: LoftOptions): Shape
|
|
1313
1394
|
```
|
|
1314
1395
|
|
|
1315
|
-
**`LoftOptions`**
|
|
1316
|
-
- `edgeLength?: number` — Marching-grid edge length for level-set meshing. Smaller = finer.
|
|
1317
|
-
- `boundsPadding?: number` — Optional extra bounds padding.
|
|
1318
|
-
|
|
1319
1396
|
#### `loftAlongSpine()` — Loft between multiple profiles positioned along an arbitrary 3D spine curve.
|
|
1320
1397
|
|
|
1321
1398
|
Unlike loft() which only supports Z heights, loftAlongSpine() places each profile at a position along a 3D spine, oriented perpendicular to the spine tangent. This enables lofting along curved paths — e.g., a wing root-to-tip transition that follows a swept-back leading edge.
|
|
@@ -2791,6 +2868,46 @@ verify.centersCoincide(label: string, a: ShapeLike, b: ShapeLike, tolerance?: nu
|
|
|
2791
2868
|
|
|
2792
2869
|
`ShapeLike`: `{ min: number[], max: number[] }`
|
|
2793
2870
|
|
|
2871
|
+
#### `verify.connectorDistance()` — Check the distance between two named connectors on a shape or group.
|
|
2872
|
+
|
|
2873
|
+
Use this when connectors + `matchTo()` define a static assembly interface. It proves the mate at runtime, unlike a plain source-level connector declaration. The common case is `expected = 0`, meaning the two connector origins should coincide after placement.
|
|
2874
|
+
|
|
2875
|
+
```ts
|
|
2876
|
+
verify.connectorDistance("leg is seated", bench, "Rail.leg_0", "Leg0.head", 0, 0.01);
|
|
2877
|
+
```
|
|
2878
|
+
|
|
2879
|
+
```ts
|
|
2880
|
+
verify.connectorDistance(label: string, target: ConnectorDistanceLike, connectorA: string, connectorB: string, expected?: number, tolerance?: number): void
|
|
2881
|
+
```
|
|
2882
|
+
|
|
2883
|
+
#### `verify.physicalComponentCount()` — Declare the expected physical connectivity component count for the returned visible model.
|
|
2884
|
+
|
|
2885
|
+
Use this for generated mechanical models that should have a clear component graph: one connected fixture, a purchased part plus a removable cartridge, a root assembly plus named intentional ghosts, and so on. `forgecad inspect mechanical-integrity` resolves the returned visible objects with the same physical-connectivity analysis used in the quality gate and fails if the actual component count differs.
|
|
2886
|
+
|
|
2887
|
+
This catches the common generated-CAD failure where a script returns a visually plausible artifact but the handle, screw, washer, cover, or terminal block is actually a separate island.
|
|
2888
|
+
|
|
2889
|
+
```ts
|
|
2890
|
+
verify.physicalComponentCount("vise is one connected installed assembly", 1);
|
|
2891
|
+
```
|
|
2892
|
+
|
|
2893
|
+
```ts
|
|
2894
|
+
verify.physicalComponentCount(label: string, expected: number): void
|
|
2895
|
+
```
|
|
2896
|
+
|
|
2897
|
+
#### `verify.intentionalOverlap()` — Declare that two visible objects intentionally overlap because the overlap is real manufacturing intent.
|
|
2898
|
+
|
|
2899
|
+
Use this only for overlaps that a mechanical reviewer would accept as actual matter sharing volume: welded/fused regions, overmolded inserts, potted electronics, cast-in hardware, or deliberately bonded laminations. This is not a shortcut for screws without holes, shafts without bores, covers without pockets, or parts placed with collision as a positioning hack.
|
|
2900
|
+
|
|
2901
|
+
`forgecad inspect mechanical-integrity --collisions` only honors this declaration when both shapes are returned as visible objects and the exact collision report finds that same object pair. Unused or non-visible declarations fail the quality gate so annotations cannot hide unrelated collisions.
|
|
2902
|
+
|
|
2903
|
+
```ts
|
|
2904
|
+
verify.intentionalOverlap("rubber grip is overmolded on handle", rubberGrip, handleCore, "overmolded insert");
|
|
2905
|
+
```
|
|
2906
|
+
|
|
2907
|
+
```ts
|
|
2908
|
+
verify.intentionalOverlap(label: string, a: ShapeLike, b: ShapeLike, reason: string): void
|
|
2909
|
+
```
|
|
2910
|
+
|
|
2794
2911
|
#### `verify.notColliding()` — Check that two shapes do not collide (minGap > 0).
|
|
2795
2912
|
|
|
2796
2913
|
```ts
|
|
@@ -2803,6 +2920,23 @@ verify.notColliding(label: string, a: ShapeLike, b: ShapeLike, searchLength?: nu
|
|
|
2803
2920
|
verify.minClearance(label: string, a: ShapeLike, b: ShapeLike, minGap: number, searchLength?: number): void
|
|
2804
2921
|
```
|
|
2805
2922
|
|
|
2923
|
+
#### `verify.clearanceBetween()` — Check that the clearance gap between two shapes is inside an allowed range.
|
|
2924
|
+
|
|
2925
|
+
Use this for seated and retained interfaces where a part must be close enough to be mechanically accountable, but must not collide beyond the allowed minimum. It catches both failure modes that make generated CAD look fake: parts floating away from their receiver, and parts intersecting their receiver because the pocket, bore, or running clearance was not modeled.
|
|
2926
|
+
|
|
2927
|
+
For contact, use a narrow range such as `[-0.01, 0.05]` to tolerate tiny numerical noise. For a running fit, use the intended clearance band.
|
|
2928
|
+
|
|
2929
|
+
Manifold-backed shapes use exact min-gap distance. Other backends use a mesh-derived min-gap check and say so in the verification message; keep `forgecad inspect mechanical-integrity --collisions` in the acceptance gate for positive-volume interference.
|
|
2930
|
+
|
|
2931
|
+
```ts
|
|
2932
|
+
verify.clearanceBetween("cover is seated on gasket", cover, gasket, -0.01, 0.05);
|
|
2933
|
+
verify.clearanceBetween("carriage runs inside rail", carriage, rail, 0.2, 0.5);
|
|
2934
|
+
```
|
|
2935
|
+
|
|
2936
|
+
```ts
|
|
2937
|
+
verify.clearanceBetween(label: string, a: ShapeLike, b: ShapeLike, minGap: number, maxGap: number, searchLength?: number): void
|
|
2938
|
+
```
|
|
2939
|
+
|
|
2806
2940
|
#### `verify.parallel()` — Check that two face normals are parallel (within toleranceDeg degrees).
|
|
2807
2941
|
|
|
2808
2942
|
```ts
|
|
@@ -2957,7 +3091,7 @@ Mock objects appear in the viewport and spatial analysis when you run a file dir
|
|
|
2957
3091
|
|
|
2958
3092
|
The shape is returned unchanged, so you can reference it for alignment, dimensioning, and `verify` checks.
|
|
2959
3093
|
|
|
2960
|
-
Mock objects participate in `forgecad run` collision detection and spatial analysis. Their names appear with a `(mock)` suffix in reports.
|
|
3094
|
+
Mock objects participate in `forgecad run --spatial bounded|exact` collision detection and spatial analysis. Their names appear with a `(mock)` suffix in reports.
|
|
2961
3095
|
|
|
2962
3096
|
In the viewport, mock objects render at reduced opacity so they are visually distinct from real geometry.
|
|
2963
3097
|
|
|
@@ -18,7 +18,7 @@ skill-order: 100
|
|
|
18
18
|
- [Grouping & Local Coordinates](#grouping-local-coordinates) — `group`
|
|
19
19
|
- [Section & Projection](#section-projection) — `intersectWithPlane`, `faceProfile`, `projectToPlane`
|
|
20
20
|
- [Transforms](#transforms) — `composeChain`
|
|
21
|
-
- [Verification](#verification) — `spec`
|
|
21
|
+
- [Verification](#verification) — `verify.that`, `verify.equal`, `verify.notEqual`, `verify.greaterThan`, `verify.lessThan`, `verify.inRange`, `verify.centersCoincide`, `verify.connectorDistance`, `verify.physicalComponentCount`, `verify.intentionalOverlap`, `verify.notColliding`, `verify.minClearance`, `verify.clearanceBetween`, `verify.parallel`, `verify.perpendicular`, `verify.coplanar`, `verify.faceAt`, `verify.sameDirection`, `verify.isEmpty`, `verify.notEmpty`, `verify.volumeApprox`, `verify.areaApprox`, `verify.boundingBoxSize`, `verify.edgeContinuity`, `verify.noTinyEdges`, `verify.noSliverFaces`, `verify.noSelfIntersection`, `spec`
|
|
22
22
|
- [Shape](#shape) — Appearance, Face Topology, Edge Topology, Transforms, Booleans & Cutting, Features, Placement, Connectors, References, Measurement
|
|
23
23
|
- [Transform](#transform)
|
|
24
24
|
- [ShapeGroup](#shapegroup) — Children, Transforms, Placement, Connectors, References
|
|
@@ -669,6 +669,207 @@ composeChain(...steps: TransformInput[]): Transform
|
|
|
669
669
|
|
|
670
670
|
### Verification
|
|
671
671
|
|
|
672
|
+
#### `verify.that()` — Custom predicate check.
|
|
673
|
+
|
|
674
|
+
```ts
|
|
675
|
+
verify.that(label: string, check: () => boolean, message?: string): void
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
#### `verify.equal()` — Check that two numbers are approximately equal (within tolerance).
|
|
679
|
+
|
|
680
|
+
```ts
|
|
681
|
+
verify.equal(label: string, actual: number, expected: number, tolerance?: number, message?: string): void
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
#### `verify.notEqual()` — Check that two numbers are NOT equal (differ by more than tolerance).
|
|
685
|
+
|
|
686
|
+
```ts
|
|
687
|
+
verify.notEqual(label: string, actual: number, unexpected: number, tolerance?: number, message?: string): void
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
#### `verify.greaterThan()` — Check that actual > min.
|
|
691
|
+
|
|
692
|
+
```ts
|
|
693
|
+
verify.greaterThan(label: string, actual: number, min: number, message?: string): void
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
#### `verify.lessThan()` — Check that actual < max.
|
|
697
|
+
|
|
698
|
+
```ts
|
|
699
|
+
verify.lessThan(label: string, actual: number, max: number, message?: string): void
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
#### `verify.inRange()` — Check that min <= actual <= max.
|
|
703
|
+
|
|
704
|
+
```ts
|
|
705
|
+
verify.inRange(label: string, actual: number, min: number, max: number, message?: string): void
|
|
706
|
+
```
|
|
707
|
+
|
|
708
|
+
#### `verify.centersCoincide()` — Check that the bounding-box centers of two shapes coincide within tolerance (mm).
|
|
709
|
+
|
|
710
|
+
```ts
|
|
711
|
+
verify.centersCoincide(label: string, a: ShapeLike, b: ShapeLike, tolerance?: number): void
|
|
712
|
+
```
|
|
713
|
+
|
|
714
|
+
`ShapeLike`: `{ min: number[], max: number[] }`
|
|
715
|
+
|
|
716
|
+
#### `verify.connectorDistance()` — Check the distance between two named connectors on a shape or group.
|
|
717
|
+
|
|
718
|
+
Use this when connectors + `matchTo()` define a static assembly interface. It proves the mate at runtime, unlike a plain source-level connector declaration. The common case is `expected = 0`, meaning the two connector origins should coincide after placement.
|
|
719
|
+
|
|
720
|
+
```ts
|
|
721
|
+
verify.connectorDistance("leg is seated", bench, "Rail.leg_0", "Leg0.head", 0, 0.01);
|
|
722
|
+
```
|
|
723
|
+
|
|
724
|
+
```ts
|
|
725
|
+
verify.connectorDistance(label: string, target: ConnectorDistanceLike, connectorA: string, connectorB: string, expected?: number, tolerance?: number): void
|
|
726
|
+
```
|
|
727
|
+
|
|
728
|
+
#### `verify.physicalComponentCount()` — Declare the expected physical connectivity component count for the returned visible model.
|
|
729
|
+
|
|
730
|
+
Use this for generated mechanical models that should have a clear component graph: one connected fixture, a purchased part plus a removable cartridge, a root assembly plus named intentional ghosts, and so on. `forgecad inspect mechanical-integrity` resolves the returned visible objects with the same physical-connectivity analysis used in the quality gate and fails if the actual component count differs.
|
|
731
|
+
|
|
732
|
+
This catches the common generated-CAD failure where a script returns a visually plausible artifact but the handle, screw, washer, cover, or terminal block is actually a separate island.
|
|
733
|
+
|
|
734
|
+
```ts
|
|
735
|
+
verify.physicalComponentCount("vise is one connected installed assembly", 1);
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
```ts
|
|
739
|
+
verify.physicalComponentCount(label: string, expected: number): void
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
#### `verify.intentionalOverlap()` — Declare that two visible objects intentionally overlap because the overlap is real manufacturing intent.
|
|
743
|
+
|
|
744
|
+
Use this only for overlaps that a mechanical reviewer would accept as actual matter sharing volume: welded/fused regions, overmolded inserts, potted electronics, cast-in hardware, or deliberately bonded laminations. This is not a shortcut for screws without holes, shafts without bores, covers without pockets, or parts placed with collision as a positioning hack.
|
|
745
|
+
|
|
746
|
+
`forgecad inspect mechanical-integrity --collisions` only honors this declaration when both shapes are returned as visible objects and the exact collision report finds that same object pair. Unused or non-visible declarations fail the quality gate so annotations cannot hide unrelated collisions.
|
|
747
|
+
|
|
748
|
+
```ts
|
|
749
|
+
verify.intentionalOverlap("rubber grip is overmolded on handle", rubberGrip, handleCore, "overmolded insert");
|
|
750
|
+
```
|
|
751
|
+
|
|
752
|
+
```ts
|
|
753
|
+
verify.intentionalOverlap(label: string, a: ShapeLike, b: ShapeLike, reason: string): void
|
|
754
|
+
```
|
|
755
|
+
|
|
756
|
+
#### `verify.notColliding()` — Check that two shapes do not collide (minGap > 0).
|
|
757
|
+
|
|
758
|
+
```ts
|
|
759
|
+
verify.notColliding(label: string, a: ShapeLike, b: ShapeLike, searchLength?: number): void
|
|
760
|
+
```
|
|
761
|
+
|
|
762
|
+
#### `verify.minClearance()` — Check that a minimum clearance gap exists between two shapes.
|
|
763
|
+
|
|
764
|
+
```ts
|
|
765
|
+
verify.minClearance(label: string, a: ShapeLike, b: ShapeLike, minGap: number, searchLength?: number): void
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
#### `verify.clearanceBetween()` — Check that the clearance gap between two shapes is inside an allowed range.
|
|
769
|
+
|
|
770
|
+
Use this for seated and retained interfaces where a part must be close enough to be mechanically accountable, but must not collide beyond the allowed minimum. It catches both failure modes that make generated CAD look fake: parts floating away from their receiver, and parts intersecting their receiver because the pocket, bore, or running clearance was not modeled.
|
|
771
|
+
|
|
772
|
+
For contact, use a narrow range such as `[-0.01, 0.05]` to tolerate tiny numerical noise. For a running fit, use the intended clearance band.
|
|
773
|
+
|
|
774
|
+
Manifold-backed shapes use exact min-gap distance. Other backends use a mesh-derived min-gap check and say so in the verification message; keep `forgecad inspect mechanical-integrity --collisions` in the acceptance gate for positive-volume interference.
|
|
775
|
+
|
|
776
|
+
```ts
|
|
777
|
+
verify.clearanceBetween("cover is seated on gasket", cover, gasket, -0.01, 0.05);
|
|
778
|
+
verify.clearanceBetween("carriage runs inside rail", carriage, rail, 0.2, 0.5);
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
```ts
|
|
782
|
+
verify.clearanceBetween(label: string, a: ShapeLike, b: ShapeLike, minGap: number, maxGap: number, searchLength?: number): void
|
|
783
|
+
```
|
|
784
|
+
|
|
785
|
+
#### `verify.parallel()` — Check that two face normals are parallel (within toleranceDeg degrees).
|
|
786
|
+
|
|
787
|
+
```ts
|
|
788
|
+
verify.parallel(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number): void
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
`FaceRefLike`: `{ normal: [ number, number, number ], center: [ number, number, number ] }`
|
|
792
|
+
|
|
793
|
+
#### `verify.perpendicular()` — Check that two face normals are perpendicular (within toleranceDeg degrees).
|
|
794
|
+
|
|
795
|
+
```ts
|
|
796
|
+
verify.perpendicular(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number): void
|
|
797
|
+
```
|
|
798
|
+
|
|
799
|
+
#### `verify.coplanar()` — Check that a face is coplanar with (same plane as) another face, meaning they are parallel AND their centers lie on the same plane.
|
|
800
|
+
|
|
801
|
+
```ts
|
|
802
|
+
verify.coplanar(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number, toleranceMm?: number): void
|
|
803
|
+
```
|
|
804
|
+
|
|
805
|
+
#### `verify.faceAt()` — Check that a face center lies at a specific position (within toleranceMm).
|
|
806
|
+
|
|
807
|
+
```ts
|
|
808
|
+
verify.faceAt(label: string, face: FaceRefLike, expectedPos: [ number, number, number ], toleranceMm?: number): void
|
|
809
|
+
```
|
|
810
|
+
|
|
811
|
+
#### `verify.sameDirection()` — Check that two face normals point in the same direction (not antiparallel). Stricter than parallel — both |angle| AND sign must match.
|
|
812
|
+
|
|
813
|
+
```ts
|
|
814
|
+
verify.sameDirection(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number): void
|
|
815
|
+
```
|
|
816
|
+
|
|
817
|
+
#### `verify.isEmpty()` — Check that a shape is empty.
|
|
818
|
+
|
|
819
|
+
```ts
|
|
820
|
+
verify.isEmpty(label: string, shape: ShapeLike, message?: string): void
|
|
821
|
+
```
|
|
822
|
+
|
|
823
|
+
#### `verify.notEmpty()` — Check that a shape is NOT empty.
|
|
824
|
+
|
|
825
|
+
```ts
|
|
826
|
+
verify.notEmpty(label: string, shape: ShapeLike, message?: string): void
|
|
827
|
+
```
|
|
828
|
+
|
|
829
|
+
#### `verify.volumeApprox()` — Check that a shape's volume is approximately equal to expected (mm³).
|
|
830
|
+
|
|
831
|
+
```ts
|
|
832
|
+
verify.volumeApprox(label: string, shape: ShapeLike, expected: number, tolerance?: number): void
|
|
833
|
+
```
|
|
834
|
+
|
|
835
|
+
#### `verify.areaApprox()` — Check that a shape's surface area is approximately equal to expected (mm²).
|
|
836
|
+
|
|
837
|
+
```ts
|
|
838
|
+
verify.areaApprox(label: string, shape: ShapeLike, expected: number, tolerance?: number): void
|
|
839
|
+
```
|
|
840
|
+
|
|
841
|
+
#### `verify.boundingBoxSize()` — Check that a shape's bounding box has approximately the given size.
|
|
842
|
+
|
|
843
|
+
```ts
|
|
844
|
+
verify.boundingBoxSize(label: string, shape: ShapeLike, expectedSize: [ number, number, number ], tolerance?: number): void
|
|
845
|
+
```
|
|
846
|
+
|
|
847
|
+
#### `verify.edgeContinuity()` — Check that every sampled seam on a shape meets a requested continuity threshold.
|
|
848
|
+
|
|
849
|
+
```ts
|
|
850
|
+
verify.edgeContinuity(label: string, shape: ShapeLike, options?: EdgeContinuityThresholds): void
|
|
851
|
+
```
|
|
852
|
+
|
|
853
|
+
**`EdgeContinuityThresholds`**: `continuity?: SurfaceContinuity`, `samples?: number`, `positionTolerance?: number`, `tangentToleranceDeg?: number`, `curvatureTolerance?: number`
|
|
854
|
+
|
|
855
|
+
#### `verify.noTinyEdges()` — Check that a shape has no tiny edges below the requested threshold.
|
|
856
|
+
|
|
857
|
+
```ts
|
|
858
|
+
verify.noTinyEdges(label: string, shape: ShapeLike, threshold?: number): void
|
|
859
|
+
```
|
|
860
|
+
|
|
861
|
+
#### `verify.noSliverFaces()` — Check that a shape has no sliver faces below the requested score threshold.
|
|
862
|
+
|
|
863
|
+
```ts
|
|
864
|
+
verify.noSliverFaces(label: string, shape: ShapeLike, threshold?: number): void
|
|
865
|
+
```
|
|
866
|
+
|
|
867
|
+
#### `verify.noSelfIntersection()` — Best-effort exact-shape validity guard for self-intersections or broken B-Rep topology.
|
|
868
|
+
|
|
869
|
+
```ts
|
|
870
|
+
verify.noSelfIntersection(label: string, shape: ShapeLike): void
|
|
871
|
+
```
|
|
872
|
+
|
|
672
873
|
#### `spec()` — Create a named, reusable bundle of verification checks.
|
|
673
874
|
|
|
674
875
|
A spec groups related `verify.*` calls under a collapsible header in the Checks panel. This makes large check suites scannable. Specs can be applied to multiple shapes and can check relationships between parts.
|
|
@@ -1859,8 +2060,12 @@ toString(): string
|
|
|
1859
2060
|
- `lessThan(label: string, actual: number, max: number, message?: string): void` — Check that actual < max.
|
|
1860
2061
|
- `inRange(label: string, actual: number, min: number, max: number, message?: string): void` — Check that min <= actual <= max.
|
|
1861
2062
|
- `centersCoincide(label: string, a: ShapeLike, b: ShapeLike, tolerance?: number): void` — Check that the bounding-box centers of two shapes coincide within tolerance (mm).
|
|
2063
|
+
- `connectorDistance(label: string, target: ConnectorDistanceLike, connectorA: string, connectorB: string, expected?: number, tolerance?: number): void` — Check the distance between two named connectors on a shape or group. Use this when connectors + `matchTo()` define a static assembly interface. It proves the mate at runtime, unlike a plain source-level connector declaration. The common case is `expected = 0`, meaning the two connector origins should coincide after placement. **Example** ```ts verify.connectorDistance("leg is seated", bench, "Rail.leg_0", "Leg0.head", 0, 0.01); ```
|
|
2064
|
+
- `physicalComponentCount(label: string, expected: number): void` — Declare the expected physical connectivity component count for the returned visible model. **Details** Use this for generated mechanical models that should have a clear component graph: one connected fixture, a purchased part plus a removable cartridge, a root assembly plus named intentional ghosts, and so on. `forgecad inspect mechanical-integrity` resolves the returned visible objects with the same physical-connectivity analysis used in the quality gate and fails if the actual component count differs. This catches the common generated-CAD failure where a script returns a visually plausible artifact but the handle, screw, washer, cover, or terminal block is actually a separate island. **Example** ```ts verify.physicalComponentCount("vise is one connected installed assembly", 1); ```
|
|
2065
|
+
- `intentionalOverlap(label: string, a: ShapeLike, b: ShapeLike, reason: string): void` — Declare that two visible objects intentionally overlap because the overlap is real manufacturing intent. **Details** Use this only for overlaps that a mechanical reviewer would accept as actual matter sharing volume: welded/fused regions, overmolded inserts, potted electronics, cast-in hardware, or deliberately bonded laminations. This is not a shortcut for screws without holes, shafts without bores, covers without pockets, or parts placed with collision as a positioning hack. `forgecad inspect mechanical-integrity --collisions` only honors this declaration when both shapes are returned as visible objects and the exact collision report finds that same object pair. Unused or non-visible declarations fail the quality gate so annotations cannot hide unrelated collisions. **Example** ```ts verify.intentionalOverlap("rubber grip is overmolded on handle", rubberGrip, handleCore, "overmolded insert"); ```
|
|
1862
2066
|
- `notColliding(label: string, a: ShapeLike, b: ShapeLike, searchLength?: number): void` — Check that two shapes do not collide (minGap > 0).
|
|
1863
2067
|
- `minClearance(label: string, a: ShapeLike, b: ShapeLike, minGap: number, searchLength?: number): void` — Check that a minimum clearance gap exists between two shapes.
|
|
2068
|
+
- `clearanceBetween(label: string, a: ShapeLike, b: ShapeLike, minGap: number, maxGap: number, searchLength?: number): void` — Check that the clearance gap between two shapes is inside an allowed range. **Details** Use this for seated and retained interfaces where a part must be close enough to be mechanically accountable, but must not collide beyond the allowed minimum. It catches both failure modes that make generated CAD look fake: parts floating away from their receiver, and parts intersecting their receiver because the pocket, bore, or running clearance was not modeled. For contact, use a narrow range such as `[-0.01, 0.05]` to tolerate tiny numerical noise. For a running fit, use the intended clearance band. Manifold-backed shapes use exact min-gap distance. Other backends use a mesh-derived min-gap check and say so in the verification message; keep `forgecad inspect mechanical-integrity --collisions` in the acceptance gate for positive-volume interference. **Example** ```ts verify.clearanceBetween("cover is seated on gasket", cover, gasket, -0.01, 0.05); verify.clearanceBetween("carriage runs inside rail", carriage, rail, 0.2, 0.5); ```
|
|
1864
2069
|
- `parallel(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number): void` — Check that two face normals are parallel (within toleranceDeg degrees).
|
|
1865
2070
|
- `perpendicular(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number): void` — Check that two face normals are perpendicular (within toleranceDeg degrees).
|
|
1866
2071
|
- `coplanar(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number, toleranceMm?: number): void` — Check that a face is coplanar with (same plane as) another face, meaning they are parallel AND their centers lie on the same plane.
|
|
@@ -9,7 +9,7 @@ Smooth curves, lofted surfaces, swept solids, splines, and high-level product sk
|
|
|
9
9
|
|
|
10
10
|
## Contents
|
|
11
11
|
|
|
12
|
-
- [Curves & Surfacing](#curves-surfacing) — `hermiteTransitionG2`, `nurbs3d`, `spline2d`, `spline3d`, `loft`, `loftAlongSpine`, `sweep`, `variableSweep`, `nurbsSurface`, `surfacePatch`, `transitionCurve`, `transitionSurface`, `connectEdges`
|
|
12
|
+
- [Curves & Surfacing](#curves-surfacing) — `Loft.station`, `Loft.leftRail`, `Loft.rightRail`, `Loft.frontRail`, `Loft.backRail`, `Loft.centerRail`, `Loft.pathOnXz`, `Loft.pathOnYz`, `Loft.pathOnXy`, `Loft.withGuideRails`, `hermiteTransitionG2`, `nurbs3d`, `spline2d`, `spline3d`, `loft`, `loftAlongSpine`, `sweep`, `variableSweep`, `nurbsSurface`, `surfacePatch`, `transitionCurve`, `transitionSurface`, `connectEdges`
|
|
13
13
|
- [Surface Members](#surface-members) — `surfaceBand`, `SurfaceBody`
|
|
14
14
|
- [Curve3D](#curve3d)
|
|
15
15
|
- [NurbsCurve3D](#nurbscurve3d)
|
|
@@ -52,6 +52,87 @@ Smooth curves, lofted surfaces, swept solids, splines, and high-level product sk
|
|
|
52
52
|
|
|
53
53
|
### Curves & Surfacing
|
|
54
54
|
|
|
55
|
+
#### `Loft.station()` — Create a loft station from a 2D profile and an axis position.
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
Loft.station(profile: Sketch, position: number): LoftStation
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
`LoftStation`: `{ profile: Sketch, position: number }`
|
|
62
|
+
|
|
63
|
+
#### `Loft.leftRail()` — Create a guide rail that constrains the section-local negative-X side.
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
Loft.leftRail(path: LoftGuideRailPath): LoftGuideRail
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`LoftGuideRail`: `{ side: LoftGuideRailSide, path: LoftGuideRailPath }`
|
|
70
|
+
|
|
71
|
+
#### `Loft.rightRail()` — Create a guide rail that constrains the section-local positive-X side.
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
Loft.rightRail(path: LoftGuideRailPath): LoftGuideRail
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
#### `Loft.frontRail()` — Create a guide rail that constrains the section-local positive-Y side.
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
Loft.frontRail(path: LoftGuideRailPath): LoftGuideRail
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
#### `Loft.backRail()` — Create a guide rail that constrains the section-local negative-Y side.
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
Loft.backRail(path: LoftGuideRailPath): LoftGuideRail
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
#### `Loft.centerRail()` — Create a guide rail that moves section centers along the loft.
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
Loft.centerRail(path: LoftGuideRailPath): LoftGuideRail
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
#### `Loft.pathOnXz()` — Place a 2D guide path onto the XZ plane.
|
|
96
|
+
|
|
97
|
+
The path's first coordinate becomes X and its second coordinate becomes Z. Use this for left/right silhouette rails authored with [`path()`](/docs/sketch#path) or [`constrainedSketch()`](/docs/sketch#constrainedsketch).
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
Loft.pathOnXz(path: LoftPath2D, y?: number): Vec3[]
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
#### `Loft.pathOnYz()` — Place a 2D guide path onto the YZ plane.
|
|
104
|
+
|
|
105
|
+
The path's first coordinate becomes Y and its second coordinate becomes Z. Use this for front/back crown rails authored with [`path()`](/docs/sketch#path) or [`constrainedSketch()`](/docs/sketch#constrainedsketch).
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
Loft.pathOnYz(path: LoftPath2D, x?: number): Vec3[]
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
#### `Loft.pathOnXy()` — Place a 2D guide path onto the XY plane.
|
|
112
|
+
|
|
113
|
+
The path's first coordinate becomes X and its second coordinate becomes Y. Use this when lofting along X or Y and a rail lives in a horizontal sketch plane.
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
Loft.pathOnXy(path: LoftPath2D, z?: number): Vec3[]
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
#### `Loft.withGuideRails()` — Loft through profile stations while forcing generated sections to follow guide rails.
|
|
120
|
+
|
|
121
|
+
Stations define the cross-section family. Guide rails define the side or center paths the loft must pass through. With opposite side rails, the section is scaled to touch both rails. With one side rail, the section keeps its interpolated size unless a center rail is also present.
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
Loft.withGuideRails(stations: LoftStation[], rails: LoftGuideRail[], options?: LoftWithGuideRailsOptions): Shape
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**`LoftOptions`**
|
|
128
|
+
- `edgeLength?: number` — Marching-grid edge length for level-set meshing. Smaller = finer.
|
|
129
|
+
- `boundsPadding?: number` — Optional extra bounds padding.
|
|
130
|
+
|
|
131
|
+
**`LoftWithGuideRailsOptions`** extends LoftOptions
|
|
132
|
+
- `axis?: LoftAxis` — Primary station axis. Default Z.
|
|
133
|
+
- `samples?: number` — Number of generated loft stations including ends. Default scales with station count.
|
|
134
|
+
- `railSamples?: number` — Number of points sampled from curve-backed rails before axis interpolation. Default 64.
|
|
135
|
+
|
|
55
136
|
#### `hermiteTransitionG2()` — Create a quintic Hermite transition curve between two edge endpoints (G2 continuity).
|
|
56
137
|
|
|
57
138
|
The curve starts at `a.point` tangent to `a.tangent` with curvature `a.curvature`, and ends at `b.point` tangent to `b.tangent` with curvature `b.curvature`, with smooth G2-continuous interpolation matching position, tangent, and curvature.
|
|
@@ -140,10 +221,6 @@ Performance note: loft is significantly heavier than primitive/extrude/revolve.
|
|
|
140
221
|
loft(profiles: Sketch[], heights: number[], options?: LoftOptions): Shape
|
|
141
222
|
```
|
|
142
223
|
|
|
143
|
-
**`LoftOptions`**
|
|
144
|
-
- `edgeLength?: number` — Marching-grid edge length for level-set meshing. Smaller = finer.
|
|
145
|
-
- `boundsPadding?: number` — Optional extra bounds padding.
|
|
146
|
-
|
|
147
224
|
#### `loftAlongSpine()` — Loft between multiple profiles positioned along an arbitrary 3D spine curve.
|
|
148
225
|
|
|
149
226
|
Unlike loft() which only supports Z heights, loftAlongSpine() places each profile at a position along a 3D spine, oriented perpendicular to the spine tangent. This enables lofting along curved paths — e.g., a wing root-to-tip transition that follows a swept-back leading edge.
|
|
@@ -821,6 +898,21 @@ path().moveTo(0,0).lineTo(10,0).lineTo(10,5).mirror('x').close()
|
|
|
821
898
|
mirror(axis: "x" | "y" | [ number, number ]): this
|
|
822
899
|
```
|
|
823
900
|
|
|
901
|
+
#### `toPolyline()` — Return the open path as a sampled 2D polyline.
|
|
902
|
+
|
|
903
|
+
This is for construction geometry such as guide rails, measured centerlines, and curve-driven helpers where the authored path should stay open instead of becoming a filled sketch or stroked profile.
|
|
904
|
+
|
|
905
|
+
```ts
|
|
906
|
+
const rail = path()
|
|
907
|
+
.moveTo(24, 0)
|
|
908
|
+
.bezierTo(32, 44, 28, 92, 18, 120)
|
|
909
|
+
.toPolyline();
|
|
910
|
+
```
|
|
911
|
+
|
|
912
|
+
```ts
|
|
913
|
+
toPolyline(): [ number, number ][]
|
|
914
|
+
```
|
|
915
|
+
|
|
824
916
|
#### `closeOffset()` — Close the path and return an offset version of the filled Sketch. Positive delta expands outward, negative shrinks inward.
|
|
825
917
|
|
|
826
918
|
```ts
|
|
@@ -87,7 +87,7 @@ Pre-built parametric parts available in user scripts as `lib.*`.
|
|
|
87
87
|
|
|
88
88
|
Every key in this object becomes a method or namespace on the `lib` object exposed to `.forge.js` scripts. The catalog includes:
|
|
89
89
|
|
|
90
|
-
**Fasteners:** `bolt`, `nut`, `washer`, `fastenerSet`, `fastenerHole`, `boltHole`, `counterbore`, `hexNut`, `holePattern`
|
|
90
|
+
**Fasteners and hardware patterns:** `bolt`, `nut`, `washer`, `fastenerSet`, `boltedServiceCover`, `datumEnclosureAssembly`, `snapLatchCoverAssembly`, `pinnedLeverAssembly`, `retainedShaftAssembly`, `capturedLinearSlide`, `capturedCartridgeGuideAssembly`, `livingHingeCoverAssembly`, `knuckledHingeAssembly`, `clevisPinJointAssembly`, `seatedBearingAssembly`, `cableGlandAnchorAssembly`, `hoseBarbPortAssembly`, `routedTubeClipAssembly`, `pcbTerminalBlockAssembly`, `thumbScrewClampAssembly`, `fastenerHole`, `boltHole`, `counterbore`, `hexNut`, `holePattern`
|
|
91
91
|
|
|
92
92
|
**Structure:** `tube`, `pipe`, `bracket`, `pipeRoute`, `elbow`, `tSlotProfile`, `tSlotExtrusion`, `profile2020BSlot6Profile`, `profile2020BSlot6`
|
|
93
93
|
|
|
@@ -121,6 +121,22 @@ Sizes outside the supported ranges will throw at runtime with a descriptive erro
|
|
|
121
121
|
- `nut(diameter: number, options?: { pitch?: number; height?: number; acrossFlats?: number; segments?: number; }): Shape` — ISO-style hex nut with a threaded bore. **Details** Constructed from the intersection of three rotated slabs with a cylindrical bore subtracted. The nut is centered at the origin, height along Z. Default proportions follow ISO 4032 loosely: height ≈ 0.8×diameter, across-flats ≈ 1.6×diameter. The bore is a clearance bore (not modelled with helical threads) for rendering efficiency. For standard M-size nuts pre-configured for a complete joint, use { **Example** ```ts const n = lib.nut(5); // M5 nut ```
|
|
122
122
|
- `washer(size: MetricSize, options?: { standard?: WasherStandard; segments?: number; }): Shape` — ISO metric flat washer (DIN 125-A). **Details** Returns a flat ring centered at the origin, thickness along Z. Dimensions are taken from { **Example** ```ts const w = lib.washer('M5'); // DIN 125-A M5 washer ```
|
|
123
123
|
- `fastenerSet(size: MetricSize, boltLength: number, options?: FastenerSetOptions): FastenerSetResult` — Complete ISO metric fastener set — bolt, nut, optional washers, and matching hole cutters. **Details** Returns all geometry for one bolted joint: the bolt, nut, up to two washers, a clearance-hole cutter, and a tap-drill cutter. All shapes are returned **un-positioned** (each on the Z-axis). Place them with `.translate()`. Sizes outside M4–M10 are supported for the washer (M2–M10); unsupported combinations will throw. **Example** ```ts const hw = lib.fastenerSet('M5', 20); const topPlate = box(60, 40, 8).translate(0, 0, 12) .subtract(hw.clearanceHole.translate(15, 10, 12)); const botPlate = box(60, 40, 8) .subtract(hw.clearanceHole.translate(15, 10, 0)); return [ { name: 'Top Plate', shape: topPlate }, { name: 'Bot Plate', shape: botPlate }, { name: 'Bolt', shape: hw.bolt.translate(15, 10, 20) }, { name: 'Nut', shape: hw.nut.translate(15, 10, -4) }, ]; ```
|
|
124
|
+
- `boltedServiceCover(options: BoltedServiceCoverOptions): BoltedServiceCoverResult` — Bolted service-cover interface with real seats, aligned holes, gasket, fused pull tabs, and installed screws. **Details** This is a higher-level mechanical pattern for the common "removable service cover" failure mode. It creates the parent ledge, cover, gasket, and screws from one shared bolt pattern so agents do not place decorative screw heads or floating pull tabs by eye. Coordinate convention: the parent frame sits from `z=0` to `parentThickness`, the gasket sits on the ledge, the cover sits above the gasket, and screw shafts run downward through the cover into the parent. All parts are centered on the XY origin. **Example** ```ts const cover = lib.boltedServiceCover({ width: 90, depth: 56, screwSize: 'M4', ledgeWidth: 10, boltInset: [6, 6], }); verify.equal('four retained cover screws', cover.screws.length, 4); return cover.parts; ```
|
|
125
|
+
- `datumEnclosureAssembly(options: DatumEnclosureAssemblyOptions): DatumEnclosureAssemblyResult` — Datum-driven enclosure tray with shared wall, ledge, standoff, cover, gasket, port, and screw geometry. **Details** This pattern is for electronics boxes, thermostat backplates, service-stack housings, camera housings, and small fixtures where generated models often place panels, ribs, bosses, ports, and covers by eye. The tray, internal ledges, standoffs, ribs, service port, gasket, cover holes, and installed screws all come from one datum system. This keeps screw axes, boss locations, wall thickness, and service openings aligned instead of relying on independent magic numbers. Coordinate convention: X/Y are the enclosure footprint, Z is up. The base tray starts at `z=0` and rises to `height`; the gasket and cover sit above the top ledge with small explicit face clearances. **Example** ```ts const enclosure = lib.datumEnclosureAssembly({ width: 96, depth: 64, height: 18, }); verify.notColliding('cover clears enclosure gasket', enclosure.cover, enclosure.gasket); verify.inRange('cover stack has small seating clearance', enclosure.dims.faceClearance, 0.01, 0.08); return enclosure.parts; ```
|
|
126
|
+
- `snapLatchCoverAssembly(options: SnapLatchCoverAssemblyOptions): SnapLatchCoverAssemblyResult` — Snap-retained cover with a receiver frame, latch windows, underside catch lands, and fused snap hooks. **Details** This pattern is for covers, cartridges, clasps, and small housings where agents often add decorative tabs without a catch. The receiver has a real service opening plus two clearance latch windows. The cover is one fused part with two flexible-looking snap fingers that pass through the windows and barb under the receiver underside. Nothing intersects in the final assembly; the hook geometry sits close enough to the catch lands to prove retention intent. Coordinate convention: the receiver frame sits from `z=0` to `parentThickness`; the cover is seated just above the receiver on +Z. Two snap hooks sit on the +/-Y ledges and tuck under the receiver. **Example** ```ts const snapCover = lib.snapLatchCoverAssembly({ width: 72, depth: 44, }); verify.notColliding('snap hooks clear receiver windows', snapCover.cover, snapCover.parent); verify.inRange('snap cover has small seating clearance', snapCover.dims.faceClearance, 0.01, 0.08); return snapCover.parts; ```
|
|
127
|
+
- `pinnedLeverAssembly(options: PinnedLeverAssemblyOptions): PinnedLeverAssemblyResult` — Retained pinned lever stack with a fused hub/arm/grip, low stop land, pivot pin, bore cutters, and thrust washers. **Details** This pattern is for the common handle/lever failure mode where a visual arm, hub, washer, and pin are placed near each other but never form a credible mechanism. The lever body is one fused part, the pin runs through aligned bores, washers sit on both sides of the lever, and the support includes a bearing land plus an optional low stop land beside the lever path. Coordinate convention: pivot axis is +Z at the XY origin. The support starts at `z=0`, the lower washer sits on top of the support, the lever sits on the lower washer, the upper washer sits on the lever, and the retained pin spans the full stack. **Example** ```ts const lever = lib.pinnedLeverAssembly({ armLength: 54, armWidth: 10, pinDiameter: 5, }); verify.equal('lever stack has five retained parts', lever.parts.length, 5); return lever.parts; ```
|
|
128
|
+
- `retainedShaftAssembly(options: RetainedShaftAssemblyOptions): RetainedShaftAssemblyResult` — Retained shaft, washer, knob, and support-cheek stack for trunnions, pivots, and adjustable clamps. **Details** This pattern replaces the common "pin, washers, and knob are near each other" visual shortcut with a mechanically accountable shaft stack. The two support cheeks get matching clearance bores, the through shaft spans the whole stack, washers and knobs share the same axis, and retaining heads keep the knobs from reading as loose floating cylinders. Coordinate convention: the shaft axis is +X through the world origin. Support cheeks are centered at `x = +/- supportSpacing / 2`. The supports are bored for clearance, so collision inspection should report no support/shaft overlap while the connectivity audit still sees one retained stack. **Example** ```ts const trunnion = lib.retainedShaftAssembly({ supportSpacing: 96, shaftDiameter: 8, supportHeight: 42, }); verify.equal('retained shaft stack has seven parts', trunnion.parts.length, 7); return trunnion.parts; ```
|
|
129
|
+
- `capturedLinearSlide(options: CapturedLinearSlideOptions): CapturedLinearSlideResult` — Captured linear slide with a U-channel rail, return lips, end stops, and a carriage posed inside the guide. **Details** This pattern is for drawer-slide, quick-release plate, and guided-carriage models where agents often place rail details and a moving block near each other without a capture relationship. The rail is one fused part with side walls, inward lips, and end stops; the carriage is wider than the lip throat but narrower than the inner rail width, so it is mechanically captured while retaining explicit clearance. Coordinate convention: rail length is along X, width is along Y, and Z is up. The rail base starts at `z=0`; the carriage sits above the base and below the return lips. `travel=0` places the carriage at the negative-X end of travel, and `travel=maxTravel` places it at the positive-X end. **Example** ```ts const slide = lib.capturedLinearSlide({ length: 160, carriageLength: 52, travel: 42, }); verify.greaterThan('carriage is captured by return lips', slide.dims.carriageWidth, slide.dims.throatWidth); return slide.parts; ```
|
|
130
|
+
- `capturedCartridgeGuideAssembly(options: CapturedCartridgeGuideAssemblyOptions): CapturedCartridgeGuideAssemblyResult` — Captured removable cartridge guide with return lips, rear stop, wide cartridge flange, and pull tab. **Details** This pattern is for pump cartridges, filter cassettes, skeg cassettes, battery cartridges, and slide-in service modules where generated models often place a tray and a loose block near each other. The guide is one fused part with side walls, inward return lips, and a rear stop. The cartridge has a wide lower flange captured under the lips and a narrower body that passes through the throat, so the model has a real retention contract without manual coordinate tuning. Coordinate convention: insertion travel is along +X. The open entry is at −X, the rear stop is at +X, the guide base starts at `z=0`, and `insertion=0` places the cartridge at the front travel limit. **Example** ```ts const cassette = lib.capturedCartridgeGuideAssembly({ length: 150, cartridgeLength: 72, }); verify.notColliding('cartridge clears guide rails', cassette.cartridge, cassette.guide); verify.greaterThan('cartridge flange is captured by lips', cassette.dims.cartridgeWidth, cassette.dims.throatWidth); return cassette.parts; ```
|
|
131
|
+
- `livingHingeCoverAssembly(options: LivingHingeCoverAssemblyOptions): LivingHingeCoverAssemblyResult` — One-piece molded living-hinge cover strip with a fixed leaf, thin flexible web, cover leaf, pull lip, snap barb, and catch land. **Details** This pattern is for small polypropylene-style lids, battery doors, sample covers, blister latches, and molded service flaps where generated models often draw a decorative hinge strip between two disconnected plates. It returns one fused molded part in its as-molded flat state: fixed mounting leaf, thin hinge web, moving cover leaf, pull lip, raised snap barb, and catch land. The flexible web is intentionally much thinner than the rigid leaves and shares material with both leaves. Coordinate convention: X is hinge length/part width, Y runs from fixed leaf through hinge web to cover leaf, and Z is thickness. The hinge web is centered on `y=0`; the fixed leaf lies at −Y and the cover leaf at +Y. **Example** ```ts const livingCover = lib.livingHingeCoverAssembly({ width: 64, coverDepth: 42, }); verify.greaterThan('living hinge is much thinner than rigid leaves', livingCover.dims.flexRatio, 3); return livingCover.parts; ```
|
|
132
|
+
- `knuckledHingeAssembly(options: KnuckledHingeAssemblyOptions): KnuckledHingeAssemblyResult` — Alternating knuckle hinge with two fused leaves and a retained pin. **Details** This pattern replaces hand-placed hinge barrels and pin ghosts with a mechanically accountable hinge. The fixed leaf owns every other knuckle, the moving leaf owns the alternating knuckles, all knuckles share one bore size, and the retained pin spans the full stack with heads outside the barrels. Coordinate convention: the hinge pin axis is +X through the world origin. The fixed leaf extends toward +Y. The moving leaf extends toward -Y and rotates about +X by `openAngleDeg`. **Example** ```ts const hinge = lib.knuckledHingeAssembly({ length: 70, leafLength: 28, openAngleDeg: 45, }); verify.equal('hinge has two leaves and one retained pin', hinge.parts.length, 3); return hinge.parts; ```
|
|
133
|
+
- `clevisPinJointAssembly(options?: ClevisPinJointAssemblyOptions): ClevisPinJointAssemblyResult` — Clevis-style pin joint with bored yoke ears, a center link eye, and a retained pin. **Details** This pattern is for crank links, damper rod ends, pump crossheads, capo/cam pivots, and small mechanism joints where agents often place an eyelet and a pin near a bracket without modeling the captured load path. The clevis is one fused part with two bored ears and a rear bridge, the center link has a real eye and arm, and the retained pin spans the full stack with heads outside the ears. Coordinate convention: the pin axis is +Y through the world origin. The center link arm extends toward +X. The clevis bridge sits behind the eye on -X, leaving the link eye clear inside the yoke. **Example** ```ts const clevis = lib.clevisPinJointAssembly({ pinDiameter: 4, linkArmLength: 38, }); verify.equal('clevis joint has three retained parts', clevis.parts.length, 3); return clevis.parts; ```
|
|
134
|
+
- `seatedBearingAssembly(options: SeatedBearingAssemblyOptions): SeatedBearingAssemblyResult` — Seated radial-bearing support with a real counterbore, shoulder, through shaft, and retaining collars. **Details** This pattern is for purchased bearings, rollers, burr-cartridge shafts, and small spindle supports where agents often place a ring and a shaft near a block without modelling the pocket that locates the bearing. The housing includes a through-bore and a larger counterbore that leaves a shoulder for the bearing outer race. The shaft is smaller than the bearing bore and carries collars outside the housing, so collision checks can distinguish intended clearance from impossible overlap. Coordinate convention: the shaft axis is +Z through the world origin. The housing block starts at `z=0`, the raised boss is on top of the block, the bearing is seated from the top counterbore, and the shaft extends above and below the housing. **Example** ```ts const bearingStack = lib.seatedBearingAssembly({ bearingOuterDiameter: 22, bearingInnerDiameter: 8, bearingWidth: 7, }); verify.greaterThan('housing has wall around bearing pocket', bearingStack.dims.bossOuterDiameter - bearingStack.dims.pocketDiameter, 4); return bearingStack.parts; ```
|
|
135
|
+
- `cableGlandAnchorAssembly(options: CableGlandAnchorAssemblyOptions): CableGlandAnchorAssemblyResult` — Cable, wire, or tube gland anchor with a real panel hole, hollow gland body, compression nut, and routed cable. **Details** This pattern is for pumps, filters, electronics boxes, vents, monitors, and fixtures where generated models often leave hoses or cables terminating in space. It creates the receiving panel hole, a hollow gland body with a panel-side flange seated in a shallow pocket, a hollow compression nut, and a cable/tube that runs through the gland bore with explicit clearance. Coordinate convention: the cable axis is +X through the world origin. The panel is centered around `x=0` with thickness along X; the flange sits on the +X side of the panel and the compression nut sits on the −X side. The cable spans the full anchor. **Example** ```ts const anchor = lib.cableGlandAnchorAssembly({ cableDiameter: 6, panelThickness: 3, }); verify.notColliding('cable clears gland bore', anchor.cable, anchor.gland); verify.clearanceBetween('gland flange is seated at panel pocket', anchor.gland, anchor.panel, 0.01, 0.2); return anchor.parts; ```
|
|
136
|
+
- `hoseBarbPortAssembly(options: HoseBarbPortAssemblyOptions): HoseBarbPortAssemblyResult` — Hose-barb pump/filter port with a bored receiver, shoulder, barb ridges, installed hose, and clamp band. **Details** This pattern is for pump heads, filters, vents, lab cartridges, and fluid fittings where generated models often leave tubes ending near a block. The receiver has a real through-port and raised boss, the fitting is hollow with a shoulder and multiple barb ridges, and the hose is modeled as an installed tube over the barb envelope with a clamp band. The hose bore is sized for the deformed installed hose, so collision checks distinguish the retained interface from impossible solid overlap. Coordinate convention: the fluid axis is +X through the world origin. The receiver block is centered around `x=0`; the raised boss and hose are on the +X side. **Example** ```ts const hosePort = lib.hoseBarbPortAssembly({ hoseInnerDiameter: 6, hoseOuterDiameter: 10, }); verify.notColliding('hose clears barb peaks', hosePort.hose, hosePort.fitting); verify.inRange('fitting shoulder seats near boss face', hosePort.dims.faceClearance, 0.01, 0.08); return hosePort.parts; ```
|
|
137
|
+
- `routedTubeClipAssembly(options: RoutedTubeClipAssemblyOptions): RoutedTubeClipAssemblyResult` — Routed tube or cable retained by saddle clips with real bores, screw holes, and installed screws. **Details** This pattern is for hoses, wires, pump tubes, sensor leads, and appliance cable runs where generated models often draw a cylinder near a wall without clips or strain relief. The base panel has receiving screw envelopes, each saddle clip has a real through-bore around the tube and vertical screw clearances, and the installed screws share those positions. Coordinate convention: the routed tube runs along +X through the world origin. The base panel starts at `z=0`; clips sit on top of the panel, and the tube passes through their bores. **Example** ```ts const route = lib.routedTubeClipAssembly({ tubeDiameter: 6, clipCount: 3, }); verify.notColliding('tube clears clip bores', route.tube, union(...route.clips)); verify.notColliding('clip screws clear retained stack', union(...route.screws), union(route.panel, ...route.clips)); return route.parts; ```
|
|
138
|
+
- `pcbTerminalBlockAssembly(options?: PcbTerminalBlockAssemblyOptions): PcbTerminalBlockAssemblyResult` — PCB terminal-block stack with a backplate, standoffs, mounting screws, pin holes, and a seated terminal block. **Details** This pattern is for thermostat backplates, appliance control panels, sensor boards, and small electronics where generated models often place a terminal block, screw heads, and holes as independent decorations. The PCB mounting holes, fused standoffs, installed screws, terminal pins, and PCB pin clearances all come from one shared datum system so the purchased block is mechanically seated and the board is actually mounted. Coordinate convention: X/Y are the board footprint, Z is up. The backplate starts at `z=0`, standoffs rise from the plate, the PCB rests on the standoffs, and the terminal block sits on top of the PCB near the front edge. **Example** ```ts const terminalStack = lib.pcbTerminalBlockAssembly({ terminalCount: 5, screwSize: 'M3', }); verify.notColliding('terminal pins clear PCB holes', terminalStack.terminalBlock, terminalStack.pcb); verify.notColliding('mounting screws clear PCB and standoff holes', union(...terminalStack.screws), union(terminalStack.pcb, terminalStack.backplate)); return terminalStack.parts; ```
|
|
139
|
+
- `thumbScrewClampAssembly(options?: ThumbScrewClampAssemblyOptions): ThumbScrewClampAssemblyResult` — Thumb-screw clamp with a C-frame, threaded boss, captive pressure pad, knob, and clamped workpiece. **Details** This pattern is for bench clamps, monitor-arm desk clamps, small vise screws, capo pressure screws, fixture hold-downs, and service brackets where generated models often place a loose screw, knob, or pressure pad near a bracket. The helper creates a one-piece clamp frame with a fixed anvil pad, a bored threaded support and boss, an installed screw with a captive pressure pad and hand knob, and a representative clamped workpiece seated between the pads. Coordinate convention: the clamp screw runs along +X. The fixed anvil is on the -X side, the threaded support and knob are on the +X side, and Z is up from the base bridge. **Example** ```ts const clamp = lib.thumbScrewClampAssembly({ screwSize: 'M6', workpieceThickness: 20, }); verify.notColliding('thumb screw clears threaded boss', clamp.clampScrew, clamp.frame); verify.clearanceBetween('pressure pad is seated on workpiece', clamp.clampScrew, clamp.workpiece, -0.01, 0.05); return clamp.parts; ```
|
|
124
140
|
- `pipeRoute(points: [ number, number, number ][], radius: number, options?: { bendRadius?: number; wall?: number; segments?: number; }): Shape` — Route a pipe (solid or hollow) through 3D waypoints with smooth bends. Each interior waypoint gets a torus-section bend. Straight segments connect them. Returns a single unioned Shape.
|
|
125
141
|
- `elbow(pipeRadius: number, bendRadius: number, angle?: number | { ... }, options?: { ... }): Shape` — Pipe elbow — a curved pipe section (torus arc) for connecting two pipe directions. By default creates a bend in the XZ plane: incoming along +Z, outgoing rotated by `angle`. The bend starts at the origin, curving away from it.
|
|
126
142
|
- `beltDrive(options: BeltDriveOptions): BeltDriveResult` — Create a flat open-belt body around two pulley pitch circles. The belt is generated as a tangent loop in the XY plane and extruded along +Z by `beltWidth`. The result includes the solid belt, the 2D belt profile, a thin pitch-path sketch for visualization, total belt length, tangent spans, and wrap metadata for each pulley. For more than two pulleys, the API intentionally asks for route intent before geometry is created. Use `route: "outer"` for the future outside-envelope mode, or an ordered route for future serpentine/idler layouts. ```ts const drive = lib.beltDrive({ pulleys: [ { name: "motor", center: [0, 0], pitchRadius: 12 }, { name: "output", center: [80, 0], pitchRadius: 28 }, ], beltWidth: 8, beltThickness: 2, }); return drive.belt; ```
|
|
@@ -1457,7 +1457,15 @@ detectArrangement(): Sketch[]
|
|
|
1457
1457
|
#### `detectArrangementRegion()` — Select the single arrangement region that contains the given seed point. Throws if no region contains the seed.
|
|
1458
1458
|
|
|
1459
1459
|
```ts
|
|
1460
|
-
detectArrangementRegion(
|
|
1460
|
+
detectArrangementRegion(_seed: [ number, number ]): Sketch
|
|
1461
|
+
```
|
|
1462
|
+
|
|
1463
|
+
#### `toPolyline()` — Return the solved constrained path as a sampled 2D polyline.
|
|
1464
|
+
|
|
1465
|
+
Use this when a construction rail was authored with `constrainedSketch()` and should feed another operation such as `Loft.pathOnXz(...)`. The sketch must contain exactly one profile path.
|
|
1466
|
+
|
|
1467
|
+
```ts
|
|
1468
|
+
toPolyline(samples?: number): [ number, number ][]
|
|
1461
1469
|
```
|
|
1462
1470
|
|
|
1463
1471
|
#### `withUpdatedConstraint()` — Re-solve the sketch after changing the value of one existing constraint.
|