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.
Files changed (90) hide show
  1. package/dist/assets/{AdminPage-jwoEgwE_.js → AdminPage-Da6hhpJx.js} +1 -1
  2. package/dist/assets/{BlogPage-Ck7g3ue2.js → BlogPage-Bl_sKeWb.js} +1 -1
  3. package/dist/assets/{DocsPage-9WaRC14b.js → DocsPage-Blz3Tp4j.js} +1 -6
  4. package/dist/assets/EditorApp-CuiPbtn5.js +12754 -0
  5. package/dist/assets/{EmbedViewer-37_PfMwv.js → EmbedViewer-BFG6-Ufm.js} +2 -2
  6. package/dist/assets/{LandingPageProofDriven-CO8WL0CY.js → LandingPageProofDriven-DB9fQd5P.js} +1 -1
  7. package/dist/assets/{PricingPage-DADKGuOa.js → PricingPage-BMxYT_F0.js} +1 -1
  8. package/dist/assets/{SettingsPage-DKKI4W49.js → SettingsPage-VVQNrCAg.js} +1 -1
  9. package/dist/assets/{app-CwI02pTA.js → app-Dl9ymBWC.js} +355 -36
  10. package/dist/assets/cli/{render-Kw5hLEcL.js → render-CFtwKCCY.js} +203 -862
  11. package/dist/assets/{sectionPlaneMath-C8N0w8o3.js → distance-BEC2RjJi.js} +4150 -801
  12. package/dist/assets/{evalWorker-D6ub3kfS.js → evalWorker-CRvbzTXm.js} +2611 -528
  13. package/dist/assets/{manifold-CwDdMKyc.js → manifold-B9QSr-qP.js} +1 -1
  14. package/dist/assets/{manifold-DTvmxSDf.js → manifold-DpBXFS2K.js} +1 -1
  15. package/dist/assets/{manifold-lru0jwVw.js → manifold-DzZ4VRPs.js} +2 -2
  16. package/dist/assets/{renderSceneState-tvtNKNRi.js → renderSceneState-BuAXF2jh.js} +1 -1
  17. package/dist/assets/{reportWorker-DeqktDGt.js → reportWorker-BNWEnRg1.js} +2606 -525
  18. package/dist/cli/render.html +1 -1
  19. package/dist/docs/index.html +2 -2
  20. package/dist/docs-raw/AI/usage.md +0 -1
  21. package/dist/docs-raw/API/core/concepts.md +11 -1
  22. package/dist/docs-raw/CLI.md +64 -13
  23. package/dist/docs-raw/beta-operations.md +4 -0
  24. package/dist/docs-raw/deployment.md +38 -23
  25. package/dist/docs-raw/generated/assembly.md +8 -3
  26. package/dist/docs-raw/generated/concepts.md +126 -46
  27. package/dist/docs-raw/generated/core.md +97 -47
  28. package/dist/docs-raw/generated/curves.md +113 -595
  29. package/dist/docs-raw/generated/lib.md +40 -3
  30. package/dist/docs-raw/generated/output.md +6 -1
  31. package/dist/docs-raw/generated/sdf.md +50 -4
  32. package/dist/docs-raw/generated/sketch.md +9 -1
  33. package/dist/docs-raw/generated/viewport.md +1 -9
  34. package/dist/docs-raw/guides/inspection-bundles.md +40 -9
  35. package/dist/docs-raw/runbook.md +3 -3
  36. package/dist/docs-raw/skills/forgecad-blockout-model.md +1 -0
  37. package/dist/docs-raw/skills/forgecad-image-replicator.md +3 -1
  38. package/dist/docs-raw/skills/forgecad-make-a-model.md +48 -4
  39. package/dist/docs-raw/skills/forgecad-render-inspect.md +3 -1
  40. package/dist/docs-raw/skills/forgecad-visual-spec.md +2 -0
  41. package/dist/docs-raw/skills/forgecad.md +2 -1
  42. package/dist/docs-raw/skills/index.md +0 -1
  43. package/dist/index.html +1 -1
  44. package/dist/sitemap.xml +6 -6
  45. package/dist-cli/blender/render.py +43 -8
  46. package/dist-cli/forgecad.js +5729 -2015
  47. package/dist-cli/forgecad.js.map +1 -1
  48. package/dist-skill/CONTEXT.md +372 -667
  49. package/dist-skill/SKILL-dev.md +2 -1
  50. package/dist-skill/SKILL.md +2 -1
  51. package/dist-skill/docs/API/core/concepts.md +11 -1
  52. package/dist-skill/docs/CLI.md +64 -13
  53. package/dist-skill/docs/generated/assembly.md +8 -3
  54. package/dist-skill/docs/generated/core.md +97 -47
  55. package/dist-skill/docs/generated/curves.md +113 -595
  56. package/dist-skill/docs/generated/lib.md +40 -3
  57. package/dist-skill/docs/generated/output.md +6 -1
  58. package/dist-skill/docs/generated/sdf.md +50 -4
  59. package/dist-skill/docs/generated/sketch.md +9 -1
  60. package/dist-skill/docs/generated/viewport.md +1 -9
  61. package/dist-skill/docs/guides/inspection-bundles.md +40 -9
  62. package/dist-skill/docs-dev/API/core/concepts.md +11 -1
  63. package/dist-skill/docs-dev/CLI.md +64 -13
  64. package/dist-skill/docs-dev/generated/assembly.md +8 -3
  65. package/dist-skill/docs-dev/generated/core.md +97 -47
  66. package/dist-skill/docs-dev/generated/curves.md +113 -595
  67. package/dist-skill/docs-dev/generated/lib.md +40 -3
  68. package/dist-skill/docs-dev/generated/output.md +6 -1
  69. package/dist-skill/docs-dev/generated/sdf.md +50 -4
  70. package/dist-skill/docs-dev/generated/sketch.md +9 -1
  71. package/dist-skill/docs-dev/generated/viewport.md +1 -9
  72. package/dist-skill/docs-dev/guides/inspection-bundles.md +40 -9
  73. package/dist-skill/library/README.md +0 -1
  74. package/dist-skill/library/forgecad-blockout-model/SKILL.md +1 -0
  75. package/dist-skill/library/forgecad-image-replicator/SKILL.md +3 -1
  76. package/dist-skill/library/forgecad-make-a-model/SKILL.md +48 -4
  77. package/dist-skill/library/forgecad-render-inspect/SKILL.md +3 -1
  78. package/dist-skill/library/forgecad-visual-spec/SKILL.md +2 -0
  79. package/examples/api/drive-wheel-regions.forge.js +43 -0
  80. package/examples/api/guided-loft-olive-oil-bottle.forge.js +135 -0
  81. package/examples/api/sdf-circular-array-knurling.forge.js +19 -0
  82. package/examples/api/sdf-pattern2d-ceramic-ripple-set.forge.js +83 -0
  83. package/examples/api/sdf-pattern2d-grip-tread.forge.js +72 -0
  84. package/examples/api/sdf-pattern2d-orbital-jewelry.forge.js +62 -0
  85. package/examples/api/sdf-surface-basket-weave.forge.js +67 -0
  86. package/examples/api/sector-gear-body.forge.js +34 -0
  87. package/package.json +20 -2
  88. package/dist/assets/EditorApp-Dja2jMmW.js +0 -12509
  89. package/dist/docs-raw/skills/forgecad-api-dogfood.md +0 -130
  90. package/dist-skill/library/forgecad-api-dogfood/SKILL.md +0 -125
@@ -10,6 +10,7 @@ Pre-built fasteners, gears, pipes, structural profiles, and utility shapes. Acce
10
10
  ## Contents
11
11
 
12
12
  - [TangentLoop2D](#tangentloop2d)
13
+ - [DriveWheelBuilder](#drivewheelbuilder)
13
14
  - [lib](#lib)
14
15
 
15
16
  ---
@@ -50,6 +51,32 @@ toProfile(): Sketch
50
51
  offsetBand(thickness: number): Sketch
51
52
  ```
52
53
 
54
+ ### `DriveWheelBuilder`
55
+
56
+ #### `addSpurTeethBetween()` — Add an involute spur-tooth window on part of the pitch circle.
57
+
58
+ ```ts
59
+ addSpurTeethBetween(options: DriveWheelSpurTeethRegionOptions): this
60
+ ```
61
+
62
+ #### `addSolidArcBetween()` — Add a constant-radius solid arc region such as a dwell, stop, or pusher.
63
+
64
+ ```ts
65
+ addSolidArcBetween(options: DriveWheelSolidArcRegionOptions): this
66
+ ```
67
+
68
+ #### `addShapeRegion()` — Add a fully custom region shape while preserving region metadata.
69
+
70
+ ```ts
71
+ addShapeRegion(name: string, shape: Shape, options?: DriveWheelShapeRegionOptions): this
72
+ ```
73
+
74
+ #### `build()` — Build the final wheel shape with a bore connector and region metadata.
75
+
76
+ ```ts
77
+ build(): Shape
78
+ ```
79
+
53
80
  ---
54
81
 
55
82
  ## Constants
@@ -58,7 +85,7 @@ offsetBand(thickness: number): Sketch
58
85
 
59
86
  Pre-built parametric parts available in user scripts as `lib.*`.
60
87
 
61
- Every key in this object becomes a method on the `lib` namespace exposed to `.forge.js` scripts. The catalog includes:
88
+ Every key in this object becomes a method or namespace on the `lib` object exposed to `.forge.js` scripts. The catalog includes:
62
89
 
63
90
  **Fasteners:** `bolt`, `nut`, `washer`, `fastenerSet`, `fastenerHole`, `boltHole`, `counterbore`, `hexNut`, `holePattern`
64
91
 
@@ -68,7 +95,9 @@ Every key in this object becomes a method on the `lib` namespace exposed to `.fo
68
95
 
69
96
  **Threads:** `thread`
70
97
 
71
- **Gears:** `spurGear`, `bevelGear`, `faceGear`, `sideGear`, `ringGear`, `rackGear`, `gearPair`, `bevelGearPair`, `faceGearPair`, `sideGearPair`
98
+ **Gears:** `spurGear`, `sectorGear`, `driveWheel`, `bevelGear`, `faceGear`, `sideGear`, `ringGear`, `rackGear`, `gearPair`, `bevelGearPair`, `faceGearPair`, `sideGearPair`
99
+
100
+ **Gear bodies:** `gearBodies.disk`, `gearBodies.diskWithHub`, `gearBodies.spoked`, `gearBodies.fromProfile` plus direct aliases `gearBodyDisk`, `gearBodyDiskWithHub`, `gearBodySpoked`, `gearBodyFromProfile`
72
101
 
73
102
  **Gear ratios (pure math helpers):** `gearRatio`, `rackRatio`, `planetaryRatio`
74
103
 
@@ -76,7 +105,7 @@ Every key in this object becomes a method on the `lib` namespace exposed to `.fo
76
105
 
77
106
  **Utilities:** `explode`
78
107
 
79
- Extend this by adding new entries here and registering the corresponding runner binding in `runner.ts`. Sizes outside the supported ranges will throw at runtime with a descriptive error.
108
+ Sizes outside the supported ranges will throw at runtime with a descriptive error.
80
109
 
81
110
  - `boltHole(diameter: number, depth: number): Shape` — Simple cylindrical through-hole cutter centered on Z=0. Subtract the result from a part to produce a plain cylindrical clearance hole. For ISO metric sizes with fit classes and counterbore/countersink, use {
82
111
  - `fastenerHole(opts: FastenerHoleOptions): Shape` — ISO metric fastener hole cutter with optional counterbore or countersink. **Details** Returns a cutter shape (subtract from a solid to produce the hole). Sizes outside M2–M10 will throw. Extend `METRIC_HOLE_TABLE` in this file to add new sizes. **Example** ```ts const plate = box(60, 40, 8) .subtract(lib.fastenerHole({ size: 'M5', fit: 'normal', depth: 8 }) .translate(15, 10, 4)); ```
@@ -114,3 +143,11 @@ Extend this by adding new entries here and registering the corresponding runner
114
143
  - `rackRatio(module: number, pinionTeeth: number): number` — Coupling ratio between a pinion and a rack. When the pinion rotates by `θ` degrees, the rack slides by `θ × (π × module × teeth / 360)` mm. Equivalently, 1mm of rack travel = `180 / (π × pitchRadius)` degrees of pinion rotation.
115
144
  - `planetaryRatio(sunTeeth: number, ringTeeth: number): number` — Planetary gear reduction ratio when the ring is held fixed. Input: sun. Output: carrier. Ratio: `1 + ringTeeth / sunTeeth`. One turn of the sun produces `1 / ratio` turns of the carrier.
116
145
  - `boltPattern(options: BoltPatternOptions): BoltPattern` — Define a bolt pattern once and cut it from multiple parts. const base = bolts.cut(box(60, 50, 10), 12, { from: -1 }); const cover = bolts.cut(box(60, 50, 3), 5, { from: -1 }); // Same positions in both parts — guaranteed aligned. ```
146
+ - `driveWheel(options?: DriveWheelOptions): DriveWheelBuilder` — Start a composable exceptional gear or drive wheel.
147
+ - `readDriveWheelMeta(shape: Shape): DriveWheelMeta | null` — Read the functional-region metadata attached by `driveWheel().build()`.
148
+ - `sectorGear(options: SectorGearOptions): Shape` — Involute sector gear with teeth on only part of the pitch circle. Specify the full-circle pitch as `teethOnFullCircle`, then choose the active tooth window with `firstTooth` and `toothCount`. The body is separate from the tooth region: pass a `gearBody...` shape for spokes, hubs, and product styling, or omit it for a simple root-radius disk. **Example** ```ts const body = lib.gearBodies.spoked({ outerRadius: 22, rimWidth: 3, hubDiameter: 10, spokeCount: 5, spokeWidth: 2.5, faceWidth: 8, boreDiameter: 5, }); const sector = lib.sectorGear({ module: 1.25, teethOnFullCircle: 36, toothCount: 10, faceWidth: 8, body, }); ```
149
+ - `gearBodies: { ... }` — Gear body preset namespace: disk, diskWithHub, spoked, and fromProfile.
150
+ - `gearBodyDisk(options: GearBodyDiskOptions): Shape` — Solid disk/ring gear body, independent from any tooth geometry.
151
+ - `gearBodyDiskWithHub(options: GearBodyDiskWithHubOptions): Shape` — Disk gear body with a raised center hub.
152
+ - `gearBodySpoked(options: GearBodySpokedOptions): Shape` — Spoked gear body with an outer rim, center hub, and radial spokes.
153
+ - `gearBodyFromProfile(profile: Sketch, options: GearBodyFromProfileOptions): Shape` — Extrude a custom 2D profile into a gear body.
@@ -144,7 +144,12 @@ robotExport(options: RobotExportOptions): CollectedRobotExport
144
144
 
145
145
  `AssemblyPartDef`: `{ name: string, part: AssemblyPart, base: Transform, metadata?: PartMetadata }`
146
146
 
147
- **`PartMetadata`**: `material?: string`, `process?: string`, `tolerance?: string`, `qty?: number`, `notes?: string`, `densityKgM3?: number`, `massKg?: number`
147
+ **`PartMetadata`**
148
+
149
+ | Option | Type | Description |
150
+ |--------|------|-------------|
151
+ | `tags?` | `string \| readonly string[]` | Viewport organization tags applied to scene objects produced from this part. |
152
+ | `material?`, `process?`, `tolerance?`, `qty?`, `notes?`, `densityKgM3?`, `massKg?` | | — |
148
153
 
149
154
  **`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`
150
155
 
@@ -191,6 +191,36 @@ intersect(...others: SdfShape[]): SdfShape
191
191
  clipBox(x: number, y: number, z: number): SdfShape
192
192
  ```
193
193
 
194
+ #### `fillWith()` — Keep only the material where this shape overlaps another SDF pattern.
195
+
196
+ ```ts
197
+ fillWith(pattern: SdfShape): SdfShape
198
+ ```
199
+
200
+ #### `fillWithGyroid()` — Keep only the gyroid lattice inside this shape.
201
+
202
+ ```ts
203
+ fillWithGyroid(options: TpmsOptions): SdfShape
204
+ ```
205
+
206
+ #### `fillWithSchwarzP()` — Keep only the Schwarz-P lattice inside this shape.
207
+
208
+ ```ts
209
+ fillWithSchwarzP(options: TpmsOptions): SdfShape
210
+ ```
211
+
212
+ #### `fillWithDiamond()` — Keep only the diamond TPMS lattice inside this shape.
213
+
214
+ ```ts
215
+ fillWithDiamond(options: TpmsOptions): SdfShape
216
+ ```
217
+
218
+ #### `fillWithLidinoid()` — Keep only the lidinoid TPMS lattice inside this shape.
219
+
220
+ ```ts
221
+ fillWithLidinoid(options: TpmsOptions): SdfShape
222
+ ```
223
+
194
224
  #### `smoothUnion()` — Smooth union — blends shapes together with a smooth radius.
195
225
 
196
226
  ```ts
@@ -269,6 +299,14 @@ bend(radius: number): SdfShape
269
299
  repeat(spacing: Vec3, count?: Vec3): SdfShape
270
300
  ```
271
301
 
302
+ #### `circularArray()` — Arrange this SDF in a circular array around the Z axis.
303
+
304
+ The source shape is translated by `offset` in +X before arraying. This uses angular domain folding, so evaluation stays O(1): the source SDF is sampled twice no matter how many copies are requested.
305
+
306
+ ```ts
307
+ circularArray(count: number, offset?: number): SdfShape
308
+ ```
309
+
272
310
  #### `shell()` — Hollow out, keeping only a shell of given thickness.
273
311
 
274
312
  ```ts
@@ -281,8 +319,8 @@ shell(thickness: number): SdfShape
281
319
  // Function displacement
282
320
  shape.displace((x, y, z) => Math.sin(x) * 0.5)
283
321
 
284
- // Pattern displacement (e.g. basketWeave)
285
- shape.displace(sdf.basketWeave({ threads: 16, spacing: 3 }))
322
+ // Pattern displacement from a 3D SDF field
323
+ shape.displace(sdf.knurl({ pitch: 2, depth: 0.3 }))
286
324
  ```
287
325
 
288
326
  ```ts
@@ -295,10 +333,16 @@ Automatically detects the shape's UV parametrization (sphere, cylinder, torus) f
295
333
 
296
334
  UV coordinates are in **surface millimeters** — patterns defined with `spacing: 3` always produce 3mm spacing, regardless of shape size.
297
335
 
336
+ Prefer `sdf.pattern2d()` or built-in surface patterns when the relief should stay on the native shader and meshing path. Callback functions are supported for experimentation, but they are opaque to the typed pattern optimizer.
337
+
298
338
  ```js
299
- // Surface-following basket weave — auto-detects sphere UV
339
+ // Native typed pattern — auto-detects sphere UV
340
+ const p = sdf.pattern2d()
341
+ const ribs = p.stripes({ spacing: 3, width: 0.8, depth: 0.35 })
342
+ .add(p.sineWave({ direction: [0, 1], wavelength: 14, amplitude: 0.08 }))
343
+
300
344
  sdf.sphere(27).shell(3)
301
- .surfaceDisplace(sdf.basketWeave({ spacing: 3, depth: 0.8 }))
345
+ .surfaceDisplace(ribs)
302
346
  .toShape()
303
347
 
304
348
  // Custom 2D pattern via function
@@ -370,9 +414,11 @@ return {
370
414
  - `brick(options?: BrickOptions): SdfShape` — Brick/stone wall pattern — running bond with mortar grooves.
371
415
  - `weave(options?: WeaveOptions): SdfShape` — Grid lattice pattern — two families of infinite slabs crossing at 90°.
372
416
  - `basketWeave(options?: BasketWeaveOptions): SurfacePattern` — Basket weave surface pattern — threads with over-under crossings in UV space. Returns a SurfacePattern for use with `.surfaceDisplace()`.
417
+ - `pattern2d(): Pattern2DBuilder` — Create typed, composable 2D surface patterns for `.surfaceDisplace()`.
373
418
  - `twist(shape: SdfShape, degreesPerUnit: number): SdfShape` — Twist an SDF shape around the Z axis.
374
419
  - `bend(shape: SdfShape, radius: number): SdfShape` — Bend an SDF shape around the Z axis.
375
420
  - `repeat(shape: SdfShape, spacing: Vec3, count?: Vec3): SdfShape` — Repeat an SDF shape in space.
421
+ - `circularArray(shape: SdfShape, count: number, offset?: number): SdfShape` — Arrange an SDF shape in a circular array around the Z axis with O(1) folded-domain evaluation.
376
422
  - `SurfacePattern: typeof SurfacePattern` — A 2D surface pattern — a heightmap function for use with `.surfaceDisplace()`.
377
423
  - `fromFunction(fn: SdfFunctionSource, options: SdfFunctionOptions): SdfShape` — Create a custom SDF from one expression; shader-safe expressions raymarch directly.
378
424
  - `Sculpt: { sphere: (radius: number) => SdfShape; box: (x: number, y: number, z: number, options?: SculptBoxOptions) => SdfShape; cylinder: (height: number, radius: number) => SdfShape; disk: (radius: number, thickness?: number) => SdfShape; circle: (radius: number, thickness?: number) => SdfShape; capsule: (height: number, radius: number) => SdfShape; torus: (majorRadius: number, minorRadius: number) => SdfShape; cone: (height: number, radius: number) => SdfShape; tube: (points: SculptPointList, options?: SculptTubeOptions) => SdfShape; curve: (points: SculptPointList, options?: SculptTubeOptions) => SdfShape; path: (points: SculptPointList, options?: SculptTubeOptions) => SdfShape; blend: (first?: SculptBlendInput | SculptBlendOptions, optionsOrShape?: SculptBlendInput | SculptBlendOptions, ...rest: (SculptBlendInput | SculptBlendOptions)[]) => SdfShape; union: (first?: SculptBlendInput, ...rest: SculptBlendInput[]) => SdfShape; carve: (base: SdfShape, cutters: SculptBlendInput, options?: SculptBlendOptions) => SdfShape; keep: (first?: SculptBlendInput | SculptBlendOptions, optionsOrShape?: SculptBlendInput | SculptBlendOptions, ...rest: (SculptBlendInput | SculptBlendOptions)[]) => SdfShape; polish: (shape: SdfShape, input?: SculptPolishInput) => SdfShape; material: (input?: SculptPolishInput) => ShapeMaterialProps & { color?: string; }; look: (preset?: SculptLookPreset) => SceneOptions; knownMaterials: typeof knownSculptMaterialPresets; }` — Sculpt-like facade: friendly liquid-modeling verbs backed by the same SDF kernel.
@@ -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(seed: [ number, number ]): Sketch
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.
@@ -59,7 +59,7 @@ When `lights` is specified, **all** default lights are removed. You must include
59
59
 
60
60
  Setting `camera.position` overrides auto-framing — the viewport will no longer auto-fit the geometry on script reload.
61
61
 
62
- 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 model.forge.js --view hero`.
62
+ 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`.
63
63
 
64
64
  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.
65
65
 
@@ -128,7 +128,6 @@ scene(options: SceneOptions): void
128
128
  | `behavior?` | `"opt-in" \| "auto"` | Whether the viewer should offer or auto-open the journey. First slice supports opt-in. |
129
129
  | `steps` | `SceneJourneyStepConfig[]` | Ordered journey spine. Branches can be added later without changing this core contract. |
130
130
  | `valid?` | `boolean` | True unless any journey or step diagnostic has level "error". |
131
- | `diagnostics?` | `SceneJourneyDiagnostic[]` | Whole-journey diagnostics, including unresolved startsAt and step target diagnostics. |
132
131
 
133
132
  **`SceneJourneyStepConfig`**
134
133
 
@@ -141,9 +140,6 @@ scene(options: SceneOptions): void
141
140
  | `camera?` | `SceneViewCameraConfig` | Optional explicit camera for this step. When omitted, the viewer fits `focus`. |
142
141
  | `resolvedFocusId?` | `string \| null` | Resolved object id after script execution, when `focus` matched exactly one object. |
143
142
  | `resolvedFocusPath?` | `string \| null` | Resolved object tree path or name after script execution. |
144
- | `diagnostics?` | `SceneJourneyDiagnostic[]` | Resolution diagnostics for this step. |
145
-
146
- `SceneJourneyDiagnostic`: `{ level: SceneJourneyDiagnosticLevel, message: string, stepId?: string, suggestions?: string[] }`
147
143
 
148
144
  **`SceneLightConfig`**
149
145
 
@@ -219,10 +215,6 @@ viewConfig({
219
215
  viewConfig(options?: ViewConfigOptions): void
220
216
  ```
221
217
 
222
- `ViewConfigOptions`: `{ jointOverlay?: JointOverlayViewConfigOptions }`
223
-
224
- **`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`
225
-
226
218
  #### `explodeView()` — Configure how the viewport explode slider offsets returned objects.
227
219
 
228
220
  Offsets are resolved from the returned object tree, not a flat list. In `radial` mode each node follows its parent branch direction, then fans locally from the immediate parent center — nested assemblies peel apart level by level. In fixed-axis or fixed-vector modes, the branch follows that axis/vector but nested descendants fan out perpendicular by default.
@@ -7,8 +7,9 @@ skill-order: 2
7
7
 
8
8
  `forgecad render inspect` writes a deterministic directory bundle for agents,
9
9
  tests, and automation. Use it when a single shaded PNG is too ambiguous and the
10
- consumer needs geometry-aware signals such as depth, normals, part identity,
11
- physical connected components, collisions, local thickness, or cross-sections.
10
+ consumer needs geometry-aware signals such as depth, normals, surface roughness,
11
+ part identity, physical connected components, collisions, local thickness, or
12
+ cross-sections.
12
13
 
13
14
  ## When To Use It
14
15
 
@@ -28,14 +29,16 @@ forgecad render inspect model.forge.js --channels rgb,mask,section
28
29
  forgecad render inspect model.forge.js --channels collisions --focus Bench
29
30
  forgecad render inspect model.forge.js --channels rgb,mask --hide "Bench.Slat0,Bench.Slat1"
30
31
  forgecad render inspect model.forge.js --channels thickness --min-thickness 1.2 --warn-thickness 2.0
32
+ forgecad render inspect channels
31
33
  ```
32
34
 
33
35
  The default output directory is `<script-name>-inspect/` next to the input file.
34
36
  Pass `--force` to replace an existing bundle directory.
35
37
 
36
38
  There are no default channels. Pass `--channels` every time as a
37
- comma-separated subset. Keep bundles targeted to the current question so heavy
38
- analyses do not run unnecessarily.
39
+ comma-separated subset. Run `forgecad render inspect channels` to list the
40
+ supported channels in the installed CLI. Keep bundles targeted to the current
41
+ question so heavy analyses do not run unnecessarily.
39
42
 
40
43
  `--focus` and `--hide` use the same object-name filtering semantics as
41
44
  `forgecad run` and `forgecad render 3d`. A bare `--focus` hides mock objects;
@@ -91,12 +94,13 @@ implemented channel in one bundle:
91
94
 
92
95
  ```bash
93
96
  forgecad render inspect model.forge.js --channels depth,normals
97
+ forgecad render inspect model.forge.js --channels rgb,roughness
94
98
  forgecad render inspect model.forge.js --channels rgb,mask,collisions
95
99
  forgecad render inspect model.forge.js --channels rgb,section,thickness
96
100
  ```
97
101
 
98
- Supported channels are `rgb`, `depth`, `normals`, `mask`, `connectivity`,
99
- `distance`, `collisions`, `thickness`, and `section`.
102
+ Supported channels are `rgb`, `depth`, `normals`, `roughness`, `mask`,
103
+ `connectivity`, `distance`, `collisions`, `thickness`, and `section`.
100
104
 
101
105
  ## Channel Semantics
102
106
 
@@ -123,6 +127,27 @@ normal = normalize((rgb / 255) * 2 - 1)
123
127
 
124
128
  Background pixels are black and should be treated as `null`.
125
129
 
130
+ `roughness` emits a mesh-dihedral surface-quality heatmap. Smooth and gently
131
+ curved triangles render as a faint translucent shadow over black, while
132
+ triangles adjacent to sharp, harsh, boundary, or non-manifold mesh edges render
133
+ in orange or magenta:
134
+
135
+ ```text
136
+ shadow = max adjacent angle < sharpAngleDeg
137
+ orange = sharpAngleDeg <= angle < harshAngleDeg
138
+ magenta = angle >= harshAngleDeg, boundary, or non-manifold
139
+ ```
140
+
141
+ The default thresholds are `smoothAngleDeg=5`, `sharpAngleDeg=30`, and
142
+ `harshAngleDeg=90`. The manifest stores the method, thresholds, palette, object
143
+ list, per-object triangle and edge counts, area percentages by smooth,
144
+ moderate, sharp, and harsh classes, angle percentiles, maximum angle, quality
145
+ score, and warnings. Moderate angles are reported in the manifest but stay in
146
+ the shadow layer by default so intentionally curved surfaces do not light up as
147
+ defects. Use this channel to spot spiky tessellation, accidental faceting,
148
+ jagged boolean residue, and dense sharp-corner regions without losing the
149
+ silhouette of otherwise smooth surfaces.
150
+
126
151
  `mask` emits one object-color image per view. Black is background. Non-black
127
152
  pixels resolve through `manifest.channels.mask.objects`, which includes object
128
153
  index, RGB color, object id, name, group, tree path, and mock flag. Edge pixels
@@ -165,9 +190,15 @@ root = largest component by body count, object count, then bbox volume
165
190
  rootDistance = shortest accumulated gap distance from root component
166
191
  ```
167
192
 
193
+ For large scenes the manifest does not materialize the complete component gap
194
+ graph, because that graph is quadratic in the number of components. The
195
+ `gapEdgeCount` field reports the logical complete-graph edge count used by the
196
+ analysis. `gapEdges` stores a compact evidence subset containing nearest-gap
197
+ and root-parent edges.
198
+
168
199
  The PNG colors components from green at the root/near distances through yellow to
169
200
  red at the farthest rooted component. The manifest stores the root component,
170
- maximum rooted distance, complete component gap edge list, nearest-gap data, and
201
+ maximum rooted distance, compact gap edge evidence, nearest-gap data, and
171
202
  shortest-path parent fields. The current v1 metric is bbox-based: it measures air
172
203
  gaps between component bounding boxes, not exact closest mesh-surface distance.
173
204
 
@@ -184,8 +215,8 @@ collision = boolean intersection volume > 0.1mm^3
184
215
  ```
185
216
 
186
217
  The manifest stores the inspected objects, collision pair names/ids, overlap
187
- volume, warnings, render style, and each collision finding's `groupIndex`,
188
- `color`, and `hex`. Exact interior pixels can be matched against
218
+ volume, broadphase counters, warnings, render style, and each collision finding's
219
+ `groupIndex`, `color`, and `hex`. Exact interior pixels can be matched against
189
220
  `manifest.channels.collisions.collisions[].color`; antialiased edges may blend
190
221
  with the ghosted source geometry. If `--focus PartA,PartB` is used, everything
191
222
  except those objects is hidden, `PartA` and `PartB` are ghosted, and their
@@ -34,7 +34,7 @@ Top-level declarations such as `const bom = ...`, `let scene = ...`, or `class S
34
34
 
35
35
  - Scripts re-execute on every parameter change (400ms debounce)
36
36
  - Geometry operations are **immutable** — shapes, sketches, groups, imported assemblies, and wood boards return new values instead of modifying in place
37
- - Must return one of: `Shape`, `Sketch`, `ShapeGroup`, `Assembly`, `SolvedAssembly`, `SdfShape`, `Array` of renderables, `Array` of `{ name, shape?, sketch?, group?, color? }`, or a **metadata object** (see below)
37
+ - Must return one of: `Shape`, `Sketch`, `ShapeGroup`, `Assembly`, `SolvedAssembly`, `SdfShape`, `Array` of renderables, `Array` of `{ name, tags?, shape?, sketch?, group?, color? }`, or a **metadata object** (see below)
38
38
 
39
39
  Top-level assembly scripts can return an unsolved `Assembly` directly; ForgeCAD solves it at default joint values for display. Return `assembly.solve(state)` when you want a specific pose. Do not call `.toGroup()` just to make an assembly render — use `.toGroup()` only when you specifically need `ShapeGroup` composition, group-style transforms, or named-child lookup.
40
40
 
@@ -67,6 +67,16 @@ return {
67
67
  };
68
68
  ```
69
69
 
70
+ Named return objects and named `group(...)` children can include `tags`. Tags are viewport metadata: they do not affect geometry, exports, face labels, or BOM rows, but the command palette can hide, show only, or focus every object with a selected tag.
71
+
72
+ ```javascript
73
+ return [
74
+ { name: 'Base Plate', tags: ['printed', 'structural'], shape: base },
75
+ { name: 'M4 Bolt A', tags: 'fastener', shape: boltA },
76
+ { name: 'M4 Bolt B', tags: 'fastener', shape: boltB },
77
+ ];
78
+ ```
79
+
70
80
  ## Coordinate System
71
81
 
72
82
  Z-up right-handed: X = left/right, Y = forward/back, Z = up/down.
@@ -113,12 +113,14 @@ forgecad run examples/constraints/06-complex-spectrogram.forge.js --solver-debug
113
113
 
114
114
  ### `forgecad render`
115
115
 
116
- Render a Forge scene. Use a subcommand — `3d`, `inspect`, `section`, `wireframe`, `sketch`, or `hq`.
116
+ Render a Forge scene. Use a subcommand — `3d`, `inspect`, `views`, `section`, `wireframe`, `sketch`, or `hq`.
117
117
 
118
118
  `forgecad render` is a group of rendering subcommands. Pick one based on what you want:
119
119
 
120
120
  - `render 3d` — standard viewport PNG, the usual way to visually verify geometry
121
121
  - `render inspect` — machine-readable inspection bundle with geometry channels and a manifest
122
+ - `render inspect channels` — list supported inspection channels
123
+ - `render views` — list named cameras declared with `scene({ views })`
122
124
  - `render wireframe` — edges only, no shading
123
125
  - `render section` — 2D cross-section cut by a plane (SVG or PNG)
124
126
  - `render sketch` — 2D sketch script to PNG
@@ -127,6 +129,8 @@ Render a Forge scene. Use a subcommand — `3d`, `inspect`, `section`, `wirefram
127
129
  ```bash
128
130
  forgecad render 3d examples/cup.forge.js
129
131
  forgecad render inspect examples/api/static-assembly-connectors.forge.js --channels rgb,mask
132
+ forgecad render inspect channels
133
+ forgecad render views examples/cup.forge.js
130
134
  forgecad render wireframe examples/cup.forge.js
131
135
  forgecad render section examples/furniture/01-table.forge.js --plane XZ
132
136
  forgecad render hq examples/cup.forge.js --preset dramatic
@@ -138,7 +142,7 @@ Render a Forge scene to PNG using the real viewport renderer.
138
142
 
139
143
  Launches a headless Chrome instance, renders the scene with the same WebGL viewport as the editor, and saves a PNG. The output path defaults to `<script-name>.png` next to the input file.
140
144
 
141
- Use `--focus` to isolate specific parts (hides everything else) or `--hide` to remove clutter like mock objects. The `--view` flag selects a named camera declared in `scene({ views })`. The `--camera` flag accepts built-in views (`front`, `top`, `iso`), `azimuth:elevation` angles, or an exact `proj/pos/target/up/fov` camera spec — pass `--camera` multiple times to render several viewpoints in one run.
145
+ Use `--focus` to isolate specific parts (hides everything else) or `--hide` to remove clutter like mock objects. The `--view` flag selects a named camera declared in `scene({ views })`. The `--camera` flag accepts built-in views (`front`, `top`, `iso`), `azimuth:elevation` angles, or an exact `proj/pos/target/up/fov` camera spec — pass `--camera` multiple times to render several viewpoints in one run. Use `--camera-json <file>` or `--scene <file>` for exact reproducible viewport cameras without shell escaping.
142
146
 
143
147
  Use `--edges=<off|thin|bold>` to control the edge overlay. For a pure wireframe look, use `render wireframe` instead.
144
148
 
@@ -152,16 +156,29 @@ forgecad render 3d examples/cup.forge.js --hide "wall,bolt"
152
156
  forgecad render 3d model.forge.js --view hero
153
157
  forgecad render 3d model.forge.js --camera 45:30
154
158
  forgecad render 3d model.forge.js --camera "proj=perspective;pos=200,-160,120;target=0,0,20;up=0,0,1;fov=38"
159
+ forgecad render 3d model.forge.js --camera-json camera.json
160
+ forgecad render 3d model.forge.js --scene scene.json
155
161
  forgecad render 3d model.forge.js --camera front --camera side
156
162
  forgecad render 3d model.forge.js --edges bold
157
163
  forgecad render 3d model.forge.js --edges off
158
164
  ```
159
165
 
166
+ ### `forgecad render views`
167
+
168
+ List named camera views declared by a model with `scene({ views })`.
169
+
170
+ Runs the script headlessly and prints every named render view with an exact camera spec that can be passed back to `render 3d`, `render hq`, or `capture`.
171
+
172
+ ```bash
173
+ forgecad render views model.forge.js
174
+ forgecad render views model.forge.js --json
175
+ ```
176
+
160
177
  ### `forgecad render inspect`
161
178
 
162
179
  Render a machine-readable inspection bundle with geometry channels and a manifest.
163
180
 
164
- Launches the headless viewport renderer and writes a directory bundle for agent and automation workflows. Every channel is opt-in with `--channels`; there is no default bundle. Selected channels emit canonical `front`, `right`, `top`, and `iso` views for RGB, depth, normals, object masks, physical connectivity, rooted component distance, collisions, and wall thickness, or a principal-plane section atlas, plus a root `manifest.json` with scene metadata, filters, object visibility, and relative file paths.
181
+ Launches the headless viewport renderer and writes a directory bundle for agent and automation workflows. Every channel is opt-in with `--channels`; there is no default bundle. Selected channels emit canonical `front`, `right`, `top`, and `iso` views for RGB, depth, normals, surface roughness, object masks, physical connectivity, rooted component distance, collisions, and wall thickness, or a principal-plane section atlas, plus a root `manifest.json` with scene metadata, filters, object visibility, and relative file paths.
165
182
 
166
183
  Use `--focus` to isolate specific parts or hide mocks, and `--hide` to remove named clutter. Output defaults to `<script-name>-inspect/` next to the input file.
167
184
 
@@ -173,6 +190,15 @@ forgecad render inspect examples/api/static-assembly-connectors.forge.js out/ben
173
190
  forgecad render inspect examples/api/static-assembly-connectors.forge.js --channels rgb,mask,collisions --hide "Bench.Slat0" --force
174
191
  ```
175
192
 
193
+ ### `forgecad render inspect channels`
194
+
195
+ List supported inspection bundle channels.
196
+
197
+ ```bash
198
+ forgecad render inspect channels
199
+ forgecad render inspect channels --json
200
+ ```
201
+
176
202
  ### `forgecad render wireframe`
177
203
 
178
204
  Render a Forge scene as a wireframe (edges only, no shading).
@@ -190,13 +216,15 @@ High-quality render via Blender Cycles — path-traced, HDRI, material presets.
190
216
 
191
217
  Exports the scene to Blender and renders with Cycles (path tracer). Requires Blender installed and on PATH.
192
218
 
193
- Choose a `--preset` for the look: `studio` (neutral product shot), `dramatic` (high-contrast), `clay` (matte, no color), `glass`, `metallic`, `toon`, `xray`, `normals`, `silhouette`, and more. Control quality vs speed with `--samples` (default 256). Use `--transparent` for a transparent background (compositing-ready).
219
+ Choose a `--preset` for the look: `studio` (neutral product shot), `dramatic` (high-contrast), `clay` (matte, no color), `glass`, `metallic`, `toon`, `xray`, `normals`, `silhouette`, and more. Control quality vs speed with `--samples` (default 256). Use `--view`, `--camera`, `--camera-json`, or `--scene <file>` for still camera control, matching `render 3d`. Use `--transparent` for a transparent background (compositing-ready).
194
220
 
195
221
  Output defaults to `<script-name>-hq.png`. Great for documentation, marketing renders, and social media.
196
222
 
197
223
  ```bash
198
224
  forgecad render hq examples/cup.forge.js
199
225
  forgecad render hq examples/cup.forge.js hero.png --preset dramatic --samples 1024
226
+ forgecad render hq examples/cup.forge.js hero.png --view hero
227
+ forgecad render hq examples/cup.forge.js hero.png --camera-json camera.json
200
228
  forgecad render hq examples/cup.forge.js --preset clay --size 2048
201
229
  forgecad render hq examples/cup.forge.js --transparent --preset glass
202
230
  ```
@@ -205,16 +233,20 @@ forgecad render hq examples/cup.forge.js --transparent --preset glass
205
233
 
206
234
  Animated orbit or joint playback.
207
235
 
208
- Renders an animated sequence by either orbiting the camera around the model or playing back a `jointsView` animation. Use `--capture orbit` (default) for a turntable rotation, `--capture animation --animation <name>` to play a named joints clip, or `--capture section-sweep` to move a clipping plane through the model. Supports `--cut-plane` to animate with a static cross-section visible.
236
+ Renders an animated sequence by either orbiting the camera around the model or playing back a `jointsView` animation. Use `--capture orbit` (default) for a turntable rotation, `--capture animation --animation <name>` to play a named joints clip, or `--capture section-sweep` to move a clipping plane through the model. Supports `--cut-plane` to animate with a static cross-section visible. Use `--view`, `--camera`, `--camera-json`, or `--scene <file>` to choose the orbit base camera or the fixed camera for animations and section sweeps.
209
237
 
210
238
  ```bash
211
239
  forgecad capture gif examples/cup.forge.js
212
240
  forgecad capture gif examples/3d-printer.forge.js out/section.gif --cut-plane "Front Section"
213
241
  forgecad capture gif model.forge.js out/raw.gif --param "Output=raw-sdf"
242
+ forgecad capture gif model.forge.js out/front.gif --camera front
243
+ forgecad capture gif model.forge.js out/hero.gif --view hero
214
244
  forgecad capture gif examples/3d-printer.forge.js out/sweep.gif --capture section-sweep --sweep-plane YZ
215
245
  forgecad capture mp4 examples/cup.forge.js
216
246
  forgecad capture mp4 examples/api/runtime-joints-view.forge.js out/step.mp4 --capture animation --animation Step
217
247
  forgecad capture mp4 model.forge.js out/raw.mp4 --param "Output=raw-sdf"
248
+ forgecad capture mp4 model.forge.js out/front.mp4 --camera front
249
+ forgecad capture mp4 model.forge.js out/hero.mp4 --view hero
218
250
  forgecad capture mp4 examples/3d-printer.forge.js out/sweep.mp4 --capture section-sweep --sweep-plane YZ --sweep-frames 180
219
251
  ```
220
252
 
@@ -247,18 +279,22 @@ forgecad render section examples/furniture/01-table.forge.js out/bold.svg --edge
247
279
  | `--focus <names>` | Focus: no arg hides mocks; comma-separated names shows only those |
248
280
  | `--hide <names>` | Hide comma-separated object names |
249
281
  | `--camera <front\|back\|side\|right\|top\|iso\|az:el\|az:el:dist\|spec>` | Camera preset, spherical (az:el), or full spec such as `proj=perspective;pos=x,y,z;target=x,y,z;up=x,y,z;fov=45`. Repeatable. |
282
+ | `--camera-json <file>` | Exact viewport camera JSON file |
250
283
  | `--view <name>` | Named camera view declared by the model with scene({ views }) |
251
284
  | `--size <px>` | Image size in pixels |
252
- | `--scene <json>` | Viewport scene state JSON |
285
+ | `--scene <json\|file>` | Viewport scene state JSON or JSON file |
253
286
  | `--background <color>` | Canvas background override |
254
287
  | `--render-mode <solid\|wireframe>` | Shaded solid (default) or wireframe only |
255
- | `--edges <off\|thin\|bold>` | Edge overlay preset in solid mode (default: thin) |
288
+ | `--edges <off\|thin\|bold>` | Edge overlay preset in solid mode (default: off) |
256
289
  | `--render-style <classic\|studio\|fast\|glass>` | Visual render style (default: classic) |
257
290
  | `--port <n>` | Vite dev server port |
291
+ | `--fresh-server` | Start a fresh renderer instead of reusing an existing one |
258
292
  | `--chrome-path <path>` | Chrome or Chromium executable path |
259
293
  | `--output <path>` | Output file path |
260
- | `--channels <rgb,depth,normals,mask,connectivity,distance,collisions,thickness,section>` | Required inspection channels to emit; no default |
261
- | `--quality <default\|live\|high>` | Mesh/render quality |
294
+ | `--json` | Print machine-readable JSON |
295
+ | `--quality <default\|live\|high>` | Mesh quality preset |
296
+ | `--backend <manifold\|occt\|truck>` | Geometry backend |
297
+ | `--channels <rgb,depth,normals,roughness,mask,connectivity,distance,collisions,thickness,section>` | Required inspection channels to emit; no default |
262
298
  | `--force` | Replace an existing bundle directory |
263
299
  | `--min-thickness <mm>` | Critical thickness threshold in model units |
264
300
  | `--warn-thickness <mm>` | Warning thickness threshold in model units |
@@ -276,7 +312,6 @@ forgecad render section examples/furniture/01-table.forge.js out/bold.svg --edge
276
312
  | `--frames <n>` | Video frames per revolution |
277
313
  | `--fps <n>` | Video frame rate |
278
314
  | `--pitch <deg>` | Camera pitch angle in degrees |
279
- | `--backend <manifold\|occt\|truck>` | Geometry backend |
280
315
  | `--format <gif\|mp4>` | Output format |
281
316
  | `--capture <orbit\|animation\|section-sweep>` | Capture preset |
282
317
  | `--animation <name>` | Named jointsView animation clip |
@@ -330,6 +365,7 @@ forgecad cut-list examples/api/sheet-stock-cut-list.forge.js
330
365
  forgecad export cutting-layout examples/api/sheet-stock-cut-list.forge.js --sheet-width 420 --sheet-height 594 --kerf 3
331
366
 
332
367
  # 3D printing
368
+ forgecad check print bracket.forge.js
333
369
  forgecad export stl bracket.forge.js
334
370
  forgecad export 3mf bracket.forge.js --quality high
335
371
 
@@ -494,11 +530,26 @@ forgecad skill one-file ~/Desktop/forgecad-context.md
494
530
  forgecad skill flattened-files ~/Desktop/forgecad-skills
495
531
  ```
496
532
 
497
- > **Workflow:** Agent writes the model -> `forgecad run` validates it -> `forgecad render inspect` produces evidence -> `forgecad check params` sweeps the parameter range -> export ships the result. All in the terminal.
533
+ > **Workflow:** Agent writes the model -> `forgecad run` validates it -> `forgecad check print` catches printability risks -> `forgecad render inspect` produces visual evidence -> `forgecad check params` sweeps parameter robustness -> export ships the result. All in the terminal.
498
534
 
499
535
  ## Validation
500
536
 
501
- Test parameter ranges and run invariant suites.
537
+ Check printability, test parameter ranges, and run invariant suites.
538
+
539
+ ### `forgecad check print`
540
+
541
+ Run fast 3D-print readiness checks for collisions, mesh health, walls, overhangs, and bed contact.
542
+
543
+ Runs a Forge script with the headless kernel and emits a slicer-adjacent printability report without launching a browser. The check is designed for agents: JSON is stable, failures name the specific print risk, and the default profile is conservative for FDM PLA on a 0.4mm nozzle.
544
+
545
+ Checks include script `verify.*` results, exact positive-volume object collisions, physical component count, mesh topology, sampled wall thickness, unsupported overhang budget, and bed-contact area. Use `--json` for automation and `--output` to save the full report while keeping the readable terminal summary.
546
+
547
+ ```bash
548
+ forgecad check print examples/api/attachTo-basics.forge.js
549
+ forgecad check print examples/api/verification-demo.forge.js --json
550
+ forgecad check print model.forge.js --min-wall 1.2 --warn-wall 2
551
+ forgecad check print model.forge.js --expect-components 1 -p "Wall Thickness=3"
552
+ ```
502
553
 
503
554
  ### `forgecad check params`
504
555
 
@@ -566,7 +617,7 @@ The CLI is free for core workflows and exports. Production outputs are free to r
566
617
 
567
618
  | Free | Production outputs | Pro |
568
619
  |------|--------------------|-----|
569
- | `run`, `dev`, `studio`, `render 3d`, `export stl`, `export 3mf`, `export svg`, `check params`, `check suite` | `cut-list`, `export sketch-pdf`, `export step`, `export brep`, `export gcode`, `export sdf`, `export urdf`, `export report`, `export cutting-layout` are free to run; Pro covers commercial use. | `render hq`, `capture gif`, `capture mp4` |
620
+ | `run`, `dev`, `studio`, `render 3d`, `export stl`, `export 3mf`, `export svg`, `check print`, `check params`, `check suite` | `cut-list`, `export sketch-pdf`, `export step`, `export brep`, `export gcode`, `export sdf`, `export urdf`, `export report`, `export cutting-layout` are free to run; Pro covers commercial use. | `render hq`, `capture gif`, `capture mp4` |
570
621
 
571
622
  ```bash
572
623
  forgecad license # Check signed-in account status
@@ -29,7 +29,12 @@ bomToCsv(rows: BomRow[]): string
29
29
 
30
30
  **`BomRow`**: `part: string`, `qty: number`, `material?: string`, `process?: string`, `tolerance?: string`, `notes?: string`, `metadata?: PartMetadata`
31
31
 
32
- **`PartMetadata`**: `material?: string`, `process?: string`, `tolerance?: string`, `qty?: number`, `notes?: string`, `densityKgM3?: number`, `massKg?: number`
32
+ **`PartMetadata`**
33
+
34
+ | Option | Type | Description |
35
+ |--------|------|-------------|
36
+ | `tags?` | `string \| readonly string[]` | Viewport organization tags applied to scene objects produced from this part. |
37
+ | `material?`, `process?`, `tolerance?`, `qty?`, `notes?`, `densityKgM3?`, `massKg?` | | — |
33
38
 
34
39
  #### `assembly()` — Create an assembly container with named parts and joints for kinematic mechanisms.
35
40
 
@@ -670,13 +675,13 @@ toGroup(): ShapeGroup
670
675
  Each part becomes `{ name, shape }` or `{ name, group: [...] }` if the part is a [`ShapeGroup`](/docs/core#shapegroup). Top-level scripts should normally return the `SolvedAssembly` directly. Use `toGroup()` when you need [`ShapeGroup`](/docs/core#shapegroup) behavior; use this method only for advanced scene-graph control where you need access to the flat per-part array with metadata.
671
676
 
672
677
  ```ts
673
- toSceneObjects(): Array<{ name: string; shape?: Shape; group?: Array<{ name: string; shape: Shape; }>; metadata?: PartMetadata; }>
678
+ toSceneObjects(): Array<{ name: string; shape?: Shape; group?: Array<{ name: string; shape: Shape; tags?: string[]; }>; metadata?: PartMetadata; }>
674
679
  ```
675
680
 
676
681
  #### `toScene()` — Backward-compatible alias for `toSceneObjects()`.
677
682
 
678
683
  ```ts
679
- toScene(): Array<{ name: string; shape?: Shape; group?: Array<{ name: string; shape: Shape; }>; metadata?: PartMetadata; }>
684
+ toScene(): Array<{ name: string; shape?: Shape; group?: Array<{ name: string; shape: Shape; tags?: string[]; }>; metadata?: PartMetadata; }>
680
685
  ```
681
686
 
682
687
  #### [`bom()`](/docs/output#bom) — Generate a bill of materials for all parts in the solved assembly.