forgecad 0.1.1 → 0.1.3
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/dist/assets/{evalWorker-DuS4V-H5.js → evalWorker-1m873KWd.js} +107 -107
- package/dist/assets/{index-d60dJDhX.js → index-Dvz3nSDc.js} +342 -326
- package/dist/assets/{manifold-DEOPQqeC.js → manifold-C38sUiKu.js} +1 -1
- package/dist/assets/{manifold-cL7A7HeM.js → manifold-Dk2u-lhj.js} +1 -1
- package/dist/assets/{manifold-DbvY5u9x.js → manifold-rOWQW9fU.js} +1 -1
- package/dist/assets/{reportWorker-GZVKtf9S.js → reportWorker-Cj587shw.js} +129 -129
- package/dist/index.html +1 -1
- package/dist-cli/forgecad.js +204 -19
- package/dist-skill/SKILL.md +31 -4561
- package/dist-skill/docs/API/README.md +24 -0
- package/dist-skill/docs/API/guides/modeling-recipes.md +246 -0
- package/dist-skill/docs/API/internals/compiler.md +300 -0
- package/dist-skill/docs/API/internals/manifold.md +7 -0
- package/dist-skill/docs/API/model-building/README.md +31 -0
- package/dist-skill/docs/API/model-building/assembly.md +205 -0
- package/dist-skill/docs/API/model-building/coordinate-system.md +43 -0
- package/dist-skill/docs/API/model-building/entities.md +282 -0
- package/dist-skill/docs/API/model-building/geometry-conventions.md +104 -0
- package/dist-skill/docs/API/model-building/positioning.md +170 -0
- package/dist-skill/docs/API/model-building/reference.md +1936 -0
- package/dist-skill/docs/API/model-building/sheet-metal.md +180 -0
- package/dist-skill/docs/API/model-building/sketch-anchor.md +32 -0
- package/dist-skill/docs/API/model-building/sketch-booleans.md +101 -0
- package/dist-skill/docs/API/model-building/sketch-core.md +68 -0
- package/dist-skill/docs/API/model-building/sketch-extrude.md +57 -0
- package/dist-skill/docs/API/model-building/sketch-on-face.md +98 -0
- package/dist-skill/docs/API/model-building/sketch-operations.md +92 -0
- package/dist-skill/docs/API/model-building/sketch-path.md +70 -0
- package/dist-skill/docs/API/model-building/sketch-primitives.md +104 -0
- package/dist-skill/docs/API/model-building/sketch-transforms.md +60 -0
- package/dist-skill/docs/API/output/bom.md +53 -0
- package/dist-skill/docs/API/output/brep-export.md +82 -0
- package/dist-skill/docs/API/output/dimensions.md +62 -0
- package/dist-skill/docs/API/runtime/viewport.md +229 -0
- package/dist-skill/docs/CLI.md +672 -0
- package/dist-skill/docs/CODING.md +345 -0
- package/dist-skill/docs/PROGRAM-LEAD.md +180 -0
- package/dist-skill/docs/VISION.md +77 -0
- package/examples/api/import-group-assembly.forge.js +34 -0
- package/examples/api/import-group-source.forge.js +35 -0
- package/package.json +1 -1
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# Sheet Metal
|
|
2
|
+
|
|
3
|
+
`sheetMetal()` is ForgeCAD's first dedicated compiler-owned sheet-metal family.
|
|
4
|
+
|
|
5
|
+
It keeps one semantic model, then derives both:
|
|
6
|
+
|
|
7
|
+
- a folded solid
|
|
8
|
+
- a flat pattern
|
|
9
|
+
|
|
10
|
+
This is a strict v1 subset. Forge does not try to infer sheet metal from arbitrary solids.
|
|
11
|
+
|
|
12
|
+
## Quick Start
|
|
13
|
+
|
|
14
|
+
```javascript
|
|
15
|
+
const cover = sheetMetal({
|
|
16
|
+
panel: { width: 180, height: 110 },
|
|
17
|
+
thickness: 1.5,
|
|
18
|
+
bendRadius: 2,
|
|
19
|
+
bendAllowance: { kFactor: 0.42 },
|
|
20
|
+
cornerRelief: { size: 4 },
|
|
21
|
+
})
|
|
22
|
+
.flange('top', { length: 18 })
|
|
23
|
+
.flange('right', { length: 18 })
|
|
24
|
+
.flange('bottom', { length: 18 })
|
|
25
|
+
.flange('left', { length: 18 })
|
|
26
|
+
.cutout('panel', rect(72, 36, true), { selfAnchor: 'center' })
|
|
27
|
+
.cutout('flange-right', roundedRect(26, 10, 5, true), { selfAnchor: 'center' });
|
|
28
|
+
|
|
29
|
+
const folded = cover.folded();
|
|
30
|
+
const flat = cover.flatPattern();
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The maintained proof artifact is [`examples/api/folded-service-panel-cover.forge.js`](../../../../examples/api/folded-service-panel-cover.forge.js).
|
|
34
|
+
|
|
35
|
+
## Practical First Pass
|
|
36
|
+
|
|
37
|
+
If the goal is "open docs and make a cover/bracket quickly," the most reliable current workflow is:
|
|
38
|
+
|
|
39
|
+
1. Build the base panel plus flanges first.
|
|
40
|
+
2. Validate `folded()` and `flatPattern()` before adding any cutouts.
|
|
41
|
+
3. Add panel cutouts before flange cutouts.
|
|
42
|
+
4. Add flange cutouts one region at a time instead of decorating every flange at once.
|
|
43
|
+
5. Re-run validation after each new region so cutout complexity stays easy to localize.
|
|
44
|
+
|
|
45
|
+
This keeps failures narrow and makes it obvious whether the issue is:
|
|
46
|
+
|
|
47
|
+
- the sheet-metal setup itself
|
|
48
|
+
- one specific region
|
|
49
|
+
- one specific cutout pattern
|
|
50
|
+
|
|
51
|
+
## Current Practical Cutout Lane
|
|
52
|
+
|
|
53
|
+
The defended API surface is wider than the current "fast to author" lane.
|
|
54
|
+
|
|
55
|
+
For quick, low-drama iteration, prefer:
|
|
56
|
+
|
|
57
|
+
- `circle2d(...)`
|
|
58
|
+
- `rect(...)`
|
|
59
|
+
- `roundedRect(...)`
|
|
60
|
+
|
|
61
|
+
These simple compile-covered sketches are the current safest first choice for panel and flange cutouts.
|
|
62
|
+
|
|
63
|
+
Decorative language still works best when expressed as several individually placed simple cutouts rather than one heavily composed art sketch. In practice, fewer larger apertures are more reliable than many tiny details when you are still exploring the part.
|
|
64
|
+
|
|
65
|
+
If runtime spikes or validation stops feeling immediate, simplify first by:
|
|
66
|
+
|
|
67
|
+
- reducing cutout count
|
|
68
|
+
- replacing composed motifs with simple repeated cutouts
|
|
69
|
+
- validating one region before mirroring the same detail to other flanges
|
|
70
|
+
|
|
71
|
+
## Placement Tips
|
|
72
|
+
|
|
73
|
+
- `selfAnchor: 'center'` is the easiest default for early iterations because it keeps panel and flange placement intent readable.
|
|
74
|
+
- Flange-local placement is easy to misread, especially on side flanges. Place one obvious asymmetric test cutout on one flange first, validate it, then mirror or repeat once the orientation is confirmed.
|
|
75
|
+
- Keep cutouts off bend regions for v1. The defended cutout lane is planar panel and flange regions only.
|
|
76
|
+
|
|
77
|
+
## API Surface
|
|
78
|
+
|
|
79
|
+
### `sheetMetal(options)`
|
|
80
|
+
|
|
81
|
+
Creates a `SheetMetalPart`.
|
|
82
|
+
|
|
83
|
+
Required options:
|
|
84
|
+
|
|
85
|
+
- `panel: { width, height }`
|
|
86
|
+
- `thickness`
|
|
87
|
+
- `bendRadius`
|
|
88
|
+
- `bendAllowance: { kFactor }`
|
|
89
|
+
|
|
90
|
+
Optional:
|
|
91
|
+
|
|
92
|
+
- `cornerRelief: { kind?: 'rect', size }`
|
|
93
|
+
|
|
94
|
+
### `part.flange(edge, options)`
|
|
95
|
+
|
|
96
|
+
Adds one edge flange.
|
|
97
|
+
|
|
98
|
+
- `edge`: `'top' | 'right' | 'bottom' | 'left'`
|
|
99
|
+
- `options.length`
|
|
100
|
+
- `options.angleDeg` defaults to `90`
|
|
101
|
+
|
|
102
|
+
Current v1 support is only `90°` flanges.
|
|
103
|
+
|
|
104
|
+
### `part.cutout(region, sketch, options?)`
|
|
105
|
+
|
|
106
|
+
Adds a planar cutout on a supported sheet-metal region.
|
|
107
|
+
|
|
108
|
+
- `region`: `'panel' | 'flange-top' | 'flange-right' | 'flange-bottom' | 'flange-left'`
|
|
109
|
+
- `sketch` must be an unplaced compile-covered 2D sketch
|
|
110
|
+
- `options.u` / `options.v` place the sketch in region-local coordinates
|
|
111
|
+
- `options.selfAnchor` works like other planar placement APIs
|
|
112
|
+
|
|
113
|
+
For exploratory authoring, prefer a loop like:
|
|
114
|
+
|
|
115
|
+
```javascript
|
|
116
|
+
let part = sheetMetal({
|
|
117
|
+
panel: { width: 180, height: 110 },
|
|
118
|
+
thickness: 1.5,
|
|
119
|
+
bendRadius: 2,
|
|
120
|
+
bendAllowance: { kFactor: 0.42 },
|
|
121
|
+
cornerRelief: { size: 4 },
|
|
122
|
+
})
|
|
123
|
+
.flange('top', { length: 18 })
|
|
124
|
+
.flange('right', { length: 18 });
|
|
125
|
+
|
|
126
|
+
part = part.cutout('panel', roundedRect(40, 10, 5, true), { selfAnchor: 'center' });
|
|
127
|
+
part = part.cutout('flange-right', circle2d(3), { u: 0, v: 0, selfAnchor: 'center' });
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
That authoring style keeps each downstream cutout step explicit and easy to debug.
|
|
131
|
+
|
|
132
|
+
### `part.regionNames()`
|
|
133
|
+
|
|
134
|
+
Returns the semantic region names currently available from the model.
|
|
135
|
+
|
|
136
|
+
### `part.folded()` / `part.flatPattern()`
|
|
137
|
+
|
|
138
|
+
Materialize the folded solid or flat pattern from the same semantic model.
|
|
139
|
+
|
|
140
|
+
Both outputs stay compiler-owned and exact-exportable inside the defended subset.
|
|
141
|
+
|
|
142
|
+
## Defended Region Names
|
|
143
|
+
|
|
144
|
+
Forge exposes the following semantic family where the corresponding flange exists:
|
|
145
|
+
|
|
146
|
+
- `panel`
|
|
147
|
+
- `flange-top`, `flange-right`, `flange-bottom`, `flange-left`
|
|
148
|
+
- `bend-top`, `bend-right`, `bend-bottom`, `bend-left`
|
|
149
|
+
|
|
150
|
+
Important behavior:
|
|
151
|
+
|
|
152
|
+
- planar panel/flange faces can resolve as descendant `region`s after downstream cutouts
|
|
153
|
+
- folded bend regions resolve explicitly as descendant `set`s because one bend can honestly span multiple surfaces
|
|
154
|
+
- flat-pattern bend regions stay explicit too, but as planar band descendants instead of guessed folded topology
|
|
155
|
+
|
|
156
|
+
## Supported V1 Subset
|
|
157
|
+
|
|
158
|
+
- one base panel
|
|
159
|
+
- up to four edge flanges
|
|
160
|
+
- constant thickness
|
|
161
|
+
- explicit bend radius
|
|
162
|
+
- explicit K-factor bend allowance input
|
|
163
|
+
- rectangular corner reliefs
|
|
164
|
+
- planar cutouts on the panel and existing flange regions
|
|
165
|
+
- folded preview and exact folded/flat lowering from the same semantic model
|
|
166
|
+
|
|
167
|
+
## Explicit Non-Goals
|
|
168
|
+
|
|
169
|
+
Not supported in v1:
|
|
170
|
+
|
|
171
|
+
- arbitrary solid-to-sheet-metal conversion
|
|
172
|
+
- hems
|
|
173
|
+
- jogs or offset bends
|
|
174
|
+
- lofted bends
|
|
175
|
+
- miter corner logic beyond the defended rectangular-relief subset
|
|
176
|
+
- nonuniform thickness
|
|
177
|
+
- bend-region cutouts
|
|
178
|
+
- non-`90°` flanges
|
|
179
|
+
|
|
180
|
+
If Forge cannot defend a requested operation inside that subset, it should fail with a targeted error instead of guessing.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Sketch Anchor Positioning
|
|
2
|
+
|
|
3
|
+
Position sketches relative to each other using named anchor points.
|
|
4
|
+
|
|
5
|
+
## Methods
|
|
6
|
+
|
|
7
|
+
### `.attachTo(target, targetAnchor, selfAnchor?, offset?)`
|
|
8
|
+
Position a sketch relative to another using named anchor points.
|
|
9
|
+
|
|
10
|
+
**Parameters:**
|
|
11
|
+
- `target` (Sketch) — The sketch to attach to
|
|
12
|
+
- `targetAnchor` (Anchor) — Point on target: 'center', 'top-left', 'top-right', 'bottom-left', 'bottom-right', 'top', 'bottom', 'left', 'right'
|
|
13
|
+
- `selfAnchor` (Anchor, optional) — Point on this sketch to align. Default: 'center'
|
|
14
|
+
- `offset` ([number, number], optional) — Additional offset after alignment
|
|
15
|
+
|
|
16
|
+
**Returns:** `Sketch`
|
|
17
|
+
|
|
18
|
+
```javascript
|
|
19
|
+
const plate = rect(50, 4);
|
|
20
|
+
const arm = rect(4, 70).attachTo(plate, 'bottom-left', 'top-left');
|
|
21
|
+
return union2d(plate, arm);
|
|
22
|
+
|
|
23
|
+
// With offset: attach then shift 5mm right
|
|
24
|
+
const shifted = rect(4, 70).attachTo(plate, 'bottom-left', 'top-left', [5, 0]);
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Anchor Points
|
|
28
|
+
|
|
29
|
+
Available anchor positions:
|
|
30
|
+
- `'center'` — geometric center
|
|
31
|
+
- `'top-left'`, `'top-right'`, `'bottom-left'`, `'bottom-right'` — corners
|
|
32
|
+
- `'top'`, `'bottom'`, `'left'`, `'right'` — edge midpoints
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Sketch Booleans
|
|
2
|
+
|
|
3
|
+
2D boolean operations for combining, subtracting, and intersecting sketches.
|
|
4
|
+
|
|
5
|
+
## Methods
|
|
6
|
+
|
|
7
|
+
### `.add(...others)`
|
|
8
|
+
Combines sketches (union). Accepts `sketch.add(a, b)` and `sketch.add([a, b])`.
|
|
9
|
+
|
|
10
|
+
```javascript
|
|
11
|
+
const combined = rect(50, 30).add(
|
|
12
|
+
circle2d(20).translate(25, 15),
|
|
13
|
+
ngon(6, 15).translate(40, 15)
|
|
14
|
+
);
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### `.subtract(...others)`
|
|
18
|
+
Subtracts one or more sketches from this one. Accepts `sketch.subtract(a, b)` and `sketch.subtract([a, b])`.
|
|
19
|
+
|
|
20
|
+
```javascript
|
|
21
|
+
const plate = rect(100, 80);
|
|
22
|
+
const hole = circle2d(10);
|
|
23
|
+
const slotCut = rect(18, 8).translate(41, 36);
|
|
24
|
+
const result = plate.subtract(hole.translate(25, 40), slotCut);
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### `.intersect(...others)`
|
|
28
|
+
Keeps only the area shared by every operand. Accepts `sketch.intersect(a, b)` and `sketch.intersect([a, b])`.
|
|
29
|
+
|
|
30
|
+
```javascript
|
|
31
|
+
const overlap = rect(50, 50).intersect(
|
|
32
|
+
circle2d(30).translate(25, 25),
|
|
33
|
+
rect(40, 20).translate(5, 15)
|
|
34
|
+
);
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Functions
|
|
38
|
+
|
|
39
|
+
### `union2d(...sketches)`
|
|
40
|
+
Combines multiple sketches into one.
|
|
41
|
+
|
|
42
|
+
```javascript
|
|
43
|
+
const combined = union2d(
|
|
44
|
+
rect(50, 30),
|
|
45
|
+
circle2d(20).translate(25, 15),
|
|
46
|
+
ngon(6, 15).translate(75, 15)
|
|
47
|
+
);
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`union2d([a, b, c])` is also supported when your sketches are already in an array.
|
|
51
|
+
|
|
52
|
+
### `difference2d(...sketches)`
|
|
53
|
+
Subtracts sketches[1..n] from sketches[0].
|
|
54
|
+
|
|
55
|
+
```javascript
|
|
56
|
+
const plate = rect(100, 80);
|
|
57
|
+
const hole1 = circle2d(10).translate(25, 40);
|
|
58
|
+
const hole2 = circle2d(10).translate(75, 40);
|
|
59
|
+
const result = difference2d(plate, hole1, hole2);
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`difference2d([base, cutter1, cutter2])` works too.
|
|
63
|
+
|
|
64
|
+
### `intersection2d(...sketches)`
|
|
65
|
+
Keeps only the area where all sketches overlap.
|
|
66
|
+
|
|
67
|
+
```javascript
|
|
68
|
+
const overlap = intersection2d(
|
|
69
|
+
rect(50, 50),
|
|
70
|
+
circle2d(30).translate(25, 25)
|
|
71
|
+
);
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
`intersection2d([a, b, c])` is also supported.
|
|
75
|
+
|
|
76
|
+
### `hull2d(...sketches)`
|
|
77
|
+
Creates the convex hull of multiple sketches.
|
|
78
|
+
|
|
79
|
+
```javascript
|
|
80
|
+
const hull = hull2d(
|
|
81
|
+
circle2d(10),
|
|
82
|
+
circle2d(10).translate(50, 0),
|
|
83
|
+
circle2d(10).translate(25, 40)
|
|
84
|
+
);
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`hull2d([a, b, c])` is also supported when your sketches are already in an array.
|
|
88
|
+
|
|
89
|
+
`hull2d()` is best for intentionally blended convex silhouettes. If you need true corner fillets while keeping some neighboring corners sharp, use `filletCorners(...)` instead.
|
|
90
|
+
|
|
91
|
+
## Performance Note
|
|
92
|
+
|
|
93
|
+
The multi-argument functions (`union2d`, `difference2d`, `intersection2d`) use Manifold's batch operations internally, which are faster than chaining `.add()` / `.subtract()` calls one by one. Prefer them when combining many sketches.
|
|
94
|
+
|
|
95
|
+
```javascript
|
|
96
|
+
// Fast — single batch operation
|
|
97
|
+
const combined = union2d(s1, s2, s3, s4, s5);
|
|
98
|
+
|
|
99
|
+
// Slower — sequential pairwise operations
|
|
100
|
+
const combined = s1.add(s2).add(s3).add(s4).add(s5);
|
|
101
|
+
```
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Sketch Core
|
|
2
|
+
|
|
3
|
+
The `Sketch` class is an immutable wrapper around Manifold's `CrossSection` that provides a chainable 2D API.
|
|
4
|
+
|
|
5
|
+
## Class: Sketch
|
|
6
|
+
|
|
7
|
+
Represents a 2D profile that can be transformed, combined with other sketches, or converted to 3D.
|
|
8
|
+
|
|
9
|
+
### Color
|
|
10
|
+
|
|
11
|
+
#### `.color(hex: string): Sketch`
|
|
12
|
+
Set the display color of this sketch. Returns a new Sketch.
|
|
13
|
+
|
|
14
|
+
```javascript
|
|
15
|
+
const red = rect(50, 30).color('#ff0000');
|
|
16
|
+
const blue = circle2d(25).color('#0066ff');
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Colors are preserved through transforms and boolean operations.
|
|
20
|
+
|
|
21
|
+
#### `.clone()` / `.duplicate()`
|
|
22
|
+
Create an explicit duplicate of a sketch wrapper.
|
|
23
|
+
|
|
24
|
+
```javascript
|
|
25
|
+
const base = rect(50, 30);
|
|
26
|
+
const a = base.clone();
|
|
27
|
+
const b = base.duplicate().translate(60, 0);
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Query Methods
|
|
31
|
+
|
|
32
|
+
#### `.area(): number`
|
|
33
|
+
Returns the area of the sketch.
|
|
34
|
+
|
|
35
|
+
```javascript
|
|
36
|
+
const sq = rect(50, 50);
|
|
37
|
+
console.log(sq.area()); // 2500
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
#### `.bounds()`
|
|
41
|
+
Returns the bounding box: `{ min: [x, y], max: [x, y] }`.
|
|
42
|
+
|
|
43
|
+
```javascript
|
|
44
|
+
const c = circle2d(25);
|
|
45
|
+
const b = c.bounds();
|
|
46
|
+
// b.min ≈ [-25, -25], b.max ≈ [25, 25]
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
#### `.isEmpty(): boolean`
|
|
50
|
+
Returns true if the sketch has no area.
|
|
51
|
+
|
|
52
|
+
#### `.numVert(): number`
|
|
53
|
+
Returns the number of vertices in the contour.
|
|
54
|
+
|
|
55
|
+
#### `.toPolygons()`
|
|
56
|
+
Returns raw polygon contours for rendering (internal use).
|
|
57
|
+
|
|
58
|
+
## Type: Anchor
|
|
59
|
+
|
|
60
|
+
Anchor points for positioning sketches:
|
|
61
|
+
- `'center'` — geometric center
|
|
62
|
+
- `'top-left'`, `'top-right'`, `'bottom-left'`, `'bottom-right'` — corners
|
|
63
|
+
- `'top'`, `'bottom'`, `'left'`, `'right'` — edge midpoints
|
|
64
|
+
|
|
65
|
+
## Dimensions
|
|
66
|
+
|
|
67
|
+
Use `dim()` / `dimLine()` for visual measurement callouts and report annotations.
|
|
68
|
+
See [../output/dimensions.md](../output/dimensions.md) for options and ownership behavior.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Sketch Extrude & Revolve
|
|
2
|
+
|
|
3
|
+
Convert 2D sketches into 3D shapes through extrusion or revolution. The sketch's color (if set) is carried over to the resulting Shape.
|
|
4
|
+
|
|
5
|
+
If a sketch has been placed with [`onFace()`](sketch-on-face.md), extrusion follows that face normal instead of the global Z axis.
|
|
6
|
+
|
|
7
|
+
## Methods
|
|
8
|
+
|
|
9
|
+
### `.extrude(height, options?)`
|
|
10
|
+
Extrudes sketch along Z axis.
|
|
11
|
+
|
|
12
|
+
**Parameters:**
|
|
13
|
+
- `height` (number) - Extrusion height
|
|
14
|
+
- `options` (object, optional):
|
|
15
|
+
- `twist` (number) - Twist angle in degrees
|
|
16
|
+
- `divisions` (number) - Number of twist steps (needed for twist)
|
|
17
|
+
- `scaleTop` (number | [number, number]) - Scale factor at top
|
|
18
|
+
- `center` (boolean) - Center along Z axis
|
|
19
|
+
|
|
20
|
+
**Returns:** `TrackedShape` (with faces: top, bottom, side)
|
|
21
|
+
|
|
22
|
+
```javascript
|
|
23
|
+
const simple = rect(50, 30).extrude(10);
|
|
24
|
+
|
|
25
|
+
const twisted = ngon(6, 20).extrude(60, {
|
|
26
|
+
twist: 90,
|
|
27
|
+
divisions: 32
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const tapered = circle2d(20).extrude(50, {
|
|
31
|
+
scaleTop: 0.5
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const badge = roundedRect(28, 10, 2, true)
|
|
35
|
+
.onFace(box(120, 60, 40, true), 'front', { v: 8 })
|
|
36
|
+
.extrude(2);
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### `.revolve(degrees?, segments?)`
|
|
40
|
+
Revolves sketch around Y axis (becomes Z in result).
|
|
41
|
+
|
|
42
|
+
Performance tip: prefer `revolve()` over `loft()` whenever the part is rotationally symmetric. Loft is for profile interpolation and is substantially heavier.
|
|
43
|
+
|
|
44
|
+
**Parameters:**
|
|
45
|
+
- `degrees` (number, optional) - Rotation angle. Default: 360 (full revolution)
|
|
46
|
+
- `segments` (number, optional) - Number of segments. Default: auto
|
|
47
|
+
|
|
48
|
+
**Returns:** `Shape`
|
|
49
|
+
|
|
50
|
+
```javascript
|
|
51
|
+
// Vase profile
|
|
52
|
+
const profile = polygon([[20, 0], [25, 30], [20, 60]]);
|
|
53
|
+
const vase = profile.revolve();
|
|
54
|
+
|
|
55
|
+
// Partial revolution (C-shape)
|
|
56
|
+
const partial = rect(5, 40).translate(20, 0).revolve(270);
|
|
57
|
+
```
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Sketch On Face
|
|
2
|
+
|
|
3
|
+
Attach a 2D sketch to a 3D face so it renders in-place and extrudes along that face normal.
|
|
4
|
+
|
|
5
|
+
This supports:
|
|
6
|
+
- canonical body faces: `front`, `back`, `left`, `right`, `top`, `bottom`
|
|
7
|
+
- tracked planar faces on `TrackedShape`, like `side-left`
|
|
8
|
+
- direct `FaceRef` targets from `tracked.face('top')`
|
|
9
|
+
- supported compiler-owned created faces on `shell()` / `hole()` / `cutout()` results, such as `inner-side-right`, `floor`, `counterbore-floor`, and `wall-right`
|
|
10
|
+
- supported compiler-owned created faces on `shell()` / `hole()` / `cutout()` results, such as `inner-side-right`, `floor`, `counterbore-floor`, and `wall-right`
|
|
11
|
+
- defended preserved faces on compile-covered boolean results when one propagated descendant keeps a unique name
|
|
12
|
+
- direct `FaceRef` targets from preserved/repeated descendants that still validate against a later compile-covered boolean target
|
|
13
|
+
|
|
14
|
+
## `.onFace(parent, face, opts?)`
|
|
15
|
+
|
|
16
|
+
Places a sketch onto a parent face using face-local coordinates.
|
|
17
|
+
|
|
18
|
+
**Parameters:**
|
|
19
|
+
- `parent` (`Shape | TrackedShape`) - target body
|
|
20
|
+
- `face` (`'front' | 'back' | 'left' | 'right' | 'top' | 'bottom' | string | FaceRef`)
|
|
21
|
+
- `opts` (object, optional):
|
|
22
|
+
- `u` (number) - face-local horizontal offset from the face center
|
|
23
|
+
- `v` (number) - face-local vertical offset from the face center
|
|
24
|
+
- `protrude` (number) - offset along the face normal. Positive = outward
|
|
25
|
+
- `selfAnchor` (`Anchor`) - which 2D sketch anchor aligns to the face center. Default: `'center'`
|
|
26
|
+
|
|
27
|
+
**Returns:** `Sketch`
|
|
28
|
+
|
|
29
|
+
## `.onFace(faceRef, opts?)`
|
|
30
|
+
|
|
31
|
+
Places a sketch directly from a tracked or compiler-owned planar `FaceRef`.
|
|
32
|
+
|
|
33
|
+
This is useful when the script has already selected a face semantically:
|
|
34
|
+
|
|
35
|
+
```javascript
|
|
36
|
+
const panel = Rectangle2D.from3Points(
|
|
37
|
+
point(-30, -18),
|
|
38
|
+
point(28, -6),
|
|
39
|
+
point(18, 24),
|
|
40
|
+
).extrude(16);
|
|
41
|
+
|
|
42
|
+
const cap = circle2d(5)
|
|
43
|
+
.onFace(panel.face('top'), { u: 12, protrude: 0.05 })
|
|
44
|
+
.extrude(1.2);
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```javascript
|
|
48
|
+
const cup = roundedRect(70, 42, 5, true)
|
|
49
|
+
.extrude(22)
|
|
50
|
+
.shell(2, { openFaces: ['top'] });
|
|
51
|
+
|
|
52
|
+
const rib = rect(6, 4)
|
|
53
|
+
.onFace(cup, 'inner-side-right', { u: 0, v: 0, protrude: 0.05 })
|
|
54
|
+
.extrude(1.2);
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
```javascript
|
|
58
|
+
const body = box(120, 60, 40, true).color('#d8dce3');
|
|
59
|
+
|
|
60
|
+
const badge = roundedRect(28, 10, 2, true)
|
|
61
|
+
.onFace(body, 'front', { v: 8 })
|
|
62
|
+
.extrude(2)
|
|
63
|
+
.color('#1d2733');
|
|
64
|
+
|
|
65
|
+
return [
|
|
66
|
+
{ name: 'Body', shape: body },
|
|
67
|
+
{ name: 'Badge', shape: badge },
|
|
68
|
+
];
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Face-local coordinates
|
|
72
|
+
|
|
73
|
+
- Canonical faces:
|
|
74
|
+
- `front` / `back`: `u = X`, `v = Z`
|
|
75
|
+
- `left` / `right`: `u` runs across the face, `v = Z`
|
|
76
|
+
- `top` / `bottom`: `u = X`, `v` runs across the face
|
|
77
|
+
- Tracked planar faces use their own stored local frame:
|
|
78
|
+
- side faces of extruded rectangles: `u` follows the source edge, `v = Z`
|
|
79
|
+
- tracked `top` / `bottom` faces follow the source sketch axes
|
|
80
|
+
- direct `FaceRef` placement uses that face's `uAxis` / `vAxis`
|
|
81
|
+
- supported shell inner walls, blind-hole floors, counterbore shoulder floors, and defended cut walls reuse compiler-owned local frames for downstream workplanes
|
|
82
|
+
- supported shell inner walls, blind-hole floors, counterbore shoulder floors, and defended cut walls reuse compiler-owned local frames for downstream workplanes
|
|
83
|
+
- compile-covered `Shape` targets now resolve defended named faces through the shared face-query table before falling back to bare canonical body heuristics
|
|
84
|
+
|
|
85
|
+
The sketch's local `+Z` becomes the face normal, so `extrude(positive)` goes outward from that face.
|
|
86
|
+
|
|
87
|
+
## Notes
|
|
88
|
+
|
|
89
|
+
- This is a planar face-placement feature, not arbitrary curved-surface projection.
|
|
90
|
+
- Tracked curved faces like `cylinder(...).face('side')` are rejected because they do not have a planar sketch frame.
|
|
91
|
+
- Supported created-face names on compiler-owned feature results are intentionally narrow, but defended split descendants now stay visible as semantic regions where Forge can keep one stable source surface.
|
|
92
|
+
- Hole/cut host faces, supported `upToFace` termination faces, and defended boolean-difference / boolean-intersection descendants can now stay queryable as face regions instead of collapsing straight to "missing face".
|
|
93
|
+
- Coplanar boolean face sets now stay placeable through `onFace(shape, name, ...)` when Forge can defend one shared planar frame; non-coplanar sets stay explicit and reject planar placement honestly.
|
|
94
|
+
- The placed sketch still supports normal 2D operations like `translate`, `rotate`, `scale`, and sketch booleans before extrusion.
|
|
95
|
+
- If multiple sketches share the same face placement, their 2D booleans preserve that shared placement.
|
|
96
|
+
- If booleans mix sketches with different 3D placements, the result drops back to an unplaced sketch.
|
|
97
|
+
- Extruding a placed sketch keeps the tracked `top` / `bottom` / `side` metadata from that extrusion, transformed into world space.
|
|
98
|
+
- Projection-driven follow-on sketches now keep compiler-visible provenance when you `projectToPlane()` a compatible projected source back onto a matching parallel plane. The defended exact subset now covers straight extrusions plus compatible shell/hole/cut/union descendants that reduce to one planar projection basis, but arbitrary projection targets still stay runtime-only.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Sketch Operations
|
|
2
|
+
|
|
3
|
+
2D operations for modifying sketch contours.
|
|
4
|
+
|
|
5
|
+
## Methods
|
|
6
|
+
|
|
7
|
+
All operations preserve the sketch's color.
|
|
8
|
+
|
|
9
|
+
### `.offset(delta, join?)`
|
|
10
|
+
Inflate (positive) or deflate (negative) the contour.
|
|
11
|
+
|
|
12
|
+
**Parameters:**
|
|
13
|
+
- `delta` (number) - Offset distance. Positive = outward, negative = inward
|
|
14
|
+
- `join` ('Square' | 'Round' | 'Miter', optional) - Corner style. Default: 'Round'
|
|
15
|
+
|
|
16
|
+
```javascript
|
|
17
|
+
const outer = rect(50, 30).offset(5); // Expand by 5mm
|
|
18
|
+
const inner = circle2d(20).offset(-2); // Shrink by 2mm
|
|
19
|
+
const sharp = ngon(6, 20).offset(3, 'Miter');
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Use the common `offset(-r).offset(+r)` pattern when you want to round **every convex corner** of a closed sketch.
|
|
23
|
+
|
|
24
|
+
### `filletCorners(points, corners)`
|
|
25
|
+
Round only specific convex corners of a polygon point list.
|
|
26
|
+
|
|
27
|
+
**Parameters:**
|
|
28
|
+
- `points` (([number, number] | Point2D)[]) - Closed polygon vertices in order
|
|
29
|
+
- `corners` (`{ index: number, radius: number, segments?: number }[]`) - Which vertices to fillet
|
|
30
|
+
|
|
31
|
+
**Returns:** `Sketch`
|
|
32
|
+
|
|
33
|
+
```javascript
|
|
34
|
+
const roofPoints = [
|
|
35
|
+
[0, 0],
|
|
36
|
+
[90, 0],
|
|
37
|
+
[90, 44],
|
|
38
|
+
[66, 74],
|
|
39
|
+
[45, 86],
|
|
40
|
+
[24, 74],
|
|
41
|
+
[0, 44],
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
const roof = filletCorners(roofPoints, [
|
|
45
|
+
{ index: 3, radius: 19 },
|
|
46
|
+
{ index: 4, radius: 19 },
|
|
47
|
+
{ index: 5, radius: 19 },
|
|
48
|
+
]);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Notes:
|
|
52
|
+
- only convex corners are supported
|
|
53
|
+
- if two neighboring fillets would overlap on the same edge, the function throws
|
|
54
|
+
- compare `polygon(points)` and `filletCorners(points, ...)` before extruding when debugging mixed sharp-and-rounded outlines
|
|
55
|
+
|
|
56
|
+
## Choosing A Rounding Strategy
|
|
57
|
+
|
|
58
|
+
- `offset(-r).offset(+r)` rounds all convex corners of an existing closed profile
|
|
59
|
+
- `stroke(points, width, 'Round')` thickens a centerline path; use it for ribs, traces, and wire-like geometry
|
|
60
|
+
- `hull2d()` of circles creates a blended convex silhouette, closer to a capsule or cap than a true corner fillet
|
|
61
|
+
- `filletCorners(points, ...)` is the right tool when some corners stay sharp and others need true tangent fillets
|
|
62
|
+
- See `examples/api/sketch-rounding-strategies.forge.js` for a side-by-side comparison
|
|
63
|
+
|
|
64
|
+
### `.hull()`
|
|
65
|
+
Returns the convex hull of this sketch.
|
|
66
|
+
|
|
67
|
+
```javascript
|
|
68
|
+
const hull = complexShape.hull();
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### `.simplify(epsilon?)`
|
|
72
|
+
Removes vertices that don't significantly affect the shape.
|
|
73
|
+
|
|
74
|
+
**Parameters:**
|
|
75
|
+
- `epsilon` (number, optional) - Tolerance for vertex removal. Default: 1e-6
|
|
76
|
+
|
|
77
|
+
```javascript
|
|
78
|
+
const simplified = complexSketch.simplify(0.1);
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### `.warp(fn)`
|
|
82
|
+
Warp vertices with an arbitrary function.
|
|
83
|
+
|
|
84
|
+
**Parameters:**
|
|
85
|
+
- `fn` ((vert: [number, number]) => void) - Function that modifies vertex coordinates in-place
|
|
86
|
+
|
|
87
|
+
```javascript
|
|
88
|
+
const warped = rect(50, 50).warp(([x, y]) => {
|
|
89
|
+
// Modify x and y in place
|
|
90
|
+
x += Math.sin(y * 0.1) * 5;
|
|
91
|
+
});
|
|
92
|
+
```
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Sketch Path Builder
|
|
2
|
+
|
|
3
|
+
Fluent API for tracing 2D outlines point by point.
|
|
4
|
+
|
|
5
|
+
## Class: PathBuilder
|
|
6
|
+
|
|
7
|
+
### `path()`
|
|
8
|
+
Creates a new path builder.
|
|
9
|
+
|
|
10
|
+
```javascript
|
|
11
|
+
const triangle = path()
|
|
12
|
+
.moveTo(0, 0)
|
|
13
|
+
.lineH(50)
|
|
14
|
+
.lineV(30)
|
|
15
|
+
.close();
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### Methods
|
|
19
|
+
|
|
20
|
+
#### `.moveTo(x, y)`
|
|
21
|
+
Set starting point.
|
|
22
|
+
|
|
23
|
+
#### `.lineTo(x, y)`
|
|
24
|
+
Line to absolute position.
|
|
25
|
+
|
|
26
|
+
#### `.lineH(dx)`
|
|
27
|
+
Horizontal line (relative).
|
|
28
|
+
|
|
29
|
+
#### `.lineV(dy)`
|
|
30
|
+
Vertical line (relative).
|
|
31
|
+
|
|
32
|
+
#### `.lineAngled(length, degrees)`
|
|
33
|
+
Line at angle (0°=right, 90°=up).
|
|
34
|
+
|
|
35
|
+
#### `.close()`
|
|
36
|
+
Close path into a `Sketch` (auto-fixes winding).
|
|
37
|
+
|
|
38
|
+
#### `.stroke(width, join?)`
|
|
39
|
+
Thicken path into solid profile (see below).
|
|
40
|
+
|
|
41
|
+
## Stroke
|
|
42
|
+
|
|
43
|
+
Thicken a polyline (centerline) into a solid profile with uniform width. Proper miter joins at vertices.
|
|
44
|
+
|
|
45
|
+
### `path().stroke(width, join?)`
|
|
46
|
+
### `stroke(points, width, join?)`
|
|
47
|
+
|
|
48
|
+
**Parameters:**
|
|
49
|
+
- `width` (number) — Profile thickness
|
|
50
|
+
- `join` ('Square' | 'Round', optional) — Corner style. Default: 'Square' (miter)
|
|
51
|
+
|
|
52
|
+
**Returns:** `Sketch`
|
|
53
|
+
|
|
54
|
+
```javascript
|
|
55
|
+
// Fluent path builder
|
|
56
|
+
const bracket = path()
|
|
57
|
+
.moveTo(0, 0)
|
|
58
|
+
.lineH(50)
|
|
59
|
+
.lineV(-70)
|
|
60
|
+
.lineAngled(20, 235)
|
|
61
|
+
.stroke(4);
|
|
62
|
+
|
|
63
|
+
// Or with point array
|
|
64
|
+
const bracket = stroke([[0, 0], [50, 0], [50, -70]], 4);
|
|
65
|
+
|
|
66
|
+
// Rounded corners
|
|
67
|
+
const rounded = stroke([[0, 0], [50, 0], [50, -50]], 4, 'Round');
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Use `stroke(..., 'Round')` for centerline-based geometry such as ribs, traces, and wire-like profiles. It is not the same as rounding selected corners of an existing closed polygon. For mixed sharp-and-rounded outlines, build the polygon first and use `filletCorners(...)`.
|