forgecad 0.7.0 → 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 +1 -1
- package/dist/assets/{AdminPage-DAu1C1ST.js → AdminPage-D4bocK4E.js} +1 -1
- package/dist/assets/{DocsPage-Gc_BCdqC.js → DocsPage-D3A_g8V3.js} +85 -45
- package/dist/assets/{EditorApp-DG1-oUSV.css → EditorApp-BWYUSpUN.css} +133 -51
- package/dist/assets/EditorApp-Cihhqcsq.js +11692 -0
- package/dist/assets/{EmbedViewer-CEO8XbV8.js → EmbedViewer-kWjKaC_t.js} +1 -1
- package/dist/assets/LandingPageProofDriven-Bg2IUc3l.css +856 -0
- package/dist/assets/LandingPageProofDriven-DXkKlyhI.js +601 -0
- package/dist/assets/{PricingPage-BSrxu6d7.js → PricingPage-BsU5vzEx.js} +1 -1
- package/dist/assets/{SettingsPage-FUCSIRq6.js → SettingsPage-PqvpAKIs.js} +1 -1
- package/dist/assets/{evalWorker-KoR0SNKq.js → evalWorker-C-hzNUMy.js} +2218 -286
- package/dist/assets/{index-wTEK39at.js → index-Pz321YAt.js} +7416 -1481
- package/dist/assets/{index-CyVd1D4D.css → index-ay13WNfa.css} +501 -2
- package/dist/assets/{manifold-B1sGWdYk.js → manifold-BcbjWLIo.js} +3 -3
- package/dist/assets/{manifold-D7o0N50J.js → manifold-DBckbFgx.js} +1 -1
- package/dist/assets/{manifold-G5sBaXzi.js → manifold-O2AAGXyj.js} +1 -1
- package/dist/assets/{reportWorker-DYcRHhv9.js → reportWorker-Dxr-5A7w.js} +2003 -259
- package/dist/docs/index.html +2 -2
- package/dist/docs-raw/CLI.md +488 -0
- package/dist/docs-raw/generated/assembly.md +19 -11
- package/dist/docs-raw/generated/concepts.md +1023 -360
- package/dist/docs-raw/generated/core.md +1165 -264
- package/dist/docs-raw/generated/curves.md +168 -1
- package/dist/docs-raw/generated/lib.md +10 -5
- package/dist/docs-raw/generated/output.md +1 -1
- package/dist/docs-raw/generated/sdf.md +208 -0
- package/dist/docs-raw/generated/sketch.md +1281 -329
- package/dist/docs-raw/generated/viewport.md +29 -2
- 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 +3148 -555
- package/dist-cli/forgecad.js.map +1 -1
- package/dist-cli/{solver-FV7TJZGI.js → solver-46FFSK2U.js} +1 -3
- package/dist-cli/{solver-FV7TJZGI.js.map → solver-46FFSK2U.js.map} +1 -1
- package/dist-skill/CONTEXT.md +3700 -1153
- package/dist-skill/SKILL-dev.md +15 -17
- package/dist-skill/SKILL.md +14 -9
- package/dist-skill/docs/API/core/concepts.md +28 -1
- package/dist-skill/docs/CLI.md +488 -0
- package/dist-skill/docs/generated/assembly.md +19 -11
- package/dist-skill/docs/generated/core.md +1165 -264
- package/dist-skill/docs/generated/curves.md +168 -1
- package/dist-skill/docs/generated/lib.md +10 -5
- package/dist-skill/docs/generated/output.md +1 -1
- package/dist-skill/docs/generated/sdf.md +208 -0
- package/dist-skill/docs/generated/sketch.md +1281 -329
- package/dist-skill/docs/generated/viewport.md +29 -2
- package/dist-skill/docs/guides/joint-design.md +78 -0
- package/dist-skill/docs-dev/API/core/concepts.md +28 -1
- package/dist-skill/docs-dev/CLI.md +488 -0
- package/dist-skill/docs-dev/coding.md +1 -1
- package/dist-skill/docs-dev/component-model.md +164 -0
- package/dist-skill/docs-dev/generated/assembly.md +19 -11
- package/dist-skill/docs-dev/generated/core.md +1165 -264
- package/dist-skill/docs-dev/generated/curves.md +168 -1
- package/dist-skill/docs-dev/generated/lib.md +10 -5
- package/dist-skill/docs-dev/generated/output.md +1 -1
- package/dist-skill/docs-dev/generated/sdf.md +208 -0
- package/dist-skill/docs-dev/generated/sketch.md +1281 -329
- package/dist-skill/docs-dev/generated/viewport.md +29 -2
- package/dist-skill/docs-dev/guides/joint-design.md +78 -0
- package/examples/api/attachTo-basics.forge.js +3 -3
- 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 +2 -2
- package/examples/api/bounding-box-visualizer.forge.js +1 -1
- package/examples/api/clone-duplicate.forge.js +1 -1
- package/examples/api/connector-assembly.forge.js +4 -2
- package/examples/api/connector-basics.forge.js +5 -5
- 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 +4 -4
- package/examples/api/fillet-showcase.forge.js +1 -1
- package/examples/api/gears-tier1.forge.js +5 -5
- package/examples/api/group-test.forge.js +2 -2
- package/examples/api/mesh-import-slats.forge.js +3 -3
- package/examples/api/patterns.forge.js +3 -3
- package/examples/api/pointAlong-orientation.forge.js +2 -2
- package/examples/api/profile-2020-b-slot6.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 +3 -3
- 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/furniture/adjustable-table.forge.js +13 -13
- package/examples/furniture/bathroom.forge.js +15 -15
- package/examples/furniture/chair.forge.js +12 -12
- 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 +7 -7
- package/examples/generative/frost-spires.forge.js +6 -6
- package/examples/generative/golden-spiral-tower.forge.js +8 -8
- 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 +9 -9
- package/examples/mechanical/5-finger-robot-hand.forge.js +4 -4
- package/examples/mechanical/airplane-propeller.forge.js +7 -7
- 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 +14 -10
- package/examples/mechanical/headphone-hanger-v2.forge.js +9 -9
- package/examples/mechanical/robot_hand.forge.js +10 -10
- package/examples/mechanical/robot_hand_2.forge.js +17 -17
- package/examples/nurbs-surface.forge.js +8 -0
- package/examples/nurbs-tube.forge.js +7 -0
- package/examples/products/bottle.forge.js +7 -7
- package/examples/products/chess-set.forge.js +6 -6
- package/examples/products/classical-piano.forge.js +9 -9
- package/examples/products/clock.forge.js +21 -21
- package/examples/products/cup.forge.js +5 -5
- package/examples/products/iphone.forge.js +12 -12
- package/examples/products/laptop.forge.js +9 -9
- 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 +5 -5
- package/examples/products/origami-fish.forge.js +6 -6
- package/examples/products/spiderman-cake.forge.js +2 -2
- package/examples/shelf/container.forge.js +5 -5
- package/examples/shelf/shelf-unit.forge.js +6 -6
- package/examples/toolbox/bolted-joint.forge.js +5 -5
- package/package.json +3 -1
- package/dist/assets/EditorApp-D9bJvtf7.js +0 -11338
- package/dist/assets/LandingPage-CdCuEOdC.js +0 -451
- package/dist-cli/chunk-PZ5AY32C.js +0 -10
- package/dist-cli/chunk-PZ5AY32C.js.map +0 -1
- package/dist-skill/docs/CLI/export.md +0 -91
- package/dist-skill/docs/CLI/projects.md +0 -107
- package/dist-skill/docs/CLI/studio_publishing.md +0 -52
- package/dist-skill/docs/CLI/validation.md +0 -66
- package/dist-skill/docs-dev/API/core/sdf-advanced.md +0 -92
- package/dist-skill/docs-dev/API/core/sdf-primitives.md +0 -58
- package/dist-skill/docs-dev/API/core/sdf-workflow.md +0 -42
- package/dist-skill/docs-dev/CLI/export.md +0 -91
- package/dist-skill/docs-dev/CLI/projects.md +0 -107
- package/dist-skill/docs-dev/CLI/studio_publishing.md +0 -52
- package/dist-skill/docs-dev/CLI/validation.md +0 -66
|
@@ -9,7 +9,7 @@ Cut planes, exploded views, joint animations, and scene configuration.
|
|
|
9
9
|
|
|
10
10
|
## Contents
|
|
11
11
|
|
|
12
|
-
- [Viewport & Runtime](#viewport-runtime) — `jointsView`, `explodeView`, `cutPlane`, `cutPlane`, `scene`, `viewConfig`, `showLabels`, `highlight`, `highlight`, `highlight`, `highlight`, `highlight`, `highlight`, `highlight`, `highlight`
|
|
12
|
+
- [Viewport & Runtime](#viewport-runtime) — `jointsView`, `explodeView`, `cutPlane`, `cutPlane`, `mock`, `scene`, `viewConfig`, `showLabels`, `highlight`, `highlight`, `highlight`, `highlight`, `highlight`, `highlight`, `highlight`, `highlight`
|
|
13
13
|
- [RouteBuilder](#routebuilder)
|
|
14
14
|
- [route](#route)
|
|
15
15
|
|
|
@@ -167,6 +167,33 @@ cutPlane(name: string, normal: [ number, number, number ], offset?: number, opti
|
|
|
167
167
|
cutPlane(name: string, normal: [ number, number, number ], options?: CutPlaneOptions): void
|
|
168
168
|
```
|
|
169
169
|
|
|
170
|
+
#### `mock()` — Register a mock (context) object for visualization and collision checking.
|
|
171
|
+
|
|
172
|
+
Mock objects appear in the viewport and spatial analysis when you run a file directly, but are excluded when the file is imported via [`require()`](/docs/core#require). This lets you model the surrounding context — walls, bolts, mating parts — without polluting the module's exports.
|
|
173
|
+
|
|
174
|
+
The shape is returned unchanged, so you can reference it for alignment, dimensioning, and `verify` checks.
|
|
175
|
+
|
|
176
|
+
Mock objects participate in `forgecad run` collision detection and spatial analysis. Their names appear with a `(mock)` suffix in reports.
|
|
177
|
+
|
|
178
|
+
In the viewport, mock objects render at reduced opacity so they are visually distinct from real geometry.
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
// bracket.forge.js
|
|
182
|
+
const wall = mock(box(100, 200, 10).translate(0, 0, -5), "wall");
|
|
183
|
+
const bolt = mock(cylinder(3, 15).translate(10, 15, 0), "bolt");
|
|
184
|
+
|
|
185
|
+
const bracket = box(20, 30, 5);
|
|
186
|
+
verify.notColliding("bracket vs wall", bracket, wall);
|
|
187
|
+
|
|
188
|
+
return bracket;
|
|
189
|
+
// When imported: only bracket is exported
|
|
190
|
+
// When run directly: bracket + wall + bolt all visible
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
mock<T extends Shape>(shape: T, name?: string): T
|
|
195
|
+
```
|
|
196
|
+
|
|
170
197
|
#### `scene()` — Configure the scene environment for the current script execution.
|
|
171
198
|
|
|
172
199
|
Controls camera position, lighting rig, background color or gradient, atmospheric fog, environment maps, post-processing effects, and capture parameters for the `forgecad capture` command. Multiple calls merge — later values override earlier ones on a per-key basis, so you can split configuration across multiple `scene()` calls.
|
|
@@ -177,7 +204,7 @@ Setting `camera.position` overrides auto-framing — the viewport will no longer
|
|
|
177
204
|
|
|
178
205
|
Post-processing effects (`bloom`, `vignette`, `grain`) work in the browser viewport only. The CLI applies camera, lights, background, fog, and `toneMappingExposure` but skips shader effects.
|
|
179
206
|
|
|
180
|
-
All numeric values accept
|
|
207
|
+
All numeric values accept `param()` expressions.
|
|
181
208
|
|
|
182
209
|
```js
|
|
183
210
|
scene({
|
|
@@ -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.
|
|
@@ -8,9 +8,9 @@
|
|
|
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
15
|
const base = box(baseW, baseD, baseH).color('#888888');
|
|
16
16
|
|
|
@@ -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,8 +9,8 @@
|
|
|
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
|
|
@@ -36,7 +36,7 @@ function vizBBox(shape) {
|
|
|
36
36
|
// --- Demo shapes ---
|
|
37
37
|
|
|
38
38
|
// A rotated box — bbox is larger than the shape itself
|
|
39
|
-
const angle =
|
|
39
|
+
const angle = Param.number("Rotation", 30, { min: 0, max: 90, unit: "°" });
|
|
40
40
|
const rotBox = box(40, 30, 20).rotateZ(angle).color('#4488cc');
|
|
41
41
|
const rotBBox = vizBBox(rotBox).color('#cc4444');
|
|
42
42
|
|
|
@@ -1,6 +1,6 @@
|
|
|
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
6
|
const block = box(36, 20, 12).color("#4a90e2");
|
|
@@ -9,20 +9,22 @@ const doorW = 60, doorH = 100, doorT = 4;
|
|
|
9
9
|
const frameT = 8, frameD = 10;
|
|
10
10
|
|
|
11
11
|
// ── Door with hinge connectors ─────────────────────────────────────────────
|
|
12
|
+
// Hinge axes point "outward" from each part. When connectors mate,
|
|
13
|
+
// they meet face-to-face (anti-parallel), producing the shared rotation axis.
|
|
12
14
|
|
|
13
15
|
const door = box(doorW, doorT, doorH)
|
|
14
16
|
.translate(doorW / 2, 0, 0)
|
|
15
17
|
.withConnectors({
|
|
16
18
|
hinge_top: connector.male("hinge_pin", {
|
|
17
19
|
origin: [0, 0, doorH * 0.3],
|
|
18
|
-
axis: [0, 0, 1],
|
|
20
|
+
axis: [0, 0, -1],
|
|
19
21
|
kind: "revolute",
|
|
20
22
|
min: 0,
|
|
21
23
|
max: 110,
|
|
22
24
|
}),
|
|
23
25
|
hinge_bottom: connector.male("hinge_pin", {
|
|
24
26
|
origin: [0, 0, -doorH * 0.3],
|
|
25
|
-
axis: [0, 0, 1],
|
|
27
|
+
axis: [0, 0, -1],
|
|
26
28
|
kind: "revolute",
|
|
27
29
|
min: 0,
|
|
28
30
|
max: 110,
|
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
// - matchTo() single-pair connector matching
|
|
7
7
|
// - Auto-bubbling: connectors on named group children are accessible via "ChildName.connectorName"
|
|
8
8
|
|
|
9
|
-
const panelH =
|
|
10
|
-
const panelD =
|
|
11
|
-
const panelT =
|
|
12
|
-
const shelfCount =
|
|
13
|
-
const cabinetW =
|
|
9
|
+
const panelH = Param.number("Panel Height", 200, { min: 100, max: 400, unit: "mm" });
|
|
10
|
+
const panelD = Param.number("Panel Depth", 120, { min: 60, max: 200, unit: "mm" });
|
|
11
|
+
const panelT = Param.number("Panel Thickness", 12, { unit: "mm" });
|
|
12
|
+
const shelfCount = Param.number("Shelves", 3, { min: 1, max: 6 });
|
|
13
|
+
const cabinetW = Param.number("Cabinet Width", 180, { min: 100, max: 400, unit: "mm" });
|
|
14
14
|
|
|
15
15
|
// ── Side Panel with shelf slots ────────────────────────────────────────────
|
|
16
16
|
|
|
@@ -11,10 +11,10 @@
|
|
|
11
11
|
* 4. Connecting rod — constrained trapezoid with symmetric sides
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
const W =
|
|
15
|
-
const H =
|
|
16
|
-
const T =
|
|
17
|
-
const R =
|
|
14
|
+
const W = Param.number('width', 60, { min: 30, max: 120, unit: 'mm' });
|
|
15
|
+
const H = Param.number('height', 40, { min: 20, max: 80, unit: 'mm' });
|
|
16
|
+
const T = Param.number('thick', 8, { min: 3, max: 20, unit: 'mm' });
|
|
17
|
+
const R = Param.number('radius', 10, { min: 5, max: 25, unit: 'mm' });
|
|
18
18
|
|
|
19
19
|
// ─── 1. Slotted plate ─────────────────────────────────────────────────────────
|
|
20
20
|
const slottedPlate = (() => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Test lib.elbow() — pipe bend primitive
|
|
2
|
-
const pipeR =
|
|
3
|
-
const bendR =
|
|
4
|
-
const angle =
|
|
2
|
+
const pipeR = Param.number("Pipe Radius", 5, { min: 2, max: 15, unit: "mm" });
|
|
3
|
+
const bendR = Param.number("Bend Radius", 25, { min: 10, max: 60, unit: "mm" });
|
|
4
|
+
const angle = Param.number("Angle", 90, { min: 15, max: 180, unit: "°" });
|
|
5
5
|
|
|
6
6
|
// Basic elbow at default orientation
|
|
7
7
|
const basic = lib.elbow(pipeR, bendR, angle).color('#B87333');
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
// .extrude(height) is the basic form.
|
|
4
4
|
// Options: { twist, divisions, scaleTop }
|
|
5
5
|
|
|
6
|
-
const r =
|
|
7
|
-
const h =
|
|
8
|
-
const twist =
|
|
9
|
-
const taper =
|
|
6
|
+
const r = Param.number("Radius", 20, { min: 10, max: 40, unit: "mm" });
|
|
7
|
+
const h = Param.number("Height", 60, { min: 20, max: 120, unit: "mm" });
|
|
8
|
+
const twist = Param.number("Twist", 90, { min: 0, max: 360, unit: "°" });
|
|
9
|
+
const taper = Param.number("Taper", 0.5, { min: 0.1, max: 1.0 });
|
|
10
10
|
const spacing = 60;
|
|
11
11
|
|
|
12
12
|
// 1. Plain extrude
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Fillet Showcase — the new fillet() API
|
|
2
2
|
// Demonstrates: fillet(), edge queries, multi-edge, curved edges
|
|
3
3
|
|
|
4
|
-
const r =
|
|
4
|
+
const r = Param.number("Fillet Radius", 3, { min: 0.5, max: 8, unit: "mm" });
|
|
5
5
|
|
|
6
6
|
// ── 1. Basic: fillet all edges of a box ─────────────────────────────────────
|
|
7
7
|
const simpleBox = box(30, 20, 15);
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// Tier 1 gears demo: spur pair + ring gear + rack gear
|
|
2
2
|
|
|
3
|
-
const moduleSize =
|
|
4
|
-
const pinionTeeth =
|
|
5
|
-
const drivenTeeth =
|
|
6
|
-
const backlash =
|
|
7
|
-
const faceWidth =
|
|
3
|
+
const moduleSize = Param.number("Module", 1.25, { min: 0.6, max: 3.0, step: 0.05 });
|
|
4
|
+
const pinionTeeth = Param.number("Pinion Teeth", 14, { min: 8, max: 28, integer: true });
|
|
5
|
+
const drivenTeeth = Param.number("Driven Teeth", 42, { min: 16, max: 90, integer: true });
|
|
6
|
+
const backlash = Param.number("Backlash", 0.05, { min: 0, max: 0.2, step: 0.01, unit: "mm" });
|
|
7
|
+
const faceWidth = Param.number("Face Width", 10, { min: 4, max: 18, unit: "mm" });
|
|
8
8
|
|
|
9
9
|
const pair = lib.gearPair({
|
|
10
10
|
pinion: {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Test assembly grouping — nested group format
|
|
2
|
-
const baseW =
|
|
3
|
-
const baseD =
|
|
2
|
+
const baseW = Param.number("Base Width", 100, { min: 60, max: 200, unit: "mm" });
|
|
3
|
+
const baseD = Param.number("Base Depth", 80, { min: 40, max: 150, unit: "mm" });
|
|
4
4
|
|
|
5
5
|
// Bed assembly
|
|
6
6
|
const bedPlate = box(baseW, baseD, 5).color('#666666');
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
// Imports an external STL mesh and intersects it with a parametric
|
|
4
4
|
// slat pattern to create a layered sculpture effect.
|
|
5
5
|
|
|
6
|
-
const slatCount =
|
|
7
|
-
const slatThickness =
|
|
8
|
-
const gap =
|
|
6
|
+
const slatCount = Param.number("Slat count", 16, { min: 4, max: 40 });
|
|
7
|
+
const slatThickness = Param.number("Slat thickness", 1.2, { min: 0.4, max: 3, unit: "mm" });
|
|
8
|
+
const gap = Param.number("Gap", 1.5, { min: 0.5, max: 5, unit: "mm" });
|
|
9
9
|
|
|
10
10
|
// Import an external mesh file
|
|
11
11
|
const mesh = importMesh("assets/sphere.stl");
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// Patterns — linearPattern and circularPattern for repeating shapes.
|
|
2
2
|
|
|
3
|
-
const count =
|
|
4
|
-
const spacing =
|
|
5
|
-
const radius =
|
|
3
|
+
const count = Param.number("Count", 6, { min: 2, max: 12, integer: true });
|
|
4
|
+
const spacing = Param.number("Spacing", 20, { min: 10, max: 40, unit: "mm" });
|
|
5
|
+
const radius = Param.number("Ring Radius", 40, { min: 20, max: 80, unit: "mm" });
|
|
6
6
|
|
|
7
7
|
// --- linearPattern: repeat along a direction ---
|
|
8
8
|
const peg = cylinder(15, 4).color('#4488cc');
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
// After pointAlong, the cylinder starts at origin and extends in that direction.
|
|
8
8
|
// Always call pointAlong BEFORE translate/attachTo.
|
|
9
9
|
|
|
10
|
-
const len =
|
|
11
|
-
const r =
|
|
10
|
+
const len = Param.number("Length", 80, { min: 30, max: 150, unit: "mm" });
|
|
11
|
+
const r = Param.number("Radius", 5, { min: 2, max: 15, unit: "mm" });
|
|
12
12
|
const spacing = 40;
|
|
13
13
|
|
|
14
14
|
// Default: along +Z (up)
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
// - direct 3D helper (`lib.profile2020BSlot6`)
|
|
5
5
|
// - parameterized technical dimensions
|
|
6
6
|
|
|
7
|
-
const length =
|
|
8
|
-
const slotDepth =
|
|
9
|
-
const slotInner =
|
|
10
|
-
const centerBore =
|
|
7
|
+
const length = Param.number("Length", 220, { min: 40, max: 800, unit: "mm" });
|
|
8
|
+
const slotDepth = Param.number("Slot Depth", 5.5, { min: 4.6, max: 6.6, step: 0.1, unit: "mm" });
|
|
9
|
+
const slotInner = Param.number("Slot Inner Width", 8.2, { min: 7, max: 10.5, step: 0.1, unit: "mm" });
|
|
10
|
+
const centerBore = Param.number("Center Bore", 5.5, { min: 0, max: 6.5, step: 0.1, unit: "mm" });
|
|
11
11
|
|
|
12
12
|
const profile2d = lib.profile2020BSlot6Profile({
|
|
13
13
|
slotInnerWidth: slotInner,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Compare common sketch-rounding strategies on the same roof profile.
|
|
2
2
|
// Only the selective fillet keeps the lower roof corners sharp.
|
|
3
3
|
|
|
4
|
-
const radius =
|
|
4
|
+
const radius = Param.number("Radius", 14, { min: 4, max: 24, unit: "mm" });
|
|
5
5
|
const gap = 120;
|
|
6
6
|
const bodyWidth = 90;
|
|
7
7
|
const bodyHeight = 44;
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* Drag the Blend Weight slider to shift the blend shape!
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
const blendWeight =
|
|
13
|
+
const blendWeight = Param.number('Blend Weight', 0.5, { min: 0.1, max: 0.9, step: 0.05 });
|
|
14
14
|
|
|
15
15
|
// ─── 1. Gothic arch — two arcs meeting at a point ──────────────────────────
|
|
16
16
|
// Two arcs sharing a crown point — smooth by construction since both
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
// Demonstrates transitionCurve(), transitionSurface(), connectEdges(),
|
|
3
3
|
// and weighted blending (G1 + G2 continuity).
|
|
4
4
|
|
|
5
|
-
const weight =
|
|
6
|
-
const weightB =
|
|
7
|
-
const radius =
|
|
5
|
+
const weight = Param.number("Weight A", 1.0, { min: 0.1, max: 5.0, step: 0.1 });
|
|
6
|
+
const weightB = Param.number("Weight B", 1.0, { min: 0.1, max: 5.0, step: 0.1 });
|
|
7
|
+
const radius = Param.number("Tube Radius", 1.5, { min: 0.3, max: 5, unit: "mm" });
|
|
8
8
|
|
|
9
9
|
// Small anchor block at a point (visual reference for where the tube connects)
|
|
10
10
|
function anchor(pos) {
|
|
@@ -21,7 +21,7 @@ sk.horizontal(bottom);
|
|
|
21
21
|
sk.vertical(right);
|
|
22
22
|
sk.parallel(bottom, top);
|
|
23
23
|
sk.parallel(right, left);
|
|
24
|
-
sk.length(bottom,
|
|
25
|
-
sk.length(right,
|
|
24
|
+
sk.length(bottom, Param.number("width", 40, { unit: "mm" }));
|
|
25
|
+
sk.length(right, Param.number("height", 30, { unit: "mm" }));
|
|
26
26
|
|
|
27
27
|
return sk.solve();
|
|
@@ -24,7 +24,7 @@ sk.absoluteAngle(lBottom, 0); // line must be at 0 degrees (= horizontal)
|
|
|
24
24
|
sk.vertical(lRight);
|
|
25
25
|
sk.vertical(lLeft);
|
|
26
26
|
sk.horizontal(lTop);
|
|
27
|
-
sk.length(lBottom,
|
|
28
|
-
sk.length(lRight,
|
|
27
|
+
sk.length(lBottom, Param.number("width", 10, { unit: "mm" }));
|
|
28
|
+
sk.length(lRight, Param.number("height", 5, { unit: "mm" }));
|
|
29
29
|
|
|
30
30
|
return sk.solve();
|
|
@@ -20,10 +20,10 @@ sk.addLoop([p1, p2, p4, p3]);
|
|
|
20
20
|
sk.fix(p1);
|
|
21
21
|
sk.horizontal(bottom);
|
|
22
22
|
sk.vertical(left);
|
|
23
|
-
sk.length(bottom,
|
|
23
|
+
sk.length(bottom, Param.number("width", 20, { unit: "mm" }));
|
|
24
24
|
|
|
25
25
|
// lineDistance already implies parallel:
|
|
26
|
-
sk.lineDistance(bottom, top,
|
|
26
|
+
sk.lineDistance(bottom, top, Param.number("height", 10, { unit: "mm" }));
|
|
27
27
|
sk.parallel(bottom, top); // redundant — lineDistance already forces parallel
|
|
28
28
|
|
|
29
29
|
sk.parallel(left, right);
|
|
@@ -20,9 +20,9 @@ sk.absoluteAngle(l1, 0);
|
|
|
20
20
|
sk.perpendicular(l1, l2);
|
|
21
21
|
sk.perpendicular(l2, l3);
|
|
22
22
|
sk.perpendicular(l3, l4);
|
|
23
|
-
sk.length(l1,
|
|
24
|
-
sk.length(l2,
|
|
25
|
-
sk.length(l3,
|
|
26
|
-
sk.length(l4,
|
|
23
|
+
sk.length(l1, Param.number("seg1", 20, { unit: "mm" }));
|
|
24
|
+
sk.length(l2, Param.number("seg2", 15, { unit: "mm" }));
|
|
25
|
+
sk.length(l3, Param.number("seg3", 15, { unit: "mm" }));
|
|
26
|
+
sk.length(l4, Param.number("seg4", 15, { unit: "mm" }));
|
|
27
27
|
|
|
28
28
|
return sk.solve();
|
|
@@ -47,9 +47,9 @@ sk.vertical(lLeft);
|
|
|
47
47
|
sk.horizontal(lLeftTop);
|
|
48
48
|
sk.vertical(lLeftUp);
|
|
49
49
|
|
|
50
|
-
sk.length(lBottom,
|
|
51
|
-
sk.length(lLeft,
|
|
52
|
-
sk.length(lLeftTop,
|
|
53
|
-
sk.length(lLeftUp,
|
|
50
|
+
sk.length(lBottom, Param.number("base_width", 50, { unit: "mm" }));
|
|
51
|
+
sk.length(lLeft, Param.number("leg_height", 20, { unit: "mm" }));
|
|
52
|
+
sk.length(lLeftTop, Param.number("step_width", 15, { unit: "mm" }));
|
|
53
|
+
sk.length(lLeftUp, Param.number("top_height", 20, { unit: "mm" }));
|
|
54
54
|
|
|
55
55
|
return sk.solve();
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
const sk = constrainedSketch();
|
|
7
7
|
|
|
8
8
|
const N = 50; // number of segments
|
|
9
|
-
const SEG_LEN =
|
|
9
|
+
const SEG_LEN = Param.number("seg_len", 5, { unit: "mm" });
|
|
10
10
|
|
|
11
11
|
// Build a spiral: each segment turns 90° left from the previous,
|
|
12
12
|
// with increasing runs (1,1,2,2,3,3,4,4,...) to form a square spiral.
|
|
@@ -8,7 +8,7 @@ const sk = constrainedSketch();
|
|
|
8
8
|
|
|
9
9
|
const COLS = 3;
|
|
10
10
|
const ROWS = 2;
|
|
11
|
-
const SIDE =
|
|
11
|
+
const SIDE = Param.number("cell_size", 8, { unit: "mm" });
|
|
12
12
|
|
|
13
13
|
// Flat-top hex: vertex 0 at 0° (right), CCW
|
|
14
14
|
const vAngles = [0, 60, 120, 180, 240, 300].map(d => d * Math.PI / 180);
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
const sk = constrainedSketch();
|
|
8
8
|
|
|
9
|
-
const W =
|
|
10
|
-
const H =
|
|
9
|
+
const W = Param.number("width", 150, { unit: "mm" });
|
|
10
|
+
const H = Param.number("height", 100, { unit: "mm" });
|
|
11
11
|
|
|
12
12
|
// Outer box corners
|
|
13
13
|
const p00 = sk.point(0, 0);
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
const sk = constrainedSketch();
|
|
8
8
|
|
|
9
|
-
const OW =
|
|
10
|
-
const OH =
|
|
11
|
-
const IW =
|
|
12
|
-
const IH =
|
|
9
|
+
const OW = Param.number("outer_w", 120, { unit: "mm" });
|
|
10
|
+
const OH = Param.number("outer_h", 100, { unit: "mm" });
|
|
11
|
+
const IW = Param.number("inner_w", 60, { unit: "mm" });
|
|
12
|
+
const IH = Param.number("inner_h", 50, { unit: "mm" });
|
|
13
13
|
|
|
14
14
|
// Outer rectangle
|
|
15
15
|
const o1 = sk.point(0, 0);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Bearing housing with exact circular arc (not polyline-approximated)
|
|
2
|
+
const profile = path()
|
|
3
|
+
.moveTo(0, 0)
|
|
4
|
+
.lineTo(50, 0)
|
|
5
|
+
.lineTo(50, 10)
|
|
6
|
+
.exactArcTo(40, 20, { radius: 10 })
|
|
7
|
+
.lineTo(0, 20)
|
|
8
|
+
.close();
|
|
9
|
+
|
|
10
|
+
const housing = profile.extrude(30);
|
|
11
|
+
const bore = cylinder(30, 8).translate(25, 10, 0);
|
|
12
|
+
return difference(housing, bore);
|