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
@@ -5,206 +5,147 @@ skill-order: 3
5
5
 
6
6
  # Positioning Strategy
7
7
 
8
- **This is the most important page for building multi-part assemblies.** Most positioning bugs come from manual coordinate arithmetic. Use the methods below in priority order.
8
+ ## Primitive origin convention
9
9
 
10
- ## Priority Order
10
+ All 3D primitives are **centered on XY, base at Z=0**:
11
11
 
12
- ### 1. Connectors + `matchTo()` Default choice for multi-part positioning
12
+ | Primitive | X range | Y range | Z range |
13
+ |-----------|---------|---------|---------|
14
+ | `box(60, 40, 20)` | [-30, 30] | [-20, 20] | [0, 20] |
15
+ | `cylinder(50, 10)` | [-10, 10] | [-10, 10] | [0, 50] |
16
+ | `sphere(15)` | [-15, 15] | [-15, 15] | [-15, 15] |
17
+ | `torus(20, 5)` | [-25, 25] | [-25, 25] | [-5, 5] |
13
18
 
14
- Define connectors on parts, then use `matchTo()` for automatic 6-DOF alignment. The child translates and rotates so its connector aligns with the target's connector origins coincide, axes oppose (plug-in model).
19
+ Sphere and torus are fully centered (symmetric in Z). Box and cylinder sit on the XY ground plane **Z goes up from zero, never negative**.
15
20
 
16
- ```javascript
17
- // Define connectors on parts
18
- const shelf = box(200, 120, 10, true).withConnectors({
19
- left_tab: connector.male("dovetail", { origin: [-100, 0, 0], axis: [-1, 0, 0] }),
20
- right_tab: connector.male("dovetail", { origin: [100, 0, 0], axis: [1, 0, 0] }),
21
- });
21
+ This means `box(w, d, h).translate(0, 0, -h/2)` is wrong for "center on Z" — the box is already at `[0, h]`, not `[-h/2, h/2]`.
22
22
 
23
- const panel = box(12, 120, 200, true).withConnectors({
24
- shelf_0: connector.female("dovetail", { origin: [6, 0, -50], axis: [1, 0, 0] }),
25
- shelf_1: connector.female("dovetail", { origin: [6, 0, 50], axis: [1, 0, 0] }),
26
- });
27
-
28
- // Single pair: "put my left_tab into panel's shelf_0"
29
- const placed = shelf.matchTo(panel, "left_tab", "shelf_0");
23
+ ---
30
24
 
31
- // Dictionary syntax for multiple pairs (same target):
32
- const placed2 = shelf.matchTo(panel, { left_tab: "shelf_0" });
25
+ Most positioning bugs come from manual coordinate arithmetic. Use these methods in priority order.
33
26
 
34
- // Named group children auto-bubble connectors via dotted paths:
35
- const cabinet = group({ name: "Left", shape: panel });
36
- shelf.matchTo(cabinet, "left_tab", "Left.shelf_0");
37
- ```
27
+ ## 1. `group()` local coordinates for multi-part assemblies
38
28
 
39
- **Why connectors first:**
40
- - **Stable.** Connector positions are authored explicitly — they don't shift when you fillet, chamfer, or boolean the part.
41
- - **Semantic.** Connectors carry type and gender — `assembly.match()` validates compatibility before connecting.
42
- - **Oriented.** Full coordinate frame (origin + axis + up), not just a point.
43
- - **Queryable.** `shape.connectorDistance('a', 'b')` lets you derive dimensions from connector positions — e.g. compute pipe length from the distance between two fittings.
44
- - **Explode-aware.** Matched parts automatically separate along connector axes when the explode slider is used.
29
+ The most common positioning bug: manually adding a parent's global offset to every sub-part. One wrong sign or forgotten variable and parts float into space. **Use `group()` to build parts in local coordinates (at the origin), then position the group once.**
45
30
 
46
- **Connector patterns:**
47
31
  ```javascript
48
- // Pipe length derived from connector spacing
49
- const length = frame.connectorDistance('inlet', 'outlet');
50
- const pipe = cylinder(length, radius);
51
-
52
- // Assembly-level connector matching with joint creation
53
- assembly
54
- .addPart("Frame", frame)
55
- .addPart("Door", door)
56
- .match("Door", "Frame", { hinge_top: "hinge_top", hinge_bottom: "hinge_bottom" });
57
-
58
- // Query connector metadata
59
- const measurements = fitting.connectorMeasurements('flange');
60
- const outerDiam = measurements.outerDiameter;
32
+ // BAD every sub-part repeats the parent's global position
33
+ const unitY = -18, unitZ = 70;
34
+ const body = lib.roundedBox(100, 20, 32, 4).translate(0, unitY, unitZ);
35
+ const panel = box(98, 2, 18).translate(0, unitY - 12, unitZ + 4);
36
+ const louver = box(88, 2, 6).translate(0, unitY - 14, unitZ - 11);
37
+ const led = sphere(1.2).translate(35, unitY - 12, unitZ + 9);
38
+
39
+ // GOOD — build at local origin, group, translate once
40
+ const body = lib.roundedBox(100, 20, 32, 4);
41
+ const panel = box(98, 2, 18).translate(0, -12, 4); // relative to local origin
42
+ const louver = box(88, 2, 6).translate(0, -14, -11); // relative to local origin
43
+ const led = sphere(1.2).translate(35, -12, 9); // relative to local origin
44
+ const indoorUnit = group(
45
+ { name: 'Body', shape: body },
46
+ { name: 'Panel', shape: panel },
47
+ { name: 'Louver', shape: louver },
48
+ { name: 'LED', shape: led },
49
+ ).translate(0, -18, 70); // ONE translate for the whole assembly
61
50
  ```
62
51
 
63
- ### 2. `pointAlong()`Orient cylinders/extrusions before positioning
64
-
65
- Cylinders default to Z-up. Instead of `rotate(90, 0, 0)` (which is confusing), use `pointAlong()`:
52
+ **Groups nest.** Build sub-assemblies as groups, then group those into larger assemblies each level has its own local origin.
66
53
 
67
54
  ```javascript
68
- // Pipe running along Y axis
69
- const pipe = cylinder(100, 5).pointAlong([0, 1, 0]);
70
-
71
- // Axle along X
72
- const axle = cylinder(80, 3).pointAlong([1, 0, 0]);
55
+ const fan = group(hub, ...blades).translate(0, 25, 0); // fan assembly
56
+ const outdoorUnit = group(
57
+ { name: 'Body', shape: casing },
58
+ { name: 'Fan', shape: fan }, // already a group
59
+ { name: 'Grille', shape: grille },
60
+ ).translate(0, 23, -42); // position the whole outdoor unit
73
61
  ```
74
62
 
75
- **Always call `pointAlong()` BEFORE `matchTo()` or `translate()`** it reorients around the origin.
63
+ **When to use something else:** `group()` preserves individual shapes — you can't boolean (subtract/intersect) a group. If a sub-part needs a boolean with the parent body, do that boolean first in local coordinates, then group the result.
76
64
 
77
- ### 3. `attachTo()` — Quick bounding-box positioning
65
+ ## 2. Connectors + `matchTo()` — default for mating interfaces
78
66
 
79
- For simple "stack this on that" placement where you don't need connectors. Uses bounding-box anchor points fast to write but fragile if geometry changes.
67
+ Define connectors on parts; `matchTo()` provides automatic 6-DOF alignment. The child translates and rotates so its connector aligns with the target's origins coincide, axes oppose (plug-in model).
80
68
 
81
69
  ```javascript
82
- const base = box(100, 100, 10);
83
- const column = cylinder(50, 8).attachTo(base, 'top', 'bottom');
70
+ const shelf = box(200, 120, 10, true).withConnectors({
71
+ left_tab: connector.male("dovetail", { origin: [-100, 0, 0], axis: [-1, 0, 0] }),
72
+ });
73
+ const panel = box(12, 120, 200, true).withConnectors({
74
+ shelf_0: connector.female("dovetail", { origin: [6, 0, -50], axis: [1, 0, 0] }),
75
+ });
76
+ const placed = shelf.matchTo(panel, "left_tab", "shelf_0");
77
+ // Dictionary form for multiple pairs on same target:
78
+ const placed2 = shelf.matchTo(panel, { left_tab: "shelf_0" });
79
+ // Named group children bubble connectors via dotted paths:
80
+ const cabinet = group({ name: "Left", shape: panel });
81
+ shelf.matchTo(cabinet, "left_tab", "Left.shelf_0");
84
82
  ```
85
83
 
86
- `child.attachTo(parent, parentAnchor, selfAnchor, offset)` parentAnchor/selfAnchor are bounding-box anchors like `'top'`, `'bottom-front-left'`, etc.
87
-
88
- **Limitations:** Anchor points are derived from the bounding box, so they shift when you fillet, chamfer, or boolean the part. No orientation, no validation, no semantic meaning. Prefer connectors for anything beyond quick prototyping.
89
-
90
- ### 5. `rotateAroundTo()` — Aim a point around a hinge/axis
84
+ **Why connectors first:** stable (don't shift on fillet/chamfer/boolean), semantic (carry type/gender), oriented (full frame), queryable (`shape.connectorDistance('a','b')`), explode-aware.
91
85
 
92
- Use this when a part already has the correct pivot/axis, and you want to solve the angle from geometry instead of doing trig by hand.
86
+ ## 3. `pointAlong()` orient cylinders before positioning
93
87
 
94
88
  ```javascript
95
- const arm = box(80, 8, 8, true)
96
- .translate(40, 0, 0)
97
- .withReferences({ points: { tip: [80, 0, 0] } });
98
-
99
- // Rotate around Z until the tip lies in the plane formed by the Z axis and the target point
100
- const aimed = arm.rotateAroundTo(
101
- [0, 0, 1],
102
- [0, 0, 0],
103
- "tip",
104
- [30, 30, 20],
105
- );
106
-
107
- // Exact line solve: throws if the target line is unreachable while preserving radius about the axis
108
- const lineHit = arm.rotateAroundTo(
109
- [0, 0, 1],
110
- [0, 0, 0],
111
- "tip",
112
- [30, 30, 0],
113
- { mode: 'line' },
114
- );
89
+ // BAD
90
+ const pipe = cylinder(100, 5).rotateX(90).translate(x, y, z);
91
+ // GOOD reads as "pipe pointing along Y"
92
+ const pipe = cylinder(100, 5).pointAlong([0, 1, 0]).translate(x, y, z);
115
93
  ```
116
94
 
117
- ### 6. `moveToLocal()` — Position relative to another shape's corner
95
+ **Always call `pointAlong()` BEFORE `matchTo()` or `translate()`** it reorients around the origin.
118
96
 
119
- When you need to place something at a specific offset from another shape's bounding box origin (min corner):
97
+ ## 4. `attachTo()` quick bounding-box positioning
120
98
 
121
99
  ```javascript
122
- const base = box(100, 100, 10);
123
- const part = box(20, 20, 30).moveToLocal(base, 10, 10, 10);
100
+ const column = cylinder(50, 8).attachTo(base, 'top', 'bottom');
124
101
  ```
125
102
 
126
- ### 7. `translate()`Only for simple offsets or connecting independently-positioned parts
103
+ `child.attachTo(parent, parentAnchor, selfAnchor, offset)`. Anchor points shift on fillet/chamfer/boolean fragile for assembly interfaces, fine for quick prototyping.
127
104
 
128
- Use `translate()` when:
129
- - Moving a shape by a known fixed amount
130
- - Positioning between two shapes whose locations you've already computed via `boundingBox()`
105
+ ## 5. `rotateAroundTo()` — aim a point around a hinge/axis
131
106
 
132
107
  ```javascript
133
- // Pipe spanning between two independently-positioned units
134
- const bb1 = indoor.boundingBox();
135
- const bb2 = outdoor.boundingBox();
136
- const pipeLen = bb2.min[1] - bb1.max[1];
137
- const pipe = cylinder(pipeLen, 5)
138
- .pointAlong([0, 1, 0])
139
- .translate(40, (bb1.max[1] + bb2.min[1]) / 2, bb1.min[2] + 15);
108
+ const aimed = arm.rotateAroundTo([0, 0, 1], [0, 0, 0], "tip", [30, 30, 20]);
109
+ // Exact line solve:
110
+ const lineHit = arm.rotateAroundTo([0, 0, 1], [0, 0, 0], "tip", [30, 30, 0], { mode: 'line' });
140
111
  ```
141
112
 
142
- ### 8. `placeReference()` / named import references For reusable multi-file parts
143
-
144
- When a part will be imported elsewhere, define semantic placement references once in the source file:
113
+ ## 6. `moveToLocal()` offset from another shape's min corner
145
114
 
146
115
  ```javascript
147
- // widget.forge.js
148
- return union(base, post).withReferences({
149
- points: {
150
- mount: [0, -16, -4],
151
- },
152
- objects: {
153
- post,
154
- },
155
- });
116
+ const part = box(20, 20, 30).moveToLocal(base, 10, 10, 10);
156
117
  ```
157
118
 
158
- Then consume them in the importing file:
119
+ ## 7. `translate()` for simple offsets or bridging computed locations
159
120
 
160
121
  ```javascript
161
- const widget = require("./widget.forge.js")
162
- .placeReference("mount", [120, 40, 0]);
122
+ const pipeLen = bb2.min[1] - bb1.max[1];
123
+ const pipe = cylinder(pipeLen, 5).pointAlong([0, 1, 0]).translate(40, (bb1.max[1] + bb2.min[1]) / 2, bb1.min[2] + 15);
163
124
  ```
164
125
 
165
- Use this when manual coordinate math starts to feel like assembly bookkeeping. For cross-file parts that need proper alignment, prefer connectors on the exported shape over placement references.
126
+ ## 8. `placeReference()` align any anchor to a world coordinate
166
127
 
167
- ## Common Mistakes
128
+ Place a shape so a named anchor point lands exactly where you want it. Accepts all built-in anchors (`'bottom'`, `'center'`, `'top-front-left'`, etc.) plus custom references from `withReferences()`.
168
129
 
169
- ### ❌ Manual center-offset math
170
130
  ```javascript
171
- // BAD: easy to get wrong, hard to read
172
- const child = box(w, d, h, true)
173
- .translate(0, -parentThickness/2 - d/2 - 5, parentHeight/2 - h/2 - 20);
174
- ```
131
+ // Ground a shape bottom face center at Z = 0
132
+ const grounded = shape.placeReference('bottom', [0, 0, 0])
175
133
 
176
- ### Connector-based positioning
177
- ```javascript
178
- // GOOD: stable, semantic, self-documenting
179
- const parent = box(100, 40, 60, true).withConnectors({
180
- mount: connector.female("bracket", { origin: [0, -20, 10], axis: [0, -1, 0] }),
181
- });
182
- const child = box(w, d, h, true).withConnectors({
183
- tab: connector.male("bracket", { origin: [0, d/2, 0], axis: [0, 1, 0] }),
184
- });
185
- const placed = child.matchTo(parent, "tab", "mount");
186
- ```
134
+ // Center at the world origin
135
+ const centered = shape.placeReference('center', [0, 0, 0])
187
136
 
188
- ### rotate() for cylinder orientation
189
- ```javascript
190
- // BAD: which axis? what happens to center?
191
- const pipe = cylinder(100, 5).rotate(90, 0, 0).translate(x, y, z);
137
+ // Align left edge to X = 10
138
+ const aligned = shape.placeReference('left', [10, 0, 0])
192
139
  ```
193
140
 
194
- ### pointAlong() for cylinder orientation
195
- ```javascript
196
- // GOOD: reads as "pipe pointing along Y"
197
- const pipe = cylinder(100, 5).pointAlong([0, 1, 0]).translate(x, y, z);
198
- ```
141
+ Also works with custom placement references for cross-file parts:
199
142
 
200
- ### ❌ Bounding-box positioning for assembly interfaces
201
143
  ```javascript
202
- // BAD: fragile changes if parent geometry is filleted or modified
203
- const shelf = plank.attachTo(sidePanel, 'left', 'right', [0, 0, -50]);
204
- ```
144
+ // widget.forge.jsdefine once
145
+ return union(base, post).withReferences({ points: { mount: [0, -16, -4] } });
205
146
 
206
- ### Connectors for assembly interfaces
207
- ```javascript
208
- // GOOD: connector positions are explicit and stable
209
- const shelf = plank.matchTo(sidePanel, "left_tab", "shelf_slot_0");
147
+ // importer consume
148
+ const widget = require("./widget.forge.js").placeReference("mount", [120, 40, 0]);
210
149
  ```
150
+
151
+ For cross-file parts needing proper alignment, prefer connectors over placement references.
@@ -0,0 +1,51 @@
1
+ ---
2
+ skill-group: core
3
+ skill-order: 1
4
+ ---
5
+
6
+ # ForgeCAD Core Concepts
7
+
8
+ ForgeCAD scripts are JavaScript that returns geometry. The forge API is globally available — no imports needed.
9
+
10
+ ```javascript
11
+ const width = param("Width", 50, { min: 20, max: 100, unit: "mm" });
12
+ return box(width, 30, 10);
13
+ ```
14
+
15
+ ## Execution Model
16
+
17
+ - Scripts re-execute on every parameter change (400ms debounce)
18
+ - All operations are **immutable** — return new shapes, never modify in place
19
+ - Must return one of: `Shape`, `Sketch`, `ShapeGroup`, `Array` of shapes/sketches/groups, or `Array` of `{ name, shape?, sketch?, color? }`
20
+
21
+ ## Coordinate System
22
+
23
+ Z-up right-handed: X = left/right, Y = forward/back, Z = up/down.
24
+
25
+ ## Colors
26
+
27
+ `.color(hex)` works on `Shape` and `Sketch`. Colors survive transforms. In booleans the first operand's color wins.
28
+
29
+ **`union()` removes colors** — shapes merge into one solid mesh. Return named objects instead:
30
+
31
+ ```javascript
32
+ return [
33
+ { name: "Base", shape: box(100, 100, 5), color: "#888888" },
34
+ { name: "Column", shape: cylinder(50, 10).translate(50, 50, 5), color: "#4488cc" },
35
+ ];
36
+ ```
37
+
38
+ ## Face Operations
39
+
40
+ Shapes carry semantic face labels through their lifecycle. The flow is:
41
+
42
+ 1. **Primitives** assign canonical names — `box()` gives you `top`, `bottom`, `side-left`, etc.; `cylinder()` gives `top`, `bottom`, `side`.
43
+ 2. **Extrusions** inherit labels from the sketch and add `top`/`bottom`.
44
+ 3. **Transforms** (translate, rotate, scale, mirror) preserve all labels.
45
+ 4. **Booleans** preserve labels from the first operand where geometry survives.
46
+
47
+ You resolve labels to geometry with `.face(name)` or `.face(query)` — see the Shape class docs for the full query API. Operations like `.pocket()`, `.boss()`, `.hole()`, and `faceProfile()` all consume face references.
48
+
49
+ ## SDF Modeling
50
+
51
+ For organic shapes, smooth blending, TPMS lattices, and surface deformations. SDF shapes convert via `.toShape()`. See [sdf-primitives.md](sdf-primitives.md).
@@ -0,0 +1,92 @@
1
+ ---
2
+ skill-group: dev-sdf
3
+ skill-order: 2
4
+ ---
5
+
6
+ # SDF Advanced Operations
7
+
8
+ ## TPMS Lattices
9
+
10
+ ```javascript
11
+ sdf.gyroid({ cellSize, thickness }) // most common for 3D printing
12
+ sdf.schwarzP({ cellSize, thickness }) // isotropic pores
13
+ sdf.diamond({ cellSize, thickness }) // stiffest structure
14
+ ```
15
+
16
+ TPMS fills all space — **always clip with an intersect before `.toShape()`**:
17
+
18
+ ```javascript
19
+ const lattice = sdf.gyroid({ cellSize: 8, thickness: 1.2 })
20
+ .intersect(sdf.sphere(25))
21
+ .toShape();
22
+ ```
23
+
24
+ `cellSize`: larger = coarser. `thickness`: thicker = denser.
25
+
26
+ ## Domain Operations
27
+
28
+ ### Twist
29
+ Rotates slices around Y axis as function of Y position.
30
+ ```javascript
31
+ sdf.cylinder(50, 8).twist(0.9).toShape() // 45° total over 50mm (0.9 deg/mm)
32
+ ```
33
+
34
+ ### Bend
35
+ Bends around Z axis. Smaller radius = tighter arc.
36
+ ```javascript
37
+ sdf.cylinder(60, 5).bend(20).toShape() // arch shape
38
+ ```
39
+
40
+ ### Repeat
41
+ Tiles in space. Spacing `0` = no repeat on that axis. Count `0` = infinite.
42
+ ```javascript
43
+ sdf.sphere(4).repeat([15, 15, 0], [3, 3, 0]).toShape() // 3×3 grid
44
+ ```
45
+
46
+ ### Shell
47
+ Hollows to surface shell of given thickness.
48
+ ```javascript
49
+ sdf.sphere(20).shell(2).subtract(sdf.box(60, 60, 30).translate(0, 0, -15)).toShape()
50
+ ```
51
+
52
+ ### Displace
53
+ Offsets surface by a function of position.
54
+ ```javascript
55
+ sdf.sphere(15).displace((x, y, z) => Math.sin(x * 0.8) * Math.sin(y * 0.8) * Math.sin(z * 0.8) * 2).toShape()
56
+ ```
57
+
58
+ **Critical gotcha for `displace()` and `fromFunction()`:** The function is serialized as a string and re-evaluated via `new Function("x","y","z", "return ...")`. This means:
59
+ - **No closure variables** — cannot reference `param()` values inside
60
+ - **Single expression only** — no `const`, `let`, `if`, `for`
61
+ - Multi-statement blocks **silently produce bad geometry**
62
+
63
+ ### Onion
64
+ Concentric shells.
65
+ ```javascript
66
+ sdf.sphere(20).onion(3, 2).toShape() // 3 shells, 2mm apart
67
+ ```
68
+
69
+ ## Morphing
70
+
71
+ Interpolate between two shapes. `t=0` = a, `t=1` = b.
72
+ ```javascript
73
+ sdf.morph(sdf.sphere(12), sdf.box(20, 20, 20), 0.5).toShape()
74
+ a.morph(b, t) // method form
75
+ ```
76
+
77
+ ## Custom SDF Functions
78
+
79
+ ```javascript
80
+ sdf.fromFunction(fn, { min: [x0, y0, z0], max: [x1, y1, z1] })
81
+ ```
82
+
83
+ `fn(x, y, z)` returns signed distance: negative = inside, positive = outside, zero = surface. Bounds are required — provide tightly to avoid wasted computation.
84
+
85
+ ```javascript
86
+ const heart = sdf.fromFunction(
87
+ (x, y, z) => (x*x + z*z*1.1 + y*y - 1)**3 - x*x*y*y*y - z*z*y*y*y * 0.11,
88
+ { min: [-20, -25, -15], max: [20, 20, 15] }
89
+ ).toShape();
90
+ ```
91
+
92
+ Same serialization gotcha as `displace()` — no closure variables, single expression only.
@@ -0,0 +1,58 @@
1
+ ---
2
+ skill-group: dev-sdf
3
+ skill-order: 1
4
+ ---
5
+
6
+ # SDF Primitives & Booleans
7
+
8
+ > **Experimental.** Slower render times than B-rep; lower mesh quality from marching-tetrahedra extraction. Use for organic forms, TPMS lattices, smooth blending. For mechanical parts, prefer B-rep.
9
+
10
+ SDF operations are accessed via the globally available `sdf` namespace. All shapes live as a lazy expression tree until `.toShape()` is called.
11
+
12
+ ## Primitives
13
+
14
+ All centered at origin unless noted.
15
+
16
+ ```javascript
17
+ sdf.sphere(radius)
18
+ sdf.box(x, y, z) // full dimensions — box(20,20,20) → 20mm cube
19
+ sdf.cylinder(height, radius) // axis along Y (rotate 90,0,0 for Z-axis)
20
+ sdf.torus(majorRadius, minorRadius) // ring in XZ plane (hole axis = Y)
21
+ sdf.capsule(height, radius) // axis along Y
22
+ sdf.cone(height, radius) // base at y=0, tip at y=height (not centered)
23
+ ```
24
+
25
+ ## Boolean Operations
26
+
27
+ ```javascript
28
+ // Sharp
29
+ a.union(b, c, d); a.subtract(b); a.intersect(b)
30
+
31
+ // Smooth — blend over transition radius
32
+ sdf.smoothUnion(a, b, { radius: 5 }) // factory: radius in options object
33
+ sdf.smoothDifference(a, b, { radius: 5 })
34
+ sdf.smoothIntersection(a, b, { radius: 5 })
35
+
36
+ a.smoothUnion(b, 5) // method: radius as direct number
37
+ a.smoothSubtract(b, 5) // note: smoothSubtract, not smoothDifference
38
+ a.smoothIntersect(b, 5)
39
+ ```
40
+
41
+ **API mismatch:** Factory functions take `{ radius }` (object). Instance methods take `radius` (number). Don't mix them.
42
+
43
+ ## Transforms
44
+
45
+ ```javascript
46
+ shape.translate(x, y, z)
47
+ shape.rotateX(deg) // also rotateY, rotateZ, rotate(axis, deg)
48
+ shape.scale(factor) // uniform only
49
+ ```
50
+
51
+ ## Reserved Names
52
+
53
+ The global scope defines `sphere`, `box`, `cylinder`, `torus`, `capsule`, `cone`, `shell` as B-rep primitives. Always use `sdf.*` prefix — and avoid naming local variables after these globals:
54
+
55
+ ```javascript
56
+ const orb = sdf.sphere(10); // correct — not 'sphere'
57
+ const s = sdf.sphere(10); // bad — shadows global 'sphere' if named 'sphere'
58
+ ```
@@ -0,0 +1,42 @@
1
+ ---
2
+ skill-group: dev-sdf
3
+ skill-order: 3
4
+ ---
5
+
6
+ # SDF Workflow & Mesh Quality
7
+
8
+ ## Converting to Shape
9
+
10
+ ```javascript
11
+ shape.toShape()
12
+ shape.toShape({ edgeLength: 0.5 }) // finer mesh
13
+ shape.toShape({ bounds: { min: [...], max: [...] } }) // override auto bounds
14
+ ```
15
+
16
+ | Option | Default | Effect |
17
+ |--------|---------|--------|
18
+ | `edgeLength` | `maxDim / 100` | Smaller = smoother, slower |
19
+ | `bounds` | auto-estimated | Override sampling volume |
20
+
21
+ For smooth shapes use `0.3–0.5`. For fast previews use `1–2`.
22
+
23
+ After meshing, Laplacian smoothing + SDF projection runs automatically (2 iterations) to reduce axis-aligned triangle artifacts.
24
+
25
+ ## Workflow Tips
26
+
27
+ - **Start coarse, refine last** — design at `edgeLength: 2`, drop to `0.5` for output
28
+ - **Clip TPMS before `toShape()`** — intersect with bounding shape first
29
+ - **Compose before meshing** — do all booleans/deformations in SDF space, then call `.toShape()` once
30
+ - **Mix with B-rep freely** — after `.toShape()`, use in `difference()`, `union()`, `.fillet()`, etc.
31
+
32
+ ```javascript
33
+ const organicBase = sdf.smoothUnion(sdf.sphere(20), sdf.box(30, 30, 10), { radius: 8 }).toShape();
34
+ return difference(organicBase, cylinder(15, 3)); // B-rep cut on SDF result
35
+ ```
36
+
37
+ ## Serialization Gotcha
38
+
39
+ `displace()` and `fromFunction()` serialize the function body via `new Function("x","y","z", "return ...")`:
40
+ - **No closure variables** — cannot reference `param()` values
41
+ - **Single expression only** — no `const`, `let`, `if`, `for`
42
+ - Multi-statement blocks silently produce bad geometry
@@ -0,0 +1,91 @@
1
+ ---
2
+ skill-group: cli
3
+ skill-order: 3
4
+ ---
5
+
6
+ # ForgeCAD CLI: Export Commands
7
+
8
+ ## SVG Export
9
+
10
+ ```bash
11
+ forgecad export svg examples/constraints/01-fully-constrained-rect.forge.js [output.svg]
12
+ ```
13
+
14
+ Pure Node — no browser needed. Runs the sketch script through the forge engine and converts polygons to SVG paths.
15
+
16
+ ## STEP / BREP Export (exact subset, CadQuery)
17
+
18
+ ```bash
19
+ forgecad export step examples/api/brep-exportable.forge.js [--output out/demo.step] [--python 3.11] [--uv /path/to/uv]
20
+ forgecad export brep examples/api/brep-exportable.forge.js
21
+ forgecad export step examples/chess-set.forge.js --allow-faceted
22
+ ```
23
+
24
+ `uv`-first: provisions CadQuery automatically. Exact-subset only by default — fails with a reason rather than silently exporting degraded geometry. With `--allow-faceted`, unsupported mesh solids export as faceted OCCT solids (tessellation-driven, not exact replay).
25
+
26
+ The maintained feature matrix: `docs/permanent/API/output/brep-export.md`.
27
+
28
+ ## G-code Toolpath Export
29
+
30
+ ```bash
31
+ forgecad export gcode examples/gcode/parametric-vase.forge.js [--output out/vase.gcode]
32
+ ```
33
+
34
+ The script must return a `GCodeBuilder` (from the `gcode()` factory). This is a toolpath scripting API, not a slicer — you define print movements in code. See `docs/permanent/API/output/gcode.md` for the full API.
35
+
36
+ ## SDF Robot Export (Gazebo)
37
+
38
+ ```bash
39
+ forgecad export sdf examples/api/sdf-rover-demo.forge.js [--output out/forge_scout]
40
+ ```
41
+
42
+ Writes a Gazebo-friendly package: `model.sdf`, `model.config`, `meshes/*.stl`, optional world file. Script must call `robotExport({...})` with an `assembly(...)` graph.
43
+
44
+ Launch flow (macOS — use split `-s`/`-g`):
45
+ ```bash
46
+ export GZ_SIM_RESOURCE_PATH="$PWD/out/forge_scout/models${GZ_SIM_RESOURCE_PATH:+:$GZ_SIM_RESOURCE_PATH}"
47
+ gz sim -s -r out/forge_scout/worlds/forge_scout_trial.sdf
48
+ gz sim -g out/forge_scout/worlds/forge_scout_trial.sdf
49
+ ```
50
+
51
+ ## URDF Export
52
+
53
+ ```bash
54
+ forgecad export urdf examples/api/sdf-rover-demo.forge.js
55
+ ```
56
+
57
+ ## PNG Render (requires Chrome/Puppeteer)
58
+
59
+ ```bash
60
+ forgecad render examples/cup.forge.js [output.png]
61
+ forgecad render examples/cup.forge.js out/scene.png --scene '<json from viewport>'
62
+ ```
63
+
64
+ Options: `--angles <front,side,top,iso>`, `--size <px>`, `--port <n>`, `--camera <spec>`, `--scene <json>`, `--background <color>`, `--chrome-path <path>`.
65
+
66
+ ## Animated Capture (GIF or MP4, requires Chrome)
67
+
68
+ ```bash
69
+ forgecad capture gif examples/cup.forge.js [output.gif]
70
+ forgecad capture mp4 examples/cup.forge.js [output.mp4]
71
+ forgecad capture mp4 examples/api/runtime-joints-view.forge.js out/step.mp4 --capture animation --animation Step
72
+ forgecad capture gif examples/3d-printer.forge.js out/section.gif --cut-plane "Front Section"
73
+ ```
74
+
75
+ `--list` prints available animation clips and cut planes. Uses `ffmpeg` when available (better GIF colors, H.264 MP4); falls back to pure-JS GIF encoder.
76
+
77
+ Key options: `--capture <orbit|animation>`, `--animation <name>`, `--cut-plane <name>`, `--camera <spec>`, `--scene <json>`, `--size <px>`, `--fps <n>`, `--frames-per-turn <n>`, `--quality <default|live|high>`.
78
+
79
+ Use `Copy CLI --scene` from the View Panel to grab the current viewport framing and paste into `render` or `capture`.
80
+
81
+ ## PDF Report
82
+
83
+ ```bash
84
+ forgecad export report examples/cup.forge.js [output.pdf] [--dim-angle-tol 18]
85
+ ```
86
+
87
+ Generates a searchable PDF with BOM page, combined model page, and per-component pages. Dimensions included per view when their axis aligns with that view's projection plane (within `--dim-angle-tol` degrees, default 12).
88
+
89
+ ## STL Export
90
+
91
+ Available in the browser UI via the Export panel (binary STL).