forgecad 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/assets/{AdminPage-DAu1C1ST.js → AdminPage-D4bocK4E.js} +1 -1
- package/dist/assets/{DocsPage-Gc_BCdqC.js → DocsPage-D3A_g8V3.js} +85 -45
- package/dist/assets/{EditorApp-DG1-oUSV.css → EditorApp-BWYUSpUN.css} +133 -51
- package/dist/assets/EditorApp-Cihhqcsq.js +11692 -0
- package/dist/assets/{EmbedViewer-CEO8XbV8.js → EmbedViewer-kWjKaC_t.js} +1 -1
- package/dist/assets/LandingPageProofDriven-Bg2IUc3l.css +856 -0
- package/dist/assets/LandingPageProofDriven-DXkKlyhI.js +601 -0
- package/dist/assets/{PricingPage-BSrxu6d7.js → PricingPage-BsU5vzEx.js} +1 -1
- package/dist/assets/{SettingsPage-FUCSIRq6.js → SettingsPage-PqvpAKIs.js} +1 -1
- package/dist/assets/{evalWorker-KoR0SNKq.js → evalWorker-C-hzNUMy.js} +2218 -286
- package/dist/assets/{index-wTEK39at.js → index-Pz321YAt.js} +7416 -1481
- package/dist/assets/{index-CyVd1D4D.css → index-ay13WNfa.css} +501 -2
- package/dist/assets/{manifold-B1sGWdYk.js → manifold-BcbjWLIo.js} +3 -3
- package/dist/assets/{manifold-D7o0N50J.js → manifold-DBckbFgx.js} +1 -1
- package/dist/assets/{manifold-G5sBaXzi.js → manifold-O2AAGXyj.js} +1 -1
- package/dist/assets/{reportWorker-DYcRHhv9.js → reportWorker-Dxr-5A7w.js} +2003 -259
- package/dist/docs/index.html +2 -2
- package/dist/docs-raw/CLI.md +488 -0
- package/dist/docs-raw/generated/assembly.md +19 -11
- package/dist/docs-raw/generated/concepts.md +1023 -360
- package/dist/docs-raw/generated/core.md +1165 -264
- package/dist/docs-raw/generated/curves.md +168 -1
- package/dist/docs-raw/generated/lib.md +10 -5
- package/dist/docs-raw/generated/output.md +1 -1
- package/dist/docs-raw/generated/sdf.md +208 -0
- package/dist/docs-raw/generated/sketch.md +1281 -329
- package/dist/docs-raw/generated/viewport.md +29 -2
- package/dist/index.html +2 -2
- package/dist/landing/proof-ams-adapter.png +0 -0
- package/dist/landing/proof-bolt-and-nut.png +0 -0
- package/dist/landing/proof-fillet-enclosure.png +0 -0
- package/dist/landing/proof-glasses.png +0 -0
- package/dist/landing/proof-gyroid.png +0 -0
- package/dist/sitemap.xml +6 -6
- package/dist-cli/forgecad.js +3148 -555
- package/dist-cli/forgecad.js.map +1 -1
- package/dist-cli/{solver-FV7TJZGI.js → solver-46FFSK2U.js} +1 -3
- package/dist-cli/{solver-FV7TJZGI.js.map → solver-46FFSK2U.js.map} +1 -1
- package/dist-skill/CONTEXT.md +3700 -1153
- package/dist-skill/SKILL-dev.md +15 -17
- package/dist-skill/SKILL.md +14 -9
- package/dist-skill/docs/API/core/concepts.md +28 -1
- package/dist-skill/docs/CLI.md +488 -0
- package/dist-skill/docs/generated/assembly.md +19 -11
- package/dist-skill/docs/generated/core.md +1165 -264
- package/dist-skill/docs/generated/curves.md +168 -1
- package/dist-skill/docs/generated/lib.md +10 -5
- package/dist-skill/docs/generated/output.md +1 -1
- package/dist-skill/docs/generated/sdf.md +208 -0
- package/dist-skill/docs/generated/sketch.md +1281 -329
- package/dist-skill/docs/generated/viewport.md +29 -2
- package/dist-skill/docs/guides/joint-design.md +78 -0
- package/dist-skill/docs-dev/API/core/concepts.md +28 -1
- package/dist-skill/docs-dev/CLI.md +488 -0
- package/dist-skill/docs-dev/coding.md +1 -1
- package/dist-skill/docs-dev/component-model.md +164 -0
- package/dist-skill/docs-dev/generated/assembly.md +19 -11
- package/dist-skill/docs-dev/generated/core.md +1165 -264
- package/dist-skill/docs-dev/generated/curves.md +168 -1
- package/dist-skill/docs-dev/generated/lib.md +10 -5
- package/dist-skill/docs-dev/generated/output.md +1 -1
- package/dist-skill/docs-dev/generated/sdf.md +208 -0
- package/dist-skill/docs-dev/generated/sketch.md +1281 -329
- package/dist-skill/docs-dev/generated/viewport.md +29 -2
- package/dist-skill/docs-dev/guides/joint-design.md +78 -0
- package/examples/api/attachTo-basics.forge.js +3 -3
- package/examples/api/bill-of-materials.forge.js +9 -9
- package/examples/api/bolt-pattern.forge.js +5 -5
- package/examples/api/boolean-operations.forge.js +2 -2
- package/examples/api/bounding-box-visualizer.forge.js +1 -1
- package/examples/api/clone-duplicate.forge.js +1 -1
- package/examples/api/connector-assembly.forge.js +4 -2
- package/examples/api/connector-basics.forge.js +5 -5
- package/examples/api/constrained-sketch-mechanical.forge.js +4 -4
- package/examples/api/elbow-test.forge.js +3 -3
- package/examples/api/extrude-options.forge.js +4 -4
- package/examples/api/fillet-showcase.forge.js +1 -1
- package/examples/api/gears-tier1.forge.js +5 -5
- package/examples/api/group-test.forge.js +2 -2
- package/examples/api/mesh-import-slats.forge.js +3 -3
- package/examples/api/patterns.forge.js +3 -3
- package/examples/api/pointAlong-orientation.forge.js +2 -2
- package/examples/api/profile-2020-b-slot6.forge.js +4 -4
- package/examples/api/sketch-rounding-strategies.forge.js +1 -1
- package/examples/api/smooth-curve-connections.forge.js +1 -1
- package/examples/api/transition-curves.forge.js +3 -3
- package/examples/constraints/01-fully-constrained-rect.forge.js +2 -2
- package/examples/constraints/02-underconstrained-triangle.forge.js +1 -1
- package/examples/constraints/03-redundant-constraints.forge.js +2 -2
- package/examples/constraints/05-parallel-with-linedistance.forge.js +2 -2
- package/examples/constraints/06-complex-spectrogram.forge.js +1 -1
- package/examples/constraints/07-perpendicular-chain.forge.js +4 -4
- package/examples/constraints/08-symmetric-bracket.forge.js +4 -4
- package/examples/constraints/09-stress-spiral.forge.js +1 -1
- package/examples/constraints/10-stress-honeycomb.forge.js +1 -1
- package/examples/constraints/11-surface-grid.forge.js +2 -2
- package/examples/constraints/12-surface-nested.forge.js +4 -4
- package/examples/constraints/13-surface-complex.forge.js +1 -1
- package/examples/exact-arc-housing.forge.js +12 -0
- package/examples/furniture/adjustable-table.forge.js +13 -13
- package/examples/furniture/bathroom.forge.js +15 -15
- package/examples/furniture/chair.forge.js +12 -12
- package/examples/furniture/picture-frame.forge.js +6 -6
- package/examples/furniture/shoe-rack-doors.forge.js +8 -8
- package/examples/furniture/shoe-rack.forge.js +7 -7
- package/examples/furniture/table-lamp.forge.js +8 -8
- package/examples/gcode/lissajous-vase.forge.js +4 -4
- package/examples/gcode/math-surface.forge.js +3 -3
- package/examples/gcode/parametric-vase.forge.js +4 -4
- package/examples/gcode/spiral-tower.forge.js +4 -4
- package/examples/generative/crystal-growth.forge.js +7 -7
- package/examples/generative/frost-spires.forge.js +6 -6
- package/examples/generative/golden-spiral-tower.forge.js +8 -8
- package/examples/generative/molten-forge.forge.js +6 -6
- package/examples/generative/neon-coral.forge.js +7 -7
- package/examples/mechanical/3d-printer.forge.js +9 -9
- package/examples/mechanical/5-finger-robot-hand.forge.js +4 -4
- package/examples/mechanical/airplane-propeller.forge.js +7 -7
- package/examples/mechanical/bolt-and-nut.forge.js +10 -10
- package/examples/mechanical/door-with-hinges.forge.js +7 -7
- package/examples/mechanical/fillet-enclosure.forge.js +14 -10
- package/examples/mechanical/headphone-hanger-v2.forge.js +9 -9
- package/examples/mechanical/robot_hand.forge.js +10 -10
- package/examples/mechanical/robot_hand_2.forge.js +17 -17
- package/examples/nurbs-surface.forge.js +8 -0
- package/examples/nurbs-tube.forge.js +7 -0
- package/examples/products/bottle.forge.js +7 -7
- package/examples/products/chess-set.forge.js +6 -6
- package/examples/products/classical-piano.forge.js +9 -9
- package/examples/products/clock.forge.js +21 -21
- package/examples/products/cup.forge.js +5 -5
- package/examples/products/iphone.forge.js +12 -12
- package/examples/products/laptop.forge.js +9 -9
- package/examples/products/laser-cut-box.forge.js +6 -6
- package/examples/products/laser-cut-tray.forge.js +6 -6
- package/examples/products/liquid-soap-dispenser.forge.js +5 -5
- package/examples/products/origami-fish.forge.js +6 -6
- package/examples/products/spiderman-cake.forge.js +2 -2
- package/examples/shelf/container.forge.js +5 -5
- package/examples/shelf/shelf-unit.forge.js +6 -6
- package/examples/toolbox/bolted-joint.forge.js +5 -5
- package/package.json +3 -1
- package/dist/assets/EditorApp-D9bJvtf7.js +0 -11338
- package/dist/assets/LandingPage-CdCuEOdC.js +0 -451
- package/dist-cli/chunk-PZ5AY32C.js +0 -10
- package/dist-cli/chunk-PZ5AY32C.js.map +0 -1
- package/dist-skill/docs/CLI/export.md +0 -91
- package/dist-skill/docs/CLI/projects.md +0 -107
- package/dist-skill/docs/CLI/studio_publishing.md +0 -52
- package/dist-skill/docs/CLI/validation.md +0 -66
- package/dist-skill/docs-dev/API/core/sdf-advanced.md +0 -92
- package/dist-skill/docs-dev/API/core/sdf-primitives.md +0 -58
- package/dist-skill/docs-dev/API/core/sdf-workflow.md +0 -42
- package/dist-skill/docs-dev/CLI/export.md +0 -91
- package/dist-skill/docs-dev/CLI/projects.md +0 -107
- package/dist-skill/docs-dev/CLI/studio_publishing.md +0 -52
- package/dist-skill/docs-dev/CLI/validation.md +0 -66
|
@@ -5,33 +5,30 @@ skill-order: 100
|
|
|
5
5
|
|
|
6
6
|
# Core API
|
|
7
7
|
|
|
8
|
-
> **Auto-generated** from `src/forge/forge-public-api.ts`. Do not edit by hand — run `npm run gen:docs` to regenerate.
|
|
9
|
-
|
|
10
8
|
3D primitives, boolean operations, transforms, patterns, imports, and parameters.
|
|
11
9
|
|
|
12
|
-
##
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
10
|
+
## Contents
|
|
11
|
+
|
|
12
|
+
- [Boolean Operations](#boolean-operations) — `union`, `difference`, `intersection`
|
|
13
|
+
- [Edge Features](#edge-features) — `fillet`, `chamfer`, `draft`, `offsetSolid`
|
|
14
|
+
- [Patterns & Layout](#patterns-layout) — `selectEdges`, `selectEdge`, `coalesceEdges`, `filletCorners`, `circularLayout`, `polygonVertices`, `linearPattern`, `circularPattern`, `linearPattern2d`, `circularPattern2d`, `mirrorCopy`
|
|
15
|
+
- [Imports & Composition](#imports-composition) — `require`, `importSvgSketch`, `importMesh`, `importStep`
|
|
16
|
+
- [Parameters](#parameters) — `Param.number`, `Param.string`, `Param.bool`, `Param.choice`, `Param.list`
|
|
17
|
+
- [Grouping & Local Coordinates](#grouping-local-coordinates) — `group`
|
|
18
|
+
- [Section & Projection](#section-projection) — `intersectWithPlane`, `faceProfile`, `projectToPlane`
|
|
19
|
+
- [Transforms](#transforms) — `composeChain`
|
|
20
|
+
- [Verification](#verification) — `spec`
|
|
21
|
+
- [Shape](#shape) — Appearance, Face Topology, Edge Topology, Transforms, Booleans & Cutting, Features, Placement, Connectors, References, Measurement
|
|
22
|
+
- [Transform](#transform)
|
|
23
|
+
- [ShapeGroup](#shapegroup) — Children, Transforms, Placement, Connectors, References
|
|
24
|
+
- [SurfacePattern](#surfacepattern)
|
|
25
|
+
- [ANCHOR3D_NAMES](#anchor3d-names)
|
|
26
|
+
- [verify](#verify)
|
|
27
|
+
- [Constraint](#constraint)
|
|
28
|
+
- [Points](#points)
|
|
29
|
+
- [connector](#connector)
|
|
25
30
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
#### `sphere()` — Create a sphere centered at the origin. Use segments for lower-poly approximations.
|
|
29
|
-
|
|
30
|
-
`sphere$1(radius: number, segments?: number): Shape`
|
|
31
|
-
|
|
32
|
-
#### `torus()` — Create a torus (donut shape) lying in the XY plane. Centered on all axes (origin is the ring center).
|
|
33
|
-
|
|
34
|
-
`torus$1(majorRadius: number, minorRadius: number, segments?: number): Shape`
|
|
31
|
+
## Functions
|
|
35
32
|
|
|
36
33
|
### Boolean Operations
|
|
37
34
|
|
|
@@ -39,33 +36,39 @@ When radiusTop differs from radius, creates a tapered cone. Use the segments par
|
|
|
39
36
|
|
|
40
37
|
Accepts individual shapes, or an array of shapes. The first operand's color is preserved in the result.
|
|
41
38
|
|
|
42
|
-
|
|
39
|
+
```ts
|
|
40
|
+
union(...inputs: ShapeOperandInput[]): Shape
|
|
41
|
+
```
|
|
43
42
|
|
|
44
43
|
#### `difference()` — Subtract shapes from a base shape (subtractive boolean).
|
|
45
44
|
|
|
46
45
|
The first shape is the base; all subsequent shapes are subtracted from it. Accepts individual shapes, or an array of shapes.
|
|
47
46
|
|
|
48
|
-
|
|
47
|
+
```ts
|
|
48
|
+
difference(...inputs: ShapeOperandInput[]): Shape
|
|
49
|
+
```
|
|
49
50
|
|
|
50
51
|
#### `intersection()` — Keep only the overlapping volume of the input shapes (intersection boolean).
|
|
51
52
|
|
|
52
53
|
Requires at least two shapes. Accepts individual shapes, or an array.
|
|
53
54
|
|
|
54
|
-
|
|
55
|
+
```ts
|
|
56
|
+
intersection(...inputs: ShapeOperandInput[]): Shape
|
|
57
|
+
```
|
|
55
58
|
|
|
56
59
|
### Edge Features
|
|
57
60
|
|
|
58
61
|
#### `fillet()` — Apply fillets (rounded edges) to one or more edges of a shape.
|
|
59
62
|
|
|
60
|
-
**Details**
|
|
61
|
-
|
|
62
63
|
Works on both straight and curved edges. Supports OCCT and Manifold backends. When using OCCT, all edges are filleted in a single kernel operation for best quality. When using Manifold, edges are filleted sequentially.
|
|
63
64
|
|
|
64
|
-
The `edges` parameter is flexible:
|
|
65
|
+
The `edges` parameter is flexible:
|
|
65
66
|
|
|
66
|
-
|
|
67
|
+
- Omit to fillet **all** sharp edges
|
|
68
|
+
- Pass an `EdgeQuery` for an inline filter (most common)
|
|
69
|
+
- Pass an `EdgeSegment` or `EdgeSegment[]` from `selectEdges()` for pre-selected edges
|
|
67
70
|
|
|
68
|
-
|
|
71
|
+
Throws if no edges match the selection, or if `radius` is not a positive finite number.
|
|
69
72
|
|
|
70
73
|
```ts
|
|
71
74
|
// Fillet all edges
|
|
@@ -79,18 +82,16 @@ const edges = selectEdges(myShape, { parallel: [0, 0, 1] })
|
|
|
79
82
|
fillet(myShape, 3, edges)
|
|
80
83
|
```
|
|
81
84
|
|
|
82
|
-
|
|
85
|
+
```ts
|
|
86
|
+
fillet(shape: Shape, radius: number, edges?: EdgeSelector, segments?: number): Shape
|
|
87
|
+
```
|
|
83
88
|
|
|
84
89
|
#### `chamfer()` — Apply chamfers (beveled edges) to one or more edges of a shape.
|
|
85
90
|
|
|
86
|
-
**Details**
|
|
87
|
-
|
|
88
91
|
Produces a 45° bevel at the specified `size` (distance from edge). Works on both straight and curved edges. Supports OCCT and Manifold backends.
|
|
89
92
|
|
|
90
93
|
The `edges` parameter accepts the same options as `fillet()`: inline `EdgeQuery`, pre-selected `EdgeSegment`/`EdgeSegment[]`, or `undefined` (all sharp edges).
|
|
91
94
|
|
|
92
|
-
**Example**
|
|
93
|
-
|
|
94
95
|
```ts
|
|
95
96
|
// Chamfer all edges
|
|
96
97
|
chamfer(myShape, 1)
|
|
@@ -99,18 +100,16 @@ chamfer(myShape, 1)
|
|
|
99
100
|
chamfer(myShape, 2, { parallel: [0, 0, 1] })
|
|
100
101
|
```
|
|
101
102
|
|
|
102
|
-
|
|
103
|
+
```ts
|
|
104
|
+
chamfer(shape: Shape, size: number, edges?: EdgeSelector): Shape
|
|
105
|
+
```
|
|
103
106
|
|
|
104
107
|
#### `draft()` — Apply a draft angle (taper) to vertical faces for mold extraction.
|
|
105
108
|
|
|
106
|
-
**Details**
|
|
107
|
-
|
|
108
109
|
Adds a taper angle to the vertical faces of a solid so that it can be extracted from a mold. The neutral plane is the Z position where the draft angle is zero — faces above and below are tapered symmetrically. Typical values for injection molding are 1–5°.
|
|
109
110
|
|
|
110
111
|
Requires the OCCT backend. Throws on Manifold.
|
|
111
112
|
|
|
112
|
-
**Example**
|
|
113
|
-
|
|
114
113
|
```ts
|
|
115
114
|
// Add 3° draft to a box for injection molding
|
|
116
115
|
draft(myBox, 3)
|
|
@@ -119,18 +118,16 @@ draft(myBox, 3)
|
|
|
119
118
|
draft(myShape, 2, [0, 0, 1], 10)
|
|
120
119
|
```
|
|
121
120
|
|
|
122
|
-
|
|
121
|
+
```ts
|
|
122
|
+
draft(shape: Shape, angleDeg: number, pullDirection?: [ number, number, number ], neutralPlaneOffset?: number): Shape
|
|
123
|
+
```
|
|
123
124
|
|
|
124
125
|
#### `offsetSolid()` — Uniformly offset all surfaces of a solid inward or outward.
|
|
125
126
|
|
|
126
|
-
**Details**
|
|
127
|
-
|
|
128
127
|
Unlike `shell()`, which hollows a solid by removing one face, `offsetSolid()` produces a new solid whose every surface is shifted by `thickness`. Positive values grow the shape outward; negative values shrink it inward.
|
|
129
128
|
|
|
130
129
|
Requires the OCCT backend. Throws on Manifold.
|
|
131
130
|
|
|
132
|
-
**Example**
|
|
133
|
-
|
|
134
131
|
```ts
|
|
135
132
|
// Grow a box outward by 1mm on all sides
|
|
136
133
|
offsetSolid(myBox, 1)
|
|
@@ -139,20 +136,18 @@ offsetSolid(myBox, 1)
|
|
|
139
136
|
offsetSolid(myShape, -0.5)
|
|
140
137
|
```
|
|
141
138
|
|
|
142
|
-
|
|
139
|
+
```ts
|
|
140
|
+
offsetSolid(shape: Shape, thickness: number): Shape
|
|
141
|
+
```
|
|
143
142
|
|
|
144
143
|
### Patterns & Layout
|
|
145
144
|
|
|
146
145
|
#### `selectEdges()` — Select all edges from a shape that match the given query.
|
|
147
146
|
|
|
148
|
-
**Details**
|
|
149
|
-
|
|
150
147
|
Extracts sharp edges from the mesh (dihedral angle > 1°), applies all filters in the query, and returns the matching `EdgeSegment[]`. When `near` is specified the results are sorted closest-first.
|
|
151
148
|
|
|
152
149
|
Works on any shape — primitives, booleans, shells, and imported meshes. Use this when tracked topology is unavailable (e.g. after a difference or on imported geometry). For simpler cases, pass an `EdgeQuery` directly to `fillet()` or `chamfer()` instead of calling `selectEdges` separately.
|
|
153
150
|
|
|
154
|
-
**Example**
|
|
155
|
-
|
|
156
151
|
```ts
|
|
157
152
|
// Fillet all top edges of a box
|
|
158
153
|
const topEdges = selectEdges(part, { atZ: 20, perpendicular: [0, 0, 1] });
|
|
@@ -162,7 +157,9 @@ for (const edge of coalesceEdges(topEdges)) {
|
|
|
162
157
|
}
|
|
163
158
|
```
|
|
164
159
|
|
|
165
|
-
|
|
160
|
+
```ts
|
|
161
|
+
selectEdges(shape: Shape, query?: EdgeQuery): EdgeSegment[]
|
|
162
|
+
```
|
|
166
163
|
|
|
167
164
|
**`EdgeQuery`**
|
|
168
165
|
|
|
@@ -199,30 +196,24 @@ for (const edge of coalesceEdges(topEdges)) {
|
|
|
199
196
|
|
|
200
197
|
#### `selectEdge()` — Select the single best-matching edge from a shape.
|
|
201
198
|
|
|
202
|
-
**Details**
|
|
203
|
-
|
|
204
199
|
When `near` is specified, returns the edge whose midpoint is closest to that point. Otherwise returns the first matching edge in mesh order. Throws if no edges match the query — useful as a guard when you expect exactly one result.
|
|
205
200
|
|
|
206
|
-
**Example**
|
|
207
|
-
|
|
208
201
|
```ts
|
|
209
202
|
// Chamfer one specific edge near a known point
|
|
210
203
|
const bottomEdge = selectEdge(part, { near: [25, 0, 0], atZ: 0 });
|
|
211
204
|
result = chamfer(result, 1.5, bottomEdge);
|
|
212
205
|
```
|
|
213
206
|
|
|
214
|
-
|
|
207
|
+
```ts
|
|
208
|
+
selectEdge(shape: Shape, query?: EdgeQuery): EdgeSegment
|
|
209
|
+
```
|
|
215
210
|
|
|
216
211
|
#### `coalesceEdges()` — Merge collinear edge segments into longer logical edges.
|
|
217
212
|
|
|
218
|
-
**Details**
|
|
219
|
-
|
|
220
213
|
Tessellation often splits one geometric edge into multiple short segments. `coalesceEdges` groups adjacent collinear segments and merges each group into a single `EdgeSegment` spanning the full extent. This is usually needed before passing edges to `fillet()` or `chamfer()` on non-primitive shapes.
|
|
221
214
|
|
|
222
215
|
The `tolerance` controls the maximum perpendicular distance from collinearity before two segments are considered non-collinear. Default: `0.01`.
|
|
223
216
|
|
|
224
|
-
**Example**
|
|
225
|
-
|
|
226
217
|
```ts
|
|
227
218
|
const topEdges = selectEdges(part, { atZ: 20 });
|
|
228
219
|
for (const edge of coalesceEdges(topEdges)) {
|
|
@@ -230,19 +221,21 @@ for (const edge of coalesceEdges(topEdges)) {
|
|
|
230
221
|
}
|
|
231
222
|
```
|
|
232
223
|
|
|
233
|
-
|
|
224
|
+
```ts
|
|
225
|
+
coalesceEdges(segments: EdgeSegment[], tolerance?: number): EdgeSegment[]
|
|
226
|
+
```
|
|
234
227
|
|
|
235
228
|
#### `filletCorners()` — Create a polygon from points with specific corners rounded to arc fillets.
|
|
236
229
|
|
|
237
|
-
**Details**
|
|
238
|
-
|
|
239
230
|
Each corner spec identifies a vertex by its index in the `points` array and the desired fillet `radius`. Both convex and concave corners are supported.
|
|
240
231
|
|
|
241
|
-
Constraints:
|
|
232
|
+
Constraints:
|
|
242
233
|
|
|
243
|
-
|
|
234
|
+
- Collinear corners cannot be filleted (throws an error)
|
|
235
|
+
- Two neighboring fillets whose tangent lengths overlap the same edge will throw
|
|
236
|
+
- Radius must be positive and small enough to fit within the adjacent edge lengths
|
|
244
237
|
|
|
245
|
-
**
|
|
238
|
+
Use `offset(-r).offset(+r)` instead if you want to round **all** convex corners uniformly. Use `filletCorners` when you need selective or mixed sharp/rounded profiles.
|
|
246
239
|
|
|
247
240
|
```ts
|
|
248
241
|
const roof = filletCorners(roofPoints, [
|
|
@@ -252,7 +245,9 @@ const roof = filletCorners(roofPoints, [
|
|
|
252
245
|
]);
|
|
253
246
|
```
|
|
254
247
|
|
|
255
|
-
|
|
248
|
+
```ts
|
|
249
|
+
filletCorners(points: PointInput[], corners: FilletCornerSpec[]): Sketch
|
|
250
|
+
```
|
|
256
251
|
|
|
257
252
|
`FilletCornerSpec`: `{ index: number, radius: number, segments?: number }`
|
|
258
253
|
|
|
@@ -273,7 +268,9 @@ for (const {x, y} of circularLayout(12, r)) {
|
|
|
273
268
|
}
|
|
274
269
|
```
|
|
275
270
|
|
|
276
|
-
|
|
271
|
+
```ts
|
|
272
|
+
circularLayout(count: number, radius: number, options?: CircularLayoutOptions): LayoutPoint[]
|
|
273
|
+
```
|
|
277
274
|
|
|
278
275
|
**`CircularLayoutOptions`**
|
|
279
276
|
- `startDeg?: number` — Angle of the first element in degrees (default: 0 = +X axis).
|
|
@@ -284,7 +281,7 @@ for (const {x, y} of circularLayout(12, r)) {
|
|
|
284
281
|
|
|
285
282
|
#### `polygonVertices()` — Compute the vertex positions of a regular polygon.
|
|
286
283
|
|
|
287
|
-
Default orientation places the first vertex at the top (90 degrees), matching the convention used by `ngon()
|
|
284
|
+
Default orientation places the first vertex at the top (90 degrees), matching the convention used by [`ngon()`](/docs/sketch#ngon).
|
|
288
285
|
|
|
289
286
|
Eliminates manual Math.sqrt(3) for triangles, pentagon vertex math, etc:
|
|
290
287
|
|
|
@@ -298,7 +295,9 @@ const v3 = [center.x + r, center.y];
|
|
|
298
295
|
const [v1, v2, v3] = polygonVertices(3, r);
|
|
299
296
|
```
|
|
300
297
|
|
|
301
|
-
|
|
298
|
+
```ts
|
|
299
|
+
polygonVertices(sides: number, radius: number, options?: PolygonVerticesOptions): LayoutPoint[]
|
|
300
|
+
```
|
|
302
301
|
|
|
303
302
|
**`PolygonVerticesOptions`**
|
|
304
303
|
- `startDeg?: number` — Angle of the first vertex in degrees (default: 90 = top).
|
|
@@ -307,28 +306,25 @@ const [v1, v2, v3] = polygonVertices(3, r);
|
|
|
307
306
|
|
|
308
307
|
#### `linearPattern()` — Repeat a shape in a linear pattern along a direction vector and union the copies.
|
|
309
308
|
|
|
310
|
-
**Details**
|
|
311
|
-
|
|
312
309
|
Creates `count` copies of `shape`, each offset by `(dx*i, dy*i, dz*i)` from the original. All copies are unioned into a single `Shape`. Distinct compiler ownership is assigned to each copy so face identity via owner-scoped canonical queries still works post-merge.
|
|
313
310
|
|
|
314
|
-
**Example**
|
|
315
|
-
|
|
316
311
|
```ts
|
|
317
312
|
// 5 cylinders, 20mm apart along X
|
|
318
313
|
linearPattern(cylinder(10, 3), 5, 20, 0)
|
|
319
314
|
```
|
|
320
315
|
|
|
321
|
-
|
|
316
|
+
```ts
|
|
317
|
+
linearPattern(shape: Shape, count: number, dx: number, dy: number, dz?: number): Shape
|
|
318
|
+
```
|
|
322
319
|
|
|
323
320
|
#### `circularPattern()` — Repeat a shape in a circular pattern around an axis and union the copies.
|
|
324
321
|
|
|
325
|
-
**Details**
|
|
326
|
-
|
|
327
322
|
Distributes `count` copies evenly around the rotation axis (360° / count per step). All copies are unioned into a single `Shape`. Distinct compiler ownership is assigned to each copy — post-merge face identity via owner-scoped canonical queries still works for pattern descendants.
|
|
328
323
|
|
|
329
|
-
Two calling conventions:
|
|
324
|
+
Two calling conventions:
|
|
330
325
|
|
|
331
|
-
**
|
|
326
|
+
- **Simple** (Z axis): `circularPattern(shape, 6)` or `circularPattern(shape, 6, centerX, centerY)`
|
|
327
|
+
- **Advanced** (arbitrary axis): `circularPattern(shape, 6, { axis, origin })`
|
|
332
328
|
|
|
333
329
|
```ts
|
|
334
330
|
// 8 holes evenly spaced around origin
|
|
@@ -338,7 +334,9 @@ circularPattern(cylinder(12, 4).translate(30, 0, -1), 8)
|
|
|
338
334
|
circularPattern(myFeature, 4, { axis: [1, 0, 0], origin: [0, 0, 50] })
|
|
339
335
|
```
|
|
340
336
|
|
|
341
|
-
|
|
337
|
+
```ts
|
|
338
|
+
circularPattern(shape: Shape, count: number, centerXOrOpts?: number | CircularPatternOptions, centerY?: number): Shape
|
|
339
|
+
```
|
|
342
340
|
|
|
343
341
|
**`CircularPatternOptions`**
|
|
344
342
|
- `centerX?: number` — Center X of the rotation (default: 0). Used when axis is Z (legacy mode).
|
|
@@ -346,36 +344,71 @@ circularPattern(myFeature, 4, { axis: [1, 0, 0], origin: [0, 0, 50] })
|
|
|
346
344
|
|
|
347
345
|
#### `linearPattern2d()` — Repeat a 2D sketch in a linear pattern and union the copies.
|
|
348
346
|
|
|
349
|
-
|
|
347
|
+
```ts
|
|
348
|
+
linearPattern2d(sketch: Sketch, count: number, dx: number, dy?: number): Sketch
|
|
349
|
+
```
|
|
350
350
|
|
|
351
351
|
#### `circularPattern2d()` — Repeat a 2D sketch in a circular pattern around a center point and union the copies.
|
|
352
352
|
|
|
353
|
-
|
|
353
|
+
```ts
|
|
354
|
+
circularPattern2d(sketch: Sketch, count: number, centerXOrOpts?: number | { centerX?: number; centerY?: number; startDeg?: number; }, centerY?: number): Sketch
|
|
355
|
+
```
|
|
354
356
|
|
|
355
357
|
#### `mirrorCopy()` — Mirror a shape across a plane and union the mirror with the original.
|
|
356
358
|
|
|
357
|
-
**Details**
|
|
358
|
-
|
|
359
359
|
The mirror plane passes through the origin and is defined by its normal vector. The mirrored copy is unioned with the original to produce a single symmetric Shape.
|
|
360
360
|
|
|
361
|
-
**Example**
|
|
362
|
-
|
|
363
361
|
```ts
|
|
364
362
|
// Mirror across the YZ plane (X=0)
|
|
365
363
|
mirrorCopy(box(50, 30, 10), [1, 0, 0])
|
|
366
364
|
```
|
|
367
365
|
|
|
368
|
-
|
|
366
|
+
```ts
|
|
367
|
+
mirrorCopy(shape: Shape, normal: [ number, number, number ]): Shape
|
|
368
|
+
```
|
|
369
369
|
|
|
370
370
|
### Imports & Composition
|
|
371
371
|
|
|
372
372
|
#### `require()` — Import a module with optional ForgeCAD parameter overrides. Returns the module's exports.
|
|
373
373
|
|
|
374
|
-
`
|
|
374
|
+
When importing a `.forge.js` file, the return value is what the script returns. If the script returns a metadata object (e.g. `{ shape: myShape, bolts: {...} }`), the caller receives the full object — renderable values and metadata together.
|
|
375
|
+
|
|
376
|
+
**Parameter scoping:** Parameters declared in required files are automatically namespaced with a `"filename#N / "` prefix (e.g. `"bracket.forge.js#1 / Width"`). This prevents collisions when multiple files declare same-named params. Each file's params appear as separate sliders.
|
|
377
|
+
|
|
378
|
+
**Parameter overrides:** When passing overrides, use the bare param name (not the scoped name). Overrides are type-checked — unrecognized keys throw an error with typo suggestions.
|
|
379
|
+
|
|
380
|
+
**Multi-file assembly pattern** — pass cross-cutting design values from the assembly to parts:
|
|
381
|
+
|
|
382
|
+
```js
|
|
383
|
+
// assembly.forge.js — owns cross-cutting params, passes to parts
|
|
384
|
+
const wall = param("Wall", 3);
|
|
385
|
+
const baseH = param("Base Height", 20);
|
|
386
|
+
|
|
387
|
+
const mount = require('./motor-mount.forge.js', { Wall: wall });
|
|
388
|
+
const base = require('./base-body.forge.js', { Wall: wall, Height: baseH });
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
**Metadata pattern** — parts publish interface data alongside geometry:
|
|
392
|
+
|
|
393
|
+
```js
|
|
394
|
+
// motor-mount.forge.js
|
|
395
|
+
return { shape: mount, bolts: { dia: 5.3, pos: holePositions } };
|
|
396
|
+
|
|
397
|
+
// base-body.forge.js
|
|
398
|
+
const mount = require('./motor-mount.forge.js');
|
|
399
|
+
mount.bolts.pos // access the metadata
|
|
400
|
+
mount.shape // access the geometry
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
```ts
|
|
404
|
+
require(path: string, paramOverrides?: Record<string, number | string>): any
|
|
405
|
+
```
|
|
375
406
|
|
|
376
407
|
#### `importSvgSketch()` — Parse an SVG file and return it as a Sketch with options for region filtering, scaling, and simplification.
|
|
377
408
|
|
|
378
|
-
|
|
409
|
+
```ts
|
|
410
|
+
importSvgSketch(fileName: string, options?: SvgImportOptions): Sketch
|
|
411
|
+
```
|
|
379
412
|
|
|
380
413
|
**`SvgImportOptions`**
|
|
381
414
|
|
|
@@ -397,26 +430,34 @@ mirrorCopy(box(50, 30, 10), [1, 0, 0])
|
|
|
397
430
|
|
|
398
431
|
#### `importMesh()` — Import an external mesh file (STL, OBJ, 3MF) as a Shape.
|
|
399
432
|
|
|
400
|
-
|
|
433
|
+
```ts
|
|
434
|
+
importMesh(fileName: string, options?: { scale?: number; center?: boolean; }): Shape
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
#### `importStep()` — Import a STEP file (.step, .stp) as an exact OCCT-backed Shape. Preserves NURBS curves, B-spline surfaces, and exact topology. Requires `setActiveBackend('occt')`.
|
|
438
|
+
|
|
439
|
+
```ts
|
|
440
|
+
importStep(fileName: string): Shape
|
|
441
|
+
```
|
|
401
442
|
|
|
402
443
|
### Parameters
|
|
403
444
|
|
|
404
|
-
#### `
|
|
445
|
+
#### `Param.number()` — Declare a numeric parameter that renders as a slider in the UI.
|
|
405
446
|
|
|
406
|
-
|
|
447
|
+
Each call registers a slider control. When the user moves the slider the entire script re-executes with the new value. Parameter values are also overridable from `require()` imports or the CLI `--param` flag — the `name` string is the key used in both cases.
|
|
407
448
|
|
|
408
|
-
|
|
449
|
+
Default range rules when options are omitted:
|
|
409
450
|
|
|
410
|
-
|
|
451
|
+
- `min` defaults to `0`
|
|
452
|
+
- `max` defaults to `defaultValue * 4`
|
|
453
|
+
- `step` is auto-calculated: `1` for integer params, `0.1` for ranges ≤ 100, `1` for larger ranges
|
|
411
454
|
|
|
412
455
|
The `unit` option is cosmetic only — no conversion is performed. Use `integer: true` for counts, sides, quantities (rounds to whole numbers; step defaults to `1`).
|
|
413
456
|
|
|
414
|
-
**Example**
|
|
415
|
-
|
|
416
457
|
```ts
|
|
417
|
-
const width =
|
|
418
|
-
const angle =
|
|
419
|
-
const sides =
|
|
458
|
+
const width = Param.number("Width", 50);
|
|
459
|
+
const angle = Param.number("Angle", 45, { min: 0, max: 180, unit: "°" });
|
|
460
|
+
const sides = Param.number("Sides", 6, { min: 3, max: 12, integer: true });
|
|
420
461
|
```
|
|
421
462
|
|
|
422
463
|
**Parameter overrides** — key must match `name` exactly:
|
|
@@ -429,18 +470,39 @@ const bracket = require("./bracket.forge.js", { Width: 80 });
|
|
|
429
470
|
// forgecad run model.forge.js --param "Wall Thickness=3"
|
|
430
471
|
```
|
|
431
472
|
|
|
432
|
-
|
|
473
|
+
Also available as the shorthand alias `param()`.
|
|
433
474
|
|
|
434
|
-
|
|
475
|
+
```ts
|
|
476
|
+
Param.number(name: string, defaultValue: number, opts?: { min?: number; max?: number; step?: number; unit?: string; integer?: boolean; reverse?: boolean; }): number
|
|
477
|
+
```
|
|
435
478
|
|
|
436
|
-
|
|
479
|
+
#### `Param.string()` — Declare a string parameter that renders as a text input in the UI.
|
|
437
480
|
|
|
438
|
-
|
|
481
|
+
String parameters let users type free-form text — labels, names, inscriptions, file paths, etc. The `name` string is the override key.
|
|
482
|
+
|
|
483
|
+
```ts
|
|
484
|
+
const label = Param.string("Label", "Hello World");
|
|
485
|
+
const name = Param.string("Name", "Part-001", { maxLength: 20 });
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
Override via import:
|
|
489
|
+
|
|
490
|
+
```ts
|
|
491
|
+
const tag = require("./tag.forge.js", { Label: "Custom Text" });
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
Only available as `Param.string()` — no standalone alias.
|
|
495
|
+
|
|
496
|
+
```ts
|
|
497
|
+
Param.string(name: string, defaultValue: string, opts?: { maxLength?: number; }): string
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
#### `Param.bool()` — Declare a boolean parameter that renders as a checkbox in the UI.
|
|
439
501
|
|
|
440
|
-
|
|
502
|
+
Internally stored as `0`/`1`. When overriding from CLI or `require()`, pass `1` for true and `0` for false. The `name` string is the override key.
|
|
441
503
|
|
|
442
504
|
```ts
|
|
443
|
-
const showHoles =
|
|
505
|
+
const showHoles = Param.bool("Show Holes", true);
|
|
444
506
|
if (showHoles) return difference(plate, cylinder(10, 5).translate(50, 30, 0));
|
|
445
507
|
return plate;
|
|
446
508
|
```
|
|
@@ -451,20 +513,20 @@ Override via import:
|
|
|
451
513
|
const pan = require("./pan.forge.js", { "Show Lid": 0 });
|
|
452
514
|
```
|
|
453
515
|
|
|
454
|
-
|
|
516
|
+
Also available as the shorthand alias `boolParam()`.
|
|
455
517
|
|
|
456
|
-
|
|
518
|
+
```ts
|
|
519
|
+
Param.bool(name: string, defaultValue: boolean): boolean
|
|
520
|
+
```
|
|
457
521
|
|
|
458
|
-
|
|
522
|
+
#### `Param.choice()` — Declare a choice parameter that renders as a dropdown in the UI.
|
|
459
523
|
|
|
460
|
-
`defaultValue` must exactly match one entry in `choices`. Returns the selected string label. Prefer `
|
|
524
|
+
`defaultValue` must exactly match one entry in `choices`. Returns the selected string label. Prefer `Param.choice` over `Param.number` when a slider would hide intent — named choices like `"wok"` are self-describing.
|
|
461
525
|
|
|
462
526
|
Overrides may be passed as the choice label string (preferred) or as a numeric index. The `name` string is the override key.
|
|
463
527
|
|
|
464
|
-
**Example**
|
|
465
|
-
|
|
466
528
|
```ts
|
|
467
|
-
const panStyle =
|
|
529
|
+
const panStyle = Param.choice("Pan Style", "frying-pan", ["frying-pan", "saute-pan", "wok"]);
|
|
468
530
|
if (panStyle === "wok") return buildWok();
|
|
469
531
|
```
|
|
470
532
|
|
|
@@ -480,17 +542,25 @@ Override via CLI:
|
|
|
480
542
|
forgecad run model.forge.js --param "Pan Style=wok"
|
|
481
543
|
```
|
|
482
544
|
|
|
483
|
-
|
|
545
|
+
Also available as the shorthand alias `choiceParam()`.
|
|
484
546
|
|
|
485
|
-
|
|
547
|
+
```ts
|
|
548
|
+
Param.choice(name: string, defaultValue: string, choices: string[]): string
|
|
549
|
+
```
|
|
486
550
|
|
|
487
|
-
|
|
551
|
+
#### `Param.list()` — Declare a list parameter — an array of struct items with per-field UI controls.
|
|
488
552
|
|
|
489
553
|
Each item in the list is a struct whose fields each render as their own control (slider, checkbox, or dropdown). The user can add/remove rows up to `minItems`/`maxItems` bounds.
|
|
490
554
|
|
|
491
|
-
Field types:
|
|
555
|
+
Field types:
|
|
492
556
|
|
|
493
|
-
|
|
557
|
+
- Boolean fields (`boolean: true` in field defs) return as `boolean`
|
|
558
|
+
- Choice fields (`choices: [...]` in field defs) return as `string`
|
|
559
|
+
- All other fields return as `number`
|
|
560
|
+
|
|
561
|
+
```ts
|
|
562
|
+
Param.list<T extends Record<string, number | boolean | string>>(name: string, defaultItems: T[], opts: { ... }): T[]
|
|
563
|
+
```
|
|
494
564
|
|
|
495
565
|
`ListParamFieldDef`: `{ min?: number, max?: number, step?: number, unit?: string, integer?: boolean, boolean?: boolean, choices?: string[] }`
|
|
496
566
|
|
|
@@ -502,48 +572,60 @@ Unlike union(), colors and individual identities are preserved. Children can be
|
|
|
502
572
|
|
|
503
573
|
**Local coordinate pattern:** Build child parts at the origin (local coordinates), then group and translate once to place the whole assembly. This eliminates the error-prone pattern of manually adding parent offsets to every sub-part.
|
|
504
574
|
|
|
505
|
-
|
|
575
|
+
```js
|
|
576
|
+
// BAD — every sub-part repeats the parent's global offset
|
|
577
|
+
const unitX = 0, unitY = -18, unitZ = 70;
|
|
578
|
+
const body = roundedBox(100, 20, 32, 4).translate(unitX, unitY, unitZ);
|
|
579
|
+
const panel = box(98, 2, 18).translate(unitX, unitY - 12, unitZ + 4);
|
|
580
|
+
const louver = box(88, 2, 6).translate(unitX, unitY - 14, unitZ - 11);
|
|
581
|
+
```
|
|
506
582
|
|
|
507
583
|
// GOOD — build at origin, group, translate once const body = roundedBox(100, 20, 32, 4); const panel = box(98, 2, 18).translate(0, -12, 4); const louver = box(88, 2, 6).translate(0, -14, -11); const indoorUnit = group( { name: 'Body', shape: body }, { name: 'Panel', shape: panel }, { name: 'Louver', shape: louver }, ).translate(0, -18, 70);
|
|
508
584
|
|
|
509
|
-
|
|
585
|
+
```ts
|
|
586
|
+
group(...items: GroupInput[]): ShapeGroup
|
|
587
|
+
```
|
|
510
588
|
|
|
511
589
|
### Section & Projection
|
|
512
590
|
|
|
513
591
|
#### `intersectWithPlane()` — Cross-section: slice a 3D shape with a plane and return the intersection as a 2D Sketch.
|
|
514
592
|
|
|
515
|
-
|
|
593
|
+
```ts
|
|
594
|
+
intersectWithPlane(shape: Shape, plane: PlaneSpec): Sketch
|
|
595
|
+
```
|
|
516
596
|
|
|
517
597
|
#### `faceProfile()` — Extract the boundary profile of a named face as a 2D sketch.
|
|
518
598
|
|
|
519
599
|
The result is returned in the face's local 2D coordinate system, making it convenient for offsets, pocket profiles, or follow-up sketch operations driven by an existing face.
|
|
520
600
|
|
|
521
|
-
|
|
601
|
+
```ts
|
|
602
|
+
faceProfile(shape: Shape, face: FaceSelector): Sketch
|
|
603
|
+
```
|
|
522
604
|
|
|
523
605
|
#### `projectToPlane()` — Orthographically project a 3D shape onto a plane and return the silhouette as a 2D Sketch.
|
|
524
606
|
|
|
525
|
-
|
|
607
|
+
```ts
|
|
608
|
+
projectToPlane(shape: Shape, plane: PlaneSpec): Sketch
|
|
609
|
+
```
|
|
526
610
|
|
|
527
611
|
### Transforms
|
|
528
612
|
|
|
529
613
|
#### `composeChain()` — Compose transforms in chain order. Equivalent to Transform.identity().mul(a).mul(b).mul(c)...
|
|
530
614
|
|
|
531
|
-
|
|
615
|
+
```ts
|
|
616
|
+
composeChain(...steps: TransformInput[]): Transform
|
|
617
|
+
```
|
|
532
618
|
|
|
533
619
|
### Verification
|
|
534
620
|
|
|
535
621
|
#### `spec()` — Create a named, reusable bundle of verification checks.
|
|
536
622
|
|
|
537
|
-
**Details**
|
|
538
|
-
|
|
539
623
|
A spec groups related `verify.*` calls under a collapsible header in the Checks panel. This makes large check suites scannable. Specs can be applied to multiple shapes and can check relationships between parts.
|
|
540
624
|
|
|
541
625
|
Specs can be defined in separate `.forge.js` files and imported via `require()` to share them across models.
|
|
542
626
|
|
|
543
627
|
`spec.check()` returns a `SpecResult` — you can inspect it programmatically or ignore the return value and let the Checks panel show results.
|
|
544
628
|
|
|
545
|
-
**Example**
|
|
546
|
-
|
|
547
629
|
```ts
|
|
548
630
|
const printable = spec("Fits printer bed", (shape) => {
|
|
549
631
|
verify.notEmpty("Has geometry", shape);
|
|
@@ -566,7 +648,9 @@ fitSpec.check(bracket, standoff);
|
|
|
566
648
|
|
|
567
649
|
**Spec-first workflow:** Write specs before building geometry. Checks go from red to green as you build — effectively TDD for CAD.
|
|
568
650
|
|
|
569
|
-
|
|
651
|
+
```ts
|
|
652
|
+
spec(name: string, checkFn: (...args: any[]) => void): Spec
|
|
653
|
+
```
|
|
570
654
|
|
|
571
655
|
**`Spec`**
|
|
572
656
|
- `name: string` — The display name of this spec
|
|
@@ -587,138 +671,955 @@ Supports transforms (translate, rotate, scale, mirror, transform, rotateAround,
|
|
|
587
671
|
|----------|------|-------------|
|
|
588
672
|
| `materialProps` | `ShapeMaterialProps | undefined` | — |
|
|
589
673
|
|
|
590
|
-
**
|
|
591
|
-
|
|
592
|
-
- `color(value: string | undefined): Shape` — Set the color of this shape (hex string, e.g. "#ff0000"). Returns a new Shape with the color applied.
|
|
593
|
-
- `material(props: ShapeMaterialProps): Shape` — Set PBR material properties for this shape's visual appearance. **Details** Returns a new Shape with the specified material properties merged on top of any previously set properties. All properties are optional — omitted keys retain their current value. Material properties survive transforms and boolean operations. Use `.color()` to set the base diffuse color; `.material()` controls how that color behaves under light (metalness, roughness, clearcoat) and can add emissive glow independent of lighting. Emissive glow pairs naturally with the `postProcessing.bloom` effect in `scene()`. **Example** ```js box(50, 50, 50).material({ metalness: 0.9, roughness: 0.1 }); // polished metal sphere(30).material({ emissive: '#ff6b35', emissiveIntensity: 2 }); // glowing cylinder(40, 20).material({ opacity: 0.4, clearcoat: 1.0, clearcoatRoughness: 0.02 }); // ice // Chainable with other shape methods box(100, 100, 10).color('#gold').material({ metalness: 0.95, roughness: 0.05 }).translate(0, 0, 50); ```
|
|
594
|
-
- `clone(): Shape` — Return a new Shape wrapper for explicit duplication in scripts.
|
|
595
|
-
- `geometryInfo(): GeometryInfo` — Inspect which backend/representation produced this solid.
|
|
596
|
-
- `withReferences(refs: PlacementReferenceInput): Shape` — Attach named placement references that survive normal transforms and imports.
|
|
597
|
-
- `referenceNames(kind?: PlacementReferenceKind): string[]` — List named placement references carried by this shape.
|
|
598
|
-
- `withPorts(ports: Record<string, PortInput>): Shape` — Deprecated alias for `withConnectors()`.
|
|
599
|
-
- `portNames(): string[]` — Deprecated alias for `connectorNames()`.
|
|
600
|
-
- `referencePoint(ref: PlacementAnchorLike): [ number, number, number ]` — Resolve a named placement reference or built-in anchor to a 3D point.
|
|
601
|
-
- `face(selector: FaceSelector): FaceRef` — Resolve a face by user-authored label or compiler-owned name. Returns a `FaceRef` that can be passed to `.onFace()`, `projectToPlane()`, or used directly in placement. **Details** `.face(name)` is a pure label lookup — it finds faces by user-authored labels, not by geometric queries. Labels are born in sketches via `.label()` / `.labelEdges()` and grow into face names through extrude, loft, revolve, and sweep. They are stable references that travel with the geometry. Labels must be unique within a shape. Use `.prefixLabels()` before combining shapes with `union()` / `difference()` to avoid collisions. Collision detection throws a clear error with a fix suggestion. For compile-covered shapes (extrude, loft, etc.) the lookup resolves via the shape's compile plan. As a fallback, planar-faced mesh shapes (e.g. results of boolean ops) are resolved via coplanar triangle clustering. **Example** ```ts // Edge labels become side face names after extrude const profile = path() .moveTo(0, 0) .lineTo(100, 0).label('floor') .lineTo(100, 50).label('wall') .lineTo(0, 50).label('ceiling') .closeLabel('left-wall'); const room = profile.extrude(30, { labels: { start: 'base', end: 'top' } }); room.face('floor'); // side face from the labeled edge room.face('base'); // base cap (user-specified) // .labelEdges() shorthand for sequential edge labeling const plate = rect(100, 50).labelEdges('south', 'east', 'north', 'west'); const solid = plate.extrude(20, { labels: { start: 'bottom', end: 'top' } }); solid.face('south'); // side face // Prefix before combining to avoid collisions const left = wing.prefixLabels('l/'); const right = wing.mirror([1, 0, 0]).prefixLabels('r/'); const full = union(left, right); full.face('l/upper'); // left wing upper surface ```
|
|
602
|
-
- `faces(query?: FaceQuery): FaceRef[]` — Return all faces matching a query, or all mesh-detected faces when no query is given.
|
|
603
|
-
- `faceNames(): string[]` — List defended semantic face names currently available on this shape, including user labels from `labelFaces()`.
|
|
604
|
-
- `prefixLabels(prefix: string): Shape` — Prefix all user-authored face labels (both sketch-edge labels and labelFaces labels). Returns a new shape with modified labels.
|
|
605
|
-
- `renameLabel(from: string, to: string): Shape` — Rename a single face label. Returns a new shape.
|
|
606
|
-
- `dropLabels(...names: string[]): Shape` — Remove specific face labels. Returns a new shape.
|
|
607
|
-
- `dropAllLabels(): Shape` — Remove all face labels. Returns a new shape.
|
|
608
|
-
- `edge(name: string): EdgeRef` — Get a named topology edge. Only available on shapes with tracked topology (from box/cylinder/extrude).
|
|
609
|
-
- `edgeNames(): string[]` — List named topology edge names. Returns empty array if shape has no tracked topology.
|
|
610
|
-
- `labelFaces(mapping: Record<string, string>): Shape` — Assign user-chosen labels to faces identified by their canonical position keys. **Details** Primitives (`box`, `cylinder`) and extrusions have internal canonical face positions (`top`, `bottom`, `side`, `side-left`, etc.) but these are **not** labels — they are just position selectors. Use `labelFaces()` to give faces meaningful, project-specific names that survive through transforms, booleans, fillets, and chamfers. The mapping keys are canonical position selectors; the values are your labels. Labels are the recommended way to identify faces for topological edge queries (`edgesOf`, `edgesBetween`). **Example** ```js // Full workflow: label → query edges → fillet let plate = box(100, 60, 5).labelFaces({ top: 'work-surface', bottom: 'mount-face' }) plate = fillet(plate, 2, plate.edgesOf('work-surface')) // Cylinder: fillet the rim where cap meets barrel let tube = cylinder(30, 10).labelFaces({ top: 'cap', side: 'barrel' }) tube = fillet(tube, 1, tube.edgesBetween('cap', 'barrel')) // Prefix before combining shapes to avoid label collisions const left = plate.prefixLabels('l/') const right = plate.mirror([1, 0, 0]).prefixLabels('r/') const full = union(left, right) full.edgesOf('l/work-surface') // still works ```
|
|
611
|
-
- `edgesOf(faceLabel: string, options?: EdgesOfOptions): EdgeSegment[]` — Return all boundary edges of a named face. **Details** Finds edges where one adjacent mesh face belongs to the target face and the other belongs to a different face. The result is coalesced (tessellation fragments merged) and can be passed directly to `fillet()` or `chamfer()`. This is a topological query — no coordinates, no tolerances, no minimum-length hacks. It works because an edge is the boundary between two faces. **Example** ```js // Fillet all top edges of a mounting plate let plate = box(120, 80, 6).labelFaces({ top: 'work-surface' }) plate = fillet(plate, 3, plate.edgesOf('work-surface')) // Shelled enclosure — fillet the outer lip let body = box(80, 50, 35).labelFaces({ top: 'opening' }) body = body.shell(2, { openFaces: ['top'] }) body = fillet(body, 1.5, body.edgesOf('opening')) // Filter: only concave edges (after a boolean subtraction) body.edgesOf('top', { concave: true }) ```
|
|
612
|
-
- `edgesBetween(faceA: string, faceB: string | string[]): EdgeSegment[]` — Return edges shared between two named faces. **Details** An edge is "between" faces A and B when one of its adjacent mesh triangles belongs to A and the other belongs to B. This is the most precise topological edge selection — "fillet the edges where the top meets the wall." The second argument can be a single face name or an array (edges between A and any of B1, B2, ...). **Example** ```js // Fillet the edge where lid meets one wall let body = box(100, 60, 30).labelFaces({ top: 'lid', 'side-left': 'wall' }) body = fillet(body, 2, body.edgesBetween('lid', 'wall')) // Fillet a cylinder rim — where the flat cap meets the curved barrel let tube = cylinder(30, 10).labelFaces({ top: 'cap', side: 'barrel' }) tube = fillet(tube, 1, tube.edgesBetween('cap', 'barrel')) // Multiple target faces at once body.edgesBetween('lid', ['left-wall', 'right-wall', 'front-wall', 'back-wall']) ```
|
|
613
|
-
- `faceHistory(name: string): FaceTransformationHistory` — Get the transformation history for a specific face.
|
|
614
|
-
- `placeReference(ref: PlacementAnchorLike, target: [ number, number, number ], offset?: [ number, number, number ]): Shape` — Translate the shape so the given anchor or reference lands on the target coordinate. Accepts any built-in anchor name (`'bottom'`, `'center'`, `'top-front-left'`, etc.) or a custom placement reference attached via `withReferences()`. ```javascript // Ground a shape — put its bottom face center at Z = 0 shape.placeReference('bottom', [0, 0, 0]) // Center at the world origin shape.placeReference('center', [0, 0, 0]) // Align left edge to X = 10 shape.placeReference('left', [10, 0, 0]) ```
|
|
615
|
-
- `translatePolar(radius: number, angleDeg: number, z?: number): Shape` — Translate using polar coordinates (radius + angle in degrees). Eliminates manual `r * Math.cos(angle * PI/180)` calculations. Example: `shape.translatePolar(50, 30)` moves 50mm at 30 degrees from +X.
|
|
616
|
-
- `translate(x: number, y: number, z: number): Shape` — Move the shape relative to its current position. All transforms are immutable and return new shapes.
|
|
617
|
-
- `moveTo(x: number, y: number, z: number): Shape` — Position the shape so its bounding box min corner is at the given global coordinate.
|
|
618
|
-
- `moveToLocal(target: Shape | { toShape(): Shape; }, x: number, y: number, z: number): Shape` — Position the shape relative to another shape's local coordinate system (bounding box min corner).
|
|
619
|
-
- `rotate(axis: [ number, number, number ], angleDeg: number, options?: { pivot?: [ number, number, number ]; }): Shape` — Rotate around an arbitrary axis through the origin.
|
|
620
|
-
- `rotateX(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): Shape` — Rotate around the X axis by the given angle in degrees.
|
|
621
|
-
- `rotateY(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): Shape` — Rotate around the Y axis by the given angle in degrees.
|
|
622
|
-
- `rotateZ(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): Shape` — Rotate around the Z axis by the given angle in degrees.
|
|
623
|
-
- `transform(m: Mat4 | Transform): Shape` — Apply a 4x4 affine transform matrix (column-major) or a Transform object.
|
|
624
|
-
- `scale(v: number | [ number, number, number ]): Shape` — Scale the shape uniformly or per-axis from the shape's bounding box center. Accepts a single number or [x, y, z] array.
|
|
625
|
-
- `scaleAround(pivot: [ number, number, number ], v: number | [ number, number, number ]): Shape` — Scale the shape uniformly or per-axis from an explicit pivot point.
|
|
626
|
-
- `mirror(normal: [ number, number, number ]): Shape` — Mirror across a plane through the shape's bounding box center, defined by its normal vector.
|
|
627
|
-
- `mirrorThrough(point: [ number, number, number ], normal: [ number, number, number ]): Shape` — Mirror across a plane through an explicit point, defined by its normal vector.
|
|
628
|
-
- `pointAlong(direction: [ number, number, number ]): Shape` — Reorient a shape so its primary axis (Z) points along the given direction. Useful for laying cylinders/extrusions along X or Y without thinking about Euler angles. The shape's origin stays at [0,0,0] — translate after pointAlong to position it. Example: cylinder(40, 5).pointAlong([1, 0, 0]) — lays cylinder along X, starting at origin
|
|
629
|
-
- `rotateAroundTo(axis: [ number, number, number ], pivot: [ number, number, number ], movingPoint: RotationPointLike, targetPoint: RotationPointLike, options?: RotateAroundToOptions): Shape` — Rotate around an axis until a moving point reaches the target line/plane defined by the axis and target point. `movingPoint` / `targetPoint` may be raw world points or this shape's anchors/references.
|
|
630
|
-
- `add(...others: ShapeOperandInput[]): Shape` — Union this shape with others (additive boolean). Method form of union().
|
|
631
|
-
- `subtract(...others: ShapeOperandInput[]): Shape` — Subtract other shapes from this one. Method form of difference().
|
|
632
|
-
- `intersect(...others: ShapeOperandInput[]): Shape` — Keep only the overlap with other shapes. Method form of intersection().
|
|
633
|
-
- `split(cutter: Shape | { toShape(): Shape; }): [ Shape, Shape ]` — Split into [inside, outside] by another shape.
|
|
634
|
-
- `splitByPlane(normal: [ number, number, number ], originOffset?: number): [ Shape, Shape ]` — Split by infinite plane. Returns [positive-side, negative-side].
|
|
635
|
-
- `trimByPlane(normal: [ number, number, number ], originOffset?: number): Shape` — Keep the positive side of the plane and discard the opposite side.
|
|
636
|
-
- `shell(thickness: number, opts?: { openFaces?: string[]; }): Shape` — Hollow out compile-covered boxes, cylinders, and straight extrudes. `openFaces` names any subset of the base shape's labeled faces to leave open (no wall).
|
|
637
|
-
- `boundingBox(): ShapeRuntimeBounds` — Get the axis-aligned bounding box as { min: [x,y,z], max: [x,y,z] }.
|
|
638
|
-
- `volume(): number` — Volume in mm cubed.
|
|
639
|
-
- `surfaceArea(): number` — Surface area in mm squared.
|
|
640
|
-
- `isEmpty(): boolean` — True if the shape contains no geometry.
|
|
641
|
-
- `numBodies(): number` — Number of disconnected solid bodies in this shape.
|
|
642
|
-
- `numTri(): number` — Triangle count of the mesh representation.
|
|
643
|
-
- `getMesh(): ShapeRuntimeMesh` — Extract triangle mesh for Three.js rendering
|
|
644
|
-
- `slice(offset?: number): any` — Slice the runtime solid by a plane normal to local Z at the given offset.
|
|
645
|
-
- `project(): any` — Orthographically project the runtime solid onto the local XY plane.
|
|
646
|
-
- `attachTo(target: ShapeAnchorTarget, targetAnchor: PlacementAnchorLike, selfAnchor?: PlacementAnchorLike, offset?: [ number, number, number ]): Shape` — Position this shape relative to another using named 3D anchor points. Anchors are bounding-box-relative: 'center', face centers ('top', 'front', ...), edge midpoints ('top-front', 'back-left', ...), and corners ('top-front-left', ...). Anchor word order is flexible: 'front-left' and 'left-front' are equivalent. Named placement references (from withReferences) can also be used as anchors.
|
|
647
|
-
- `onFace(parent: ShapeAnchorTarget, face: "front" | "back" | "left" | "right" | "top" | "bottom", opts?: { u?: number; v?: number; protrude?: number; }): Shape` — Place this shape on a face of a parent shape. Think of it like sticking a label on a box surface: - `face` picks which surface ('front', 'back', 'top', etc.) - `u, v` position within that face's 2D plane (from center) - front/back: u = left/right (X), v = up/down (Z) - left/right: u = forward/back (Y), v = up/down (Z) - top/bottom: u = left/right (X), v = forward/back (Y) - `protrude` = how far the child sticks out (positive = outward from face)
|
|
648
|
-
- `seatInto(target: Shape, surface: string, options?: SeatIntoOptions): Shape` — Slide this shape along an axis until a labeled face is embedded in the target body. Position the shape roughly first (translate/rotate), then call seatInto to auto-adjust the penetration depth. No manual coordinate math needed. ```js // Wing root embeds into fuselage — adapts to any fuselage shape wing.translate(0, wingY, 0).seatInto(fuselage, 'root'); // Sensor pod sits flush on fuselage surface pod.translate(0, station, radius + 20).seatInto(fuselage, 'base', { depth: 'flush' }); // Antenna with 3mm gasket standoff mast.translate(0, station, radius + 50).seatInto(fuselage, 'mount', { depth: 'flush', gap: 3 }); ```
|
|
649
|
-
- `seatOver(target: Shape, targetSurface: string, options?: SeatIntoOptions): Shape` — Slide this shape until a target's labeled face is fully covered (inside this shape). The inverse of `seatInto`: instead of embedding *your* face into the target, you move until the *target's* face is embedded inside you. ```js // Nacelle moves up until pylon's bottom face is inside the nacelle nacelle.translate(rough).seatOver(pylon, 'bottom'); // Cap slides down over a post until post's top face is covered cap.translate(rough).seatOver(post, 'top'); ```
|
|
650
|
-
- `withConnectors(connectors: Record<string, ConnectorInput>): Shape` — Attach named connectors — attachment points that survive transforms and imports. Connectors can be bare (position + orientation) or typed (with connectorType/gender for compatibility matching).
|
|
651
|
-
- `connectorNames(): string[]` — List all connector names on this shape.
|
|
652
|
-
- `connectorsByType(type: string): Array<{ name: string; port: PortDef; }>` — Get all connectors of a given type.
|
|
653
|
-
- `connectorDistance(nameA: string, nameB: string): number` — Distance between two connector origins on this shape.
|
|
654
|
-
- `connectorMeasurements(name: string): Record<string, number | string>` — Get measurements metadata from a connector.
|
|
655
|
-
- `matchTo(targetOrPairs: Shape | MatchTarget | Array<[ Shape | MatchTarget, string, string ]>, selfConnOrDict?: string | Record<string, string>, targetConnOrOptions?: string | MatchToOptions, maybeOptions?: MatchToOptions): Shape` — Position this shape by matching connectors to a target. Overloads: - Single pair: `matchTo(target, selfConn, targetConn, options?)` - Dictionary (same target): `matchTo(target, { selfConn: targetConn, ... }, options?)` - Multi-target: `matchTo([ [target1, selfConn1, targetConn1], ... ], options?)`
|
|
656
|
-
- `pocket(face: FaceSelector, depth: number, opts?: PocketOptions): Shape` — Cut a pocket (cavity) into this solid through the named face. box(100, 100, 20).pocket('top', 8) box(100, 100, 20).pocket('top', 8, { inset: 5 }) box(100, 100, 20).pocket('top', 8, { scale: 0.8 })
|
|
657
|
-
- `boss(face: FaceSelector, height: number, opts?: BossOptions): Shape` — Add a boss (protrusion) from the named face. box(100, 100, 20).boss('top', 5) box(100, 100, 20).boss('top', 10, { scale: 0.6 })
|
|
658
|
-
- `hole(faceOrRef: SketchFaceTarget | FaceRef, opts: ShapeHoleOptions): Shape` — Drill a hole into this solid at a face. box(50, 50, 20).hole('top', { diameter: 8, depth: 10 }) box(50, 50, 20).hole('top', { diameter: 6, counterbore: { diameter: 12, depth: 3 } })
|
|
659
|
-
- `cutout(sketch: Sketch, opts?: ShapeCutoutOptions): Shape` — Cut a profile-shaped pocket through a face using a placed sketch. The sketch must be placed on a face with `Sketch.onFace(...)`. The cut follows the sketch's 2D profile. const profile = circle2d(10).onFace(body, 'top'); body.cutout(profile, { depth: 5 })
|
|
674
|
+
**Appearance**
|
|
660
675
|
|
|
661
|
-
|
|
676
|
+
#### `color()` — Set the color of this shape (hex string, e.g. "#ff0000"). Returns a new Shape with the color applied.
|
|
662
677
|
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
- `static scale(v: number | Vec3): Transform` — Create a uniform or per-axis scale transform.
|
|
667
|
-
- `static rotationAxis(axis: Vec3, angleDeg: number, pivot?: Vec3): Transform` — Create a rotation around an arbitrary axis, optionally about a pivot.
|
|
668
|
-
- `static rotateAroundTo(axis: Vec3, pivot: Vec3, movingPoint: Vec3, targetPoint: Vec3, options?: RotateAroundToOptions): Transform` — Solve the rotation needed to move one point onto a target line or plane.
|
|
669
|
-
- `mul(other: TransformInput): Transform` — Compose transforms in chain order: `a.mul(b)` applies `a`, then `b`.
|
|
670
|
-
- `translate(x: number, y: number, z: number): Transform` — Translate after the current transform.
|
|
671
|
-
- `rotateAxis(axis: Vec3, angleDeg: number, pivot?: Vec3): Transform` — Rotate after the current transform.
|
|
672
|
-
- `inverse(): Transform` — Return the inverse transform.
|
|
673
|
-
- `point(p: Vec3): Vec3` — Transform a point using homogeneous coordinates.
|
|
674
|
-
- `vector(v: Vec3): Vec3` — Transform a direction vector without translation.
|
|
675
|
-
- `toArray(): Mat4` — Return the transform as a raw 4x4 matrix array.
|
|
678
|
+
```ts
|
|
679
|
+
color(value: string | undefined): Shape
|
|
680
|
+
```
|
|
676
681
|
|
|
677
|
-
|
|
682
|
+
#### `material()` — Set PBR material properties for this shape's visual appearance.
|
|
678
683
|
|
|
679
|
-
|
|
684
|
+
Returns a new Shape with the specified material properties merged on top of any previously set properties. All properties are optional — omitted keys retain their current value. Material properties survive transforms and boolean operations.
|
|
680
685
|
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
686
|
+
Use `.color()` to set the base diffuse color; `.material()` controls how that color behaves under light (metalness, roughness, clearcoat) and can add emissive glow independent of lighting. Emissive glow pairs naturally with the `postProcessing.bloom` effect in [`scene()`](/docs/viewport#scene).
|
|
687
|
+
|
|
688
|
+
```js
|
|
689
|
+
box(50, 50, 50).material({ metalness: 0.9, roughness: 0.1 }); // polished metal
|
|
690
|
+
sphere(30).material({ emissive: '#ff6b35', emissiveIntensity: 2 }); // glowing
|
|
691
|
+
cylinder(40, 20).material({ opacity: 0.4, clearcoat: 1.0, clearcoatRoughness: 0.02 }); // ice
|
|
692
|
+
|
|
693
|
+
// Chainable with other shape methods
|
|
694
|
+
box(100, 100, 10).color('#gold').material({ metalness: 0.95, roughness: 0.05 }).translate(0, 0, 50);
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
```ts
|
|
698
|
+
material(props: ShapeMaterialProps): Shape
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
**Face Topology**
|
|
702
|
+
|
|
703
|
+
#### `face()` — Resolve a face by user-authored label or compiler-owned name. Returns a `FaceRef` that can be passed to `.onFace()`, `projectToPlane()`, or used directly in placement.
|
|
704
|
+
|
|
705
|
+
`.face(name)` is a pure label lookup — it finds faces by user-authored labels, not by geometric queries. Labels are born in sketches via `.label()` / `.labelEdges()` and grow into face names through extrude, loft, revolve, and sweep. They are stable references that travel with the geometry.
|
|
706
|
+
|
|
707
|
+
Labels must be unique within a shape. Use `.prefixLabels()` before combining shapes with `union()` / `difference()` to avoid collisions. Collision detection throws a clear error with a fix suggestion.
|
|
708
|
+
|
|
709
|
+
For compile-covered shapes (extrude, loft, etc.) the lookup resolves via the shape's compile plan. As a fallback, planar-faced mesh shapes (e.g. results of boolean ops) are resolved via coplanar triangle clustering.
|
|
710
|
+
|
|
711
|
+
```ts
|
|
712
|
+
// Edge labels become side face names after extrude
|
|
713
|
+
const profile = path()
|
|
714
|
+
.moveTo(0, 0)
|
|
715
|
+
.lineTo(100, 0).label('floor')
|
|
716
|
+
.lineTo(100, 50).label('wall')
|
|
717
|
+
.lineTo(0, 50).label('ceiling')
|
|
718
|
+
.closeLabel('left-wall');
|
|
719
|
+
const room = profile.extrude(30, { labels: { start: 'base', end: 'top' } });
|
|
720
|
+
room.face('floor'); // side face from the labeled edge
|
|
721
|
+
room.face('base'); // base cap (user-specified)
|
|
722
|
+
|
|
723
|
+
// .labelEdges() shorthand for sequential edge labeling
|
|
724
|
+
const plate = rect(100, 50).labelEdges('south', 'east', 'north', 'west');
|
|
725
|
+
const solid = plate.extrude(20, { labels: { start: 'bottom', end: 'top' } });
|
|
726
|
+
solid.face('south'); // side face
|
|
727
|
+
|
|
728
|
+
// Prefix before combining to avoid collisions
|
|
729
|
+
const left = wing.prefixLabels('l/');
|
|
730
|
+
const right = wing.mirror([1, 0, 0]).prefixLabels('r/');
|
|
731
|
+
const full = union(left, right);
|
|
732
|
+
full.face('l/upper'); // left wing upper surface
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
```ts
|
|
736
|
+
face(selector: FaceSelector): FaceRef
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
#### `faces()` — Return all faces matching a query, or all mesh-detected faces when no query is given.
|
|
740
|
+
|
|
741
|
+
```ts
|
|
742
|
+
faces(query?: FaceQuery): FaceRef[]
|
|
743
|
+
```
|
|
744
|
+
|
|
745
|
+
#### `faceNames()` — List defended semantic face names currently available on this shape, including user labels from `labelFaces()`.
|
|
746
|
+
|
|
747
|
+
```ts
|
|
748
|
+
faceNames(): string[]
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
#### `labelFaces()` — Assign user-chosen labels to faces identified by their canonical position keys.
|
|
752
|
+
|
|
753
|
+
Primitives (`box`, `cylinder`) and extrusions have internal canonical face positions (`top`, `bottom`, `side`, `side-left`, etc.) but these are **not** labels — they are just position selectors. Use `labelFaces()` to give faces meaningful, project-specific names that survive through transforms, booleans, fillets, and chamfers.
|
|
754
|
+
|
|
755
|
+
The mapping keys are canonical position selectors; the values are your labels. Labels are the recommended way to identify faces for topological edge queries (`edgesOf`, `edgesBetween`).
|
|
756
|
+
|
|
757
|
+
```js
|
|
758
|
+
// Full workflow: label → query edges → fillet
|
|
759
|
+
let plate = box(100, 60, 5).labelFaces({ top: 'work-surface', bottom: 'mount-face' })
|
|
760
|
+
plate = fillet(plate, 2, plate.edgesOf('work-surface'))
|
|
761
|
+
|
|
762
|
+
// Cylinder: fillet the rim where cap meets barrel
|
|
763
|
+
let tube = cylinder(30, 10).labelFaces({ top: 'cap', side: 'barrel' })
|
|
764
|
+
tube = fillet(tube, 1, tube.edgesBetween('cap', 'barrel'))
|
|
765
|
+
|
|
766
|
+
// Prefix before combining shapes to avoid label collisions
|
|
767
|
+
const left = plate.prefixLabels('l/')
|
|
768
|
+
const right = plate.mirror([1, 0, 0]).prefixLabels('r/')
|
|
769
|
+
const full = union(left, right)
|
|
770
|
+
full.edgesOf('l/work-surface') // still works
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
```ts
|
|
774
|
+
labelFaces(mapping: Record<string, string>): Shape
|
|
775
|
+
```
|
|
776
|
+
|
|
777
|
+
#### `prefixLabels()` — Prefix all user-authored face labels (both sketch-edge labels and labelFaces labels). Returns a new shape with modified labels.
|
|
778
|
+
|
|
779
|
+
```ts
|
|
780
|
+
prefixLabels(prefix: string): Shape
|
|
781
|
+
```
|
|
782
|
+
|
|
783
|
+
#### `renameLabel()` — Rename a single face label. Returns a new shape.
|
|
784
|
+
|
|
785
|
+
```ts
|
|
786
|
+
renameLabel(from: string, to: string): Shape
|
|
787
|
+
```
|
|
788
|
+
|
|
789
|
+
#### `dropLabels()` — Remove specific face labels. Returns a new shape.
|
|
790
|
+
|
|
791
|
+
```ts
|
|
792
|
+
dropLabels(...names: string[]): Shape
|
|
793
|
+
```
|
|
794
|
+
|
|
795
|
+
#### `dropAllLabels()` — Remove all face labels. Returns a new shape.
|
|
796
|
+
|
|
797
|
+
```ts
|
|
798
|
+
dropAllLabels(): Shape
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
#### `faceHistory()` — Get the transformation history for a specific face.
|
|
802
|
+
|
|
803
|
+
```ts
|
|
804
|
+
faceHistory(name: string): FaceTransformationHistory
|
|
805
|
+
```
|
|
806
|
+
|
|
807
|
+
**Edge Topology**
|
|
808
|
+
|
|
809
|
+
#### `edge()` — Get a named topology edge. Only available on shapes with tracked topology (from box/cylinder/extrude).
|
|
810
|
+
|
|
811
|
+
```ts
|
|
812
|
+
edge(name: string): EdgeRef
|
|
813
|
+
```
|
|
814
|
+
|
|
815
|
+
#### `edgeNames()` — List named topology edge names. Returns empty array if shape has no tracked topology.
|
|
816
|
+
|
|
817
|
+
```ts
|
|
818
|
+
edgeNames(): string[]
|
|
819
|
+
```
|
|
820
|
+
|
|
821
|
+
#### `edgesOf()` — Return all boundary edges of a named face.
|
|
822
|
+
|
|
823
|
+
Finds edges where one adjacent mesh face belongs to the target face and the other belongs to a different face. The result is coalesced (tessellation fragments merged) and can be passed directly to `fillet()` or `chamfer()`.
|
|
824
|
+
|
|
825
|
+
This is a topological query — no coordinates, no tolerances, no minimum-length hacks. It works because an edge is the boundary between two faces.
|
|
826
|
+
|
|
827
|
+
```js
|
|
828
|
+
// Fillet all top edges of a mounting plate
|
|
829
|
+
let plate = box(120, 80, 6).labelFaces({ top: 'work-surface' })
|
|
830
|
+
plate = fillet(plate, 3, plate.edgesOf('work-surface'))
|
|
831
|
+
|
|
832
|
+
// Shelled enclosure — fillet the outer lip
|
|
833
|
+
let body = box(80, 50, 35).labelFaces({ top: 'opening' })
|
|
834
|
+
body = body.shell(2, { openFaces: ['top'] })
|
|
835
|
+
body = fillet(body, 1.5, body.edgesOf('opening'))
|
|
836
|
+
|
|
837
|
+
// Filter: only concave edges (after a boolean subtraction)
|
|
838
|
+
body.edgesOf('top', { concave: true })
|
|
839
|
+
```
|
|
840
|
+
|
|
841
|
+
```ts
|
|
842
|
+
edgesOf(faceLabel: string, options?: EdgesOfOptions): EdgeSegment[]
|
|
843
|
+
```
|
|
844
|
+
|
|
845
|
+
#### `edgesBetween()` — Return edges shared between two named faces.
|
|
846
|
+
|
|
847
|
+
An edge is "between" faces A and B when one of its adjacent mesh triangles belongs to A and the other belongs to B. This is the most precise topological edge selection — "fillet the edges where the top meets the wall."
|
|
848
|
+
|
|
849
|
+
The second argument can be a single face name or an array (edges between A and any of B1, B2, ...).
|
|
685
850
|
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
851
|
+
```js
|
|
852
|
+
// Fillet the edge where lid meets one wall
|
|
853
|
+
let body = box(100, 60, 30).labelFaces({ top: 'lid', 'side-left': 'wall' })
|
|
854
|
+
body = fillet(body, 2, body.edgesBetween('lid', 'wall'))
|
|
855
|
+
|
|
856
|
+
// Fillet a cylinder rim — where the flat cap meets the curved barrel
|
|
857
|
+
let tube = cylinder(30, 10).labelFaces({ top: 'cap', side: 'barrel' })
|
|
858
|
+
tube = fillet(tube, 1, tube.edgesBetween('cap', 'barrel'))
|
|
859
|
+
|
|
860
|
+
// Multiple target faces at once
|
|
861
|
+
body.edgesBetween('lid', ['left-wall', 'right-wall', 'front-wall', 'back-wall'])
|
|
862
|
+
```
|
|
863
|
+
|
|
864
|
+
```ts
|
|
865
|
+
edgesBetween(faceA: string, faceB: string | string[]): EdgeSegment[]
|
|
866
|
+
```
|
|
867
|
+
|
|
868
|
+
**Transforms**
|
|
869
|
+
|
|
870
|
+
#### `translate()` — Move the shape relative to its current position. All transforms are immutable and return new shapes.
|
|
871
|
+
|
|
872
|
+
```ts
|
|
873
|
+
translate(x: number, y: number, z: number): Shape
|
|
874
|
+
```
|
|
875
|
+
|
|
876
|
+
#### `translatePolar()` — Translate using polar coordinates (radius + angle in degrees). Eliminates manual `r * Math.cos(angle * PI/180)` calculations.
|
|
877
|
+
|
|
878
|
+
Example: `shape.translatePolar(50, 30)` moves 50mm at 30 degrees from +X.
|
|
879
|
+
|
|
880
|
+
```ts
|
|
881
|
+
translatePolar(radius: number, angleDeg: number, z?: number): Shape
|
|
882
|
+
```
|
|
883
|
+
|
|
884
|
+
#### `moveTo()` — Position the shape so its bounding box min corner is at the given global coordinate.
|
|
885
|
+
|
|
886
|
+
```ts
|
|
887
|
+
moveTo(x: number, y: number, z: number): Shape
|
|
888
|
+
```
|
|
889
|
+
|
|
890
|
+
#### `moveToLocal()` — Position the shape relative to another shape's local coordinate system (bounding box min corner).
|
|
891
|
+
|
|
892
|
+
```ts
|
|
893
|
+
moveToLocal(target: Shape | { toShape(): Shape; }, x: number, y: number, z: number): Shape
|
|
894
|
+
```
|
|
895
|
+
|
|
896
|
+
#### `rotate()` — Rotate around an arbitrary axis through the origin.
|
|
897
|
+
|
|
898
|
+
```ts
|
|
899
|
+
rotate(axis: [ number, number, number ], angleDeg: number, options?: { pivot?: [ number, number, number ]; }): Shape
|
|
900
|
+
```
|
|
901
|
+
|
|
902
|
+
#### `rotateX()` — Rotate around the X axis by the given angle in degrees.
|
|
903
|
+
|
|
904
|
+
```ts
|
|
905
|
+
rotateX(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): Shape
|
|
906
|
+
```
|
|
907
|
+
|
|
908
|
+
#### `rotateY()` — Rotate around the Y axis by the given angle in degrees.
|
|
909
|
+
|
|
910
|
+
```ts
|
|
911
|
+
rotateY(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): Shape
|
|
912
|
+
```
|
|
913
|
+
|
|
914
|
+
#### `rotateZ()` — Rotate around the Z axis by the given angle in degrees.
|
|
915
|
+
|
|
916
|
+
```ts
|
|
917
|
+
rotateZ(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): Shape
|
|
918
|
+
```
|
|
919
|
+
|
|
920
|
+
#### `rotateAroundTo()` — Rotate around an axis until a moving point reaches the target line/plane defined by the axis and target point. `movingPoint` / `targetPoint` may be raw world points or this shape's anchors/references.
|
|
921
|
+
|
|
922
|
+
```ts
|
|
923
|
+
rotateAroundTo(axis: [ number, number, number ], pivot: [ number, number, number ], movingPoint: RotationPointLike, targetPoint: RotationPointLike, options?: RotateAroundToOptions): Shape
|
|
924
|
+
```
|
|
925
|
+
|
|
926
|
+
#### `transform()` — Apply a 4x4 affine transform matrix (column-major) or a Transform object.
|
|
927
|
+
|
|
928
|
+
```ts
|
|
929
|
+
transform(m: Mat4 | Transform): Shape
|
|
930
|
+
```
|
|
931
|
+
|
|
932
|
+
#### `scale()` — Scale the shape uniformly or per-axis from the shape's bounding box center. Accepts a single number or [x, y, z] array.
|
|
933
|
+
|
|
934
|
+
```ts
|
|
935
|
+
scale(v: number | [ number, number, number ]): Shape
|
|
936
|
+
```
|
|
937
|
+
|
|
938
|
+
#### `scaleAround()` — Scale the shape uniformly or per-axis from an explicit pivot point.
|
|
939
|
+
|
|
940
|
+
```ts
|
|
941
|
+
scaleAround(pivot: [ number, number, number ], v: number | [ number, number, number ]): Shape
|
|
942
|
+
```
|
|
943
|
+
|
|
944
|
+
#### `mirror()` — Mirror across a plane through the shape's bounding box center, defined by its normal vector.
|
|
945
|
+
|
|
946
|
+
```ts
|
|
947
|
+
mirror(normal: [ number, number, number ]): Shape
|
|
948
|
+
```
|
|
949
|
+
|
|
950
|
+
#### `mirrorThrough()` — Mirror across a plane through an explicit point, defined by its normal vector.
|
|
951
|
+
|
|
952
|
+
```ts
|
|
953
|
+
mirrorThrough(point: [ number, number, number ], normal: [ number, number, number ]): Shape
|
|
954
|
+
```
|
|
955
|
+
|
|
956
|
+
#### `pointAlong()` — Reorient a shape so its primary axis (Z) points along the given direction. Useful for laying cylinders/extrusions along X or Y without thinking about Euler angles. The shape's origin stays at [0,0,0] — translate after pointAlong to position it.
|
|
957
|
+
|
|
958
|
+
Example: cylinder(40, 5).pointAlong([1, 0, 0]) — lays cylinder along X, starting at origin
|
|
959
|
+
|
|
960
|
+
```ts
|
|
961
|
+
pointAlong(direction: [ number, number, number ]): Shape
|
|
962
|
+
```
|
|
963
|
+
|
|
964
|
+
**Booleans & Cutting**
|
|
965
|
+
|
|
966
|
+
#### `add()` — Union this shape with others (additive boolean). Method form of union().
|
|
967
|
+
|
|
968
|
+
```ts
|
|
969
|
+
add(...others: ShapeOperandInput[]): Shape
|
|
970
|
+
```
|
|
971
|
+
|
|
972
|
+
#### `subtract()` — Subtract other shapes from this one. Method form of difference().
|
|
973
|
+
|
|
974
|
+
```ts
|
|
975
|
+
subtract(...others: ShapeOperandInput[]): Shape
|
|
976
|
+
```
|
|
977
|
+
|
|
978
|
+
#### `intersect()` — Keep only the overlap with other shapes. Method form of intersection().
|
|
979
|
+
|
|
980
|
+
```ts
|
|
981
|
+
intersect(...others: ShapeOperandInput[]): Shape
|
|
982
|
+
```
|
|
983
|
+
|
|
984
|
+
#### `split()` — Split into [inside, outside] by another shape.
|
|
985
|
+
|
|
986
|
+
```ts
|
|
987
|
+
split(cutter: Shape | { toShape(): Shape; }): [ Shape, Shape ]
|
|
988
|
+
```
|
|
989
|
+
|
|
990
|
+
#### `splitByPlane()` — Split by infinite plane. Returns [positive-side, negative-side].
|
|
991
|
+
|
|
992
|
+
```ts
|
|
993
|
+
splitByPlane(normal: [ number, number, number ], originOffset?: number): [ Shape, Shape ]
|
|
994
|
+
```
|
|
995
|
+
|
|
996
|
+
#### `trimByPlane()` — Keep the positive side of the plane and discard the opposite side.
|
|
997
|
+
|
|
998
|
+
```ts
|
|
999
|
+
trimByPlane(normal: [ number, number, number ], originOffset?: number): Shape
|
|
1000
|
+
```
|
|
1001
|
+
|
|
1002
|
+
**Features**
|
|
1003
|
+
|
|
1004
|
+
#### `shell()` — Hollow out compile-covered boxes, cylinders, and straight extrudes. `openFaces` names any subset of the base shape's labeled faces to leave open (no wall).
|
|
1005
|
+
|
|
1006
|
+
```ts
|
|
1007
|
+
shell(thickness: number, opts?: { openFaces?: string[]; }): Shape
|
|
1008
|
+
```
|
|
1009
|
+
|
|
1010
|
+
#### `pocket()` — Cut a pocket (cavity) into this solid through the named face.
|
|
1011
|
+
|
|
1012
|
+
```js
|
|
1013
|
+
box(100, 100, 20).pocket('top', 8)
|
|
1014
|
+
box(100, 100, 20).pocket('top', 8, { inset: 5 })
|
|
1015
|
+
box(100, 100, 20).pocket('top', 8, { scale: 0.8 })
|
|
1016
|
+
```
|
|
1017
|
+
|
|
1018
|
+
```ts
|
|
1019
|
+
pocket(face: FaceSelector, depth: number, opts?: PocketOptions): Shape
|
|
1020
|
+
```
|
|
1021
|
+
|
|
1022
|
+
#### `boss()` — Add a boss (protrusion) from the named face.
|
|
1023
|
+
|
|
1024
|
+
```js
|
|
1025
|
+
box(100, 100, 20).boss('top', 5)
|
|
1026
|
+
box(100, 100, 20).boss('top', 10, { scale: 0.6 })
|
|
1027
|
+
```
|
|
1028
|
+
|
|
1029
|
+
```ts
|
|
1030
|
+
boss(face: FaceSelector, height: number, opts?: BossOptions): Shape
|
|
1031
|
+
```
|
|
1032
|
+
|
|
1033
|
+
#### `hole()` — Drill a hole into this solid at a face.
|
|
1034
|
+
|
|
1035
|
+
```js
|
|
1036
|
+
box(50, 50, 20).hole('top', { diameter: 8, depth: 10 })
|
|
1037
|
+
box(50, 50, 20).hole('top', { diameter: 6, counterbore: { diameter: 12, depth: 3 } })
|
|
1038
|
+
```
|
|
1039
|
+
|
|
1040
|
+
```ts
|
|
1041
|
+
hole(faceOrRef: SketchFaceTarget | FaceRef, opts: ShapeHoleOptions): Shape
|
|
1042
|
+
```
|
|
1043
|
+
|
|
1044
|
+
#### `cutout()` — Cut a profile-shaped pocket through a face using a placed sketch.
|
|
1045
|
+
|
|
1046
|
+
The sketch must be placed on a face with `Sketch.onFace(...)`. The cut follows the sketch's 2D profile.
|
|
1047
|
+
|
|
1048
|
+
```js
|
|
1049
|
+
const profile = circle2d(10).onFace(body, 'top');
|
|
1050
|
+
body.cutout(profile, { depth: 5 })
|
|
1051
|
+
```
|
|
1052
|
+
|
|
1053
|
+
```ts
|
|
1054
|
+
cutout(sketch: Sketch, opts?: ShapeCutoutOptions): Shape
|
|
1055
|
+
```
|
|
1056
|
+
|
|
1057
|
+
**Placement**
|
|
1058
|
+
|
|
1059
|
+
#### `placeReference()` — Translate the shape so the given anchor or reference lands on the target coordinate.
|
|
1060
|
+
|
|
1061
|
+
Accepts any built-in anchor name (`'bottom'`, `'center'`, `'top-front-left'`, etc.) or a custom placement reference attached via `withReferences()`.
|
|
1062
|
+
|
|
1063
|
+
```javascript
|
|
1064
|
+
// Ground a shape — put its bottom face center at Z = 0
|
|
1065
|
+
shape.placeReference('bottom', [0, 0, 0])
|
|
1066
|
+
|
|
1067
|
+
// Center at the world origin
|
|
1068
|
+
shape.placeReference('center', [0, 0, 0])
|
|
1069
|
+
|
|
1070
|
+
// Align left edge to X = 10
|
|
1071
|
+
shape.placeReference('left', [10, 0, 0])
|
|
1072
|
+
```
|
|
1073
|
+
|
|
1074
|
+
```ts
|
|
1075
|
+
placeReference(ref: PlacementAnchorLike, target: [ number, number, number ], offset?: [ number, number, number ]): Shape
|
|
1076
|
+
```
|
|
1077
|
+
|
|
1078
|
+
#### `attachTo()` — Position this shape relative to another using named 3D anchor points.
|
|
1079
|
+
|
|
1080
|
+
Anchors are bounding-box-relative: 'center', face centers ('top', 'front', ...), edge midpoints ('top-front', 'back-left', ...), and corners ('top-front-left', ...). Anchor word order is flexible: 'front-left' and 'left-front' are equivalent. Named placement references (from withReferences) can also be used as anchors.
|
|
1081
|
+
|
|
1082
|
+
```ts
|
|
1083
|
+
attachTo(target: ShapeAnchorTarget, targetAnchor: PlacementAnchorLike, selfAnchor?: PlacementAnchorLike, offset?: [ number, number, number ]): Shape
|
|
1084
|
+
```
|
|
1085
|
+
|
|
1086
|
+
#### `onFace()` — Place this shape on a face of a parent shape.
|
|
1087
|
+
|
|
1088
|
+
Think of it like sticking a label on a box surface:
|
|
1089
|
+
|
|
1090
|
+
- `face` picks which surface ('front', 'back', 'top', etc.)
|
|
1091
|
+
- `u, v` position within that face's 2D plane (from center)
|
|
1092
|
+
- front/back: u = left/right (X), v = up/down (Z)
|
|
1093
|
+
- left/right: u = forward/back (Y), v = up/down (Z)
|
|
1094
|
+
- top/bottom: u = left/right (X), v = forward/back (Y)
|
|
1095
|
+
- `protrude` = how far the child sticks out (positive = outward from face)
|
|
1096
|
+
|
|
1097
|
+
```ts
|
|
1098
|
+
onFace(parent: ShapeAnchorTarget, face: "front" | "back" | "left" | "right" | "top" | "bottom", opts?: { u?: number; v?: number; protrude?: number; }): Shape
|
|
1099
|
+
```
|
|
1100
|
+
|
|
1101
|
+
#### `seatInto()` — Slide this shape along an axis until a labeled face is embedded in the target body.
|
|
1102
|
+
|
|
1103
|
+
Position the shape roughly first (translate/rotate), then call seatInto to auto-adjust the penetration depth. No manual coordinate math needed.
|
|
1104
|
+
|
|
1105
|
+
```js
|
|
1106
|
+
// Wing root embeds into fuselage — adapts to any fuselage shape
|
|
1107
|
+
wing.translate(0, wingY, 0).seatInto(fuselage, 'root');
|
|
1108
|
+
|
|
1109
|
+
// Sensor pod sits flush on fuselage surface
|
|
1110
|
+
pod.translate(0, station, radius + 20).seatInto(fuselage, 'base', { depth: 'flush' });
|
|
1111
|
+
|
|
1112
|
+
// Antenna with 3mm gasket standoff
|
|
1113
|
+
mast.translate(0, station, radius + 50).seatInto(fuselage, 'mount', { depth: 'flush', gap: 3 });
|
|
1114
|
+
```
|
|
1115
|
+
|
|
1116
|
+
```ts
|
|
1117
|
+
seatInto(target: Shape, surface: string, options?: SeatIntoOptions): Shape
|
|
1118
|
+
```
|
|
1119
|
+
|
|
1120
|
+
#### `seatOver()` — Slide this shape until a target's labeled face is fully covered (inside this shape).
|
|
1121
|
+
|
|
1122
|
+
The inverse of `seatInto`: instead of embedding *your* face into the target, you move until the *target's* face is embedded inside you.
|
|
1123
|
+
|
|
1124
|
+
```js
|
|
1125
|
+
// Nacelle moves up until pylon's bottom face is inside the nacelle
|
|
1126
|
+
nacelle.translate(rough).seatOver(pylon, 'bottom');
|
|
1127
|
+
|
|
1128
|
+
// Cap slides down over a post until post's top face is covered
|
|
1129
|
+
cap.translate(rough).seatOver(post, 'top');
|
|
1130
|
+
```
|
|
1131
|
+
|
|
1132
|
+
```ts
|
|
1133
|
+
seatOver(target: Shape, targetSurface: string, options?: SeatIntoOptions): Shape
|
|
1134
|
+
```
|
|
1135
|
+
|
|
1136
|
+
**Connectors**
|
|
1137
|
+
|
|
1138
|
+
#### `withConnectors()` — Attach named connectors — attachment points that survive transforms and imports. Connectors can be bare (position + orientation) or typed (with connectorType/gender for compatibility matching).
|
|
1139
|
+
|
|
1140
|
+
```ts
|
|
1141
|
+
withConnectors(connectors: Record<string, ConnectorInput>): Shape
|
|
1142
|
+
```
|
|
1143
|
+
|
|
1144
|
+
#### `connectorNames()` — List all connector names on this shape.
|
|
1145
|
+
|
|
1146
|
+
```ts
|
|
1147
|
+
connectorNames(): string[]
|
|
1148
|
+
```
|
|
1149
|
+
|
|
1150
|
+
#### `connectorsByType()` — Get all connectors of a given type.
|
|
1151
|
+
|
|
1152
|
+
```ts
|
|
1153
|
+
connectorsByType(type: string): Array<{ name: string; port: PortDef; }>
|
|
1154
|
+
```
|
|
1155
|
+
|
|
1156
|
+
#### `connectorDistance()` — Distance between two connector origins on this shape.
|
|
1157
|
+
|
|
1158
|
+
```ts
|
|
1159
|
+
connectorDistance(nameA: string, nameB: string): number
|
|
1160
|
+
```
|
|
1161
|
+
|
|
1162
|
+
#### `connectorMeasurements()` — Get measurements metadata from a connector.
|
|
1163
|
+
|
|
1164
|
+
```ts
|
|
1165
|
+
connectorMeasurements(name: string): Record<string, number | string>
|
|
1166
|
+
```
|
|
1167
|
+
|
|
1168
|
+
#### `matchTo()` — Position this shape by matching connectors to a target.
|
|
1169
|
+
|
|
1170
|
+
Overloads:
|
|
1171
|
+
|
|
1172
|
+
- Single pair: `matchTo(target, selfConn, targetConn, options?)`
|
|
1173
|
+
- Dictionary (same target): `matchTo(target, { selfConn: targetConn, ... }, options?)`
|
|
1174
|
+
- Multi-target: `matchTo([ [target1, selfConn1, targetConn1], ... ], options?)`
|
|
1175
|
+
|
|
1176
|
+
```ts
|
|
1177
|
+
matchTo(targetOrPairs: Shape | MatchTarget | Array<[ Shape | MatchTarget, string, string ]>, selfConnOrDict?: string | Record<string, string>, targetConnOrOptions?: string | MatchToOptions, maybeOptions?: MatchToOptions): Shape
|
|
1178
|
+
```
|
|
1179
|
+
|
|
1180
|
+
**References**
|
|
1181
|
+
|
|
1182
|
+
#### `withReferences()` — Attach named placement references that survive normal transforms and imports.
|
|
1183
|
+
|
|
1184
|
+
```ts
|
|
1185
|
+
withReferences(refs: PlacementReferenceInput): Shape
|
|
1186
|
+
```
|
|
1187
|
+
|
|
1188
|
+
#### `referenceNames()` — List named placement references carried by this shape.
|
|
1189
|
+
|
|
1190
|
+
```ts
|
|
1191
|
+
referenceNames(kind?: PlacementReferenceKind): string[]
|
|
1192
|
+
```
|
|
1193
|
+
|
|
1194
|
+
#### `referencePoint()` — Resolve a named placement reference or built-in anchor to a 3D point.
|
|
1195
|
+
|
|
1196
|
+
```ts
|
|
1197
|
+
referencePoint(ref: PlacementAnchorLike): [ number, number, number ]
|
|
1198
|
+
```
|
|
1199
|
+
|
|
1200
|
+
#### `withPorts()` — Deprecated alias for `withConnectors()`.
|
|
1201
|
+
|
|
1202
|
+
```ts
|
|
1203
|
+
withPorts(ports: Record<string, PortInput>): Shape
|
|
1204
|
+
```
|
|
1205
|
+
|
|
1206
|
+
#### `portNames()` — Deprecated alias for `connectorNames()`.
|
|
1207
|
+
|
|
1208
|
+
```ts
|
|
1209
|
+
portNames(): string[]
|
|
1210
|
+
```
|
|
1211
|
+
|
|
1212
|
+
**Measurement**
|
|
1213
|
+
|
|
1214
|
+
#### `boundingBox()` — Get the axis-aligned bounding box as { min: [x,y,z], max: [x,y,z] }.
|
|
1215
|
+
|
|
1216
|
+
```ts
|
|
1217
|
+
boundingBox(): ShapeRuntimeBounds
|
|
1218
|
+
```
|
|
1219
|
+
|
|
1220
|
+
#### `volume()` — Volume in mm cubed.
|
|
1221
|
+
|
|
1222
|
+
```ts
|
|
1223
|
+
volume(): number
|
|
1224
|
+
```
|
|
1225
|
+
|
|
1226
|
+
#### `surfaceArea()` — Surface area in mm squared.
|
|
1227
|
+
|
|
1228
|
+
```ts
|
|
1229
|
+
surfaceArea(): number
|
|
1230
|
+
```
|
|
1231
|
+
|
|
1232
|
+
#### `isEmpty()` — True if the shape contains no geometry.
|
|
1233
|
+
|
|
1234
|
+
```ts
|
|
1235
|
+
isEmpty(): boolean
|
|
1236
|
+
```
|
|
1237
|
+
|
|
1238
|
+
#### `numBodies()` — Number of disconnected solid bodies in this shape.
|
|
1239
|
+
|
|
1240
|
+
```ts
|
|
1241
|
+
numBodies(): number
|
|
1242
|
+
```
|
|
1243
|
+
|
|
1244
|
+
#### `numTri()` — Triangle count of the mesh representation.
|
|
1245
|
+
|
|
1246
|
+
```ts
|
|
1247
|
+
numTri(): number
|
|
1248
|
+
```
|
|
1249
|
+
|
|
1250
|
+
**Other**
|
|
1251
|
+
|
|
1252
|
+
#### `clone()` — Return a new Shape wrapper for explicit duplication in scripts.
|
|
1253
|
+
|
|
1254
|
+
```ts
|
|
1255
|
+
clone(): Shape
|
|
1256
|
+
```
|
|
1257
|
+
|
|
1258
|
+
#### `geometryInfo()` — Inspect which backend/representation produced this solid.
|
|
1259
|
+
|
|
1260
|
+
```ts
|
|
1261
|
+
geometryInfo(): GeometryInfo
|
|
1262
|
+
```
|
|
1263
|
+
|
|
1264
|
+
#### `getMesh()` — Extract triangle mesh for Three.js rendering
|
|
1265
|
+
|
|
1266
|
+
```ts
|
|
1267
|
+
getMesh(): ShapeRuntimeMesh
|
|
1268
|
+
```
|
|
1269
|
+
|
|
1270
|
+
#### `slice()` — Slice the runtime solid by a plane normal to local Z at the given offset.
|
|
1271
|
+
|
|
1272
|
+
```ts
|
|
1273
|
+
slice(offset?: number): any
|
|
1274
|
+
```
|
|
1275
|
+
|
|
1276
|
+
#### `project()` — Orthographically project the runtime solid onto the local XY plane.
|
|
1277
|
+
|
|
1278
|
+
```ts
|
|
1279
|
+
project(): any
|
|
1280
|
+
```
|
|
1281
|
+
|
|
1282
|
+
### `Transform`
|
|
1283
|
+
|
|
1284
|
+
#### `identity()` — Return the identity transform.
|
|
1285
|
+
|
|
1286
|
+
```ts
|
|
1287
|
+
static identity(): Transform
|
|
1288
|
+
```
|
|
1289
|
+
|
|
1290
|
+
#### `from()` — Wrap an existing `Transform` or raw 4x4 matrix as a `Transform`.
|
|
1291
|
+
|
|
1292
|
+
```ts
|
|
1293
|
+
static from(input: TransformInput): Transform
|
|
1294
|
+
```
|
|
1295
|
+
|
|
1296
|
+
#### `translation()` — Create a translation transform.
|
|
1297
|
+
|
|
1298
|
+
```ts
|
|
1299
|
+
static translation(x: number, y: number, z: number): Transform
|
|
1300
|
+
```
|
|
1301
|
+
|
|
1302
|
+
#### `scale()` — Create a uniform or per-axis scale transform.
|
|
1303
|
+
|
|
1304
|
+
```ts
|
|
1305
|
+
static scale(v: number | Vec3): Transform
|
|
1306
|
+
```
|
|
1307
|
+
|
|
1308
|
+
#### `rotationAxis()` — Create a rotation around an arbitrary axis, optionally about a pivot.
|
|
1309
|
+
|
|
1310
|
+
```ts
|
|
1311
|
+
static rotationAxis(axis: Vec3, angleDeg: number, pivot?: Vec3): Transform
|
|
1312
|
+
```
|
|
1313
|
+
|
|
1314
|
+
#### `rotateAroundTo()` — Solve the rotation needed to move one point onto a target line or plane.
|
|
1315
|
+
|
|
1316
|
+
```ts
|
|
1317
|
+
static rotateAroundTo(axis: Vec3, pivot: Vec3, movingPoint: Vec3, targetPoint: Vec3, options?: RotateAroundToOptions): Transform
|
|
1318
|
+
```
|
|
1319
|
+
|
|
1320
|
+
#### `mul()` — Compose transforms in chain order: `a.mul(b)` applies `a`, then `b`.
|
|
1321
|
+
|
|
1322
|
+
```ts
|
|
1323
|
+
mul(other: TransformInput): Transform
|
|
1324
|
+
```
|
|
1325
|
+
|
|
1326
|
+
#### `translate()` — Translate after the current transform.
|
|
1327
|
+
|
|
1328
|
+
```ts
|
|
1329
|
+
translate(x: number, y: number, z: number): Transform
|
|
1330
|
+
```
|
|
1331
|
+
|
|
1332
|
+
#### `rotateAxis()` — Rotate after the current transform.
|
|
1333
|
+
|
|
1334
|
+
```ts
|
|
1335
|
+
rotateAxis(axis: Vec3, angleDeg: number, pivot?: Vec3): Transform
|
|
1336
|
+
```
|
|
1337
|
+
|
|
1338
|
+
#### `inverse()` — Return the inverse transform.
|
|
1339
|
+
|
|
1340
|
+
```ts
|
|
1341
|
+
inverse(): Transform
|
|
1342
|
+
```
|
|
1343
|
+
|
|
1344
|
+
#### [`point()`](/docs/sketch#point) — Transform a point using homogeneous coordinates.
|
|
1345
|
+
|
|
1346
|
+
```ts
|
|
1347
|
+
point(p: Vec3): Vec3
|
|
1348
|
+
```
|
|
1349
|
+
|
|
1350
|
+
#### `vector()` — Transform a direction vector without translation.
|
|
1351
|
+
|
|
1352
|
+
```ts
|
|
1353
|
+
vector(v: Vec3): Vec3
|
|
1354
|
+
```
|
|
1355
|
+
|
|
1356
|
+
#### `toArray()` — Return the transform as a raw 4x4 matrix array.
|
|
1357
|
+
|
|
1358
|
+
```ts
|
|
1359
|
+
toArray(): Mat4
|
|
1360
|
+
```
|
|
1361
|
+
|
|
1362
|
+
### `ShapeGroup`
|
|
1363
|
+
|
|
1364
|
+
**Properties:**
|
|
1365
|
+
|
|
1366
|
+
| Property | Type | Description |
|
|
1367
|
+
|----------|------|-------------|
|
|
1368
|
+
| `children` | `GroupChild[]` | — |
|
|
1369
|
+
| `childNames` | `Array<string | undefined>` | — |
|
|
1370
|
+
|
|
1371
|
+
**Children**
|
|
1372
|
+
|
|
1373
|
+
#### `child()` — Return the named child by name. Throws if not found. Useful when importing a multipart group and working on components individually.
|
|
1374
|
+
|
|
1375
|
+
```ts
|
|
1376
|
+
child(name: string): GroupChild
|
|
1377
|
+
```
|
|
1378
|
+
|
|
1379
|
+
#### `childName()` — Return the optional name of the child at `index`.
|
|
1380
|
+
|
|
1381
|
+
```ts
|
|
1382
|
+
childName(index: number): string | undefined
|
|
1383
|
+
```
|
|
1384
|
+
|
|
1385
|
+
**Transforms**
|
|
1386
|
+
|
|
1387
|
+
#### `translate()` — Move the entire group by (x, y, z). All children move together as a unit.
|
|
1388
|
+
|
|
1389
|
+
```ts
|
|
1390
|
+
translate(x: number, y: number, z: number): ShapeGroup
|
|
1391
|
+
```
|
|
1392
|
+
|
|
1393
|
+
#### `moveTo()` — Move the group so its bounding-box min corner lands at the given coordinate.
|
|
1394
|
+
|
|
1395
|
+
```ts
|
|
1396
|
+
moveTo(x: number, y: number, z: number): ShapeGroup
|
|
1397
|
+
```
|
|
1398
|
+
|
|
1399
|
+
#### `moveToLocal()` — Move the group relative to another part's bounding-box min corner.
|
|
1400
|
+
|
|
1401
|
+
```ts
|
|
1402
|
+
moveToLocal(target: Shape | ShapeGroup, x: number, y: number, z: number): ShapeGroup
|
|
1403
|
+
```
|
|
1404
|
+
|
|
1405
|
+
#### `rotate()` — Rotate the group around an arbitrary axis through the origin.
|
|
1406
|
+
|
|
1407
|
+
```ts
|
|
1408
|
+
rotate(axis: [ number, number, number ], angleDeg: number, options?: { pivot?: [ number, number, number ]; }): ShapeGroup
|
|
1409
|
+
```
|
|
1410
|
+
|
|
1411
|
+
#### `rotateX()` — Rotate the group around the X axis.
|
|
1412
|
+
|
|
1413
|
+
```ts
|
|
1414
|
+
rotateX(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): ShapeGroup
|
|
1415
|
+
```
|
|
1416
|
+
|
|
1417
|
+
#### `rotateY()` — Rotate the group around the Y axis.
|
|
1418
|
+
|
|
1419
|
+
```ts
|
|
1420
|
+
rotateY(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): ShapeGroup
|
|
1421
|
+
```
|
|
1422
|
+
|
|
1423
|
+
#### `rotateZ()` — Rotate the group around the Z axis.
|
|
1424
|
+
|
|
1425
|
+
```ts
|
|
1426
|
+
rotateZ(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): ShapeGroup
|
|
1427
|
+
```
|
|
1428
|
+
|
|
1429
|
+
#### `rotateAroundAxis()` — Rotate around an arbitrary axis, optionally through a pivot point.
|
|
1430
|
+
|
|
1431
|
+
```ts
|
|
1432
|
+
rotateAroundAxis(axis: [ number, number, number ], angleDeg: number, pivot?: [ number, number, number ]): ShapeGroup
|
|
1433
|
+
```
|
|
1434
|
+
|
|
1435
|
+
#### `rotateAroundTo()` — Rotate around an axis until a moving point reaches the target line/plane defined by the axis and target point. ShapeGroup string points use built-in anchors only.
|
|
1436
|
+
|
|
1437
|
+
```ts
|
|
1438
|
+
rotateAroundTo(axis: [ number, number, number ], pivot: [ number, number, number ], movingPoint: Anchor3D | [ number, number, number ], targetPoint: Anchor3D | [ number, number, number ], options?: RotateAroundToOptions): ShapeGroup
|
|
1439
|
+
```
|
|
1440
|
+
|
|
1441
|
+
#### `pointAlong()` — Reorient the group so its local Z axis points along `direction`.
|
|
1442
|
+
|
|
1443
|
+
```ts
|
|
1444
|
+
pointAlong(direction: [ number, number, number ]): ShapeGroup
|
|
1445
|
+
```
|
|
1446
|
+
|
|
1447
|
+
#### `transform()` — Apply a 4x4 transform matrix or `Transform` to all 3D children.
|
|
1448
|
+
|
|
1449
|
+
```ts
|
|
1450
|
+
transform(m: Mat4 | Transform): ShapeGroup
|
|
1451
|
+
```
|
|
1452
|
+
|
|
1453
|
+
#### `scale()` — Scale uniformly or per-axis from the group's bounding-box center.
|
|
1454
|
+
|
|
1455
|
+
```ts
|
|
1456
|
+
scale(v: number | [ number, number, number ]): ShapeGroup
|
|
1457
|
+
```
|
|
1458
|
+
|
|
1459
|
+
#### `scaleAround()` — Scale uniformly or per-axis from an explicit pivot point.
|
|
1460
|
+
|
|
1461
|
+
```ts
|
|
1462
|
+
scaleAround(pivot: [ number, number, number ], v: number | [ number, number, number ]): ShapeGroup
|
|
1463
|
+
```
|
|
1464
|
+
|
|
1465
|
+
#### `mirror()` — Mirror across a plane through the group's bounding-box center.
|
|
1466
|
+
|
|
1467
|
+
```ts
|
|
1468
|
+
mirror(normal: [ number, number, number ]): ShapeGroup
|
|
1469
|
+
```
|
|
1470
|
+
|
|
1471
|
+
#### `mirrorThrough()` — Mirror across a plane through an explicit point.
|
|
1472
|
+
|
|
1473
|
+
```ts
|
|
1474
|
+
mirrorThrough(point: [ number, number, number ], normal: [ number, number, number ]): ShapeGroup
|
|
1475
|
+
```
|
|
1476
|
+
|
|
1477
|
+
**Placement**
|
|
1478
|
+
|
|
1479
|
+
#### `placeReference()` — Translate the group so the given anchor or reference lands on the target coordinate.
|
|
1480
|
+
|
|
1481
|
+
Accepts any built-in anchor name (`'bottom'`, `'center'`, `'top-front-left'`, etc.) or a custom placement reference attached via `withReferences()`.
|
|
1482
|
+
|
|
1483
|
+
```javascript
|
|
1484
|
+
// Ground a group — put its bottom at Z = 0
|
|
1485
|
+
assembly.placeReference('bottom', [0, 0, 0])
|
|
1486
|
+
|
|
1487
|
+
// Use a custom reference from a multi-file part
|
|
1488
|
+
const placed = require('./bracket-assembly.forge.js').group
|
|
1489
|
+
.placeReference('mountCenter', [0, 0, 50]);
|
|
1490
|
+
```
|
|
1491
|
+
|
|
1492
|
+
```ts
|
|
1493
|
+
placeReference(ref: PlacementAnchorLike, target: [ number, number, number ], offset?: [ number, number, number ]): ShapeGroup
|
|
1494
|
+
```
|
|
1495
|
+
|
|
1496
|
+
#### `attachTo()` — Attach this group to a face or anchor on another part.
|
|
1497
|
+
|
|
1498
|
+
`targetAnchor` can be a built-in anchor name or a custom reference name on the target. `selfAnchor` selects the anchor on this group to align.
|
|
1499
|
+
|
|
1500
|
+
```ts
|
|
1501
|
+
attachTo(target: Shape | ShapeGroup, targetAnchor: Anchor3D | string, selfAnchor?: Anchor3D, offset?: [ number, number, number ]): ShapeGroup
|
|
1502
|
+
```
|
|
1503
|
+
|
|
1504
|
+
#### `onFace()` — Place this group on a face of a parent shape. See Shape.onFace() for full documentation.
|
|
1505
|
+
|
|
1506
|
+
```ts
|
|
1507
|
+
onFace(parent: Shape | ShapeGroup, face: "front" | "back" | "left" | "right" | "top" | "bottom", opts?: { u?: number; v?: number; protrude?: number; }): ShapeGroup
|
|
1508
|
+
```
|
|
1509
|
+
|
|
1510
|
+
**Connectors**
|
|
1511
|
+
|
|
1512
|
+
#### `withConnectors()` — Attach named connectors — attachment points that survive transforms. Connectors can be bare (position + orientation) or typed (with connectorType/gender for compatibility matching).
|
|
1513
|
+
|
|
1514
|
+
```ts
|
|
1515
|
+
withConnectors(connectors: Record<string, ConnectorInput>): ShapeGroup
|
|
1516
|
+
```
|
|
1517
|
+
|
|
1518
|
+
#### `connectorNames()` — List all connector names, including "ChildName.connectorName" from named children.
|
|
1519
|
+
|
|
1520
|
+
```ts
|
|
1521
|
+
connectorNames(): string[]
|
|
1522
|
+
```
|
|
1523
|
+
|
|
1524
|
+
#### `connectorsByType()` — Get all connectors of a given type, including from named children.
|
|
1525
|
+
|
|
1526
|
+
```ts
|
|
1527
|
+
connectorsByType(type: string): Array<{ name: string; port: PortDef; }>
|
|
1528
|
+
```
|
|
1529
|
+
|
|
1530
|
+
#### `connectorDistance()` — Distance between two connector origins on this group (supports dotted child paths).
|
|
1531
|
+
|
|
1532
|
+
```ts
|
|
1533
|
+
connectorDistance(nameA: string, nameB: string): number
|
|
1534
|
+
```
|
|
1535
|
+
|
|
1536
|
+
#### `connectorMeasurements()` — Get measurements metadata from a connector (supports dotted child paths).
|
|
1537
|
+
|
|
1538
|
+
```ts
|
|
1539
|
+
connectorMeasurements(name: string): Record<string, number | string>
|
|
1540
|
+
```
|
|
1541
|
+
|
|
1542
|
+
#### `matchTo()` — Position this group by matching connectors to a target. Connector names support dotted paths into named children: "ChildName.connectorName".
|
|
1543
|
+
|
|
1544
|
+
Overloads:
|
|
1545
|
+
|
|
1546
|
+
- Single pair: `matchTo(target, selfConn, targetConn, options?)`
|
|
1547
|
+
- Dictionary (same target): `matchTo(target, { selfConn: targetConn, ... }, options?)`
|
|
1548
|
+
- Multi-target: `matchTo([ [target1, selfConn1, targetConn1], ... ], options?)`
|
|
1549
|
+
|
|
1550
|
+
```ts
|
|
1551
|
+
matchTo(targetOrPairs: Shape | ShapeGroup | Array<[ Shape | ShapeGroup, string, string ]>, selfConnOrDict?: string | Record<string, string>, targetConnOrOptions?: string | MatchToOptions, maybeOptions?: MatchToOptions): ShapeGroup
|
|
1552
|
+
```
|
|
1553
|
+
|
|
1554
|
+
**References**
|
|
1555
|
+
|
|
1556
|
+
#### `withReferences()` — Attach named placement references to this group. References survive normal transforms (translate/rotate/scale/mirror/transform).
|
|
1557
|
+
|
|
1558
|
+
```javascript
|
|
1559
|
+
const bracket = group(
|
|
1560
|
+
{ name: 'Left', shape: leftShape },
|
|
1561
|
+
{ name: 'Right', shape: rightShape },
|
|
1562
|
+
).withReferences({
|
|
1563
|
+
points: { mountCenter: [0, 0, 0] },
|
|
1564
|
+
});
|
|
1565
|
+
```
|
|
1566
|
+
|
|
1567
|
+
```ts
|
|
1568
|
+
withReferences(refs: PlacementReferenceInput): ShapeGroup
|
|
1569
|
+
```
|
|
1570
|
+
|
|
1571
|
+
#### `referenceNames()` — List named placement references carried by this group.
|
|
1572
|
+
|
|
1573
|
+
```ts
|
|
1574
|
+
referenceNames(kind?: PlacementReferenceKind): string[]
|
|
1575
|
+
```
|
|
1576
|
+
|
|
1577
|
+
#### `referencePoint()` — Resolve a named placement reference or built-in Anchor3D to a 3D point. Named refs take priority over built-in anchors.
|
|
1578
|
+
|
|
1579
|
+
```ts
|
|
1580
|
+
referencePoint(ref: PlacementAnchorLike): [ number, number, number ]
|
|
1581
|
+
```
|
|
1582
|
+
|
|
1583
|
+
#### `withPorts()` — Backward-compatible alias for `withConnectors()`.
|
|
1584
|
+
|
|
1585
|
+
```ts
|
|
1586
|
+
withPorts(ports: Record<string, PortInput>): ShapeGroup
|
|
1587
|
+
```
|
|
1588
|
+
|
|
1589
|
+
#### `portNames()` — Backward-compatible alias for `connectorNames()`.
|
|
1590
|
+
|
|
1591
|
+
```ts
|
|
1592
|
+
portNames(): string[]
|
|
1593
|
+
```
|
|
1594
|
+
|
|
1595
|
+
**Other**
|
|
1596
|
+
|
|
1597
|
+
#### `clone()` — Return a deep-cloned ShapeGroup tree (refs copied).
|
|
1598
|
+
|
|
1599
|
+
```ts
|
|
1600
|
+
clone(): ShapeGroup
|
|
1601
|
+
```
|
|
1602
|
+
|
|
1603
|
+
#### `boundingBox()` — Return the combined 3D bounding box of all children.
|
|
1604
|
+
|
|
1605
|
+
```ts
|
|
1606
|
+
boundingBox(): { min: [ number, number, number ]; max: [ number, number, number ]; }
|
|
1607
|
+
```
|
|
1608
|
+
|
|
1609
|
+
#### `color()` — Return a copy of the group with the given display color applied to each child.
|
|
1610
|
+
|
|
1611
|
+
```ts
|
|
1612
|
+
color(hex: string): ShapeGroup
|
|
1613
|
+
```
|
|
1614
|
+
|
|
1615
|
+
### `SurfacePattern`
|
|
1616
|
+
|
|
1617
|
+
**Properties:**
|
|
1618
|
+
|
|
1619
|
+
| Property | Type | Description |
|
|
1620
|
+
|----------|------|-------------|
|
|
1621
|
+
| `body` | `string` | Function body: receives (u, v) in surface mm, returns height displacement. |
|
|
1622
|
+
| `constants` | `Record<string, number>` | Named constants injected into the function. |
|
|
722
1623
|
|
|
723
1624
|
---
|
|
724
1625
|
|
|
@@ -734,19 +1635,19 @@ Supports transforms (translate, rotate, scale, mirror, transform, rotateAround,
|
|
|
734
1635
|
- `greaterThan(label: string, actual: number, min: number, message?: string): void` — Check that actual > min.
|
|
735
1636
|
- `lessThan(label: string, actual: number, max: number, message?: string): void` — Check that actual < max.
|
|
736
1637
|
- `inRange(label: string, actual: number, min: number, max: number, message?: string): void` — Check that min <= actual <= max.
|
|
737
|
-
- `centersCoincide(label: string, a: ShapeLike
|
|
738
|
-
- `notColliding(label: string, a: ShapeLike
|
|
739
|
-
- `minClearance(label: string, a: ShapeLike
|
|
1638
|
+
- `centersCoincide(label: string, a: ShapeLike, b: ShapeLike, tolerance?: number): void` — Check that the bounding-box centers of two shapes coincide within tolerance (mm).
|
|
1639
|
+
- `notColliding(label: string, a: ShapeLike, b: ShapeLike, searchLength?: number): void` — Check that two shapes do not collide (minGap > 0).
|
|
1640
|
+
- `minClearance(label: string, a: ShapeLike, b: ShapeLike, minGap: number, searchLength?: number): void` — Check that a minimum clearance gap exists between two shapes.
|
|
740
1641
|
- `parallel(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number): void` — Check that two face normals are parallel (within toleranceDeg degrees).
|
|
741
1642
|
- `perpendicular(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number): void` — Check that two face normals are perpendicular (within toleranceDeg degrees).
|
|
742
1643
|
- `coplanar(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number, toleranceMm?: number): void` — Check that a face is coplanar with (same plane as) another face, meaning they are parallel AND their centers lie on the same plane.
|
|
743
1644
|
- `faceAt(label: string, face: FaceRefLike, expectedPos: [ number` — Check that a face center lies at a specific position (within toleranceMm).
|
|
744
1645
|
- `sameDirection(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number): void` — Check that two face normals point in the same direction (not antiparallel). Stricter than parallel — both |angle| AND sign must match.
|
|
745
|
-
- `isEmpty(label: string, shape: ShapeLike
|
|
746
|
-
- `notEmpty(label: string, shape: ShapeLike
|
|
747
|
-
- `volumeApprox(label: string, shape: ShapeLike
|
|
748
|
-
- `areaApprox(label: string, shape: ShapeLike
|
|
749
|
-
- `boundingBoxSize(label: string, shape: ShapeLike
|
|
1646
|
+
- `isEmpty(label: string, shape: ShapeLike, message?: string): void` — Check that a shape is empty.
|
|
1647
|
+
- `notEmpty(label: string, shape: ShapeLike, message?: string): void` — Check that a shape is NOT empty.
|
|
1648
|
+
- `volumeApprox(label: string, shape: ShapeLike, expected: number, tolerance?: number): void` — Check that a shape's volume is approximately equal to expected (mm³).
|
|
1649
|
+
- `areaApprox(label: string, shape: ShapeLike, expected: number, tolerance?: number): void` — Check that a shape's surface area is approximately equal to expected (mm²).
|
|
1650
|
+
- `boundingBoxSize(label: string, shape: ShapeLike, expectedSize: [ number` — Check that a shape's bounding box has approximately the given size.
|
|
750
1651
|
|
|
751
1652
|
### `Constraint`
|
|
752
1653
|
|