forgecad 0.1.1 → 0.1.3
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/{evalWorker-DuS4V-H5.js → evalWorker-1m873KWd.js} +107 -107
- package/dist/assets/{index-d60dJDhX.js → index-Dvz3nSDc.js} +342 -326
- package/dist/assets/{manifold-DEOPQqeC.js → manifold-C38sUiKu.js} +1 -1
- package/dist/assets/{manifold-cL7A7HeM.js → manifold-Dk2u-lhj.js} +1 -1
- package/dist/assets/{manifold-DbvY5u9x.js → manifold-rOWQW9fU.js} +1 -1
- package/dist/assets/{reportWorker-GZVKtf9S.js → reportWorker-Cj587shw.js} +129 -129
- package/dist/index.html +1 -1
- package/dist-cli/forgecad.js +204 -19
- package/dist-skill/SKILL.md +31 -4561
- package/dist-skill/docs/API/README.md +24 -0
- package/dist-skill/docs/API/guides/modeling-recipes.md +246 -0
- package/dist-skill/docs/API/internals/compiler.md +300 -0
- package/dist-skill/docs/API/internals/manifold.md +7 -0
- package/dist-skill/docs/API/model-building/README.md +31 -0
- package/dist-skill/docs/API/model-building/assembly.md +205 -0
- package/dist-skill/docs/API/model-building/coordinate-system.md +43 -0
- package/dist-skill/docs/API/model-building/entities.md +282 -0
- package/dist-skill/docs/API/model-building/geometry-conventions.md +104 -0
- package/dist-skill/docs/API/model-building/positioning.md +170 -0
- package/dist-skill/docs/API/model-building/reference.md +1936 -0
- package/dist-skill/docs/API/model-building/sheet-metal.md +180 -0
- package/dist-skill/docs/API/model-building/sketch-anchor.md +32 -0
- package/dist-skill/docs/API/model-building/sketch-booleans.md +101 -0
- package/dist-skill/docs/API/model-building/sketch-core.md +68 -0
- package/dist-skill/docs/API/model-building/sketch-extrude.md +57 -0
- package/dist-skill/docs/API/model-building/sketch-on-face.md +98 -0
- package/dist-skill/docs/API/model-building/sketch-operations.md +92 -0
- package/dist-skill/docs/API/model-building/sketch-path.md +70 -0
- package/dist-skill/docs/API/model-building/sketch-primitives.md +104 -0
- package/dist-skill/docs/API/model-building/sketch-transforms.md +60 -0
- package/dist-skill/docs/API/output/bom.md +53 -0
- package/dist-skill/docs/API/output/brep-export.md +82 -0
- package/dist-skill/docs/API/output/dimensions.md +62 -0
- package/dist-skill/docs/API/runtime/viewport.md +229 -0
- package/dist-skill/docs/CLI.md +672 -0
- package/dist-skill/docs/CODING.md +345 -0
- package/dist-skill/docs/PROGRAM-LEAD.md +180 -0
- package/dist-skill/docs/VISION.md +77 -0
- package/examples/api/import-group-assembly.forge.js +34 -0
- package/examples/api/import-group-source.forge.js +35 -0
- package/package.json +1 -1
|
@@ -0,0 +1,672 @@
|
|
|
1
|
+
# ForgeCAD CLI
|
|
2
|
+
|
|
3
|
+
## Architecture
|
|
4
|
+
|
|
5
|
+
All CLI tools share the **same forge engine** as the browser UI. There is one source of truth for geometry logic — no code duplication.
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
src/forge/headless.ts ← Single entry point for all contexts
|
|
9
|
+
├── kernel.ts ← Manifold WASM wrapper (Shape, box, cylinder, sphere, etc.)
|
|
10
|
+
├── runner.ts ← Script sandbox (Function() with full forge API injected)
|
|
11
|
+
├── section.ts ← Plane intersection / projection
|
|
12
|
+
├── sketch/ ← Complete 2D sketch system (primitives, transforms, booleans,
|
|
13
|
+
│ constraints, entities, topology, patterns, fillets, arc bridge)
|
|
14
|
+
├── params.ts ← Parameter system
|
|
15
|
+
├── library.ts ← Part library
|
|
16
|
+
├── meshToGeometry.ts ← Manifold mesh → Three.js BufferGeometry
|
|
17
|
+
└── sceneBuilder.ts ← Three.js scene setup (lighting, camera, materials)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Browser** imports via `src/forge/index.ts` → re-exports from `headless.ts`.
|
|
21
|
+
**CLI tools** import directly from `src/forge/headless.ts`.
|
|
22
|
+
|
|
23
|
+
The key function is `runScript(code, fileName, allFiles)` — it wraps user code in a `Function()` sandbox with the entire forge API injected, and transpiles project files so standard JS `import` / `export` / `require(...)` work for shared utility modules. CLI scripts just call `init()` + `runScript()` and work with the results.
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
Install the package and link the local binary once:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install
|
|
31
|
+
npm link
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
After that, use `forgecad ...` directly from your shell.
|
|
35
|
+
|
|
36
|
+
### Shell Autocomplete
|
|
37
|
+
|
|
38
|
+
ForgeCAD now ships shell completion scripts in the usual modern-tool style:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
forgecad completion bash
|
|
42
|
+
forgecad completion zsh
|
|
43
|
+
forgecad completion fish
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Quick install:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# bash
|
|
50
|
+
echo 'source <(forgecad completion bash)' >> ~/.bashrc
|
|
51
|
+
|
|
52
|
+
# zsh
|
|
53
|
+
mkdir -p ~/.zsh/completions
|
|
54
|
+
forgecad completion zsh > ~/.zsh/completions/_forgecad
|
|
55
|
+
echo 'fpath=(~/.zsh/completions $fpath)' >> ~/.zshrc
|
|
56
|
+
echo 'autoload -Uz compinit && compinit' >> ~/.zshrc
|
|
57
|
+
|
|
58
|
+
# fish
|
|
59
|
+
mkdir -p ~/.config/fish/completions
|
|
60
|
+
forgecad completion fish > ~/.config/fish/completions/forgecad.fish
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The completions are contextual:
|
|
64
|
+
|
|
65
|
+
- nested subcommands such as `forgecad notebook view` and `forgecad export step`
|
|
66
|
+
- command-specific flags and common enum values
|
|
67
|
+
- ForgeCAD file suggestions where a command expects `.forge.js`, `.sketch.js`, or `.forge-notebook.json`
|
|
68
|
+
|
|
69
|
+
## Available Commands
|
|
70
|
+
|
|
71
|
+
### Notebook Cells (server-backed)
|
|
72
|
+
|
|
73
|
+
Forge notebooks live in `.forge-notebook.json` files and behave like lightweight Jupyter notebooks for ForgeCAD code cells.
|
|
74
|
+
|
|
75
|
+
The browser and CLI both use the Vite server for notebook execution. The CLI does not run Forge locally for notebook cells; it auto-starts or reuses the Forge server, sends the cell code, then prints the returned output summary.
|
|
76
|
+
|
|
77
|
+
Append a new code cell and run it immediately in one command:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
forgecad notebook examples/demo.forge-notebook.json --code "show(box(40, 20, 10));"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
If the target notebook file does not exist yet, append mode auto-creates it first with the default ForgeCAD notebook structure, then adds the new cell.
|
|
84
|
+
|
|
85
|
+
Or pipe a larger cell in through stdin:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
cat /tmp/cell.js | forgecad notebook examples/demo.forge-notebook.json
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Re-run the last preview cell, or a specific cell id:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
forgecad notebook examples/demo.forge-notebook.json
|
|
95
|
+
forgecad notebook run examples/demo.forge-notebook.json <cell-id>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
View the notebook in the terminal without dumping raw JSON:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
forgecad notebook view examples/demo.forge-notebook.json
|
|
102
|
+
forgecad notebook view examples/demo.forge-notebook.json preview
|
|
103
|
+
forgecad notebook view examples/demo.forge-notebook.json 2
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
`view` is local-only. It parses the notebook JSON and renders notebook metadata, numbered source lines, and stored outputs for each cell. The optional selector accepts a 1-based cell number, an exact cell id, or `preview`.
|
|
107
|
+
|
|
108
|
+
`run`/`view` expect the notebook file to already exist. Auto-creation only applies to append flows (`--code`, `--file`, stdin, or the explicit `append` subcommand).
|
|
109
|
+
|
|
110
|
+
Export a notebook into a plain `.forge.js` script:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
forgecad notebook export examples/demo.forge-notebook.json
|
|
114
|
+
forgecad notebook export examples/demo.forge-notebook.json out/demo-from-notebook.forge.js
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
If you already have a Forge server running, point the CLI at it:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
forgecad notebook examples/demo.forge-notebook.json --server http://localhost:5173 --code "show(box(40, 20, 10));"
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Notebook paths are resolved from the shell working directory before the CLI calls the server, so the server's opened project root does not add an extra path prefix.
|
|
124
|
+
|
|
125
|
+
Notebook cell behavior:
|
|
126
|
+
|
|
127
|
+
- Cells share state top-to-bottom
|
|
128
|
+
- `show(value)` pins the geometry that should stay visible in the viewport
|
|
129
|
+
- A trailing expression is also treated as the cell value
|
|
130
|
+
- Cell outputs are written back into the notebook JSON, similar to Jupyter
|
|
131
|
+
|
|
132
|
+
For the `forgecad` entrypoints below, passing a `.forge-notebook.json` uses that notebook's preview cell. That means you can inspect with `view`, validate with `run`, and render or capture the current preview without exporting first.
|
|
133
|
+
|
|
134
|
+
### Script Validation
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
forgecad run examples/cup.forge.js
|
|
138
|
+
forgecad run examples/api/notebook-iteration.forge-notebook.json
|
|
139
|
+
forgecad run examples/cup.forge.js --debug-imports
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Runs a `.forge.js`, `.sketch.js`, or notebook preview cell in the real runtime and prints object stats, diagnostics, and execution time.
|
|
143
|
+
|
|
144
|
+
`--debug-imports` adds an import trace (source file, target file, overrides, return type, success/error phase), useful when debugging `importPart()`/`importSketch()` behavior.
|
|
145
|
+
|
|
146
|
+
### SVG Export (no browser needed)
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
forgecad export svg examples/frame.sketch.js [output.svg]
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Runs a `.sketch.js` script in Node.js using the real forge engine and outputs SVG. No browser, no Puppeteer — pure Node.
|
|
153
|
+
|
|
154
|
+
**How it works:** Initializes the Manifold WASM kernel, runs the script through `runScript()`, extracts the Sketch result, converts polygons to SVG paths.
|
|
155
|
+
|
|
156
|
+
### STEP / BREP Export (exact subset, Python + CadQuery)
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
forgecad export step examples/api/brep-exportable.forge.js
|
|
160
|
+
forgecad export brep examples/api/brep-exportable.forge.js
|
|
161
|
+
|
|
162
|
+
# Optional overrides:
|
|
163
|
+
forgecad export step examples/api/brep-exportable.forge.js --output out/demo.step
|
|
164
|
+
forgecad export step examples/api/brep-exportable.forge.js --python 3.11
|
|
165
|
+
forgecad export step examples/api/brep-exportable.forge.js --uv /custom/path/to/uv
|
|
166
|
+
forgecad export step examples/chess-set.forge.js --allow-faceted
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
This exporter is `uv`-first. `cli/forge-brep-export.py` carries inline dependency metadata, so `uv run` provisions CadQuery automatically for the exporter environment.
|
|
170
|
+
|
|
171
|
+
By default this exporter is exact-subset only. It does **not** silently convert arbitrary triangle meshes back into fake BREP. Instead, Forge lowers compile-covered geometry into the `cadquery-occt` compiler target and exports that exact subset through CadQuery/OpenCascade.
|
|
172
|
+
|
|
173
|
+
If you pass `--allow-faceted`, unsupported closed mesh solids are exported as explicit faceted OCCT solids. This keeps hull-heavy designs exportable to STEP/BREP, but that fallback is tessellation-driven rather than exact replay.
|
|
174
|
+
|
|
175
|
+
The maintained feature matrix lives in [`docs/permanent/API/output/brep-export.md`](API/output/brep-export.md).
|
|
176
|
+
|
|
177
|
+
If any returned solid object falls outside the exact subset, the CLI fails with a reason instead of silently exporting degraded geometry. When a scene mixes solids and 2D sketches, the exact solids export and the sketch-only objects are skipped with a warning.
|
|
178
|
+
|
|
179
|
+
With `--allow-faceted`, mesh-solid blockers that still lack an exact replay plan are exported as faceted solids instead of failing. The CLI prints which objects used the fallback.
|
|
180
|
+
|
|
181
|
+
For coverage runs across many examples, use the `uv` matrix scripts:
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
uv run scripts/brep/matrix.py --format step examples
|
|
185
|
+
uv run scripts/brep/matrix.py --format brep examples
|
|
186
|
+
uv run scripts/brep/rerun_failures.py tmp/brep-matrix-step-20260306T120000Z.json
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
These scripts use the repo-local `.venv-brep/.venv/bin/python` by default, run exports through a bounded parallel worker pool, and write JSON reports under `tmp/`.
|
|
190
|
+
|
|
191
|
+
### SDF Robot Export (Gazebo package)
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
forgecad export sdf examples/api/sdf-rover-demo.forge.js
|
|
195
|
+
|
|
196
|
+
# Optional output directory:
|
|
197
|
+
forgecad export sdf examples/api/sdf-rover-demo.forge.js --output out/forge_scout
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
This exporter writes a Gazebo-friendly package workspace:
|
|
201
|
+
|
|
202
|
+
- `models/<model-name>/model.sdf`
|
|
203
|
+
- `models/<model-name>/model.config`
|
|
204
|
+
- `models/<model-name>/meshes/*.stl`
|
|
205
|
+
- `worlds/<world-name>.sdf` when the script requests a demo world
|
|
206
|
+
- `manifest.json` with topic names, link/joint mappings, and exporter warnings
|
|
207
|
+
|
|
208
|
+
The script must call `robotExport({...})` with an `assembly(...)` graph. The exporter uses the declared parts + joints directly; it does **not** try to infer a robot from flattened scene meshes.
|
|
209
|
+
|
|
210
|
+
When `world.generateDemoWorld` and `world.keyboardTeleop.enabled` are on, the exported world includes both:
|
|
211
|
+
|
|
212
|
+
- Gazebo's GUI `KeyPublisher` plugin
|
|
213
|
+
- server-side `TriggeredPublisher` bindings that map arrow keys to the diff-drive `cmd_vel` topic
|
|
214
|
+
|
|
215
|
+
Recommended launch flow:
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
export GZ_SIM_RESOURCE_PATH="$PWD/out/forge_scout/models${GZ_SIM_RESOURCE_PATH:+:$GZ_SIM_RESOURCE_PATH}"
|
|
219
|
+
|
|
220
|
+
# Terminal 1: server
|
|
221
|
+
gz sim -s -r out/forge_scout/worlds/forge_scout_trial.sdf
|
|
222
|
+
|
|
223
|
+
# Terminal 2: GUI client using the same world layout
|
|
224
|
+
gz sim -g out/forge_scout/worlds/forge_scout_trial.sdf
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Notes:
|
|
228
|
+
|
|
229
|
+
- On macOS, use the split `-s` / `-g` flow above. `gz sim <world.sdf>` is not supported there.
|
|
230
|
+
- Click the 3D view so it has keyboard focus, then use `W` / `X` for forward / reverse, `A` / `D` to rotate, `Q` / `E` / `Z` / `C` for diagonals, and `S` or `Space` to stop.
|
|
231
|
+
- For older exports created before the GUI plugin was added, load `Key Publisher` manually from the Gazebo GUI plugins menu.
|
|
232
|
+
|
|
233
|
+
Current behavior:
|
|
234
|
+
|
|
235
|
+
- Per-link geometry is exported as STL mesh assets
|
|
236
|
+
- Collision geometry reuses the same mesh unless `collision: 'none'` is set on a link
|
|
237
|
+
- Link mass comes from `massKg`, else `densityKgM3 * volume`, else a default density
|
|
238
|
+
- Inertia is an approximate box fit based on link bounds
|
|
239
|
+
- Coupled joints are currently rejected
|
|
240
|
+
- Parts without geometry are currently rejected
|
|
241
|
+
|
|
242
|
+
### PNG Render (requires Chrome)
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
forgecad render examples/cup.forge.js [output.png]
|
|
246
|
+
forgecad render examples/api/notebook-iteration.forge-notebook.json [output.png]
|
|
247
|
+
forgecad render examples/cup.forge.js out/scene.png --scene '{"camera":{"projectionMode":"perspective","position":[200,-160,120],"target":[0,0,20],"up":[0,0,1]},"objects":{"obj-2":{"visible":false},"obj-3":{"opacity":0.35}}}'
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Renders 3D shapes to PNG images from multiple camera angles. Uses Puppeteer to launch headless Chrome with WebGL for Three.js rendering.
|
|
251
|
+
|
|
252
|
+
When the input is a notebook, `forgecad render` renders the notebook's preview cell.
|
|
253
|
+
|
|
254
|
+
**How it works:**
|
|
255
|
+
1. `cli/forge-render.mjs` — Node launcher script. Auto-starts Vite dev server if not running, launches Puppeteer.
|
|
256
|
+
2. `cli/render.html` + `cli/render.ts` — Loaded in the browser by Puppeteer. Imports from `src/forge/headless.ts`, runs the script, builds a Three.js scene, renders from multiple angles.
|
|
257
|
+
3. Screenshots are captured as base64 PNG and saved to disk.
|
|
258
|
+
|
|
259
|
+
**Environment variables:**
|
|
260
|
+
|
|
261
|
+
| Variable | Default | Description |
|
|
262
|
+
|----------|---------|-------------|
|
|
263
|
+
| `FORGE_ANGLES` | `front,side,top,iso` | Camera angles to render |
|
|
264
|
+
| `FORGE_SIZE` | `1024` | Image size in pixels |
|
|
265
|
+
| `FORGE_PORT` | `5173` | Vite dev server port |
|
|
266
|
+
| `CHROME_PATH` | Auto-detected | Chrome/Chromium executable path |
|
|
267
|
+
|
|
268
|
+
**CLI options:**
|
|
269
|
+
- `--angles <front,side,top,iso>` — standard angles to render
|
|
270
|
+
- `--size <px>` — output size override
|
|
271
|
+
- `--port <n>` — Vite port override
|
|
272
|
+
- `--camera <spec>` — exact camera pose, e.g. `proj=perspective;pos=120,80,120;target=0,0,0;up=0,0,1`
|
|
273
|
+
- `--scene <json>` — full scene state copied from the viewport, including camera plus object visibility/opacity/color overrides
|
|
274
|
+
- `--background <color>` — background override
|
|
275
|
+
- `--chrome-path <path>` — Chrome executable path override
|
|
276
|
+
|
|
277
|
+
**Camera angles:** `front` (−Y), `back` (+Y), `side` (+X), `top` (+Z), `iso` (diagonal)
|
|
278
|
+
|
|
279
|
+
### Animated Capture (GIF or MP4, requires Chrome)
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
forgecad capture gif examples/cup.forge.js [output.gif]
|
|
283
|
+
forgecad capture mp4 examples/cup.forge.js [output.mp4]
|
|
284
|
+
forgecad capture gif examples/api/notebook-assembly-debug.forge-notebook.json --list
|
|
285
|
+
forgecad capture mp4 examples/api/runtime-joints-view.forge.js out/step.mp4 --capture animation --animation Step
|
|
286
|
+
forgecad capture gif examples/3d-printer.forge.js out/section.gif --cut-plane "Front Section"
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Creates high-quality animated captures from the real Forge viewport renderer:
|
|
290
|
+
- Orbit captures with optional wireframe pass
|
|
291
|
+
- Fixed-camera animation captures for `jointsView()` clips
|
|
292
|
+
- Named cut-plane captures
|
|
293
|
+
- Exact camera replay via `--camera`
|
|
294
|
+
- Full viewport scene replay via `--scene`
|
|
295
|
+
|
|
296
|
+
When the input is a notebook, `forgecad capture gif` / `forgecad capture mp4` capture the notebook's preview cell.
|
|
297
|
+
|
|
298
|
+
**How it works:**
|
|
299
|
+
1. Auto-starts (or reuses) the Vite dev server.
|
|
300
|
+
2. Loads `cli/render.html` in headless Chrome.
|
|
301
|
+
3. Runs the script once, then captures frames from the same scene while applying the selected animation, cut planes, and camera pose.
|
|
302
|
+
4. Encodes with `ffmpeg` when available:
|
|
303
|
+
- GIF: palettegen/paletteuse for much better colors
|
|
304
|
+
- MP4: H.264 via `libx264`
|
|
305
|
+
5. Falls back to the pure-JS GIF encoder only when `ffmpeg` is unavailable.
|
|
306
|
+
|
|
307
|
+
**Options:**
|
|
308
|
+
- `--format <gif|mp4>` — output format
|
|
309
|
+
- `--capture <orbit|animation>` — moving orbit camera or fixed animation camera
|
|
310
|
+
- `--animation <name>` — select one `jointsView()` clip
|
|
311
|
+
- `--animation-loops <n>` — repeat the chosen clip
|
|
312
|
+
- `--cut-plane <name>` — enable a named cut plane (repeatable)
|
|
313
|
+
- `--camera <spec>` — exact camera pose, e.g. `proj=perspective;pos=120,80,120;target=0,0,0;up=0,0,1`
|
|
314
|
+
- `--scene <json>` — full scene state copied from the viewport, including camera plus object visibility/opacity/color overrides
|
|
315
|
+
- `--render-mode <solid|wireframe>` — primary render mode
|
|
316
|
+
- `--include-wireframe-pass` / `--no-wireframe-pass` — control the extra wireframe pass
|
|
317
|
+
- `--size <px>` — output frame resolution (default `960`)
|
|
318
|
+
- `--pixel-ratio <n>` — render supersampling factor (default `2`)
|
|
319
|
+
- `--fps <n>` — capture frame rate (default `24`)
|
|
320
|
+
- `--frames-per-turn <n>` — frames per full orbit pass (default `72`)
|
|
321
|
+
- `--hold-frames <n>` — freeze frames before each pass (default `6`)
|
|
322
|
+
- `--pitch <deg>` — orbit elevation override
|
|
323
|
+
- `--background <color>` — background color (default `#252526`)
|
|
324
|
+
- `--quality <default|live|high>` — Forge geometry quality preset for export (default `high`)
|
|
325
|
+
- `--encoder <auto|ffmpeg|js>` — GIF encoder strategy
|
|
326
|
+
- `--crf <n>` — MP4 quality for `libx264` (default `18`)
|
|
327
|
+
- `--list` — print the script's available animation clips and cut planes
|
|
328
|
+
- `--port <n>` — Vite port (default `5173`)
|
|
329
|
+
- `--chrome-path <path>` — Chrome executable path override
|
|
330
|
+
- `--ffmpeg-path <path>` — ffmpeg executable path override
|
|
331
|
+
|
|
332
|
+
**Environment variables:**
|
|
333
|
+
- `FORGE_CAPTURE_SIZE`
|
|
334
|
+
- `FORGE_CAPTURE_PIXEL_RATIO`
|
|
335
|
+
- `FORGE_CAPTURE_FPS`
|
|
336
|
+
- `FORGE_CAPTURE_FRAMES_PER_TURN`
|
|
337
|
+
- `FORGE_CAPTURE_HOLD_FRAMES`
|
|
338
|
+
- `FORGE_CAPTURE_PITCH_DEG`
|
|
339
|
+
- `FORGE_CAPTURE_BACKGROUND`
|
|
340
|
+
- `FORGE_CAPTURE_QUALITY`
|
|
341
|
+
- `FORGE_CAPTURE_ANIMATION_LOOPS`
|
|
342
|
+
- `FORGE_CAPTURE_CRF`
|
|
343
|
+
- `FFMPEG_PATH`
|
|
344
|
+
- Legacy `FORGE_GIF_*` vars are still honored as fallbacks
|
|
345
|
+
- `FORGE_PORT`
|
|
346
|
+
- `CHROME_PATH`
|
|
347
|
+
|
|
348
|
+
**UI scene handoff:**
|
|
349
|
+
- The View Panel exposes a `Camera` section.
|
|
350
|
+
- Use `Copy CLI --scene` to grab the current viewport framing plus per-object scene overrides and paste it directly into `render`, `capture gif`, or `capture mp4`.
|
|
351
|
+
|
|
352
|
+
### PDF Report (2D drawing pack)
|
|
353
|
+
|
|
354
|
+
```bash
|
|
355
|
+
forgecad export report examples/cup.forge.js [output.pdf]
|
|
356
|
+
forgecad export report examples/cup.forge.js [output.pdf] --dim-angle-tol 18
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
Generates a searchable-text PDF report with multiple projected drawing views:
|
|
360
|
+
- Bill of Materials page (auto-summed from script `bom()` entries)
|
|
361
|
+
- Combined model page (front/right/top/isometric)
|
|
362
|
+
- Disassembled component pages (same view set per unique component geometry; repeated identical items collapse into one page)
|
|
363
|
+
- Auto-generated detail continuation pages for elongated/high-detail views (separate pages, not overlayed)
|
|
364
|
+
- `dim()` annotations included per view only when their axis aligns with that view's projection plane axes
|
|
365
|
+
|
|
366
|
+
BOM aggregation rules:
|
|
367
|
+
- Each `bom(quantity, description, { unit })` call contributes one raw entry
|
|
368
|
+
- Report export groups by `key` (if provided) else by normalized `description + unit`
|
|
369
|
+
- Quantities are summed per group and rendered as line items in the BOM table
|
|
370
|
+
|
|
371
|
+
Component dimension ownership for disassembled pages:
|
|
372
|
+
- Preferred: explicit binding via `dim(..., { component: \"Part Name\" })`
|
|
373
|
+
- Imported-part ownership: `dim(..., { currentComponent: true })` to pin to the owning returned component instance (no bbox heuristic)
|
|
374
|
+
- Other-component ownership: `dim(..., { component: \"Tabletop\" })`
|
|
375
|
+
- If multiple owners are bound (e.g. `currentComponent: true` plus another component), it is treated as shared and stays on the overview page
|
|
376
|
+
- Fallback: automatic ownership only when both dimension endpoints are unambiguously inside exactly one returned component bounding box
|
|
377
|
+
- Ambiguous dimensions are intentionally skipped for disassembled pages
|
|
378
|
+
|
|
379
|
+
Optional report flag:
|
|
380
|
+
- `--dim-angle-tol <degrees>`: include dimensions whose projected direction is within this many degrees of the nearest view axis (default: `12`)
|
|
381
|
+
|
|
382
|
+
### STL Export (from browser)
|
|
383
|
+
|
|
384
|
+
STL export is available in the browser UI via the Export panel. Binary STL format.
|
|
385
|
+
|
|
386
|
+
### Parameter Validation
|
|
387
|
+
|
|
388
|
+
```bash
|
|
389
|
+
forgecad check params examples/shoe-rack-doors.forge.js [--samples 10]
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
Samples each parameter across its range and checks for runtime errors, degenerate geometry (volume ≈ 0), and new collisions between parts. Skips intra-group collisions when assembly groups are used.
|
|
393
|
+
|
|
394
|
+
**Options:**
|
|
395
|
+
- `--samples N` — Number of sample points per parameter (default: 8)
|
|
396
|
+
|
|
397
|
+
**Output example:**
|
|
398
|
+
```
|
|
399
|
+
✓ Baseline: 6 objects, 12 params
|
|
400
|
+
✓ Checked 91 parameter samples (8 per param)
|
|
401
|
+
|
|
402
|
+
⚠ Found 8 issues across 4 parameters:
|
|
403
|
+
|
|
404
|
+
Parameter "Bottom Left Door":
|
|
405
|
+
💥 New collision at values: -120.0, -102.9
|
|
406
|
+
Bottom Left Door ∩ Frame (shared vol: 2561.9mm³)
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
### Transform/Assembly Invariant Check
|
|
410
|
+
|
|
411
|
+
```bash
|
|
412
|
+
forgecad check transforms
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
Runs fast math-level invariants to catch transform order and frame composition regressions before they leak into examples.
|
|
416
|
+
|
|
417
|
+
### Compiler Snapshot Check
|
|
418
|
+
|
|
419
|
+
```bash
|
|
420
|
+
forgecad check compiler
|
|
421
|
+
forgecad check compiler --case segmented-runtime-hints
|
|
422
|
+
forgecad check compiler --update
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
Runs curated compiler regression cases and compares them against committed snapshots.
|
|
426
|
+
This is a unit-style invariant check, not just a debugger convenience.
|
|
427
|
+
The ordinary multi-feature part corpus lives in [`examples/compiler-corpus/README.md`](../../examples/compiler-corpus/README.md).
|
|
428
|
+
|
|
429
|
+
Each snapshot records:
|
|
430
|
+
- Forge compile plans
|
|
431
|
+
- CadQuery/OCCT lowerings
|
|
432
|
+
- export routing decisions
|
|
433
|
+
- quantized runtime Manifold mesh summaries
|
|
434
|
+
- quantized compiler-lowered Manifold mesh summaries
|
|
435
|
+
|
|
436
|
+
This check also fails if:
|
|
437
|
+
- a plan-covered shape or sketch no longer matches its compiler-lowered runtime output
|
|
438
|
+
- export manifests drift away from the per-object compiler routing decisions
|
|
439
|
+
- exact/faceted support claims stop matching the lowered artifacts and diagnostics
|
|
440
|
+
|
|
441
|
+
### Query Propagation Snapshot Check
|
|
442
|
+
|
|
443
|
+
```bash
|
|
444
|
+
forgecad check query-propagation
|
|
445
|
+
forgecad check query-propagation --case hull-runtime-boundary
|
|
446
|
+
forgecad check query-propagation --update
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
Runs focused topology-rewrite query-propagation snapshots without dumping the
|
|
450
|
+
entire compiler scene. This keeps supported, ambiguous, and intentionally
|
|
451
|
+
unsupported rewrite semantics reviewable as the propagation layer evolves.
|
|
452
|
+
|
|
453
|
+
Each snapshot records:
|
|
454
|
+
- the propagated shape objects that actually carry topology-rewrite metadata
|
|
455
|
+
- exact versus faceted routing outcomes for those objects
|
|
456
|
+
- deterministic rewrite-operation ordering
|
|
457
|
+
- preserved and created query summaries
|
|
458
|
+
- explicit ambiguity/unsupported diagnostic codes
|
|
459
|
+
|
|
460
|
+
This check also fails if:
|
|
461
|
+
- a defended propagation case loses the expected preserved or created query shape
|
|
462
|
+
- a known unsupported rewrite stops reporting its explicit diagnostic boundary
|
|
463
|
+
- a multi-feature corpus part stops surfacing the expected rewrite ordering
|
|
464
|
+
|
|
465
|
+
### Example Architecture Gate
|
|
466
|
+
|
|
467
|
+
```bash
|
|
468
|
+
forgecad check examples
|
|
469
|
+
forgecad check examples --family api-parts --family compiler-corpus
|
|
470
|
+
forgecad check examples --example examples/api/brep-exportable.forge.js
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
Runs the checked example manifest for the entire `examples/` tree.
|
|
474
|
+
|
|
475
|
+
The manifest currently lives in `cli/example-manifest/` and covers every:
|
|
476
|
+
|
|
477
|
+
- `.forge.js`
|
|
478
|
+
- `.sketch.js`
|
|
479
|
+
- `.forge-notebook.json`
|
|
480
|
+
|
|
481
|
+
The command always verifies manifest coverage first, so it fails if:
|
|
482
|
+
|
|
483
|
+
- a new example file was added without classification
|
|
484
|
+
- a checked manifest entry points at a missing file
|
|
485
|
+
- an example's assigned validation path fails
|
|
486
|
+
- a `part` example's declared route expectation no longer matches the compiler report
|
|
487
|
+
|
|
488
|
+
Current example classes:
|
|
489
|
+
|
|
490
|
+
- `part`: runtime execution plus optional exact/faceted route assertions on the selected primary shapes
|
|
491
|
+
- `assembly`: runtime solve + scene emission, not exact-route parity
|
|
492
|
+
- `runtime-scene`: viewport/report/runtime examples that still need to execute successfully
|
|
493
|
+
- `sketch`: sketch payload validation via the sketch export path
|
|
494
|
+
- `notebook`: preview-cell validation for `.forge-notebook.json`
|
|
495
|
+
- `experimental`: temporary fenced examples that still have to run
|
|
496
|
+
|
|
497
|
+
The gate dispatches by declared validation path, not just by class label:
|
|
498
|
+
|
|
499
|
+
- `part-runtime`: execute and then enforce any declared exact/faceted route contract
|
|
500
|
+
- `assembly-runtime`: execute and validate solved-scene/assembly-owned runtime behavior
|
|
501
|
+
- `runtime-scene`: execute as a viewport/report/runtime scene without treating it as part-route evidence
|
|
502
|
+
- `sketch-svg`: render returned sketch payloads through the sketch SVG path
|
|
503
|
+
- `notebook-preview`: materialize and execute the notebook preview cell
|
|
504
|
+
- `experimental-runtime`: execute only, while the example stays outside the active architecture claim
|
|
505
|
+
|
|
506
|
+
For non-part entries, the manifest can also pin specific runtime surfaces that
|
|
507
|
+
must remain available to repo checks, such as BOM entries, cut planes,
|
|
508
|
+
`jointsView()` controls, grouped scene structure, or collected
|
|
509
|
+
`robotExport(...)` data.
|
|
510
|
+
|
|
511
|
+
Current part route states:
|
|
512
|
+
|
|
513
|
+
- `exact`: selected primary shapes must stay on the exact compiler route
|
|
514
|
+
- `faceted`: exact must stay blocked and allow-faceted must succeed with diagnostics
|
|
515
|
+
- `holdout`: runtime-checked, but intentionally outside the exact-route claim because the example still mixes route outcomes or depends on a documented unsupported capability; this is a temporary recovery state and should normally trend back to zero
|
|
516
|
+
|
|
517
|
+
Successful runs also print the current temporary fence list, including each
|
|
518
|
+
remaining `holdout` or `experimental` entry's blocker and follow-up task, so
|
|
519
|
+
the command output can be used directly in a phase-entry review.
|
|
520
|
+
|
|
521
|
+
Use `--family` when a task owns only one manifest lane, and `--example` when you
|
|
522
|
+
want to debug a single checked artifact.
|
|
523
|
+
|
|
524
|
+
### Invariant Test Suite
|
|
525
|
+
|
|
526
|
+
```bash
|
|
527
|
+
forgecad check suite
|
|
528
|
+
npm test
|
|
529
|
+
npm run test:examples
|
|
530
|
+
npm run test:compiler
|
|
531
|
+
npm run test:compiler:update
|
|
532
|
+
npm run test:query-propagation
|
|
533
|
+
npm run test:query-propagation:update
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
ForgeCAD's current unit-test surface is assertion-based CLI checks, not a separate Vitest/Jest harness.
|
|
537
|
+
|
|
538
|
+
The important entrypoints are:
|
|
539
|
+
- `npm test` runs the repo invariant suite (`transforms`, `dimensions`, `placement`, `js-modules`, `brep`, `compiler`, `query-propagation`, `examples`, `api`)
|
|
540
|
+
- `npm run test:examples` runs the example architecture gate across the checked `examples/` manifest
|
|
541
|
+
- `npm run test:compiler` runs just the compiler snapshot/invariant suite
|
|
542
|
+
- `npm run test:compiler:update` refreshes committed compiler snapshots after an intentional change
|
|
543
|
+
- `npm run test:query-propagation` runs the focused topology-rewrite query-propagation snapshots
|
|
544
|
+
- `npm run test:query-propagation:update` refreshes those query-propagation snapshots after an intentional change
|
|
545
|
+
- `forgecad check suite` is the CLI equivalent of the invariant suite runner
|
|
546
|
+
|
|
547
|
+
### Dimension Propagation Invariant Check
|
|
548
|
+
|
|
549
|
+
```bash
|
|
550
|
+
forgecad check dimensions
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
Runs shape-level invariants for dimension metadata propagation across:
|
|
554
|
+
- transform APIs (`translate`, `rotate`, `transform`, `scale`, `mirror`, `rotateAround`)
|
|
555
|
+
- copy/style APIs (`clone`, `color`, `setColor`, `smooth/refine/simplify`)
|
|
556
|
+
- boolean APIs (`add/subtract/intersect`, plus `union/difference/intersection/hull3d`)
|
|
557
|
+
- import runtime path (`importPart(...).color(...).translate(...)`)
|
|
558
|
+
|
|
559
|
+
### Dimension Debugger
|
|
560
|
+
|
|
561
|
+
```bash
|
|
562
|
+
forgecad debug dimensions /path/to/file.forge.js [--all]
|
|
563
|
+
forgecad debug dimensions /path/to/file.forge.js [--all] [--dim-angle-tol 12]
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
Prints:
|
|
567
|
+
- total object count
|
|
568
|
+
- total dimension count
|
|
569
|
+
- per-view visibility counts (`front/right/top/iso`) using report angle tolerance
|
|
570
|
+
- report ownership routing (`combined` vs `component:<name>`) per dimension
|
|
571
|
+
- per-object approximate dimension ownership (both endpoints inside object bbox)
|
|
572
|
+
- a dimension coordinate list (first 20 by default, `--all` for full dump)
|
|
573
|
+
|
|
574
|
+
### Compiler Debugger
|
|
575
|
+
|
|
576
|
+
```bash
|
|
577
|
+
forgecad debug compiler /path/to/file.forge.js
|
|
578
|
+
forgecad debug compiler /path/to/file.forge.js --compact
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
Prints JSON for the current script's compiler state, including:
|
|
582
|
+
- per-object compile plans
|
|
583
|
+
- CadQuery/OCCT lowering diagnostics and lowered plans
|
|
584
|
+
- faceted fallback eligibility
|
|
585
|
+
- runtime Manifold summaries
|
|
586
|
+
- compiler-lowered Manifold summaries
|
|
587
|
+
|
|
588
|
+
### Local Branch Cleanup
|
|
589
|
+
|
|
590
|
+
```bash
|
|
591
|
+
uv run cli/forge-prune-local-branches.py
|
|
592
|
+
uv run cli/forge-prune-local-branches.py --dry-run
|
|
593
|
+
uv run cli/forge-prune-local-branches.py --base mainline
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
This is a `uv`-backed Python utility for repository housekeeping. It finds local branches with no matching remote branch that are already merged into the selected base ref, shows them in a Rich terminal UI, then prompts one by one before deleting anything.
|
|
597
|
+
|
|
598
|
+
Behavior:
|
|
599
|
+
- Deletes with `git branch -d`, not force-delete
|
|
600
|
+
- Removes linked worktrees first when the branch is checked out in a secondary worktree
|
|
601
|
+
- Requires an explicit `force` choice if one of those linked worktrees is dirty
|
|
602
|
+
- Refuses to touch the current worktree, the primary worktree, or prunable/missing worktree entries
|
|
603
|
+
- `--path` lets you point at any location inside the target repository
|
|
604
|
+
|
|
605
|
+
## Adding New CLI Commands
|
|
606
|
+
|
|
607
|
+
1. Create or extend a module under `cli/`
|
|
608
|
+
2. Import from `../src/forge/headless`
|
|
609
|
+
3. Call `await init()` to load the WASM kernel
|
|
610
|
+
4. Use `runScript(code, fileName, allFiles)` to execute user scripts
|
|
611
|
+
5. Register the new subcommand in `cli/forgecad.ts`
|
|
612
|
+
|
|
613
|
+
### Minimal Example
|
|
614
|
+
|
|
615
|
+
```typescript
|
|
616
|
+
#!/usr/bin/env node
|
|
617
|
+
import { readFileSync } from 'fs';
|
|
618
|
+
import { init, runScript } from '../src/forge/headless';
|
|
619
|
+
|
|
620
|
+
const code = readFileSync(process.argv[2], 'utf-8');
|
|
621
|
+
|
|
622
|
+
await init();
|
|
623
|
+
const result = runScript(code, 'main.forge.js', {});
|
|
624
|
+
|
|
625
|
+
if (result.error) {
|
|
626
|
+
console.error(result.error);
|
|
627
|
+
process.exit(1);
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
for (const obj of result.objects) {
|
|
631
|
+
if (obj.shape) {
|
|
632
|
+
console.log(`${obj.name}: volume=${obj.shape.volume().toFixed(1)}mm³`);
|
|
633
|
+
}
|
|
634
|
+
if (obj.sketch) {
|
|
635
|
+
console.log(`${obj.name}: area=${obj.sketch.area().toFixed(1)}mm²`);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
### Cross-file imports
|
|
641
|
+
|
|
642
|
+
When running scripts that use `importSketch()` / `importSvgSketch()` / `importPart()` or plain JS module imports, pass all project files (or at least all files reachable by imports), keyed by project-relative path. This supports root-relative and relative imports, utility `.js` modules, and `.svg` assets (`./assets/logo.svg`):
|
|
643
|
+
|
|
644
|
+
```typescript
|
|
645
|
+
import { readdirSync, readFileSync } from 'fs';
|
|
646
|
+
|
|
647
|
+
const allFiles: Record<string, string> = {};
|
|
648
|
+
for (const f of readdirSync(scriptDir)) {
|
|
649
|
+
if (f.endsWith('.forge.js') || f.endsWith('.sketch.js') || f.endsWith('.js') || f.endsWith('.svg')) {
|
|
650
|
+
allFiles[f] = readFileSync(join(scriptDir, f), 'utf-8');
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
const result = runScript(code, 'main.forge.js', allFiles);
|
|
655
|
+
```
|
|
656
|
+
|
|
657
|
+
For utility modules that want explicit ForgeCAD imports instead of globals, use the virtual runtime module:
|
|
658
|
+
|
|
659
|
+
```javascript
|
|
660
|
+
import { box, union } from "forgecad";
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
Keep using `importPart()` / `importSketch()` for model/sketch files when you want ForgeCAD-specific behavior like param override scopes or SVG parsing.
|
|
664
|
+
|
|
665
|
+
## Dependencies
|
|
666
|
+
|
|
667
|
+
| Package | Purpose | Context |
|
|
668
|
+
|---------|---------|---------|
|
|
669
|
+
| `forgecad` | Installable CLI binary (`forgecad ...`) | Runtime package |
|
|
670
|
+
| `puppeteer-core` | Headless Chrome for PNG/GIF/MP4 rendering | Runtime dependency |
|
|
671
|
+
| `manifold-3d` | Geometry kernel (WASM) | Works in both Node and browser |
|
|
672
|
+
| `three` | 3D rendering (used by render.ts) | Loaded in browser context by Puppeteer |
|