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
@@ -0,0 +1,77 @@
1
+ ---
2
+ skill-group: recipes
3
+ skill-order: 1
4
+ ---
5
+
6
+ # Modeling Recipes
7
+
8
+ ## Iteration Bias
9
+
10
+ - Default to a buildable first pass instead of a long proposal.
11
+ - Replace a broken model wholesale when that is faster than incremental patching.
12
+ - Validate early with `forgecad run <file>`.
13
+
14
+ ## Common Patterns
15
+
16
+ ### Hollow Shell
17
+ ```javascript
18
+ const outerBox = box(outer, outer, outer, true);
19
+ const innerBox = box(outer - 2 * wall, outer - 2 * wall, outer - 2 * wall, true);
20
+ return outerBox.subtract(innerBox);
21
+ ```
22
+
23
+ ### Sketch-Based Twist
24
+ ```javascript
25
+ const outer = ngon(sides, radius);
26
+ const inner = ngon(sides, radius - wall);
27
+ return outer.subtract(inner).extrude(height, { twist: 45, divisions: 32 });
28
+ ```
29
+
30
+ ### Rounded Profiles
31
+ ```javascript
32
+ // All convex corners — offset trick
33
+ const base = rect(50, 30).offset(-3, 'Round').offset(3, 'Round');
34
+
35
+ // Selected corners only
36
+ const roof = filletCorners(roofPoints, [
37
+ { index: 3, radius: 19 },
38
+ { index: 4, radius: 19 },
39
+ { index: 5, radius: 19 },
40
+ ]);
41
+ ```
42
+
43
+ ### Choosing the right sketch-rounding tool
44
+
45
+ - `offset(-r).offset(+r)` — round every convex corner of a closed outline
46
+ - `stroke(points, width, 'Round')` — centerline-based geometry (ribs, traces)
47
+ - `filletCorners(points, ...)` — selective true-corner fillets on mixed profiles
48
+
49
+ ## Best Practices
50
+
51
+ - All dimensions in millimeters; angles in degrees.
52
+ - Primitives are centered on XY, base at Z=0. Use `placeReference('center', [0,0,0])` to center on all axes.
53
+ - Prefer named intermediate values over deeply nested one-liners.
54
+ - `union2d`, `difference2d`, `intersection2d` batch faster than chained `.add()` / `.subtract()`.
55
+
56
+ ## Debugging
57
+
58
+ ```javascript
59
+ console.log("Volume:", shape.volume());
60
+ ```
61
+
62
+ For sketch-heavy work, compare the raw profile and rounded profile side-by-side before extruding:
63
+
64
+ ```javascript
65
+ return [
66
+ { name: "Raw", sketch: polygon(roofPoints) },
67
+ { name: "Rounded", sketch: filletCorners(roofPoints, [...]).translate(120, 0) },
68
+ ];
69
+ ```
70
+
71
+ ## Common Errors
72
+
73
+ - `"Kernel not initialized"` — internal/runtime issue, reload the app
74
+ - zero dimensions or self-intersecting sketches → invalid geometry
75
+ - wrong variable name → `"Cannot read property of undefined"`
76
+
77
+ For larger runnable examples, read `examples/api/`.
@@ -0,0 +1,151 @@
1
+ ---
2
+ skill-group: geometry
3
+ skill-order: 3
4
+ ---
5
+
6
+ # Positioning Strategy
7
+
8
+ ## Primitive origin convention
9
+
10
+ All 3D primitives are **centered on XY, base at Z=0**:
11
+
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] |
18
+
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**.
20
+
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
+
23
+ ---
24
+
25
+ Most positioning bugs come from manual coordinate arithmetic. Use these methods in priority order.
26
+
27
+ ## 1. `group()` — local coordinates for multi-part assemblies
28
+
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.**
30
+
31
+ ```javascript
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
50
+ ```
51
+
52
+ **Groups nest.** Build sub-assemblies as groups, then group those into larger assemblies — each level has its own local origin.
53
+
54
+ ```javascript
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
61
+ ```
62
+
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.
64
+
65
+ ## 2. Connectors + `matchTo()` — default for mating interfaces
66
+
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).
68
+
69
+ ```javascript
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");
82
+ ```
83
+
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.
85
+
86
+ ## 3. `pointAlong()` — orient cylinders before positioning
87
+
88
+ ```javascript
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);
93
+ ```
94
+
95
+ **Always call `pointAlong()` BEFORE `matchTo()` or `translate()`** — it reorients around the origin.
96
+
97
+ ## 4. `attachTo()` — quick bounding-box positioning
98
+
99
+ ```javascript
100
+ const column = cylinder(50, 8).attachTo(base, 'top', 'bottom');
101
+ ```
102
+
103
+ `child.attachTo(parent, parentAnchor, selfAnchor, offset)`. Anchor points shift on fillet/chamfer/boolean — fragile for assembly interfaces, fine for quick prototyping.
104
+
105
+ ## 5. `rotateAroundTo()` — aim a point around a hinge/axis
106
+
107
+ ```javascript
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' });
111
+ ```
112
+
113
+ ## 6. `moveToLocal()` — offset from another shape's min corner
114
+
115
+ ```javascript
116
+ const part = box(20, 20, 30).moveToLocal(base, 10, 10, 10);
117
+ ```
118
+
119
+ ## 7. `translate()` — for simple offsets or bridging computed locations
120
+
121
+ ```javascript
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);
124
+ ```
125
+
126
+ ## 8. `placeReference()` — align any anchor to a world coordinate
127
+
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()`.
129
+
130
+ ```javascript
131
+ // Ground a shape — bottom face center at Z = 0
132
+ const grounded = shape.placeReference('bottom', [0, 0, 0])
133
+
134
+ // Center at the world origin
135
+ const centered = shape.placeReference('center', [0, 0, 0])
136
+
137
+ // Align left edge to X = 10
138
+ const aligned = shape.placeReference('left', [10, 0, 0])
139
+ ```
140
+
141
+ Also works with custom placement references for cross-file parts:
142
+
143
+ ```javascript
144
+ // widget.forge.js — define once
145
+ return union(base, post).withReferences({ points: { mount: [0, -16, -4] } });
146
+
147
+ // importer — consume
148
+ const widget = require("./widget.forge.js").placeReference("mount", [120, 40, 0]);
149
+ ```
150
+
151
+ For cross-file parts needing proper alignment, prefer connectors over placement references.
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  skill-group: dev-skill
3
3
  skill-order: 1
4
- skill-tiers: [dev]
5
4
  ---
6
5
 
7
6
  # Skill System Maintenance
@@ -47,14 +46,12 @@ The build script discovers docs by scanning `docs/permanent/` for files with `sk
47
46
  |-------|----------|---------|-------------|
48
47
  | `skill-group` | Yes | — | Group name (must match `GROUP_REGISTRY` in build script) |
49
48
  | `skill-order` | No | 50 | Sort order within the group |
50
- | `skill-tiers` | No | `[standard, one-file]` | Which outputs include this file |
51
49
 
52
- ### Tier values
50
+ ### Inclusion rules
53
51
 
54
- - `[standard, one-file]` (default) — included in installed skill and CONTEXT.md
55
- - `[standard]`installed skill only (too verbose for one-file paste)
56
- - `[one-file]`CONTEXT.md only (slim excerpt replacing a standard doc)
57
- - `[dev]` — dev skill only (SKILL-dev.md)
52
+ - **`skill-group: <name>`** — included in SKILL.md (index) and CONTEXT.md (inlined).
53
+ - **`skill-group: dev-<name>`** dev-only, appears in SKILL-dev.md only.
54
+ - **No `skill-group`**not part of the skill at all.
58
55
 
59
56
  ### Adding a new group
60
57
 
@@ -62,12 +59,11 @@ If you need a new group, add it to `GROUP_REGISTRY` in `scripts/build-forgecad-s
62
59
 
63
60
  ## Adding Dev-Only Docs
64
61
 
65
- Use `skill-tiers: [dev]` in the frontmatter. These appear in `SKILL-dev.md` but not in `SKILL.md` or `CONTEXT.md`. Use this for:
62
+ Use a `skill-group` with a `dev-` prefix (e.g. `dev-compiler`, `dev-solver`, `dev-conventions`). These appear in `SKILL-dev.md` but not in `SKILL.md` or `CONTEXT.md`. Use this for:
66
63
 
67
64
  - Compiler/solver internals
68
65
  - Coding conventions and PR guidelines
69
- - Deployment and release processes
70
- - Full CLI reference (vs the slim `skill-cli.md` excerpt)
66
+ - Full CLI reference (vs the slim user-facing docs)
71
67
 
72
68
  ## Deciding Standard vs Dev
73
69
 
@@ -78,6 +74,21 @@ Use `skill-tiers: [dev]` in the frontmatter. These appear in `SKILL-dev.md` but
78
74
  | Does it cover team process (coding standards, releases, CI)? | Dev only |
79
75
  | Is it a CLI command only developers run (`check suite`, `sdf`, debug flags)? | Dev only |
80
76
 
77
+ ## Orphan files
78
+
79
+ `skill install` uses `cpSync` — it copies `dist-skill/docs/` on top of the installed skill directory but **does not delete** files that no longer exist in the source. If a doc is removed or loses its `skill-group` frontmatter, its copy persists in `~/.agents/skills/forgecad/docs/` as an orphan.
80
+
81
+ To check for orphans:
82
+ ```bash
83
+ for f in $(find ~/.agents/skills/forgecad/docs -name '*.md'); do
84
+ echo "$f" | grep -q '/generated/' && continue
85
+ base=$(echo "$f" | sed "s|$HOME/.agents/skills/forgecad/docs/|docs/permanent/|")
86
+ [ -f "$base" ] || echo "ORPHAN: $f"
87
+ done
88
+ ```
89
+
90
+ Delete any orphans found. They're stale copies that can mislead agents with outdated information.
91
+
81
92
  ## Verifying
82
93
 
83
94
  After changes, check that the build succeeds and the doc counts look right:
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  skill-group: dev-compiler
3
3
  skill-order: 1
4
- skill-tiers: [dev]
5
4
  ---
6
5
 
7
6
  # Forge Compiler Architecture
@@ -173,10 +172,10 @@ Current progress:
173
172
  - trim/split-by-plane, shell, hole, cut, boolean, and edge-finish rewrites now also declare descendant contracts on top of that propagation kernel
174
173
  - the shared descendant-resolution layer turns those lineage inputs into defended singles, face regions/sets, edge chains, or explicit unsupported results instead of making each caller reverse-engineer rewrite heuristics
175
174
  - trim/split-by-plane now expose their plane-cap face plus descendant-region contracts for preserved source surfaces, while hole/cut host-face splits and supported boolean difference/intersection descendants stay reviewable as defended regions instead of disappearing behind one generic ambiguity bucket
176
- - boolean rewrites now consume that same contract: supported unions still preserve operand canonical-face/query lineage when the source owners stay distinct, and coplanar/multi-member descendants can now surface as defended face sets instead of only masked name conflicts
175
+ - boolean rewrites now consume that same contract: supported unions still preserve operand label/query lineage when the source owners stay distinct, and coplanar/multi-member descendants can now surface as defended face sets instead of only masked name conflicts
177
176
  - shell, hole, and cut now layer defended created-face slots plus defended descendant regions on top of that propagation kernel instead of leaving all post-rewrite face meaning as placeholders
178
177
  - compile-covered `Shape.face(name)` resolution now reads from the compile graph plus the descendant layer, so supported shell inner walls, blind-hole floors, cut-created walls, split host faces, and defended boolean face sets can produce real `FaceRef` values after topology rewrites
179
- - non-canonical `onFace(shape, 'inner-side-right', ...)` placement now routes through that same compiler-owned face resolver, while direct `FaceRef` placements preserve created-face/propagated-face provenance instead of collapsing everything back to anonymous refs
178
+ - label-based `onFace(shape, 'inner-side-right', ...)` placement now routes through the compiler-owned face resolver, while direct `FaceRef` placements preserve created-face/propagated-face provenance instead of collapsing everything back to anonymous refs
180
179
  - `src/forge/kernel.ts` and the compiler inspection surface now expose collected topology-rewrite propagation plus descendant contracts directly, and the placement/compiler invariants assert that those contracts stay inspectable and deterministic through later transforms
181
180
  - `forgecad check query-propagation` now snapshots both the propagation surface and the descendant contracts directly, so defended plane-cap faces, split-face regions, face sets, merged-edge chains, and explicit unsupported rewrite boundaries stay reviewable without wading through the full compiler-scene baseline
182
181
 
@@ -217,10 +216,10 @@ Current progress:
217
216
  - the regression suite now also includes a file-backed ordinary-parts corpus under `examples/compiler-corpus/`, so shell, richer hole/cut workflows, projection replay, trim-created faces, repeated descendants, and finishing flows are exercised together instead of only as isolated unit slices
218
217
  - the MLP closeout review surface now lives in that corpus plus `forgecad check compiler`, `forgecad check query-propagation`, and `forgecad check brep`, so the defended subset is reviewable from the repo instead of from tribal knowledge
219
218
  - mirrored downstream features and helper-driven linear/circular repetition now preserve repeated-result ownership on top of the shared face-query backbone
220
- - supported boolean unions now also preserve owner-scoped canonical face queries from repeated descendants, and compiler regressions cover both explicit duplicate-owner merge ambiguity and later boolean chains that inherit those propagated queries
219
+ - supported boolean unions now also preserve owner-scoped label queries from repeated descendants, and compiler regressions cover both explicit duplicate-owner merge ambiguity and later boolean chains that inherit those propagated queries
221
220
  - exact export regression coverage now includes a repeated-feature part where a mirrored descendant drives a downstream workplane feature inside a boolean chain
222
221
  - `projectToPlane()` sketches now keep an explicit projection node in the compiler graph instead of collapsing immediately to anonymous runtime geometry
223
- - compile-covered `Sketch.onFace(shape, name)` resolution now prefers the defended face-query table on `Shape` targets, so supported boolean-preserved names and explicit repeated-descendant `FaceRef`s stay visible to later features instead of falling back to anonymous canonical heuristics
222
+ - compile-covered `Sketch.onFace(shape, name)` resolution now prefers the defended face-query table on `Shape` targets, so supported boolean-preserved names and explicit repeated-descendant `FaceRef`s stay visible to later features instead of falling back to anonymous heuristics
224
223
  - the supported exact subset can now replay projection-driven follow-on features when the source reduces to one defended planar projection basis: placed straight extrusions, compatible shell/hole/cut descendants, and boolean unions of compatible projected operands all stay aligned on matching parallel target planes
225
224
 
226
225
  Current limits:
@@ -230,7 +229,7 @@ Current limits:
230
229
  - `filletEdge()` / `chamferEdge()` v1 cover tracked vertical edges on compile-covered `box()` and `rectangle(...).extrude(...)` bodies plus preserved propagated sibling edges through supported edge-finish and boolean-union chains; the selected rewritten edge now resolves as an explicit descendant edge-chain for inspection/debug, but not yet as a new single finishable edge target
231
230
  - two-sided extents, tapered cutouts, and thread metadata are now supported; combined counterbore+countersink heads, modeled helical threads, and broader durable identity beyond today's defended shell/hole/cut created-face subset are still missing
232
231
  - `upToFace` currently requires a planar termination face parallel to the feature direction, but defended split termination faces can now stay queryable as descendant regions where Forge still owns the source plane
233
- - boolean/pattern propagation currently defends owner-scoped canonical face lineage through supported unions, and the descendant layer can now expose some post-merge/post-difference results as face sets/regions, but broader durable per-face ownership after arbitrary merged topology changes is still incomplete
232
+ - boolean/pattern propagation currently defends owner-scoped face label lineage through supported unions, and the descendant layer can now expose some post-merge/post-difference results as face sets/regions, but broader durable per-face ownership after arbitrary merged topology changes is still incomplete
234
233
  - repeated-feature ownership currently tracks repeated bodies and mirrored descendants, not durable per-face identity after merged pattern topology changes
235
234
  - shell, hole/cut, tracked-edge finishing, and repeated-feature workflows now preserve parent-body ownership lineage, but stable downstream face/edge ownership after topology-changing edits is still not solved, which is why richer boolean-difference/intersection targets and broader fillet/chamfer workflows remain harder next layers
236
235
  - projection replay still rejects boolean difference/intersection sources, trim/fillet/chamfer silhouette changes, and non-parallel projection bases with explicit compiler diagnostics instead of silently pretending those paths are exact-safe
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  skill-group: dev-solver
3
3
  skill-order: 2
4
- skill-tiers: [dev]
5
4
  ---
6
5
 
7
6
  # Constraint Solver Quality — What's Tunable vs What's Architecture
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  skill-group: dev-solver
3
3
  skill-order: 1
4
- skill-tiers: [dev]
5
4
  ---
6
5
 
7
6
  # Constraint Solver Internals
@@ -1,7 +1,6 @@
1
1
  ---
2
- skill-group: dev-sketch-pipeline
3
- skill-order: 1
4
- skill-tiers: [dev]
2
+ skill-group: dev-compiler
3
+ skill-order: 2
5
4
  ---
6
5
 
7
6
  # 2D Sketch Pipeline: Manifold ↔ ProfileCompilePlan ↔ OCCT
@@ -12,27 +12,27 @@ const baseW = param("Base Width", 100, { min: 50, max: 200, unit: "mm" });
12
12
  const baseD = param("Base Depth", 80, { min: 40, max: 150, unit: "mm" });
13
13
  const baseH = param("Base Height", 10, { min: 5, max: 30, unit: "mm" });
14
14
 
15
- const base = box(baseW, baseD, baseH, true).color('#888888');
15
+ const base = box(baseW, baseD, baseH).color('#888888');
16
16
 
17
17
  // Stack on top: column's bottom face meets base's top face
18
18
  const column = cylinder(40, 8).color('#4488cc')
19
19
  .attachTo(base, 'top', 'bottom');
20
20
 
21
21
  // Protrude from front: button's back face meets base's front face
22
- const button = box(20, 6, 10, true).color('#cc4444')
22
+ const button = box(20, 6, 10).color('#cc4444')
23
23
  .attachTo(base, 'front', 'back');
24
24
 
25
25
  // Hang below: bracket's top face meets base's bottom face
26
- const bracket = box(30, 30, 5, true).color('#44cc44')
26
+ const bracket = box(30, 30, 5).color('#44cc44')
27
27
  .attachTo(base, 'bottom', 'top');
28
28
 
29
29
  // Attach to side with offset: panel's left face meets base's right face,
30
30
  // then shift 0mm on X, 0mm on Y, 10mm up on Z
31
- const sidePanel = box(4, 40, 25, true).color('#cc8844')
31
+ const sidePanel = box(4, 40, 25).color('#cc8844')
32
32
  .attachTo(base, 'right', 'left', [0, 0, 10]);
33
33
 
34
34
  // Corner alignment: small cube at top-front-right corner of base
35
- const corner = box(8, 8, 8, true).color('#8844cc')
35
+ const corner = box(8, 8, 8).color('#8844cc')
36
36
  .attachTo(base, 'top-front-right', 'bottom-back-left');
37
37
 
38
38
  return [
@@ -15,7 +15,7 @@ const spacing = 80;
15
15
 
16
16
  // Two overlapping shapes for each demo
17
17
  function makePair(offsetX) {
18
- const a = box(size, size, size, true).translate(offsetX, 0, 0).color('#4488cc');
18
+ const a = box(size, size, size).translate(offsetX, 0, 0).color('#4488cc');
19
19
  const b = sphere(size * 0.6).translate(offsetX + size - overlap, 0, 0).color('#cc4444');
20
20
  return [a, b];
21
21
  }
@@ -26,7 +26,7 @@ const unioned = union(u1, u2).color('#8866cc');
26
26
 
27
27
  // 2. Difference — box minus sphere and cross-bore
28
28
  const [d1, d2] = makePair(spacing);
29
- const d3 = cylinder(size * 1.2, size * 0.14, undefined, undefined, true)
29
+ const d3 = cylinder(size * 1.2, size * 0.14)
30
30
  .pointAlong([0, 1, 0])
31
31
  .translate(spacing, 0, 0);
32
32
  const diffed = d1.subtract(d2, d3);
@@ -36,7 +36,7 @@ const [i1, i2] = makePair(2 * spacing);
36
36
  const intersected = intersection(i1, i2).color('#cc8844');
37
37
 
38
38
  // Show the original shapes (translucent-ish via separate objects) for reference
39
- const refA = box(size, size, size, true).translate(3 * spacing, 0, 0).color('#4488cc');
39
+ const refA = box(size, size, size).translate(3 * spacing, 0, 0).color('#4488cc');
40
40
  const refB = sphere(size * 0.6).translate(3 * spacing + size - overlap, 0, 0).color('#cc4444');
41
41
 
42
42
  return [
@@ -37,7 +37,7 @@ function vizBBox(shape) {
37
37
 
38
38
  // A rotated box — bbox is larger than the shape itself
39
39
  const angle = param("Rotation", 30, { min: 0, max: 90, unit: "°" });
40
- const rotBox = box(40, 30, 20, true).rotate(0, 0, angle).color('#4488cc');
40
+ const rotBox = box(40, 30, 20).rotateZ(angle).color('#4488cc');
41
41
  const rotBBox = vizBBox(rotBox).color('#cc4444');
42
42
 
43
43
  // A sphere — bbox is a perfect cube around it
@@ -45,7 +45,7 @@ const sph = sphere(20).translate(80, 0, 0).color('#44cc44');
45
45
  const sphBBox = vizBBox(sph).color('#cc4444');
46
46
 
47
47
  // A tilted cylinder — bbox shows the extent
48
- const tiltCyl = cylinder(50, 10).rotate(30, 0, 0).translate(0, 80, 0).color('#cc88ff');
48
+ const tiltCyl = cylinder(50, 10).rotateX(30).translate(0, 80, 0).color('#cc88ff');
49
49
  const cylBBox = vizBBox(tiltCyl).color('#cc4444');
50
50
 
51
51
  return [
@@ -3,7 +3,7 @@
3
3
  const spacing = param("Spacing", 90, { min: 40, max: 180, unit: "mm" });
4
4
 
5
5
  // --- Shape clone ---
6
- const block = box(36, 20, 12, true).color("#4a90e2");
6
+ const block = box(36, 20, 12).color("#4a90e2");
7
7
  const blockL = block.clone().translate(-spacing / 2, 0, 0);
8
8
  const blockR = block.duplicate().translate(spacing / 2, 0, 0);
9
9
 
@@ -7,17 +7,17 @@ const size = 25;
7
7
  const gap = 5;
8
8
 
9
9
  // --- Three colored boxes ---
10
- const red = box(size, size, size, true).color('#cc4444');
11
- const green = box(size, size, size, true).color('#44cc44').translate(size + gap, 0, 0);
12
- const blue = box(size, size, size, true).color('#4444cc').translate(2 * (size + gap), 0, 0);
10
+ const red = box(size, size, size).color('#cc4444');
11
+ const green = box(size, size, size).color('#44cc44').translate(size + gap, 0, 0);
12
+ const blue = box(size, size, size).color('#4444cc').translate(2 * (size + gap), 0, 0);
13
13
 
14
14
  // BAD: union kills individual colors — result is all red (first shape's color)
15
15
  const merged = union(red, green, blue).translate(-80, 0, 0);
16
16
 
17
17
  // GOOD: separate objects keep their colors
18
- const redSep = box(size, size, size, true).color('#cc4444').translate(80, 0, 0);
19
- const greenSep = box(size, size, size, true).color('#44cc44').translate(80 + size + gap, 0, 0);
20
- const blueSep = box(size, size, size, true).color('#4444cc').translate(80 + 2 * (size + gap), 0, 0);
18
+ const redSep = box(size, size, size).color('#cc4444').translate(80, 0, 0);
19
+ const greenSep = box(size, size, size).color('#44cc44').translate(80 + size + gap, 0, 0);
20
+ const blueSep = box(size, size, size).color('#4444cc').translate(80 + 2 * (size + gap), 0, 0);
21
21
 
22
22
  return [
23
23
  { name: "❌ Union (all one color)", shape: merged },
@@ -10,7 +10,7 @@ const frameT = 8, frameD = 10;
10
10
 
11
11
  // ── Door with hinge connectors ─────────────────────────────────────────────
12
12
 
13
- const door = box(doorW, doorT, doorH, true)
13
+ const door = box(doorW, doorT, doorH)
14
14
  .translate(doorW / 2, 0, 0)
15
15
  .withConnectors({
16
16
  hinge_top: connector.male("hinge_pin", {
@@ -32,11 +32,11 @@ const door = box(doorW, doorT, doorH, true)
32
32
 
33
33
  // ── Frame with matching hinge connectors ───────────────────────────────────
34
34
 
35
- const leftPost = box(frameT, frameD, doorH + frameT, true)
35
+ const leftPost = box(frameT, frameD, doorH + frameT)
36
36
  .translate(-(doorW / 2 + frameT / 2), 0, frameT / 4);
37
- const rightPost = box(frameT, frameD, doorH + frameT, true)
37
+ const rightPost = box(frameT, frameD, doorH + frameT)
38
38
  .translate(doorW / 2 + frameT / 2, 0, frameT / 4);
39
- const lintel = box(doorW + frameT * 2, frameD, frameT, true)
39
+ const lintel = box(doorW + frameT * 2, frameD, frameT)
40
40
  .translate(0, 0, doorH / 2 + frameT / 2);
41
41
 
42
42
  const frame = union(leftPost, rightPost, lintel)
@@ -15,7 +15,7 @@ const cabinetW = param("Cabinet Width", 180, { min: 100, max: 400, unit: "mm" })
15
15
  // ── Side Panel with shelf slots ────────────────────────────────────────────
16
16
 
17
17
  function makeSidePanel(facingDir) {
18
- const panel = box(panelT, panelD, panelH, true);
18
+ const panel = box(panelT, panelD, panelH);
19
19
  const connectors = {};
20
20
 
21
21
  for (let i = 0; i < shelfCount; i++) {
@@ -47,7 +47,7 @@ console.log("Cabinet connectors:", cabinet.connectorNames());
47
47
 
48
48
  const shelfW = cabinetW - panelT;
49
49
  const shelfT = 10;
50
- const shelf = box(shelfW, panelD - 10, shelfT, true)
50
+ const shelf = box(shelfW, panelD - 10, shelfT)
51
51
  .withConnectors({
52
52
  left_tab: connector.male("dovetail", {
53
53
  origin: [-shelfW / 2, 0, 0],
@@ -1,7 +1,7 @@
1
- // Extrude options — twist, taper, center.
1
+ // Extrude options — twist, taper, scaleTop.
2
2
  //
3
3
  // .extrude(height) is the basic form.
4
- // Options: { twist, divisions, scaleTop, center }
4
+ // Options: { twist, divisions, scaleTop }
5
5
 
6
6
  const r = param("Radius", 20, { min: 10, max: 40, unit: "mm" });
7
7
  const h = param("Height", 60, { min: 20, max: 120, unit: "mm" });
@@ -23,22 +23,16 @@ const tapered = circle2d(r).extrude(h, { scaleTop: taper })
23
23
  .translate(2 * spacing, 0, 0)
24
24
  .color('#44cc88');
25
25
 
26
- // 4. Centered extrude shape is centered along Z instead of starting at Z=0
27
- const centered = rect(r * 1.5, r, true).extrude(h, { center: true })
28
- .translate(3 * spacing, 0, 0)
29
- .color('#cc44cc');
30
-
31
- // 5. Combined: twist + taper
26
+ // 4. Combined: twist + taper
32
27
  const combo = star(5, r, r * 0.5).extrude(h, {
33
28
  twist: twist,
34
29
  scaleTop: taper,
35
30
  divisions: 32,
36
- }).translate(4 * spacing, 0, 0).color('#cccc44');
31
+ }).translate(3 * spacing, 0, 0).color('#cccc44');
37
32
 
38
33
  return [
39
34
  { name: "Plain", shape: plain },
40
35
  { name: "Twisted", shape: twisted },
41
36
  { name: "Tapered", shape: tapered },
42
- { name: "Centered (Z)", shape: centered },
43
37
  { name: "Twist + Taper", shape: combo },
44
38
  ];
@@ -1,20 +1,18 @@
1
- const shellBase = roundedRect(90, 56, 6, true).extrude(28);
1
+ const shellBase = roundedRect(90, 56, 6).extrude(28);
2
2
  const cup = shellBase.shell(2.5, { openFaces: ['top'] }).color('#6f7b86');
3
- const innerWallPad = roundedRect(16, 9, 1.8, true)
3
+ const innerWallPad = roundedRect(16, 9, 1.8)
4
4
  .onFace(cup, 'inner-side-right', { u: 0, v: -3, protrude: 0.05, selfAnchor: 'center' })
5
5
  .extrude(1.4)
6
- // .toShape()
7
6
  .color('#f2b16a');
8
7
 
9
- const holeBase = roundedRect(72, 44, 5, true).extrude(20);
8
+ const holeBase = roundedRect(72, 44, 5).extrude(20);
10
9
  const drilled = holeBase.hole('top', { diameter: 8, u: 16, v: -8, depth: 10 }).color('#7a8792');
11
10
  const floorBoss = circle2d(3)
12
11
  .onFace(drilled, 'floor', { u: 0, v: 0, protrude: 0.05, selfAnchor: 'center' })
13
12
  .extrude(1.2)
14
- // .toShape()
15
13
  .color('#d46452');
16
14
 
17
- const counterboreBase = roundedRect(72, 44, 5, true).extrude(20);
15
+ const counterboreBase = roundedRect(72, 44, 5).extrude(20);
18
16
  const counterboreExitFace = counterboreBase.face('bottom');
19
17
  const counterbored = counterboreBase.hole('top', {
20
18
  diameter: 6,
@@ -26,17 +24,15 @@ const counterbored = counterboreBase.hole('top', {
26
24
  const shoulderPad = rect(4, 3)
27
25
  .onFace(counterbored, 'counterbore-floor', { u: 0, v: 0, protrude: 0.05, selfAnchor: 'center' })
28
26
  .extrude(0.9)
29
- // .toShape()
30
27
  .color('#f0c36c');
31
28
 
32
- const cutBase = roundedRect(78, 46, 5, true).extrude(22);
33
- const pocket = roundedRect(20, 12, 2, true)
29
+ const cutBase = roundedRect(78, 46, 5).extrude(22);
30
+ const pocket = roundedRect(20, 12, 2)
34
31
  .onFace(cutBase, 'front', { u: 0, v: 4, selfAnchor: 'center' });
35
32
  const cut = cutBase.cutout(pocket, { depth: 8 }).color('#64707d');
36
33
  const wallTab = rect(5, 4)
37
34
  .onFace(cut, 'wall-right', { u: 0, v: 0, protrude: 0.05, selfAnchor: 'center' })
38
35
  .extrude(1)
39
- // .toShape()
40
36
  .color('#5ba6d6');
41
37
 
42
38
  return [
@@ -16,7 +16,7 @@ const vertFilleted = fillet(tallBox, r, { parallel: [0, 0, 1], convex: true });
16
16
 
17
17
  // ── 4. Boolean result: fillet the sharp edges after a cut ────────────────────
18
18
  const base = box(40, 30, 20);
19
- const cutter = cylinder(25, 10, 10, 32, true).translate(20, 15, 10);
19
+ const cutter = cylinder(25, 10, 10, 32).translate(20, 15, 10);
20
20
  const cutPart = difference(base, cutter);
21
21
  const cutFilleted = fillet(cutPart, 2, { convex: true, minLength: 3 });
22
22
 
@@ -40,8 +40,8 @@ export function buildFoldedServicePanelCoverPart() {
40
40
  .flange('right', { length: 18, angleDeg: 90 })
41
41
  .flange('bottom', { length: 18, angleDeg: 90 })
42
42
  .flange('left', { length: 18, angleDeg: 90 })
43
- .cutout('panel', rect(72, 36, true), { selfAnchor: 'center' })
44
- .cutout('flange-right', roundedRect(26, 10, 5, true), { selfAnchor: 'center' });
43
+ .cutout('panel', rect(72, 36), { selfAnchor: 'center' })
44
+ .cutout('flange-right', roundedRect(26, 10, 5), { selfAnchor: 'center' });
45
45
 
46
46
  const holes = [
47
47
  mountingHole(2.2, -68, -37),
@@ -14,7 +14,7 @@ const crossBar = box(baseW + 20, 5, 5).translate(-10, baseD / 2, 63).color('#aaa
14
14
 
15
15
  // Extruder (intentionally overlaps crossbar — intra-group collision)
16
16
  const nozzle = cylinder(15, 4).translate(baseW / 2, baseD / 2, 48).color('#ff8800');
17
- const heatsink = box(20, 20, 10, true).translate(baseW / 2, baseD / 2, 60).color('#cccccc');
17
+ const heatsink = box(20, 20, 10).translate(baseW / 2, baseD / 2, 60).color('#cccccc');
18
18
 
19
19
  return [
20
20
  { name: "Bed Assembly", group: [
@@ -6,7 +6,7 @@
6
6
  // Use union when you need a single solid (e.g., to subtract from something).
7
7
  // Use group when you want parts to move together but stay visually distinct.
8
8
 
9
- const base = box(60, 60, 5, true).color('#888888');
9
+ const base = box(60, 60, 5).color('#888888');
10
10
  const col = cylinder(30, 5).color('#cc4444')
11
11
  .attachTo(base, 'top', 'bottom');
12
12