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.
Files changed (41) hide show
  1. package/dist/assets/{evalWorker-DuS4V-H5.js → evalWorker-1m873KWd.js} +107 -107
  2. package/dist/assets/{index-d60dJDhX.js → index-Dvz3nSDc.js} +342 -326
  3. package/dist/assets/{manifold-DEOPQqeC.js → manifold-C38sUiKu.js} +1 -1
  4. package/dist/assets/{manifold-cL7A7HeM.js → manifold-Dk2u-lhj.js} +1 -1
  5. package/dist/assets/{manifold-DbvY5u9x.js → manifold-rOWQW9fU.js} +1 -1
  6. package/dist/assets/{reportWorker-GZVKtf9S.js → reportWorker-Cj587shw.js} +129 -129
  7. package/dist/index.html +1 -1
  8. package/dist-cli/forgecad.js +204 -19
  9. package/dist-skill/SKILL.md +31 -4561
  10. package/dist-skill/docs/API/README.md +24 -0
  11. package/dist-skill/docs/API/guides/modeling-recipes.md +246 -0
  12. package/dist-skill/docs/API/internals/compiler.md +300 -0
  13. package/dist-skill/docs/API/internals/manifold.md +7 -0
  14. package/dist-skill/docs/API/model-building/README.md +31 -0
  15. package/dist-skill/docs/API/model-building/assembly.md +205 -0
  16. package/dist-skill/docs/API/model-building/coordinate-system.md +43 -0
  17. package/dist-skill/docs/API/model-building/entities.md +282 -0
  18. package/dist-skill/docs/API/model-building/geometry-conventions.md +104 -0
  19. package/dist-skill/docs/API/model-building/positioning.md +170 -0
  20. package/dist-skill/docs/API/model-building/reference.md +1936 -0
  21. package/dist-skill/docs/API/model-building/sheet-metal.md +180 -0
  22. package/dist-skill/docs/API/model-building/sketch-anchor.md +32 -0
  23. package/dist-skill/docs/API/model-building/sketch-booleans.md +101 -0
  24. package/dist-skill/docs/API/model-building/sketch-core.md +68 -0
  25. package/dist-skill/docs/API/model-building/sketch-extrude.md +57 -0
  26. package/dist-skill/docs/API/model-building/sketch-on-face.md +98 -0
  27. package/dist-skill/docs/API/model-building/sketch-operations.md +92 -0
  28. package/dist-skill/docs/API/model-building/sketch-path.md +70 -0
  29. package/dist-skill/docs/API/model-building/sketch-primitives.md +104 -0
  30. package/dist-skill/docs/API/model-building/sketch-transforms.md +60 -0
  31. package/dist-skill/docs/API/output/bom.md +53 -0
  32. package/dist-skill/docs/API/output/brep-export.md +82 -0
  33. package/dist-skill/docs/API/output/dimensions.md +62 -0
  34. package/dist-skill/docs/API/runtime/viewport.md +229 -0
  35. package/dist-skill/docs/CLI.md +672 -0
  36. package/dist-skill/docs/CODING.md +345 -0
  37. package/dist-skill/docs/PROGRAM-LEAD.md +180 -0
  38. package/dist-skill/docs/VISION.md +77 -0
  39. package/examples/api/import-group-assembly.forge.js +34 -0
  40. package/examples/api/import-group-source.forge.js +35 -0
  41. 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(...)`.