forgecad 0.6.3 → 0.7.0
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/README.md +2 -11
- package/dist/assets/{AdminPage-CeqCUUgu.js → AdminPage-DAu1C1ST.js} +250 -151
- package/dist/assets/{BlogPage-P_AJP0v9.js → BlogPage-CJEXL_zJ.js} +94 -70
- package/dist/assets/{DocsPage-CKRV2iq2.js → DocsPage-Gc_BCdqC.js} +269 -143
- package/dist/assets/EditorApp-D9bJvtf7.js +11338 -0
- package/dist/assets/{EditorApp-CnC2k4cW.css → EditorApp-DG1-oUSV.css} +459 -87
- package/dist/assets/{EmbedViewer-DBlzmQ5i.js → EmbedViewer-CEO8XbV8.js} +2 -4
- package/dist/assets/LandingPage-CdCuEOdC.js +451 -0
- package/dist/assets/PricingPage-BSrxu6d7.js +232 -0
- package/dist/assets/{SettingsPage-BqCh9JcC.js → SettingsPage-FUCSIRq6.js} +129 -5
- package/dist/assets/{evalWorker-Ql-aKwLA.js → evalWorker-KoR0SNKq.js} +6770 -2914
- package/dist/assets/{index-2hfs_ub0.css → index-CyVd1D4D.css} +227 -53
- package/dist/assets/{Viewport-CoB46f5R.js → index-wTEK39at.js} +31385 -6439
- package/dist/assets/{javascript-DCxGoE5Y.js → javascript-DAl8Gmyo.js} +1 -1
- package/dist/assets/{manifold-CqNMHHKO.js → manifold-B1sGWdYk.js} +4 -3
- package/dist/assets/{manifold-Cce9wRFz.js → manifold-D7o0N50J.js} +1 -1
- package/dist/assets/{manifold-D6BeHIOo.js → manifold-G5sBaXzi.js} +1 -1
- package/dist/assets/{reportWorker-sFEFonXf.js → reportWorker-DYcRHhv9.js} +6798 -3341
- package/dist/assets/{vendor-react-Dt7-aaJH.js → vendor-react-CG3i_wp0.js} +65 -8
- package/dist/docs-raw/generated/assembly.md +691 -112
- package/dist/docs-raw/generated/concepts.md +1225 -1400
- package/dist/docs-raw/generated/core.md +464 -1412
- package/dist/docs-raw/generated/curves.md +593 -117
- package/dist/docs-raw/generated/lib.md +38 -748
- package/dist/docs-raw/generated/output.md +139 -245
- package/dist/docs-raw/generated/sheet-metal.md +473 -21
- package/dist/docs-raw/generated/sketch.md +553 -349
- package/dist/docs-raw/generated/viewport.md +345 -303
- package/dist/docs-raw/generated/wood.md +104 -0
- package/dist/index.html +2 -2
- package/dist/sitemap.xml +6 -6
- package/dist-cli/chunk-PZ5AY32C.js +10 -0
- package/dist-cli/chunk-PZ5AY32C.js.map +1 -0
- package/dist-cli/forgecad.js +9435 -5407
- package/dist-cli/forgecad.js.map +1 -0
- package/dist-cli/solver-FV7TJZGI.js +365 -0
- package/dist-cli/solver-FV7TJZGI.js.map +1 -0
- package/dist-skill/CONTEXT.md +3186 -7145
- package/dist-skill/SKILL-dev.md +21 -63
- package/dist-skill/SKILL.md +12 -56
- package/dist-skill/docs/API/core/concepts.md +16 -98
- package/dist-skill/docs/CLI/export.md +91 -0
- package/dist-skill/docs/CLI/projects.md +107 -0
- package/dist-skill/docs/CLI/studio_publishing.md +52 -0
- package/dist-skill/docs/CLI/validation.md +66 -0
- package/dist-skill/docs/generated/assembly.md +691 -112
- package/dist-skill/docs/generated/core.md +464 -1412
- package/dist-skill/docs/generated/curves.md +593 -117
- package/dist-skill/docs/generated/lib.md +38 -748
- package/dist-skill/docs/generated/output.md +139 -245
- package/dist-skill/docs/generated/sheet-metal.md +473 -21
- package/dist-skill/docs/generated/sketch.md +553 -349
- package/dist-skill/docs/generated/viewport.md +345 -303
- package/dist-skill/docs/generated/wood.md +104 -0
- package/dist-skill/docs/guides/coordinate-system.md +11 -17
- package/dist-skill/docs/guides/geometry-conventions.md +13 -70
- package/dist-skill/docs/guides/modeling-recipes.md +22 -195
- package/dist-skill/docs/guides/positioning.md +88 -147
- package/dist-skill/docs-dev/API/core/concepts.md +51 -0
- package/dist-skill/docs-dev/API/core/sdf-advanced.md +92 -0
- package/dist-skill/docs-dev/API/core/sdf-primitives.md +58 -0
- package/dist-skill/docs-dev/API/core/sdf-workflow.md +42 -0
- package/dist-skill/docs-dev/CLI/export.md +91 -0
- package/dist-skill/docs-dev/CLI/projects.md +107 -0
- package/dist-skill/docs-dev/CLI/studio_publishing.md +52 -0
- package/dist-skill/docs-dev/CLI/validation.md +66 -0
- package/dist-skill/{docs → docs-dev}/blueprint-first.md +5 -0
- package/dist-skill/{docs → docs-dev}/coding-best-practices.md +6 -8
- package/dist-skill/{docs → docs-dev}/coding.md +1 -3
- package/dist-skill/docs-dev/generated/assembly.md +771 -0
- package/dist-skill/docs-dev/generated/core.md +775 -0
- package/dist-skill/docs-dev/generated/curves.md +688 -0
- package/dist-skill/docs-dev/generated/lib.md +50 -0
- package/dist-skill/docs-dev/generated/output.md +234 -0
- package/dist-skill/docs-dev/generated/sheet-metal.md +506 -0
- package/dist-skill/docs-dev/generated/sketch.md +801 -0
- package/dist-skill/docs-dev/generated/viewport.md +486 -0
- package/dist-skill/docs-dev/generated/wood.md +104 -0
- package/dist-skill/docs-dev/guides/coordinate-system.md +46 -0
- package/dist-skill/docs-dev/guides/geometry-conventions.md +52 -0
- package/dist-skill/docs-dev/guides/modeling-recipes.md +77 -0
- package/dist-skill/docs-dev/guides/positioning.md +151 -0
- package/dist-skill/{docs → docs-dev}/guides/skill-maintenance.md +21 -10
- package/dist-skill/{docs → docs-dev}/internals/compiler.md +5 -6
- package/dist-skill/{docs → docs-dev}/internals/constraint-solver-quality.md +0 -1
- package/dist-skill/{docs → docs-dev}/internals/constraint-solver.md +0 -1
- package/dist-skill/{docs → docs-dev}/internals/sketch-2d-pipeline.md +2 -3
- package/examples/api/attachTo-basics.forge.js +5 -5
- package/examples/api/boolean-operations.forge.js +3 -3
- package/examples/api/bounding-box-visualizer.forge.js +2 -2
- package/examples/api/clone-duplicate.forge.js +1 -1
- package/examples/api/colors-union-vs-array.forge.js +6 -6
- package/examples/api/connector-assembly.forge.js +4 -4
- package/examples/api/connector-basics.forge.js +2 -2
- package/examples/api/extrude-options.forge.js +4 -10
- package/examples/api/feature-created-faces.forge.js +6 -10
- package/examples/api/fillet-showcase.forge.js +1 -1
- package/examples/api/folded-service-panel-cover.forge.js +2 -2
- package/examples/api/group-test.forge.js +1 -1
- package/examples/api/group-vs-union.forge.js +1 -1
- package/examples/api/highlight-debug.forge.js +4 -0
- package/examples/api/js-module-pillars.js +1 -1
- package/examples/api/js-module-scene.js +2 -2
- package/examples/api/mesh-import-slats.forge.js +1 -1
- package/examples/api/pointAlong-orientation.forge.js +1 -1
- package/examples/api/profile-2020-b-slot6.forge.js +0 -1
- package/examples/api/route-perimeter-flange.forge.js +1 -1
- package/examples/api/sdf-rover-demo.forge.js +10 -10
- package/examples/api/sketch-on-face-demo.forge.js +2 -2
- package/examples/api/sketch-regions.forge.js +4 -4
- package/examples/api/transition-curves.forge.js +1 -1
- package/examples/api/variable-sweep-pure-sdf-test.forge.js +162 -0
- package/examples/api/variable-sweep-test.forge.js +2 -2
- package/examples/api/wood-joinery.forge.js +60 -0
- package/examples/compiler-corpus/enclosure-shell-cuts.forge.js +3 -3
- package/examples/compiler-corpus/fastener-plate-variants.forge.js +2 -2
- package/examples/experiments/drone-arm.forge.js +53 -0
- package/examples/furniture/adjustable-table.forge.js +2 -2
- package/examples/furniture/bathroom.forge.js +11 -11
- package/examples/furniture/chair.forge.js +1 -1
- package/examples/generative/crystal-growth.forge.js +2 -2
- package/examples/generative/frost-spires.forge.js +3 -3
- package/examples/generative/golden-spiral-tower.forge.js +3 -3
- package/examples/mechanical/3d-printer.forge.js +28 -28
- package/examples/mechanical/5-finger-robot-hand.forge.js +15 -15
- package/examples/mechanical/airplane-propeller.forge.js +2 -2
- package/examples/mechanical/fillet-enclosure.forge.js +1 -1
- package/examples/mechanical/headphone-hanger-v2.forge.js +2 -2
- package/examples/mechanical/robot_hand.forge.js +15 -15
- package/examples/mechanical/robot_hand_2.forge.js +9 -9
- package/examples/products/bottle.forge.js +1 -1
- package/examples/products/chess-set.forge.js +19 -19
- package/examples/products/classical-piano.forge.js +11 -11
- package/examples/products/clock.forge.js +12 -12
- package/examples/products/iphone.forge.js +8 -8
- package/examples/products/laptop.forge.js +15 -15
- package/examples/products/liquid-soap-dispenser.forge.js +18 -18
- package/examples/products/origami-fish.forge.js +8 -6
- package/examples/products/spiderman-cake.forge.js +4 -4
- package/examples/toolbox/bolted-joint.forge.js +2 -2
- package/package.json +7 -4
- package/dist/assets/EditorApp-B-vQvgam.js +0 -9888
- package/dist/assets/LandingPage-C5n9hDXI.js +0 -322
- package/dist/assets/PublishedModelPage-Dt7PCVBj.js +0 -146
- package/dist/assets/__vite-browser-external-CURh0WXD.js +0 -8
- package/dist/assets/deserializeRunResult-BLAFoiE0.js +0 -19365
- package/dist/assets/index-1CYp3zUp.js +0 -1455
- package/dist/docs-raw/CLI.md +0 -865
- package/dist-skill/docs/API/API.md +0 -1666
- package/dist-skill/docs/API/README.md +0 -37
- package/dist-skill/docs/API/assembly/assembly.md +0 -617
- package/dist-skill/docs/API/core/edge-queries.md +0 -130
- package/dist-skill/docs/API/core/parameters.md +0 -122
- package/dist-skill/docs/API/core/reserved-terms.md +0 -137
- package/dist-skill/docs/API/core/sdf.md +0 -326
- package/dist-skill/docs/API/core/skill-cli.md +0 -194
- package/dist-skill/docs/API/core/skill-guide.md +0 -205
- package/dist-skill/docs/API/core/specs.md +0 -186
- package/dist-skill/docs/API/core/topology.md +0 -372
- package/dist-skill/docs/API/entities.md +0 -268
- package/dist-skill/docs/API/output/bom.md +0 -58
- package/dist-skill/docs/API/output/brep-export.md +0 -87
- package/dist-skill/docs/API/output/dimensions.md +0 -67
- package/dist-skill/docs/API/output/export.md +0 -110
- package/dist-skill/docs/API/output/gcode.md +0 -195
- package/dist-skill/docs/API/runtime/viewport.md +0 -420
- package/dist-skill/docs/API/sheet-metal/sheet-metal.md +0 -185
- package/dist-skill/docs/API/sketch/anchor.md +0 -37
- package/dist-skill/docs/API/sketch/booleans.md +0 -91
- package/dist-skill/docs/API/sketch/core.md +0 -73
- package/dist-skill/docs/API/sketch/extrude.md +0 -62
- package/dist-skill/docs/API/sketch/on-face.md +0 -104
- package/dist-skill/docs/API/sketch/operations.md +0 -78
- package/dist-skill/docs/API/sketch/path.md +0 -75
- package/dist-skill/docs/API/sketch/primitives.md +0 -146
- package/dist-skill/docs/API/sketch/regions.md +0 -80
- package/dist-skill/docs/API/sketch/text.md +0 -108
- package/dist-skill/docs/API/sketch/transforms.md +0 -65
- package/dist-skill/docs/API/toolbox/fasteners.md +0 -129
- package/dist-skill/docs/CLI.md +0 -865
- package/dist-skill/docs/INDEX.md +0 -94
- package/dist-skill/docs/RELEASING.md +0 -55
- package/dist-skill/docs/cli-monetization.md +0 -111
- package/dist-skill/docs/deployment.md +0 -281
- package/dist-skill/docs/generated/concepts.md +0 -2112
- package/dist-skill/docs/internals/shape-from-slices.md +0 -152
- package/dist-skill/docs/platform/admin.md +0 -45
- package/dist-skill/docs/platform/architecture.md +0 -79
- package/dist-skill/docs/platform/auth.md +0 -110
- package/dist-skill/docs/platform/email.md +0 -67
- package/dist-skill/docs/platform/projects.md +0 -111
- package/dist-skill/docs/platform/sharing.md +0 -90
- package/dist-skill/docs/runbook.md +0 -345
|
@@ -5,206 +5,147 @@ skill-order: 3
|
|
|
5
5
|
|
|
6
6
|
# Positioning Strategy
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
## Primitive origin convention
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
All 3D primitives are **centered on XY, base at Z=0**:
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
| Primitive | X range | Y range | Z range |
|
|
13
|
+
|-----------|---------|---------|---------|
|
|
14
|
+
| `box(60, 40, 20)` | [-30, 30] | [-20, 20] | [0, 20] |
|
|
15
|
+
| `cylinder(50, 10)` | [-10, 10] | [-10, 10] | [0, 50] |
|
|
16
|
+
| `sphere(15)` | [-15, 15] | [-15, 15] | [-15, 15] |
|
|
17
|
+
| `torus(20, 5)` | [-25, 25] | [-25, 25] | [-5, 5] |
|
|
13
18
|
|
|
14
|
-
|
|
19
|
+
Sphere and torus are fully centered (symmetric in Z). Box and cylinder sit on the XY ground plane — **Z goes up from zero, never negative**.
|
|
15
20
|
|
|
16
|
-
|
|
17
|
-
// Define connectors on parts
|
|
18
|
-
const shelf = box(200, 120, 10, true).withConnectors({
|
|
19
|
-
left_tab: connector.male("dovetail", { origin: [-100, 0, 0], axis: [-1, 0, 0] }),
|
|
20
|
-
right_tab: connector.male("dovetail", { origin: [100, 0, 0], axis: [1, 0, 0] }),
|
|
21
|
-
});
|
|
21
|
+
This means `box(w, d, h).translate(0, 0, -h/2)` is wrong for "center on Z" — the box is already at `[0, h]`, not `[-h/2, h/2]`.
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
shelf_0: connector.female("dovetail", { origin: [6, 0, -50], axis: [1, 0, 0] }),
|
|
25
|
-
shelf_1: connector.female("dovetail", { origin: [6, 0, 50], axis: [1, 0, 0] }),
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
// Single pair: "put my left_tab into panel's shelf_0"
|
|
29
|
-
const placed = shelf.matchTo(panel, "left_tab", "shelf_0");
|
|
23
|
+
---
|
|
30
24
|
|
|
31
|
-
|
|
32
|
-
const placed2 = shelf.matchTo(panel, { left_tab: "shelf_0" });
|
|
25
|
+
Most positioning bugs come from manual coordinate arithmetic. Use these methods in priority order.
|
|
33
26
|
|
|
34
|
-
|
|
35
|
-
const cabinet = group({ name: "Left", shape: panel });
|
|
36
|
-
shelf.matchTo(cabinet, "left_tab", "Left.shelf_0");
|
|
37
|
-
```
|
|
27
|
+
## 1. `group()` — local coordinates for multi-part assemblies
|
|
38
28
|
|
|
39
|
-
**
|
|
40
|
-
- **Stable.** Connector positions are authored explicitly — they don't shift when you fillet, chamfer, or boolean the part.
|
|
41
|
-
- **Semantic.** Connectors carry type and gender — `assembly.match()` validates compatibility before connecting.
|
|
42
|
-
- **Oriented.** Full coordinate frame (origin + axis + up), not just a point.
|
|
43
|
-
- **Queryable.** `shape.connectorDistance('a', 'b')` lets you derive dimensions from connector positions — e.g. compute pipe length from the distance between two fittings.
|
|
44
|
-
- **Explode-aware.** Matched parts automatically separate along connector axes when the explode slider is used.
|
|
29
|
+
The most common positioning bug: manually adding a parent's global offset to every sub-part. One wrong sign or forgotten variable and parts float into space. **Use `group()` to build parts in local coordinates (at the origin), then position the group once.**
|
|
45
30
|
|
|
46
|
-
**Connector patterns:**
|
|
47
31
|
```javascript
|
|
48
|
-
//
|
|
49
|
-
const
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
//
|
|
59
|
-
const
|
|
60
|
-
const
|
|
32
|
+
// BAD — every sub-part repeats the parent's global position
|
|
33
|
+
const unitY = -18, unitZ = 70;
|
|
34
|
+
const body = lib.roundedBox(100, 20, 32, 4).translate(0, unitY, unitZ);
|
|
35
|
+
const panel = box(98, 2, 18).translate(0, unitY - 12, unitZ + 4);
|
|
36
|
+
const louver = box(88, 2, 6).translate(0, unitY - 14, unitZ - 11);
|
|
37
|
+
const led = sphere(1.2).translate(35, unitY - 12, unitZ + 9);
|
|
38
|
+
|
|
39
|
+
// GOOD — build at local origin, group, translate once
|
|
40
|
+
const body = lib.roundedBox(100, 20, 32, 4);
|
|
41
|
+
const panel = box(98, 2, 18).translate(0, -12, 4); // relative to local origin
|
|
42
|
+
const louver = box(88, 2, 6).translate(0, -14, -11); // relative to local origin
|
|
43
|
+
const led = sphere(1.2).translate(35, -12, 9); // relative to local origin
|
|
44
|
+
const indoorUnit = group(
|
|
45
|
+
{ name: 'Body', shape: body },
|
|
46
|
+
{ name: 'Panel', shape: panel },
|
|
47
|
+
{ name: 'Louver', shape: louver },
|
|
48
|
+
{ name: 'LED', shape: led },
|
|
49
|
+
).translate(0, -18, 70); // ONE translate for the whole assembly
|
|
61
50
|
```
|
|
62
51
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
Cylinders default to Z-up. Instead of `rotate(90, 0, 0)` (which is confusing), use `pointAlong()`:
|
|
52
|
+
**Groups nest.** Build sub-assemblies as groups, then group those into larger assemblies — each level has its own local origin.
|
|
66
53
|
|
|
67
54
|
```javascript
|
|
68
|
-
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
//
|
|
72
|
-
|
|
55
|
+
const fan = group(hub, ...blades).translate(0, 25, 0); // fan assembly
|
|
56
|
+
const outdoorUnit = group(
|
|
57
|
+
{ name: 'Body', shape: casing },
|
|
58
|
+
{ name: 'Fan', shape: fan }, // already a group
|
|
59
|
+
{ name: 'Grille', shape: grille },
|
|
60
|
+
).translate(0, 23, -42); // position the whole outdoor unit
|
|
73
61
|
```
|
|
74
62
|
|
|
75
|
-
**
|
|
63
|
+
**When to use something else:** `group()` preserves individual shapes — you can't boolean (subtract/intersect) a group. If a sub-part needs a boolean with the parent body, do that boolean first in local coordinates, then group the result.
|
|
76
64
|
|
|
77
|
-
|
|
65
|
+
## 2. Connectors + `matchTo()` — default for mating interfaces
|
|
78
66
|
|
|
79
|
-
|
|
67
|
+
Define connectors on parts; `matchTo()` provides automatic 6-DOF alignment. The child translates and rotates so its connector aligns with the target's — origins coincide, axes oppose (plug-in model).
|
|
80
68
|
|
|
81
69
|
```javascript
|
|
82
|
-
const
|
|
83
|
-
|
|
70
|
+
const shelf = box(200, 120, 10, true).withConnectors({
|
|
71
|
+
left_tab: connector.male("dovetail", { origin: [-100, 0, 0], axis: [-1, 0, 0] }),
|
|
72
|
+
});
|
|
73
|
+
const panel = box(12, 120, 200, true).withConnectors({
|
|
74
|
+
shelf_0: connector.female("dovetail", { origin: [6, 0, -50], axis: [1, 0, 0] }),
|
|
75
|
+
});
|
|
76
|
+
const placed = shelf.matchTo(panel, "left_tab", "shelf_0");
|
|
77
|
+
// Dictionary form for multiple pairs on same target:
|
|
78
|
+
const placed2 = shelf.matchTo(panel, { left_tab: "shelf_0" });
|
|
79
|
+
// Named group children bubble connectors via dotted paths:
|
|
80
|
+
const cabinet = group({ name: "Left", shape: panel });
|
|
81
|
+
shelf.matchTo(cabinet, "left_tab", "Left.shelf_0");
|
|
84
82
|
```
|
|
85
83
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
**Limitations:** Anchor points are derived from the bounding box, so they shift when you fillet, chamfer, or boolean the part. No orientation, no validation, no semantic meaning. Prefer connectors for anything beyond quick prototyping.
|
|
89
|
-
|
|
90
|
-
### 5. `rotateAroundTo()` — Aim a point around a hinge/axis
|
|
84
|
+
**Why connectors first:** stable (don't shift on fillet/chamfer/boolean), semantic (carry type/gender), oriented (full frame), queryable (`shape.connectorDistance('a','b')`), explode-aware.
|
|
91
85
|
|
|
92
|
-
|
|
86
|
+
## 3. `pointAlong()` — orient cylinders before positioning
|
|
93
87
|
|
|
94
88
|
```javascript
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
// Rotate around Z until the tip lies in the plane formed by the Z axis and the target point
|
|
100
|
-
const aimed = arm.rotateAroundTo(
|
|
101
|
-
[0, 0, 1],
|
|
102
|
-
[0, 0, 0],
|
|
103
|
-
"tip",
|
|
104
|
-
[30, 30, 20],
|
|
105
|
-
);
|
|
106
|
-
|
|
107
|
-
// Exact line solve: throws if the target line is unreachable while preserving radius about the axis
|
|
108
|
-
const lineHit = arm.rotateAroundTo(
|
|
109
|
-
[0, 0, 1],
|
|
110
|
-
[0, 0, 0],
|
|
111
|
-
"tip",
|
|
112
|
-
[30, 30, 0],
|
|
113
|
-
{ mode: 'line' },
|
|
114
|
-
);
|
|
89
|
+
// BAD
|
|
90
|
+
const pipe = cylinder(100, 5).rotateX(90).translate(x, y, z);
|
|
91
|
+
// GOOD — reads as "pipe pointing along Y"
|
|
92
|
+
const pipe = cylinder(100, 5).pointAlong([0, 1, 0]).translate(x, y, z);
|
|
115
93
|
```
|
|
116
94
|
|
|
117
|
-
|
|
95
|
+
**Always call `pointAlong()` BEFORE `matchTo()` or `translate()`** — it reorients around the origin.
|
|
118
96
|
|
|
119
|
-
|
|
97
|
+
## 4. `attachTo()` — quick bounding-box positioning
|
|
120
98
|
|
|
121
99
|
```javascript
|
|
122
|
-
const
|
|
123
|
-
const part = box(20, 20, 30).moveToLocal(base, 10, 10, 10);
|
|
100
|
+
const column = cylinder(50, 8).attachTo(base, 'top', 'bottom');
|
|
124
101
|
```
|
|
125
102
|
|
|
126
|
-
|
|
103
|
+
`child.attachTo(parent, parentAnchor, selfAnchor, offset)`. Anchor points shift on fillet/chamfer/boolean — fragile for assembly interfaces, fine for quick prototyping.
|
|
127
104
|
|
|
128
|
-
|
|
129
|
-
- Moving a shape by a known fixed amount
|
|
130
|
-
- Positioning between two shapes whose locations you've already computed via `boundingBox()`
|
|
105
|
+
## 5. `rotateAroundTo()` — aim a point around a hinge/axis
|
|
131
106
|
|
|
132
107
|
```javascript
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
const
|
|
136
|
-
const pipeLen = bb2.min[1] - bb1.max[1];
|
|
137
|
-
const pipe = cylinder(pipeLen, 5)
|
|
138
|
-
.pointAlong([0, 1, 0])
|
|
139
|
-
.translate(40, (bb1.max[1] + bb2.min[1]) / 2, bb1.min[2] + 15);
|
|
108
|
+
const aimed = arm.rotateAroundTo([0, 0, 1], [0, 0, 0], "tip", [30, 30, 20]);
|
|
109
|
+
// Exact line solve:
|
|
110
|
+
const lineHit = arm.rotateAroundTo([0, 0, 1], [0, 0, 0], "tip", [30, 30, 0], { mode: 'line' });
|
|
140
111
|
```
|
|
141
112
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
When a part will be imported elsewhere, define semantic placement references once in the source file:
|
|
113
|
+
## 6. `moveToLocal()` — offset from another shape's min corner
|
|
145
114
|
|
|
146
115
|
```javascript
|
|
147
|
-
|
|
148
|
-
return union(base, post).withReferences({
|
|
149
|
-
points: {
|
|
150
|
-
mount: [0, -16, -4],
|
|
151
|
-
},
|
|
152
|
-
objects: {
|
|
153
|
-
post,
|
|
154
|
-
},
|
|
155
|
-
});
|
|
116
|
+
const part = box(20, 20, 30).moveToLocal(base, 10, 10, 10);
|
|
156
117
|
```
|
|
157
118
|
|
|
158
|
-
|
|
119
|
+
## 7. `translate()` — for simple offsets or bridging computed locations
|
|
159
120
|
|
|
160
121
|
```javascript
|
|
161
|
-
const
|
|
162
|
-
|
|
122
|
+
const pipeLen = bb2.min[1] - bb1.max[1];
|
|
123
|
+
const pipe = cylinder(pipeLen, 5).pointAlong([0, 1, 0]).translate(40, (bb1.max[1] + bb2.min[1]) / 2, bb1.min[2] + 15);
|
|
163
124
|
```
|
|
164
125
|
|
|
165
|
-
|
|
126
|
+
## 8. `placeReference()` — align any anchor to a world coordinate
|
|
166
127
|
|
|
167
|
-
|
|
128
|
+
Place a shape so a named anchor point lands exactly where you want it. Accepts all built-in anchors (`'bottom'`, `'center'`, `'top-front-left'`, etc.) plus custom references from `withReferences()`.
|
|
168
129
|
|
|
169
|
-
### ❌ Manual center-offset math
|
|
170
130
|
```javascript
|
|
171
|
-
//
|
|
172
|
-
const
|
|
173
|
-
.translate(0, -parentThickness/2 - d/2 - 5, parentHeight/2 - h/2 - 20);
|
|
174
|
-
```
|
|
131
|
+
// Ground a shape — bottom face center at Z = 0
|
|
132
|
+
const grounded = shape.placeReference('bottom', [0, 0, 0])
|
|
175
133
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
// GOOD: stable, semantic, self-documenting
|
|
179
|
-
const parent = box(100, 40, 60, true).withConnectors({
|
|
180
|
-
mount: connector.female("bracket", { origin: [0, -20, 10], axis: [0, -1, 0] }),
|
|
181
|
-
});
|
|
182
|
-
const child = box(w, d, h, true).withConnectors({
|
|
183
|
-
tab: connector.male("bracket", { origin: [0, d/2, 0], axis: [0, 1, 0] }),
|
|
184
|
-
});
|
|
185
|
-
const placed = child.matchTo(parent, "tab", "mount");
|
|
186
|
-
```
|
|
134
|
+
// Center at the world origin
|
|
135
|
+
const centered = shape.placeReference('center', [0, 0, 0])
|
|
187
136
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
// BAD: which axis? what happens to center?
|
|
191
|
-
const pipe = cylinder(100, 5).rotate(90, 0, 0).translate(x, y, z);
|
|
137
|
+
// Align left edge to X = 10
|
|
138
|
+
const aligned = shape.placeReference('left', [10, 0, 0])
|
|
192
139
|
```
|
|
193
140
|
|
|
194
|
-
|
|
195
|
-
```javascript
|
|
196
|
-
// GOOD: reads as "pipe pointing along Y"
|
|
197
|
-
const pipe = cylinder(100, 5).pointAlong([0, 1, 0]).translate(x, y, z);
|
|
198
|
-
```
|
|
141
|
+
Also works with custom placement references for cross-file parts:
|
|
199
142
|
|
|
200
|
-
### ❌ Bounding-box positioning for assembly interfaces
|
|
201
143
|
```javascript
|
|
202
|
-
//
|
|
203
|
-
|
|
204
|
-
```
|
|
144
|
+
// widget.forge.js — define once
|
|
145
|
+
return union(base, post).withReferences({ points: { mount: [0, -16, -4] } });
|
|
205
146
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
// GOOD: connector positions are explicit and stable
|
|
209
|
-
const shelf = plank.matchTo(sidePanel, "left_tab", "shelf_slot_0");
|
|
147
|
+
// importer — consume
|
|
148
|
+
const widget = require("./widget.forge.js").placeReference("mount", [120, 40, 0]);
|
|
210
149
|
```
|
|
150
|
+
|
|
151
|
+
For cross-file parts needing proper alignment, prefer connectors over placement references.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
skill-group: core
|
|
3
|
+
skill-order: 1
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# ForgeCAD Core Concepts
|
|
7
|
+
|
|
8
|
+
ForgeCAD scripts are JavaScript that returns geometry. The forge API is globally available — no imports needed.
|
|
9
|
+
|
|
10
|
+
```javascript
|
|
11
|
+
const width = param("Width", 50, { min: 20, max: 100, unit: "mm" });
|
|
12
|
+
return box(width, 30, 10);
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Execution Model
|
|
16
|
+
|
|
17
|
+
- Scripts re-execute on every parameter change (400ms debounce)
|
|
18
|
+
- All operations are **immutable** — return new shapes, never modify in place
|
|
19
|
+
- Must return one of: `Shape`, `Sketch`, `ShapeGroup`, `Array` of shapes/sketches/groups, or `Array` of `{ name, shape?, sketch?, color? }`
|
|
20
|
+
|
|
21
|
+
## Coordinate System
|
|
22
|
+
|
|
23
|
+
Z-up right-handed: X = left/right, Y = forward/back, Z = up/down.
|
|
24
|
+
|
|
25
|
+
## Colors
|
|
26
|
+
|
|
27
|
+
`.color(hex)` works on `Shape` and `Sketch`. Colors survive transforms. In booleans the first operand's color wins.
|
|
28
|
+
|
|
29
|
+
**`union()` removes colors** — shapes merge into one solid mesh. Return named objects instead:
|
|
30
|
+
|
|
31
|
+
```javascript
|
|
32
|
+
return [
|
|
33
|
+
{ name: "Base", shape: box(100, 100, 5), color: "#888888" },
|
|
34
|
+
{ name: "Column", shape: cylinder(50, 10).translate(50, 50, 5), color: "#4488cc" },
|
|
35
|
+
];
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Face Operations
|
|
39
|
+
|
|
40
|
+
Shapes carry semantic face labels through their lifecycle. The flow is:
|
|
41
|
+
|
|
42
|
+
1. **Primitives** assign canonical names — `box()` gives you `top`, `bottom`, `side-left`, etc.; `cylinder()` gives `top`, `bottom`, `side`.
|
|
43
|
+
2. **Extrusions** inherit labels from the sketch and add `top`/`bottom`.
|
|
44
|
+
3. **Transforms** (translate, rotate, scale, mirror) preserve all labels.
|
|
45
|
+
4. **Booleans** preserve labels from the first operand where geometry survives.
|
|
46
|
+
|
|
47
|
+
You resolve labels to geometry with `.face(name)` or `.face(query)` — see the Shape class docs for the full query API. Operations like `.pocket()`, `.boss()`, `.hole()`, and `faceProfile()` all consume face references.
|
|
48
|
+
|
|
49
|
+
## SDF Modeling
|
|
50
|
+
|
|
51
|
+
For organic shapes, smooth blending, TPMS lattices, and surface deformations. SDF shapes convert via `.toShape()`. See [sdf-primitives.md](sdf-primitives.md).
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
---
|
|
2
|
+
skill-group: dev-sdf
|
|
3
|
+
skill-order: 2
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# SDF Advanced Operations
|
|
7
|
+
|
|
8
|
+
## TPMS Lattices
|
|
9
|
+
|
|
10
|
+
```javascript
|
|
11
|
+
sdf.gyroid({ cellSize, thickness }) // most common for 3D printing
|
|
12
|
+
sdf.schwarzP({ cellSize, thickness }) // isotropic pores
|
|
13
|
+
sdf.diamond({ cellSize, thickness }) // stiffest structure
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
TPMS fills all space — **always clip with an intersect before `.toShape()`**:
|
|
17
|
+
|
|
18
|
+
```javascript
|
|
19
|
+
const lattice = sdf.gyroid({ cellSize: 8, thickness: 1.2 })
|
|
20
|
+
.intersect(sdf.sphere(25))
|
|
21
|
+
.toShape();
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`cellSize`: larger = coarser. `thickness`: thicker = denser.
|
|
25
|
+
|
|
26
|
+
## Domain Operations
|
|
27
|
+
|
|
28
|
+
### Twist
|
|
29
|
+
Rotates slices around Y axis as function of Y position.
|
|
30
|
+
```javascript
|
|
31
|
+
sdf.cylinder(50, 8).twist(0.9).toShape() // 45° total over 50mm (0.9 deg/mm)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Bend
|
|
35
|
+
Bends around Z axis. Smaller radius = tighter arc.
|
|
36
|
+
```javascript
|
|
37
|
+
sdf.cylinder(60, 5).bend(20).toShape() // arch shape
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Repeat
|
|
41
|
+
Tiles in space. Spacing `0` = no repeat on that axis. Count `0` = infinite.
|
|
42
|
+
```javascript
|
|
43
|
+
sdf.sphere(4).repeat([15, 15, 0], [3, 3, 0]).toShape() // 3×3 grid
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Shell
|
|
47
|
+
Hollows to surface shell of given thickness.
|
|
48
|
+
```javascript
|
|
49
|
+
sdf.sphere(20).shell(2).subtract(sdf.box(60, 60, 30).translate(0, 0, -15)).toShape()
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Displace
|
|
53
|
+
Offsets surface by a function of position.
|
|
54
|
+
```javascript
|
|
55
|
+
sdf.sphere(15).displace((x, y, z) => Math.sin(x * 0.8) * Math.sin(y * 0.8) * Math.sin(z * 0.8) * 2).toShape()
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Critical gotcha for `displace()` and `fromFunction()`:** The function is serialized as a string and re-evaluated via `new Function("x","y","z", "return ...")`. This means:
|
|
59
|
+
- **No closure variables** — cannot reference `param()` values inside
|
|
60
|
+
- **Single expression only** — no `const`, `let`, `if`, `for`
|
|
61
|
+
- Multi-statement blocks **silently produce bad geometry**
|
|
62
|
+
|
|
63
|
+
### Onion
|
|
64
|
+
Concentric shells.
|
|
65
|
+
```javascript
|
|
66
|
+
sdf.sphere(20).onion(3, 2).toShape() // 3 shells, 2mm apart
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Morphing
|
|
70
|
+
|
|
71
|
+
Interpolate between two shapes. `t=0` = a, `t=1` = b.
|
|
72
|
+
```javascript
|
|
73
|
+
sdf.morph(sdf.sphere(12), sdf.box(20, 20, 20), 0.5).toShape()
|
|
74
|
+
a.morph(b, t) // method form
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Custom SDF Functions
|
|
78
|
+
|
|
79
|
+
```javascript
|
|
80
|
+
sdf.fromFunction(fn, { min: [x0, y0, z0], max: [x1, y1, z1] })
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`fn(x, y, z)` returns signed distance: negative = inside, positive = outside, zero = surface. Bounds are required — provide tightly to avoid wasted computation.
|
|
84
|
+
|
|
85
|
+
```javascript
|
|
86
|
+
const heart = sdf.fromFunction(
|
|
87
|
+
(x, y, z) => (x*x + z*z*1.1 + y*y - 1)**3 - x*x*y*y*y - z*z*y*y*y * 0.11,
|
|
88
|
+
{ min: [-20, -25, -15], max: [20, 20, 15] }
|
|
89
|
+
).toShape();
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Same serialization gotcha as `displace()` — no closure variables, single expression only.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
skill-group: dev-sdf
|
|
3
|
+
skill-order: 1
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# SDF Primitives & Booleans
|
|
7
|
+
|
|
8
|
+
> **Experimental.** Slower render times than B-rep; lower mesh quality from marching-tetrahedra extraction. Use for organic forms, TPMS lattices, smooth blending. For mechanical parts, prefer B-rep.
|
|
9
|
+
|
|
10
|
+
SDF operations are accessed via the globally available `sdf` namespace. All shapes live as a lazy expression tree until `.toShape()` is called.
|
|
11
|
+
|
|
12
|
+
## Primitives
|
|
13
|
+
|
|
14
|
+
All centered at origin unless noted.
|
|
15
|
+
|
|
16
|
+
```javascript
|
|
17
|
+
sdf.sphere(radius)
|
|
18
|
+
sdf.box(x, y, z) // full dimensions — box(20,20,20) → 20mm cube
|
|
19
|
+
sdf.cylinder(height, radius) // axis along Y (rotate 90,0,0 for Z-axis)
|
|
20
|
+
sdf.torus(majorRadius, minorRadius) // ring in XZ plane (hole axis = Y)
|
|
21
|
+
sdf.capsule(height, radius) // axis along Y
|
|
22
|
+
sdf.cone(height, radius) // base at y=0, tip at y=height (not centered)
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Boolean Operations
|
|
26
|
+
|
|
27
|
+
```javascript
|
|
28
|
+
// Sharp
|
|
29
|
+
a.union(b, c, d); a.subtract(b); a.intersect(b)
|
|
30
|
+
|
|
31
|
+
// Smooth — blend over transition radius
|
|
32
|
+
sdf.smoothUnion(a, b, { radius: 5 }) // factory: radius in options object
|
|
33
|
+
sdf.smoothDifference(a, b, { radius: 5 })
|
|
34
|
+
sdf.smoothIntersection(a, b, { radius: 5 })
|
|
35
|
+
|
|
36
|
+
a.smoothUnion(b, 5) // method: radius as direct number
|
|
37
|
+
a.smoothSubtract(b, 5) // note: smoothSubtract, not smoothDifference
|
|
38
|
+
a.smoothIntersect(b, 5)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**API mismatch:** Factory functions take `{ radius }` (object). Instance methods take `radius` (number). Don't mix them.
|
|
42
|
+
|
|
43
|
+
## Transforms
|
|
44
|
+
|
|
45
|
+
```javascript
|
|
46
|
+
shape.translate(x, y, z)
|
|
47
|
+
shape.rotateX(deg) // also rotateY, rotateZ, rotate(axis, deg)
|
|
48
|
+
shape.scale(factor) // uniform only
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Reserved Names
|
|
52
|
+
|
|
53
|
+
The global scope defines `sphere`, `box`, `cylinder`, `torus`, `capsule`, `cone`, `shell` as B-rep primitives. Always use `sdf.*` prefix — and avoid naming local variables after these globals:
|
|
54
|
+
|
|
55
|
+
```javascript
|
|
56
|
+
const orb = sdf.sphere(10); // correct — not 'sphere'
|
|
57
|
+
const s = sdf.sphere(10); // bad — shadows global 'sphere' if named 'sphere'
|
|
58
|
+
```
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
skill-group: dev-sdf
|
|
3
|
+
skill-order: 3
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# SDF Workflow & Mesh Quality
|
|
7
|
+
|
|
8
|
+
## Converting to Shape
|
|
9
|
+
|
|
10
|
+
```javascript
|
|
11
|
+
shape.toShape()
|
|
12
|
+
shape.toShape({ edgeLength: 0.5 }) // finer mesh
|
|
13
|
+
shape.toShape({ bounds: { min: [...], max: [...] } }) // override auto bounds
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
| Option | Default | Effect |
|
|
17
|
+
|--------|---------|--------|
|
|
18
|
+
| `edgeLength` | `maxDim / 100` | Smaller = smoother, slower |
|
|
19
|
+
| `bounds` | auto-estimated | Override sampling volume |
|
|
20
|
+
|
|
21
|
+
For smooth shapes use `0.3–0.5`. For fast previews use `1–2`.
|
|
22
|
+
|
|
23
|
+
After meshing, Laplacian smoothing + SDF projection runs automatically (2 iterations) to reduce axis-aligned triangle artifacts.
|
|
24
|
+
|
|
25
|
+
## Workflow Tips
|
|
26
|
+
|
|
27
|
+
- **Start coarse, refine last** — design at `edgeLength: 2`, drop to `0.5` for output
|
|
28
|
+
- **Clip TPMS before `toShape()`** — intersect with bounding shape first
|
|
29
|
+
- **Compose before meshing** — do all booleans/deformations in SDF space, then call `.toShape()` once
|
|
30
|
+
- **Mix with B-rep freely** — after `.toShape()`, use in `difference()`, `union()`, `.fillet()`, etc.
|
|
31
|
+
|
|
32
|
+
```javascript
|
|
33
|
+
const organicBase = sdf.smoothUnion(sdf.sphere(20), sdf.box(30, 30, 10), { radius: 8 }).toShape();
|
|
34
|
+
return difference(organicBase, cylinder(15, 3)); // B-rep cut on SDF result
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Serialization Gotcha
|
|
38
|
+
|
|
39
|
+
`displace()` and `fromFunction()` serialize the function body via `new Function("x","y","z", "return ...")`:
|
|
40
|
+
- **No closure variables** — cannot reference `param()` values
|
|
41
|
+
- **Single expression only** — no `const`, `let`, `if`, `for`
|
|
42
|
+
- Multi-statement blocks silently produce bad geometry
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
---
|
|
2
|
+
skill-group: cli
|
|
3
|
+
skill-order: 3
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# ForgeCAD CLI: Export Commands
|
|
7
|
+
|
|
8
|
+
## SVG Export
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
forgecad export svg examples/constraints/01-fully-constrained-rect.forge.js [output.svg]
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Pure Node — no browser needed. Runs the sketch script through the forge engine and converts polygons to SVG paths.
|
|
15
|
+
|
|
16
|
+
## STEP / BREP Export (exact subset, CadQuery)
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
forgecad export step examples/api/brep-exportable.forge.js [--output out/demo.step] [--python 3.11] [--uv /path/to/uv]
|
|
20
|
+
forgecad export brep examples/api/brep-exportable.forge.js
|
|
21
|
+
forgecad export step examples/chess-set.forge.js --allow-faceted
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`uv`-first: provisions CadQuery automatically. Exact-subset only by default — fails with a reason rather than silently exporting degraded geometry. With `--allow-faceted`, unsupported mesh solids export as faceted OCCT solids (tessellation-driven, not exact replay).
|
|
25
|
+
|
|
26
|
+
The maintained feature matrix: `docs/permanent/API/output/brep-export.md`.
|
|
27
|
+
|
|
28
|
+
## G-code Toolpath Export
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
forgecad export gcode examples/gcode/parametric-vase.forge.js [--output out/vase.gcode]
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The script must return a `GCodeBuilder` (from the `gcode()` factory). This is a toolpath scripting API, not a slicer — you define print movements in code. See `docs/permanent/API/output/gcode.md` for the full API.
|
|
35
|
+
|
|
36
|
+
## SDF Robot Export (Gazebo)
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
forgecad export sdf examples/api/sdf-rover-demo.forge.js [--output out/forge_scout]
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Writes a Gazebo-friendly package: `model.sdf`, `model.config`, `meshes/*.stl`, optional world file. Script must call `robotExport({...})` with an `assembly(...)` graph.
|
|
43
|
+
|
|
44
|
+
Launch flow (macOS — use split `-s`/`-g`):
|
|
45
|
+
```bash
|
|
46
|
+
export GZ_SIM_RESOURCE_PATH="$PWD/out/forge_scout/models${GZ_SIM_RESOURCE_PATH:+:$GZ_SIM_RESOURCE_PATH}"
|
|
47
|
+
gz sim -s -r out/forge_scout/worlds/forge_scout_trial.sdf
|
|
48
|
+
gz sim -g out/forge_scout/worlds/forge_scout_trial.sdf
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## URDF Export
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
forgecad export urdf examples/api/sdf-rover-demo.forge.js
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## PNG Render (requires Chrome/Puppeteer)
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
forgecad render examples/cup.forge.js [output.png]
|
|
61
|
+
forgecad render examples/cup.forge.js out/scene.png --scene '<json from viewport>'
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Options: `--angles <front,side,top,iso>`, `--size <px>`, `--port <n>`, `--camera <spec>`, `--scene <json>`, `--background <color>`, `--chrome-path <path>`.
|
|
65
|
+
|
|
66
|
+
## Animated Capture (GIF or MP4, requires Chrome)
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
forgecad capture gif examples/cup.forge.js [output.gif]
|
|
70
|
+
forgecad capture mp4 examples/cup.forge.js [output.mp4]
|
|
71
|
+
forgecad capture mp4 examples/api/runtime-joints-view.forge.js out/step.mp4 --capture animation --animation Step
|
|
72
|
+
forgecad capture gif examples/3d-printer.forge.js out/section.gif --cut-plane "Front Section"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`--list` prints available animation clips and cut planes. Uses `ffmpeg` when available (better GIF colors, H.264 MP4); falls back to pure-JS GIF encoder.
|
|
76
|
+
|
|
77
|
+
Key options: `--capture <orbit|animation>`, `--animation <name>`, `--cut-plane <name>`, `--camera <spec>`, `--scene <json>`, `--size <px>`, `--fps <n>`, `--frames-per-turn <n>`, `--quality <default|live|high>`.
|
|
78
|
+
|
|
79
|
+
Use `Copy CLI --scene` from the View Panel to grab the current viewport framing and paste into `render` or `capture`.
|
|
80
|
+
|
|
81
|
+
## PDF Report
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
forgecad export report examples/cup.forge.js [output.pdf] [--dim-angle-tol 18]
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Generates a searchable PDF with BOM page, combined model page, and per-component pages. Dimensions included per view when their axis aligns with that view's projection plane (within `--dim-angle-tol` degrees, default 12).
|
|
88
|
+
|
|
89
|
+
## STL Export
|
|
90
|
+
|
|
91
|
+
Available in the browser UI via the Export panel (binary STL).
|