forgecad 0.6.3 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -12
- package/dist/assets/{AdminPage-CeqCUUgu.js → AdminPage-D4bocK4E.js} +250 -151
- package/dist/assets/{BlogPage-P_AJP0v9.js → BlogPage-CJEXL_zJ.js} +94 -70
- package/dist/assets/{DocsPage-CKRV2iq2.js → DocsPage-D3A_g8V3.js} +329 -163
- package/dist/assets/{EditorApp-CnC2k4cW.css → EditorApp-BWYUSpUN.css} +590 -136
- package/dist/assets/EditorApp-Cihhqcsq.js +11692 -0
- package/dist/assets/{EmbedViewer-DBlzmQ5i.js → EmbedViewer-kWjKaC_t.js} +2 -4
- package/dist/assets/LandingPageProofDriven-Bg2IUc3l.css +856 -0
- package/dist/assets/LandingPageProofDriven-DXkKlyhI.js +601 -0
- package/dist/assets/PricingPage-BsU5vzEx.js +232 -0
- package/dist/assets/{SettingsPage-BqCh9JcC.js → SettingsPage-PqvpAKIs.js} +129 -5
- package/dist/assets/{evalWorker-Ql-aKwLA.js → evalWorker-C-hzNUMy.js} +8949 -3161
- package/dist/assets/{Viewport-CoB46f5R.js → index-Pz321YAt.js} +38382 -7501
- package/dist/assets/{index-2hfs_ub0.css → index-ay13WNfa.css} +726 -53
- package/dist/assets/{javascript-DCxGoE5Y.js → javascript-DAl8Gmyo.js} +1 -1
- package/dist/assets/{manifold-CqNMHHKO.js → manifold-BcbjWLIo.js} +4 -3
- package/dist/assets/{manifold-Cce9wRFz.js → manifold-DBckbFgx.js} +1 -1
- package/dist/assets/{manifold-D6BeHIOo.js → manifold-O2AAGXyj.js} +1 -1
- package/dist/assets/{reportWorker-sFEFonXf.js → reportWorker-Dxr-5A7w.js} +8760 -3559
- package/dist/assets/{vendor-react-Dt7-aaJH.js → vendor-react-CG3i_wp0.js} +65 -8
- package/dist/docs/index.html +2 -2
- package/dist/docs-raw/CLI.md +341 -718
- package/dist/docs-raw/generated/assembly.md +699 -112
- package/dist/docs-raw/generated/concepts.md +1834 -1346
- package/dist/docs-raw/generated/core.md +1012 -1059
- package/dist/docs-raw/generated/curves.md +759 -116
- package/dist/docs-raw/generated/lib.md +43 -748
- package/dist/docs-raw/generated/output.md +139 -245
- package/dist/docs-raw/generated/sdf.md +208 -0
- package/dist/docs-raw/generated/sheet-metal.md +473 -21
- package/dist/docs-raw/generated/sketch.md +1518 -362
- package/dist/docs-raw/generated/viewport.md +368 -299
- package/dist/docs-raw/generated/wood.md +104 -0
- package/dist/index.html +2 -2
- package/dist/landing/proof-ams-adapter.png +0 -0
- package/dist/landing/proof-bolt-and-nut.png +0 -0
- package/dist/landing/proof-fillet-enclosure.png +0 -0
- package/dist/landing/proof-glasses.png +0 -0
- package/dist/landing/proof-gyroid.png +0 -0
- package/dist/sitemap.xml +6 -6
- package/dist-cli/forgecad.js +12321 -5700
- package/dist-cli/forgecad.js.map +1 -0
- package/dist-cli/solver-46FFSK2U.js +363 -0
- package/dist-cli/solver-46FFSK2U.js.map +1 -0
- package/dist-skill/CONTEXT.md +4890 -6302
- package/dist-skill/SKILL-dev.md +22 -66
- package/dist-skill/SKILL.md +20 -59
- package/dist-skill/docs/API/core/concepts.md +37 -92
- package/dist-skill/docs/CLI.md +341 -718
- package/dist-skill/docs/generated/assembly.md +699 -112
- package/dist-skill/docs/generated/core.md +1012 -1059
- package/dist-skill/docs/generated/curves.md +759 -116
- package/dist-skill/docs/generated/lib.md +43 -748
- package/dist-skill/docs/generated/output.md +139 -245
- package/dist-skill/docs/generated/sdf.md +208 -0
- package/dist-skill/docs/generated/sheet-metal.md +473 -21
- package/dist-skill/docs/generated/sketch.md +1518 -362
- package/dist-skill/docs/generated/viewport.md +368 -299
- 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/joint-design.md +78 -0
- 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 +78 -0
- package/dist-skill/docs-dev/CLI.md +488 -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 +2 -4
- package/dist-skill/docs-dev/component-model.md +164 -0
- package/dist-skill/docs-dev/generated/assembly.md +779 -0
- package/dist-skill/docs-dev/generated/core.md +1676 -0
- package/dist-skill/docs-dev/generated/curves.md +855 -0
- package/dist-skill/docs-dev/generated/lib.md +55 -0
- package/dist-skill/docs-dev/generated/output.md +234 -0
- package/dist-skill/docs-dev/generated/sdf.md +208 -0
- package/dist-skill/docs-dev/generated/sheet-metal.md +506 -0
- package/dist-skill/docs-dev/generated/sketch.md +1753 -0
- package/dist-skill/docs-dev/generated/viewport.md +513 -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/joint-design.md +78 -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 +8 -8
- package/examples/api/bill-of-materials.forge.js +9 -9
- package/examples/api/bolt-pattern.forge.js +5 -5
- package/examples/api/boolean-operations.forge.js +5 -5
- package/examples/api/bounding-box-visualizer.forge.js +3 -3
- package/examples/api/clone-duplicate.forge.js +2 -2
- package/examples/api/colors-union-vs-array.forge.js +6 -6
- package/examples/api/connector-assembly.forge.js +8 -6
- package/examples/api/connector-basics.forge.js +7 -7
- package/examples/api/constrained-sketch-mechanical.forge.js +4 -4
- package/examples/api/elbow-test.forge.js +3 -3
- package/examples/api/extrude-options.forge.js +8 -14
- package/examples/api/feature-created-faces.forge.js +6 -10
- package/examples/api/fillet-showcase.forge.js +2 -2
- package/examples/api/folded-service-panel-cover.forge.js +2 -2
- package/examples/api/gears-tier1.forge.js +5 -5
- package/examples/api/group-test.forge.js +3 -3
- 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 +4 -4
- package/examples/api/patterns.forge.js +3 -3
- package/examples/api/pointAlong-orientation.forge.js +3 -3
- package/examples/api/profile-2020-b-slot6.forge.js +4 -5
- 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/sketch-rounding-strategies.forge.js +1 -1
- package/examples/api/smooth-curve-connections.forge.js +1 -1
- package/examples/api/transition-curves.forge.js +4 -4
- 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/constraints/01-fully-constrained-rect.forge.js +2 -2
- package/examples/constraints/02-underconstrained-triangle.forge.js +1 -1
- package/examples/constraints/03-redundant-constraints.forge.js +2 -2
- package/examples/constraints/05-parallel-with-linedistance.forge.js +2 -2
- package/examples/constraints/06-complex-spectrogram.forge.js +1 -1
- package/examples/constraints/07-perpendicular-chain.forge.js +4 -4
- package/examples/constraints/08-symmetric-bracket.forge.js +4 -4
- package/examples/constraints/09-stress-spiral.forge.js +1 -1
- package/examples/constraints/10-stress-honeycomb.forge.js +1 -1
- package/examples/constraints/11-surface-grid.forge.js +2 -2
- package/examples/constraints/12-surface-nested.forge.js +4 -4
- package/examples/constraints/13-surface-complex.forge.js +1 -1
- package/examples/exact-arc-housing.forge.js +12 -0
- package/examples/experiments/drone-arm.forge.js +53 -0
- package/examples/furniture/adjustable-table.forge.js +15 -15
- package/examples/furniture/bathroom.forge.js +26 -26
- package/examples/furniture/chair.forge.js +13 -13
- package/examples/furniture/picture-frame.forge.js +6 -6
- package/examples/furniture/shoe-rack-doors.forge.js +8 -8
- package/examples/furniture/shoe-rack.forge.js +7 -7
- package/examples/furniture/table-lamp.forge.js +8 -8
- package/examples/gcode/lissajous-vase.forge.js +4 -4
- package/examples/gcode/math-surface.forge.js +3 -3
- package/examples/gcode/parametric-vase.forge.js +4 -4
- package/examples/gcode/spiral-tower.forge.js +4 -4
- package/examples/generative/crystal-growth.forge.js +9 -9
- package/examples/generative/frost-spires.forge.js +9 -9
- package/examples/generative/golden-spiral-tower.forge.js +11 -11
- package/examples/generative/molten-forge.forge.js +6 -6
- package/examples/generative/neon-coral.forge.js +7 -7
- package/examples/mechanical/3d-printer.forge.js +37 -37
- package/examples/mechanical/5-finger-robot-hand.forge.js +19 -19
- package/examples/mechanical/airplane-propeller.forge.js +9 -9
- package/examples/mechanical/bolt-and-nut.forge.js +10 -10
- package/examples/mechanical/door-with-hinges.forge.js +7 -7
- package/examples/mechanical/fillet-enclosure.forge.js +15 -11
- package/examples/mechanical/headphone-hanger-v2.forge.js +11 -11
- package/examples/mechanical/robot_hand.forge.js +24 -24
- package/examples/mechanical/robot_hand_2.forge.js +26 -26
- package/examples/nurbs-surface.forge.js +8 -0
- package/examples/nurbs-tube.forge.js +7 -0
- package/examples/products/bottle.forge.js +8 -8
- package/examples/products/chess-set.forge.js +25 -25
- package/examples/products/classical-piano.forge.js +20 -20
- package/examples/products/clock.forge.js +33 -33
- package/examples/products/cup.forge.js +5 -5
- package/examples/products/iphone.forge.js +20 -20
- package/examples/products/laptop.forge.js +24 -24
- package/examples/products/laser-cut-box.forge.js +6 -6
- package/examples/products/laser-cut-tray.forge.js +6 -6
- package/examples/products/liquid-soap-dispenser.forge.js +23 -23
- package/examples/products/origami-fish.forge.js +14 -12
- package/examples/products/spiderman-cake.forge.js +6 -6
- package/examples/shelf/container.forge.js +5 -5
- package/examples/shelf/shelf-unit.forge.js +6 -6
- package/examples/toolbox/bolted-joint.forge.js +7 -7
- package/package.json +9 -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-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/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,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
skill-group: geometry
|
|
3
|
+
skill-order: 2
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Geometry Conventions
|
|
7
|
+
|
|
8
|
+
ForgeCAD wraps Manifold (mesh kernel) and Three.js (Y-up renderer). This doc captures convention mismatches and how ForgeCAD resolves them.
|
|
9
|
+
|
|
10
|
+
## Winding Order
|
|
11
|
+
|
|
12
|
+
CCW = positive area, CW = empty in Manifold's `CrossSection`. ForgeCAD auto-fixes at all entry points:
|
|
13
|
+
- `polygon(points)` — computes signed area (shoelace), reverses if CW
|
|
14
|
+
- `path().close()` — same fix
|
|
15
|
+
|
|
16
|
+
**Rule for new code:** Any function accepting user point arrays that creates a `CrossSection` MUST auto-fix winding.
|
|
17
|
+
|
|
18
|
+
## Coordinate System (Z-up vs Y-up)
|
|
19
|
+
|
|
20
|
+
Three.js is Y-up; ForgeCAD is Z-up. Fix applied at camera level (`camera.up = (0,0,1)`) — geometry coordinates are native Z-up. Never swap Y/Z in geometry.
|
|
21
|
+
|
|
22
|
+
## Revolution Axis
|
|
23
|
+
|
|
24
|
+
`CrossSection.revolve()` revolves around Y. Profile X = radial distance, Profile Y = height (becomes Z after revolution). Profile must be at X > 0.
|
|
25
|
+
|
|
26
|
+
## Boolean Winding (3D)
|
|
27
|
+
|
|
28
|
+
Manifold requires consistent outward face normals. ForgeCAD only creates meshes through Manifold's own constructors, which guarantee correct normals.
|
|
29
|
+
|
|
30
|
+
## Transform Order
|
|
31
|
+
|
|
32
|
+
Transforms apply left-to-right. `rotate()`, `scale()`, `mirror()` operate around bounding-box center. Use `rotateAround([0,0,0], ...)` to orbit around world origin.
|
|
33
|
+
|
|
34
|
+
For explicit transform objects: `A.mul(B)` = apply A then B; `composeChain(A, B, C)` = A→B→C.
|
|
35
|
+
|
|
36
|
+
## Assembly Frame Composition
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
childWorld = composeChain(childBase, jointMotion, jointFrame, parentWorld)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Prefer `composeChain(...)` over manual `.mul(...).mul(...)` in kinematics code to avoid order mistakes.
|
|
43
|
+
|
|
44
|
+
## Summary
|
|
45
|
+
|
|
46
|
+
| Convention | User sees | Kernel needs | Where we fix it |
|
|
47
|
+
|---|---|---|---|
|
|
48
|
+
| Winding | Any point order | CCW | `polygon()`, `path().close()` |
|
|
49
|
+
| Up axis | Z-up | Y-up (Three.js) | `camera.up`, gizmo labels |
|
|
50
|
+
| Revolution | "revolve this profile" | Profile in X-Y, X>0 | Documented only |
|
|
51
|
+
| Face normals | Doesn't think about it | Outward-pointing | Manifold constructors |
|
|
52
|
+
| Transform order | Left-to-right chain | Post-multiply | Native match |
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
---
|
|
2
|
+
skill-group: recipes
|
|
3
|
+
skill-order: 5
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Joint Design Recipes
|
|
7
|
+
|
|
8
|
+
How to build mechanical joints — clevis-tongue hinges, ball-and-socket, dovetails — that actually rotate without binding and stop where they should.
|
|
9
|
+
|
|
10
|
+
## The Cavity Rule
|
|
11
|
+
|
|
12
|
+
Every mechanical joint has a **cavity** in one part and a **tenon** in the other. The cavity must be a real empty volume — not a gap implied by the absence of two separate solids.
|
|
13
|
+
|
|
14
|
+
If two adjacent parts in an assembly show a collision volume larger than the expected clearance volume in `forgecad run`, one part is missing its cavity. Both parts have solid material at the same joint position. This will look fine at rest pose but will block rotation and produce confusing joint behavior.
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
// BAD — body has a stadium cap at both ends; the "slot" between two clevis tines
|
|
18
|
+
// is just empty space next to a solid body cap. The next phalanx's tongue knuckle
|
|
19
|
+
// has nowhere to go (it intersects the previous body's cap).
|
|
20
|
+
const body = stadiumBar(L); // cap at X=0 AND X=L
|
|
21
|
+
const tine1 = box(...).translate(L, Y_OFF, 0);
|
|
22
|
+
const tine2 = box(...).translate(L, -Y_OFF, 0);
|
|
23
|
+
let phalanx = union(body, tine1, tine2);
|
|
24
|
+
|
|
25
|
+
// GOOD — body ends FLAT before the joint. Tines extend forward to the pivot.
|
|
26
|
+
// The X = L-KNUCK_R..L+KNUCK_R volume between the tines is genuinely empty.
|
|
27
|
+
const body = box(L - KNUCK_R, TONG_T, H, true).translate((L - KNUCK_R) / 2, 0, 0);
|
|
28
|
+
const tongueKnuckle = knuckleDisc(0, 0, TONG_T); // proximal cap only
|
|
29
|
+
let phalanx = union(tongueKnuckle, body, tine1, tine2, ...tineCaps);
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
After applying the cavity rule, `forgecad run` collision volume between adjacent parts in a clevis-tongue chain should drop to **zero** (or a few mm³ of clearance overlap). If it doesn't, there's still solid material where there should be a cavity.
|
|
33
|
+
|
|
34
|
+
## Connecting Cantilevers
|
|
35
|
+
|
|
36
|
+
A clevis tine arm at Y=±Y_OFF is geometrically separate from a body at Y=±TONG_T/2. With Y_OFF > TONG_T/2 + clearance, there is a **physical gap** between them. The tines float — they would snap off as soon as load is applied.
|
|
37
|
+
|
|
38
|
+
Always add a **yoke**: a short slab spanning the full clevis width, sitting between the body's flat distal end and the tines' attachment point. The yoke fills the Y gap so material is continuous from the body through to each tine.
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
const yokeLen = 3; // a few mm of structural overlap
|
|
42
|
+
const yokeStart = L - KNUCK_R - yokeLen;
|
|
43
|
+
const totalY = (Y_OFF + TINE_T / 2) * 2; // full clevis width
|
|
44
|
+
const yoke = box(yokeLen, totalY, H, true)
|
|
45
|
+
.translate(yokeStart + yokeLen / 2, 0, 0);
|
|
46
|
+
phalanx = union(phalanx, yoke);
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Hard Stops vs Slider Limits
|
|
50
|
+
|
|
51
|
+
`addRevolute({ min: 0, max: 90 })` sets **slider limits** — the viewport won't let the user drag past them, but the geometry permits any rotation. There is no physical stop.
|
|
52
|
+
|
|
53
|
+
For a **geometric** hard stop (parts can't backbend past extension, or can't curl past full closure), add a small protrusion on one part that interferes with the other at the limit angle:
|
|
54
|
+
|
|
55
|
+
- **Extension stop at 0°** (typical for fingers, knees, elbows): add a small "lip" on the dorsal side of the proximal end of the child phalanx, sized so it just touches the parent's distal dorsal corner at 0°. Negative rotation (backbending) is then blocked by part-on-part contact.
|
|
56
|
+
- **Flexion stop at θmax**: add a similar lip on the palmar side, or rely on the body-to-body collision when bodies meet.
|
|
57
|
+
|
|
58
|
+
Verify with `forgecad run` at the limit poses — the contact pair should show ~0 mm³ collision (just touching), and rotation past the limit should report a non-zero collision volume.
|
|
59
|
+
|
|
60
|
+
## Knuckle Sizing
|
|
61
|
+
|
|
62
|
+
For a clevis-tongue joint with body height H, the tongue knuckle radius and clevis tine knuckle radius must satisfy:
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
KNUCK_R >= H / 2
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
If the knuckle radius is smaller than the body's half-height, the body's corners protrude beyond the knuckle envelope. When the joint rotates, those corners sweep through space outside the cylindrical envelope and collide with the adjacent part.
|
|
69
|
+
|
|
70
|
+
Setting `KNUCK_R = H / 2` exactly makes the body cross-section a stadium that perfectly fits the knuckle envelope.
|
|
71
|
+
|
|
72
|
+
## Verification Workflow
|
|
73
|
+
|
|
74
|
+
1. Build the joint at rest pose. Run `forgecad run`. Check collision volumes.
|
|
75
|
+
2. If adjacent parts in the joint show > clearance-volume of overlap → missing cavity (apply the cavity rule).
|
|
76
|
+
3. Render with `--focus PartName` to inspect each part in isolation. The clevis end should clearly show a gap between the tines (the cavity).
|
|
77
|
+
4. Render at curl angles (set joint debug params) at 30°, 60°, 90°. No new collisions should appear from rotation.
|
|
78
|
+
5. Render at -10° (backbend test). Either no rotation possible (geometric stop in place) or rotation occurs and you need to add a stop.
|
|
@@ -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
|
|
@@ -8,31 +8,31 @@
|
|
|
8
8
|
// 2 words = edge midpoint: 'top-front', 'back-left', etc.
|
|
9
9
|
// 3 words = corner: 'top-front-left', 'bottom-back-right', etc.
|
|
10
10
|
|
|
11
|
-
const baseW =
|
|
12
|
-
const baseD =
|
|
13
|
-
const baseH =
|
|
11
|
+
const baseW = Param.number("Base Width", 100, { min: 50, max: 200, unit: "mm" });
|
|
12
|
+
const baseD = Param.number("Base Depth", 80, { min: 40, max: 150, unit: "mm" });
|
|
13
|
+
const baseH = Param.number("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 [
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
// API demo: script-declared bill of materials that gets auto-summed in report export
|
|
2
2
|
|
|
3
|
-
const frameWidth =
|
|
4
|
-
const frameDepth =
|
|
5
|
-
const legHeight =
|
|
6
|
-
const tubeW =
|
|
7
|
-
const tubeH =
|
|
8
|
-
|
|
9
|
-
const frontBolts =
|
|
10
|
-
const rearBolts =
|
|
11
|
-
const boltLength =
|
|
3
|
+
const frameWidth = Param.number('Frame Width', 900, { min: 300, max: 1800, unit: 'mm' });
|
|
4
|
+
const frameDepth = Param.number('Frame Depth', 500, { min: 200, max: 1200, unit: 'mm' });
|
|
5
|
+
const legHeight = Param.number('Leg Height', 720, { min: 300, max: 1200, unit: 'mm' });
|
|
6
|
+
const tubeW = Param.number('Tube Width', 30, { min: 15, max: 80, unit: 'mm' });
|
|
7
|
+
const tubeH = Param.number('Tube Height', 20, { min: 10, max: 80, unit: 'mm' });
|
|
8
|
+
|
|
9
|
+
const frontBolts = Param.number('Front Bolts', 8, { min: 0, max: 64, integer: true });
|
|
10
|
+
const rearBolts = Param.number('Rear Bolts', 8, { min: 0, max: 64, integer: true });
|
|
11
|
+
const boltLength = Param.number('Bolt Length', 16, { min: 6, max: 60, unit: 'mm' });
|
|
12
12
|
|
|
13
13
|
const wall = 2;
|
|
14
14
|
const longTubeMm = frameWidth * 2;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// Bolt Pattern — circularPattern + linearPattern demo
|
|
2
2
|
|
|
3
|
-
const baseR =
|
|
4
|
-
const baseH =
|
|
5
|
-
const boltR =
|
|
6
|
-
const boltCount =
|
|
7
|
-
const boltCircleR =
|
|
3
|
+
const baseR = Param.number("Base Radius", 40, { min: 20, max: 80, unit: "mm" });
|
|
4
|
+
const baseH = Param.number("Base Height", 10, { min: 5, max: 20, unit: "mm" });
|
|
5
|
+
const boltR = Param.number("Bolt Radius", 3, { min: 1, max: 6, unit: "mm" });
|
|
6
|
+
const boltCount = Param.number("Bolt Count", 6, { min: 3, max: 12 });
|
|
7
|
+
const boltCircleR = Param.number("Bolt Circle", 30, { min: 15, max: 70, unit: "mm" });
|
|
8
8
|
|
|
9
9
|
// Base plate
|
|
10
10
|
const base = circle2d(baseR).extrude(baseH);
|
|
@@ -9,13 +9,13 @@
|
|
|
9
9
|
// and the helper functions also accept arrays:
|
|
10
10
|
// difference([a, b, c]), union([a, b, c]), intersection([a, b, c])
|
|
11
11
|
|
|
12
|
-
const size =
|
|
13
|
-
const overlap =
|
|
12
|
+
const size = Param.number("Size", 30, { min: 15, max: 50, unit: "mm" });
|
|
13
|
+
const overlap = Param.number("Overlap", 15, { min: 0, max: 30, unit: "mm" });
|
|
14
14
|
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 [
|
|
@@ -36,8 +36,8 @@ function vizBBox(shape) {
|
|
|
36
36
|
// --- Demo shapes ---
|
|
37
37
|
|
|
38
38
|
// A rotated box — bbox is larger than the shape itself
|
|
39
|
-
const angle =
|
|
40
|
-
const rotBox = box(40, 30, 20
|
|
39
|
+
const angle = Param.number("Rotation", 30, { min: 0, max: 90, unit: "°" });
|
|
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 [
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// clone() / duplicate() — explicit copy helpers for Shape, TrackedShape, Sketch, and ShapeGroup.
|
|
2
2
|
|
|
3
|
-
const spacing =
|
|
3
|
+
const spacing = Param.number("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
|
|