forgecad 0.6.3 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -11
- package/dist/assets/{AdminPage-CeqCUUgu.js → AdminPage-DAu1C1ST.js} +250 -151
- package/dist/assets/{BlogPage-P_AJP0v9.js → BlogPage-CJEXL_zJ.js} +94 -70
- package/dist/assets/{DocsPage-CKRV2iq2.js → DocsPage-Gc_BCdqC.js} +269 -143
- package/dist/assets/EditorApp-D9bJvtf7.js +11338 -0
- package/dist/assets/{EditorApp-CnC2k4cW.css → EditorApp-DG1-oUSV.css} +459 -87
- package/dist/assets/{EmbedViewer-DBlzmQ5i.js → EmbedViewer-CEO8XbV8.js} +2 -4
- package/dist/assets/LandingPage-CdCuEOdC.js +451 -0
- package/dist/assets/PricingPage-BSrxu6d7.js +232 -0
- package/dist/assets/{SettingsPage-BqCh9JcC.js → SettingsPage-FUCSIRq6.js} +129 -5
- package/dist/assets/{evalWorker-Ql-aKwLA.js → evalWorker-KoR0SNKq.js} +6770 -2914
- package/dist/assets/{index-2hfs_ub0.css → index-CyVd1D4D.css} +227 -53
- package/dist/assets/{Viewport-CoB46f5R.js → index-wTEK39at.js} +31385 -6439
- package/dist/assets/{javascript-DCxGoE5Y.js → javascript-DAl8Gmyo.js} +1 -1
- package/dist/assets/{manifold-CqNMHHKO.js → manifold-B1sGWdYk.js} +4 -3
- package/dist/assets/{manifold-Cce9wRFz.js → manifold-D7o0N50J.js} +1 -1
- package/dist/assets/{manifold-D6BeHIOo.js → manifold-G5sBaXzi.js} +1 -1
- package/dist/assets/{reportWorker-sFEFonXf.js → reportWorker-DYcRHhv9.js} +6798 -3341
- package/dist/assets/{vendor-react-Dt7-aaJH.js → vendor-react-CG3i_wp0.js} +65 -8
- package/dist/docs-raw/generated/assembly.md +691 -112
- package/dist/docs-raw/generated/concepts.md +1225 -1400
- package/dist/docs-raw/generated/core.md +464 -1412
- package/dist/docs-raw/generated/curves.md +593 -117
- package/dist/docs-raw/generated/lib.md +38 -748
- package/dist/docs-raw/generated/output.md +139 -245
- package/dist/docs-raw/generated/sheet-metal.md +473 -21
- package/dist/docs-raw/generated/sketch.md +553 -349
- package/dist/docs-raw/generated/viewport.md +345 -303
- package/dist/docs-raw/generated/wood.md +104 -0
- package/dist/index.html +2 -2
- package/dist/sitemap.xml +6 -6
- package/dist-cli/chunk-PZ5AY32C.js +10 -0
- package/dist-cli/chunk-PZ5AY32C.js.map +1 -0
- package/dist-cli/forgecad.js +9435 -5407
- package/dist-cli/forgecad.js.map +1 -0
- package/dist-cli/solver-FV7TJZGI.js +365 -0
- package/dist-cli/solver-FV7TJZGI.js.map +1 -0
- package/dist-skill/CONTEXT.md +3186 -7145
- package/dist-skill/SKILL-dev.md +21 -63
- package/dist-skill/SKILL.md +12 -56
- package/dist-skill/docs/API/core/concepts.md +16 -98
- package/dist-skill/docs/CLI/export.md +91 -0
- package/dist-skill/docs/CLI/projects.md +107 -0
- package/dist-skill/docs/CLI/studio_publishing.md +52 -0
- package/dist-skill/docs/CLI/validation.md +66 -0
- package/dist-skill/docs/generated/assembly.md +691 -112
- package/dist-skill/docs/generated/core.md +464 -1412
- package/dist-skill/docs/generated/curves.md +593 -117
- package/dist-skill/docs/generated/lib.md +38 -748
- package/dist-skill/docs/generated/output.md +139 -245
- package/dist-skill/docs/generated/sheet-metal.md +473 -21
- package/dist-skill/docs/generated/sketch.md +553 -349
- package/dist-skill/docs/generated/viewport.md +345 -303
- package/dist-skill/docs/generated/wood.md +104 -0
- package/dist-skill/docs/guides/coordinate-system.md +11 -17
- package/dist-skill/docs/guides/geometry-conventions.md +13 -70
- package/dist-skill/docs/guides/modeling-recipes.md +22 -195
- package/dist-skill/docs/guides/positioning.md +88 -147
- package/dist-skill/docs-dev/API/core/concepts.md +51 -0
- package/dist-skill/docs-dev/API/core/sdf-advanced.md +92 -0
- package/dist-skill/docs-dev/API/core/sdf-primitives.md +58 -0
- package/dist-skill/docs-dev/API/core/sdf-workflow.md +42 -0
- package/dist-skill/docs-dev/CLI/export.md +91 -0
- package/dist-skill/docs-dev/CLI/projects.md +107 -0
- package/dist-skill/docs-dev/CLI/studio_publishing.md +52 -0
- package/dist-skill/docs-dev/CLI/validation.md +66 -0
- package/dist-skill/{docs → docs-dev}/blueprint-first.md +5 -0
- package/dist-skill/{docs → docs-dev}/coding-best-practices.md +6 -8
- package/dist-skill/{docs → docs-dev}/coding.md +1 -3
- package/dist-skill/docs-dev/generated/assembly.md +771 -0
- package/dist-skill/docs-dev/generated/core.md +775 -0
- package/dist-skill/docs-dev/generated/curves.md +688 -0
- package/dist-skill/docs-dev/generated/lib.md +50 -0
- package/dist-skill/docs-dev/generated/output.md +234 -0
- package/dist-skill/docs-dev/generated/sheet-metal.md +506 -0
- package/dist-skill/docs-dev/generated/sketch.md +801 -0
- package/dist-skill/docs-dev/generated/viewport.md +486 -0
- package/dist-skill/docs-dev/generated/wood.md +104 -0
- package/dist-skill/docs-dev/guides/coordinate-system.md +46 -0
- package/dist-skill/docs-dev/guides/geometry-conventions.md +52 -0
- package/dist-skill/docs-dev/guides/modeling-recipes.md +77 -0
- package/dist-skill/docs-dev/guides/positioning.md +151 -0
- package/dist-skill/{docs → docs-dev}/guides/skill-maintenance.md +21 -10
- package/dist-skill/{docs → docs-dev}/internals/compiler.md +5 -6
- package/dist-skill/{docs → docs-dev}/internals/constraint-solver-quality.md +0 -1
- package/dist-skill/{docs → docs-dev}/internals/constraint-solver.md +0 -1
- package/dist-skill/{docs → docs-dev}/internals/sketch-2d-pipeline.md +2 -3
- package/examples/api/attachTo-basics.forge.js +5 -5
- package/examples/api/boolean-operations.forge.js +3 -3
- package/examples/api/bounding-box-visualizer.forge.js +2 -2
- package/examples/api/clone-duplicate.forge.js +1 -1
- package/examples/api/colors-union-vs-array.forge.js +6 -6
- package/examples/api/connector-assembly.forge.js +4 -4
- package/examples/api/connector-basics.forge.js +2 -2
- package/examples/api/extrude-options.forge.js +4 -10
- package/examples/api/feature-created-faces.forge.js +6 -10
- package/examples/api/fillet-showcase.forge.js +1 -1
- package/examples/api/folded-service-panel-cover.forge.js +2 -2
- package/examples/api/group-test.forge.js +1 -1
- package/examples/api/group-vs-union.forge.js +1 -1
- package/examples/api/highlight-debug.forge.js +4 -0
- package/examples/api/js-module-pillars.js +1 -1
- package/examples/api/js-module-scene.js +2 -2
- package/examples/api/mesh-import-slats.forge.js +1 -1
- package/examples/api/pointAlong-orientation.forge.js +1 -1
- package/examples/api/profile-2020-b-slot6.forge.js +0 -1
- package/examples/api/route-perimeter-flange.forge.js +1 -1
- package/examples/api/sdf-rover-demo.forge.js +10 -10
- package/examples/api/sketch-on-face-demo.forge.js +2 -2
- package/examples/api/sketch-regions.forge.js +4 -4
- package/examples/api/transition-curves.forge.js +1 -1
- package/examples/api/variable-sweep-pure-sdf-test.forge.js +162 -0
- package/examples/api/variable-sweep-test.forge.js +2 -2
- package/examples/api/wood-joinery.forge.js +60 -0
- package/examples/compiler-corpus/enclosure-shell-cuts.forge.js +3 -3
- package/examples/compiler-corpus/fastener-plate-variants.forge.js +2 -2
- package/examples/experiments/drone-arm.forge.js +53 -0
- package/examples/furniture/adjustable-table.forge.js +2 -2
- package/examples/furniture/bathroom.forge.js +11 -11
- package/examples/furniture/chair.forge.js +1 -1
- package/examples/generative/crystal-growth.forge.js +2 -2
- package/examples/generative/frost-spires.forge.js +3 -3
- package/examples/generative/golden-spiral-tower.forge.js +3 -3
- package/examples/mechanical/3d-printer.forge.js +28 -28
- package/examples/mechanical/5-finger-robot-hand.forge.js +15 -15
- package/examples/mechanical/airplane-propeller.forge.js +2 -2
- package/examples/mechanical/fillet-enclosure.forge.js +1 -1
- package/examples/mechanical/headphone-hanger-v2.forge.js +2 -2
- package/examples/mechanical/robot_hand.forge.js +15 -15
- package/examples/mechanical/robot_hand_2.forge.js +9 -9
- package/examples/products/bottle.forge.js +1 -1
- package/examples/products/chess-set.forge.js +19 -19
- package/examples/products/classical-piano.forge.js +11 -11
- package/examples/products/clock.forge.js +12 -12
- package/examples/products/iphone.forge.js +8 -8
- package/examples/products/laptop.forge.js +15 -15
- package/examples/products/liquid-soap-dispenser.forge.js +18 -18
- package/examples/products/origami-fish.forge.js +8 -6
- package/examples/products/spiderman-cake.forge.js +4 -4
- package/examples/toolbox/bolted-joint.forge.js +2 -2
- package/package.json +7 -4
- package/dist/assets/EditorApp-B-vQvgam.js +0 -9888
- package/dist/assets/LandingPage-C5n9hDXI.js +0 -322
- package/dist/assets/PublishedModelPage-Dt7PCVBj.js +0 -146
- package/dist/assets/__vite-browser-external-CURh0WXD.js +0 -8
- package/dist/assets/deserializeRunResult-BLAFoiE0.js +0 -19365
- package/dist/assets/index-1CYp3zUp.js +0 -1455
- package/dist/docs-raw/CLI.md +0 -865
- package/dist-skill/docs/API/API.md +0 -1666
- package/dist-skill/docs/API/README.md +0 -37
- package/dist-skill/docs/API/assembly/assembly.md +0 -617
- package/dist-skill/docs/API/core/edge-queries.md +0 -130
- package/dist-skill/docs/API/core/parameters.md +0 -122
- package/dist-skill/docs/API/core/reserved-terms.md +0 -137
- package/dist-skill/docs/API/core/sdf.md +0 -326
- package/dist-skill/docs/API/core/skill-cli.md +0 -194
- package/dist-skill/docs/API/core/skill-guide.md +0 -205
- package/dist-skill/docs/API/core/specs.md +0 -186
- package/dist-skill/docs/API/core/topology.md +0 -372
- package/dist-skill/docs/API/entities.md +0 -268
- package/dist-skill/docs/API/output/bom.md +0 -58
- package/dist-skill/docs/API/output/brep-export.md +0 -87
- package/dist-skill/docs/API/output/dimensions.md +0 -67
- package/dist-skill/docs/API/output/export.md +0 -110
- package/dist-skill/docs/API/output/gcode.md +0 -195
- package/dist-skill/docs/API/runtime/viewport.md +0 -420
- package/dist-skill/docs/API/sheet-metal/sheet-metal.md +0 -185
- package/dist-skill/docs/API/sketch/anchor.md +0 -37
- package/dist-skill/docs/API/sketch/booleans.md +0 -91
- package/dist-skill/docs/API/sketch/core.md +0 -73
- package/dist-skill/docs/API/sketch/extrude.md +0 -62
- package/dist-skill/docs/API/sketch/on-face.md +0 -104
- package/dist-skill/docs/API/sketch/operations.md +0 -78
- package/dist-skill/docs/API/sketch/path.md +0 -75
- package/dist-skill/docs/API/sketch/primitives.md +0 -146
- package/dist-skill/docs/API/sketch/regions.md +0 -80
- package/dist-skill/docs/API/sketch/text.md +0 -108
- package/dist-skill/docs/API/sketch/transforms.md +0 -65
- package/dist-skill/docs/API/toolbox/fasteners.md +0 -129
- package/dist-skill/docs/CLI.md +0 -865
- package/dist-skill/docs/INDEX.md +0 -94
- package/dist-skill/docs/RELEASING.md +0 -55
- package/dist-skill/docs/cli-monetization.md +0 -111
- package/dist-skill/docs/deployment.md +0 -281
- package/dist-skill/docs/generated/concepts.md +0 -2112
- package/dist-skill/docs/internals/shape-from-slices.md +0 -152
- package/dist-skill/docs/platform/admin.md +0 -45
- package/dist-skill/docs/platform/architecture.md +0 -79
- package/dist-skill/docs/platform/auth.md +0 -110
- package/dist-skill/docs/platform/email.md +0 -67
- package/dist-skill/docs/platform/projects.md +0 -111
- package/dist-skill/docs/platform/sharing.md +0 -90
- package/dist-skill/docs/runbook.md +0 -345
|
@@ -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
|
-
###
|
|
50
|
+
### Inclusion rules
|
|
53
51
|
|
|
54
|
-
-
|
|
55
|
-
-
|
|
56
|
-
- `
|
|
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-
|
|
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
|
-
-
|
|
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
|
|
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
|
-
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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).
|
|
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
|
|
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
|
|
11
|
-
const green = box(size, size, size
|
|
12
|
-
const blue = box(size, size, size
|
|
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
|
|
19
|
-
const greenSep = box(size, size, size
|
|
20
|
-
const blueSep = box(size, size, size
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
1
|
+
// Extrude options — twist, taper, scaleTop.
|
|
2
2
|
//
|
|
3
3
|
// .extrude(height) is the basic form.
|
|
4
|
-
// Options: { twist, divisions, scaleTop
|
|
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.
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
33
|
-
const pocket = roundedRect(20, 12, 2
|
|
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
|
|
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
|
|
44
|
-
.cutout('flange-right', roundedRect(26, 10, 5
|
|
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
|
|
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
|
|
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
|
|