forgecad 0.6.3 → 0.8.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 (234) hide show
  1. package/README.md +3 -12
  2. package/dist/assets/{AdminPage-CeqCUUgu.js → AdminPage-D4bocK4E.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-D3A_g8V3.js} +329 -163
  5. package/dist/assets/{EditorApp-CnC2k4cW.css → EditorApp-BWYUSpUN.css} +590 -136
  6. package/dist/assets/EditorApp-Cihhqcsq.js +11692 -0
  7. package/dist/assets/{EmbedViewer-DBlzmQ5i.js → EmbedViewer-kWjKaC_t.js} +2 -4
  8. package/dist/assets/LandingPageProofDriven-Bg2IUc3l.css +856 -0
  9. package/dist/assets/LandingPageProofDriven-DXkKlyhI.js +601 -0
  10. package/dist/assets/PricingPage-BsU5vzEx.js +232 -0
  11. package/dist/assets/{SettingsPage-BqCh9JcC.js → SettingsPage-PqvpAKIs.js} +129 -5
  12. package/dist/assets/{evalWorker-Ql-aKwLA.js → evalWorker-C-hzNUMy.js} +8949 -3161
  13. package/dist/assets/{Viewport-CoB46f5R.js → index-Pz321YAt.js} +38382 -7501
  14. package/dist/assets/{index-2hfs_ub0.css → index-ay13WNfa.css} +726 -53
  15. package/dist/assets/{javascript-DCxGoE5Y.js → javascript-DAl8Gmyo.js} +1 -1
  16. package/dist/assets/{manifold-CqNMHHKO.js → manifold-BcbjWLIo.js} +4 -3
  17. package/dist/assets/{manifold-Cce9wRFz.js → manifold-DBckbFgx.js} +1 -1
  18. package/dist/assets/{manifold-D6BeHIOo.js → manifold-O2AAGXyj.js} +1 -1
  19. package/dist/assets/{reportWorker-sFEFonXf.js → reportWorker-Dxr-5A7w.js} +8760 -3559
  20. package/dist/assets/{vendor-react-Dt7-aaJH.js → vendor-react-CG3i_wp0.js} +65 -8
  21. package/dist/docs/index.html +2 -2
  22. package/dist/docs-raw/CLI.md +341 -718
  23. package/dist/docs-raw/generated/assembly.md +699 -112
  24. package/dist/docs-raw/generated/concepts.md +1834 -1346
  25. package/dist/docs-raw/generated/core.md +1012 -1059
  26. package/dist/docs-raw/generated/curves.md +759 -116
  27. package/dist/docs-raw/generated/lib.md +43 -748
  28. package/dist/docs-raw/generated/output.md +139 -245
  29. package/dist/docs-raw/generated/sdf.md +208 -0
  30. package/dist/docs-raw/generated/sheet-metal.md +473 -21
  31. package/dist/docs-raw/generated/sketch.md +1518 -362
  32. package/dist/docs-raw/generated/viewport.md +368 -299
  33. package/dist/docs-raw/generated/wood.md +104 -0
  34. package/dist/index.html +2 -2
  35. package/dist/landing/proof-ams-adapter.png +0 -0
  36. package/dist/landing/proof-bolt-and-nut.png +0 -0
  37. package/dist/landing/proof-fillet-enclosure.png +0 -0
  38. package/dist/landing/proof-glasses.png +0 -0
  39. package/dist/landing/proof-gyroid.png +0 -0
  40. package/dist/sitemap.xml +6 -6
  41. package/dist-cli/forgecad.js +12321 -5700
  42. package/dist-cli/forgecad.js.map +1 -0
  43. package/dist-cli/solver-46FFSK2U.js +363 -0
  44. package/dist-cli/solver-46FFSK2U.js.map +1 -0
  45. package/dist-skill/CONTEXT.md +4890 -6302
  46. package/dist-skill/SKILL-dev.md +22 -66
  47. package/dist-skill/SKILL.md +20 -59
  48. package/dist-skill/docs/API/core/concepts.md +37 -92
  49. package/dist-skill/docs/CLI.md +341 -718
  50. package/dist-skill/docs/generated/assembly.md +699 -112
  51. package/dist-skill/docs/generated/core.md +1012 -1059
  52. package/dist-skill/docs/generated/curves.md +759 -116
  53. package/dist-skill/docs/generated/lib.md +43 -748
  54. package/dist-skill/docs/generated/output.md +139 -245
  55. package/dist-skill/docs/generated/sdf.md +208 -0
  56. package/dist-skill/docs/generated/sheet-metal.md +473 -21
  57. package/dist-skill/docs/generated/sketch.md +1518 -362
  58. package/dist-skill/docs/generated/viewport.md +368 -299
  59. package/dist-skill/docs/generated/wood.md +104 -0
  60. package/dist-skill/docs/guides/coordinate-system.md +11 -17
  61. package/dist-skill/docs/guides/geometry-conventions.md +13 -70
  62. package/dist-skill/docs/guides/joint-design.md +78 -0
  63. package/dist-skill/docs/guides/modeling-recipes.md +22 -195
  64. package/dist-skill/docs/guides/positioning.md +88 -147
  65. package/dist-skill/docs-dev/API/core/concepts.md +78 -0
  66. package/dist-skill/docs-dev/CLI.md +488 -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 +2 -4
  70. package/dist-skill/docs-dev/component-model.md +164 -0
  71. package/dist-skill/docs-dev/generated/assembly.md +779 -0
  72. package/dist-skill/docs-dev/generated/core.md +1676 -0
  73. package/dist-skill/docs-dev/generated/curves.md +855 -0
  74. package/dist-skill/docs-dev/generated/lib.md +55 -0
  75. package/dist-skill/docs-dev/generated/output.md +234 -0
  76. package/dist-skill/docs-dev/generated/sdf.md +208 -0
  77. package/dist-skill/docs-dev/generated/sheet-metal.md +506 -0
  78. package/dist-skill/docs-dev/generated/sketch.md +1753 -0
  79. package/dist-skill/docs-dev/generated/viewport.md +513 -0
  80. package/dist-skill/docs-dev/generated/wood.md +104 -0
  81. package/dist-skill/docs-dev/guides/coordinate-system.md +46 -0
  82. package/dist-skill/docs-dev/guides/geometry-conventions.md +52 -0
  83. package/dist-skill/docs-dev/guides/joint-design.md +78 -0
  84. package/dist-skill/docs-dev/guides/modeling-recipes.md +77 -0
  85. package/dist-skill/docs-dev/guides/positioning.md +151 -0
  86. package/dist-skill/{docs → docs-dev}/guides/skill-maintenance.md +21 -10
  87. package/dist-skill/{docs → docs-dev}/internals/compiler.md +5 -6
  88. package/dist-skill/{docs → docs-dev}/internals/constraint-solver-quality.md +0 -1
  89. package/dist-skill/{docs → docs-dev}/internals/constraint-solver.md +0 -1
  90. package/dist-skill/{docs → docs-dev}/internals/sketch-2d-pipeline.md +2 -3
  91. package/examples/api/attachTo-basics.forge.js +8 -8
  92. package/examples/api/bill-of-materials.forge.js +9 -9
  93. package/examples/api/bolt-pattern.forge.js +5 -5
  94. package/examples/api/boolean-operations.forge.js +5 -5
  95. package/examples/api/bounding-box-visualizer.forge.js +3 -3
  96. package/examples/api/clone-duplicate.forge.js +2 -2
  97. package/examples/api/colors-union-vs-array.forge.js +6 -6
  98. package/examples/api/connector-assembly.forge.js +8 -6
  99. package/examples/api/connector-basics.forge.js +7 -7
  100. package/examples/api/constrained-sketch-mechanical.forge.js +4 -4
  101. package/examples/api/elbow-test.forge.js +3 -3
  102. package/examples/api/extrude-options.forge.js +8 -14
  103. package/examples/api/feature-created-faces.forge.js +6 -10
  104. package/examples/api/fillet-showcase.forge.js +2 -2
  105. package/examples/api/folded-service-panel-cover.forge.js +2 -2
  106. package/examples/api/gears-tier1.forge.js +5 -5
  107. package/examples/api/group-test.forge.js +3 -3
  108. package/examples/api/group-vs-union.forge.js +1 -1
  109. package/examples/api/highlight-debug.forge.js +4 -0
  110. package/examples/api/js-module-pillars.js +1 -1
  111. package/examples/api/js-module-scene.js +2 -2
  112. package/examples/api/mesh-import-slats.forge.js +4 -4
  113. package/examples/api/patterns.forge.js +3 -3
  114. package/examples/api/pointAlong-orientation.forge.js +3 -3
  115. package/examples/api/profile-2020-b-slot6.forge.js +4 -5
  116. package/examples/api/route-perimeter-flange.forge.js +1 -1
  117. package/examples/api/sdf-rover-demo.forge.js +10 -10
  118. package/examples/api/sketch-on-face-demo.forge.js +2 -2
  119. package/examples/api/sketch-regions.forge.js +4 -4
  120. package/examples/api/sketch-rounding-strategies.forge.js +1 -1
  121. package/examples/api/smooth-curve-connections.forge.js +1 -1
  122. package/examples/api/transition-curves.forge.js +4 -4
  123. package/examples/api/variable-sweep-pure-sdf-test.forge.js +162 -0
  124. package/examples/api/variable-sweep-test.forge.js +2 -2
  125. package/examples/api/wood-joinery.forge.js +60 -0
  126. package/examples/compiler-corpus/enclosure-shell-cuts.forge.js +3 -3
  127. package/examples/compiler-corpus/fastener-plate-variants.forge.js +2 -2
  128. package/examples/constraints/01-fully-constrained-rect.forge.js +2 -2
  129. package/examples/constraints/02-underconstrained-triangle.forge.js +1 -1
  130. package/examples/constraints/03-redundant-constraints.forge.js +2 -2
  131. package/examples/constraints/05-parallel-with-linedistance.forge.js +2 -2
  132. package/examples/constraints/06-complex-spectrogram.forge.js +1 -1
  133. package/examples/constraints/07-perpendicular-chain.forge.js +4 -4
  134. package/examples/constraints/08-symmetric-bracket.forge.js +4 -4
  135. package/examples/constraints/09-stress-spiral.forge.js +1 -1
  136. package/examples/constraints/10-stress-honeycomb.forge.js +1 -1
  137. package/examples/constraints/11-surface-grid.forge.js +2 -2
  138. package/examples/constraints/12-surface-nested.forge.js +4 -4
  139. package/examples/constraints/13-surface-complex.forge.js +1 -1
  140. package/examples/exact-arc-housing.forge.js +12 -0
  141. package/examples/experiments/drone-arm.forge.js +53 -0
  142. package/examples/furniture/adjustable-table.forge.js +15 -15
  143. package/examples/furniture/bathroom.forge.js +26 -26
  144. package/examples/furniture/chair.forge.js +13 -13
  145. package/examples/furniture/picture-frame.forge.js +6 -6
  146. package/examples/furniture/shoe-rack-doors.forge.js +8 -8
  147. package/examples/furniture/shoe-rack.forge.js +7 -7
  148. package/examples/furniture/table-lamp.forge.js +8 -8
  149. package/examples/gcode/lissajous-vase.forge.js +4 -4
  150. package/examples/gcode/math-surface.forge.js +3 -3
  151. package/examples/gcode/parametric-vase.forge.js +4 -4
  152. package/examples/gcode/spiral-tower.forge.js +4 -4
  153. package/examples/generative/crystal-growth.forge.js +9 -9
  154. package/examples/generative/frost-spires.forge.js +9 -9
  155. package/examples/generative/golden-spiral-tower.forge.js +11 -11
  156. package/examples/generative/molten-forge.forge.js +6 -6
  157. package/examples/generative/neon-coral.forge.js +7 -7
  158. package/examples/mechanical/3d-printer.forge.js +37 -37
  159. package/examples/mechanical/5-finger-robot-hand.forge.js +19 -19
  160. package/examples/mechanical/airplane-propeller.forge.js +9 -9
  161. package/examples/mechanical/bolt-and-nut.forge.js +10 -10
  162. package/examples/mechanical/door-with-hinges.forge.js +7 -7
  163. package/examples/mechanical/fillet-enclosure.forge.js +15 -11
  164. package/examples/mechanical/headphone-hanger-v2.forge.js +11 -11
  165. package/examples/mechanical/robot_hand.forge.js +24 -24
  166. package/examples/mechanical/robot_hand_2.forge.js +26 -26
  167. package/examples/nurbs-surface.forge.js +8 -0
  168. package/examples/nurbs-tube.forge.js +7 -0
  169. package/examples/products/bottle.forge.js +8 -8
  170. package/examples/products/chess-set.forge.js +25 -25
  171. package/examples/products/classical-piano.forge.js +20 -20
  172. package/examples/products/clock.forge.js +33 -33
  173. package/examples/products/cup.forge.js +5 -5
  174. package/examples/products/iphone.forge.js +20 -20
  175. package/examples/products/laptop.forge.js +24 -24
  176. package/examples/products/laser-cut-box.forge.js +6 -6
  177. package/examples/products/laser-cut-tray.forge.js +6 -6
  178. package/examples/products/liquid-soap-dispenser.forge.js +23 -23
  179. package/examples/products/origami-fish.forge.js +14 -12
  180. package/examples/products/spiderman-cake.forge.js +6 -6
  181. package/examples/shelf/container.forge.js +5 -5
  182. package/examples/shelf/shelf-unit.forge.js +6 -6
  183. package/examples/toolbox/bolted-joint.forge.js +7 -7
  184. package/package.json +9 -4
  185. package/dist/assets/EditorApp-B-vQvgam.js +0 -9888
  186. package/dist/assets/LandingPage-C5n9hDXI.js +0 -322
  187. package/dist/assets/PublishedModelPage-Dt7PCVBj.js +0 -146
  188. package/dist/assets/__vite-browser-external-CURh0WXD.js +0 -8
  189. package/dist/assets/deserializeRunResult-BLAFoiE0.js +0 -19365
  190. package/dist/assets/index-1CYp3zUp.js +0 -1455
  191. package/dist-skill/docs/API/API.md +0 -1666
  192. package/dist-skill/docs/API/README.md +0 -37
  193. package/dist-skill/docs/API/assembly/assembly.md +0 -617
  194. package/dist-skill/docs/API/core/edge-queries.md +0 -130
  195. package/dist-skill/docs/API/core/parameters.md +0 -122
  196. package/dist-skill/docs/API/core/reserved-terms.md +0 -137
  197. package/dist-skill/docs/API/core/sdf.md +0 -326
  198. package/dist-skill/docs/API/core/skill-cli.md +0 -194
  199. package/dist-skill/docs/API/core/skill-guide.md +0 -205
  200. package/dist-skill/docs/API/core/specs.md +0 -186
  201. package/dist-skill/docs/API/core/topology.md +0 -372
  202. package/dist-skill/docs/API/entities.md +0 -268
  203. package/dist-skill/docs/API/output/bom.md +0 -58
  204. package/dist-skill/docs/API/output/brep-export.md +0 -87
  205. package/dist-skill/docs/API/output/dimensions.md +0 -67
  206. package/dist-skill/docs/API/output/export.md +0 -110
  207. package/dist-skill/docs/API/output/gcode.md +0 -195
  208. package/dist-skill/docs/API/runtime/viewport.md +0 -420
  209. package/dist-skill/docs/API/sheet-metal/sheet-metal.md +0 -185
  210. package/dist-skill/docs/API/sketch/anchor.md +0 -37
  211. package/dist-skill/docs/API/sketch/booleans.md +0 -91
  212. package/dist-skill/docs/API/sketch/core.md +0 -73
  213. package/dist-skill/docs/API/sketch/extrude.md +0 -62
  214. package/dist-skill/docs/API/sketch/on-face.md +0 -104
  215. package/dist-skill/docs/API/sketch/operations.md +0 -78
  216. package/dist-skill/docs/API/sketch/path.md +0 -75
  217. package/dist-skill/docs/API/sketch/primitives.md +0 -146
  218. package/dist-skill/docs/API/sketch/regions.md +0 -80
  219. package/dist-skill/docs/API/sketch/text.md +0 -108
  220. package/dist-skill/docs/API/sketch/transforms.md +0 -65
  221. package/dist-skill/docs/API/toolbox/fasteners.md +0 -129
  222. package/dist-skill/docs/INDEX.md +0 -94
  223. package/dist-skill/docs/RELEASING.md +0 -55
  224. package/dist-skill/docs/cli-monetization.md +0 -111
  225. package/dist-skill/docs/deployment.md +0 -281
  226. package/dist-skill/docs/generated/concepts.md +0 -2112
  227. package/dist-skill/docs/internals/shape-from-slices.md +0 -152
  228. package/dist-skill/docs/platform/admin.md +0 -45
  229. package/dist-skill/docs/platform/architecture.md +0 -79
  230. package/dist-skill/docs/platform/auth.md +0 -110
  231. package/dist-skill/docs/platform/email.md +0 -67
  232. package/dist-skill/docs/platform/projects.md +0 -111
  233. package/dist-skill/docs/platform/sharing.md +0 -90
  234. 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,78 @@
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, `Array` of `{ name, shape?, sketch?, color? }`, or a **metadata object** (see below)
20
+
21
+ ### Metadata Object Return
22
+
23
+ A script can return a plain object whose values include renderable geometry alongside non-renderable metadata. All renderable entries (Shape, Sketch, ShapeGroup, Assembly, or Array of named objects) are rendered; non-renderable entries are silently skipped. This is useful for multi-file projects where a part needs to publish interface data (bolt positions, dimensions) to other files:
24
+
25
+ ```javascript
26
+ // motor-mount.forge.js — renders standalone, exports metadata via require()
27
+ const holePositions = [[17, 15], [-29, 15], [17, -15], [-29, -15]];
28
+ return {
29
+ shape: mount.color('#556B2F'), // rendered
30
+ bolts: { dia: 5.3, pos: holePositions }, // metadata — skipped in render, available via require()
31
+ };
32
+
33
+ // base-body.forge.js — imports mount, accesses .bolts
34
+ const mount = require('./motor-mount.forge.js');
35
+ for (const [x, y] of mount.bolts.pos) { ... } // use metadata
36
+ // mount.shape is the Shape if you need it in an assembly
37
+ ```
38
+
39
+ Arrays inside the object are also rendered:
40
+
41
+ ```javascript
42
+ return {
43
+ parts: [{ name: 'Left', shape: leftShape }, { name: 'Right', shape: rightShape }],
44
+ armWidth: 6, // metadata
45
+ };
46
+ ```
47
+
48
+ ## Coordinate System
49
+
50
+ Z-up right-handed: X = left/right, Y = forward/back, Z = up/down.
51
+
52
+ ## Colors
53
+
54
+ `.color(hex)` works on `Shape` and `Sketch`. Colors survive transforms. In booleans the first operand's color wins.
55
+
56
+ **`union()` removes colors** — shapes merge into one solid mesh. Return named objects instead:
57
+
58
+ ```javascript
59
+ return [
60
+ { name: "Base", shape: box(100, 100, 5), color: "#888888" },
61
+ { name: "Column", shape: cylinder(50, 10).translate(50, 50, 5), color: "#4488cc" },
62
+ ];
63
+ ```
64
+
65
+ ## Face Operations
66
+
67
+ Shapes carry semantic face labels through their lifecycle. The flow is:
68
+
69
+ 1. **Primitives** assign canonical names — `box()` gives you `top`, `bottom`, `side-left`, etc.; `cylinder()` gives `top`, `bottom`, `side`.
70
+ 2. **Extrusions** inherit labels from the sketch and add `top`/`bottom`.
71
+ 3. **Transforms** (translate, rotate, scale, mirror) preserve all labels.
72
+ 4. **Booleans** preserve labels from the first operand where geometry survives.
73
+
74
+ 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.
75
+
76
+ ## SDF Modeling
77
+
78
+ For organic shapes, smooth blending, TPMS lattices, and surface deformations. SDF shapes convert via `.toShape()`. See [sdf-primitives.md](sdf-primitives.md).