forgecad 0.9.4 → 0.9.6
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-jwoEgwE_.js → AdminPage-Da6hhpJx.js} +1 -1
- package/dist/assets/{BlogPage-Ck7g3ue2.js → BlogPage-Bl_sKeWb.js} +1 -1
- package/dist/assets/{DocsPage-9WaRC14b.js → DocsPage-Blz3Tp4j.js} +1 -6
- package/dist/assets/EditorApp-CuiPbtn5.js +12754 -0
- package/dist/assets/{EmbedViewer-37_PfMwv.js → EmbedViewer-BFG6-Ufm.js} +2 -2
- package/dist/assets/{LandingPageProofDriven-CO8WL0CY.js → LandingPageProofDriven-DB9fQd5P.js} +1 -1
- package/dist/assets/{PricingPage-DADKGuOa.js → PricingPage-BMxYT_F0.js} +1 -1
- package/dist/assets/{SettingsPage-DKKI4W49.js → SettingsPage-VVQNrCAg.js} +1 -1
- package/dist/assets/{app-CwI02pTA.js → app-Dl9ymBWC.js} +355 -36
- package/dist/assets/cli/{render-Kw5hLEcL.js → render-CFtwKCCY.js} +203 -862
- package/dist/assets/{sectionPlaneMath-C8N0w8o3.js → distance-BEC2RjJi.js} +4150 -801
- package/dist/assets/{evalWorker-D6ub3kfS.js → evalWorker-CRvbzTXm.js} +2611 -528
- package/dist/assets/{manifold-CwDdMKyc.js → manifold-B9QSr-qP.js} +1 -1
- package/dist/assets/{manifold-DTvmxSDf.js → manifold-DpBXFS2K.js} +1 -1
- package/dist/assets/{manifold-lru0jwVw.js → manifold-DzZ4VRPs.js} +2 -2
- package/dist/assets/{renderSceneState-tvtNKNRi.js → renderSceneState-BuAXF2jh.js} +1 -1
- package/dist/assets/{reportWorker-DeqktDGt.js → reportWorker-BNWEnRg1.js} +2606 -525
- package/dist/cli/render.html +1 -1
- package/dist/docs/index.html +2 -2
- package/dist/docs-raw/AI/usage.md +0 -1
- package/dist/docs-raw/API/core/concepts.md +11 -1
- package/dist/docs-raw/CLI.md +64 -13
- package/dist/docs-raw/beta-operations.md +4 -0
- package/dist/docs-raw/deployment.md +38 -23
- package/dist/docs-raw/generated/assembly.md +8 -3
- package/dist/docs-raw/generated/concepts.md +126 -46
- package/dist/docs-raw/generated/core.md +97 -47
- package/dist/docs-raw/generated/curves.md +113 -595
- package/dist/docs-raw/generated/lib.md +40 -3
- package/dist/docs-raw/generated/output.md +6 -1
- package/dist/docs-raw/generated/sdf.md +50 -4
- package/dist/docs-raw/generated/sketch.md +9 -1
- package/dist/docs-raw/generated/viewport.md +1 -9
- package/dist/docs-raw/guides/inspection-bundles.md +40 -9
- package/dist/docs-raw/runbook.md +3 -3
- package/dist/docs-raw/skills/forgecad-blockout-model.md +1 -0
- package/dist/docs-raw/skills/forgecad-image-replicator.md +3 -1
- package/dist/docs-raw/skills/forgecad-make-a-model.md +48 -4
- package/dist/docs-raw/skills/forgecad-render-inspect.md +3 -1
- package/dist/docs-raw/skills/forgecad-visual-spec.md +2 -0
- package/dist/docs-raw/skills/forgecad.md +2 -1
- package/dist/docs-raw/skills/index.md +0 -1
- package/dist/index.html +1 -1
- package/dist/sitemap.xml +6 -6
- package/dist-cli/blender/render.py +43 -8
- package/dist-cli/forgecad.js +5729 -2015
- package/dist-cli/forgecad.js.map +1 -1
- package/dist-skill/CONTEXT.md +372 -667
- package/dist-skill/SKILL-dev.md +2 -1
- package/dist-skill/SKILL.md +2 -1
- package/dist-skill/docs/API/core/concepts.md +11 -1
- package/dist-skill/docs/CLI.md +64 -13
- package/dist-skill/docs/generated/assembly.md +8 -3
- package/dist-skill/docs/generated/core.md +97 -47
- package/dist-skill/docs/generated/curves.md +113 -595
- package/dist-skill/docs/generated/lib.md +40 -3
- package/dist-skill/docs/generated/output.md +6 -1
- package/dist-skill/docs/generated/sdf.md +50 -4
- package/dist-skill/docs/generated/sketch.md +9 -1
- package/dist-skill/docs/generated/viewport.md +1 -9
- package/dist-skill/docs/guides/inspection-bundles.md +40 -9
- package/dist-skill/docs-dev/API/core/concepts.md +11 -1
- package/dist-skill/docs-dev/CLI.md +64 -13
- package/dist-skill/docs-dev/generated/assembly.md +8 -3
- package/dist-skill/docs-dev/generated/core.md +97 -47
- package/dist-skill/docs-dev/generated/curves.md +113 -595
- package/dist-skill/docs-dev/generated/lib.md +40 -3
- package/dist-skill/docs-dev/generated/output.md +6 -1
- package/dist-skill/docs-dev/generated/sdf.md +50 -4
- package/dist-skill/docs-dev/generated/sketch.md +9 -1
- package/dist-skill/docs-dev/generated/viewport.md +1 -9
- package/dist-skill/docs-dev/guides/inspection-bundles.md +40 -9
- package/dist-skill/library/README.md +0 -1
- package/dist-skill/library/forgecad-blockout-model/SKILL.md +1 -0
- package/dist-skill/library/forgecad-image-replicator/SKILL.md +3 -1
- package/dist-skill/library/forgecad-make-a-model/SKILL.md +48 -4
- package/dist-skill/library/forgecad-render-inspect/SKILL.md +3 -1
- package/dist-skill/library/forgecad-visual-spec/SKILL.md +2 -0
- package/examples/api/drive-wheel-regions.forge.js +43 -0
- package/examples/api/guided-loft-olive-oil-bottle.forge.js +135 -0
- package/examples/api/sdf-circular-array-knurling.forge.js +19 -0
- package/examples/api/sdf-pattern2d-ceramic-ripple-set.forge.js +83 -0
- package/examples/api/sdf-pattern2d-grip-tread.forge.js +72 -0
- package/examples/api/sdf-pattern2d-orbital-jewelry.forge.js +62 -0
- package/examples/api/sdf-surface-basket-weave.forge.js +67 -0
- package/examples/api/sector-gear-body.forge.js +34 -0
- package/package.json +20 -2
- package/dist/assets/EditorApp-Dja2jMmW.js +0 -12509
- package/dist/docs-raw/skills/forgecad-api-dogfood.md +0 -130
- package/dist-skill/library/forgecad-api-dogfood/SKILL.md +0 -125
|
@@ -4,10 +4,10 @@ Every public API function belongs to one of 16 fundamental concepts. This docume
|
|
|
4
4
|
|
|
5
5
|
## Concepts
|
|
6
6
|
|
|
7
|
-
- **[C1: Primitive Construction](#c1-primitive-construction)** — Create geometry from parameters — no input geometry required. *(
|
|
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)*
|
|
@@ -259,6 +259,12 @@ sdf.basketWeave(options?: BasketWeaveOptions): SurfacePattern
|
|
|
259
259
|
- `threadWidth?: number` — Thread width in mm. Default: 1.5
|
|
260
260
|
- `depth?: number` — Thread protrusion depth in mm. Default: 0.8
|
|
261
261
|
|
|
262
|
+
#### `sdf.pattern2d()` — Create typed, composable 2D surface patterns for `.surfaceDisplace()`.
|
|
263
|
+
|
|
264
|
+
```ts
|
|
265
|
+
sdf.pattern2d(): Pattern2DBuilder
|
|
266
|
+
```
|
|
267
|
+
|
|
262
268
|
#### `sdf.twist()` — Twist an SDF shape around the Z axis.
|
|
263
269
|
|
|
264
270
|
```ts
|
|
@@ -277,6 +283,12 @@ sdf.bend(shape: SdfShape, radius: number): SdfShape
|
|
|
277
283
|
sdf.repeat(shape: SdfShape, spacing: Vec3, count?: Vec3): SdfShape
|
|
278
284
|
```
|
|
279
285
|
|
|
286
|
+
#### `sdf.circularArray()` — Arrange an SDF shape in a circular array around the Z axis with O(1) folded-domain evaluation.
|
|
287
|
+
|
|
288
|
+
```ts
|
|
289
|
+
sdf.circularArray(shape: SdfShape, count: number, offset?: number): SdfShape
|
|
290
|
+
```
|
|
291
|
+
|
|
280
292
|
#### `sdf.SurfacePattern()` — A 2D surface pattern — a heightmap function for use with `.surfaceDisplace()`.
|
|
281
293
|
|
|
282
294
|
```ts
|
|
@@ -362,7 +374,6 @@ sdf.Sculpt: { sphere: (radius: number) => SdfShape; box: (x: number, y: number,
|
|
|
362
374
|
| `behavior?` | `"opt-in" \| "auto"` | Whether the viewer should offer or auto-open the journey. First slice supports opt-in. |
|
|
363
375
|
| `steps` | `SceneJourneyStepConfig[]` | Ordered journey spine. Branches can be added later without changing this core contract. |
|
|
364
376
|
| `valid?` | `boolean` | True unless any journey or step diagnostic has level "error". |
|
|
365
|
-
| `diagnostics?` | `SceneJourneyDiagnostic[]` | Whole-journey diagnostics, including unresolved startsAt and step target diagnostics. |
|
|
366
377
|
|
|
367
378
|
**`SceneJourneyStepConfig`**
|
|
368
379
|
|
|
@@ -375,9 +386,6 @@ sdf.Sculpt: { sphere: (radius: number) => SdfShape; box: (x: number, y: number,
|
|
|
375
386
|
| `camera?` | `SceneViewCameraConfig` | Optional explicit camera for this step. When omitted, the viewer fits `focus`. |
|
|
376
387
|
| `resolvedFocusId?` | `string \| null` | Resolved object id after script execution, when `focus` matched exactly one object. |
|
|
377
388
|
| `resolvedFocusPath?` | `string \| null` | Resolved object tree path or name after script execution. |
|
|
378
|
-
| `diagnostics?` | `SceneJourneyDiagnostic[]` | Resolution diagnostics for this step. |
|
|
379
|
-
|
|
380
|
-
`SceneJourneyDiagnostic`: `{ level: SceneJourneyDiagnosticLevel, message: string, stepId?: string, suggestions?: string[] }`
|
|
381
389
|
|
|
382
390
|
**`SceneLightConfig`**
|
|
383
391
|
|
|
@@ -994,6 +1002,87 @@ composeChain(...steps: TransformInput[]): Transform
|
|
|
994
1002
|
|
|
995
1003
|
Convert a 2D profile into a 3D solid (extrude, revolve, loft, sweep).
|
|
996
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
|
+
|
|
997
1086
|
#### `Analysis.EdgeContinuity()`
|
|
998
1087
|
|
|
999
1088
|
```ts
|
|
@@ -1059,14 +1148,6 @@ Blend.Surface(options: BlendSurfaceOptions): Shape
|
|
|
1059
1148
|
|
|
1060
1149
|
`SurfaceMatchConstraintInput`: `{ target: EdgeRef, continuity?: SurfaceContinuity }`
|
|
1061
1150
|
|
|
1062
|
-
#### `Blend.CornerY()` — Current implementation uses continuity-controlled edge fillets on solid edges. It does not yet provide a dedicated open-sheet or multi-patch Y-corner solver. Follow progress: https://github.com/KoStard/forgecad-private/issues/162
|
|
1063
|
-
|
|
1064
|
-
```ts
|
|
1065
|
-
Blend.CornerY(options: BlendCornerYOptions): Shape
|
|
1066
|
-
```
|
|
1067
|
-
|
|
1068
|
-
`BlendCornerYOptions`: `{ edges: EdgeRef[], cornerTolerance?: number, minBranchAngleDeg?: number }`
|
|
1069
|
-
|
|
1070
1151
|
#### `Surface.Nurbs()`
|
|
1071
1152
|
|
|
1072
1153
|
```ts
|
|
@@ -1254,11 +1335,8 @@ connectEdges(edgeA: EdgeSegment, edgeB: EdgeSegment, options?: ConnectEdgesOptio
|
|
|
1254
1335
|
| `normalA` | `Vec3` | Normal of first adjacent face. |
|
|
1255
1336
|
| `normalB` | `Vec3` | Normal of second adjacent face (same as normalA for boundary edges). |
|
|
1256
1337
|
| `boundary` | `boolean` | true if this is a boundary (unmatched) edge — unusual for closed solids. |
|
|
1257
|
-
| `nativeTopology?` | `EdgeNativeTopologyRef` | Native kernel topology identity when the active backend can provide one. |
|
|
1258
1338
|
| `start`, `end`, `midpoint`, `length` | | — |
|
|
1259
1339
|
|
|
1260
|
-
`EdgeNativeTopologyRef`: `{ backend: "truck", edge: number }`
|
|
1261
|
-
|
|
1262
1340
|
**`TransitionCurveOptions`**
|
|
1263
1341
|
- `weightA?: number` — Weight for the start edge. Controls tangent magnitude at the start. - 1.0 (default): balanced transition - > 1.0: curve follows start edge longer before turning - < 1.0: curve turns sooner at the start
|
|
1264
1342
|
- `weightB?: number` — Weight for the end edge. Controls tangent magnitude at the end. - 1.0 (default): balanced transition - > 1.0: curve follows end edge longer before turning - < 1.0: curve turns sooner at the end
|
|
@@ -1315,10 +1393,6 @@ Performance note: loft is significantly heavier than primitive/extrude/revolve.
|
|
|
1315
1393
|
loft(profiles: Sketch[], heights: number[], options?: LoftOptions): Shape
|
|
1316
1394
|
```
|
|
1317
1395
|
|
|
1318
|
-
**`LoftOptions`**
|
|
1319
|
-
- `edgeLength?: number` — Marching-grid edge length for level-set meshing. Smaller = finer.
|
|
1320
|
-
- `boundsPadding?: number` — Optional extra bounds padding.
|
|
1321
|
-
|
|
1322
1396
|
#### `loftAlongSpine()` — Loft between multiple profiles positioned along an arbitrary 3D spine curve.
|
|
1323
1397
|
|
|
1324
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.
|
|
@@ -1495,11 +1569,8 @@ coalesceEdges(segments: EdgeSegment[], tolerance?: number): EdgeSegment[]
|
|
|
1495
1569
|
| `normalA` | `Vec3` | Normal of first adjacent face. |
|
|
1496
1570
|
| `normalB` | `Vec3` | Normal of second adjacent face (same as normalA for boundary edges). |
|
|
1497
1571
|
| `boundary` | `boolean` | true if this is a boundary (unmatched) edge — unusual for closed solids. |
|
|
1498
|
-
| `nativeTopology?` | `EdgeNativeTopologyRef` | Native kernel topology identity when the active backend can provide one. |
|
|
1499
1572
|
| `start`, `end`, `midpoint`, `length` | | — |
|
|
1500
1573
|
|
|
1501
|
-
`EdgeNativeTopologyRef`: `{ backend: "truck", edge: number }`
|
|
1502
|
-
|
|
1503
1574
|
#### `selectEdge()` — Select the single best-matching edge from a shape.
|
|
1504
1575
|
|
|
1505
1576
|
When `near` is specified, returns the edge whose midpoint is closest to that point. Otherwise returns the first matching edge in mesh order. Throws if no edges match the query — useful as a guard when you expect exactly one result.
|
|
@@ -1559,9 +1630,11 @@ selectEdges(shape: Shape, query?: EdgeQuery): EdgeSegment[]
|
|
|
1559
1630
|
|
|
1560
1631
|
Modify edges of a solid — fillets, chamfers, draft, offset.
|
|
1561
1632
|
|
|
1562
|
-
#### `chamfer()` — Apply chamfers (beveled edges) to one or more edges of a shape.
|
|
1633
|
+
#### `chamfer()` — Apply experimental chamfers (beveled edges) to one or more edges of a shape.
|
|
1634
|
+
|
|
1635
|
+
**Experimental**: chamfers are still backend-sensitive. The Manifold backend is known to produce incorrect results for some edge-finish cases, and the OCCT backend can be very slow, especially with broad edge selections. Prefer targeted edge selectors and inspect the result before treating it as production-ready geometry.
|
|
1563
1636
|
|
|
1564
|
-
Produces a 45° bevel at the specified `size` (distance from edge).
|
|
1637
|
+
Produces a 45° bevel at the specified `size` (distance from edge). Edge selections compile into backend operations; unsupported selections fail as explicit kernel gaps instead of using TypeScript geometry fallbacks.
|
|
1565
1638
|
|
|
1566
1639
|
The `edges` parameter accepts the same options as `fillet()`: inline `EdgeQuery`, pre-selected `EdgeSegment`/`EdgeSegment[]`, or `undefined` (all sharp edges).
|
|
1567
1640
|
|
|
@@ -1595,9 +1668,11 @@ draft(myShape, 2, [0, 0, 1], 10)
|
|
|
1595
1668
|
draft(shape: Shape, angleDeg: number, pullDirection?: [ number, number, number ], neutralPlaneOffset?: number): Shape
|
|
1596
1669
|
```
|
|
1597
1670
|
|
|
1598
|
-
#### `fillet()` — Apply fillets (rounded edges) to one or more edges of a shape.
|
|
1671
|
+
#### `fillet()` — Apply experimental fillets (rounded edges) to one or more edges of a shape.
|
|
1599
1672
|
|
|
1600
|
-
|
|
1673
|
+
**Experimental**: fillets are still backend-sensitive. The Manifold backend is known to produce incorrect results for some edge-finish cases, and the OCCT backend can be very slow, especially with broad edge selections. Prefer targeted edge selectors and inspect the result before treating it as production-ready geometry.
|
|
1674
|
+
|
|
1675
|
+
Edge selections compile into backend operations; unsupported selections fail as explicit kernel gaps instead of using TypeScript geometry fallbacks.
|
|
1601
1676
|
|
|
1602
1677
|
The `edges` parameter is flexible:
|
|
1603
1678
|
|
|
@@ -2165,7 +2240,12 @@ bomToCsv(rows: BomRow[]): string
|
|
|
2165
2240
|
|
|
2166
2241
|
**`BomRow`**: `part: string`, `qty: number`, `material?: string`, `process?: string`, `tolerance?: string`, `notes?: string`, `metadata?: PartMetadata`
|
|
2167
2242
|
|
|
2168
|
-
**`PartMetadata
|
|
2243
|
+
**`PartMetadata`**
|
|
2244
|
+
|
|
2245
|
+
| Option | Type | Description |
|
|
2246
|
+
|--------|------|-------------|
|
|
2247
|
+
| `tags?` | `string \| readonly string[]` | Viewport organization tags applied to scene objects produced from this part. |
|
|
2248
|
+
| `material?`, `process?`, `tolerance?`, `qty?`, `notes?`, `densityKgM3?`, `massKg?` | | — |
|
|
2169
2249
|
|
|
2170
2250
|
#### `joint()` — Create a revolute joint that auto-generates a parameter slider and rotates the shape.
|
|
2171
2251
|
|
|
@@ -2599,7 +2679,12 @@ robotExport(options: RobotExportOptions): CollectedRobotExport
|
|
|
2599
2679
|
|
|
2600
2680
|
`AssemblyPartDef`: `{ name: string, part: AssemblyPart, base: Transform, metadata?: PartMetadata }`
|
|
2601
2681
|
|
|
2602
|
-
**`PartMetadata
|
|
2682
|
+
**`PartMetadata`**
|
|
2683
|
+
|
|
2684
|
+
| Option | Type | Description |
|
|
2685
|
+
|--------|------|-------------|
|
|
2686
|
+
| `tags?` | `string \| readonly string[]` | Viewport organization tags applied to scene objects produced from this part. |
|
|
2687
|
+
| `material?`, `process?`, `tolerance?`, `qty?`, `notes?`, `densityKgM3?`, `massKg?` | | — |
|
|
2603
2688
|
|
|
2604
2689
|
**`AssemblyJointDef`**: `name: string`, `type: JointType`, `parent: string`, `child: string`, `frame: Transform`, `axis: Vec3`, `min?: number`, `max?: number`, `defaultValue: number`, `unit?: string`, `effort?: number`, `velocity?: number`, `damping?: number`, `friction?: number`, `connectorRefs?: JointConnectorRefs`
|
|
2605
2690
|
|
|
@@ -2978,7 +3063,7 @@ When `lights` is specified, **all** default lights are removed. You must include
|
|
|
2978
3063
|
|
|
2979
3064
|
Setting `camera.position` overrides auto-framing — the viewport will no longer auto-fit the geometry on script reload.
|
|
2980
3065
|
|
|
2981
|
-
Named render views let scripts check in repeatable cameras next to the model code. The canonical shape is `{ camera: { position, target } }`, and a direct camera shorthand `{ position, target }` is also accepted. Use the canonical shape when you may add view metadata later. Use it from the CLI with `forgecad render 3d
|
|
3066
|
+
Named render views let scripts check in repeatable cameras next to the model code. The canonical shape is `{ camera: { position, target } }`, and a direct camera shorthand `{ position, target }` is also accepted. Use the canonical shape when you may add view metadata later. Use it from the CLI with `--view hero` on `forgecad render 3d`, `forgecad render hq`, or `forgecad capture`.
|
|
2982
3067
|
|
|
2983
3068
|
Model journeys let scripts check in a compact guided path through named objects. Each journey has ordered `steps`; each step can name a `focus` target by object name/tree path, provide a caption, and optionally provide an explicit camera. In the viewer, journeys are opt-in: they appear as a small Explore control and do not move the camera until the user starts them. Use `forgecad run model.forge.js --journeys` or `--journeys-json` to inspect resolved targets.
|
|
2984
3069
|
|
|
@@ -3047,7 +3132,6 @@ scene(options: SceneOptions): void
|
|
|
3047
3132
|
| `behavior?` | `"opt-in" \| "auto"` | Whether the viewer should offer or auto-open the journey. First slice supports opt-in. |
|
|
3048
3133
|
| `steps` | `SceneJourneyStepConfig[]` | Ordered journey spine. Branches can be added later without changing this core contract. |
|
|
3049
3134
|
| `valid?` | `boolean` | True unless any journey or step diagnostic has level "error". |
|
|
3050
|
-
| `diagnostics?` | `SceneJourneyDiagnostic[]` | Whole-journey diagnostics, including unresolved startsAt and step target diagnostics. |
|
|
3051
3135
|
|
|
3052
3136
|
**`SceneJourneyStepConfig`**
|
|
3053
3137
|
|
|
@@ -3060,9 +3144,6 @@ scene(options: SceneOptions): void
|
|
|
3060
3144
|
| `camera?` | `SceneViewCameraConfig` | Optional explicit camera for this step. When omitted, the viewer fits `focus`. |
|
|
3061
3145
|
| `resolvedFocusId?` | `string \| null` | Resolved object id after script execution, when `focus` matched exactly one object. |
|
|
3062
3146
|
| `resolvedFocusPath?` | `string \| null` | Resolved object tree path or name after script execution. |
|
|
3063
|
-
| `diagnostics?` | `SceneJourneyDiagnostic[]` | Resolution diagnostics for this step. |
|
|
3064
|
-
|
|
3065
|
-
`SceneJourneyDiagnostic`: `{ level: SceneJourneyDiagnosticLevel, message: string, stepId?: string, suggestions?: string[] }`
|
|
3066
3147
|
|
|
3067
3148
|
**`SceneLightConfig`**
|
|
3068
3149
|
|
|
@@ -3146,10 +3227,6 @@ viewConfig({
|
|
|
3146
3227
|
viewConfig(options?: ViewConfigOptions): void
|
|
3147
3228
|
```
|
|
3148
3229
|
|
|
3149
|
-
`ViewConfigOptions`: `{ jointOverlay?: JointOverlayViewConfigOptions }`
|
|
3150
|
-
|
|
3151
|
-
**`JointOverlayViewConfigOptions`**: `enabled?: boolean`, `axisColor?: string`, `axisCoreColor?: string`, `arcColor?: string`, `zeroColor?: string`, `arcVisualLimitDeg?: number`, `axisLengthScale?: number`, `axisLengthMin?: number`, `axisLineRadiusScale?: number`, `axisLineRadiusMin?: number`, `axisLineRadiusMax?: number`, `spokeLineRadiusScale?: number`, `spokeLineRadiusMin?: number`, `spokeLineRadiusMax?: number`, `arcLineRadiusScale?: number`, `arcLineRadiusMin?: number`, `arcLineRadiusMax?: number`, `axisDotRadiusScale?: number`, `axisDotRadiusMin?: number`, `axisArrowRadiusScale?: number`, `axisArrowRadiusMin?: number`, `axisArrowLengthScale?: number`, `axisArrowLengthMin?: number`, `axisArrowOffsetFactor?: number`, `arcRadiusScale?: number`, `arcRadiusMin?: number`, `arcDotRadiusScale?: number`, `arcDotRadiusMin?: number`, `arcArrowRadiusScale?: number`, `arcArrowRadiusMin?: number`, `arcArrowLengthScale?: number`, `arcArrowLengthMin?: number`, `arcArrowOffsetFactor?: number`, `arcStepDeg?: number`, `arcMinSteps?: number`, `arcTubeSegmentsMin?: number`, `arcTubeSegmentsFactor?: number`, `arcTubeRadialSegments?: number`
|
|
3152
|
-
|
|
3153
3230
|
#### `spec()` — Create a named, reusable bundle of verification checks.
|
|
3154
3231
|
|
|
3155
3232
|
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.
|
|
@@ -3197,18 +3274,21 @@ Bring external geometry or other ForgeCAD modules into the current script.
|
|
|
3197
3274
|
|
|
3198
3275
|
Unlike union(), child colors and individual identities are preserved. Children can be plain shapes, named descriptors ({ name, shape/sketch/group }), or nested groups. The returned ShapeGroup supports all Shape transforms (translate, rotate, etc.).
|
|
3199
3276
|
|
|
3277
|
+
Named descriptors can include `tags` for viewport organization. Tags do not affect geometry; they let the command palette hide, show only, or focus all objects with the same tag.
|
|
3278
|
+
|
|
3200
3279
|
**Local coordinate pattern:** Build child parts at the origin (local coordinates), then group and translate once to place the whole assembly. This eliminates the error-prone pattern of manually adding parent offsets to every sub-part.
|
|
3201
3280
|
|
|
3202
3281
|
```js
|
|
3203
|
-
|
|
3204
|
-
const
|
|
3205
|
-
const
|
|
3206
|
-
const
|
|
3207
|
-
|
|
3282
|
+
const body = roundedBox(100, 20, 32, 4);
|
|
3283
|
+
const panel = box(98, 2, 18).translate(0, -12, 4);
|
|
3284
|
+
const louver = box(88, 2, 6).translate(0, -14, -11);
|
|
3285
|
+
const indoorUnit = group(
|
|
3286
|
+
{ name: 'Body', shape: body },
|
|
3287
|
+
{ name: 'Panel', tags: 'cover', shape: panel },
|
|
3288
|
+
{ name: 'Louver', tags: ['cover', 'moving'], shape: louver },
|
|
3289
|
+
).translate(0, -18, 70);
|
|
3208
3290
|
```
|
|
3209
3291
|
|
|
3210
|
-
// GOOD — build at origin, group, translate once const body = roundedBox(100, 20, 32, 4); const panel = box(98, 2, 18).translate(0, -12, 4); const louver = box(88, 2, 6).translate(0, -14, -11); const indoorUnit = group( { name: 'Body', shape: body }, { name: 'Panel', shape: panel }, { name: 'Louver', shape: louver }, ).translate(0, -18, 70);
|
|
3211
|
-
|
|
3212
3292
|
```ts
|
|
3213
3293
|
group(...items: GroupInput[]): ShapeGroup
|
|
3214
3294
|
```
|
|
@@ -18,15 +18,15 @@ 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
|
-
- [Backend Runtime](#backend-runtime) — `initKernel`, `setActiveBackend`, `activateBackend`, `getActiveBackend`
|
|
22
21
|
- [Verification](#verification) — `spec`
|
|
23
22
|
- [Shape](#shape) — Appearance, Face Topology, Edge Topology, Transforms, Booleans & Cutting, Features, Placement, Connectors, References, Measurement
|
|
24
23
|
- [Transform](#transform)
|
|
25
24
|
- [ShapeGroup](#shapegroup) — Children, Transforms, Placement, Connectors, References
|
|
26
25
|
- [SurfacePattern](#surfacepattern)
|
|
26
|
+
- [Pattern2D](#pattern2d)
|
|
27
|
+
- [Pattern2DBuilder](#pattern2dbuilder)
|
|
27
28
|
- [ShapeRef](#shaperef)
|
|
28
29
|
- [ANCHOR3D_NAMES](#anchor3d-names)
|
|
29
|
-
- [DEFAULT_ACTIVE_BACKEND](#default-active-backend)
|
|
30
30
|
- [verify](#verify)
|
|
31
31
|
- [Constraint](#constraint)
|
|
32
32
|
- [Points](#points)
|
|
@@ -121,9 +121,11 @@ intersection(...inputs: ShapeOperandInput[]): Shape
|
|
|
121
121
|
|
|
122
122
|
### Edge Features
|
|
123
123
|
|
|
124
|
-
#### `fillet()` — Apply fillets (rounded edges) to one or more edges of a shape.
|
|
124
|
+
#### `fillet()` — Apply experimental fillets (rounded edges) to one or more edges of a shape.
|
|
125
125
|
|
|
126
|
-
|
|
126
|
+
**Experimental**: fillets are still backend-sensitive. The Manifold backend is known to produce incorrect results for some edge-finish cases, and the OCCT backend can be very slow, especially with broad edge selections. Prefer targeted edge selectors and inspect the result before treating it as production-ready geometry.
|
|
127
|
+
|
|
128
|
+
Edge selections compile into backend operations; unsupported selections fail as explicit kernel gaps instead of using TypeScript geometry fallbacks.
|
|
127
129
|
|
|
128
130
|
The `edges` parameter is flexible:
|
|
129
131
|
|
|
@@ -149,9 +151,11 @@ fillet(myShape, 3, edges)
|
|
|
149
151
|
fillet(shape: Shape, radius: number, edges?: EdgeSelector, segments?: number): Shape
|
|
150
152
|
```
|
|
151
153
|
|
|
152
|
-
#### `chamfer()` — Apply chamfers (beveled edges) to one or more edges of a shape.
|
|
154
|
+
#### `chamfer()` — Apply experimental chamfers (beveled edges) to one or more edges of a shape.
|
|
155
|
+
|
|
156
|
+
**Experimental**: chamfers are still backend-sensitive. The Manifold backend is known to produce incorrect results for some edge-finish cases, and the OCCT backend can be very slow, especially with broad edge selections. Prefer targeted edge selectors and inspect the result before treating it as production-ready geometry.
|
|
153
157
|
|
|
154
|
-
Produces a 45° bevel at the specified `size` (distance from edge).
|
|
158
|
+
Produces a 45° bevel at the specified `size` (distance from edge). Edge selections compile into backend operations; unsupported selections fail as explicit kernel gaps instead of using TypeScript geometry fallbacks.
|
|
155
159
|
|
|
156
160
|
The `edges` parameter accepts the same options as `fillet()`: inline `EdgeQuery`, pre-selected `EdgeSegment`/`EdgeSegment[]`, or `undefined` (all sharp edges).
|
|
157
161
|
|
|
@@ -376,11 +380,8 @@ selectEdges(shape: Shape, query?: EdgeQuery): EdgeSegment[]
|
|
|
376
380
|
| `normalA` | `Vec3` | Normal of first adjacent face. |
|
|
377
381
|
| `normalB` | `Vec3` | Normal of second adjacent face (same as normalA for boundary edges). |
|
|
378
382
|
| `boundary` | `boolean` | true if this is a boundary (unmatched) edge — unusual for closed solids. |
|
|
379
|
-
| `nativeTopology?` | `EdgeNativeTopologyRef` | Native kernel topology identity when the active backend can provide one. |
|
|
380
383
|
| `start`, `end`, `midpoint`, `length` | | — |
|
|
381
384
|
|
|
382
|
-
`EdgeNativeTopologyRef`: `{ backend: "truck", edge: number }`
|
|
383
|
-
|
|
384
385
|
#### `selectEdge()` — Select the single best-matching edge from a shape.
|
|
385
386
|
|
|
386
387
|
When `near` is specified, returns the edge whose midpoint is closest to that point. Otherwise returns the first matching edge in mesh order. Throws if no edges match the query — useful as a guard when you expect exactly one result.
|
|
@@ -481,7 +482,7 @@ importSvgSketch(fileName: string, options?: SvgImportOptions): Sketch
|
|
|
481
482
|
importMesh(fileName: string, options?: { scale?: number; center?: boolean; }): Shape
|
|
482
483
|
```
|
|
483
484
|
|
|
484
|
-
#### `importStep()` — Import a STEP file (.step, .stp) as an exact OCCT-backed Shape. Preserves NURBS curves, B-spline surfaces, and exact topology. Requires
|
|
485
|
+
#### `importStep()` — Import a STEP file (.step, .stp) as an exact OCCT-backed Shape. Preserves NURBS curves, B-spline surfaces, and exact topology. Requires running with the OCCT backend.
|
|
485
486
|
|
|
486
487
|
```ts
|
|
487
488
|
importStep(fileName: string): Shape
|
|
@@ -617,18 +618,21 @@ Param.list<T extends Record<string, number | boolean | string>>(name: string, de
|
|
|
617
618
|
|
|
618
619
|
Unlike union(), child colors and individual identities are preserved. Children can be plain shapes, named descriptors ({ name, shape/sketch/group }), or nested groups. The returned ShapeGroup supports all Shape transforms (translate, rotate, etc.).
|
|
619
620
|
|
|
621
|
+
Named descriptors can include `tags` for viewport organization. Tags do not affect geometry; they let the command palette hide, show only, or focus all objects with the same tag.
|
|
622
|
+
|
|
620
623
|
**Local coordinate pattern:** Build child parts at the origin (local coordinates), then group and translate once to place the whole assembly. This eliminates the error-prone pattern of manually adding parent offsets to every sub-part.
|
|
621
624
|
|
|
622
625
|
```js
|
|
623
|
-
|
|
624
|
-
const
|
|
625
|
-
const
|
|
626
|
-
const
|
|
627
|
-
|
|
626
|
+
const body = roundedBox(100, 20, 32, 4);
|
|
627
|
+
const panel = box(98, 2, 18).translate(0, -12, 4);
|
|
628
|
+
const louver = box(88, 2, 6).translate(0, -14, -11);
|
|
629
|
+
const indoorUnit = group(
|
|
630
|
+
{ name: 'Body', shape: body },
|
|
631
|
+
{ name: 'Panel', tags: 'cover', shape: panel },
|
|
632
|
+
{ name: 'Louver', tags: ['cover', 'moving'], shape: louver },
|
|
633
|
+
).translate(0, -18, 70);
|
|
628
634
|
```
|
|
629
635
|
|
|
630
|
-
// GOOD — build at origin, group, translate once const body = roundedBox(100, 20, 32, 4); const panel = box(98, 2, 18).translate(0, -12, 4); const louver = box(88, 2, 6).translate(0, -14, -11); const indoorUnit = group( { name: 'Body', shape: body }, { name: 'Panel', shape: panel }, { name: 'Louver', shape: louver }, ).translate(0, -18, 70);
|
|
631
|
-
|
|
632
636
|
```ts
|
|
633
637
|
group(...items: GroupInput[]): ShapeGroup
|
|
634
638
|
```
|
|
@@ -663,32 +667,6 @@ projectToPlane(shape: Shape, plane: PlaneSpec): Sketch
|
|
|
663
667
|
composeChain(...steps: TransformInput[]): Transform
|
|
664
668
|
```
|
|
665
669
|
|
|
666
|
-
### Backend Runtime
|
|
667
|
-
|
|
668
|
-
#### `initKernel()`
|
|
669
|
-
|
|
670
|
-
```ts
|
|
671
|
-
initKernel(): Promise<unknown>
|
|
672
|
-
```
|
|
673
|
-
|
|
674
|
-
#### `setActiveBackend()`
|
|
675
|
-
|
|
676
|
-
```ts
|
|
677
|
-
setActiveBackend(backend: ActiveBackend): void
|
|
678
|
-
```
|
|
679
|
-
|
|
680
|
-
#### `activateBackend()` — Set the active backend and ensure its WASM module is initialized. Call this instead of `setActiveBackend` when you're about to execute code — it guarantees the backend is ready, not just selected.
|
|
681
|
-
|
|
682
|
-
```ts
|
|
683
|
-
activateBackend(backend: ActiveBackend): Promise<void>
|
|
684
|
-
```
|
|
685
|
-
|
|
686
|
-
#### `getActiveBackend()`
|
|
687
|
-
|
|
688
|
-
```ts
|
|
689
|
-
getActiveBackend(): ActiveBackend
|
|
690
|
-
```
|
|
691
|
-
|
|
692
670
|
### Verification
|
|
693
671
|
|
|
694
672
|
#### `spec()` — Create a named, reusable bundle of verification checks.
|
|
@@ -1674,6 +1652,82 @@ color(hex: string): ShapeGroup
|
|
|
1674
1652
|
| `body` | `string` | Function body: receives (u, v) in surface mm, returns height displacement. |
|
|
1675
1653
|
| `constants` | `Record<string, number>` | Named constants injected into the function. |
|
|
1676
1654
|
|
|
1655
|
+
### `Pattern2D`
|
|
1656
|
+
|
|
1657
|
+
#### `add()` — Add this pattern to one or more patterns or constant height offsets.
|
|
1658
|
+
|
|
1659
|
+
```ts
|
|
1660
|
+
add(...patterns: Pattern2DInput[]): Pattern2D
|
|
1661
|
+
```
|
|
1662
|
+
|
|
1663
|
+
#### `subtract()` — Subtract another pattern or constant height offset from this pattern.
|
|
1664
|
+
|
|
1665
|
+
```ts
|
|
1666
|
+
subtract(pattern: Pattern2DInput): Pattern2D
|
|
1667
|
+
```
|
|
1668
|
+
|
|
1669
|
+
#### `multiply()` — Multiply this pattern by one or more patterns or numeric scale factors.
|
|
1670
|
+
|
|
1671
|
+
```ts
|
|
1672
|
+
multiply(...patterns: Pattern2DInput[]): Pattern2D
|
|
1673
|
+
```
|
|
1674
|
+
|
|
1675
|
+
#### `min()` — Keep the lower height between this pattern and one or more other patterns.
|
|
1676
|
+
|
|
1677
|
+
```ts
|
|
1678
|
+
min(...patterns: Pattern2DInput[]): Pattern2D
|
|
1679
|
+
```
|
|
1680
|
+
|
|
1681
|
+
#### `max()` — Keep the higher height between this pattern and one or more other patterns.
|
|
1682
|
+
|
|
1683
|
+
```ts
|
|
1684
|
+
max(...patterns: Pattern2DInput[]): Pattern2D
|
|
1685
|
+
```
|
|
1686
|
+
|
|
1687
|
+
#### `clamp()` — Limit pattern height to the inclusive `[min, max]` range in millimeters.
|
|
1688
|
+
|
|
1689
|
+
```ts
|
|
1690
|
+
clamp(min: number, max: number): Pattern2D
|
|
1691
|
+
```
|
|
1692
|
+
|
|
1693
|
+
#### `abs()` — Convert negative heights to positive heights.
|
|
1694
|
+
|
|
1695
|
+
```ts
|
|
1696
|
+
abs(): Pattern2D
|
|
1697
|
+
```
|
|
1698
|
+
|
|
1699
|
+
#### `negate()` — Flip the pattern height sign.
|
|
1700
|
+
|
|
1701
|
+
```ts
|
|
1702
|
+
negate(): Pattern2D
|
|
1703
|
+
```
|
|
1704
|
+
|
|
1705
|
+
### `Pattern2DBuilder`
|
|
1706
|
+
|
|
1707
|
+
#### `constant()` — Create a constant-height pattern in millimeters.
|
|
1708
|
+
|
|
1709
|
+
```ts
|
|
1710
|
+
constant(value?: number): Pattern2D
|
|
1711
|
+
```
|
|
1712
|
+
|
|
1713
|
+
#### `sineWave()` — Create a sinusoidal wave pattern in UV space.
|
|
1714
|
+
|
|
1715
|
+
```ts
|
|
1716
|
+
sineWave(options: Pattern2DSineWaveOptions): Pattern2D
|
|
1717
|
+
```
|
|
1718
|
+
|
|
1719
|
+
#### `stripes()` — Create recessed stripe bands in UV space.
|
|
1720
|
+
|
|
1721
|
+
```ts
|
|
1722
|
+
stripes(options: Pattern2DStripesOptions): Pattern2D
|
|
1723
|
+
```
|
|
1724
|
+
|
|
1725
|
+
#### `overUnderWeave()` — Create an over-under woven relief pattern in UV space.
|
|
1726
|
+
|
|
1727
|
+
```ts
|
|
1728
|
+
overUnderWeave(options: Pattern2DOverUnderWeaveOptions): Pattern2D
|
|
1729
|
+
```
|
|
1730
|
+
|
|
1677
1731
|
### `ShapeRef`
|
|
1678
1732
|
|
|
1679
1733
|
A first-class reference path over a shape's semantic faces and face relationships.
|
|
@@ -1796,10 +1850,6 @@ toString(): string
|
|
|
1796
1850
|
|
|
1797
1851
|
### `ANCHOR3D_NAMES`
|
|
1798
1852
|
|
|
1799
|
-
### `DEFAULT_ACTIVE_BACKEND`
|
|
1800
|
-
|
|
1801
|
-
Default geometry backend when no explicit backend is selected.
|
|
1802
|
-
|
|
1803
1853
|
### `verify`
|
|
1804
1854
|
|
|
1805
1855
|
- `that(label: string, check: () => boolean, message?: string): void` — Custom predicate check.
|