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
|
@@ -1,1666 +0,0 @@
|
|
|
1
|
-
**Important**: See [colors-and-unions.md](colors-and-unions.md) for a crucial guide on preserving colors when returning multiple objects vs. using `union()`.
|
|
2
|
-
|
|
3
|
-
# ForgeCAD API Reference
|
|
4
|
-
|
|
5
|
-
**For AI Agents**: This document contains everything needed to write parametric CAD code in ForgeCAD.
|
|
6
|
-
|
|
7
|
-
## Core Concepts
|
|
8
|
-
|
|
9
|
-
ForgeCAD scripts are JavaScript code that returns geometry. The forge API is globally available — no imports needed.
|
|
10
|
-
|
|
11
|
-
### Basic Structure
|
|
12
|
-
```javascript
|
|
13
|
-
// 1. Declare parameters (creates UI sliders)
|
|
14
|
-
const width = param("Width", 50, { min: 20, max: 100, unit: "mm" });
|
|
15
|
-
|
|
16
|
-
// 2. Create geometry
|
|
17
|
-
const shape = box(width, 30, 10);
|
|
18
|
-
|
|
19
|
-
// 3. Return the final shape
|
|
20
|
-
return shape;
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
### Execution Model
|
|
24
|
-
- Scripts execute on every parameter change (400ms debounce)
|
|
25
|
-
- All operations are **immutable** — they return new shapes, never modify in place
|
|
26
|
-
- Must return one of:
|
|
27
|
-
- A `Shape` (3D solid)
|
|
28
|
-
- A `Sketch` (2D profile — rendered flat on XY plane)
|
|
29
|
-
- A `TrackedShape` (3D solid with named faces/edges — auto-unwrapped)
|
|
30
|
-
- A `ShapeGroup` (multiple shapes/sketches grouped for joint transforms)
|
|
31
|
-
- An `Array` of shapes/sketches/groups (multi-object scene)
|
|
32
|
-
- An `Array` of `{ name, shape?, sketch?, color? }` objects (named multi-object scene)
|
|
33
|
-
|
|
34
|
-
### Runtime vs Module Imports
|
|
35
|
-
- In Forge script runtime (editor / `runScript`), `box()` and `cylinder()` return `TrackedShape` wrappers with topology names.
|
|
36
|
-
- In direct TypeScript imports from `src/forge/headless.ts`, `box()` and `cylinder()` return kernel `Shape`.
|
|
37
|
-
- If you need tracked topology in direct imports, use `trackedBox()` / `trackedCylinder()` or entity extrusion (`rectangle(...).extrude(...)`, `circle(...).extrude(...)`).
|
|
38
|
-
|
|
39
|
-
### ⚠️ Important: Unions Remove Colors
|
|
40
|
-
|
|
41
|
-
When you use `union()` to combine shapes, the result becomes a single solid mesh with only one color. Individual colors assigned to the original shapes are lost:
|
|
42
|
-
|
|
43
|
-
```javascript
|
|
44
|
-
// ❌ BAD: Colors are lost after union!
|
|
45
|
-
const red = box(30, 30, 30).color('#ff0000');
|
|
46
|
-
const blue = box(20, 20, 20).translate(30, 0, 0).color('#0066ff');
|
|
47
|
-
return union(red, blue); // Result is all one color (red)
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
**Solution**: Return objects as a composite response instead:
|
|
51
|
-
|
|
52
|
-
```javascript
|
|
53
|
-
// ✅ GOOD: Each object keeps its color
|
|
54
|
-
const red = box(30, 30, 30).color('#ff0000');
|
|
55
|
-
const blue = box(20, 20, 20).translate(30, 0, 0).color('#0066ff');
|
|
56
|
-
|
|
57
|
-
// Return as named objects to preserve individual colors and materials
|
|
58
|
-
return [
|
|
59
|
-
{ "label": red }, // Each gets its own color, toggle, and controls
|
|
60
|
-
{ "label": blue }
|
|
61
|
-
];
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
Each object in the array gets its own visibility toggle, opacity control, and color picker in the View Panel.
|
|
65
|
-
|
|
66
|
-
See [colors-and-unions.md](colors-and-unions.md) for complete details on when to union vs. return separate objects.
|
|
67
|
-
|
|
68
|
-
### Coordinate System
|
|
69
|
-
ForgeCAD uses **Z-up** right-handed coordinates:
|
|
70
|
-
- **X** = left/right
|
|
71
|
-
- **Y** = forward/back
|
|
72
|
-
- **Z** = up/down
|
|
73
|
-
|
|
74
|
-
See [coordinate-system.md](coordinate-system.md) for view mapping details.
|
|
75
|
-
|
|
76
|
-
## Parameters
|
|
77
|
-
|
|
78
|
-
For the full parameter guide, including overrides and dropdown parameters, see [core/parameters.md](core/parameters.md).
|
|
79
|
-
|
|
80
|
-
### `param(name, default, options?)`
|
|
81
|
-
Declares a parameter and creates a UI slider.
|
|
82
|
-
|
|
83
|
-
**Parameters:**
|
|
84
|
-
- `name` (string) - Display name in UI
|
|
85
|
-
- `default` (number) - Initial value
|
|
86
|
-
- `options` (object, optional):
|
|
87
|
-
- `min` (number) - Minimum value (default: 0)
|
|
88
|
-
- `max` (number) - Maximum value (default: default * 4)
|
|
89
|
-
- `step` (number) - Slider increment (auto-calculated if not provided)
|
|
90
|
-
- `unit` (string) - Display unit like "mm", "°", "%"
|
|
91
|
-
- `integer` (boolean) - If true, value is always rounded to whole number. Step defaults to 1. Use for counts, quantities, sides, etc.
|
|
92
|
-
|
|
93
|
-
**Returns:** Current parameter value (number)
|
|
94
|
-
|
|
95
|
-
**Examples:**
|
|
96
|
-
```javascript
|
|
97
|
-
const width = param("Width", 50);
|
|
98
|
-
const angle = param("Angle", 45, { min: 0, max: 180, unit: "°" });
|
|
99
|
-
const thick = param("Thickness", 2, { min: 0.5, max: 10, step: 0.5, unit: "mm" });
|
|
100
|
-
const count = param("Count", 5, { min: 1, max: 20, integer: true });
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
### `boolParam(name, default)`
|
|
104
|
-
Declares a boolean parameter and creates a UI checkbox.
|
|
105
|
-
|
|
106
|
-
**Parameters:**
|
|
107
|
-
- `name` (string) - Display name in UI
|
|
108
|
-
- `default` (boolean) - Initial checkbox state
|
|
109
|
-
|
|
110
|
-
**Returns:** Current parameter value (boolean)
|
|
111
|
-
|
|
112
|
-
**Example:**
|
|
113
|
-
```javascript
|
|
114
|
-
const showLid = boolParam("Show Lid", true);
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
### `choiceParam(name, default, choices)`
|
|
118
|
-
Declares a string choice parameter and creates a UI dropdown.
|
|
119
|
-
|
|
120
|
-
**Parameters:**
|
|
121
|
-
- `name` (string) - Display name in UI
|
|
122
|
-
- `default` (string) - Initial selected label. Must match one of `choices`
|
|
123
|
-
- `choices` (string[]) - Allowed labels in the dropdown
|
|
124
|
-
|
|
125
|
-
**Returns:** Current selected label (string)
|
|
126
|
-
|
|
127
|
-
**Example:**
|
|
128
|
-
```javascript
|
|
129
|
-
const panStyle = choiceParam("Pan Style", "frying-pan", [
|
|
130
|
-
"frying-pan",
|
|
131
|
-
"saute-pan",
|
|
132
|
-
"saucepan",
|
|
133
|
-
"wok",
|
|
134
|
-
]);
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
## Colors
|
|
138
|
-
|
|
139
|
-
Both Shape and Sketch support colors via `.color()`:
|
|
140
|
-
|
|
141
|
-
```javascript
|
|
142
|
-
const red = box(50, 50, 50).color('#ff0000');
|
|
143
|
-
const blue = circle2d(25).color('#0066ff');
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
Colors are preserved through transforms and boolean operations (the first operand's color wins).
|
|
147
|
-
|
|
148
|
-
When returning multiple objects, colors can also be set per-object:
|
|
149
|
-
|
|
150
|
-
```javascript
|
|
151
|
-
return [
|
|
152
|
-
{ name: "Base", shape: box(100, 100, 5), color: "#888888" },
|
|
153
|
-
{ name: "Column", shape: cylinder(50, 10).translate(50, 50, 5), color: "#4488cc" },
|
|
154
|
-
];
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
## Cut Planes
|
|
158
|
-
|
|
159
|
-
### `cutPlane(name, normal, offset?)`
|
|
160
|
-
Defines a named section plane for inspection. Appears as a toggle in the View Panel. When enabled, geometry on one side of the plane is clipped away, revealing the interior.
|
|
161
|
-
|
|
162
|
-
**Parameters:**
|
|
163
|
-
- `name` (string) - Display name in View Panel
|
|
164
|
-
- `normal` ([number, number, number]) - Direction vector pointing toward the side that gets removed
|
|
165
|
-
- `offset` (number, optional) - Distance from origin along the normal where the cut happens. Default: 0
|
|
166
|
-
|
|
167
|
-
**Returns:** void (side effect: registers the plane for UI toggle)
|
|
168
|
-
|
|
169
|
-
**Examples:**
|
|
170
|
-
```javascript
|
|
171
|
-
// Horizontal section at Z=30 — removes everything above
|
|
172
|
-
cutPlane("Top Section", [0, 0, 1], 30);
|
|
173
|
-
|
|
174
|
-
// Vertical section at Y=0 — removes the front half
|
|
175
|
-
cutPlane("Front Section", [0, -1, 0], 0);
|
|
176
|
-
|
|
177
|
-
// Diagonal cut
|
|
178
|
-
cutPlane("Diagonal", [1, 1, 0], 20);
|
|
179
|
-
|
|
180
|
-
// Parametric cut position
|
|
181
|
-
const cutZ = param("Cut Height", 10, { min: -50, max: 50, unit: "mm" });
|
|
182
|
-
cutPlane("Horizontal", [0, 0, 1], cutZ);
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
**How it works:**
|
|
186
|
-
- Cut planes are GPU-accelerated (Three.js clipping planes) — instant on any geometry complexity
|
|
187
|
-
- Multiple planes can be defined and toggled independently
|
|
188
|
-
- Planes are per-script — they reset on each execution
|
|
189
|
-
- Toggle state persists in the UI across parameter changes
|
|
190
|
-
- Active planes can be visualized with built-in viewport guides (no model geometry required)
|
|
191
|
-
- `Show guides` toggles renderer-side plane visuals
|
|
192
|
-
- `Fill` + `Opacity` controls translucent section fill
|
|
193
|
-
- `Border` toggles plane outline
|
|
194
|
-
- `Normal axis` shows orientation direction (the clipped side points along the plane normal)
|
|
195
|
-
|
|
196
|
-
**Use cases:**
|
|
197
|
-
- Inspect internal features (holes, cavities, wall thickness)
|
|
198
|
-
- Verify alignment of hidden parts
|
|
199
|
-
- Create section views for documentation
|
|
200
|
-
- Debug boolean operation results
|
|
201
|
-
|
|
202
|
-
See `examples/api/section-plane-visualization.forge.js` for a focused multi-plane setup.
|
|
203
|
-
|
|
204
|
-
## View Explode Overrides
|
|
205
|
-
|
|
206
|
-
### `explodeView(options?)`
|
|
207
|
-
Overrides default viewport exploded-view behavior. The View Panel explode slider is always available; this API only changes how the slider is interpreted for the current script.
|
|
208
|
-
|
|
209
|
-
**Parameters:**
|
|
210
|
-
- `options` (object, optional):
|
|
211
|
-
- `enabled` (boolean) - Set `false` to disable viewport explode offsets for this script.
|
|
212
|
-
- `amountScale` (number) - Multiplies the UI explode amount.
|
|
213
|
-
- `mode` (`'radial' | 'x' | 'y' | 'z' | [x, y, z]`) - Global default direction.
|
|
214
|
-
- `axisLock` (`'x' | 'y' | 'z'`) - Global axis lock.
|
|
215
|
-
- `byName` (`Record<string, { stage?, direction?, axisLock? }>`)- Per-object overrides by final object name.
|
|
216
|
-
|
|
217
|
-
**Returns:** `void` (side effect: registers view behavior for this run)
|
|
218
|
-
|
|
219
|
-
```javascript
|
|
220
|
-
explodeView({
|
|
221
|
-
amountScale: 1.2,
|
|
222
|
-
mode: 'radial',
|
|
223
|
-
byName: {
|
|
224
|
-
"Shaft": { direction: [1, 0, 0], stage: 1.6 },
|
|
225
|
-
"Housing": { stage: 0.4 },
|
|
226
|
-
},
|
|
227
|
-
});
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
```javascript
|
|
231
|
-
// Disable global explode offsets for this model
|
|
232
|
-
explodeView({ enabled: false });
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
## Bill of Materials
|
|
236
|
-
|
|
237
|
-
### `bom(quantity, description, opts?)`
|
|
238
|
-
Registers a bill-of-materials entry for report export. Use this for real-world parts/materials that cannot be inferred from geometry alone.
|
|
239
|
-
|
|
240
|
-
**Parameters:**
|
|
241
|
-
- `quantity` (number) - Amount to add (must be finite and `>= 0`). `0` is ignored.
|
|
242
|
-
- `description` (string) - Human-readable item description.
|
|
243
|
-
- `opts` (object, optional):
|
|
244
|
-
- `unit` (string) - Unit label such as `"mm"`, `"pieces"`, `"kg"` (default: `"pieces"`)
|
|
245
|
-
- `key` (string) - Explicit aggregation key. Use this when descriptions vary but should still sum to one line item.
|
|
246
|
-
|
|
247
|
-
**Returns:** `void` (side effect: registers BOM item for report generation)
|
|
248
|
-
|
|
249
|
-
**Examples:**
|
|
250
|
-
```javascript
|
|
251
|
-
const tubeLen = param("Tube Length", 1200, { min: 300, max: 4000, unit: "mm" });
|
|
252
|
-
const tubeW = param("Tube Width", 30, { min: 10, max: 100, unit: "mm" });
|
|
253
|
-
const tubeH = param("Tube Height", 20, { min: 10, max: 100, unit: "mm" });
|
|
254
|
-
const boltCount = param("Bolt Count", 16, { min: 0, max: 200, integer: true });
|
|
255
|
-
const boltLength = param("Bolt Length", 16, { min: 6, max: 80, unit: "mm" });
|
|
256
|
-
|
|
257
|
-
bom(tubeLen, `iron tube with dimensions ${tubeW} x ${tubeH}`, { unit: "mm" });
|
|
258
|
-
bom(boltCount, `M4 bolt of ${boltLength} mm length`, { unit: "pieces" });
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
**Auto-summing behavior in report export:**
|
|
262
|
-
- Entries with the same normalized `description + unit` are summed into one row
|
|
263
|
-
- `key` overrides default grouping when you need custom merge behavior
|
|
264
|
-
- Summed rows are rendered on a dedicated **Bill of Materials** page in the generated PDF report
|
|
265
|
-
|
|
266
|
-
See `examples/api/bill-of-materials.forge.js` for a complete parametric example.
|
|
267
|
-
|
|
268
|
-
## Dimension Annotations
|
|
269
|
-
|
|
270
|
-
Dimension annotations are visual callouts for measurement/reporting.
|
|
271
|
-
They are **not constraints** and do not drive geometry.
|
|
272
|
-
|
|
273
|
-
### `dim(from, to, opts?)`
|
|
274
|
-
Adds a dimension annotation between two points.
|
|
275
|
-
|
|
276
|
-
**Parameters:**
|
|
277
|
-
- `from` (`[number, number] | [number, number, number] | Point2D`) - Start point
|
|
278
|
-
- `to` (`[number, number] | [number, number, number] | Point2D`) - End point
|
|
279
|
-
- `opts` (object, optional):
|
|
280
|
-
- `offset` (number) - Visual offset from geometry (default: `10`)
|
|
281
|
-
- `label` (string) - Override label text
|
|
282
|
-
- `color` (string) - Annotation color (hex)
|
|
283
|
-
- `component` (`string | string[]`) - Explicit report ownership target(s) by returned object name
|
|
284
|
-
- `currentComponent` (boolean) - Bind ownership to the current returned component instance (especially useful inside `importPart()` files)
|
|
285
|
-
|
|
286
|
-
**Returns:** `void` (side effect: registers dimension annotation)
|
|
287
|
-
|
|
288
|
-
```javascript
|
|
289
|
-
dim([0, 0, 0], [200, 0, 0], { label: "Width" });
|
|
290
|
-
dim([0, 0, 0], [0, 0, 50], { label: "Height", offset: 15, color: "#ffaa44" });
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
Ownership examples:
|
|
294
|
-
```javascript
|
|
295
|
-
// Own the dimension by the current imported instance (deterministic)
|
|
296
|
-
dim([0, 0, 0], [0, 80, 0], { label: "Leg Width", currentComponent: true });
|
|
297
|
-
|
|
298
|
-
// Route dimension to another named component page
|
|
299
|
-
dim([0, 0, 0], [0, 0, 18], { label: "Top Gap", component: "Tabletop" });
|
|
300
|
-
```
|
|
301
|
-
|
|
302
|
-
### `dimLine(line, opts?)`
|
|
303
|
-
Adds a dimension annotation along a `Line2D`.
|
|
304
|
-
|
|
305
|
-
**Parameters:**
|
|
306
|
-
- `line` (`Line2D`) - Source line
|
|
307
|
-
- `opts` (same as `dim`)
|
|
308
|
-
|
|
309
|
-
**Returns:** `void`
|
|
310
|
-
|
|
311
|
-
```javascript
|
|
312
|
-
const a = point(0, 0);
|
|
313
|
-
const b = point(120, 0);
|
|
314
|
-
dimLine(line(a, b), { label: "Span", offset: -12 });
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
### Report Ownership Behavior
|
|
318
|
-
- `component: "Name"` assigns to that returned object when the name resolves uniquely
|
|
319
|
-
- `currentComponent: true` assigns to the owning returned component instance
|
|
320
|
-
- If multiple owners are bound (for example `currentComponent: true` plus another component), the dimension is treated as shared and stays on the assembly overview page
|
|
321
|
-
- Without explicit ownership, report export falls back to automatic bbox-based inference
|
|
322
|
-
|
|
323
|
-
See `examples/api/dimensioned-bracket.forge.js` for baseline dimension usage.
|
|
324
|
-
|
|
325
|
-
## 3D Primitives
|
|
326
|
-
|
|
327
|
-
### `box(x, y, z, center?)`
|
|
328
|
-
Creates a rectangular box with named faces and edges.
|
|
329
|
-
|
|
330
|
-
**Parameters:**
|
|
331
|
-
- `x, y, z` (number) - Dimensions
|
|
332
|
-
- `center` (boolean, optional) - If true, centers at origin. Default: false (corner at origin)
|
|
333
|
-
|
|
334
|
-
**Returns (script runtime):** `TrackedShape` (with faces: top, bottom, side-left, side-right, side-top, side-bottom; edges: vert-bl, vert-br, vert-tr, vert-tl, etc.)
|
|
335
|
-
|
|
336
|
-
```javascript
|
|
337
|
-
const cube = box(50, 50, 50, true); // Centered cube
|
|
338
|
-
const plate = box(100, 80, 5); // Corner at origin
|
|
339
|
-
plate.face('top'); // FaceRef { normal, center }
|
|
340
|
-
plate.edge('vert-bl'); // EdgeRef { start, end }
|
|
341
|
-
```
|
|
342
|
-
|
|
343
|
-
### `cylinder(height, radius, radiusTop?, segments?, center?)`
|
|
344
|
-
Creates a cylinder or cone with named faces and edges.
|
|
345
|
-
|
|
346
|
-
**Parameters:**
|
|
347
|
-
- `height` (number) - Height along Z axis
|
|
348
|
-
- `radius` (number) - Bottom radius
|
|
349
|
-
- `radiusTop` (number, optional) - Top radius. If different from radius, creates a cone. Default: same as radius
|
|
350
|
-
- `segments` (number, optional) - Number of sides. Default: auto (smooth circle)
|
|
351
|
-
- `center` (boolean, optional) - If true, centers along Z. Default: false
|
|
352
|
-
|
|
353
|
-
**Returns (script runtime):** `TrackedShape` (with faces: top, bottom, side; edges: top-rim, bottom-rim)
|
|
354
|
-
|
|
355
|
-
```javascript
|
|
356
|
-
const cyl = cylinder(50, 10); // Cylinder
|
|
357
|
-
const cone = cylinder(50, 20, 5); // Cone (tapered)
|
|
358
|
-
const hex = cylinder(10, 15, 15, 6); // Hexagonal prism
|
|
359
|
-
cyl.face('top'); // FaceRef
|
|
360
|
-
cyl.face('side'); // FaceRef
|
|
361
|
-
```
|
|
362
|
-
|
|
363
|
-
### `trackedBox(x, y, z, center?)` / `trackedCylinder(...)`
|
|
364
|
-
Explicit tracked primitive constructors for direct module imports (`src/forge/headless.ts`).
|
|
365
|
-
|
|
366
|
-
```typescript
|
|
367
|
-
import { init, trackedBox, trackedCylinder } from './src/forge/headless';
|
|
368
|
-
await init();
|
|
369
|
-
const body = trackedBox(100, 60, 30, true);
|
|
370
|
-
const pin = trackedCylinder(20, 3).attachTo(body, 'right', 'left');
|
|
371
|
-
```
|
|
372
|
-
|
|
373
|
-
### `sphere(radius, segments?)`
|
|
374
|
-
Creates a sphere.
|
|
375
|
-
|
|
376
|
-
**Parameters:**
|
|
377
|
-
- `radius` (number) - Sphere radius
|
|
378
|
-
- `segments` (number, optional) - Tessellation detail. Default: auto (smooth)
|
|
379
|
-
|
|
380
|
-
**Returns:** `Shape`
|
|
381
|
-
|
|
382
|
-
```javascript
|
|
383
|
-
const ball = sphere(25);
|
|
384
|
-
const lowPoly = sphere(25, 8); // Octahedron-like
|
|
385
|
-
```
|
|
386
|
-
|
|
387
|
-
## 3D Transforms
|
|
388
|
-
|
|
389
|
-
All transforms are **chainable** and **immutable** (return new shapes).
|
|
390
|
-
|
|
391
|
-
### `.clone()` / `.duplicate()`
|
|
392
|
-
Create an explicit copy handle of a shape (same geometry/color) so you can branch variants clearly.
|
|
393
|
-
|
|
394
|
-
```javascript
|
|
395
|
-
const bracket = box(60, 20, 8);
|
|
396
|
-
const left = bracket.clone().translate(-40, 0, 0);
|
|
397
|
-
const right = bracket.duplicate().translate(40, 0, 0);
|
|
398
|
-
```
|
|
399
|
-
|
|
400
|
-
### `.translate(x, y, z)`
|
|
401
|
-
Moves the shape relative to its current position.
|
|
402
|
-
|
|
403
|
-
```javascript
|
|
404
|
-
const moved = box(10, 10, 10).translate(50, 0, 0);
|
|
405
|
-
```
|
|
406
|
-
|
|
407
|
-
### `.moveTo(x, y, z)`
|
|
408
|
-
Positions the shape so its bounding box min corner is at the given global coordinate.
|
|
409
|
-
|
|
410
|
-
```javascript
|
|
411
|
-
// Place a box at exactly (100, 50, 0) in world space
|
|
412
|
-
const placed = box(30, 30, 10).moveTo(100, 50, 0);
|
|
413
|
-
```
|
|
414
|
-
|
|
415
|
-
### `.moveToLocal(target, x, y, z)`
|
|
416
|
-
Positions the shape relative to another shape's local coordinate system (bounding box min corner).
|
|
417
|
-
|
|
418
|
-
**Parameters:**
|
|
419
|
-
- `target` (Shape | TrackedShape) — The reference shape
|
|
420
|
-
- `x, y, z` (number) — Offset from target's bounding box min corner
|
|
421
|
-
|
|
422
|
-
```javascript
|
|
423
|
-
const base = box(100, 100, 10);
|
|
424
|
-
const part = box(20, 20, 30);
|
|
425
|
-
|
|
426
|
-
// Place part at (10, 10, 10) relative to base's origin corner
|
|
427
|
-
const placed = part.moveToLocal(base, 10, 10, 10);
|
|
428
|
-
```
|
|
429
|
-
|
|
430
|
-
### `.rotate(x, y, z)`
|
|
431
|
-
Rotates using Euler angles in **degrees** around the shape's bounding-box center.
|
|
432
|
-
|
|
433
|
-
**Parameters:**
|
|
434
|
-
- `x, y, z` (number) - Rotation in degrees around each axis
|
|
435
|
-
|
|
436
|
-
```javascript
|
|
437
|
-
const rotated = box(50, 20, 10).rotate(0, 0, 45); // 45° around Z
|
|
438
|
-
const tilted = cylinder(50, 10).rotate(90, 0, 0); // Lay on side
|
|
439
|
-
```
|
|
440
|
-
|
|
441
|
-
### `.scale(v)`
|
|
442
|
-
Scales the shape from its bounding-box center.
|
|
443
|
-
|
|
444
|
-
**Parameters:**
|
|
445
|
-
- `v` (number | [number, number, number]) - Uniform scale or per-axis scale
|
|
446
|
-
|
|
447
|
-
```javascript
|
|
448
|
-
const bigger = sphere(10).scale(2); // 2x larger
|
|
449
|
-
const stretched = box(10, 10, 10).scale([2, 1, 0.5]); // Non-uniform
|
|
450
|
-
```
|
|
451
|
-
|
|
452
|
-
### `.mirror(normal)`
|
|
453
|
-
Mirrors across a plane defined by its normal vector, passing through the shape's bounding-box center.
|
|
454
|
-
|
|
455
|
-
**Parameters:**
|
|
456
|
-
- `normal` ([number, number, number]) - Plane normal (doesn't need to be unit length)
|
|
457
|
-
|
|
458
|
-
```javascript
|
|
459
|
-
const mirrored = shape.mirror([1, 0, 0]); // Mirror across YZ plane
|
|
460
|
-
```
|
|
461
|
-
|
|
462
|
-
### `.rotateAround(axis, angleDeg, pivot?)`
|
|
463
|
-
Rotates around an arbitrary axis through a pivot point.
|
|
464
|
-
|
|
465
|
-
**Parameters:**
|
|
466
|
-
- `axis` ([number, number, number]) - Rotation axis direction
|
|
467
|
-
- `angleDeg` (number) - Rotation angle in degrees
|
|
468
|
-
- `pivot` ([number, number, number], optional) - Pivot point. Default: origin
|
|
469
|
-
|
|
470
|
-
```javascript
|
|
471
|
-
// Rotate a door 45° around Z axis at the hinge position
|
|
472
|
-
const opened = door.rotateAround([0, 0, 1], 45, [hingeX, hingeY, 0]);
|
|
473
|
-
```
|
|
474
|
-
|
|
475
|
-
### `.pointAlong(direction)`
|
|
476
|
-
Reorients a shape so its primary axis (Z) points along the given direction. Useful for laying cylinders and extrusions along X or Y without thinking about Euler angles.
|
|
477
|
-
|
|
478
|
-
**Parameters:**
|
|
479
|
-
- `direction` ([number, number, number]) - Target direction vector
|
|
480
|
-
|
|
481
|
-
```javascript
|
|
482
|
-
// Lay a cylinder along the X axis
|
|
483
|
-
const axle = cylinder(100, 5).pointAlong([1, 0, 0]);
|
|
484
|
-
|
|
485
|
-
// Symmetric hinges pointing outward from center
|
|
486
|
-
const hingeL = cylinder(40, 5).pointAlong([-1, 0, 0]).translate(-50, 0, 0);
|
|
487
|
-
const hingeR = cylinder(40, 5).pointAlong([1, 0, 0]).translate(50, 0, 0);
|
|
488
|
-
```
|
|
489
|
-
|
|
490
|
-
### `Transform` primitives (for kinematic chains)
|
|
491
|
-
Use `Transform` when manual pivot math becomes hard to maintain.
|
|
492
|
-
|
|
493
|
-
```javascript
|
|
494
|
-
const T = Transform.identity()
|
|
495
|
-
.translate(0, 0, 120)
|
|
496
|
-
.rotateAxis([0, 0, 1], 35);
|
|
497
|
-
|
|
498
|
-
const p = T.point([10, 0, 0]); // transform a point
|
|
499
|
-
const v = T.vector([1, 0, 0]); // transform a direction (no translation)
|
|
500
|
-
```
|
|
501
|
-
|
|
502
|
-
Core methods:
|
|
503
|
-
- `Transform.identity()`
|
|
504
|
-
- `Transform.translation(x, y, z)`
|
|
505
|
-
- `Transform.rotationAxis(axis, angleDeg, pivot?)`
|
|
506
|
-
- `Transform.scale(v)`
|
|
507
|
-
- `T.mul(other)` (chain-composition order)
|
|
508
|
-
- `composeChain(a, b, c, ...)` explicit left-to-right chain composition
|
|
509
|
-
- `T.inverse()`
|
|
510
|
-
- `shape.transform(T)` / `trackedShape.transform(T)` / `group.transform(T)`
|
|
511
|
-
|
|
512
|
-
## Joints
|
|
513
|
-
|
|
514
|
-
### `joint(name, shape, pivot, opts?)`
|
|
515
|
-
Create a revolute (hinge) joint. Auto-creates a param slider and rotates the shape.
|
|
516
|
-
|
|
517
|
-
**Parameters:**
|
|
518
|
-
- `name` (string) - Display name for the angle parameter
|
|
519
|
-
- `shape` (Shape) - The shape to rotate
|
|
520
|
-
- `pivot` ([number, number, number]) - The pivot point
|
|
521
|
-
- `opts` (object, optional):
|
|
522
|
-
- `axis` ([number, number, number]) - Rotation axis. Default: [0, 0, 1] (Z axis)
|
|
523
|
-
- `min` (number) - Minimum angle. Default: 0
|
|
524
|
-
- `max` (number) - Maximum angle. Default: 180
|
|
525
|
-
- `default` (number) - Initial angle. Default: 0
|
|
526
|
-
- `unit` (string) - Display unit. Default: "°"
|
|
527
|
-
|
|
528
|
-
**Returns:** `Shape` (rotated by the current slider value)
|
|
529
|
-
|
|
530
|
-
```javascript
|
|
531
|
-
// One line: creates a "Lid Angle" slider and rotates the lid around the hinge
|
|
532
|
-
const openLid = joint("Lid Angle", lid, [0, boxDepth, boxHeight], {
|
|
533
|
-
axis: [1, 0, 0],
|
|
534
|
-
max: 120,
|
|
535
|
-
default: 45,
|
|
536
|
-
});
|
|
537
|
-
```
|
|
538
|
-
|
|
539
|
-
## Assembly Graph (Mechanisms)
|
|
540
|
-
|
|
541
|
-
See also: `assembly.md`.
|
|
542
|
-
|
|
543
|
-
### `assembly(name?)`
|
|
544
|
-
Creates an assembly container with named parts + joints.
|
|
545
|
-
|
|
546
|
-
```javascript
|
|
547
|
-
const mech = assembly("Two-Link Arm")
|
|
548
|
-
.addPart("base", box(80, 80, 20, true))
|
|
549
|
-
.addPart("link1", box(120, 24, 24).translate(0, -12, -12))
|
|
550
|
-
.addPart("link2", box(100, 20, 20).translate(0, -10, -10))
|
|
551
|
-
.addJoint("shoulder", "revolute", "base", "link1", {
|
|
552
|
-
axis: [0, 1, 0],
|
|
553
|
-
min: -30, max: 120, default: 20,
|
|
554
|
-
frame: Transform.identity().translate(0, 0, 20),
|
|
555
|
-
})
|
|
556
|
-
.addJoint("elbow", "revolute", "link1", "link2", {
|
|
557
|
-
axis: [0, 1, 0],
|
|
558
|
-
min: -20, max: 140, default: 40,
|
|
559
|
-
frame: Transform.identity().translate(120, 0, 0),
|
|
560
|
-
});
|
|
561
|
-
|
|
562
|
-
const solved = mech.solve();
|
|
563
|
-
return solved.toScene();
|
|
564
|
-
```
|
|
565
|
-
|
|
566
|
-
Key methods:
|
|
567
|
-
- `addPart(name, shape, { transform?, metadata? })`
|
|
568
|
-
- `addFrame(name, { transform? })` for virtual mechanism frames
|
|
569
|
-
- `addJoint(name, type, parent, child, opts)` where `type` is `'fixed' | 'revolute' | 'prismatic'`
|
|
570
|
-
- `addRevolute(...)`, `addPrismatic(...)`, `addFixed(...)` shorthand helpers
|
|
571
|
-
- `solve(state?)` with per-joint value overrides
|
|
572
|
-
- `sweepJoint(jointName, from, to, steps, baseState?, collisionOptions?)`
|
|
573
|
-
|
|
574
|
-
Solved assembly helpers:
|
|
575
|
-
- `solved.toScene()` for rendering
|
|
576
|
-
- `solved.collisionReport()` for interference checks
|
|
577
|
-
- `solved.minClearance(partA, partB, searchLength?)`
|
|
578
|
-
- `solved.bom()` / `solved.bomCsv()`
|
|
579
|
-
- `bomToCsv(rows)` (standalone helper)
|
|
580
|
-
|
|
581
|
-
## 3D Boolean Operations
|
|
582
|
-
|
|
583
|
-
### `union(...shapes)`
|
|
584
|
-
Combines shapes (additive).
|
|
585
|
-
|
|
586
|
-
```javascript
|
|
587
|
-
const combined = union(
|
|
588
|
-
box(50, 50, 10),
|
|
589
|
-
cylinder(20, 15).translate(25, 25, 10)
|
|
590
|
-
);
|
|
591
|
-
```
|
|
592
|
-
|
|
593
|
-
### `difference(...shapes)`
|
|
594
|
-
Subtracts shapes[1..n] from shapes[0].
|
|
595
|
-
|
|
596
|
-
```javascript
|
|
597
|
-
const plate = box(100, 100, 5);
|
|
598
|
-
const hole = cylinder(6, 10);
|
|
599
|
-
const result = difference(plate, hole.translate(50, 50, 0));
|
|
600
|
-
|
|
601
|
-
// Or using method syntax:
|
|
602
|
-
const result = plate.subtract(hole.translate(50, 50, 0));
|
|
603
|
-
```
|
|
604
|
-
|
|
605
|
-
### `intersection(...shapes)`
|
|
606
|
-
Keeps only overlapping volume.
|
|
607
|
-
|
|
608
|
-
```javascript
|
|
609
|
-
const overlap = intersection(
|
|
610
|
-
sphere(30),
|
|
611
|
-
box(40, 40, 40, true)
|
|
612
|
-
);
|
|
613
|
-
```
|
|
614
|
-
|
|
615
|
-
### Method Syntax
|
|
616
|
-
Shapes also have boolean methods:
|
|
617
|
-
|
|
618
|
-
```javascript
|
|
619
|
-
shape.add(other) // Same as union(shape, other)
|
|
620
|
-
shape.subtract(other) // Same as difference(shape, other)
|
|
621
|
-
shape.intersect(other) // Same as intersection(shape, other)
|
|
622
|
-
```
|
|
623
|
-
|
|
624
|
-
## Group
|
|
625
|
-
|
|
626
|
-
### `group(...items)`
|
|
627
|
-
Groups multiple shapes/sketches for joint transforms without merging them into a single mesh. Unlike `union`, colors and individual identities are preserved.
|
|
628
|
-
|
|
629
|
-
**Parameters:**
|
|
630
|
-
- `...items` (Shape | Sketch | TrackedShape) - Items to group
|
|
631
|
-
|
|
632
|
-
**Returns:** `ShapeGroup`
|
|
633
|
-
|
|
634
|
-
```javascript
|
|
635
|
-
const base = box(100, 100, 5).color('#888888');
|
|
636
|
-
const column = cylinder(40, 5).translate(50, 50, 5).color('#4488cc');
|
|
637
|
-
|
|
638
|
-
// Group them — they stay separate but transform together
|
|
639
|
-
const assembly = group(base, column).translate(200, 0, 0);
|
|
640
|
-
return assembly;
|
|
641
|
-
```
|
|
642
|
-
|
|
643
|
-
### ShapeGroup Methods
|
|
644
|
-
All transforms are chainable and return a new ShapeGroup:
|
|
645
|
-
|
|
646
|
-
```javascript
|
|
647
|
-
group.translate(x, y, z)
|
|
648
|
-
group.rotate(x, y, z)
|
|
649
|
-
group.scale(v)
|
|
650
|
-
group.mirror(normal)
|
|
651
|
-
group.color(hex) // applies to all children
|
|
652
|
-
group.clone()
|
|
653
|
-
group.duplicate() // alias
|
|
654
|
-
```
|
|
655
|
-
|
|
656
|
-
When a ShapeGroup is returned from a script, each child becomes a separate viewport object with its own visibility/color controls.
|
|
657
|
-
|
|
658
|
-
## Positioning
|
|
659
|
-
|
|
660
|
-
### Connectors + `.matchTo()` — Primary positioning method
|
|
661
|
-
|
|
662
|
-
Define named connectors on parts, then use `matchTo()` for automatic 6-DOF alignment. The child translates and rotates so its connector aligns with the target's — origins coincide, axes oppose (plug-in model).
|
|
663
|
-
|
|
664
|
-
**Connectors** are stable (don't shift when geometry changes), semantic (typed, gendered), oriented (origin + axis + up), and validated (`assembly.match()` checks compatibility).
|
|
665
|
-
|
|
666
|
-
```javascript
|
|
667
|
-
const shelf = box(200, 120, 10, true).withConnectors({
|
|
668
|
-
left_tab: connector.male("dovetail", { origin: [-100, 0, 0], axis: [-1, 0, 0] }),
|
|
669
|
-
right_tab: connector.male("dovetail", { origin: [100, 0, 0], axis: [1, 0, 0] }),
|
|
670
|
-
});
|
|
671
|
-
|
|
672
|
-
const panel = box(12, 120, 200, true).withConnectors({
|
|
673
|
-
shelf_0: connector.female("dovetail", { origin: [6, 0, -50], axis: [1, 0, 0] }),
|
|
674
|
-
});
|
|
675
|
-
|
|
676
|
-
// Align shelf's left_tab to panel's shelf_0
|
|
677
|
-
const placed = shelf.matchTo(panel, "left_tab", "shelf_0");
|
|
678
|
-
|
|
679
|
-
// Dictionary syntax for multiple pairs:
|
|
680
|
-
const placed2 = shelf.matchTo(panel, { left_tab: "shelf_0" });
|
|
681
|
-
```
|
|
682
|
-
|
|
683
|
-
**Connector utilities:**
|
|
684
|
-
- `shape.connectorDistance('a', 'b')` — distance between two connectors (derive dimensions)
|
|
685
|
-
- `shape.connectorMeasurements('name')` — custom metadata (diameter, thread pitch, etc.)
|
|
686
|
-
- `shape.connectorsByType('pipe')` — list all connectors of a given type
|
|
687
|
-
- `assembly.match("Child", "Parent", pairs)` — multi-pair matching with joint creation
|
|
688
|
-
|
|
689
|
-
### `.attachTo(target, targetAnchor, selfAnchor?, offset?)`
|
|
690
|
-
Quick bounding-box positioning. Fast to write but fragile — anchor points shift when geometry is filleted, chamfered, or booleaned. Prefer connectors for assembly interfaces.
|
|
691
|
-
|
|
692
|
-
Available on both `Shape` and `TrackedShape`.
|
|
693
|
-
|
|
694
|
-
**Anchor3D values:**
|
|
695
|
-
- `'center'` — bounding box center
|
|
696
|
-
- Face centers: `'front'` (−Y), `'back'` (+Y), `'left'` (−X), `'right'` (+X), `'top'` (+Z), `'bottom'` (−Z)
|
|
697
|
-
- Edge midpoints: `'front-left'`, `'top-right'`, `'bottom-back'`, etc.
|
|
698
|
-
- Corners: `'top-front-left'`, `'bottom-back-right'`, etc.
|
|
699
|
-
|
|
700
|
-
```javascript
|
|
701
|
-
const base = box(100, 100, 10);
|
|
702
|
-
const column = cylinder(50, 8);
|
|
703
|
-
const placed = column.attachTo(base, 'top', 'bottom');
|
|
704
|
-
```
|
|
705
|
-
|
|
706
|
-
### `.onFace(parent, face, opts?)`
|
|
707
|
-
Place a shape on a specific face of a parent shape. Good for surface details (vents, displays, buttons).
|
|
708
|
-
|
|
709
|
-
**Parameters:**
|
|
710
|
-
- `face` ('front' | 'back' | 'left' | 'right' | 'top' | 'bottom')
|
|
711
|
-
- `opts.u` / `opts.v` — offset within the face from center
|
|
712
|
-
- `opts.protrude` — how far the child sticks out
|
|
713
|
-
|
|
714
|
-
```javascript
|
|
715
|
-
const vent = box(80, 2, 12, true).color('#333')
|
|
716
|
-
.onFace(body, 'front', { v: -15, protrude: 2 });
|
|
717
|
-
```
|
|
718
|
-
|
|
719
|
-
## Advanced 3D Operations
|
|
720
|
-
|
|
721
|
-
### `levelSet(sdf, bounds, edgeLength, level?)`
|
|
722
|
-
Create a shape from a signed distance function (SDF). Positive = inside.
|
|
723
|
-
|
|
724
|
-
```javascript
|
|
725
|
-
const gyroid = levelSet(
|
|
726
|
-
([x, y, z]) => Math.sin(x) * Math.cos(y) + Math.sin(y) * Math.cos(z) + Math.sin(z) * Math.cos(x),
|
|
727
|
-
{ min: [-10, -10, -10], max: [10, 10, 10] },
|
|
728
|
-
0.5, // edge length (resolution)
|
|
729
|
-
);
|
|
730
|
-
```
|
|
731
|
-
|
|
732
|
-
### Smoothing
|
|
733
|
-
|
|
734
|
-
```javascript
|
|
735
|
-
// Mark edges for smoothing, then subdivide
|
|
736
|
-
const smooth = box(50, 50, 50, true)
|
|
737
|
-
.smoothOut(60) // edges sharper than 60° get smoothed
|
|
738
|
-
.refine(4); // subdivide 4 times
|
|
739
|
-
|
|
740
|
-
// Or refine by edge length / tolerance
|
|
741
|
-
shape.refineToLength(2); // max edge length 2mm
|
|
742
|
-
shape.refineToTolerance(0.1); // max deviation 0.1mm from smooth surface
|
|
743
|
-
```
|
|
744
|
-
|
|
745
|
-
### Cutting
|
|
746
|
-
|
|
747
|
-
```javascript
|
|
748
|
-
// Split by another shape → [inside, outside]
|
|
749
|
-
const [inside, outside] = shape.split(cutter);
|
|
750
|
-
|
|
751
|
-
// Split by infinite plane → [below, above]
|
|
752
|
-
const [below, above] = shape.splitByPlane([0, 0, 1], 10); // Z=10 plane
|
|
753
|
-
|
|
754
|
-
// Trim: keep only one side
|
|
755
|
-
const trimmed = shape.trimByPlane([0, 0, 1], 10);
|
|
756
|
-
```
|
|
757
|
-
|
|
758
|
-
### Plane Operations
|
|
759
|
-
|
|
760
|
-
```javascript
|
|
761
|
-
// Cross-section: intersect shape with a plane → Sketch
|
|
762
|
-
const section = intersectWithPlane(shape, { plane: 'XY', offset: 10 });
|
|
763
|
-
|
|
764
|
-
// Project: flatten shape onto a plane → Sketch
|
|
765
|
-
const shadow = projectToPlane(shape, { origin: [0, 0, 0], normal: [0, 0, 1] });
|
|
766
|
-
```
|
|
767
|
-
|
|
768
|
-
## 2D Sketches
|
|
769
|
-
|
|
770
|
-
Sketches are 2D profiles that can be extruded or revolved into 3D.
|
|
771
|
-
|
|
772
|
-
### 2D Primitives
|
|
773
|
-
|
|
774
|
-
#### `rect(width, height, center?)`
|
|
775
|
-
Rectangle.
|
|
776
|
-
|
|
777
|
-
```javascript
|
|
778
|
-
const r = rect(50, 30);
|
|
779
|
-
const centered = rect(50, 30, true);
|
|
780
|
-
```
|
|
781
|
-
|
|
782
|
-
#### `circle2d(radius, segments?)`
|
|
783
|
-
Circle.
|
|
784
|
-
|
|
785
|
-
```javascript
|
|
786
|
-
const c = circle2d(25);
|
|
787
|
-
const octagon = circle2d(25, 8);
|
|
788
|
-
```
|
|
789
|
-
|
|
790
|
-
#### `roundedRect(width, height, radius, center?)`
|
|
791
|
-
Rectangle with rounded corners.
|
|
792
|
-
|
|
793
|
-
```javascript
|
|
794
|
-
const rounded = roundedRect(60, 40, 5);
|
|
795
|
-
```
|
|
796
|
-
|
|
797
|
-
#### `polygon(points)`
|
|
798
|
-
Polygon from array of [x, y] points.
|
|
799
|
-
|
|
800
|
-
```javascript
|
|
801
|
-
const triangle = polygon([[0, 0], [50, 0], [25, 40]]);
|
|
802
|
-
```
|
|
803
|
-
|
|
804
|
-
#### `ngon(sides, radius)`
|
|
805
|
-
Regular polygon (equilateral).
|
|
806
|
-
|
|
807
|
-
```javascript
|
|
808
|
-
const hex = ngon(6, 25);
|
|
809
|
-
const triangle = ngon(3, 30);
|
|
810
|
-
```
|
|
811
|
-
|
|
812
|
-
#### `ellipse(rx, ry, segments?)`
|
|
813
|
-
Ellipse.
|
|
814
|
-
|
|
815
|
-
```javascript
|
|
816
|
-
const oval = ellipse(40, 20);
|
|
817
|
-
```
|
|
818
|
-
|
|
819
|
-
#### `slot(length, width)`
|
|
820
|
-
Oblong shape (rectangle with semicircle ends).
|
|
821
|
-
|
|
822
|
-
```javascript
|
|
823
|
-
const oblong = slot(60, 20);
|
|
824
|
-
```
|
|
825
|
-
|
|
826
|
-
#### `star(points, outerRadius, innerRadius)`
|
|
827
|
-
Star shape.
|
|
828
|
-
|
|
829
|
-
```javascript
|
|
830
|
-
const star5 = star(5, 30, 15);
|
|
831
|
-
```
|
|
832
|
-
|
|
833
|
-
### Path Builder
|
|
834
|
-
|
|
835
|
-
Fluent API for tracing 2D outlines point by point.
|
|
836
|
-
|
|
837
|
-
#### `path()`
|
|
838
|
-
Creates a new path builder.
|
|
839
|
-
|
|
840
|
-
```javascript
|
|
841
|
-
const triangle = path()
|
|
842
|
-
.moveTo(0, 0)
|
|
843
|
-
.lineH(50)
|
|
844
|
-
.lineV(30)
|
|
845
|
-
.close();
|
|
846
|
-
```
|
|
847
|
-
|
|
848
|
-
**Methods:**
|
|
849
|
-
- `.moveTo(x, y)` — Set starting point
|
|
850
|
-
- `.lineTo(x, y)` — Line to absolute position
|
|
851
|
-
- `.lineH(dx)` — Horizontal line (relative)
|
|
852
|
-
- `.lineV(dy)` — Vertical line (relative)
|
|
853
|
-
- `.lineAngled(length, degrees)` — Line at angle (0°=right, 90°=up)
|
|
854
|
-
- `.close()` — Close path into a `Sketch` (auto-fixes winding)
|
|
855
|
-
- `.stroke(width, join?)` — Thicken path into solid profile (see below)
|
|
856
|
-
|
|
857
|
-
### Stroke
|
|
858
|
-
|
|
859
|
-
Thicken a polyline (centerline) into a solid profile with uniform width. Proper miter joins at vertices.
|
|
860
|
-
|
|
861
|
-
#### `path().stroke(width, join?)`
|
|
862
|
-
#### `stroke(points, width, join?)`
|
|
863
|
-
|
|
864
|
-
**Parameters:**
|
|
865
|
-
- `width` (number) — Profile thickness
|
|
866
|
-
- `join` ('Square' | 'Round', optional) — Corner style. Default: 'Square' (miter)
|
|
867
|
-
|
|
868
|
-
**Returns:** `Sketch`
|
|
869
|
-
|
|
870
|
-
```javascript
|
|
871
|
-
// Fluent path builder
|
|
872
|
-
const bracket = path()
|
|
873
|
-
.moveTo(0, 0)
|
|
874
|
-
.lineH(50)
|
|
875
|
-
.lineV(-70)
|
|
876
|
-
.lineAngled(20, 235)
|
|
877
|
-
.stroke(4);
|
|
878
|
-
|
|
879
|
-
// Or with point array
|
|
880
|
-
const bracket = stroke([[0, 0], [50, 0], [50, -70]], 4);
|
|
881
|
-
|
|
882
|
-
// Rounded corners
|
|
883
|
-
const rounded = stroke([[0, 0], [50, 0], [50, -50]], 4, 'Round');
|
|
884
|
-
```
|
|
885
|
-
|
|
886
|
-
### Anchor Positioning
|
|
887
|
-
|
|
888
|
-
#### `.attachTo(target, targetAnchor, selfAnchor?, offset?)`
|
|
889
|
-
Position a sketch relative to another using named anchor points.
|
|
890
|
-
|
|
891
|
-
**Parameters:**
|
|
892
|
-
- `target` (Sketch) — The sketch to attach to
|
|
893
|
-
- `targetAnchor` (Anchor) — Point on target: 'center', 'top-left', 'top-right', 'bottom-left', 'bottom-right', 'top', 'bottom', 'left', 'right'
|
|
894
|
-
- `selfAnchor` (Anchor, optional) — Point on this sketch to align. Default: 'center'
|
|
895
|
-
- `offset` ([number, number], optional) — Additional offset after alignment
|
|
896
|
-
|
|
897
|
-
**Returns:** `Sketch`
|
|
898
|
-
|
|
899
|
-
```javascript
|
|
900
|
-
const plate = rect(50, 4);
|
|
901
|
-
const arm = rect(4, 70).attachTo(plate, 'bottom-left', 'top-left');
|
|
902
|
-
return union2d(plate, arm);
|
|
903
|
-
|
|
904
|
-
// With offset: attach then shift 5mm right
|
|
905
|
-
const shifted = rect(4, 70).attachTo(plate, 'bottom-left', 'top-left', [5, 0]);
|
|
906
|
-
```
|
|
907
|
-
|
|
908
|
-
#### `.rotateAround(degrees, pivot)`
|
|
909
|
-
Rotate around a specific point instead of origin.
|
|
910
|
-
|
|
911
|
-
```javascript
|
|
912
|
-
const hook = rect(4, 20).rotateAround(-35, [2, 0]);
|
|
913
|
-
```
|
|
914
|
-
|
|
915
|
-
### 2D Transforms
|
|
916
|
-
|
|
917
|
-
Same as 3D but in 2D. `rotate`, `scale`, and `mirror` use the sketch's bounding-box center by default:
|
|
918
|
-
|
|
919
|
-
```javascript
|
|
920
|
-
sketch.translate(x, y?)
|
|
921
|
-
sketch.rotate(degrees)
|
|
922
|
-
sketch.scale(v) // v can be number or [x, y]
|
|
923
|
-
sketch.mirror([nx, ny])
|
|
924
|
-
sketch.clone()
|
|
925
|
-
sketch.duplicate() // alias
|
|
926
|
-
```
|
|
927
|
-
|
|
928
|
-
### 2D Boolean Operations
|
|
929
|
-
|
|
930
|
-
```javascript
|
|
931
|
-
union2d(...sketches)
|
|
932
|
-
difference2d(...sketches)
|
|
933
|
-
intersection2d(...sketches)
|
|
934
|
-
hull2d(...sketches) // Convex hull
|
|
935
|
-
|
|
936
|
-
// Or method syntax:
|
|
937
|
-
sketch.add(other)
|
|
938
|
-
sketch.subtract(other)
|
|
939
|
-
sketch.intersect(other)
|
|
940
|
-
sketch.hull()
|
|
941
|
-
```
|
|
942
|
-
|
|
943
|
-
### 2D Operations
|
|
944
|
-
|
|
945
|
-
#### `.offset(delta, join?)`
|
|
946
|
-
Inflate (positive) or deflate (negative) the contour.
|
|
947
|
-
|
|
948
|
-
**Parameters:**
|
|
949
|
-
- `delta` (number) - Offset distance. Positive = outward, negative = inward
|
|
950
|
-
- `join` ('Square' | 'Round' | 'Miter', optional) - Corner style. Default: 'Round'
|
|
951
|
-
|
|
952
|
-
```javascript
|
|
953
|
-
const outer = rect(50, 30).offset(5); // Expand by 5mm
|
|
954
|
-
const inner = circle2d(20).offset(-2); // Shrink by 2mm
|
|
955
|
-
const sharp = ngon(6, 20).offset(3, 'Miter');
|
|
956
|
-
```
|
|
957
|
-
|
|
958
|
-
#### `.simplify(epsilon?)`
|
|
959
|
-
Removes vertices that don't significantly affect the shape.
|
|
960
|
-
|
|
961
|
-
```javascript
|
|
962
|
-
const simplified = complexSketch.simplify(0.1);
|
|
963
|
-
```
|
|
964
|
-
|
|
965
|
-
### 2D → 3D Conversion
|
|
966
|
-
|
|
967
|
-
#### `.extrude(height, options?)`
|
|
968
|
-
Extrudes sketch along Z axis.
|
|
969
|
-
|
|
970
|
-
**Parameters:**
|
|
971
|
-
- `height` (number) - Extrusion height
|
|
972
|
-
- `options` (object, optional):
|
|
973
|
-
- `twist` (number) - Twist angle in degrees
|
|
974
|
-
- `divisions` (number) - Number of twist steps (needed for twist)
|
|
975
|
-
- `scaleTop` (number | [number, number]) - Scale factor at top
|
|
976
|
-
- `center` (boolean) - Center along Z axis
|
|
977
|
-
|
|
978
|
-
**Returns:** `TrackedShape` (with faces: top, bottom, side)
|
|
979
|
-
|
|
980
|
-
```javascript
|
|
981
|
-
const simple = rect(50, 30).extrude(10);
|
|
982
|
-
|
|
983
|
-
const twisted = ngon(6, 20).extrude(60, {
|
|
984
|
-
twist: 90,
|
|
985
|
-
divisions: 32
|
|
986
|
-
});
|
|
987
|
-
|
|
988
|
-
const tapered = circle2d(20).extrude(50, {
|
|
989
|
-
scaleTop: 0.5
|
|
990
|
-
});
|
|
991
|
-
```
|
|
992
|
-
|
|
993
|
-
#### `.revolve(degrees?, segments?)`
|
|
994
|
-
Revolves sketch around Y axis (becomes Z in result).
|
|
995
|
-
|
|
996
|
-
**Parameters:**
|
|
997
|
-
- `degrees` (number, optional) - Rotation angle. Default: 360 (full revolution)
|
|
998
|
-
- `segments` (number, optional) - Number of segments. Default: auto
|
|
999
|
-
|
|
1000
|
-
**Returns:** `Shape`
|
|
1001
|
-
|
|
1002
|
-
```javascript
|
|
1003
|
-
// Vase profile
|
|
1004
|
-
const profile = polygon([[20, 0], [25, 30], [20, 60]]);
|
|
1005
|
-
const vase = profile.revolve();
|
|
1006
|
-
|
|
1007
|
-
// Partial revolution (C-shape)
|
|
1008
|
-
const partial = rect(5, 40).translate(20, 0).revolve(270);
|
|
1009
|
-
```
|
|
1010
|
-
|
|
1011
|
-
## Named Entities & Topology
|
|
1012
|
-
|
|
1013
|
-
ForgeCAD provides first-class geometric entities with stable identity and named parts.
|
|
1014
|
-
|
|
1015
|
-
### Point2D
|
|
1016
|
-
```javascript
|
|
1017
|
-
const p = point(10, 20);
|
|
1018
|
-
p.distanceTo(point(30, 40));
|
|
1019
|
-
p.midpointTo(point(30, 40));
|
|
1020
|
-
p.translate(5, 5);
|
|
1021
|
-
p.toTuple(); // [10, 20]
|
|
1022
|
-
```
|
|
1023
|
-
|
|
1024
|
-
### Line2D
|
|
1025
|
-
```javascript
|
|
1026
|
-
const l = line(0, 0, 50, 0);
|
|
1027
|
-
l.length; // 50
|
|
1028
|
-
l.midpoint; // Point2D
|
|
1029
|
-
l.angle; // degrees
|
|
1030
|
-
l.direction; // [1, 0]
|
|
1031
|
-
l.parallel(10); // offset line
|
|
1032
|
-
|
|
1033
|
-
// Line-line intersection (infinite lines)
|
|
1034
|
-
const l2 = line(25, -10, 25, 40);
|
|
1035
|
-
l.intersect(l2); // Point2D(25, 0) — treats as infinite lines
|
|
1036
|
-
l.intersectSegment(l2); // Point2D or null — only if segments actually cross
|
|
1037
|
-
```
|
|
1038
|
-
|
|
1039
|
-
### Circle2D
|
|
1040
|
-
```javascript
|
|
1041
|
-
const c = circle(0, 0, 25);
|
|
1042
|
-
c.diameter; // 50
|
|
1043
|
-
c.pointAtAngle(90); // Point2D at top
|
|
1044
|
-
c.toSketch(); // convert to Sketch
|
|
1045
|
-
c.extrude(30); // TrackedShape with top/bottom/side faces
|
|
1046
|
-
```
|
|
1047
|
-
|
|
1048
|
-
### Rectangle2D
|
|
1049
|
-
```javascript
|
|
1050
|
-
const r = rectangle(0, 0, 100, 60);
|
|
1051
|
-
r.side('top'); // Line2D
|
|
1052
|
-
r.vertex('bottom-left'); // Point2D
|
|
1053
|
-
r.width; r.height; r.center;
|
|
1054
|
-
|
|
1055
|
-
// Diagonals — returns [bl-tr, br-tl] as Line2D pair
|
|
1056
|
-
const [d1, d2] = r.diagonals();
|
|
1057
|
-
const center = d1.intersect(d2); // Point2D at center
|
|
1058
|
-
|
|
1059
|
-
r.toSketch(); // convert to Sketch
|
|
1060
|
-
r.extrude(20); // TrackedShape with named faces/edges
|
|
1061
|
-
```
|
|
1062
|
-
|
|
1063
|
-
### TrackedShape (3D with topology)
|
|
1064
|
-
```javascript
|
|
1065
|
-
const box = rectangle(0, 0, 100, 60).extrude(20);
|
|
1066
|
-
|
|
1067
|
-
box.face('top'); // FaceRef { normal, center }
|
|
1068
|
-
box.face('side-left'); // side face from rect's left edge
|
|
1069
|
-
box.face('front'); // alias for side-bottom
|
|
1070
|
-
box.edge('vert-bl'); // vertical edge at bottom-left corner
|
|
1071
|
-
box.edge('top-front'); // alias for top-bottom
|
|
1072
|
-
box.faceNames(); // all face names
|
|
1073
|
-
box.edgeNames({ includeAliases: true }); // include alias names too
|
|
1074
|
-
|
|
1075
|
-
box.translate(50, 0, 0); // preserves topology
|
|
1076
|
-
box.rotate(0, 0, 45); // preserves topology
|
|
1077
|
-
box.rotateAroundEdge('top-bottom', 90); // rotate around named edge
|
|
1078
|
-
box.toShape(); // unwrap to plain Shape for booleans
|
|
1079
|
-
box.clone(); // explicit duplicate with topology
|
|
1080
|
-
```
|
|
1081
|
-
|
|
1082
|
-
### Utility Functions
|
|
1083
|
-
```javascript
|
|
1084
|
-
degrees(45); // 45 (identity — for readability)
|
|
1085
|
-
radians(Math.PI / 4); // 45 (converts radians to degrees)
|
|
1086
|
-
```
|
|
1087
|
-
|
|
1088
|
-
## Patterns
|
|
1089
|
-
|
|
1090
|
-
### `linearPattern(shape, count, dx, dy, dz?)`
|
|
1091
|
-
Repeat a shape along a direction vector.
|
|
1092
|
-
|
|
1093
|
-
```javascript
|
|
1094
|
-
const row = linearPattern(cylinder(10, 3), 5, 20, 0);
|
|
1095
|
-
```
|
|
1096
|
-
|
|
1097
|
-
### `circularPattern(shape, count, centerX?, centerY?)`
|
|
1098
|
-
Repeat a shape around the Z axis.
|
|
1099
|
-
|
|
1100
|
-
```javascript
|
|
1101
|
-
const holes = circularPattern(cylinder(12, 4).translate(30, 0, -1), 8);
|
|
1102
|
-
```
|
|
1103
|
-
|
|
1104
|
-
### `mirrorCopy(shape, normal)`
|
|
1105
|
-
Mirror and union with original.
|
|
1106
|
-
|
|
1107
|
-
```javascript
|
|
1108
|
-
const full = mirrorCopy(box(50, 30, 10), [1, 0, 0]);
|
|
1109
|
-
```
|
|
1110
|
-
|
|
1111
|
-
## Fillets & Chamfers
|
|
1112
|
-
|
|
1113
|
-
Approximate fillets and chamfers for vertical edges using topology references.
|
|
1114
|
-
|
|
1115
|
-
### `filletEdge(shape, edge, radius, quadrant?, segments?)`
|
|
1116
|
-
```javascript
|
|
1117
|
-
const b = rectangle(0, 0, 50, 50).extrude(20);
|
|
1118
|
-
const filleted = filletEdge(b, b.edge('vert-br'), 5, [-1, -1]);
|
|
1119
|
-
```
|
|
1120
|
-
|
|
1121
|
-
### `chamferEdge(shape, edge, size, quadrant?)`
|
|
1122
|
-
```javascript
|
|
1123
|
-
const chamfered = chamferEdge(b, b.edge('vert-br'), 3, [-1, -1]);
|
|
1124
|
-
```
|
|
1125
|
-
|
|
1126
|
-
The `quadrant` parameter `[signX, signY]` indicates which direction the material is relative to the edge. `[-1, -1]` means material is in the −X, −Y direction.
|
|
1127
|
-
|
|
1128
|
-
## Arc Bridge
|
|
1129
|
-
|
|
1130
|
-
### `arcBridgeBetweenRects(rectA, rectB, segments?)`
|
|
1131
|
-
Build a smooth arc surface connecting two rectangular areas via their closest parallel edges.
|
|
1132
|
-
|
|
1133
|
-
```javascript
|
|
1134
|
-
const base = rectangle(0, 0, 300, 200);
|
|
1135
|
-
const screen = rectangle(0, 200, 300, 200);
|
|
1136
|
-
const hinge = arcBridgeBetweenRects(base, screen, 16);
|
|
1137
|
-
```
|
|
1138
|
-
|
|
1139
|
-
## Constrained Sketches
|
|
1140
|
-
|
|
1141
|
-
Declarative constraint-based 2D sketching with automatic solving.
|
|
1142
|
-
|
|
1143
|
-
```javascript
|
|
1144
|
-
const sketch = constrainedSketch();
|
|
1145
|
-
const p1 = sketch.point(0, 0, true); // fixed point
|
|
1146
|
-
const p2 = sketch.point(50, 0);
|
|
1147
|
-
const p3 = sketch.point(50, 30);
|
|
1148
|
-
const l1 = sketch.line(p1, p2);
|
|
1149
|
-
const l2 = sketch.line(p2, p3);
|
|
1150
|
-
|
|
1151
|
-
sketch.close();
|
|
1152
|
-
|
|
1153
|
-
Constraint.horizontal(sketch, l1);
|
|
1154
|
-
Constraint.vertical(sketch, l2);
|
|
1155
|
-
Constraint.length(sketch, l1, 50);
|
|
1156
|
-
|
|
1157
|
-
const result = sketch.solve();
|
|
1158
|
-
// result is a ConstraintSketch (extends Sketch)
|
|
1159
|
-
// result.constraintMeta.status → 'under' | 'fully' | 'over'
|
|
1160
|
-
```
|
|
1161
|
-
|
|
1162
|
-
### Available Constraints
|
|
1163
|
-
`Constraint.horizontal`, `Constraint.vertical`, `Constraint.makeParallel`, `Constraint.perpendicular`, `Constraint.equalLength`, `Constraint.distance`, `Constraint.length`, `Constraint.enforceAngle`, `Constraint.fix`, `Constraint.coincident`
|
|
1164
|
-
|
|
1165
|
-
Constraints accept both string IDs and entity objects (Point2D, Line2D) — entities are auto-imported into the builder.
|
|
1166
|
-
|
|
1167
|
-
## Multi-File Projects
|
|
1168
|
-
|
|
1169
|
-
ForgeCAD supports multi-file projects. Files are either **sketches** (`.sketch.js`, return a `Sketch`) or **parts** (`.forge.js`, return a `Shape` or `TrackedShape`).
|
|
1170
|
-
|
|
1171
|
-
### File Types
|
|
1172
|
-
- `*.sketch.js` — 2D sketch file, must return a `Sketch`
|
|
1173
|
-
- `*.forge.js` — 3D part file, must return a `Shape` or `TrackedShape`
|
|
1174
|
-
|
|
1175
|
-
### Import Path Resolution
|
|
1176
|
-
- `./file.forge.js` and `../file.forge.js` resolve relative to the file that calls `importSketch()` / `importPart()`
|
|
1177
|
-
- Bare paths like `api/bracket.forge.js` resolve from the opened project root
|
|
1178
|
-
- Leading `/` is treated as project-root relative
|
|
1179
|
-
|
|
1180
|
-
### `importSketch(fileName, paramOverrides?)`
|
|
1181
|
-
Executes another file and returns its result as a `Sketch`. The target file must return a `Sketch`.
|
|
1182
|
-
|
|
1183
|
-
**Parameters:**
|
|
1184
|
-
- `fileName` (string) — Import path (e.g. `"./profile.sketch.js"` or `"api/profile.sketch.js"`)
|
|
1185
|
-
- `paramOverrides` (optional object) — Import-time parameter overrides by param name
|
|
1186
|
-
|
|
1187
|
-
**Returns:** `Sketch`
|
|
1188
|
-
|
|
1189
|
-
```javascript
|
|
1190
|
-
// In a .forge.js file:
|
|
1191
|
-
const profile = importSketch("bracket-profile.sketch.js", {
|
|
1192
|
-
"Width": 42,
|
|
1193
|
-
"Height": 18,
|
|
1194
|
-
});
|
|
1195
|
-
return profile.extrude(50);
|
|
1196
|
-
```
|
|
1197
|
-
|
|
1198
|
-
### `importPart(fileName, paramOverrides?)`
|
|
1199
|
-
Executes another file and returns its result as a `Shape`. The target file may return either `Shape` or `TrackedShape` (tracked results are auto-unwrapped to `Shape`).
|
|
1200
|
-
|
|
1201
|
-
**Parameters:**
|
|
1202
|
-
- `fileName` (string) — Import path (e.g. `"./bracket.forge.js"` or `"api/bracket.forge.js"`)
|
|
1203
|
-
- `paramOverrides` (optional object) — Import-time parameter overrides by param name
|
|
1204
|
-
|
|
1205
|
-
**Returns:** `Shape` (chainable)
|
|
1206
|
-
|
|
1207
|
-
```javascript
|
|
1208
|
-
// Assembly: import parts and position them
|
|
1209
|
-
const bracket = importPart("bracket.forge.js", { "Thickness": 4 });
|
|
1210
|
-
const bracket2 = importPart("bracket.forge.js", { "Thickness": 8 })
|
|
1211
|
-
.translate(100, 0, 0)
|
|
1212
|
-
.rotate(0, 0, 180);
|
|
1213
|
-
|
|
1214
|
-
return union(bracket, bracket2);
|
|
1215
|
-
```
|
|
1216
|
-
|
|
1217
|
-
### Import Rules
|
|
1218
|
-
- Circular imports are detected and throw an error
|
|
1219
|
-
- Imported files can be instantiated multiple times
|
|
1220
|
-
- `paramOverrides` only affects that import call (other imports are independent)
|
|
1221
|
-
- Params supplied through `paramOverrides` are treated as fixed arguments for that import call
|
|
1222
|
-
- Relative imports (`./` / `../`) are resolved from the current file path
|
|
1223
|
-
- `importPart()` accepts imported `Shape` or `TrackedShape` results and always returns a chainable `Shape`
|
|
1224
|
-
- The returned `Shape` or `Sketch` is fully chainable — use `.translate()`, `.rotate()`, `.subtract()`, etc.
|
|
1225
|
-
|
|
1226
|
-
### Typical Project Structure
|
|
1227
|
-
```
|
|
1228
|
-
my-project/
|
|
1229
|
-
├── base-profile.sketch.js ← 2D cross-section
|
|
1230
|
-
├── bracket.forge.js ← extrudes the sketch, adds holes
|
|
1231
|
-
└── assembly.forge.js ← imports multiple parts, positions them
|
|
1232
|
-
```
|
|
1233
|
-
|
|
1234
|
-
## Part Library
|
|
1235
|
-
|
|
1236
|
-
Pre-built parametric parts available via `lib.xxx()`. No imports needed.
|
|
1237
|
-
|
|
1238
|
-
### `lib.boltHole(diameter, depth)`
|
|
1239
|
-
Through-hole cylinder (centered).
|
|
1240
|
-
|
|
1241
|
-
### `lib.fastenerHole(opts)`
|
|
1242
|
-
Standardized metric hole helper with fits and optional counterbore/countersink.
|
|
1243
|
-
|
|
1244
|
-
```javascript
|
|
1245
|
-
const m4 = lib.fastenerHole({
|
|
1246
|
-
size: "M4",
|
|
1247
|
-
fit: "normal", // close | normal | loose | tap
|
|
1248
|
-
depth: 12,
|
|
1249
|
-
counterbore: { depth: 3.5 }, // diameter auto from size unless provided
|
|
1250
|
-
});
|
|
1251
|
-
```
|
|
1252
|
-
|
|
1253
|
-
### `lib.counterbore(holeDia, boreDia, boreDepth, totalDepth)`
|
|
1254
|
-
Through-hole with wider recess at top.
|
|
1255
|
-
|
|
1256
|
-
### `lib.tube(outerX, outerY, outerZ, wall)`
|
|
1257
|
-
Rectangular hollow tube.
|
|
1258
|
-
|
|
1259
|
-
### `lib.pipe(height, outerRadius, wall, segments?)`
|
|
1260
|
-
Hollow cylinder.
|
|
1261
|
-
|
|
1262
|
-
### `lib.hexNut(acrossFlats, height, holeDia)`
|
|
1263
|
-
Hex nut via intersection of 3 rotated slabs, with center bore.
|
|
1264
|
-
|
|
1265
|
-
### `lib.roundedBox(x, y, z, radius)`
|
|
1266
|
-
Approximate rounded box via union of axis-aligned slabs.
|
|
1267
|
-
|
|
1268
|
-
### `lib.bracket(width, height, depth, thick, holeDia?)`
|
|
1269
|
-
L-shaped mounting bracket with optional holes.
|
|
1270
|
-
|
|
1271
|
-
### `lib.holePattern(rows, cols, spacingX, spacingY, holeDia, depth)`
|
|
1272
|
-
Grid of cylindrical holes.
|
|
1273
|
-
|
|
1274
|
-
### `lib.explode(items, options?)`
|
|
1275
|
-
Apply deterministic exploded-view offsets to assembly structures while preserving names, colors, and nesting.
|
|
1276
|
-
|
|
1277
|
-
Works with:
|
|
1278
|
-
- arrays of shapes/sketches/named items
|
|
1279
|
-
- named nested `{ name, group: [...] }` assembly trees
|
|
1280
|
-
- `ShapeGroup` outputs (including nested `group(...)`)
|
|
1281
|
-
|
|
1282
|
-
**Parameters:**
|
|
1283
|
-
- `items` (`ExplodeItem[] | ShapeGroup`) - Assembly structure to explode
|
|
1284
|
-
- `options` (object, optional):
|
|
1285
|
-
- `amount` (number) - Base explode distance. Default: `10`
|
|
1286
|
-
- `stages` (number[]) - Per-depth multipliers (`depth 1 = stages[0]`). If depth exceeds list, last value is reused
|
|
1287
|
-
- `mode` (`'radial' | 'x' | 'y' | 'z' | [x, y, z]`) - Default direction mode. Default: `'radial'`
|
|
1288
|
-
- `axisLock` (`'x' | 'y' | 'z'`) - Optional global axis lock
|
|
1289
|
-
- `byName` (`Record<string, { stage?, direction?, axisLock? }>`)- Per-part/group overrides by item name
|
|
1290
|
-
- `byPath` (`Record<string, { stage?, direction?, axisLock? }>`)- Low-level overrides by traversal path
|
|
1291
|
-
|
|
1292
|
-
Named items may also include an inline override:
|
|
1293
|
-
`{ name: "Bolt A", shape: bolt, explode: { stage: 1.5, direction: [1, 0, 0] } }`
|
|
1294
|
-
|
|
1295
|
-
**Returns:** Same structure type as input, with translated geometry.
|
|
1296
|
-
|
|
1297
|
-
```javascript
|
|
1298
|
-
const explodeAmt = param("Explode", 0, { min: 0, max: 40, unit: "mm" });
|
|
1299
|
-
|
|
1300
|
-
const assembly = [
|
|
1301
|
-
{ name: "Body", shape: box(80, 50, 30, true).color('#6c7a89') },
|
|
1302
|
-
{ name: "Drive", group: [
|
|
1303
|
-
{ name: "Shaft", shape: cylinder(60, 4, undefined, undefined, true).pointAlong([1, 0, 0]).color('#c7d0d8') },
|
|
1304
|
-
{ name: "Rotor", shape: cylinder(20, 12, undefined, undefined, true).color('#8897a8') },
|
|
1305
|
-
]},
|
|
1306
|
-
];
|
|
1307
|
-
|
|
1308
|
-
return lib.explode(assembly, {
|
|
1309
|
-
amount: explodeAmt,
|
|
1310
|
-
stages: [0.4, 0.8],
|
|
1311
|
-
mode: 'radial',
|
|
1312
|
-
byName: {
|
|
1313
|
-
"Shaft": { direction: [1, 0, 0], stage: 1.4 },
|
|
1314
|
-
},
|
|
1315
|
-
});
|
|
1316
|
-
```
|
|
1317
|
-
|
|
1318
|
-
### `lib.pipeRoute(points, radius, options?)`
|
|
1319
|
-
Route a pipe through 3D waypoints with smooth torus bends at corners.
|
|
1320
|
-
|
|
1321
|
-
**Parameters:**
|
|
1322
|
-
- `points` ([number, number, number][]) - Array of 3D waypoints
|
|
1323
|
-
- `radius` (number) - Pipe outer radius
|
|
1324
|
-
- `options` (object, optional):
|
|
1325
|
-
- `bendRadius` (number) - Radius of bends at corners. Default: `radius * 4`
|
|
1326
|
-
- `wall` (number) - Wall thickness for hollow pipe. If omitted, pipe is solid
|
|
1327
|
-
- `segments` (number) - Circumferential segments. Default: 32
|
|
1328
|
-
|
|
1329
|
-
**Returns:** `Shape`
|
|
1330
|
-
|
|
1331
|
-
```javascript
|
|
1332
|
-
// Solid copper pipe with 90° bends
|
|
1333
|
-
const refrigPipe = lib.pipeRoute(
|
|
1334
|
-
[[0, 0, 0], [100, 0, 0], [100, 80, 0], [100, 80, 60]],
|
|
1335
|
-
4,
|
|
1336
|
-
{ bendRadius: 20 }
|
|
1337
|
-
).color('#B87333');
|
|
1338
|
-
|
|
1339
|
-
// Hollow drain pipe
|
|
1340
|
-
const drainPipe = lib.pipeRoute(
|
|
1341
|
-
[[0, 0, 20], [60, 0, 20], [60, 80, 20]],
|
|
1342
|
-
3,
|
|
1343
|
-
{ bendRadius: 15, wall: 1 }
|
|
1344
|
-
).color('#CCCCCC');
|
|
1345
|
-
```
|
|
1346
|
-
|
|
1347
|
-
### `lib.elbow(pipeRadius, bendRadius, angle?, options?)`
|
|
1348
|
-
Curved pipe section (torus arc) for connecting two pipe directions. Creates a bend at the origin.
|
|
1349
|
-
|
|
1350
|
-
**Parameters:**
|
|
1351
|
-
- `pipeRadius` (number) - Pipe outer radius
|
|
1352
|
-
- `bendRadius` (number) - Centerline bend radius
|
|
1353
|
-
- `angle` (number, optional) - Bend angle in degrees. Default: 90
|
|
1354
|
-
|
|
1355
|
-
**Options:**
|
|
1356
|
-
- `wall` (number) - Wall thickness for hollow pipe
|
|
1357
|
-
- `segments` (number) - Circumferential segments. Default: 32
|
|
1358
|
-
- `from` ([number, number, number]) - Incoming direction vector
|
|
1359
|
-
- `to` ([number, number, number]) - Outgoing direction vector (overrides angle)
|
|
1360
|
-
|
|
1361
|
-
**Alternative call:** `lib.elbow(pipeRadius, bendRadius, { from, to, wall, segments })`
|
|
1362
|
-
|
|
1363
|
-
```javascript
|
|
1364
|
-
// Simple 90° elbow
|
|
1365
|
-
const bend = lib.elbow(5, 20, 90);
|
|
1366
|
-
|
|
1367
|
-
// 45° hollow elbow
|
|
1368
|
-
const bend45 = lib.elbow(5, 20, 45, { wall: 1.5 });
|
|
1369
|
-
|
|
1370
|
-
// Direction-based: connect Z-up pipe to X-right pipe
|
|
1371
|
-
const bend = lib.elbow(5, 20, { from: [0, 0, 1], to: [1, 0, 0] });
|
|
1372
|
-
```
|
|
1373
|
-
|
|
1374
|
-
### `lib.thread(diameter, pitch, length, options?)`
|
|
1375
|
-
External thread (helical ridge) via twisted extrusion. Returns a threaded cylinder along +Z.
|
|
1376
|
-
|
|
1377
|
-
**Options:**
|
|
1378
|
-
- `depth` (number) - Thread depth. Default: `pitch * 0.35`
|
|
1379
|
-
- `segments` (number) - Circumferential segments. Default: 36
|
|
1380
|
-
|
|
1381
|
-
```javascript
|
|
1382
|
-
const m8thread = lib.thread(8, 1.25, 30);
|
|
1383
|
-
const smooth = lib.thread(8, 1.0, 30, { segments: 48 });
|
|
1384
|
-
```
|
|
1385
|
-
|
|
1386
|
-
### `lib.bolt(diameter, length, options?)`
|
|
1387
|
-
Hex bolt with real helical threads. Head at z=0, shaft extends along −Z.
|
|
1388
|
-
|
|
1389
|
-
**Options:**
|
|
1390
|
-
- `pitch` (number) - Thread pitch. Default: `diameter * 0.15`
|
|
1391
|
-
- `headHeight` (number) - Default: `diameter * 0.65`
|
|
1392
|
-
- `headAcrossFlats` (number) - Default: `diameter * 1.6`
|
|
1393
|
-
- `threadLength` (number) - Threaded portion. Default: full length
|
|
1394
|
-
- `segments` (number) - Circumferential segments. Default: 36
|
|
1395
|
-
|
|
1396
|
-
```javascript
|
|
1397
|
-
const m8bolt = lib.bolt(8, 30);
|
|
1398
|
-
const custom = lib.bolt(10, 40, { pitch: 1.5, headHeight: 7 });
|
|
1399
|
-
```
|
|
1400
|
-
|
|
1401
|
-
### `lib.nut(diameter, options?)`
|
|
1402
|
-
Hex nut with bore, centered at origin.
|
|
1403
|
-
|
|
1404
|
-
**Options:**
|
|
1405
|
-
- `pitch` (number) - Default: `diameter * 0.15`
|
|
1406
|
-
- `height` (number) - Default: `diameter * 0.8`
|
|
1407
|
-
- `acrossFlats` (number) - Default: `diameter * 1.6`
|
|
1408
|
-
- `segments` (number) - Circumferential segments. Default: 36
|
|
1409
|
-
|
|
1410
|
-
```javascript
|
|
1411
|
-
const m8nut = lib.nut(8);
|
|
1412
|
-
const m8nut2 = lib.nut(8, { height: 6.5, acrossFlats: 13 });
|
|
1413
|
-
```
|
|
1414
|
-
|
|
1415
|
-
## Common Patterns
|
|
1416
|
-
|
|
1417
|
-
### Parametric Box with Holes
|
|
1418
|
-
```javascript
|
|
1419
|
-
const w = param("Width", 80, { min: 40, max: 150, unit: "mm" });
|
|
1420
|
-
const h = param("Height", 60, { min: 30, max: 100, unit: "mm" });
|
|
1421
|
-
const t = param("Thickness", 5, { min: 2, max: 10, unit: "mm" });
|
|
1422
|
-
const holeD = param("Hole Diameter", 8, { min: 4, max: 20, unit: "mm" });
|
|
1423
|
-
|
|
1424
|
-
const base = box(w, h, t);
|
|
1425
|
-
const hole = cylinder(t + 2, holeD / 2).translate(w / 2, h / 2, -1);
|
|
1426
|
-
|
|
1427
|
-
return base.subtract(hole);
|
|
1428
|
-
```
|
|
1429
|
-
|
|
1430
|
-
### Hollow Shell (Wall Thickness)
|
|
1431
|
-
```javascript
|
|
1432
|
-
const outer = param("Outer Size", 50, { min: 20, max: 100, unit: "mm" });
|
|
1433
|
-
const wall = param("Wall", 3, { min: 1, max: 10, unit: "mm" });
|
|
1434
|
-
|
|
1435
|
-
const outerBox = box(outer, outer, outer, true);
|
|
1436
|
-
const innerBox = box(outer - 2 * wall, outer - 2 * wall, outer - 2 * wall, true);
|
|
1437
|
-
|
|
1438
|
-
return outerBox.subtract(innerBox);
|
|
1439
|
-
```
|
|
1440
|
-
|
|
1441
|
-
### Array/Pattern
|
|
1442
|
-
```javascript
|
|
1443
|
-
const count = param("Count", 5, { min: 2, max: 10 });
|
|
1444
|
-
const spacing = param("Spacing", 15, { min: 5, max: 30, unit: "mm" });
|
|
1445
|
-
|
|
1446
|
-
let shapes = [];
|
|
1447
|
-
for (let i = 0; i < count; i++) {
|
|
1448
|
-
shapes.push(cylinder(10, 5).translate(i * spacing, 0, 0));
|
|
1449
|
-
}
|
|
1450
|
-
|
|
1451
|
-
return union(...shapes);
|
|
1452
|
-
```
|
|
1453
|
-
|
|
1454
|
-
### Sketch-Based Design
|
|
1455
|
-
```javascript
|
|
1456
|
-
const sides = param("Sides", 6, { min: 3, max: 12 });
|
|
1457
|
-
const radius = param("Radius", 25, { min: 10, max: 50, unit: "mm" });
|
|
1458
|
-
const height = param("Height", 60, { min: 20, max: 120, unit: "mm" });
|
|
1459
|
-
const wall = param("Wall", 3, { min: 1, max: 8, unit: "mm" });
|
|
1460
|
-
|
|
1461
|
-
const outer = ngon(sides, radius);
|
|
1462
|
-
const inner = ngon(sides, radius - wall);
|
|
1463
|
-
const profile = outer.subtract(inner);
|
|
1464
|
-
|
|
1465
|
-
return profile.extrude(height, { twist: 45, divisions: 32 });
|
|
1466
|
-
```
|
|
1467
|
-
|
|
1468
|
-
### Rounded Edges
|
|
1469
|
-
```javascript
|
|
1470
|
-
// Use offset on 2D sketch before extruding
|
|
1471
|
-
const base = rect(50, 30).offset(-3, 'Round').offset(3, 'Round');
|
|
1472
|
-
return base.extrude(10);
|
|
1473
|
-
```
|
|
1474
|
-
|
|
1475
|
-
### Chamfers and Fillets
|
|
1476
|
-
```javascript
|
|
1477
|
-
// Chamfer: subtract angled box
|
|
1478
|
-
const part = box(50, 50, 20);
|
|
1479
|
-
const chamfer = box(10, 60, 10)
|
|
1480
|
-
.rotate(0, 45, 0)
|
|
1481
|
-
.translate(50, -5, 15);
|
|
1482
|
-
|
|
1483
|
-
return part.subtract(chamfer);
|
|
1484
|
-
```
|
|
1485
|
-
|
|
1486
|
-
## Query Methods
|
|
1487
|
-
|
|
1488
|
-
### 3D Shape Queries
|
|
1489
|
-
```javascript
|
|
1490
|
-
shape.volume() // Volume in mm³
|
|
1491
|
-
shape.surfaceArea() // Surface area in mm²
|
|
1492
|
-
shape.boundingBox() // { min: [x,y,z], max: [x,y,z] }
|
|
1493
|
-
shape.isEmpty() // true if no geometry
|
|
1494
|
-
shape.numTri() // Triangle count
|
|
1495
|
-
shape.minGap(other, 50) // Minimum distance to another shape (within search radius)
|
|
1496
|
-
```
|
|
1497
|
-
|
|
1498
|
-
### 2D Sketch Queries
|
|
1499
|
-
```javascript
|
|
1500
|
-
sketch.area() // Area in mm²
|
|
1501
|
-
sketch.bounds() // { min: [x,y], max: [x,y] }
|
|
1502
|
-
sketch.isEmpty() // true if no area
|
|
1503
|
-
sketch.numVert() // Vertex count
|
|
1504
|
-
```
|
|
1505
|
-
|
|
1506
|
-
## Returning Multiple Objects
|
|
1507
|
-
|
|
1508
|
-
Scripts can return arrays to display multiple objects in the viewport:
|
|
1509
|
-
|
|
1510
|
-
```javascript
|
|
1511
|
-
// Simple array — auto-named "Object 1", "Object 2", etc.
|
|
1512
|
-
return [
|
|
1513
|
-
box(50, 50, 10),
|
|
1514
|
-
cylinder(20, 8).translate(25, 25, 10),
|
|
1515
|
-
];
|
|
1516
|
-
|
|
1517
|
-
// Named objects with colors
|
|
1518
|
-
return [
|
|
1519
|
-
{ name: "Base Plate", shape: box(100, 100, 5), color: "#888888" },
|
|
1520
|
-
{ name: "Column", shape: cylinder(50, 10).translate(50, 50, 5), color: "#4488cc" },
|
|
1521
|
-
{ name: "Profile", sketch: circle2d(20), color: "#ff6600" },
|
|
1522
|
-
];
|
|
1523
|
-
```
|
|
1524
|
-
|
|
1525
|
-
Each object gets its own visibility toggle, opacity slider, and color picker in the View Panel.
|
|
1526
|
-
|
|
1527
|
-
### Assembly Groups
|
|
1528
|
-
|
|
1529
|
-
For complex assemblies, use nested groups to organize related parts:
|
|
1530
|
-
|
|
1531
|
-
```javascript
|
|
1532
|
-
return [
|
|
1533
|
-
{ name: "Bed Assembly", group: [
|
|
1534
|
-
{ name: "Bed Plate", shape: bedPlate },
|
|
1535
|
-
{ name: "Glass Bed", shape: glass },
|
|
1536
|
-
{ name: "Heater", shape: heater },
|
|
1537
|
-
]},
|
|
1538
|
-
{ name: "Gantry", group: [
|
|
1539
|
-
{ name: "Left Rail", shape: leftRail },
|
|
1540
|
-
{ name: "Right Rail", shape: rightRail },
|
|
1541
|
-
{ name: "Cross Bar", shape: crossBar },
|
|
1542
|
-
]},
|
|
1543
|
-
];
|
|
1544
|
-
```
|
|
1545
|
-
|
|
1546
|
-
**Benefits:**
|
|
1547
|
-
- **Spatial analysis** skips intra-group collision checks (intentional overlaps)
|
|
1548
|
-
- **Group-level summary** reports relationships between assemblies
|
|
1549
|
-
- **Object listing** shows group tags: `Bed Plate [Bed Assembly]`
|
|
1550
|
-
- **Parameter validation** (`param-check` CLI) ignores collisions within groups
|
|
1551
|
-
|
|
1552
|
-
## Best Practices
|
|
1553
|
-
|
|
1554
|
-
### Performance
|
|
1555
|
-
- Boolean operations are expensive - minimize them
|
|
1556
|
-
- Use parameters for values that might change
|
|
1557
|
-
- Avoid deep nesting of operations in loops
|
|
1558
|
-
|
|
1559
|
-
### Readability
|
|
1560
|
-
```javascript
|
|
1561
|
-
// Good: Named intermediate shapes
|
|
1562
|
-
const base = box(100, 100, 10);
|
|
1563
|
-
const hole = cylinder(12, 8);
|
|
1564
|
-
const result = base.subtract(hole.translate(50, 50, 0));
|
|
1565
|
-
return result;
|
|
1566
|
-
|
|
1567
|
-
// Avoid: Deep nesting
|
|
1568
|
-
return box(100, 100, 10).subtract(cylinder(12, 8).translate(50, 50, 0));
|
|
1569
|
-
```
|
|
1570
|
-
|
|
1571
|
-
### Units
|
|
1572
|
-
- All dimensions are in millimeters by default
|
|
1573
|
-
- Angles are in degrees
|
|
1574
|
-
- Use `unit` parameter option for clarity
|
|
1575
|
-
|
|
1576
|
-
### Centering
|
|
1577
|
-
```javascript
|
|
1578
|
-
// Centered primitives are easier to position
|
|
1579
|
-
const centered = box(50, 50, 50, true).translate(x, y, z);
|
|
1580
|
-
|
|
1581
|
-
// Corner-based requires offset calculation
|
|
1582
|
-
const corner = box(50, 50, 50).translate(x - 25, y - 25, z - 25);
|
|
1583
|
-
```
|
|
1584
|
-
|
|
1585
|
-
## Debugging
|
|
1586
|
-
|
|
1587
|
-
### Console Output
|
|
1588
|
-
```javascript
|
|
1589
|
-
console.log("Width:", width);
|
|
1590
|
-
console.log("Volume:", shape.volume());
|
|
1591
|
-
```
|
|
1592
|
-
|
|
1593
|
-
### Incremental Building
|
|
1594
|
-
```javascript
|
|
1595
|
-
// Build up complex shapes step by step
|
|
1596
|
-
const base = box(50, 50, 10);
|
|
1597
|
-
// return base; // Uncomment to see just the base
|
|
1598
|
-
|
|
1599
|
-
const withHole = base.subtract(cylinder(12, 5).translate(25, 25, 0));
|
|
1600
|
-
// return withHole; // Uncomment to see with hole
|
|
1601
|
-
|
|
1602
|
-
return withHole.add(cylinder(20, 3).translate(25, 25, 10));
|
|
1603
|
-
```
|
|
1604
|
-
|
|
1605
|
-
## Error Handling
|
|
1606
|
-
|
|
1607
|
-
Common errors:
|
|
1608
|
-
- **"Kernel not initialized"** - Internal error, reload page
|
|
1609
|
-
- **"Cannot read property of undefined"** - Check variable names and parameter declarations
|
|
1610
|
-
- **Invalid geometry** - Usually from degenerate shapes (zero dimensions, self-intersecting sketches)
|
|
1611
|
-
- **Script execution error** - Check console for JavaScript errors
|
|
1612
|
-
|
|
1613
|
-
## Complete Examples
|
|
1614
|
-
|
|
1615
|
-
### Parametric Phone Stand
|
|
1616
|
-
```javascript
|
|
1617
|
-
const width = param("Width", 80, { min: 40, max: 150, unit: "mm" });
|
|
1618
|
-
const depth = param("Depth", 60, { min: 30, max: 100, unit: "mm" });
|
|
1619
|
-
const thick = param("Thickness", 5, { min: 2, max: 15, unit: "mm" });
|
|
1620
|
-
const backH = param("Back Height", 40, { min: 20, max: 80, unit: "mm" });
|
|
1621
|
-
const cableD = param("Cable Hole", 8, { min: 4, max: 15, unit: "mm" });
|
|
1622
|
-
|
|
1623
|
-
const base = box(width, depth, thick);
|
|
1624
|
-
const back = box(width, thick, backH).translate(0, depth - thick, thick);
|
|
1625
|
-
const lip = box(width, 10, 8).translate(0, 0, thick);
|
|
1626
|
-
const hole = cylinder(thick + 2, cableD / 2)
|
|
1627
|
-
.rotate(90, 0, 0)
|
|
1628
|
-
.translate(width / 2, depth / 2, -1);
|
|
1629
|
-
|
|
1630
|
-
return union(base, back, lip).subtract(hole);
|
|
1631
|
-
```
|
|
1632
|
-
|
|
1633
|
-
### Multi-Object Scene with Colors
|
|
1634
|
-
```javascript
|
|
1635
|
-
const base = box(100, 100, 5).color('#888888');
|
|
1636
|
-
const col1 = cylinder(40, 5).translate(20, 20, 5).color('#cc4444');
|
|
1637
|
-
const col2 = cylinder(40, 5).translate(80, 20, 5).color('#4444cc');
|
|
1638
|
-
const col3 = cylinder(40, 5).translate(50, 80, 5).color('#44cc44');
|
|
1639
|
-
const top = box(100, 100, 3).translate(0, 0, 45).color('#888888');
|
|
1640
|
-
|
|
1641
|
-
return [
|
|
1642
|
-
{ name: "Base", shape: base },
|
|
1643
|
-
{ name: "Column A", shape: col1 },
|
|
1644
|
-
{ name: "Column B", shape: col2 },
|
|
1645
|
-
{ name: "Column C", shape: col3 },
|
|
1646
|
-
{ name: "Top", shape: top },
|
|
1647
|
-
];
|
|
1648
|
-
```
|
|
1649
|
-
|
|
1650
|
-
### Entity-Based Design with Topology
|
|
1651
|
-
```javascript
|
|
1652
|
-
const baseRect = rectangle(0, 0, 80, 60);
|
|
1653
|
-
const base = baseRect.extrude(20);
|
|
1654
|
-
|
|
1655
|
-
// Fillet two corners
|
|
1656
|
-
let result = filletEdge(base, base.edge('vert-br'), 8, [-1, -1]);
|
|
1657
|
-
result = filletEdge(result, base.edge('vert-bl'), 8, [1, -1]);
|
|
1658
|
-
|
|
1659
|
-
// Subtract hole pattern
|
|
1660
|
-
const holes = circularPattern(
|
|
1661
|
-
cylinder(25, 4).translate(40, 30, -1),
|
|
1662
|
-
4, 40, 30,
|
|
1663
|
-
);
|
|
1664
|
-
|
|
1665
|
-
return result.toShape().subtract(holes);
|
|
1666
|
-
```
|