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.
Files changed (193) hide show
  1. package/README.md +2 -11
  2. package/dist/assets/{AdminPage-CeqCUUgu.js → AdminPage-DAu1C1ST.js} +250 -151
  3. package/dist/assets/{BlogPage-P_AJP0v9.js → BlogPage-CJEXL_zJ.js} +94 -70
  4. package/dist/assets/{DocsPage-CKRV2iq2.js → DocsPage-Gc_BCdqC.js} +269 -143
  5. package/dist/assets/EditorApp-D9bJvtf7.js +11338 -0
  6. package/dist/assets/{EditorApp-CnC2k4cW.css → EditorApp-DG1-oUSV.css} +459 -87
  7. package/dist/assets/{EmbedViewer-DBlzmQ5i.js → EmbedViewer-CEO8XbV8.js} +2 -4
  8. package/dist/assets/LandingPage-CdCuEOdC.js +451 -0
  9. package/dist/assets/PricingPage-BSrxu6d7.js +232 -0
  10. package/dist/assets/{SettingsPage-BqCh9JcC.js → SettingsPage-FUCSIRq6.js} +129 -5
  11. package/dist/assets/{evalWorker-Ql-aKwLA.js → evalWorker-KoR0SNKq.js} +6770 -2914
  12. package/dist/assets/{index-2hfs_ub0.css → index-CyVd1D4D.css} +227 -53
  13. package/dist/assets/{Viewport-CoB46f5R.js → index-wTEK39at.js} +31385 -6439
  14. package/dist/assets/{javascript-DCxGoE5Y.js → javascript-DAl8Gmyo.js} +1 -1
  15. package/dist/assets/{manifold-CqNMHHKO.js → manifold-B1sGWdYk.js} +4 -3
  16. package/dist/assets/{manifold-Cce9wRFz.js → manifold-D7o0N50J.js} +1 -1
  17. package/dist/assets/{manifold-D6BeHIOo.js → manifold-G5sBaXzi.js} +1 -1
  18. package/dist/assets/{reportWorker-sFEFonXf.js → reportWorker-DYcRHhv9.js} +6798 -3341
  19. package/dist/assets/{vendor-react-Dt7-aaJH.js → vendor-react-CG3i_wp0.js} +65 -8
  20. package/dist/docs-raw/generated/assembly.md +691 -112
  21. package/dist/docs-raw/generated/concepts.md +1225 -1400
  22. package/dist/docs-raw/generated/core.md +464 -1412
  23. package/dist/docs-raw/generated/curves.md +593 -117
  24. package/dist/docs-raw/generated/lib.md +38 -748
  25. package/dist/docs-raw/generated/output.md +139 -245
  26. package/dist/docs-raw/generated/sheet-metal.md +473 -21
  27. package/dist/docs-raw/generated/sketch.md +553 -349
  28. package/dist/docs-raw/generated/viewport.md +345 -303
  29. package/dist/docs-raw/generated/wood.md +104 -0
  30. package/dist/index.html +2 -2
  31. package/dist/sitemap.xml +6 -6
  32. package/dist-cli/chunk-PZ5AY32C.js +10 -0
  33. package/dist-cli/chunk-PZ5AY32C.js.map +1 -0
  34. package/dist-cli/forgecad.js +9435 -5407
  35. package/dist-cli/forgecad.js.map +1 -0
  36. package/dist-cli/solver-FV7TJZGI.js +365 -0
  37. package/dist-cli/solver-FV7TJZGI.js.map +1 -0
  38. package/dist-skill/CONTEXT.md +3186 -7145
  39. package/dist-skill/SKILL-dev.md +21 -63
  40. package/dist-skill/SKILL.md +12 -56
  41. package/dist-skill/docs/API/core/concepts.md +16 -98
  42. package/dist-skill/docs/CLI/export.md +91 -0
  43. package/dist-skill/docs/CLI/projects.md +107 -0
  44. package/dist-skill/docs/CLI/studio_publishing.md +52 -0
  45. package/dist-skill/docs/CLI/validation.md +66 -0
  46. package/dist-skill/docs/generated/assembly.md +691 -112
  47. package/dist-skill/docs/generated/core.md +464 -1412
  48. package/dist-skill/docs/generated/curves.md +593 -117
  49. package/dist-skill/docs/generated/lib.md +38 -748
  50. package/dist-skill/docs/generated/output.md +139 -245
  51. package/dist-skill/docs/generated/sheet-metal.md +473 -21
  52. package/dist-skill/docs/generated/sketch.md +553 -349
  53. package/dist-skill/docs/generated/viewport.md +345 -303
  54. package/dist-skill/docs/generated/wood.md +104 -0
  55. package/dist-skill/docs/guides/coordinate-system.md +11 -17
  56. package/dist-skill/docs/guides/geometry-conventions.md +13 -70
  57. package/dist-skill/docs/guides/modeling-recipes.md +22 -195
  58. package/dist-skill/docs/guides/positioning.md +88 -147
  59. package/dist-skill/docs-dev/API/core/concepts.md +51 -0
  60. package/dist-skill/docs-dev/API/core/sdf-advanced.md +92 -0
  61. package/dist-skill/docs-dev/API/core/sdf-primitives.md +58 -0
  62. package/dist-skill/docs-dev/API/core/sdf-workflow.md +42 -0
  63. package/dist-skill/docs-dev/CLI/export.md +91 -0
  64. package/dist-skill/docs-dev/CLI/projects.md +107 -0
  65. package/dist-skill/docs-dev/CLI/studio_publishing.md +52 -0
  66. package/dist-skill/docs-dev/CLI/validation.md +66 -0
  67. package/dist-skill/{docs → docs-dev}/blueprint-first.md +5 -0
  68. package/dist-skill/{docs → docs-dev}/coding-best-practices.md +6 -8
  69. package/dist-skill/{docs → docs-dev}/coding.md +1 -3
  70. package/dist-skill/docs-dev/generated/assembly.md +771 -0
  71. package/dist-skill/docs-dev/generated/core.md +775 -0
  72. package/dist-skill/docs-dev/generated/curves.md +688 -0
  73. package/dist-skill/docs-dev/generated/lib.md +50 -0
  74. package/dist-skill/docs-dev/generated/output.md +234 -0
  75. package/dist-skill/docs-dev/generated/sheet-metal.md +506 -0
  76. package/dist-skill/docs-dev/generated/sketch.md +801 -0
  77. package/dist-skill/docs-dev/generated/viewport.md +486 -0
  78. package/dist-skill/docs-dev/generated/wood.md +104 -0
  79. package/dist-skill/docs-dev/guides/coordinate-system.md +46 -0
  80. package/dist-skill/docs-dev/guides/geometry-conventions.md +52 -0
  81. package/dist-skill/docs-dev/guides/modeling-recipes.md +77 -0
  82. package/dist-skill/docs-dev/guides/positioning.md +151 -0
  83. package/dist-skill/{docs → docs-dev}/guides/skill-maintenance.md +21 -10
  84. package/dist-skill/{docs → docs-dev}/internals/compiler.md +5 -6
  85. package/dist-skill/{docs → docs-dev}/internals/constraint-solver-quality.md +0 -1
  86. package/dist-skill/{docs → docs-dev}/internals/constraint-solver.md +0 -1
  87. package/dist-skill/{docs → docs-dev}/internals/sketch-2d-pipeline.md +2 -3
  88. package/examples/api/attachTo-basics.forge.js +5 -5
  89. package/examples/api/boolean-operations.forge.js +3 -3
  90. package/examples/api/bounding-box-visualizer.forge.js +2 -2
  91. package/examples/api/clone-duplicate.forge.js +1 -1
  92. package/examples/api/colors-union-vs-array.forge.js +6 -6
  93. package/examples/api/connector-assembly.forge.js +4 -4
  94. package/examples/api/connector-basics.forge.js +2 -2
  95. package/examples/api/extrude-options.forge.js +4 -10
  96. package/examples/api/feature-created-faces.forge.js +6 -10
  97. package/examples/api/fillet-showcase.forge.js +1 -1
  98. package/examples/api/folded-service-panel-cover.forge.js +2 -2
  99. package/examples/api/group-test.forge.js +1 -1
  100. package/examples/api/group-vs-union.forge.js +1 -1
  101. package/examples/api/highlight-debug.forge.js +4 -0
  102. package/examples/api/js-module-pillars.js +1 -1
  103. package/examples/api/js-module-scene.js +2 -2
  104. package/examples/api/mesh-import-slats.forge.js +1 -1
  105. package/examples/api/pointAlong-orientation.forge.js +1 -1
  106. package/examples/api/profile-2020-b-slot6.forge.js +0 -1
  107. package/examples/api/route-perimeter-flange.forge.js +1 -1
  108. package/examples/api/sdf-rover-demo.forge.js +10 -10
  109. package/examples/api/sketch-on-face-demo.forge.js +2 -2
  110. package/examples/api/sketch-regions.forge.js +4 -4
  111. package/examples/api/transition-curves.forge.js +1 -1
  112. package/examples/api/variable-sweep-pure-sdf-test.forge.js +162 -0
  113. package/examples/api/variable-sweep-test.forge.js +2 -2
  114. package/examples/api/wood-joinery.forge.js +60 -0
  115. package/examples/compiler-corpus/enclosure-shell-cuts.forge.js +3 -3
  116. package/examples/compiler-corpus/fastener-plate-variants.forge.js +2 -2
  117. package/examples/experiments/drone-arm.forge.js +53 -0
  118. package/examples/furniture/adjustable-table.forge.js +2 -2
  119. package/examples/furniture/bathroom.forge.js +11 -11
  120. package/examples/furniture/chair.forge.js +1 -1
  121. package/examples/generative/crystal-growth.forge.js +2 -2
  122. package/examples/generative/frost-spires.forge.js +3 -3
  123. package/examples/generative/golden-spiral-tower.forge.js +3 -3
  124. package/examples/mechanical/3d-printer.forge.js +28 -28
  125. package/examples/mechanical/5-finger-robot-hand.forge.js +15 -15
  126. package/examples/mechanical/airplane-propeller.forge.js +2 -2
  127. package/examples/mechanical/fillet-enclosure.forge.js +1 -1
  128. package/examples/mechanical/headphone-hanger-v2.forge.js +2 -2
  129. package/examples/mechanical/robot_hand.forge.js +15 -15
  130. package/examples/mechanical/robot_hand_2.forge.js +9 -9
  131. package/examples/products/bottle.forge.js +1 -1
  132. package/examples/products/chess-set.forge.js +19 -19
  133. package/examples/products/classical-piano.forge.js +11 -11
  134. package/examples/products/clock.forge.js +12 -12
  135. package/examples/products/iphone.forge.js +8 -8
  136. package/examples/products/laptop.forge.js +15 -15
  137. package/examples/products/liquid-soap-dispenser.forge.js +18 -18
  138. package/examples/products/origami-fish.forge.js +8 -6
  139. package/examples/products/spiderman-cake.forge.js +4 -4
  140. package/examples/toolbox/bolted-joint.forge.js +2 -2
  141. package/package.json +7 -4
  142. package/dist/assets/EditorApp-B-vQvgam.js +0 -9888
  143. package/dist/assets/LandingPage-C5n9hDXI.js +0 -322
  144. package/dist/assets/PublishedModelPage-Dt7PCVBj.js +0 -146
  145. package/dist/assets/__vite-browser-external-CURh0WXD.js +0 -8
  146. package/dist/assets/deserializeRunResult-BLAFoiE0.js +0 -19365
  147. package/dist/assets/index-1CYp3zUp.js +0 -1455
  148. package/dist/docs-raw/CLI.md +0 -865
  149. package/dist-skill/docs/API/API.md +0 -1666
  150. package/dist-skill/docs/API/README.md +0 -37
  151. package/dist-skill/docs/API/assembly/assembly.md +0 -617
  152. package/dist-skill/docs/API/core/edge-queries.md +0 -130
  153. package/dist-skill/docs/API/core/parameters.md +0 -122
  154. package/dist-skill/docs/API/core/reserved-terms.md +0 -137
  155. package/dist-skill/docs/API/core/sdf.md +0 -326
  156. package/dist-skill/docs/API/core/skill-cli.md +0 -194
  157. package/dist-skill/docs/API/core/skill-guide.md +0 -205
  158. package/dist-skill/docs/API/core/specs.md +0 -186
  159. package/dist-skill/docs/API/core/topology.md +0 -372
  160. package/dist-skill/docs/API/entities.md +0 -268
  161. package/dist-skill/docs/API/output/bom.md +0 -58
  162. package/dist-skill/docs/API/output/brep-export.md +0 -87
  163. package/dist-skill/docs/API/output/dimensions.md +0 -67
  164. package/dist-skill/docs/API/output/export.md +0 -110
  165. package/dist-skill/docs/API/output/gcode.md +0 -195
  166. package/dist-skill/docs/API/runtime/viewport.md +0 -420
  167. package/dist-skill/docs/API/sheet-metal/sheet-metal.md +0 -185
  168. package/dist-skill/docs/API/sketch/anchor.md +0 -37
  169. package/dist-skill/docs/API/sketch/booleans.md +0 -91
  170. package/dist-skill/docs/API/sketch/core.md +0 -73
  171. package/dist-skill/docs/API/sketch/extrude.md +0 -62
  172. package/dist-skill/docs/API/sketch/on-face.md +0 -104
  173. package/dist-skill/docs/API/sketch/operations.md +0 -78
  174. package/dist-skill/docs/API/sketch/path.md +0 -75
  175. package/dist-skill/docs/API/sketch/primitives.md +0 -146
  176. package/dist-skill/docs/API/sketch/regions.md +0 -80
  177. package/dist-skill/docs/API/sketch/text.md +0 -108
  178. package/dist-skill/docs/API/sketch/transforms.md +0 -65
  179. package/dist-skill/docs/API/toolbox/fasteners.md +0 -129
  180. package/dist-skill/docs/CLI.md +0 -865
  181. package/dist-skill/docs/INDEX.md +0 -94
  182. package/dist-skill/docs/RELEASING.md +0 -55
  183. package/dist-skill/docs/cli-monetization.md +0 -111
  184. package/dist-skill/docs/deployment.md +0 -281
  185. package/dist-skill/docs/generated/concepts.md +0 -2112
  186. package/dist-skill/docs/internals/shape-from-slices.md +0 -152
  187. package/dist-skill/docs/platform/admin.md +0 -45
  188. package/dist-skill/docs/platform/architecture.md +0 -79
  189. package/dist-skill/docs/platform/auth.md +0 -110
  190. package/dist-skill/docs/platform/email.md +0 -67
  191. package/dist-skill/docs/platform/projects.md +0 -111
  192. package/dist-skill/docs/platform/sharing.md +0 -90
  193. 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
- ```