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,104 @@
|
|
|
1
|
+
---
|
|
2
|
+
skill-group: toolbox
|
|
3
|
+
skill-order: 100
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Woodworking
|
|
7
|
+
|
|
8
|
+
Wood boards with grain/species metadata, and joinery operations: dado, rabbet, mortise & tenon. Access via `Wood.*`.
|
|
9
|
+
|
|
10
|
+
## Contents
|
|
11
|
+
|
|
12
|
+
- [WoodBoard](#woodboard)
|
|
13
|
+
- [Wood](#wood)
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Classes
|
|
18
|
+
|
|
19
|
+
### `WoodBoard`
|
|
20
|
+
|
|
21
|
+
A board of wood with metadata for manufacturing: grain direction, species, and dimensions. The underlying geometry is a simple box.
|
|
22
|
+
|
|
23
|
+
Shape is mutable — joint operations (Wood.dado, Wood.rabbet, Wood.mortiseAndTenon) subtract material in-place. Transform methods return new WoodBoard instances preserving all metadata.
|
|
24
|
+
|
|
25
|
+
**Properties:**
|
|
26
|
+
|
|
27
|
+
| Property | Type | Description |
|
|
28
|
+
|----------|------|-------------|
|
|
29
|
+
| `shape` | `Shape` | The underlying 3D shape — mutable, joints modify it in-place. |
|
|
30
|
+
| `width` | `number` | Board width (mm) — the longer flat dimension |
|
|
31
|
+
| `height` | `number` | Board height (mm) — the shorter flat dimension |
|
|
32
|
+
| `thickness` | `number` | Board thickness (mm) |
|
|
33
|
+
| `grain` | `string` | Grain direction: "long" or "cross" |
|
|
34
|
+
| `species` | `string` | Wood species, e.g. "birch", "oak" |
|
|
35
|
+
| `material` | `string` | Material label for BOM |
|
|
36
|
+
|
|
37
|
+
**Methods:**
|
|
38
|
+
|
|
39
|
+
#### `cut()` — Subtract a cutter from this board, modifying it in-place. Used by joint functions (dado, rabbet, mortiseAndTenon).
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
cut(cutter: Shape): this
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
#### `translate()` — Translate the board in 3D space.
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
translate(x: number, y: number, z: number): WoodBoard
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
#### `rotate()` — Rotate the board around an axis by a given angle in degrees.
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
rotate(axis: [ number, number, number ], angleDeg: number, options?: { pivot?: [ number, number, number ]; }): WoodBoard
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
#### `rotateX()` — Rotate the board around the X axis by a given angle in degrees.
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
rotateX(angleDeg: number): WoodBoard
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
#### `rotateY()` — Rotate the board around the Y axis by a given angle in degrees.
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
rotateY(angleDeg: number): WoodBoard
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
#### `rotateZ()` — Rotate the board around the Z axis by a given angle in degrees.
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
rotateZ(angleDeg: number): WoodBoard
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
#### `mirror()` — Mirror the board across a plane defined by its normal.
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
mirror(normal: [ number, number, number ]): WoodBoard
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
#### `color()` — Set the board's display color.
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
color(value: string): WoodBoard
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
#### `clone()` — Clone the board (creates an independent copy of the underlying shape).
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
clone(): WoodBoard
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Constants
|
|
96
|
+
|
|
97
|
+
### `Wood`
|
|
98
|
+
|
|
99
|
+
Woodworking namespace — create boards and cut joints. **Boards:** `Wood.board()` creates a WoodBoard with grain, species, and BOM metadata. **Joints:** `Wood.dado()`, `Wood.rabbet()`, `Wood.mortiseAndTenon()` mutate boards in-place by subtracting material, following the same pattern as LaserKit's [`fingerJoint()`](/docs/sheet-metal#fingerjoint).
|
|
100
|
+
|
|
101
|
+
- `readonly board: (width: number, height: number, thickness: number, opts?: WoodBoardOptions) => WoodBoard` — Create a wood board with metadata for manufacturing. The board is a box(width, height, thickness) centered on XY, base at Z=0. Width along X, height along Y, thickness along Z (0 to thickness).
|
|
102
|
+
- `dado(host: WoodBoard, guest: WoodBoard, opts: DadoOptions): void` — Cut a dado (channel) across the face of a host board for a guest board to sit in. Mutates `host.shape` by subtracting a rectangular channel.
|
|
103
|
+
- `rabbet(board: WoodBoard, opts: RabbetOptions): void` — Cut a rabbet (L-shaped step) along an edge of a board. Mutates `board.shape` by subtracting a step from the specified edge.
|
|
104
|
+
- `mortiseAndTenon(mortiseBoard: WoodBoard, tenonBoard: WoodBoard, opts?: MortiseAndTenonOptions): void` — Cut a mortise in one board and shape a tenon on another. Mutates both boards — subtracts the mortise pocket and removes shoulder material to form the tenon.
|
|
@@ -17,19 +17,18 @@ ForgeCAD uses a **Z-up** right-handed coordinate system.
|
|
|
17
17
|
|
|
18
18
|
## Standard Views
|
|
19
19
|
|
|
20
|
-
| View | Camera position direction | Sees plane |
|
|
21
|
-
|
|
22
|
-
| Front | −Y
|
|
23
|
-
| Back | +Y
|
|
24
|
-
| Right | +X
|
|
25
|
-
| Left | −X
|
|
26
|
-
| Top | +Z
|
|
27
|
-
| Bottom | −Z
|
|
28
|
-
| Iso | +X −Y +Z (diagonal) | — | Z |
|
|
20
|
+
| View | Camera position direction | Sees plane |
|
|
21
|
+
|--------|--------------------------|------------|
|
|
22
|
+
| Front | −Y | XZ |
|
|
23
|
+
| Back | +Y | XZ |
|
|
24
|
+
| Right | +X | YZ |
|
|
25
|
+
| Left | −X | YZ |
|
|
26
|
+
| Top | +Z | XY |
|
|
27
|
+
| Bottom | −Z | XY |
|
|
29
28
|
|
|
30
29
|
## GizmoViewcube Face Mapping
|
|
31
30
|
|
|
32
|
-
Three.js BoxGeometry material indices (
|
|
31
|
+
Three.js BoxGeometry material indices vs ForgeCAD labels (Z-up remapping):
|
|
33
32
|
|
|
34
33
|
| Index | Three.js direction | ForgeCAD label |
|
|
35
34
|
|-------|--------------------|----------------|
|
|
@@ -40,13 +39,8 @@ Three.js BoxGeometry material indices (cube face order):
|
|
|
40
39
|
| 4 | +Z | Top |
|
|
41
40
|
| 5 | −Z | Bottom |
|
|
42
41
|
|
|
43
|
-
Default drei labels are `['Right',
|
|
44
|
-
For Z-up we pass `faces={['Right', 'Left', 'Front', 'Back', 'Top', 'Bottom']}`.
|
|
45
|
-
|
|
46
|
-
## Kernel Axis Convention
|
|
47
|
-
|
|
48
|
-
Manifold (the geometry kernel) is Y-up internally. ForgeCAD is Z-up externally. If a kernel-facing operation behaves as if axes are swapped, check whether a Manifold call is still assuming Y-up semantics.
|
|
42
|
+
Default drei labels are Y-up; ForgeCAD passes `faces={['Right','Left','Front','Back','Top','Bottom']}`.
|
|
49
43
|
|
|
50
44
|
## Grid
|
|
51
45
|
|
|
52
|
-
The ground plane is XY (Z = 0).
|
|
46
|
+
The ground plane is XY (Z = 0). Extrusion goes along +Z. Manifold is Y-up internally — if a kernel-facing operation behaves as if axes are swapped, check for Manifold Y-up semantics leaking through.
|
|
@@ -5,105 +5,48 @@ skill-order: 2
|
|
|
5
5
|
|
|
6
6
|
# Geometry Conventions
|
|
7
7
|
|
|
8
|
-
ForgeCAD wraps Manifold (
|
|
9
|
-
|
|
10
|
-
**Core principle: the user script should never need to know about kernel or renderer internals.** If the user writes something geometrically reasonable, it should work. All convention translation happens inside ForgeCAD's layer.
|
|
8
|
+
ForgeCAD wraps Manifold (mesh kernel) and Three.js (Y-up renderer). This doc captures convention mismatches and how ForgeCAD resolves them.
|
|
11
9
|
|
|
12
10
|
## Winding Order
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
**The problem:** Manifold silently produces empty geometry for CW polygons. A user writing `polygon([[0,0], [50,0], [50,30]])` vs `polygon([[0,0], [50,30], [50,0]])` gets either a triangle or nothing, with no error.
|
|
17
|
-
|
|
18
|
-
**ForgeCAD's fix:** All entry points that accept raw points auto-fix winding:
|
|
19
|
-
- `polygon(points)` — computes signed area, reverses if CW
|
|
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
|
|
20
14
|
- `path().close()` — same fix
|
|
21
15
|
|
|
22
|
-
**
|
|
23
|
-
```
|
|
24
|
-
signedArea = Σ (x₂ - x₁)(y₂ + y₁)
|
|
25
|
-
```
|
|
26
|
-
If `signedArea > 0` → CW → reverse to make CCW.
|
|
27
|
-
|
|
28
|
-
**Implementation:** `src/forge/sketch/primitives.ts` (polygon), `src/forge/sketch/path.ts` (close).
|
|
29
|
-
|
|
30
|
-
**Rule for new code:** Any function that takes user-provided point arrays and creates a `CrossSection` MUST auto-fix winding. Never pass raw user points to Manifold without this check.
|
|
16
|
+
**Rule for new code:** Any function accepting user point arrays that creates a `CrossSection` MUST auto-fix winding.
|
|
31
17
|
|
|
32
18
|
## Coordinate System (Z-up vs Y-up)
|
|
33
19
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
**ForgeCAD's fix:** We set `camera.up = (0, 0, 1)` everywhere. Geometry coordinates are native Z-up — no matrix swizzling. The camera orientation handles the visual mapping.
|
|
37
|
-
|
|
38
|
-
**Where this matters:**
|
|
39
|
-
- `camera.up.set(0, 0, 1)` in `sceneBuilder.ts` and `render.ts`
|
|
40
|
-
- GizmoViewcube face labels remapped (see coordinate-system.md)
|
|
41
|
-
- Grid plane is XY (Z=0)
|
|
42
|
-
- Extrusion goes along +Z
|
|
43
|
-
- Revolution axis is Y (sketch plane), result maps to Z-up space
|
|
44
|
-
|
|
45
|
-
**Rule for new code:** Never swap Y/Z in geometry. Always fix it at the camera/renderer level.
|
|
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.
|
|
46
21
|
|
|
47
22
|
## Revolution Axis
|
|
48
23
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
**The mapping:**
|
|
52
|
-
- Profile X coordinate → radial distance from center
|
|
53
|
-
- Profile Y coordinate → height (becomes Z after revolution)
|
|
54
|
-
- Profile must be on the positive X side (X > 0) for valid geometry
|
|
55
|
-
|
|
56
|
-
**Rule for new code:** Document which axis any new sweep/revolution operation uses. If it differs from user expectation, add a transform wrapper.
|
|
24
|
+
`CrossSection.revolve()` revolves around Y. Profile X = radial distance, Profile Y = height (becomes Z after revolution). Profile must be at X > 0.
|
|
57
25
|
|
|
58
26
|
## Boolean Winding (3D)
|
|
59
27
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
**ForgeCAD's fix:** We only create meshes through Manifold's own constructors (`extrude`, `revolve`, `cylinder`, `sphere`, etc.), which guarantee correct normals. No raw mesh import path exists yet.
|
|
63
|
-
|
|
64
|
-
**Rule for new code:** If adding mesh import (STL, OBJ), run `Manifold.asOriginal()` or validate manifoldness before allowing booleans.
|
|
28
|
+
Manifold requires consistent outward face normals. ForgeCAD only creates meshes through Manifold's own constructors, which guarantee correct normals.
|
|
65
29
|
|
|
66
30
|
## Transform Order
|
|
67
31
|
|
|
68
|
-
|
|
32
|
+
Transforms apply left-to-right. `rotate()`, `scale()`, `mirror()` operate around bounding-box center. Use `rotateAround([0,0,0], ...)` to orbit around world origin.
|
|
69
33
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
For explicit transform objects:
|
|
73
|
-
- `A.mul(B)` means **apply A, then B**.
|
|
74
|
-
- `composeChain(A, B, C)` means **A -> B -> C**.
|
|
75
|
-
|
|
76
|
-
**Rule for new code:** Keep this chain order everywhere. Document any operation that deviates.
|
|
34
|
+
For explicit transform objects: `A.mul(B)` = apply A then B; `composeChain(A, B, C)` = A→B→C.
|
|
77
35
|
|
|
78
36
|
## Assembly Frame Composition
|
|
79
37
|
|
|
80
|
-
This is where regressions are most likely if convention is unclear.
|
|
81
|
-
|
|
82
|
-
For a point in child geometry-local coordinates:
|
|
83
|
-
- local -> `childBase` -> `jointMotion(value)` -> `jointFrame` -> `parentWorld`
|
|
84
|
-
|
|
85
|
-
In Forge chain notation:
|
|
86
38
|
```ts
|
|
87
39
|
childWorld = composeChain(childBase, jointMotion, jointFrame, parentWorld)
|
|
88
40
|
```
|
|
89
41
|
|
|
90
|
-
|
|
91
|
-
```txt
|
|
92
|
-
T_world_child = T_parent_world * T_joint_frame * T_joint_motion * T_child_base
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
**Rule for new code:** In kinematics/assembly code, prefer `composeChain(...)` over manual `.mul(...).mul(...)` sequences to avoid order mistakes.
|
|
42
|
+
Prefer `composeChain(...)` over manual `.mul(...).mul(...)` in kinematics code to avoid order mistakes.
|
|
96
43
|
|
|
97
|
-
## Summary
|
|
98
|
-
|
|
99
|
-
These are the places where ForgeCAD translates between "what the user means" and "what the kernel needs":
|
|
44
|
+
## Summary
|
|
100
45
|
|
|
101
46
|
| Convention | User sees | Kernel needs | Where we fix it |
|
|
102
47
|
|---|---|---|---|
|
|
103
48
|
| Winding | Any point order | CCW | `polygon()`, `path().close()` |
|
|
104
49
|
| Up axis | Z-up | Y-up (Three.js) | `camera.up`, gizmo labels |
|
|
105
|
-
| Revolution | "revolve this profile" | Profile in X-Y, X>0 | Documented
|
|
50
|
+
| Revolution | "revolve this profile" | Profile in X-Y, X>0 | Documented only |
|
|
106
51
|
| Face normals | Doesn't think about it | Outward-pointing | Manifold constructors |
|
|
107
|
-
| Transform order | Left-to-right chain | Post-multiply | Native match
|
|
108
|
-
|
|
109
|
-
When adding new geometry operations, check this table. If the operation introduces a new convention mismatch between user intent and kernel requirement, either auto-fix it (preferred) or document it clearly in the API docs.
|
|
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.
|
|
@@ -5,246 +5,73 @@ skill-order: 1
|
|
|
5
5
|
|
|
6
6
|
# Modeling Recipes
|
|
7
7
|
|
|
8
|
-
This file collects patterns, best practices, debugging tips, and example snippets that are useful once you already know the model-building API.
|
|
9
|
-
|
|
10
8
|
## Iteration Bias
|
|
11
9
|
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
- Keep printed hardware structurally honest: use it for guides, spacers, retainers, and moderate-load mechanisms; use wood or metal for primary strength.
|
|
16
|
-
- Validate early with `forgecad run <file>` and refine from the actual runtime result.
|
|
17
|
-
- Prefer a few clean part files over one giant script once a design has repeated hardware or a small mechanism.
|
|
18
|
-
|
|
19
|
-
Notebook helpers worth using during iteration:
|
|
20
|
-
|
|
21
|
-
- `show(...)` pins the current intermediate geometry in the viewport
|
|
22
|
-
- `forgecad notebook view <file> preview` prints the preview cell with stored outputs in the terminal
|
|
23
|
-
- `forgecad run <file>.forge-notebook.json` validates the preview cell and runs the usual spatial analysis
|
|
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>`.
|
|
24
13
|
|
|
25
14
|
## Common Patterns
|
|
26
15
|
|
|
27
|
-
###
|
|
28
|
-
```javascript
|
|
29
|
-
const w = param("Width", 80, { min: 40, max: 150, unit: "mm" });
|
|
30
|
-
const h = param("Height", 60, { min: 30, max: 100, unit: "mm" });
|
|
31
|
-
const t = param("Thickness", 5, { min: 2, max: 10, unit: "mm" });
|
|
32
|
-
const holeD = param("Hole Diameter", 8, { min: 4, max: 20, unit: "mm" });
|
|
33
|
-
|
|
34
|
-
const base = box(w, h, t);
|
|
35
|
-
const hole = cylinder(t + 2, holeD / 2).translate(w / 2, h / 2, -1);
|
|
36
|
-
|
|
37
|
-
return base.subtract(hole);
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
### Hollow Shell (Wall Thickness)
|
|
16
|
+
### Hollow Shell
|
|
41
17
|
```javascript
|
|
42
|
-
const outer = param("Outer Size", 50, { min: 20, max: 100, unit: "mm" });
|
|
43
|
-
const wall = param("Wall", 3, { min: 1, max: 10, unit: "mm" });
|
|
44
|
-
|
|
45
18
|
const outerBox = box(outer, outer, outer, true);
|
|
46
19
|
const innerBox = box(outer - 2 * wall, outer - 2 * wall, outer - 2 * wall, true);
|
|
47
|
-
|
|
48
20
|
return outerBox.subtract(innerBox);
|
|
49
21
|
```
|
|
50
22
|
|
|
51
|
-
###
|
|
52
|
-
```javascript
|
|
53
|
-
const count = param("Count", 5, { min: 2, max: 10 });
|
|
54
|
-
const spacing = param("Spacing", 15, { min: 5, max: 30, unit: "mm" });
|
|
55
|
-
|
|
56
|
-
let shapes = [];
|
|
57
|
-
for (let i = 0; i < count; i++) {
|
|
58
|
-
shapes.push(cylinder(10, 5).translate(i * spacing, 0, 0));
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return union(...shapes);
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
### Sketch-Based Design
|
|
23
|
+
### Sketch-Based Twist
|
|
65
24
|
```javascript
|
|
66
|
-
const sides = param("Sides", 6, { min: 3, max: 12 });
|
|
67
|
-
const radius = param("Radius", 25, { min: 10, max: 50, unit: "mm" });
|
|
68
|
-
const height = param("Height", 60, { min: 20, max: 120, unit: "mm" });
|
|
69
|
-
const wall = param("Wall", 3, { min: 1, max: 8, unit: "mm" });
|
|
70
|
-
|
|
71
25
|
const outer = ngon(sides, radius);
|
|
72
26
|
const inner = ngon(sides, radius - wall);
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
return profile.extrude(height, { twist: 45, divisions: 32 });
|
|
27
|
+
return outer.subtract(inner).extrude(height, { twist: 45, divisions: 32 });
|
|
76
28
|
```
|
|
77
29
|
|
|
78
30
|
### Rounded Profiles
|
|
79
31
|
```javascript
|
|
32
|
+
// All convex corners — offset trick
|
|
80
33
|
const base = rect(50, 30).offset(-3, 'Round').offset(3, 'Round');
|
|
81
|
-
return base.extrude(10);
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
Use that pattern when every convex corner should round. For mixed sharp-and-rounded outlines, fillet only the intended vertices instead:
|
|
85
|
-
|
|
86
|
-
```javascript
|
|
87
|
-
const roofPoints = [
|
|
88
|
-
[0, 0],
|
|
89
|
-
[90, 0],
|
|
90
|
-
[90, 44],
|
|
91
|
-
[66, 74],
|
|
92
|
-
[45, 86],
|
|
93
|
-
[24, 74],
|
|
94
|
-
[0, 44],
|
|
95
|
-
];
|
|
96
34
|
|
|
35
|
+
// Selected corners only
|
|
97
36
|
const roof = filletCorners(roofPoints, [
|
|
98
37
|
{ index: 3, radius: 19 },
|
|
99
38
|
{ index: 4, radius: 19 },
|
|
100
39
|
{ index: 5, radius: 19 },
|
|
101
40
|
]);
|
|
102
|
-
|
|
103
|
-
return roof.extrude(12);
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
### Chamfers and Fillets
|
|
107
|
-
```javascript
|
|
108
|
-
const part = box(50, 50, 20);
|
|
109
|
-
const chamfer = box(10, 60, 10)
|
|
110
|
-
.rotate(0, 45, 0)
|
|
111
|
-
.translate(50, -5, 15);
|
|
112
|
-
|
|
113
|
-
return part.subtract(chamfer);
|
|
114
41
|
```
|
|
115
42
|
|
|
116
43
|
### Choosing the right sketch-rounding tool
|
|
117
44
|
|
|
118
|
-
- `offset(-r).offset(+r)`
|
|
119
|
-
- `stroke(points, width, 'Round')`
|
|
120
|
-
- `filletCorners(points, ...)`
|
|
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
|
|
121
48
|
|
|
122
49
|
## Best Practices
|
|
123
50
|
|
|
124
|
-
|
|
125
|
-
-
|
|
126
|
-
-
|
|
127
|
-
-
|
|
128
|
-
|
|
129
|
-
### Readability
|
|
130
|
-
```javascript
|
|
131
|
-
const base = box(100, 100, 10);
|
|
132
|
-
const hole = cylinder(12, 8);
|
|
133
|
-
const result = base.subtract(hole.translate(50, 50, 0));
|
|
134
|
-
return result;
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
Prefer named intermediate values over deeply nested one-liners.
|
|
138
|
-
|
|
139
|
-
### Units
|
|
140
|
-
- All dimensions are millimeters by default
|
|
141
|
-
- Angles are degrees
|
|
142
|
-
- Use the `unit` parameter option when it helps the reader
|
|
143
|
-
|
|
144
|
-
### Centering
|
|
145
|
-
```javascript
|
|
146
|
-
const centered = box(50, 50, 50, true).translate(x, y, z);
|
|
147
|
-
const corner = box(50, 50, 50).translate(x - 25, y - 25, z - 25);
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
Centered primitives are usually easier to position.
|
|
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()`.
|
|
151
55
|
|
|
152
56
|
## Debugging
|
|
153
57
|
|
|
154
|
-
### Console Output
|
|
155
58
|
```javascript
|
|
156
|
-
console.log("Width:", width);
|
|
157
59
|
console.log("Volume:", shape.volume());
|
|
158
60
|
```
|
|
159
61
|
|
|
160
|
-
|
|
161
|
-
```javascript
|
|
162
|
-
const base = box(50, 50, 10);
|
|
163
|
-
// return base;
|
|
164
|
-
|
|
165
|
-
const withHole = base.subtract(cylinder(12, 5).translate(25, 25, 0));
|
|
166
|
-
// return withHole;
|
|
167
|
-
|
|
168
|
-
return withHole.add(cylinder(20, 3).translate(25, 25, 10));
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
For sketch-heavy work, compare the raw profile and the rounded profile before extruding:
|
|
62
|
+
For sketch-heavy work, compare the raw profile and rounded profile side-by-side before extruding:
|
|
172
63
|
|
|
173
64
|
```javascript
|
|
174
|
-
const raw = polygon(roofPoints);
|
|
175
|
-
const rounded = filletCorners(roofPoints, [
|
|
176
|
-
{ index: 3, radius: 19 },
|
|
177
|
-
{ index: 4, radius: 19 },
|
|
178
|
-
{ index: 5, radius: 19 },
|
|
179
|
-
]);
|
|
180
|
-
|
|
181
65
|
return [
|
|
182
|
-
{ name: "Raw", sketch:
|
|
183
|
-
{ name: "Rounded", sketch:
|
|
66
|
+
{ name: "Raw", sketch: polygon(roofPoints) },
|
|
67
|
+
{ name: "Rounded", sketch: filletCorners(roofPoints, [...]).translate(120, 0) },
|
|
184
68
|
];
|
|
185
69
|
```
|
|
186
70
|
|
|
187
|
-
##
|
|
188
|
-
|
|
189
|
-
Common errors:
|
|
190
|
-
- `"Kernel not initialized"` - internal/runtime issue, reload the app
|
|
191
|
-
- `"Cannot read property of undefined"` - usually a bad variable name or missing declaration
|
|
192
|
-
- invalid geometry - commonly caused by zero dimensions or self-intersecting sketches
|
|
193
|
-
- script execution error - inspect the JS error in console output
|
|
194
|
-
|
|
195
|
-
## Example Snippets
|
|
196
|
-
|
|
197
|
-
### Parametric Phone Stand
|
|
198
|
-
```javascript
|
|
199
|
-
const width = param("Width", 80, { min: 40, max: 150, unit: "mm" });
|
|
200
|
-
const depth = param("Depth", 60, { min: 30, max: 100, unit: "mm" });
|
|
201
|
-
const thick = param("Thickness", 5, { min: 2, max: 15, unit: "mm" });
|
|
202
|
-
const backH = param("Back Height", 40, { min: 20, max: 80, unit: "mm" });
|
|
203
|
-
const cableD = param("Cable Hole", 8, { min: 4, max: 15, unit: "mm" });
|
|
204
|
-
|
|
205
|
-
const base = box(width, depth, thick);
|
|
206
|
-
const back = box(width, thick, backH).translate(0, depth - thick, thick);
|
|
207
|
-
const lip = box(width, 10, 8).translate(0, 0, thick);
|
|
208
|
-
const hole = cylinder(thick + 2, cableD / 2)
|
|
209
|
-
.rotate(90, 0, 0)
|
|
210
|
-
.translate(width / 2, depth / 2, -1);
|
|
211
|
-
|
|
212
|
-
return union(base, back, lip).subtract(hole);
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
### Multi-Object Scene with Colors
|
|
216
|
-
```javascript
|
|
217
|
-
const base = box(100, 100, 5).color('#888888');
|
|
218
|
-
const col1 = cylinder(40, 5).translate(20, 20, 5).color('#cc4444');
|
|
219
|
-
const col2 = cylinder(40, 5).translate(80, 20, 5).color('#4444cc');
|
|
220
|
-
const col3 = cylinder(40, 5).translate(50, 80, 5).color('#44cc44');
|
|
221
|
-
const top = box(100, 100, 3).translate(0, 0, 45).color('#888888');
|
|
222
|
-
|
|
223
|
-
return [
|
|
224
|
-
{ name: "Base", shape: base },
|
|
225
|
-
{ name: "Column A", shape: col1 },
|
|
226
|
-
{ name: "Column B", shape: col2 },
|
|
227
|
-
{ name: "Column C", shape: col3 },
|
|
228
|
-
{ name: "Top", shape: top },
|
|
229
|
-
];
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
### Entity-Based Design with Topology
|
|
233
|
-
```javascript
|
|
234
|
-
const baseRect = rectangle(0, 0, 80, 60);
|
|
235
|
-
const base = baseRect.extrude(20);
|
|
236
|
-
|
|
237
|
-
const result = filletEdge(base.toShape(), base.edge('vert-br'), 8, [-1, -1])
|
|
238
|
-
.hole(base.face('top'), { diameter: 6, u: -16, v: 10, depth: 8 });
|
|
239
|
-
|
|
240
|
-
const holes = circularPattern(
|
|
241
|
-
cylinder(25, 4).translate(40, 30, -1),
|
|
242
|
-
4, 40, 30,
|
|
243
|
-
);
|
|
244
|
-
|
|
245
|
-
return result.subtract(holes);
|
|
246
|
-
```
|
|
71
|
+
## Common Errors
|
|
247
72
|
|
|
248
|
-
|
|
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"`
|
|
249
76
|
|
|
250
77
|
For larger runnable examples, read `examples/api/`.
|