forgecad 0.1.3 → 0.1.5
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 +11 -0
- package/dist/assets/{evalWorker-1m873KWd.js → evalWorker-CbAa_9qg.js} +108 -108
- package/dist/assets/{index-Dvz3nSDc.js → index-DFa4fntx.js} +335 -335
- package/dist/assets/{manifold-C38sUiKu.js → manifold-BGMOVi-H.js} +1 -1
- package/dist/assets/{manifold-rOWQW9fU.js → manifold-BNJTX3qf.js} +1 -1
- package/dist/assets/{manifold-Dk2u-lhj.js → manifold-CL39pgcA.js} +1 -1
- package/dist/assets/{reportWorker-Cj587shw.js → reportWorker-C5Ot2xSE.js} +122 -122
- package/dist/index.html +1 -1
- package/dist-cli/forgecad.js +117 -22
- package/dist-skill/CONTEXT.md +4799 -0
- package/dist-skill/docs/API/model-building/assembly.md +41 -0
- package/dist-skill/docs/API/model-building/reference.md +61 -7
- package/dist-skill/docs/VISION.md +2 -1
- package/examples/api/import-assembly-source.forge.js +22 -0
- package/examples/api/import-assembly.forge.js +26 -0
- package/package.json +1 -1
|
@@ -131,9 +131,50 @@ show(solved.toScene());
|
|
|
131
131
|
|
|
132
132
|
That keeps mechanism setup in earlier cells and collision/sweep investigation in the current preview cell.
|
|
133
133
|
|
|
134
|
+
## Importing assemblies from other files
|
|
135
|
+
|
|
136
|
+
Use `importAssembly(fileName, paramOverrides?)` to import an assembly defined in another file. The source file must `return` the `Assembly` instance directly (not `.solve()`).
|
|
137
|
+
|
|
138
|
+
```javascript
|
|
139
|
+
// arm.forge.js — source file
|
|
140
|
+
const mech = assembly("Arm")
|
|
141
|
+
.addPart("Base", box(80, 80, 20, true))
|
|
142
|
+
.addPart("Link", box(140, 24, 24).translate(0, -12, -12))
|
|
143
|
+
.addRevolute("shoulder", "Base", "Link", {
|
|
144
|
+
axis: [0, 1, 0],
|
|
145
|
+
min: -30,
|
|
146
|
+
max: 120,
|
|
147
|
+
default: 25,
|
|
148
|
+
frame: Transform.identity().translate(0, 0, 20),
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
return mech; // return Assembly, not mech.solve()
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
```javascript
|
|
155
|
+
// scene.forge.js — consumer
|
|
156
|
+
const arm = importAssembly("arm.forge.js");
|
|
157
|
+
|
|
158
|
+
// Access named parts by name (positioned at default or given joint state)
|
|
159
|
+
const base = arm.part("Base");
|
|
160
|
+
const link = arm.part("Link", { shoulder: 60 });
|
|
161
|
+
|
|
162
|
+
// Convert to a ShapeGroup — children named after assembly part names
|
|
163
|
+
const g = arm.toGroup({ shoulder: 45 });
|
|
164
|
+
const baseChild = g.child("Base");
|
|
165
|
+
|
|
166
|
+
// Full kinematic access
|
|
167
|
+
arm.assembly.sweepJoint("shoulder", -30, 120, 24);
|
|
168
|
+
const solved = arm.solve({ shoulder: 45 });
|
|
169
|
+
console.log(solved.bom());
|
|
170
|
+
|
|
171
|
+
return arm.toGroup({ shoulder: 45 });
|
|
172
|
+
```
|
|
173
|
+
|
|
134
174
|
## Common pitfalls
|
|
135
175
|
- If parts vanish in the viewport, check whether a cut plane is active before debugging kinematics. The viewer-side APIs live in [../runtime/viewport.md](../runtime/viewport.md).
|
|
136
176
|
- If a returned object is empty, Forge logs a warning in script output.
|
|
177
|
+
- `importAssembly()` requires the source file to return the `Assembly` object before calling `.solve()`. If you call `.solve()` in the source file and return a `SolvedAssembly`, use `importGroup()` instead (convert with `.toScene()` → group).
|
|
137
178
|
|
|
138
179
|
## Metadata
|
|
139
180
|
- `addPart(..., { metadata })` attaches per-part metadata to an assembly part.
|
|
@@ -1284,14 +1284,67 @@ const rightOnly = bracketB.child("Bracket Right").translate(200, 0, 0);
|
|
|
1284
1284
|
return [bracketA, leftOnly, rightOnly];
|
|
1285
1285
|
```
|
|
1286
1286
|
|
|
1287
|
-
|
|
1287
|
+
### `importAssembly(fileName, paramOverrides?)`
|
|
1288
|
+
Executes another file and returns its result as an `ImportedAssembly`. The target file **must** return an `Assembly` instance directly (before calling `.solve()`). Use this when the source file is authored with `assembly()` and you want to access named parts or kinematic structure without rewriting it as a `group()`.
|
|
1288
1289
|
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1290
|
+
**Parameters:**
|
|
1291
|
+
- `fileName` (string) — Import path (e.g. `"./sub-arm.forge.js"`)
|
|
1292
|
+
- `paramOverrides` (optional object) — Import-time parameter overrides by param name
|
|
1293
|
+
|
|
1294
|
+
**Returns:** `ImportedAssembly` with the following methods:
|
|
1295
|
+
- `.assembly` — the raw `Assembly` for `sweepJoint`, `describe`, etc.
|
|
1296
|
+
- `.solve(state?)` — delegate to `Assembly.solve()`, returns `SolvedAssembly`
|
|
1297
|
+
- `.part(name, state?)` — returns the named part positioned at the given joint state (defaults to each joint's `default` value)
|
|
1298
|
+
- `.toGroup(state?)` — converts all parts to a `ShapeGroup` with children named after the assembly part names
|
|
1299
|
+
|
|
1300
|
+
```javascript
|
|
1301
|
+
// sub-arm.forge.js ← the source file — returns Assembly, not solved
|
|
1302
|
+
const mech = assembly("Sub Arm")
|
|
1303
|
+
.addPart("Base", box(60, 60, 16, true))
|
|
1304
|
+
.addPart("Link", box(120, 20, 20).translate(0, -10, -10))
|
|
1305
|
+
.addRevolute("shoulder", "Base", "Link", {
|
|
1306
|
+
axis: [0, 1, 0],
|
|
1307
|
+
min: -45,
|
|
1308
|
+
max: 120,
|
|
1309
|
+
default: 30,
|
|
1310
|
+
frame: Transform.identity().translate(0, 0, 16),
|
|
1311
|
+
});
|
|
1312
|
+
|
|
1313
|
+
return mech; // return Assembly directly
|
|
1314
|
+
```
|
|
1315
|
+
|
|
1316
|
+
```javascript
|
|
1317
|
+
// scene.forge.js ← the consumer
|
|
1318
|
+
const angle = param("Angle", 45, { unit: "°" });
|
|
1319
|
+
|
|
1320
|
+
const subArm = importAssembly("sub-arm.forge.js", { "Link Length": 100 });
|
|
1321
|
+
|
|
1322
|
+
// Access a specific part positioned at a given state
|
|
1323
|
+
const baseShape = subArm.part("Base");
|
|
1324
|
+
const linkShape = subArm.part("Link", { shoulder: angle });
|
|
1325
|
+
|
|
1326
|
+
// Convert to a named ShapeGroup — children match assembly part names
|
|
1327
|
+
const asGroup = subArm.toGroup({ shoulder: angle });
|
|
1328
|
+
const base = asGroup.child("Base");
|
|
1329
|
+
|
|
1330
|
+
// Full kinematic access via the underlying assembly
|
|
1331
|
+
const swept = subArm.assembly.sweepJoint("shoulder", -45, 120, 20);
|
|
1332
|
+
|
|
1333
|
+
// Place two copies at different joint states
|
|
1334
|
+
const copy1 = subArm.toGroup({ shoulder: 45 });
|
|
1335
|
+
const copy2 = subArm.toGroup({ shoulder: -20 }).translate(200, 0, 0);
|
|
1336
|
+
return [copy1, copy2];
|
|
1337
|
+
```
|
|
1338
|
+
|
|
1339
|
+
**When to use `importGroup` vs `importAssembly`:**
|
|
1340
|
+
|
|
1341
|
+
| | `importPart` | `importGroup` | `importAssembly` |
|
|
1342
|
+
|---|---|---|---|
|
|
1343
|
+
| Source returns | `Shape` or `TrackedShape` | `ShapeGroup` via `group(...)` | `Assembly` (unsolved) |
|
|
1344
|
+
| Result type | `Shape` | `ShapeGroup` | `ImportedAssembly` |
|
|
1345
|
+
| Access children | Not possible | `.child("Name")` | `.part("Name", state?)` or `.toGroup().child("Name")` |
|
|
1346
|
+
| Kinematic access | None | None | `.solve()`, `.assembly.sweepJoint()` |
|
|
1347
|
+
| Multiple poses | No | No | Yes — call `.toGroup(state)` with different states |
|
|
1295
1348
|
|
|
1296
1349
|
### Import Rules
|
|
1297
1350
|
- Circular imports are detected and throw an error
|
|
@@ -1301,6 +1354,7 @@ return [bracketA, leftOnly, rightOnly];
|
|
|
1301
1354
|
- Relative imports (`./` / `../`) are resolved from the current file path
|
|
1302
1355
|
- `importPart()` accepts `Shape` or `TrackedShape` results and always returns a chainable `Shape`
|
|
1303
1356
|
- `importGroup()` accepts only `ShapeGroup` results; use `group(...)` as the return value in the source file
|
|
1357
|
+
- `importAssembly()` accepts only `Assembly` results; return the `assembly(...)` instance before calling `.solve()`
|
|
1304
1358
|
- Source files can attach placement references with `.withReferences({ points, edges, surfaces, objects })` — works on both `Shape` and `ShapeGroup`
|
|
1305
1359
|
- Imported tracked solids keep their named faces/edges as `surfaces.<faceName>` and `edges.<edgeName>` references
|
|
1306
1360
|
- SVG import supports deterministic region filtering (`regionSelection`, `maxRegions`, area thresholds)
|
|
@@ -37,6 +37,7 @@ The kernel is not the product. The modeling layer on top is.
|
|
|
37
37
|
- **View controls** — Render modes (solid/wireframe/overlay), projection (perspective/orthographic), named views (front/back/left/right/top/bottom/iso), fit-to-view, zoom-to-selection
|
|
38
38
|
- **STL export** — Binary STL export from the browser UI
|
|
39
39
|
- **Cut planes** — `cutPlane()` defines named section planes for inspection. Viewport sectioning uses `trimByPlane()` for capped solids, with GPU clipping fallback on trim failures
|
|
40
|
+
- **Compile plan inspector** — selecting a shape opens a Construction panel showing its build tree (Union → Box, Cylinder, Fillet, …). Clicking any node previews that sub-shape as an X-ray ghost in the viewport (visible through the parent solid). Navigate with arrow keys; Escape or clicking elsewhere exits.
|
|
40
41
|
|
|
41
42
|
### Gaps to close (Fusion360 parity)
|
|
42
43
|
|
|
@@ -51,7 +52,7 @@ The kernel is not the product. The modeling layer on top is.
|
|
|
51
52
|
| Thread/helix | Helical sweep for threads, springs | Mechanical fasteners | Medium (threads done via SDF, general helix sweep still missing) |
|
|
52
53
|
|
|
53
54
|
### What we deliberately skip
|
|
54
|
-
- **
|
|
55
|
+
- **Editable history tree / timeline** — code IS the history. You read it top to bottom. No need for a separate feature tree when the script is the tree. (Note: the compile plan inspector above is read-only — it shows what the code produced, not a parallel editable feature history.)
|
|
55
56
|
- **Direct modeling** — push/pull faces interactively. Not relevant for code-first CAD.
|
|
56
57
|
- **Full GUI-style assembly mate solving** — Forge now supports code-level assembly graphs (`assembly()`, revolute/prismatic/fixed joints, collision checks, BOM metadata), but not full interactive face-mate workflows like Fusion's assembly workspace.
|
|
57
58
|
- **Photorealistic rendering** — not a rendering tool. Basic viewport materials are sufficient. Export to STL for slicing or external renderers.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Source file for import-assembly demo.
|
|
2
|
+
// Returns an Assembly directly (not solved) so the importer controls state.
|
|
3
|
+
|
|
4
|
+
const linkLen = param("Link Length", 120, { min: 60, max: 200 });
|
|
5
|
+
|
|
6
|
+
const base = box(60, 60, 16, true).translate(0, 0, 8).color("#6e7b88");
|
|
7
|
+
const link = box(linkLen, 20, 20)
|
|
8
|
+
.translate(0, -10, -10)
|
|
9
|
+
.color("#5f87c6");
|
|
10
|
+
|
|
11
|
+
const mech = assembly("Sub Arm")
|
|
12
|
+
.addPart("Base", base, { metadata: { material: "PETG", qty: 1 } })
|
|
13
|
+
.addPart("Link", link, { metadata: { material: "PETG-CF", qty: 1 } })
|
|
14
|
+
.addRevolute("shoulder", "Base", "Link", {
|
|
15
|
+
axis: [0, 1, 0],
|
|
16
|
+
min: -45,
|
|
17
|
+
max: 120,
|
|
18
|
+
default: 30,
|
|
19
|
+
frame: Transform.identity().translate(0, 0, 16),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
return mech;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// importAssembly() demo
|
|
2
|
+
// Shows how to import an Assembly, access named parts, and convert to a group.
|
|
3
|
+
|
|
4
|
+
const angle = param("Shoulder Angle", 45, { min: -45, max: 120, unit: "°" });
|
|
5
|
+
|
|
6
|
+
// Import the sub-assembly — get back an ImportedAssembly
|
|
7
|
+
const subArm = importAssembly("api/import-assembly-source.forge.js", { "Link Length": 100 });
|
|
8
|
+
|
|
9
|
+
// Access a specific part by name (positioned at default joint state)
|
|
10
|
+
const baseShape = subArm.part("Base");
|
|
11
|
+
const linkShape = subArm.part("Link", { shoulder: angle });
|
|
12
|
+
|
|
13
|
+
// Solve the whole sub-assembly at a specific joint state
|
|
14
|
+
const solved = subArm.solve({ shoulder: angle });
|
|
15
|
+
console.log("BOM:", solved.bom());
|
|
16
|
+
|
|
17
|
+
// Convert to a named ShapeGroup — children match part names
|
|
18
|
+
const asGroup = subArm.toGroup({ shoulder: angle });
|
|
19
|
+
const groupedBase = asGroup.child("Base");
|
|
20
|
+
const groupedLink = asGroup.child("Link");
|
|
21
|
+
|
|
22
|
+
// Place a copy of the full sub-assembly, and a shifted copy
|
|
23
|
+
const copy1 = subArm.toGroup({ shoulder: angle });
|
|
24
|
+
const copy2 = subArm.toGroup({ shoulder: -20 }).translate(200, 0, 0);
|
|
25
|
+
|
|
26
|
+
return [copy1, copy2];
|