forgecad 0.9.14 → 0.9.15
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/LICENSE +6 -4
- package/README.md +8 -4
- package/dist/assets/{AdminPage-eWGs2K6H.js → AdminPage-CDyGUinA.js} +2 -2
- package/dist/assets/{BenchmarkPage-CTrLKfpo.js → BenchmarkPage-DfPMY_-d.js} +4 -15
- package/dist/assets/{BlogPage-5nPesyds.js → BlogPage-kF0fkdJT.js} +2 -2
- package/dist/assets/{DocsPage-C4Y3nbYc.js → DocsPage-B954L3YN.js} +9 -3
- package/dist/assets/EditorApp-Beb-IZ0y.js +14014 -0
- package/dist/assets/{EditorApp-BAnckbsk.css → EditorApp-CuDLxKqL.css} +698 -0
- package/dist/assets/{EmbedViewer-C8fB4n5U.js → EmbedViewer-C77B-TrF.js} +3 -3
- package/dist/assets/{LandingPageProofDriven-jSz0LaMM.js → LandingPageProofDriven-Cr6fXMDj.js} +35 -37
- package/dist/assets/LegalPage-BRlScr9A.css +91 -0
- package/dist/assets/LegalPage-Dzklqmmg.js +39 -0
- package/dist/assets/{PricingPage-BMedqFef.css → PricingPage-BPF6HKyO.css} +25 -0
- package/dist/assets/{PricingPage-B83B90zh.js → PricingPage-zWXkvlwl.js} +19 -19
- package/dist/assets/{SettingsPage-DY889pcu.js → SettingsPage-Bz0of4KQ.js} +2 -2
- package/dist/assets/app-CE3sYcV7.css +3890 -0
- package/dist/assets/{app-bEww1ic4.js → app-D3kDkggg.js} +2293 -946
- package/dist/assets/cli/{render-Cho2uKG_.js → render-DSY3mMQa.js} +337 -7
- package/dist/assets/{constructionHistoryWorker-HYwzJY4m.js → constructionHistoryWorker-gpDo-uH2.js} +927 -243
- package/dist/assets/{evalWorker-CjQwJSE-.js → evalWorker-CU0Ke6DP.js} +7800 -4164
- package/dist/assets/{forgecad_geometry-CH2nvuLA.js → forgecad_geometry-Dgceylq9.js} +43 -1
- package/dist/assets/forgecad_geometry_bg-dD4RNQF1.wasm +0 -0
- package/dist/assets/{inspectWorker-DeRnMVv1.js → inspectWorker-COyp8XXA.js} +927 -243
- package/dist/assets/{javascript-70-4uGcz.js → javascript-1kQXfVaz.js} +1 -1
- package/dist/assets/landing-proof-driven-DiGqdtWa.js +18 -0
- package/dist/assets/{landing-proof-driven-oFYW6mjz.css → landing-proof-driven-ORyigZ6p.css} +13 -7
- package/dist/assets/legalContent-ZfFGMmi4.js +251 -0
- package/dist/assets/{manifold-CG9Fokx-.js → manifold-BRI5prcH.js} +1 -1
- package/dist/assets/{manifold-uRzgk5O8.js → manifold-C-3h2M7p.js} +2 -2
- package/dist/assets/{manifold-rmfAcdwF.js → manifold-DNkrUWpA.js} +1 -1
- package/dist/assets/{reportWorker-4cW_ZpoS.js → reportWorker-CdBz5bNg.js} +7538 -10857
- package/dist/assets/{scalar-sampling-budget-CfDiFvh7.js → scalar-sampling-budget-wJF98aY9.js} +6935 -4331
- package/dist/assets/{scanProxyWorker-Bs2TDgLw.js → scanProxyWorker-B-9VbLIs.js} +32 -1
- package/dist/assets/{solver-DuJAO8S6.js → solver-BZ9LPTHs.js} +1 -1
- package/dist/assets/solver_bg-DAHZJ_rw.wasm +0 -0
- package/dist/assets/{targets-D6PWsv6X.js → targets-B9sGB5nB.js} +1 -1
- package/dist/assets/{vendor-react-Da3A2QmU.js → vendor-react-6j1Kke-Y.js} +6 -5
- package/dist/cli/render.html +1 -1
- package/dist/docs/index.html +2 -2
- package/dist/docs-raw/AI/ai-native-cad.md +50 -0
- package/dist/docs-raw/AI/usage.md +3 -12
- package/dist/docs-raw/CLI.md +30 -10
- package/dist/docs-raw/component-model.md +27 -11
- package/dist/docs-raw/generated/assembly.md +301 -212
- package/dist/docs-raw/generated/concepts.md +235 -237
- package/dist/docs-raw/generated/core.md +283 -6
- package/dist/docs-raw/generated/curves.md +274 -361
- package/dist/docs-raw/generated/lib.md +7 -1
- package/dist/docs-raw/generated/output.md +19 -4
- package/dist/docs-raw/generated/runtime-names.md +41 -0
- package/dist/docs-raw/generated/sdf.md +31 -0
- package/dist/docs-raw/generated/sheet-metal.md +9 -0
- package/dist/docs-raw/generated/sketch.md +44 -1
- package/dist/docs-raw/generated/viewport.md +11 -3
- package/dist/docs-raw/guides/coordinate-system.md +20 -16
- package/dist/docs-raw/guides/geometry-conventions.md +2 -2
- package/dist/docs-raw/guides/inspection-bundles.md +2 -1
- package/dist/docs-raw/guides/joint-design.md +24 -0
- package/dist/docs-raw/guides/positioning.md +13 -3
- package/dist/docs-raw/legal/privacy.md +63 -0
- package/dist/docs-raw/legal/software-license.md +55 -0
- package/dist/docs-raw/legal/terms.md +87 -0
- package/dist/docs-raw/skills/forgecad-3d-reconstruction.md +1 -1
- package/dist/docs-raw/skills/forgecad-blockout-model.md +1 -1
- package/dist/docs-raw/skills/forgecad-component-model.md +11 -2
- package/dist/docs-raw/skills/forgecad-high-level-spec.md +1 -1
- package/dist/docs-raw/skills/forgecad-image-replicator.md +8 -8
- package/dist/docs-raw/skills/forgecad-lld.md +1 -1
- package/dist/docs-raw/skills/forgecad-make-a-model.md +1 -1
- package/dist/docs-raw/skills/forgecad-model-grader.md +2 -2
- package/dist/docs-raw/skills/forgecad-prepare-prompt.md +2 -2
- package/dist/docs-raw/skills/forgecad-project.md +1 -1
- package/dist/docs-raw/skills/forgecad-reconstruction-benchmark.md +1 -1
- package/dist/docs-raw/skills/forgecad-render-inspect.md +4 -2
- package/dist/docs-raw/skills/forgecad-visual-spec.md +1 -1
- package/dist/docs-raw/skills/forgecad.md +4 -3
- package/dist/index.html +40 -12
- package/dist/llms.txt +8 -0
- package/dist/site.webmanifest +1 -1
- package/dist/sitemap.xml +49 -13
- package/dist-cli/{check-compiler-U5SOPN7X.js → check-compiler-SDX5QIXI.js} +1 -2
- package/dist-cli/{check-query-propagation-XOKNSSYU.js → check-query-propagation-EAYEFT77.js} +1 -2
- package/dist-cli/{chunk-EXWGNL6K.js → chunk-N4O47JLF.js} +12540 -9046
- package/dist-cli/forgecad.js +1786 -679
- package/dist-cli/{forgecad_geometry-GYVNKPIE.js → forgecad_geometry-QOQIIP53.js} +42 -1
- package/dist-cli/forgecad_geometry_bg.wasm +0 -0
- package/dist-cli/{solver-46FFSK2U.js → solver-OK4HECRH.js} +0 -1
- package/dist-cli/solver_bg.wasm +0 -0
- package/dist-skill/CONTEXT.md +1117 -721
- package/dist-skill/SKILL.md +3 -2
- package/dist-skill/docs/API/core/concepts.md +64 -1
- package/dist-skill/docs/CLI.md +30 -10
- package/dist-skill/docs/generated/assembly.md +277 -229
- package/dist-skill/docs/generated/core.md +283 -6
- package/dist-skill/docs/generated/curves.md +272 -362
- package/dist-skill/docs/generated/lib.md +7 -1
- package/dist-skill/docs/generated/output.md +19 -4
- package/dist-skill/docs/generated/runtime-names.md +41 -0
- package/dist-skill/docs/generated/sdf.md +31 -0
- package/dist-skill/docs/generated/sheet-metal.md +9 -0
- package/dist-skill/docs/generated/sketch.md +44 -2
- package/dist-skill/docs/generated/viewport.md +2 -87
- package/dist-skill/docs/guides/coordinate-system.md +20 -16
- package/dist-skill/docs/guides/geometry-conventions.md +2 -2
- package/dist-skill/docs/guides/inspection-bundles.md +2 -1
- package/dist-skill/docs/guides/joint-design.md +24 -0
- package/dist-skill/docs/guides/positioning.md +13 -3
- package/dist-skill/library/forgecad-component-model/SKILL.md +10 -1
- package/dist-skill/library/forgecad-image-replicator/SKILL.md +6 -6
- package/dist-skill/library/forgecad-image-replicator/scripts/compare_images.py +166 -0
- package/dist-skill/library/forgecad-model-grader/SKILL.md +1 -1
- package/dist-skill/library/forgecad-prepare-prompt/SKILL.md +1 -1
- package/dist-skill/library/forgecad-render-inspect/SKILL.md +3 -1
- package/examples/api/assembly-kinematics-foundation.forge.js +65 -0
- package/examples/api/assembly-kinematics-four-bar.forge.js +115 -0
- package/examples/api/assembly-kinematics-limb.forge.js +116 -0
- package/examples/api/connector-frame-rig-chain.forge.js +102 -0
- package/examples/api/exact-sheet-shell-assembly.forge.js +0 -2
- package/examples/api/exact-surface-studio.forge.js +6 -8
- package/examples/api/helix-basics.forge.js +6 -6
- package/examples/api/lean-foundations/README.md +12 -0
- package/examples/api/lean-foundations/curve-blend-exact.forge.js +22 -0
- package/examples/api/lean-foundations/curve-fit-interpolation.forge.js +18 -0
- package/examples/api/lean-foundations/curve-helix-canonicalization.forge.js +27 -0
- package/examples/api/lean-foundations/curve-route-canonicalization.forge.js +16 -0
- package/examples/api/lean-foundations/curve-trim-reverse.forge.js +24 -0
- package/examples/api/lean-foundations/exact-curve-arc.forge.js +36 -0
- package/examples/api/mixed-edge-finishes-proof.forge.js +8 -11
- package/examples/api/route3d-elbow.forge.js +68 -0
- package/examples/api/transition-curves.forge.js +44 -15
- package/examples/api/y-blend-corner-showcase.forge.js +0 -2
- package/examples/generative/coral-vase.forge.js +1 -1
- package/examples/nurbs-tube.forge.js +1 -1
- package/package.json +14 -13
- package/dist/assets/EditorApp-lXv53A1m.js +0 -13610
- package/dist/assets/app-CsHnaBWt.css +0 -1789
- package/dist/assets/forgecad_geometry_bg-C5_E9Oa9.wasm +0 -0
- package/dist/assets/solver_bg-CWvv4lnN.wasm +0 -0
- package/dist/docs-raw/API/README.md +0 -16
- package/dist/docs-raw/API/core/concepts.md +0 -118
- package/dist/docs-raw/INDEX.md +0 -138
- package/dist/docs-raw/RELEASING.md +0 -87
- package/dist/docs-raw/agent-native-api.md +0 -27
- package/dist/docs-raw/beta-deployment.md +0 -304
- package/dist/docs-raw/beta-operations.md +0 -325
- package/dist/docs-raw/blueprint-first.md +0 -145
- package/dist/docs-raw/cli-monetization.md +0 -112
- package/dist/docs-raw/coding-best-practices.md +0 -120
- package/dist/docs-raw/coding.md +0 -340
- package/dist/docs-raw/deployment.md +0 -374
- package/dist/docs-raw/guides/skill-maintenance.md +0 -161
- package/dist/docs-raw/guides/surface-members.md +0 -82
- package/dist/docs-raw/harbor-cli.md +0 -854
- package/dist/docs-raw/internals/backend-vocabulary.md +0 -35
- package/dist/docs-raw/internals/compiler.md +0 -307
- package/dist/docs-raw/internals/constraint-solver-quality.md +0 -161
- package/dist/docs-raw/internals/constraint-solver.md +0 -176
- package/dist/docs-raw/internals/shape-from-slices.md +0 -152
- package/dist/docs-raw/internals/sketch-2d-pipeline.md +0 -108
- package/dist/docs-raw/platform/admin.md +0 -45
- package/dist/docs-raw/platform/architecture.md +0 -82
- package/dist/docs-raw/platform/auth.md +0 -139
- package/dist/docs-raw/platform/email.md +0 -67
- package/dist/docs-raw/platform/google-oauth-setup.md +0 -88
- package/dist/docs-raw/platform/observability.md +0 -197
- package/dist/docs-raw/platform/projects.md +0 -111
- package/dist/docs-raw/platform/sharing.md +0 -90
- package/dist/docs-raw/product/README.md +0 -39
- package/dist/docs-raw/product/api-as-product-language.md +0 -13
- package/dist/docs-raw/product/business-model.md +0 -15
- package/dist/docs-raw/product/competitive-positioning.md +0 -17
- package/dist/docs-raw/product/creative-manufacturing.md +0 -15
- package/dist/docs-raw/product/founder-story.md +0 -11
- package/dist/docs-raw/product/manufacturing-workflows.md +0 -15
- package/dist/docs-raw/product/onboarding-first-experience.md +0 -256
- package/dist/docs-raw/product/product-loop.md +0 -17
- package/dist/docs-raw/product/strategic-decisions.md +0 -22
- package/dist/docs-raw/product/user-outreach-email-templates.md +0 -161
- package/dist/docs-raw/product/user-segments.md +0 -15
- package/dist/docs-raw/product/vision.md +0 -26
- package/dist/docs-raw/rl-environments.md +0 -350
- package/dist/docs-raw/runbook.md +0 -611
- package/dist-cli/check-compiler-U5SOPN7X.js.map +0 -1
- package/dist-cli/check-query-propagation-XOKNSSYU.js.map +0 -1
- package/dist-cli/chunk-EXWGNL6K.js.map +0 -1
- package/dist-cli/forgecad.js.map +0 -1
- package/dist-cli/forgecad_geometry-GYVNKPIE.js.map +0 -1
- package/dist-cli/solver-46FFSK2U.js.map +0 -1
- package/dist-skill/SKILL-dev.md +0 -145
- package/dist-skill/docs-dev/API/core/concepts.md +0 -118
- package/dist-skill/docs-dev/CLI.md +0 -677
- package/dist-skill/docs-dev/agent-native-api.md +0 -27
- package/dist-skill/docs-dev/blueprint-first.md +0 -145
- package/dist-skill/docs-dev/coding-best-practices.md +0 -120
- package/dist-skill/docs-dev/coding.md +0 -340
- package/dist-skill/docs-dev/component-model.md +0 -164
- package/dist-skill/docs-dev/generated/assembly.md +0 -794
- package/dist-skill/docs-dev/generated/core.md +0 -2117
- package/dist-skill/docs-dev/generated/curves.md +0 -2583
- package/dist-skill/docs-dev/generated/lib.md +0 -169
- package/dist-skill/docs-dev/generated/output.md +0 -247
- package/dist-skill/docs-dev/generated/sdf.md +0 -446
- package/dist-skill/docs-dev/generated/sheet-metal.md +0 -504
- package/dist-skill/docs-dev/generated/sketch.md +0 -1811
- package/dist-skill/docs-dev/generated/viewport.md +0 -585
- package/dist-skill/docs-dev/generated/wood.md +0 -108
- package/dist-skill/docs-dev/guides/coordinate-system.md +0 -46
- package/dist-skill/docs-dev/guides/geometry-conventions.md +0 -52
- package/dist-skill/docs-dev/guides/inspection-bundles.md +0 -485
- package/dist-skill/docs-dev/guides/joint-design.md +0 -78
- package/dist-skill/docs-dev/guides/modeling-recipes.md +0 -78
- package/dist-skill/docs-dev/guides/positioning.md +0 -161
- package/dist-skill/docs-dev/guides/skill-maintenance.md +0 -161
- package/dist-skill/docs-dev/internals/backend-vocabulary.md +0 -35
- package/dist-skill/docs-dev/internals/compiler.md +0 -307
- package/dist-skill/docs-dev/internals/constraint-solver-quality.md +0 -161
- package/dist-skill/docs-dev/internals/constraint-solver.md +0 -176
- package/dist-skill/docs-dev/internals/sketch-2d-pipeline.md +0 -108
- package/dist-skill/library/forgecad-image-replicator/scripts/compare_images.mjs +0 -289
package/dist-skill/CONTEXT.md
CHANGED
|
@@ -123,7 +123,32 @@ const bomHelpers = require("./bom.js");
|
|
|
123
123
|
bomHelpers.addFasteners(...);
|
|
124
124
|
```
|
|
125
125
|
|
|
126
|
-
Top-level declarations such as `const bom = ...`, `let
|
|
126
|
+
Top-level lexical declarations such as `const bom = ...`, `let slot = ...`, `const lib = ...`, `const joint = ...`, or `class Shape {}` collide with the injected runtime names. This is checked when the script runs, so agents should treat these names as reserved before executing the model.
|
|
127
|
+
|
|
128
|
+
Use project-specific local names instead:
|
|
129
|
+
|
|
130
|
+
```javascript
|
|
131
|
+
// BAD — `lib` and `slot` are injected runtime names.
|
|
132
|
+
const lib = require("./wheel-lib.js");
|
|
133
|
+
const slot = rect(20, 5);
|
|
134
|
+
|
|
135
|
+
// GOOD — local names describe the project role.
|
|
136
|
+
const wheelLib = require("./wheel-lib.js");
|
|
137
|
+
const axleSlotSketch = rect(20, 5);
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
If a helper module exports a name that matches a ForgeCAD runtime name, keep the module under one local object name or rename during destructuring:
|
|
141
|
+
|
|
142
|
+
```javascript
|
|
143
|
+
// GOOD — no top-level `slot` declaration.
|
|
144
|
+
const wheelHelpers = require("./wheel-helpers.js");
|
|
145
|
+
const axleSlot = wheelHelpers.slot(...);
|
|
146
|
+
|
|
147
|
+
// GOOD — imported helper is renamed.
|
|
148
|
+
const { slot: makeAxleSlot } = require("./wheel-helpers.js");
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
The complete collision-reserved list is generated from the runtime source in [Runtime Names](../../generated/runtime-names.md). Check that list before using natural local names such as `lib`, `slot`, `joint`, `group`, `path`, `text2d`, `scene`, or `Shape`.
|
|
127
152
|
|
|
128
153
|
## Execution Model
|
|
129
154
|
|
|
@@ -162,6 +187,44 @@ return {
|
|
|
162
187
|
};
|
|
163
188
|
```
|
|
164
189
|
|
|
190
|
+
### Forge-Aware Builder Modules
|
|
191
|
+
|
|
192
|
+
A `.forge.js` file can also return builder functions. Those functions keep access to
|
|
193
|
+
the ForgeCAD runtime names from their defining file, so this is the supported way
|
|
194
|
+
to share sketch, profile, shape, or assembly builders that need APIs such as
|
|
195
|
+
`path()`, `circle2d()`, `box()`, or `assembly()`.
|
|
196
|
+
|
|
197
|
+
This pattern works well when a file should be both directly inspectable and
|
|
198
|
+
importable: render the flat profiles or part preview when the file is opened on
|
|
199
|
+
its own, and export builders under a named object for the assembly file.
|
|
200
|
+
|
|
201
|
+
```javascript
|
|
202
|
+
// profiles.forge.js - inspectable on its own, reusable through require()
|
|
203
|
+
function wheelProfile() {
|
|
204
|
+
return circle2d(40).subtract(circle2d(18));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return {
|
|
208
|
+
preview: [{ name: 'Wheel profile', sketch: wheelProfile() }],
|
|
209
|
+
make: { wheelProfile },
|
|
210
|
+
};
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
```javascript
|
|
214
|
+
// main.forge.js - reuses the exact inspected profile
|
|
215
|
+
const profiles = require('./profiles.forge.js');
|
|
216
|
+
const wheel = profiles.make.wheelProfile().extrude(8);
|
|
217
|
+
return wheel;
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Keep builder modules pure over top-level constants, top-level `param()` values,
|
|
221
|
+
or explicit function arguments. Do not declare new `param()` values inside an
|
|
222
|
+
exported builder if callers need `require('./profiles.forge.js', { Width: 80 })`
|
|
223
|
+
overrides: import overrides are validated while the module loads, before any
|
|
224
|
+
exported builder is called. For plain constants, tables, math helpers, and
|
|
225
|
+
formatting code that does not construct ForgeCAD geometry, use `.js` helper
|
|
226
|
+
modules instead.
|
|
227
|
+
|
|
165
228
|
Named return objects and named `group(...)` children can include `tags`. Tags are viewport metadata: they do not affect geometry, exports, face labels, or BOM rows, but the command palette can hide, show only, or focus every object with a selected tag.
|
|
166
229
|
|
|
167
230
|
```javascript
|
|
@@ -214,6 +277,47 @@ For organic shapes, smooth blending, TPMS lattices, and surface deformations. Re
|
|
|
214
277
|
|
|
215
278
|
---
|
|
216
279
|
|
|
280
|
+
<!-- generated/runtime-names.md -->
|
|
281
|
+
|
|
282
|
+
# Runtime Names
|
|
283
|
+
|
|
284
|
+
Generated by `scripts/gen-api-docs.mjs` from `src/forge/script-runtime/runScript.ts`. Do not edit by hand.
|
|
285
|
+
|
|
286
|
+
ForgeCAD injects API functions, classes, namespaces, and sandbox guard names into every `.forge.js` script. Top-level lexical declarations using these names collide with the injected runtime bindings when the script runs.
|
|
287
|
+
|
|
288
|
+
Agents should avoid declaring these names with top-level `const`, `let`, destructured imports, or `class` declarations. Use project-specific local names such as `wheelLib`, `axleSlotSketch`, `driveJointConfig`, or `labelTextSketch` instead.
|
|
289
|
+
|
|
290
|
+
These collision-reserved names are case-sensitive:
|
|
291
|
+
|
|
292
|
+
```text
|
|
293
|
+
activateBackend, Analysis, arcSlot, assembly, Assembly, assemblyInstructions, assemblyPreview, Blend
|
|
294
|
+
bom, bomToCsv, boolParam, box, cameraTrajectory, Carrier, chamfer, chamferTrackedEdge
|
|
295
|
+
choiceParam, circle, circle2d, Circle2D, circularLayout, circularPattern, circularPattern2d, coalesceEdges
|
|
296
|
+
combine, COMMON_KERFS, compareWith, composeChain, connectEdges, connector, console, constrainedSketch
|
|
297
|
+
Constraint, Counterbore, Curve, Curve3D, cutPlane, cylinder, degrees, difference
|
|
298
|
+
difference2d, dim, dimLine, draft, ellipse, explodeView, faceProfile, fillet
|
|
299
|
+
filletCorners, filletTrackedEdge, fingerJoint, flatPanel, flatPart, formatInstructions, Function, gcode
|
|
300
|
+
GCodeBuilder, getActiveBackend, global, globalThis, group, Helix, HelixCurve, hermiteTransitionG2
|
|
301
|
+
highlight, Import, ImportedAssembly, importMesh, importStep, importSvgSketch, initKernel, intersection
|
|
302
|
+
intersection2d, intersectWithPlane, joint, jointsView, laserKit, lib, line, Line2D
|
|
303
|
+
linearPattern, linearPattern2d, listParam, loadFont, loft, Loft, loftAlongSpine, lookupKerf
|
|
304
|
+
mirrorCopy, mock, ngon, nurbs3d, NurbsCurve3D, nurbsSurface, NurbsSurface, offsetSolid
|
|
305
|
+
param, Param, path, point, Point2D, Points, polygon, polygonVertices
|
|
306
|
+
port, Product, ProductHandleBuilder, ProductHandleFeature, ProductPanelBuilder, ProductRibbonBuilder, ProductSkin, ProductSkinBuilder
|
|
307
|
+
ProductSpoutBuilder, ProductStationBuilder, ProductSurfaceBuilder, ProductSurfaceRef, projectToPlane, queueMicrotask, radians, rect
|
|
308
|
+
Rectangle2D, Ribs, robotExport, roundedRect, Route3D, scene, Sculpt, sdf
|
|
309
|
+
SdfShape, selectEdge, selectEdges, self, setActiveBackend, setImmediate, setInterval, setTimeout
|
|
310
|
+
Shape, ShapeGroup, sheetMetal, SheetMetalPart, sheetStock, Sketch, sketchToDxf, sketchToSvg
|
|
311
|
+
slot, Slot, SolvedAssembly, spec, sphere, spline2d, spline3d, star
|
|
312
|
+
stroke, Surface, SurfaceBody, SurfaceMembers, surfacePatch, sweep, tabSlot, text2d
|
|
313
|
+
textWidth, torus, toShape, Transform, transitionCurve, transitionSurface, union, union2d
|
|
314
|
+
variableSweep, verify, viewConfig, Viewport, window, Wood
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
`showLabels` is also a runtime global, but it is not part of the top-level collision check. Avoid reusing it unless you intentionally want a local value with that name.
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
217
321
|
<!-- generated/core.md -->
|
|
218
322
|
|
|
219
323
|
# Core API
|
|
@@ -238,6 +342,8 @@ For organic shapes, smooth blending, TPMS lattices, and surface deformations. Re
|
|
|
238
342
|
- [SurfacePattern](#surfacepattern)
|
|
239
343
|
- [Pattern2D](#pattern2d)
|
|
240
344
|
- [Pattern2DBuilder](#pattern2dbuilder)
|
|
345
|
+
- [HermiteCurve3D](#hermitecurve3d)
|
|
346
|
+
- [QuinticHermiteCurve3D](#quintichermitecurve3d)
|
|
241
347
|
- [ShapeRef](#shaperef)
|
|
242
348
|
- [ANCHOR3D_NAMES](#anchor3d-names)
|
|
243
349
|
- [verify](#verify)
|
|
@@ -577,9 +683,9 @@ selectEdges(shape: Shape, query?: EdgeQuery): EdgeSegment[]
|
|
|
577
683
|
| `minLength?` | `number` | Filter: minimum edge length. |
|
|
578
684
|
| `maxLength?` | `number` | Filter: maximum edge length. |
|
|
579
685
|
| `within?` | `BoundingRegion` | Filter: edge midpoint must be within this bounding region. |
|
|
580
|
-
| `atZ?` | `number` | Shorthand: edge midpoint Z
|
|
581
|
-
| `tolerance?` | `number` | Position tolerance for approximate matches
|
|
582
|
-
| `angleTolerance?` | `number` | Angular tolerance in degrees for `parallel`/`perpendicular` filters
|
|
686
|
+
| `atZ?` | `number` | Shorthand: edge midpoint Z is approximately this value within `tolerance`. |
|
|
687
|
+
| `tolerance?` | `number` | Position tolerance for approximate matches. Used by `atZ` and `near`. Default: `1.0`. |
|
|
688
|
+
| `angleTolerance?` | `number` | Angular tolerance in degrees for `parallel`/`perpendicular` filters. Default: `10`. |
|
|
583
689
|
|
|
584
690
|
`BoundingRegion`: `{ xMin?: number, xMax?: number, yMin?: number, yMax?: number, zMin?: number, zMax?: number }`
|
|
585
691
|
|
|
@@ -631,7 +737,16 @@ coalesceEdges(segments: EdgeSegment[], tolerance?: number): EdgeSegment[]
|
|
|
631
737
|
|
|
632
738
|
#### `require()` — Import a module with optional ForgeCAD parameter overrides. Returns the module's exports.
|
|
633
739
|
|
|
634
|
-
When importing a `.forge.js` file,
|
|
740
|
+
When importing a `.forge.js` file, most return values are passed through exactly as the script returns them. Assembly returns have one extra composition rule: an unsolved [`Assembly`](/docs/assembly#assembly) is wrapped as an [`ImportedAssembly`](/docs/assembly#importedassembly), preserving `solve(state)` and `mergeInto()` across file boundaries, while a returned [`SolvedAssembly`](/docs/assembly#solvedassembly) stays a [`SolvedAssembly`](/docs/assembly#solvedassembly). If the script returns a metadata object (e.g. `{ shape: myShape, bolts: {...} }`), the caller receives the full object — renderable values and metadata together.
|
|
741
|
+
|
|
742
|
+
**Assembly return contract**
|
|
743
|
+
|
|
744
|
+
| `.forge.js` return value | `require()` result |
|
|
745
|
+
|---|---|
|
|
746
|
+
| `Assembly` | `ImportedAssembly` |
|
|
747
|
+
| `SolvedAssembly` | `SolvedAssembly` |
|
|
748
|
+
|
|
749
|
+
[`ImportedAssembly`](/docs/assembly#importedassembly) exposes default-pose helpers such as `getPart()`, `collisionReport()`, and `minClearance()`. Use `solve(state)` first when inspecting a non-default pose.
|
|
635
750
|
|
|
636
751
|
**Path rule:** Always include the file extension in relative imports: use `require("./part.forge.js")` for model files and `require("./helpers.js")` for plain helper modules. ForgeCAD does not apply Node-style extension inference, so `require("./part")` will not find `part.forge.js` or `part.js`.
|
|
637
752
|
|
|
@@ -662,6 +777,26 @@ mount.bolts.pos // access the metadata
|
|
|
662
777
|
mount.shape // access the geometry
|
|
663
778
|
```
|
|
664
779
|
|
|
780
|
+
**Forge-aware builder module pattern** — use `.forge.js` modules for reusable sketch, profile, shape, or assembly builders that need ForgeCAD runtime APIs:
|
|
781
|
+
|
|
782
|
+
```js
|
|
783
|
+
// profiles.forge.js — inspectable on its own, reusable through require()
|
|
784
|
+
function wheelProfile() {
|
|
785
|
+
return circle2d(40).subtract(circle2d(18));
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
return {
|
|
789
|
+
preview: [{ name: 'Wheel profile', sketch: wheelProfile() }],
|
|
790
|
+
make: { wheelProfile },
|
|
791
|
+
};
|
|
792
|
+
|
|
793
|
+
// main.forge.js
|
|
794
|
+
const profiles = require('./profiles.forge.js');
|
|
795
|
+
const wheel = profiles.make.wheelProfile().extrude(8);
|
|
796
|
+
```
|
|
797
|
+
|
|
798
|
+
Keep exported builders pure over top-level constants, top-level `param()` values, or explicit function arguments. Do not declare new `param()` values inside an exported builder if callers need `require('./profiles.forge.js', { Width: 80 })` overrides: import overrides are validated while the module loads, before any exported builder is called. Use plain `.js` modules only for pure constants, tables, math helpers, and formatting code that does not construct ForgeCAD geometry.
|
|
799
|
+
|
|
665
800
|
```ts
|
|
666
801
|
require(path: string, paramOverrides?: Record<string, number | string>): any
|
|
667
802
|
```
|
|
@@ -690,10 +825,18 @@ importSvgSketch(fileName: string, options?: SvgImportOptions): Sketch
|
|
|
690
825
|
| `simplify?` | `number` | Simplification tolerance for final sketch cleanup. |
|
|
691
826
|
| `invertY?` | `boolean` | Flip SVG Y-down coordinates to CAD Y-up. Enabled by default. |
|
|
692
827
|
|
|
693
|
-
#### `importMesh()` — Import an external mesh file (STL, OBJ, 3MF)
|
|
828
|
+
#### `importMesh()` — Import an external mesh file (STL, OBJ, 3MF).
|
|
829
|
+
|
|
830
|
+
By default, 3MF build items are flattened into one Shape for compatibility. Use `separateObjects: true` to import 3MF build items/resource objects as a named ShapeGroup whose children are targetable by `forgecad ls`. Use `object` to import one item by the stable ref/name reported by `forgecad run`.
|
|
831
|
+
|
|
832
|
+
```js
|
|
833
|
+
const all = importMesh("./assembly.3mf", { separateObjects: true });
|
|
834
|
+
const pin = all.child("Pin #001");
|
|
835
|
+
const plate = importMesh("./assembly.3mf", { object: "3mf:build:001:object:7" });
|
|
836
|
+
```
|
|
694
837
|
|
|
695
838
|
```ts
|
|
696
|
-
importMesh(fileName: string, options?: { scale?: number; center?: boolean; }): Shape
|
|
839
|
+
importMesh(fileName: string, options?: { scale?: number; center?: boolean; object?: string; separateObjects?: boolean; }): Shape | ShapeGroup
|
|
697
840
|
```
|
|
698
841
|
|
|
699
842
|
#### `importStep()` — Import a STEP file (.step, .stp) as an exact OCCT-backed Shape. Preserves NURBS curves, B-spline surfaces, and exact topology. Requires running with the OCCT backend.
|
|
@@ -1166,6 +1309,25 @@ box(100, 100, 10).color('#gold').material({ metalness: 0.95, roughness: 0.05 }).
|
|
|
1166
1309
|
material(props: ShapeMaterialProps): Shape
|
|
1167
1310
|
```
|
|
1168
1311
|
|
|
1312
|
+
**`ShapeMaterialProps`**
|
|
1313
|
+
|
|
1314
|
+
| Option | Type | Description |
|
|
1315
|
+
|--------|------|-------------|
|
|
1316
|
+
| `metalness?` | `number` | Metalness factor (0 = dielectric, 1 = metal). Default: 0.05 |
|
|
1317
|
+
| `roughness?` | `number` | Roughness factor (0 = mirror, 1 = fully diffuse). Default: 0.35 |
|
|
1318
|
+
| `emissive?` | `string` | Emissive glow color (hex string, e.g. "#ff6b35"). |
|
|
1319
|
+
| `emissiveIntensity?` | `number` | Emissive intensity multiplier. Default: 1 |
|
|
1320
|
+
| `opacity?` | `number` | Opacity (0 = fully transparent, 1 = fully opaque). Default: 1 |
|
|
1321
|
+
| `wireframe?` | `boolean` | Render as wireframe. Default: false |
|
|
1322
|
+
| `clearcoat?` | `number` | Clearcoat intensity (0–1). Default: 0.1 |
|
|
1323
|
+
| `clearcoatRoughness?` | `number` | Clearcoat roughness (0–1). Default: 0.4 |
|
|
1324
|
+
| `transmission?` | `number` | Glass/translucency transmission factor (0–1). Renderer support depends on target. |
|
|
1325
|
+
| `ior?` | `number` | Index of refraction for transmissive materials. Typical glass is ~1.45. |
|
|
1326
|
+
| `thickness?` | `number` | Approximate transmissive volume thickness in model units. |
|
|
1327
|
+
| `specularIntensity?` | `number` | Specular highlight intensity (0–1). |
|
|
1328
|
+
| `specularColor?` | `string` | Specular highlight tint. |
|
|
1329
|
+
| `reflectivity?` | `number` | Reflection strength for supported renderers (0–1). |
|
|
1330
|
+
|
|
1169
1331
|
**Face Topology**
|
|
1170
1332
|
|
|
1171
1333
|
#### `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.
|
|
@@ -1286,6 +1448,15 @@ body.edgesOf('top', { concave: true })
|
|
|
1286
1448
|
edgesOf(faceLabel: string, options?: EdgesOfOptions): EdgeSegment[]
|
|
1287
1449
|
```
|
|
1288
1450
|
|
|
1451
|
+
**`EdgesOfOptions`**
|
|
1452
|
+
|
|
1453
|
+
| Option | Type | Description |
|
|
1454
|
+
|--------|------|-------------|
|
|
1455
|
+
| `exclude?` | `string \| string[]` | Exclude edges shared with these named faces. |
|
|
1456
|
+
| `convex?` | `boolean` | Additional geometric filter: only convex edges. |
|
|
1457
|
+
| `concave?` | `boolean` | Additional geometric filter: only concave edges. |
|
|
1458
|
+
| `minLength?` | `number` | Minimum edge length filter. |
|
|
1459
|
+
|
|
1289
1460
|
#### `edgesBetween()` — Return edges shared between two named faces.
|
|
1290
1461
|
|
|
1291
1462
|
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."
|
|
@@ -1367,6 +1538,8 @@ rotateZ(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): Sh
|
|
|
1367
1538
|
rotateAroundTo(axis: [ number, number, number ], pivot: [ number, number, number ], movingPoint: RotationPointLike, targetPoint: RotationPointLike, options?: RotateAroundToOptions): Shape
|
|
1368
1539
|
```
|
|
1369
1540
|
|
|
1541
|
+
`RotateAroundToOptions`: `{ mode?: RotateAroundToMode }`
|
|
1542
|
+
|
|
1370
1543
|
#### `transform()` — Apply a 4x4 affine transform matrix (column-major) or a Transform object.
|
|
1371
1544
|
|
|
1372
1545
|
```ts
|
|
@@ -1463,6 +1636,11 @@ box(100, 100, 20).pocket('top', 8, { scale: 0.8 })
|
|
|
1463
1636
|
pocket(face: FaceSelector, depth: number, opts?: PocketOptions): Shape
|
|
1464
1637
|
```
|
|
1465
1638
|
|
|
1639
|
+
**`PocketOptions`**
|
|
1640
|
+
- `inset?: number` — Shrink the face boundary inward by this many mm before extruding. Produces angled walls when combined with depth. Default: 0 (full face).
|
|
1641
|
+
- `scale?: number` — Scale the face profile uniformly (e.g. 0.8 = 80% of the face area). Mutually exclusive with `inset`; `inset` takes precedence if both are set.
|
|
1642
|
+
- `join?: "Square" | "Round" | "Miter"` — Corner join style when using `inset`. Default: 'Round'.
|
|
1643
|
+
|
|
1466
1644
|
#### `boss()` — Add a boss (protrusion) from the named face.
|
|
1467
1645
|
|
|
1468
1646
|
```js
|
|
@@ -1485,6 +1663,30 @@ box(50, 50, 20).hole('top', { diameter: 6, counterbore: { diameter: 12, depth: 3
|
|
|
1485
1663
|
hole(faceOrRef: SketchFaceTarget | FaceRef, opts: ShapeHoleOptions): Shape
|
|
1486
1664
|
```
|
|
1487
1665
|
|
|
1666
|
+
**`FaceRef`**
|
|
1667
|
+
|
|
1668
|
+
| Option | Type | Description |
|
|
1669
|
+
|--------|------|-------------|
|
|
1670
|
+
| `normal` | `[ number, number, number ]` | Normal direction of the face |
|
|
1671
|
+
| `center` | `[ number, number, number ]` | Center point of the face |
|
|
1672
|
+
| `query?` | `FaceQueryRef` | Compiler-owned face query when available. |
|
|
1673
|
+
| `planar?` | `boolean` | True when the face can host a 2D sketch placement frame |
|
|
1674
|
+
| `uAxis?` | `[ number, number, number ]` | Face-local horizontal axis for planar faces |
|
|
1675
|
+
| `vAxis?` | `[ number, number, number ]` | Face-local vertical axis for planar faces |
|
|
1676
|
+
| `surface?` | `FaceSurface` | Analytic surface family when the backend can identify one. |
|
|
1677
|
+
| `descendant?` | `FaceDescendantMetadata` | Shared descendant-resolution metadata when this face is a semantic region/set. |
|
|
1678
|
+
| `name` | | — |
|
|
1679
|
+
|
|
1680
|
+
**`FaceDescendantMetadata`**: `kind: "single" | "face-set"`, `semantic: FaceDescendantSemantic`, `memberCount: number`, `memberNames: string[]`, `coplanar: boolean`
|
|
1681
|
+
|
|
1682
|
+
**`ShapeHoleOptions`**: `diameter: number`, `depth?: number`, `upToFace?: SketchFaceTarget | FaceRef`, `extent?: ShapeFeatureExtentOptions`, `u?: number`, `v?: number`, `counterbore?: { diameter: number; depth: number; }`, `countersink?: { diameter: number; angleDeg?: number; }`, `thread?: ShapeHoleThreadOptions`
|
|
1683
|
+
|
|
1684
|
+
`ShapeFeatureExtentOptions`: `{ forward: ShapeFeatureExtentSideOptions, reverse?: ShapeFeatureExtentSideOptions }`
|
|
1685
|
+
|
|
1686
|
+
`ShapeFeatureExtentSideOptions`: `{ depth?: number, upToFace?: SketchFaceTarget | FaceRef, through?: boolean }`
|
|
1687
|
+
|
|
1688
|
+
**`ShapeHoleThreadOptions`**: `designation?: string`, `pitch?: number`, `class?: string`, `handedness?: "right" | "left"`, `depth?: number`, `modeled?: boolean`
|
|
1689
|
+
|
|
1488
1690
|
#### `cutout()` — Cut a profile-shaped pocket through a face using a placed sketch.
|
|
1489
1691
|
|
|
1490
1692
|
The sketch must be placed on a face with `Sketch.onFace(...)`. The cut follows the sketch's 2D profile.
|
|
@@ -1498,6 +1700,8 @@ body.cutout(profile, { depth: 5 })
|
|
|
1498
1700
|
cutout(sketch: Sketch, opts?: ShapeCutoutOptions): Shape
|
|
1499
1701
|
```
|
|
1500
1702
|
|
|
1703
|
+
**`ShapeCutoutOptions`**: `depth?: number`, `upToFace?: SketchFaceTarget | FaceRef`, `extent?: ShapeFeatureExtentOptions`, `taperScale?: number | [ number, number ]`
|
|
1704
|
+
|
|
1501
1705
|
**Placement**
|
|
1502
1706
|
|
|
1503
1707
|
#### `placeReference()` — Translate the shape so the given anchor or reference lands on the target coordinate.
|
|
@@ -1561,6 +1765,11 @@ mast.translate(0, station, radius + 50).seatInto(fuselage, 'mount', { depth: 'fl
|
|
|
1561
1765
|
seatInto(target: Shape, surface: string, options?: SeatIntoOptions): Shape
|
|
1562
1766
|
```
|
|
1563
1767
|
|
|
1768
|
+
**`SeatIntoOptions`**
|
|
1769
|
+
- `along?: [ number, number, number ]` — Movement axis. Default: inverted face normal (points into target).
|
|
1770
|
+
- `depth?: "full" | "flush" | number` — How deep to embed. 'full' = entire face inside. 'flush' = nearest point touches. number = mm past flush. Default: 'full'.
|
|
1771
|
+
- `gap?: number` — Standoff gap in mm. Positive = gap between face and target. Negative = extra penetration. Default: 0.
|
|
1772
|
+
|
|
1564
1773
|
#### `seatOver()` — Slide this shape until a target's labeled face is fully covered (inside this shape).
|
|
1565
1774
|
|
|
1566
1775
|
The inverse of `seatInto`: instead of embedding *your* face into the target, you move until the *target's* face is embedded inside you.
|
|
@@ -1585,6 +1794,10 @@ seatOver(target: Shape, targetSurface: string, options?: SeatIntoOptions): Shape
|
|
|
1585
1794
|
withConnectors(connectors: Record<string, ConnectorInput>): Shape
|
|
1586
1795
|
```
|
|
1587
1796
|
|
|
1797
|
+
**`PortInput`**: `origin?: [ number, number, number ]`, `axis?: [ number, number, number ]`, `start?: [ number, number, number ]`, `end?: [ number, number, number ]`, `up?: [ number, number, number ]`, `kind?: JointType`, `min?: number`, `max?: number`
|
|
1798
|
+
|
|
1799
|
+
`ConnectorInput`: `{ connectorType?: string, gender?: ConnectorGender, measurements?: Record<string, number | string> }`
|
|
1800
|
+
|
|
1588
1801
|
#### `connectorNames()` — List all connector names on this shape.
|
|
1589
1802
|
|
|
1590
1803
|
```ts
|
|
@@ -1621,6 +1834,8 @@ Overloads:
|
|
|
1621
1834
|
matchTo(targetOrPairs: Shape | MatchTarget | Array<[ Shape | MatchTarget, string, string ]>, selfConnOrDict?: string | Record<string, string>, targetConnOrOptions?: string | MatchToOptions, maybeOptions?: MatchToOptions): Shape
|
|
1622
1835
|
```
|
|
1623
1836
|
|
|
1837
|
+
`MatchToOptions`: `{ force?: boolean, angle?: number, distance?: number }`
|
|
1838
|
+
|
|
1624
1839
|
**References**
|
|
1625
1840
|
|
|
1626
1841
|
#### `withReferences()` — Attach named placement references that survive normal transforms and imports.
|
|
@@ -1629,6 +1844,12 @@ matchTo(targetOrPairs: Shape | MatchTarget | Array<[ Shape | MatchTarget, string
|
|
|
1629
1844
|
withReferences(refs: PlacementReferenceInput): Shape
|
|
1630
1845
|
```
|
|
1631
1846
|
|
|
1847
|
+
**`PlacementReferenceInput`**: `points?: Record<string, [ number, number, number ]>`, `edges?: Record<string, PlacementEdgeRef>`, `surfaces?: Record<string, PlacementSurfaceRef>`, `objects?: Record<string, PlacementObjectInput>`
|
|
1848
|
+
|
|
1849
|
+
`PlacementEdgeRef`: `{ start: Vec3, end: Vec3 }`
|
|
1850
|
+
|
|
1851
|
+
`PlacementSurfaceRef`: `{ center: Vec3, normal: Vec3 }`
|
|
1852
|
+
|
|
1632
1853
|
#### `referenceNames()` — List named placement references carried by this shape.
|
|
1633
1854
|
|
|
1634
1855
|
```ts
|
|
@@ -1790,6 +2011,24 @@ translate(x: number, y: number, z: number): Transform
|
|
|
1790
2011
|
rotateAxis(axis: Vec3, angleDeg: number, pivot?: Vec3): Transform
|
|
1791
2012
|
```
|
|
1792
2013
|
|
|
2014
|
+
#### `rotateX()` — Rotate about the X axis after the current transform (parity with `Shape.rotateX`).
|
|
2015
|
+
|
|
2016
|
+
```ts
|
|
2017
|
+
rotateX(angleDeg: number, pivot?: Vec3): Transform
|
|
2018
|
+
```
|
|
2019
|
+
|
|
2020
|
+
#### `rotateY()` — Rotate about the Y axis after the current transform (parity with `Shape.rotateY`).
|
|
2021
|
+
|
|
2022
|
+
```ts
|
|
2023
|
+
rotateY(angleDeg: number, pivot?: Vec3): Transform
|
|
2024
|
+
```
|
|
2025
|
+
|
|
2026
|
+
#### `rotateZ()` — Rotate about the Z axis after the current transform (parity with `Shape.rotateZ`).
|
|
2027
|
+
|
|
2028
|
+
```ts
|
|
2029
|
+
rotateZ(angleDeg: number, pivot?: Vec3): Transform
|
|
2030
|
+
```
|
|
2031
|
+
|
|
1793
2032
|
#### `inverse()` — Return the inverse transform.
|
|
1794
2033
|
|
|
1795
2034
|
```ts
|
|
@@ -2133,18 +2372,160 @@ constant(value?: number): Pattern2D
|
|
|
2133
2372
|
sineWave(options: Pattern2DSineWaveOptions): Pattern2D
|
|
2134
2373
|
```
|
|
2135
2374
|
|
|
2375
|
+
**`Pattern2DSineWaveOptions`**
|
|
2376
|
+
|
|
2377
|
+
| Option | Type | Description |
|
|
2378
|
+
|--------|------|-------------|
|
|
2379
|
+
| `direction?` | `Vec2` | Direction the wave advances in UV space. Default: [1, 0]. |
|
|
2380
|
+
| `wavelength` | `number` | Distance between wave peaks in surface millimeters. |
|
|
2381
|
+
| `amplitude?` | `number` | Height amplitude in millimeters. Default: 1. |
|
|
2382
|
+
| `phase?` | `number` | Phase offset in radians. Default: 0. |
|
|
2383
|
+
| `bias?` | `number` | Constant height offset in millimeters. Default: 0. |
|
|
2384
|
+
|
|
2136
2385
|
#### `stripes()` — Create recessed stripe bands in UV space.
|
|
2137
2386
|
|
|
2138
2387
|
```ts
|
|
2139
2388
|
stripes(options: Pattern2DStripesOptions): Pattern2D
|
|
2140
2389
|
```
|
|
2141
2390
|
|
|
2391
|
+
**`Pattern2DStripesOptions`**
|
|
2392
|
+
|
|
2393
|
+
| Option | Type | Description |
|
|
2394
|
+
|--------|------|-------------|
|
|
2395
|
+
| `direction?` | `Vec2` | Direction perpendicular to the stripe bands in UV space. Default: [1, 0]. |
|
|
2396
|
+
| `spacing` | `number` | Center-to-center spacing in surface millimeters. |
|
|
2397
|
+
| `width` | `number` | Stripe width in surface millimeters. |
|
|
2398
|
+
| `depth?` | `number` | Stripe groove depth in millimeters. Default: 1. |
|
|
2399
|
+
|
|
2142
2400
|
#### `overUnderWeave()` — Create an over-under woven relief pattern in UV space.
|
|
2143
2401
|
|
|
2144
2402
|
```ts
|
|
2145
2403
|
overUnderWeave(options: Pattern2DOverUnderWeaveOptions): Pattern2D
|
|
2146
2404
|
```
|
|
2147
2405
|
|
|
2406
|
+
**`Pattern2DOverUnderWeaveOptions`**
|
|
2407
|
+
|
|
2408
|
+
| Option | Type | Description |
|
|
2409
|
+
|--------|------|-------------|
|
|
2410
|
+
| `spacing` | `number \| Vec2` | Thread center-to-center spacing. A number uses the same spacing for U and V. |
|
|
2411
|
+
| `threadWidth` | `number \| Vec2` | Thread width. A number uses the same width for U and V. |
|
|
2412
|
+
| `depth?` | `number` | Thread groove depth in millimeters. Default: 0.8. |
|
|
2413
|
+
| `underScale?` | `number` | Relative height of the under-crossing thread. Default: 0.15. |
|
|
2414
|
+
|
|
2415
|
+
### `HermiteCurve3D`
|
|
2416
|
+
|
|
2417
|
+
**Properties:**
|
|
2418
|
+
|
|
2419
|
+
| Property | Type | Description |
|
|
2420
|
+
|----------|------|-------------|
|
|
2421
|
+
| `p0` | `Vec3` | Start position |
|
|
2422
|
+
| `p1` | `Vec3` | End position |
|
|
2423
|
+
| `t0` | `Vec3` | Scaled tangent at start (direction * weight * chordLength) |
|
|
2424
|
+
| `t1` | `Vec3` | Scaled tangent at end (direction * weight * chordLength) |
|
|
2425
|
+
| `chordLength` | `number` | Chord length (straight-line distance between endpoints) |
|
|
2426
|
+
|
|
2427
|
+
**Methods:**
|
|
2428
|
+
|
|
2429
|
+
#### `pointAt()` — Evaluate position at parameter t ∈ [0, 1]
|
|
2430
|
+
|
|
2431
|
+
```ts
|
|
2432
|
+
pointAt(t: number): Vec3
|
|
2433
|
+
```
|
|
2434
|
+
|
|
2435
|
+
#### `tangentAt()` — Evaluate tangent (first derivative) at parameter t ∈ [0, 1]
|
|
2436
|
+
|
|
2437
|
+
```ts
|
|
2438
|
+
tangentAt(t: number): Vec3
|
|
2439
|
+
```
|
|
2440
|
+
|
|
2441
|
+
#### `curvatureAt()` — Evaluate curvature vector (second derivative) at parameter t ∈ [0, 1]
|
|
2442
|
+
|
|
2443
|
+
```ts
|
|
2444
|
+
curvatureAt(t: number): Vec3
|
|
2445
|
+
```
|
|
2446
|
+
|
|
2447
|
+
#### `sample()` — Sample the curve as a polyline of evenly-spaced parameter values.
|
|
2448
|
+
|
|
2449
|
+
```ts
|
|
2450
|
+
sample(count?: number): Vec3[]
|
|
2451
|
+
```
|
|
2452
|
+
|
|
2453
|
+
#### `length()` — Approximate arc length by sampling.
|
|
2454
|
+
|
|
2455
|
+
```ts
|
|
2456
|
+
length(samples?: number): number
|
|
2457
|
+
```
|
|
2458
|
+
|
|
2459
|
+
#### `sampleAdaptive()` — Sample with adaptive density — more points where curvature is higher. Returns at least `minCount` points, up to `maxCount`.
|
|
2460
|
+
|
|
2461
|
+
```ts
|
|
2462
|
+
sampleAdaptive(minCount?: number, maxCount?: number): Vec3[]
|
|
2463
|
+
```
|
|
2464
|
+
|
|
2465
|
+
#### `toPolyline()` — Convert to a format compatible with sweep() path input.
|
|
2466
|
+
|
|
2467
|
+
```ts
|
|
2468
|
+
toPolyline(samples?: number): Vec3[]
|
|
2469
|
+
```
|
|
2470
|
+
|
|
2471
|
+
### `QuinticHermiteCurve3D`
|
|
2472
|
+
|
|
2473
|
+
**Properties:**
|
|
2474
|
+
|
|
2475
|
+
| Property | Type | Description |
|
|
2476
|
+
|----------|------|-------------|
|
|
2477
|
+
| `p0` | `Vec3` | Start position |
|
|
2478
|
+
| `p1` | `Vec3` | End position |
|
|
2479
|
+
| `t0` | `Vec3` | Scaled tangent at start (direction * weight * chordLength) |
|
|
2480
|
+
| `t1` | `Vec3` | Scaled tangent at end (direction * weight * chordLength) |
|
|
2481
|
+
| `c0` | `Vec3` | Scaled second derivative at start (curvature * weight² * chordLength²) |
|
|
2482
|
+
| `c1` | `Vec3` | Scaled second derivative at end (curvature * weight² * chordLength²) |
|
|
2483
|
+
| `chordLength` | `number` | Chord length (straight-line distance between endpoints) |
|
|
2484
|
+
|
|
2485
|
+
**Methods:**
|
|
2486
|
+
|
|
2487
|
+
#### `pointAt()` — Evaluate position at parameter t ∈ [0, 1]
|
|
2488
|
+
|
|
2489
|
+
```ts
|
|
2490
|
+
pointAt(t: number): Vec3
|
|
2491
|
+
```
|
|
2492
|
+
|
|
2493
|
+
#### `tangentAt()` — Evaluate tangent (first derivative, normalized) at parameter t ∈ [0, 1]
|
|
2494
|
+
|
|
2495
|
+
```ts
|
|
2496
|
+
tangentAt(t: number): Vec3
|
|
2497
|
+
```
|
|
2498
|
+
|
|
2499
|
+
#### `curvatureAt()` — Evaluate curvature vector (second derivative) at parameter t ∈ [0, 1]
|
|
2500
|
+
|
|
2501
|
+
```ts
|
|
2502
|
+
curvatureAt(t: number): Vec3
|
|
2503
|
+
```
|
|
2504
|
+
|
|
2505
|
+
#### `sample()` — Sample the curve as a polyline of evenly-spaced parameter values.
|
|
2506
|
+
|
|
2507
|
+
```ts
|
|
2508
|
+
sample(count?: number): Vec3[]
|
|
2509
|
+
```
|
|
2510
|
+
|
|
2511
|
+
#### `length()` — Approximate arc length by sampling.
|
|
2512
|
+
|
|
2513
|
+
```ts
|
|
2514
|
+
length(samples?: number): number
|
|
2515
|
+
```
|
|
2516
|
+
|
|
2517
|
+
#### `sampleAdaptive()` — Sample with adaptive density — more points where curvature is higher. Returns at least `minCount` points, up to `maxCount`.
|
|
2518
|
+
|
|
2519
|
+
```ts
|
|
2520
|
+
sampleAdaptive(minCount?: number, maxCount?: number): Vec3[]
|
|
2521
|
+
```
|
|
2522
|
+
|
|
2523
|
+
#### `toPolyline()` — Convert to a format compatible with sweep() path input.
|
|
2524
|
+
|
|
2525
|
+
```ts
|
|
2526
|
+
toPolyline(samples?: number): Vec3[]
|
|
2527
|
+
```
|
|
2528
|
+
|
|
2148
2529
|
### `ShapeRef`
|
|
2149
2530
|
|
|
2150
2531
|
A first-class reference path over a shape's semantic faces and face relationships.
|
|
@@ -2337,39 +2718,43 @@ Namespaced file import helpers for formats that should not add new lowercase glo
|
|
|
2337
2718
|
|
|
2338
2719
|
ForgeCAD uses a **Z-up** right-handed coordinate system.
|
|
2339
2720
|
|
|
2721
|
+
For objects with a clear facing direction, model the front/face/nose/camera side toward **-Y**. The rear/back side is **+Y**. In code, a forward/fore direction vector is `[0, -1, 0]`.
|
|
2722
|
+
|
|
2340
2723
|
## Axes
|
|
2341
2724
|
|
|
2342
2725
|
| Axis | Direction | Positive |
|
|
2343
2726
|
|------|-----------------|----------|
|
|
2344
2727
|
| X | Left / Right | Right |
|
|
2345
|
-
| Y |
|
|
2728
|
+
| Y | Front / Back | Back |
|
|
2346
2729
|
| Z | Up / Down | Up |
|
|
2347
2730
|
|
|
2348
2731
|
## Standard Views
|
|
2349
2732
|
|
|
2733
|
+
The camera position direction says where the camera sits relative to the model. A front view camera sits at `-Y` and looks toward `+Y`, so it sees the model's `-Y` front face.
|
|
2734
|
+
|
|
2350
2735
|
| View | Camera position direction | Sees plane |
|
|
2351
|
-
|
|
2352
|
-
| Front |
|
|
2353
|
-
| Back | +Y
|
|
2736
|
+
|--------|---------------------------|------------|
|
|
2737
|
+
| Front | -Y | XZ |
|
|
2738
|
+
| Back | +Y | XZ |
|
|
2354
2739
|
| Right | +X | YZ |
|
|
2355
|
-
| Left |
|
|
2740
|
+
| Left | -X | YZ |
|
|
2356
2741
|
| Top | +Z | XY |
|
|
2357
|
-
| Bottom |
|
|
2742
|
+
| Bottom | -Z | XY |
|
|
2358
2743
|
|
|
2359
2744
|
## GizmoViewcube Face Mapping
|
|
2360
2745
|
|
|
2361
|
-
|
|
2746
|
+
Renderer/view-cube internals may have their own material ordering. Map any view-cube labels to the ForgeCAD directions below:
|
|
2362
2747
|
|
|
2363
|
-
|
|
|
2364
|
-
|
|
2365
|
-
|
|
|
2366
|
-
|
|
|
2367
|
-
|
|
|
2368
|
-
|
|
|
2369
|
-
|
|
|
2370
|
-
|
|
|
2748
|
+
| Direction | ForgeCAD label |
|
|
2749
|
+
|-----------|----------------|
|
|
2750
|
+
| +X | Right |
|
|
2751
|
+
| -X | Left |
|
|
2752
|
+
| +Y | Back |
|
|
2753
|
+
| -Y | Front |
|
|
2754
|
+
| +Z | Top |
|
|
2755
|
+
| -Z | Bottom |
|
|
2371
2756
|
|
|
2372
|
-
|
|
2757
|
+
The face/anchor API is the source of truth: `front` resolves to the minimum-Y side and `back` resolves to the maximum-Y side.
|
|
2373
2758
|
|
|
2374
2759
|
## Grid
|
|
2375
2760
|
|
|
@@ -2397,7 +2782,7 @@ Three.js is Y-up; ForgeCAD is Z-up. Fix applied at camera level (`camera.up = (0
|
|
|
2397
2782
|
|
|
2398
2783
|
## Revolution Axis
|
|
2399
2784
|
|
|
2400
|
-
`
|
|
2785
|
+
`Sketch.revolve()` produces a world Z-axis solid of revolution. Profile X = radial distance from the Z axis, Profile Y = world Z height after revolution. Profile should stay at X > 0 unless intentionally touching the axis.
|
|
2401
2786
|
|
|
2402
2787
|
## Boolean Winding (3D)
|
|
2403
2788
|
|
|
@@ -2423,7 +2808,7 @@ Prefer `composeChain(...)` over manual `.mul(...).mul(...)` in kinematics code t
|
|
|
2423
2808
|
|---|---|---|---|
|
|
2424
2809
|
| Winding | Any point order | CCW | `polygon()`, `path().close()` |
|
|
2425
2810
|
| Up axis | Z-up | Y-up (Three.js) | `camera.up`, gizmo labels |
|
|
2426
|
-
| Revolution | "revolve this profile" | Profile
|
|
2811
|
+
| Revolution | "revolve this profile" | Profile X = radius, Profile Y = world Z height | Documented and regression-tested |
|
|
2427
2812
|
| Face normals | Doesn't think about it | Outward-pointing | Manifold constructors |
|
|
2428
2813
|
| Transform order | Left-to-right chain | Post-multiply | Native match |
|
|
2429
2814
|
|
|
@@ -2439,6 +2824,16 @@ For any fixed assembly where parts are meant to stay in contact in the final mod
|
|
|
2439
2824
|
|
|
2440
2825
|
Use raw `translate()` and `rotate()` when parts are intentionally free-floating or when you are doing quick exploratory layout. Use `attachTo()` for rough bounding-box placement. But if the relationship is a real interface, make it explicit with connectors.
|
|
2441
2826
|
|
|
2827
|
+
## Mechanisms: connector frames vs link points
|
|
2828
|
+
|
|
2829
|
+
For serial articulated parts (hinges, hips, knees, levers, wheels), use `assembly().connect()` with connectors. `connect()` aligns the full connector frame: `origin` is the pivot/contact point, `axis` is the hinge line or slide direction, and `up` locks the zero-angle twist of the child part. `up` is a local roll reference, not world up; author it explicitly whenever the rest pose matters.
|
|
2830
|
+
|
|
2831
|
+
Do not use `addPart(..., { mate: { connector, toLink } })` when the part must point along a bone or inherit orientation from a link edge. Link mates are point attachments only: they translate the connector origin onto the solved link position and preserve the part's existing rotation. That is good for markers, sensors, labels, and debug handles. It is not a bone-frame API.
|
|
2832
|
+
|
|
2833
|
+
Use link graphs (`link()`, `edgeBetweenLinks()`, `addAngleBetweenLinks()`) when the hard part is solving point positions, especially closed loops. Use connector-frame joints when the hard part is orienting real physical parts.
|
|
2834
|
+
|
|
2835
|
+
For bilateral mechanisms, remember that connector-frame revolute values are physical axis-local values. Mirrored hinge axes need negated physical revolute values for the same mirrored pose, and physical limits mirror as `[min, max] -> [-max, -min]`. If you want `HipR: 10` and `HipL: 10` to mean the same semantic pose, drive a mirrored link graph or write an explicit state mapping instead of assuming equal connector-joint values are symmetric.
|
|
2836
|
+
|
|
2442
2837
|
## Primitive origin convention
|
|
2443
2838
|
|
|
2444
2839
|
All 3D primitives are **centered on XY, base at Z=0**:
|
|
@@ -2462,14 +2857,14 @@ Most positioning bugs come from manual coordinate arithmetic. Use these methods
|
|
|
2462
2857
|
|
|
2463
2858
|
## 1. Connectors + `matchTo()` — default for mating interfaces
|
|
2464
2859
|
|
|
2465
|
-
Define connectors on parts; `matchTo()` provides automatic
|
|
2860
|
+
Define connectors on parts; `matchTo()` provides automatic alignment. With one connector pair, the child translates and rotates so its connector aligns with the target's — origins coincide, axes oppose (plug-in model), and `up` pins the roll reference. With multiple connector pairs, the connector origins define the rigid transform; still author meaningful `axis` and `up` values so the same connectors remain useful for `connect()`, audits, and future matching.
|
|
2466
2861
|
|
|
2467
2862
|
```javascript
|
|
2468
2863
|
const shelf = box(200, 120, 10).translate(0, 0, -5).withConnectors({
|
|
2469
|
-
left_tab: connector.male("dovetail", { origin: [-100, 0, 0], axis: [-1, 0, 0] }),
|
|
2864
|
+
left_tab: connector.male("dovetail", { origin: [-100, 0, 0], axis: [-1, 0, 0], up: [0, 0, 1] }),
|
|
2470
2865
|
});
|
|
2471
2866
|
const panel = box(12, 120, 200).translate(0, 0, -100).withConnectors({
|
|
2472
|
-
shelf_0: connector.female("dovetail", { origin: [6, 0, -50], axis: [1, 0, 0] }),
|
|
2867
|
+
shelf_0: connector.female("dovetail", { origin: [6, 0, -50], axis: [1, 0, 0], up: [0, 0, 1] }),
|
|
2473
2868
|
});
|
|
2474
2869
|
const placed = shelf.matchTo(panel, "left_tab", "shelf_0");
|
|
2475
2870
|
// Dictionary form for multiple pairs on same target:
|
|
@@ -3089,7 +3484,6 @@ addRegularPolygon(sk: ConstrainedSketchBuilder, options: RegularPolygonOptions):
|
|
|
3089
3484
|
| `startAngle?` | `number` | Angle (in degrees) of vertex[0] measured from the +X axis (CCW positive). Default: 0 (rightmost vertex). |
|
|
3090
3485
|
| `blockRotation?` | `boolean` | Prevent 180° rotation (ensures first edge maintains its initial direction). Default: false. |
|
|
3091
3486
|
|
|
3092
|
-
|
|
3093
3487
|
**`ConstrainedRegularPolygon`** extends ConstrainedPolygon
|
|
3094
3488
|
- `center: PointId` — Center point. Use `sk.fix(poly.center, x, y)` to pin location, or `sk.coincident(poly.center, other)` to align with other geometry.
|
|
3095
3489
|
|
|
@@ -3329,7 +3723,7 @@ region(seed: [ number, number ]): Sketch
|
|
|
3329
3723
|
extrude(height: number, opts?: { twist?: number; divisions?: number; scaleTop?: number | [ number, number ]; }): Shape
|
|
3330
3724
|
```
|
|
3331
3725
|
|
|
3332
|
-
#### `revolve()` — Revolve this 2D sketch around the
|
|
3726
|
+
#### `revolve()` — Revolve this 2D sketch around the world Z axis. Sketch X is radius; sketch Y becomes world Z height.
|
|
3333
3727
|
|
|
3334
3728
|
```ts
|
|
3335
3729
|
revolve(degrees?: number, segments?: number): Shape
|
|
@@ -3360,9 +3754,25 @@ Use this when a 2D profile should be oriented onto a 3D face before extrusion or
|
|
|
3360
3754
|
onFace(parentOrFace: Shape | { toShape(): Shape; } | { _bbox(): { min: number[]; max: number[]; }; } | FaceRef, faceOrOpts?: "front" | "back" | "left" | "right" | "top" | "bottom" | string | FaceRef | { u?: number; v?: number; protrude?: number; selfAnchor?: Anchor; }, opts?: { u?: number; v?: number; protrude?: number; selfAnchor?: Anchor; }): Sketch
|
|
3361
3755
|
```
|
|
3362
3756
|
|
|
3363
|
-
|
|
3757
|
+
**`FaceRef`**
|
|
3364
3758
|
|
|
3365
|
-
|
|
3759
|
+
| Option | Type | Description |
|
|
3760
|
+
|--------|------|-------------|
|
|
3761
|
+
| `normal` | `[ number, number, number ]` | Normal direction of the face |
|
|
3762
|
+
| `center` | `[ number, number, number ]` | Center point of the face |
|
|
3763
|
+
| `query?` | `FaceQueryRef` | Compiler-owned face query when available. |
|
|
3764
|
+
| `planar?` | `boolean` | True when the face can host a 2D sketch placement frame |
|
|
3765
|
+
| `uAxis?` | `[ number, number, number ]` | Face-local horizontal axis for planar faces |
|
|
3766
|
+
| `vAxis?` | `[ number, number, number ]` | Face-local vertical axis for planar faces |
|
|
3767
|
+
| `surface?` | `FaceSurface` | Analytic surface family when the backend can identify one. |
|
|
3768
|
+
| `descendant?` | `FaceDescendantMetadata` | Shared descendant-resolution metadata when this face is a semantic region/set. |
|
|
3769
|
+
| `name` | | — |
|
|
3770
|
+
|
|
3771
|
+
**`FaceDescendantMetadata`**: `kind: "single" | "face-set"`, `semantic: FaceDescendantSemantic`, `memberCount: number`, `memberNames: string[]`, `coplanar: boolean`
|
|
3772
|
+
|
|
3773
|
+
**Labels**
|
|
3774
|
+
|
|
3775
|
+
#### `labelEdge()` — Label the single boundary edge (for circles, single-loop profiles). Returns a new sketch.
|
|
3366
3776
|
|
|
3367
3777
|
```ts
|
|
3368
3778
|
labelEdge(name: string): Sketch
|
|
@@ -3658,6 +4068,16 @@ regularPolygon(options: RegularPolygonOptions): ConstrainedRegularPolygon
|
|
|
3658
4068
|
groupRect(options: GroupRectOptions): ConstrainedGroupRect
|
|
3659
4069
|
```
|
|
3660
4070
|
|
|
4071
|
+
**`GroupRectOptions`**
|
|
4072
|
+
|
|
4073
|
+
| Option | Type | Description |
|
|
4074
|
+
|--------|------|-------------|
|
|
4075
|
+
| `x?` | `number` | Bottom-left x coordinate (world). Default: 0. |
|
|
4076
|
+
| `y?` | `number` | Bottom-left y coordinate (world). Default: 0. |
|
|
4077
|
+
| `width` | `number` | Width (along x in local coords). Required. |
|
|
4078
|
+
| `height` | `number` | Height (along y in local coords). Required. |
|
|
4079
|
+
| `allowRotation?` | `boolean` | Allow the solver to rotate this rectangle. Default: false. |
|
|
4080
|
+
|
|
3661
4081
|
**Geometric Constraints**
|
|
3662
4082
|
|
|
3663
4083
|
#### `horizontal()` — Constrain a line to be horizontal (parallel to the X axis).
|
|
@@ -4001,6 +4421,23 @@ result.withUpdatedConstraint('cst-5', 120); // update a dimension without rebuil
|
|
|
4001
4421
|
solve(options?: SolveOptions): ConstraintSketch | Sketch
|
|
4002
4422
|
```
|
|
4003
4423
|
|
|
4424
|
+
**`SolveOptions`**
|
|
4425
|
+
|
|
4426
|
+
| Option | Type | Description |
|
|
4427
|
+
|--------|------|-------------|
|
|
4428
|
+
| `iterations?` | `number` | Maximum number of LM outer iterations per restart. |
|
|
4429
|
+
| `tolerance?` | `number` | Infinity-norm residual tolerance for declaring convergence. |
|
|
4430
|
+
| `restarts?` | `number` | Number of deterministic restart seeds used by the global solver. |
|
|
4431
|
+
| `warmStartIterations?` | `number` | Optional projector iterations used only for initialisation, not as the main solver. |
|
|
4432
|
+
| `maxScaledStep?` | `number` | Maximum LM step length in scaled variable space. Larger = bolder, smaller = safer. |
|
|
4433
|
+
| `skipRedundancyCheck?` | `boolean` | Skip redundancy detection (safe when topology is unchanged and previous DOF >= 0). |
|
|
4434
|
+
| `presolveConstraintId?` | `string` | Run the targeted presolve hook for this constraint before the main solve. |
|
|
4435
|
+
| `fallbackRestarts?` | `number` | When set and the first solve exceeds tolerance*5, retry with this many restarts. |
|
|
4436
|
+
| `progressive?` | `boolean` | Add constraints progressively with short LM solves, all in one WASM call. |
|
|
4437
|
+
| `timeBudgetMs?` | `number` | Wall-clock time budget in ms for the entire solve. 0 = no limit. |
|
|
4438
|
+
| `debugConstructiveTranscript?` | `boolean` | Capture a readable constructive transcript in `constraintMeta.debug`. |
|
|
4439
|
+
| `debugSvgSnapshots?` | `boolean` | Capture SVG snapshots for constructive steps in `constraintMeta.debug`. |
|
|
4440
|
+
|
|
4004
4441
|
#### `solveConstraintsOnly()` — Run the solver without building a full `ConstraintSketch`.
|
|
4005
4442
|
|
|
4006
4443
|
Lighter than `solve()` — skips profile and DOF analysis. Useful for lightweight constraint validation or progress monitoring mid-construction.
|
|
@@ -4409,15 +4846,13 @@ Smooth curves, lofted surfaces, swept solids, splines, and high-level product sk
|
|
|
4409
4846
|
|
|
4410
4847
|
## Contents
|
|
4411
4848
|
|
|
4412
|
-
- [Curves & Surfacing](#curves-surfacing) — `
|
|
4849
|
+
- [Curves & Surfacing](#curves-surfacing) — `Curve.Blend`, `Curve.BlendG2`, `Curve.Arc`, `Curve.Line`, `Curve.Polyline`, `Curve.Spline`, `Curve.Nurbs`, `Curve.Fit`, `Curve.Trim`, `Curve.Reverse`, `Curve.Route`, `Curve.Helix`, `Loft.station`, `Loft.leftRail`, `Loft.rightRail`, `Loft.frontRail`, `Loft.backRail`, `Loft.centerRail`, `Loft.pathOnXz`, `Loft.pathOnYz`, `Loft.pathOnXy`, `Loft.withGuideRails`, `spline2d`, `loft`, `sweep`, `variableSweep`, `nurbsSurface`, `surfacePatch`
|
|
4413
4850
|
- [Surface Members](#surface-members) — `surfaceBand`, `SurfaceBody`
|
|
4414
4851
|
- [Curve3D](#curve3d)
|
|
4415
|
-
- [
|
|
4852
|
+
- [Route3D](#route3d)
|
|
4416
4853
|
- [NurbsCurve3D](#nurbscurve3d)
|
|
4417
4854
|
- [NurbsSurface](#nurbssurface)
|
|
4418
4855
|
- [PathBuilder](#pathbuilder) — Line Segments, Arcs, Curves, Closing & Output
|
|
4419
|
-
- [HermiteCurve3D](#hermitecurve3d)
|
|
4420
|
-
- [QuinticHermiteCurve3D](#quintichermitecurve3d)
|
|
4421
4856
|
- [ProductSkin](#productskin)
|
|
4422
4857
|
- [ProductSurfaceRef](#productsurfaceref)
|
|
4423
4858
|
- [ProductSurfaceBuilder](#productsurfacebuilder)
|
|
@@ -4439,6 +4874,7 @@ Smooth curves, lofted surfaces, swept solids, splines, and high-level product sk
|
|
|
4439
4874
|
- [SurfaceJoinBuilder](#surfacejoinbuilder)
|
|
4440
4875
|
- [CounterboreBuilder](#counterborebuilder)
|
|
4441
4876
|
- [RoundedSlotBuilder](#roundedslotbuilder)
|
|
4877
|
+
- [Curve](#curve)
|
|
4442
4878
|
- [Surface](#surface)
|
|
4443
4879
|
- [Blend](#blend)
|
|
4444
4880
|
- [Analysis](#analysis)
|
|
@@ -4448,97 +4884,186 @@ Smooth curves, lofted surfaces, swept solids, splines, and high-level product sk
|
|
|
4448
4884
|
- [Slot](#slot)
|
|
4449
4885
|
- [Counterbore](#counterbore)
|
|
4450
4886
|
- [Ribs](#ribs)
|
|
4451
|
-
- [Helix](#helix)
|
|
4452
4887
|
|
|
4453
4888
|
## Functions
|
|
4454
4889
|
|
|
4455
4890
|
### Curves & Surfacing
|
|
4456
4891
|
|
|
4457
|
-
#### `
|
|
4892
|
+
#### `Curve.Blend()` — Create an exact G1 blend curve between two directed endpoints.
|
|
4893
|
+
|
|
4894
|
+
The returned curve is a cubic non-rational `NurbsCurve3D`: ForgeCAD converts the endpoint positions and tangents into Bezier control points, so the curve can feed `sweep` and exact surface boundaries through the existing `nurbs` IR rather than a sampled polyline.
|
|
4895
|
+
|
|
4896
|
+
```js
|
|
4897
|
+
const rail = Curve.Blend(
|
|
4898
|
+
{ point: [0, 0, 0], tangent: [1, 0, 0], weight: 0.8 },
|
|
4899
|
+
{ point: [40, 20, 8], tangent: [0, 1, 0], weight: 0.8 },
|
|
4900
|
+
);
|
|
4901
|
+
const tube = sweep(circle2d(2), rail);
|
|
4902
|
+
```
|
|
4458
4903
|
|
|
4459
4904
|
```ts
|
|
4460
|
-
|
|
4905
|
+
Curve.Blend(start: CurveBlendEndpoint, end: CurveBlendEndpoint): NurbsCurve3D
|
|
4461
4906
|
```
|
|
4462
4907
|
|
|
4463
|
-
|
|
4908
|
+
**`CurveBlendEndpoint`**
|
|
4909
|
+
- `point: Vec3` — Endpoint position.
|
|
4910
|
+
- `tangent: Vec3` — Tangent direction at this endpoint. Magnitude is ignored.
|
|
4911
|
+
- `weight?: number` — Tangent reach relative to the endpoint chord length. Default 1.
|
|
4464
4912
|
|
|
4465
|
-
#### `
|
|
4913
|
+
#### `Curve.BlendG2()` — Create an exact G2 blend curve between two directed endpoints.
|
|
4914
|
+
|
|
4915
|
+
This is the curvature-aware companion to `Curve.Blend()`. It returns a degree-5 non-rational `NurbsCurve3D` that matches endpoint position, tangent direction, and optional curvature/second-derivative vectors.
|
|
4916
|
+
|
|
4917
|
+
```js
|
|
4918
|
+
const rail = Curve.BlendG2(
|
|
4919
|
+
{ point: [0, 0, 0], tangent: [1, 0, 0], curvature: [0, 0.02, 0] },
|
|
4920
|
+
{ point: [50, 20, 0], tangent: [0, 1, 0], curvature: [-0.02, 0, 0] },
|
|
4921
|
+
);
|
|
4922
|
+
```
|
|
4466
4923
|
|
|
4467
4924
|
```ts
|
|
4468
|
-
|
|
4925
|
+
Curve.BlendG2(start: CurveBlendG2Endpoint, end: CurveBlendG2Endpoint): NurbsCurve3D
|
|
4469
4926
|
```
|
|
4470
4927
|
|
|
4471
|
-
|
|
4928
|
+
**`CurveBlendG2Endpoint`** extends CurveBlendEndpoint
|
|
4929
|
+
- `curvature?: Vec3` — Optional endpoint curvature/second-derivative vector. Default is zero.
|
|
4472
4930
|
|
|
4473
|
-
#### `
|
|
4931
|
+
#### `Curve.Arc()` — Create an exact circular 3D arc from start, end, and start tangent.
|
|
4932
|
+
|
|
4933
|
+
The returned curve is a rational quadratic `NurbsCurve3D`, split into stable spans when needed, so it can feed `sweep` without sampling the authoring intent away.
|
|
4934
|
+
|
|
4935
|
+
```js
|
|
4936
|
+
const rail = Curve.Arc({
|
|
4937
|
+
start: [40, 0, 0],
|
|
4938
|
+
end: [0, 40, 0],
|
|
4939
|
+
tangent: [0, 1, 0],
|
|
4940
|
+
});
|
|
4941
|
+
const tube = sweep(circle2d(2), rail);
|
|
4942
|
+
```
|
|
4474
4943
|
|
|
4475
4944
|
```ts
|
|
4476
|
-
|
|
4945
|
+
Curve.Arc(options: CurveArcOptions): NurbsCurve3D
|
|
4477
4946
|
```
|
|
4478
4947
|
|
|
4479
|
-
|
|
4948
|
+
**`CurveArcOptions`**
|
|
4949
|
+
- `start: Vec3` — Arc start point.
|
|
4950
|
+
- `end: Vec3` — Arc end point.
|
|
4951
|
+
- `tangent: Vec3` — Tangent direction at the start point. Magnitude is ignored.
|
|
4952
|
+
|
|
4953
|
+
#### `Curve.Line()` — Create an exact straight 3D NURBS line segment.
|
|
4954
|
+
|
|
4955
|
+
```js
|
|
4956
|
+
const rail = Curve.Line([0, 0, 0], [80, 0, 15]);
|
|
4957
|
+
const rib = sweep(circle2d(2), rail);
|
|
4958
|
+
```
|
|
4480
4959
|
|
|
4481
4960
|
```ts
|
|
4482
|
-
|
|
4961
|
+
Curve.Line(start: Vec3, end: Vec3): NurbsCurve3D
|
|
4483
4962
|
```
|
|
4484
4963
|
|
|
4485
|
-
#### `
|
|
4964
|
+
#### `Curve.Polyline()` — Create a polyline path as cloned 3D points.
|
|
4965
|
+
|
|
4966
|
+
Polylines are exact as route/path input to `sweep`. Use `Curve.Route` when the centerline needs bend and endpoint-frame metadata.
|
|
4486
4967
|
|
|
4487
4968
|
```ts
|
|
4488
|
-
|
|
4969
|
+
Curve.Polyline(points: Vec3[]): Vec3[]
|
|
4489
4970
|
```
|
|
4490
4971
|
|
|
4491
|
-
#### `
|
|
4972
|
+
#### `Curve.Spline()` — Create a smooth Catmull-Rom spline path.
|
|
4973
|
+
|
|
4974
|
+
This is a smooth sampled curve object. Use `Curve.Nurbs` when the path must preserve exact control-point and knot data.
|
|
4492
4975
|
|
|
4493
4976
|
```ts
|
|
4494
|
-
|
|
4977
|
+
Curve.Spline(points: Vec3[], options?: Spline3DOptions): Curve3D
|
|
4495
4978
|
```
|
|
4496
4979
|
|
|
4497
|
-
|
|
4980
|
+
**`Spline3DOptions`**
|
|
4981
|
+
- `closed?: boolean` — Closed loop (default false).
|
|
4982
|
+
- `tension?: number` — Catmull-Rom tension in [0, 1]. 0 = very round, 1 = linear-ish. Default 0.5.
|
|
4498
4983
|
|
|
4499
|
-
|
|
4984
|
+
#### `Curve.Nurbs()` — Create an exact NURBS 3D curve from control points, weights, knots, and degree.
|
|
4985
|
+
|
|
4986
|
+
```js
|
|
4987
|
+
const rail = Curve.Nurbs([[0, 0, 0], [30, 4, 12], [60, -4, 12], [90, 0, 0]]);
|
|
4988
|
+
const tube = sweep(circle2d(2), rail);
|
|
4989
|
+
```
|
|
4500
4990
|
|
|
4501
4991
|
```ts
|
|
4502
|
-
|
|
4992
|
+
Curve.Nurbs(points: Vec3[], options?: NurbsCurve3DOptions): NurbsCurve3D
|
|
4503
4993
|
```
|
|
4504
4994
|
|
|
4505
|
-
|
|
4995
|
+
**`NurbsCurve3DOptions`**
|
|
4506
4996
|
|
|
4507
|
-
|
|
4997
|
+
| Option | Type | Description |
|
|
4998
|
+
|--------|------|-------------|
|
|
4999
|
+
| `degree?` | `number` | Polynomial degree (default 3 = cubic). Must be ≥ 1. |
|
|
5000
|
+
| `weights?` | `number[]` | Rational weights, one per control point (default: all 1.0 = non-rational). |
|
|
5001
|
+
| `knots?` | `number[]` | Knot vector (default: uniform clamped). Must have length = controlPoints.length + degree + 1. |
|
|
5002
|
+
| `closed?` | `boolean` | Whether the curve is closed/periodic (default false). |
|
|
5003
|
+
|
|
5004
|
+
#### `Curve.Fit()` — Fit a non-rational NURBS curve that interpolates every input point.
|
|
5005
|
+
|
|
5006
|
+
This is global B-spline interpolation, not approximate curve reduction: ForgeCAD computes chord-length parameters, averaged clamped knots, solves the control points, then verifies the interpolation residual against `tolerance`.
|
|
5007
|
+
|
|
5008
|
+
```js
|
|
5009
|
+
const rail = Curve.Fit(
|
|
5010
|
+
[[0, 0, 0], [20, 8, 12], [50, -4, 18], [80, 0, 0]],
|
|
5011
|
+
{ degree: 3, tolerance: 0.001 },
|
|
5012
|
+
);
|
|
5013
|
+
const tube = sweep(circle2d(2), rail);
|
|
5014
|
+
```
|
|
4508
5015
|
|
|
4509
5016
|
```ts
|
|
4510
|
-
|
|
5017
|
+
Curve.Fit(points: Vec3[], options?: CurveFitOptions): NurbsCurve3D
|
|
4511
5018
|
```
|
|
4512
5019
|
|
|
4513
|
-
|
|
5020
|
+
**`CurveFitOptions`**
|
|
5021
|
+
- `degree?: number` — Polynomial degree. Default is cubic, reduced automatically for short point lists.
|
|
5022
|
+
- `tolerance?: number` — Maximum allowed interpolation residual in model units. Default 1e-7.
|
|
4514
5023
|
|
|
4515
|
-
|
|
5024
|
+
#### `Curve.Trim()` — Extract an exact curve segment from normalized parameter `start` to `end`.
|
|
5025
|
+
|
|
5026
|
+
`NurbsCurve3D` inputs are trimmed with exact knot insertion/subdomain extraction. Polyline point arrays are trimmed by arclength over their exact line segments. Sampled `Curve3D` splines are rejected until ForgeCAD has a tolerance-controlled rebuild path.
|
|
4516
5027
|
|
|
4517
5028
|
```ts
|
|
4518
|
-
|
|
5029
|
+
Curve.Trim<T extends CurveTrimInput>(curve: T, start: number, end: number): CurveTrimOutput<T>
|
|
4519
5030
|
```
|
|
4520
5031
|
|
|
4521
|
-
#### `
|
|
5032
|
+
#### `Curve.Reverse()` — Reverse an exact curve without changing its geometry.
|
|
4522
5033
|
|
|
4523
|
-
|
|
5034
|
+
`NurbsCurve3D` inputs reverse control points, weights, and knots. Polyline point arrays are cloned and reversed. Sampled `Curve3D` splines are rejected until ForgeCAD has a tolerance-controlled rebuild path.
|
|
4524
5035
|
|
|
4525
5036
|
```ts
|
|
4526
|
-
|
|
5037
|
+
Curve.Reverse<T extends CurveTrimInput>(curve: T): CurveTrimOutput<T>
|
|
4527
5038
|
```
|
|
4528
5039
|
|
|
4529
|
-
|
|
4530
|
-
- `edgeLength?: number` — Marching-grid edge length for level-set meshing. Smaller = finer.
|
|
4531
|
-
- `boundsPadding?: number` — Optional extra bounds padding.
|
|
5040
|
+
#### `Curve.Route()` — Build analytic 3D line/arc routes for sweeps.
|
|
4532
5041
|
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
5042
|
+
`Curve.Route.fromPolyline()` is the canonical route API. It returns a `Route3D` value object, preserving exact route segments, named port frames, and the lowerable `route3d` sweep compile plan.
|
|
5043
|
+
|
|
5044
|
+
```js
|
|
5045
|
+
const route = Curve.Route.fromPolyline(
|
|
5046
|
+
[[0, 0, 0], [0, 0, 50], [40, 0, 50]],
|
|
5047
|
+
{ cornerRadius: 12, startPort: 'inlet', endPort: 'outlet' },
|
|
5048
|
+
);
|
|
5049
|
+
const tube = sweep(circle2d(4), route);
|
|
5050
|
+
```
|
|
5051
|
+
|
|
5052
|
+
```ts
|
|
5053
|
+
Curve.Route: typeof Route3D
|
|
5054
|
+
```
|
|
4537
5055
|
|
|
4538
|
-
#### `Helix
|
|
5056
|
+
#### `Curve.Helix()` — Build helical paths and swept coils.
|
|
5057
|
+
|
|
5058
|
+
`Curve.Helix` is the canonical namespace for helical paths and coils. It uses the same sweep-based lowering as other curve paths.
|
|
5059
|
+
|
|
5060
|
+
```js
|
|
5061
|
+
const guide = Curve.Helix.path({ radius: 20, pitch: 6, turns: 4 });
|
|
5062
|
+
const spring = Curve.Helix.coil({ radius: 20, pitch: 6, turns: 4, wireRadius: 1 });
|
|
5063
|
+
```
|
|
4539
5064
|
|
|
4540
5065
|
```ts
|
|
4541
|
-
Helix
|
|
5066
|
+
Curve.Helix: { path(options: HelixOptions): CurveHelixPath; coil: CurveHelixCoil; }
|
|
4542
5067
|
```
|
|
4543
5068
|
|
|
4544
5069
|
**`HelixOptions`**
|
|
@@ -4553,66 +5078,88 @@ Helix.path(options: HelixOptions): HelixCurve
|
|
|
4553
5078
|
| `clockwise?` | `boolean` | Reverse winding direction when viewed from +Z. |
|
|
4554
5079
|
| `samplesPerTurn?` | `number` | Point samples per turn for the metadata path. Default 32. |
|
|
4555
5080
|
|
|
4556
|
-
|
|
5081
|
+
`CurveHelixPath`: `{ radius: number, pitch: number, turns: number, height: number, startAngle: number, clockwise: boolean }`
|
|
4557
5082
|
|
|
4558
|
-
|
|
5083
|
+
#### `Loft.station()` — Create a loft station from a 2D profile and an axis position.
|
|
5084
|
+
|
|
5085
|
+
```ts
|
|
5086
|
+
Loft.station(profile: Sketch, position: number): LoftStation
|
|
5087
|
+
```
|
|
5088
|
+
|
|
5089
|
+
`LoftStation`: `{ profile: Sketch, position: number }`
|
|
5090
|
+
|
|
5091
|
+
#### `Loft.leftRail()` — Create a guide rail that constrains the section-local negative-X side.
|
|
5092
|
+
|
|
5093
|
+
```ts
|
|
5094
|
+
Loft.leftRail(path: LoftGuideRailPath): LoftGuideRail
|
|
5095
|
+
```
|
|
5096
|
+
|
|
5097
|
+
`LoftGuideRail`: `{ side: LoftGuideRailSide, path: LoftGuideRailPath }`
|
|
4559
5098
|
|
|
4560
|
-
|
|
4561
|
-
- `Helix.coil(profile: Sketch, options: HelixCoilOptions): Shape`
|
|
5099
|
+
#### `Loft.rightRail()` — Create a guide rail that constrains the section-local positive-X side.
|
|
4562
5100
|
|
|
5101
|
+
```ts
|
|
5102
|
+
Loft.rightRail(path: LoftGuideRailPath): LoftGuideRail
|
|
5103
|
+
```
|
|
4563
5104
|
|
|
4564
|
-
|
|
4565
|
-
- `wireRadius?: number` — Radius of the circular wire profile. Required unless a custom profile is passed.
|
|
4566
|
-
- `profileSegments?: number` — Segment count for the default circular wire profile. Default 24.
|
|
4567
|
-
- `divisionsPerTurn?: number` — Sweep path samples per turn. Default 32.
|
|
5105
|
+
#### `Loft.frontRail()` — Create a guide rail that constrains the section-local positive-Y side.
|
|
4568
5106
|
|
|
4569
|
-
|
|
5107
|
+
```ts
|
|
5108
|
+
Loft.frontRail(path: LoftGuideRailPath): LoftGuideRail
|
|
5109
|
+
```
|
|
4570
5110
|
|
|
4571
|
-
|
|
5111
|
+
#### `Loft.backRail()` — Create a guide rail that constrains the section-local negative-Y side.
|
|
4572
5112
|
|
|
4573
5113
|
```ts
|
|
4574
|
-
|
|
5114
|
+
Loft.backRail(path: LoftGuideRailPath): LoftGuideRail
|
|
4575
5115
|
```
|
|
4576
5116
|
|
|
4577
|
-
|
|
5117
|
+
#### `Loft.centerRail()` — Create a guide rail that moves section centers along the loft.
|
|
4578
5118
|
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
| `tangent` | `Vec3` | Tangent direction (will be normalized internally) |
|
|
4583
|
-
| `curvature?` | `Vec3` | Second derivative / curvature vector. Default [0, 0, 0]. |
|
|
4584
|
-
| `weight?` | `number` | Weight: scales tangent magnitude relative to chord length. Default 1.0. |
|
|
5119
|
+
```ts
|
|
5120
|
+
Loft.centerRail(path: LoftGuideRailPath): LoftGuideRail
|
|
5121
|
+
```
|
|
4585
5122
|
|
|
4586
|
-
#### `
|
|
5123
|
+
#### `Loft.pathOnXz()` — Place a 2D guide path onto the XZ plane.
|
|
4587
5124
|
|
|
4588
|
-
|
|
5125
|
+
The path's first coordinate becomes X and its second coordinate becomes Z. Use this for left/right silhouette rails authored with [`path()`](/docs/sketch#path) or [`constrainedSketch()`](/docs/sketch#constrainedsketch).
|
|
4589
5126
|
|
|
4590
|
-
```
|
|
4591
|
-
|
|
4592
|
-
const curve = nurbs3d([[0,0,0], [10,5,0], [20,-5,10], [30,0,5]]);
|
|
4593
|
-
const tube = sweep(circle(2), curve);
|
|
5127
|
+
```ts
|
|
5128
|
+
Loft.pathOnXz(path: LoftPath2D, y?: number): Vec3[]
|
|
4594
5129
|
```
|
|
4595
5130
|
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
)
|
|
5131
|
+
#### `Loft.pathOnYz()` — Place a 2D guide path onto the YZ plane.
|
|
5132
|
+
|
|
5133
|
+
The path's first coordinate becomes Y and its second coordinate becomes Z. Use this for front/back crown rails authored with [`path()`](/docs/sketch#path) or [`constrainedSketch()`](/docs/sketch#constrainedsketch).
|
|
5134
|
+
|
|
5135
|
+
```ts
|
|
5136
|
+
Loft.pathOnYz(path: LoftPath2D, x?: number): Vec3[]
|
|
4602
5137
|
```
|
|
4603
5138
|
|
|
5139
|
+
#### `Loft.pathOnXy()` — Place a 2D guide path onto the XY plane.
|
|
5140
|
+
|
|
5141
|
+
The path's first coordinate becomes X and its second coordinate becomes Y. Use this when lofting along X or Y and a rail lives in a horizontal sketch plane.
|
|
5142
|
+
|
|
4604
5143
|
```ts
|
|
4605
|
-
|
|
5144
|
+
Loft.pathOnXy(path: LoftPath2D, z?: number): Vec3[]
|
|
4606
5145
|
```
|
|
4607
5146
|
|
|
4608
|
-
|
|
5147
|
+
#### `Loft.withGuideRails()` — Loft through profile stations while forcing generated sections to follow guide rails.
|
|
4609
5148
|
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
5149
|
+
Stations define the cross-section family. Guide rails define the side or center paths the loft must pass through. With opposite side rails, the section is scaled to touch both rails. With one side rail, the section keeps its interpolated size unless a center rail is also present.
|
|
5150
|
+
|
|
5151
|
+
```ts
|
|
5152
|
+
Loft.withGuideRails(stations: LoftStation[], rails: LoftGuideRail[], options?: LoftWithGuideRailsOptions): Shape
|
|
5153
|
+
```
|
|
5154
|
+
|
|
5155
|
+
**`LoftOptions`**
|
|
5156
|
+
- `edgeLength?: number` — Marching-grid edge length for level-set meshing. Smaller = finer.
|
|
5157
|
+
- `boundsPadding?: number` — Optional extra bounds padding.
|
|
5158
|
+
|
|
5159
|
+
**`LoftWithGuideRailsOptions`** extends LoftOptions
|
|
5160
|
+
- `axis?: LoftAxis` — Primary station axis. Default Z.
|
|
5161
|
+
- `samples?: number` — Number of generated loft stations including ends. Default scales with station count.
|
|
5162
|
+
- `railSamples?: number` — Number of points sampled from curve-backed rails before axis interpolation. Default 64.
|
|
4616
5163
|
|
|
4617
5164
|
#### `spline2d()` — Build a smooth Catmull-Rom spline sketch from 2D control points.
|
|
4618
5165
|
|
|
@@ -4632,18 +5179,6 @@ spline2d(points: Vec2[], options?: Spline2DOptions): Sketch
|
|
|
4632
5179
|
| `strokeWidth?` | `number` | For open splines, provide stroke width to return a solid Sketch. If omitted for open splines, an error is thrown. |
|
|
4633
5180
|
| `join?` | `"Round" \| "Square"` | Stroke join for open splines. Default 'Round'. |
|
|
4634
5181
|
|
|
4635
|
-
#### `spline3d()` — Create a reusable 3D spline curve object (Catmull-Rom).
|
|
4636
|
-
|
|
4637
|
-
The returned Curve3D provides sample(), pointAt(t), tangentAt(t), and length() for downstream use in sweep() or manual path operations.
|
|
4638
|
-
|
|
4639
|
-
```ts
|
|
4640
|
-
spline3d(points: Vec3[], options?: Spline3DOptions): Curve3D
|
|
4641
|
-
```
|
|
4642
|
-
|
|
4643
|
-
**`Spline3DOptions`**
|
|
4644
|
-
- `closed?: boolean` — Closed loop (default false).
|
|
4645
|
-
- `tension?: number` — Catmull-Rom tension in [0, 1]. 0 = very round, 1 = linear-ish. Default 0.5.
|
|
4646
|
-
|
|
4647
5182
|
#### `loft()` — Loft between multiple sketches along Z stations.
|
|
4648
5183
|
|
|
4649
5184
|
Profiles can differ in topology and vertex count: interpolation is done on signed-distance fields and meshed with level-set extraction. Heights must be strictly increasing. Compatible loft stacks can also stay on the maintained export-backend path.
|
|
@@ -4654,29 +5189,6 @@ Performance note: loft is significantly heavier than primitive/extrude/revolve.
|
|
|
4654
5189
|
loft(profiles: Sketch[], heights: number[], options?: LoftOptions): Shape
|
|
4655
5190
|
```
|
|
4656
5191
|
|
|
4657
|
-
#### `loftAlongSpine()` — Loft between multiple profiles positioned along an arbitrary 3D spine curve.
|
|
4658
|
-
|
|
4659
|
-
Unlike loft() which only supports Z heights, loftAlongSpine() places each profile at a position along a 3D spine, oriented perpendicular to the spine tangent. This enables lofting along curved paths — e.g., a wing root-to-tip transition that follows a swept-back leading edge.
|
|
4660
|
-
|
|
4661
|
-
The tValues array specifies where each profile sits along the spine (0 = start, 1 = end). Must have the same length as profiles and be in [0, 1].
|
|
4662
|
-
|
|
4663
|
-
Internally uses variableSweep infrastructure with SDF interpolation.
|
|
4664
|
-
|
|
4665
|
-
Performance note: uses level-set meshing, heavier than simple loft().
|
|
4666
|
-
|
|
4667
|
-
```ts
|
|
4668
|
-
loftAlongSpine(profiles: Sketch[], spine: Curve3D | Vec3[], tValues: number[], options?: LoftAlongSpineOptions): Shape
|
|
4669
|
-
```
|
|
4670
|
-
|
|
4671
|
-
**`LoftAlongSpineOptions`**
|
|
4672
|
-
|
|
4673
|
-
| Option | Type | Description |
|
|
4674
|
-
|--------|------|-------------|
|
|
4675
|
-
| `samples?` | `number` | Number of samples when spine is a Curve3D. Default 48. |
|
|
4676
|
-
| `edgeLength?` | `number` | Marching-grid edge length for level-set meshing. Smaller = finer. |
|
|
4677
|
-
| `boundsPadding?` | `number` | Optional extra bounds padding. |
|
|
4678
|
-
| `up?` | `Vec3` | Preferred "up" vector for local profile frame. Auto fallback is used near parallel segments. |
|
|
4679
|
-
|
|
4680
5192
|
#### `sweep()`
|
|
4681
5193
|
|
|
4682
5194
|
```ts
|
|
@@ -4800,106 +5312,6 @@ surfacePatch(curves: { ... }, options?: SurfacePatchOptions): Shape
|
|
|
4800
5312
|
- `thickness?: number` — Thickness of the generated solid. Default 0 for an open exact sheet.
|
|
4801
5313
|
- `approximate?: boolean` — Allow explicit approximation for non-exact curve inputs such as Curve3D samples.
|
|
4802
5314
|
|
|
4803
|
-
#### `transitionCurve()` — Create a smooth transition curve between two edges.
|
|
4804
|
-
|
|
4805
|
-
Returns a `HermiteCurve3D` that starts at `edgeA.point` tangent to `edgeA.tangent` and ends at `edgeB.point` tangent to `edgeB.tangent`.
|
|
4806
|
-
|
|
4807
|
-
The curve maintains G1 continuity (matching tangent direction) at both endpoints. Weight parameters control the shape of the transition.
|
|
4808
|
-
|
|
4809
|
-
```js
|
|
4810
|
-
// Connect two edges with a balanced transition
|
|
4811
|
-
const curve = transitionCurve(
|
|
4812
|
-
{ point: [0, 0, 0], tangent: [1, 0, 0] },
|
|
4813
|
-
{ point: [10, 5, 0], tangent: [1, 0, 0] },
|
|
4814
|
-
);
|
|
4815
|
-
```
|
|
4816
|
-
|
|
4817
|
-
// Weighted: curve hugs edge A longer const weighted = transitionCurve( { point: [0, 0, 0], tangent: [1, 0, 0] }, { point: [10, 5, 0], tangent: [1, 0, 0] }, { weightA: 2.0, weightB: 0.5 }, );
|
|
4818
|
-
|
|
4819
|
-
```
|
|
4820
|
-
|
|
4821
|
-
```ts
|
|
4822
|
-
transitionCurve(edgeA: TransitionEdge, edgeB: TransitionEdge, options?: TransitionCurveOptions): HermiteCurve3D
|
|
4823
|
-
```
|
|
4824
|
-
|
|
4825
|
-
**`TransitionEdge`**
|
|
4826
|
-
- `point: Vec3` — Connection point on the edge. Can be any point along the edge where the transition should connect.
|
|
4827
|
-
- `tangent: Vec3` — Tangent direction at the connection point. This is the direction the curve should initially follow when leaving this edge. For a straight edge, this is typically the edge direction pointing "outward" (away from the body of the edge, toward the other edge).
|
|
4828
|
-
- `normal?: Vec3` — Surface normal at the connection point (optional). Used as a hint for the sweep frame's up vector.
|
|
4829
|
-
|
|
4830
|
-
**`TransitionCurveOptions`**
|
|
4831
|
-
- `weightA?: number` — Weight for the start edge. Controls tangent magnitude at the start. - 1.0 (default): balanced transition - > 1.0: curve follows start edge longer before turning - < 1.0: curve turns sooner at the start
|
|
4832
|
-
- `weightB?: number` — Weight for the end edge. Controls tangent magnitude at the end. - 1.0 (default): balanced transition - > 1.0: curve follows end edge longer before turning - < 1.0: curve turns sooner at the end
|
|
4833
|
-
- `samples?: number` — Number of sample points for the output polyline. Default 64. Higher values give smoother curves at the cost of more geometry.
|
|
4834
|
-
|
|
4835
|
-
#### `transitionSurface()` — Create a solid transition surface between two edges by sweeping a profile along a Hermite transition curve.
|
|
4836
|
-
|
|
4837
|
-
This produces a watertight solid that smoothly connects the two edges. Works with both Manifold and OCCT backends.
|
|
4838
|
-
|
|
4839
|
-
```js
|
|
4840
|
-
// Circular tube connecting two edges
|
|
4841
|
-
const tube = transitionSurface(
|
|
4842
|
-
{ point: [0, 0, 0], tangent: [1, 0, 0] },
|
|
4843
|
-
{ point: [10, 5, 3], tangent: [0, 1, 0] },
|
|
4844
|
-
{ radius: 0.5 },
|
|
4845
|
-
);
|
|
4846
|
-
```
|
|
4847
|
-
|
|
4848
|
-
// Custom profile with weights const custom = transitionSurface( { point: [0, 0, 0], tangent: [1, 0, 0] }, { point: [10, 5, 3], tangent: [0, 1, 0] }, { profile: mySketch, weightA: 1.5, weightB: 0.8 }, );
|
|
4849
|
-
|
|
4850
|
-
```
|
|
4851
|
-
|
|
4852
|
-
```ts
|
|
4853
|
-
transitionSurface(edgeA: TransitionEdge, edgeB: TransitionEdge, options?: TransitionSurfaceOptions): Shape
|
|
4854
|
-
```
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
**`TransitionSurfaceOptions`** extends TransitionCurveOptions
|
|
4858
|
-
|
|
4859
|
-
| Option | Type | Description |
|
|
4860
|
-
|--------|------|-------------|
|
|
4861
|
-
| `profile?` | `Sketch` | Cross-section profile to sweep along the transition curve. If omitted, a circular profile with `radius` is used. |
|
|
4862
|
-
| `radius?` | `number` | Radius of circular cross-section (used when `profile` is omitted). Default: 5% of chord length. |
|
|
4863
|
-
| `rectangleSection?` | `{ width: number; height: number; }` | Width and height for rectangular cross-section. Alternative to `radius` when `profile` is omitted. |
|
|
4864
|
-
| `up?` | `Vec3` | Preferred up vector for the sweep frame. Default: auto-detected. |
|
|
4865
|
-
| `edgeLength?` | `number` | Edge length for level-set meshing. Smaller = finer. |
|
|
4866
|
-
| `boundsPadding?` | `number` | Extra bounds padding for level-set meshing. |
|
|
4867
|
-
|
|
4868
|
-
#### `connectEdges()` — Create a transition surface or solid bridge between two edge segments.
|
|
4869
|
-
|
|
4870
|
-
Tangents can be inferred from neighboring geometry or supplied explicitly through `options`. This is useful for loft-like blends where you want a direct connection between two edge spans.
|
|
4871
|
-
|
|
4872
|
-
```ts
|
|
4873
|
-
connectEdges(edgeA: EdgeSegment, edgeB: EdgeSegment, options?: ConnectEdgesOptions): Shape
|
|
4874
|
-
```
|
|
4875
|
-
|
|
4876
|
-
**`EdgeSegment`**
|
|
4877
|
-
|
|
4878
|
-
| Option | Type | Description |
|
|
4879
|
-
|--------|------|-------------|
|
|
4880
|
-
| `index` | `number` | Stable index within the extraction (deterministic for a given mesh). |
|
|
4881
|
-
| `direction` | `Vec3` | Normalized direction from start → end. |
|
|
4882
|
-
| `dihedralAngle` | `number` | Dihedral angle in degrees (0 = coplanar, 180 = knife edge). |
|
|
4883
|
-
| `convex` | `boolean` | true = outside corner (convex), false = inside corner (concave). |
|
|
4884
|
-
| `normalA` | `Vec3` | Normal of first adjacent face. |
|
|
4885
|
-
| `normalB` | `Vec3` | Normal of second adjacent face (same as normalA for boundary edges). |
|
|
4886
|
-
| `boundary` | `boolean` | true if this is a boundary (unmatched) edge — unusual for closed solids. |
|
|
4887
|
-
| `start`, `end`, `midpoint`, `length` | | — |
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
**`ConnectEdgesOptions`** extends TransitionSurfaceOptions
|
|
4891
|
-
|
|
4892
|
-
| Option | Type | Description |
|
|
4893
|
-
|--------|------|-------------|
|
|
4894
|
-
| `endA?` | `EdgeEnd` | Which end of edge A to connect. Default: 'start'. |
|
|
4895
|
-
| `endB?` | `EdgeEnd` | Which end of edge B to connect. Default: 'start'. |
|
|
4896
|
-
| `tangentModeA?` | `TangentMode` | Tangent mode for edge A. Default: 'along'. |
|
|
4897
|
-
| `tangentModeB?` | `TangentMode` | Tangent mode for edge B. Default: 'along'. |
|
|
4898
|
-
| `tangentA?` | `Vec3` | Explicit tangent for edge A. |
|
|
4899
|
-
| `tangentB?` | `Vec3` | Explicit tangent for edge B. |
|
|
4900
|
-
| `flipA?` | `boolean` | Flip tangent A. |
|
|
4901
|
-
| `flipB?` | `boolean` | Flip tangent B. |
|
|
4902
|
-
|
|
4903
5315
|
### Surface Members
|
|
4904
5316
|
|
|
4905
5317
|
#### `surfaceBand()`
|
|
@@ -4979,47 +5391,76 @@ tangentAt(t: number): Vec3
|
|
|
4979
5391
|
length(samples?: number): number
|
|
4980
5392
|
```
|
|
4981
5393
|
|
|
4982
|
-
### `
|
|
5394
|
+
### `Route3D`
|
|
4983
5395
|
|
|
4984
|
-
Metadata-bearing
|
|
5396
|
+
Metadata-bearing analytic 3D route made from line and arc segments.
|
|
4985
5397
|
|
|
4986
|
-
|
|
5398
|
+
Use `Curve.Route.fromPolyline()` when you know the virtual design skeleton points and bend radius. ForgeCAD computes tangent trim points, bend arcs, total length, and named start/end port frames. Pass the route directly to `sweep()`.
|
|
4987
5399
|
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
5400
|
+
```js
|
|
5401
|
+
const route = Curve.Route.fromPolyline(
|
|
5402
|
+
[[0, 0, 0], [0, 0, 80], [60, 0, 80]],
|
|
5403
|
+
{ cornerRadius: 24, startPort: "inlet", endPort: "outlet" },
|
|
5404
|
+
);
|
|
5405
|
+
const pipe = sweep(difference2d(circle2d(8), circle2d(6)), route);
|
|
5406
|
+
const outlet = route.port("outlet");
|
|
5407
|
+
```
|
|
4996
5408
|
|
|
4997
|
-
|
|
5409
|
+
#### `fromPolyline()` — Build a line/arc route from virtual polyline corner points.
|
|
4998
5410
|
|
|
4999
|
-
|
|
5411
|
+
```ts
|
|
5412
|
+
static fromPolyline(points: Route3DVec3[], options?: Route3DFromPolylineOptions): Route3D
|
|
5413
|
+
```
|
|
5414
|
+
|
|
5415
|
+
**`Route3DFromPolylineOptions`**
|
|
5416
|
+
|
|
5417
|
+
| Option | Type | Description |
|
|
5418
|
+
|--------|------|-------------|
|
|
5419
|
+
| `cornerRadius?` | `number` | Bend radius applied to every virtual interior corner. Default 0 keeps sharp polyline corners. |
|
|
5420
|
+
| `startPort?` | `string` | Name for the start port. Default "start". |
|
|
5421
|
+
| `endPort?` | `string` | Name for the end port. Default "end". |
|
|
5422
|
+
| `up?` | `Vec3` | Preferred up vector for deterministic port frames. Default [0, 0, 1]. |
|
|
5423
|
+
|
|
5424
|
+
#### `length()` — Total centerline length, including line and bend arc segments.
|
|
5000
5425
|
|
|
5001
5426
|
```ts
|
|
5002
|
-
|
|
5427
|
+
get length(): number
|
|
5003
5428
|
```
|
|
5004
5429
|
|
|
5005
|
-
#### `
|
|
5430
|
+
#### `segments()` — Exact line and arc segments that make up this route.
|
|
5006
5431
|
|
|
5007
5432
|
```ts
|
|
5008
|
-
|
|
5433
|
+
get segments(): Route3DSegment[]
|
|
5009
5434
|
```
|
|
5010
5435
|
|
|
5011
|
-
#### `
|
|
5436
|
+
#### `ports()` — Named port frames, keyed by port name.
|
|
5012
5437
|
|
|
5013
5438
|
```ts
|
|
5014
|
-
|
|
5439
|
+
get ports(): Record<string, RoutePortFrame>
|
|
5015
5440
|
```
|
|
5016
5441
|
|
|
5017
|
-
#### `
|
|
5442
|
+
#### `port()` — Return one named route port frame.
|
|
5443
|
+
|
|
5444
|
+
```ts
|
|
5445
|
+
port(name: string): RoutePortFrame
|
|
5446
|
+
```
|
|
5447
|
+
|
|
5448
|
+
#### `toSweepPathPlan()` — Convert this route to the compile plan consumed by sweep().
|
|
5018
5449
|
|
|
5019
5450
|
```ts
|
|
5020
|
-
|
|
5451
|
+
toSweepPathPlan(): SweepPathCompilePlan
|
|
5021
5452
|
```
|
|
5022
5453
|
|
|
5454
|
+
#### `toPolyline()` — Sample this analytic route as a polyline for inspection or backend lowering.
|
|
5455
|
+
|
|
5456
|
+
```ts
|
|
5457
|
+
toPolyline(options?: number | Route3DToPolylineOptions): Route3DVec3[]
|
|
5458
|
+
```
|
|
5459
|
+
|
|
5460
|
+
**`Route3DToPolylineOptions`**
|
|
5461
|
+
- `samples?: number` — Approximate target point count for the full route.
|
|
5462
|
+
- `maxAngleDeg?: number` — Maximum angular spacing on arc segments. Default 6 degrees.
|
|
5463
|
+
|
|
5023
5464
|
### `NurbsCurve3D`
|
|
5024
5465
|
|
|
5025
5466
|
**Properties:**
|
|
@@ -5393,120 +5834,6 @@ toPolyline(): [ number, number ][]
|
|
|
5393
5834
|
closeOffset(delta: number, join?: "Round" | "Square" | "Miter"): Sketch
|
|
5394
5835
|
```
|
|
5395
5836
|
|
|
5396
|
-
### `HermiteCurve3D`
|
|
5397
|
-
|
|
5398
|
-
**Properties:**
|
|
5399
|
-
|
|
5400
|
-
| Property | Type | Description |
|
|
5401
|
-
|----------|------|-------------|
|
|
5402
|
-
| `p0` | `Vec3` | Start position |
|
|
5403
|
-
| `p1` | `Vec3` | End position |
|
|
5404
|
-
| `t0` | `Vec3` | Scaled tangent at start (direction * weight * chordLength) |
|
|
5405
|
-
| `t1` | `Vec3` | Scaled tangent at end (direction * weight * chordLength) |
|
|
5406
|
-
| `chordLength` | `number` | Chord length (straight-line distance between endpoints) |
|
|
5407
|
-
|
|
5408
|
-
**Methods:**
|
|
5409
|
-
|
|
5410
|
-
#### `pointAt()` — Evaluate position at parameter t ∈ [0, 1]
|
|
5411
|
-
|
|
5412
|
-
```ts
|
|
5413
|
-
pointAt(t: number): Vec3
|
|
5414
|
-
```
|
|
5415
|
-
|
|
5416
|
-
#### `tangentAt()` — Evaluate tangent (first derivative) at parameter t ∈ [0, 1]
|
|
5417
|
-
|
|
5418
|
-
```ts
|
|
5419
|
-
tangentAt(t: number): Vec3
|
|
5420
|
-
```
|
|
5421
|
-
|
|
5422
|
-
#### `curvatureAt()` — Evaluate curvature vector (second derivative) at parameter t ∈ [0, 1]
|
|
5423
|
-
|
|
5424
|
-
```ts
|
|
5425
|
-
curvatureAt(t: number): Vec3
|
|
5426
|
-
```
|
|
5427
|
-
|
|
5428
|
-
#### `sample()` — Sample the curve as a polyline of evenly-spaced parameter values.
|
|
5429
|
-
|
|
5430
|
-
```ts
|
|
5431
|
-
sample(count?: number): Vec3[]
|
|
5432
|
-
```
|
|
5433
|
-
|
|
5434
|
-
#### `length()` — Approximate arc length by sampling.
|
|
5435
|
-
|
|
5436
|
-
```ts
|
|
5437
|
-
length(samples?: number): number
|
|
5438
|
-
```
|
|
5439
|
-
|
|
5440
|
-
#### `sampleAdaptive()` — Sample with adaptive density — more points where curvature is higher. Returns at least `minCount` points, up to `maxCount`.
|
|
5441
|
-
|
|
5442
|
-
```ts
|
|
5443
|
-
sampleAdaptive(minCount?: number, maxCount?: number): Vec3[]
|
|
5444
|
-
```
|
|
5445
|
-
|
|
5446
|
-
#### `toPolyline()` — Convert to a format compatible with sweep() path input.
|
|
5447
|
-
|
|
5448
|
-
```ts
|
|
5449
|
-
toPolyline(samples?: number): Vec3[]
|
|
5450
|
-
```
|
|
5451
|
-
|
|
5452
|
-
### `QuinticHermiteCurve3D`
|
|
5453
|
-
|
|
5454
|
-
**Properties:**
|
|
5455
|
-
|
|
5456
|
-
| Property | Type | Description |
|
|
5457
|
-
|----------|------|-------------|
|
|
5458
|
-
| `p0` | `Vec3` | Start position |
|
|
5459
|
-
| `p1` | `Vec3` | End position |
|
|
5460
|
-
| `t0` | `Vec3` | Scaled tangent at start (direction * weight * chordLength) |
|
|
5461
|
-
| `t1` | `Vec3` | Scaled tangent at end (direction * weight * chordLength) |
|
|
5462
|
-
| `c0` | `Vec3` | Scaled second derivative at start (curvature * weight² * chordLength²) |
|
|
5463
|
-
| `c1` | `Vec3` | Scaled second derivative at end (curvature * weight² * chordLength²) |
|
|
5464
|
-
| `chordLength` | `number` | Chord length (straight-line distance between endpoints) |
|
|
5465
|
-
|
|
5466
|
-
**Methods:**
|
|
5467
|
-
|
|
5468
|
-
#### `pointAt()` — Evaluate position at parameter t ∈ [0, 1]
|
|
5469
|
-
|
|
5470
|
-
```ts
|
|
5471
|
-
pointAt(t: number): Vec3
|
|
5472
|
-
```
|
|
5473
|
-
|
|
5474
|
-
#### `tangentAt()` — Evaluate tangent (first derivative, normalized) at parameter t ∈ [0, 1]
|
|
5475
|
-
|
|
5476
|
-
```ts
|
|
5477
|
-
tangentAt(t: number): Vec3
|
|
5478
|
-
```
|
|
5479
|
-
|
|
5480
|
-
#### `curvatureAt()` — Evaluate curvature vector (second derivative) at parameter t ∈ [0, 1]
|
|
5481
|
-
|
|
5482
|
-
```ts
|
|
5483
|
-
curvatureAt(t: number): Vec3
|
|
5484
|
-
```
|
|
5485
|
-
|
|
5486
|
-
#### `sample()` — Sample the curve as a polyline of evenly-spaced parameter values.
|
|
5487
|
-
|
|
5488
|
-
```ts
|
|
5489
|
-
sample(count?: number): Vec3[]
|
|
5490
|
-
```
|
|
5491
|
-
|
|
5492
|
-
#### `length()` — Approximate arc length by sampling.
|
|
5493
|
-
|
|
5494
|
-
```ts
|
|
5495
|
-
length(samples?: number): number
|
|
5496
|
-
```
|
|
5497
|
-
|
|
5498
|
-
#### `sampleAdaptive()` — Sample with adaptive density — more points where curvature is higher. Returns at least `minCount` points, up to `maxCount`.
|
|
5499
|
-
|
|
5500
|
-
```ts
|
|
5501
|
-
sampleAdaptive(minCount?: number, maxCount?: number): Vec3[]
|
|
5502
|
-
```
|
|
5503
|
-
|
|
5504
|
-
#### `toPolyline()` — Convert to a format compatible with sweep() path input.
|
|
5505
|
-
|
|
5506
|
-
```ts
|
|
5507
|
-
toPolyline(samples?: number): Vec3[]
|
|
5508
|
-
```
|
|
5509
|
-
|
|
5510
5837
|
### `ProductSkin`
|
|
5511
5838
|
|
|
5512
5839
|
**Properties:**
|
|
@@ -5939,7 +6266,6 @@ attachTo(ref: ProductRefInput, options?: ProductPanelAttachOptions): Shape
|
|
|
5939
6266
|
|
|
5940
6267
|
`ProductSurfaceRef`
|
|
5941
6268
|
|
|
5942
|
-
|
|
5943
6269
|
`ProductPanelAttachOptions`: `{ at?: Partial<ProductSkinRefQuery>, thickness?: number, material?: ProductMaterial, color?: string }`
|
|
5944
6270
|
|
|
5945
6271
|
### `ProductRibbonBuilder`
|
|
@@ -6263,6 +6589,8 @@ bottom(options?: { angle?: number; offset?: number; }): SurfaceAnchor<CylinderSu
|
|
|
6263
6589
|
pointAt(coordinate: CylinderSurfaceCoordinate): Vec3
|
|
6264
6590
|
```
|
|
6265
6591
|
|
|
6592
|
+
`CylinderSurfaceCoordinate`: `{ kind?: "cylinder", angle: number, z: number, offset?: number }`
|
|
6593
|
+
|
|
6266
6594
|
#### `mirrorPoint()`
|
|
6267
6595
|
|
|
6268
6596
|
```ts
|
|
@@ -6382,6 +6710,8 @@ bottom(options?: { x?: number; offset?: number; }): SurfaceAnchor<PlaneSurfaceCo
|
|
|
6382
6710
|
pointAt(coordinate: PlaneSurfaceCoordinate): Vec3
|
|
6383
6711
|
```
|
|
6384
6712
|
|
|
6713
|
+
`PlaneSurfaceCoordinate`: `{ kind?: "plane", x: number, y: number, offset?: number }`
|
|
6714
|
+
|
|
6385
6715
|
#### `mirrorPoint()`
|
|
6386
6716
|
|
|
6387
6717
|
```ts
|
|
@@ -6666,6 +6996,8 @@ boundaries(samples?: number): SurfaceBandBoundarySample[]
|
|
|
6666
6996
|
withHole(name: string, input: SurfaceBandHoleInput): SurfaceBand<C>
|
|
6667
6997
|
```
|
|
6668
6998
|
|
|
6999
|
+
`SurfaceBandHoleInput`: `{ length: number, width: number, along?: number, across?: number }`
|
|
7000
|
+
|
|
6669
7001
|
#### `holes()` — Resolve recorded hole regions into member-local across/along loops.
|
|
6670
7002
|
|
|
6671
7003
|
```ts
|
|
@@ -6688,6 +7020,8 @@ holes(): SurfaceBandHoleRegion[]
|
|
|
6688
7020
|
carrier(carrier: CarrierSurface): this
|
|
6689
7021
|
```
|
|
6690
7022
|
|
|
7023
|
+
`CarrierSurface`: `{ name: string, kind: SurfaceCarrierKind }`
|
|
7024
|
+
|
|
6691
7025
|
#### `member()`
|
|
6692
7026
|
|
|
6693
7027
|
```ts
|
|
@@ -6732,6 +7066,8 @@ band(): this
|
|
|
6732
7066
|
at(anchor: SurfaceAnchor<C>): this
|
|
6733
7067
|
```
|
|
6734
7068
|
|
|
7069
|
+
`SurfaceAnchor`: `{ carrier: CarrierSurface<C>, coordinate: C }`
|
|
7070
|
+
|
|
6735
7071
|
#### `size()`
|
|
6736
7072
|
|
|
6737
7073
|
```ts
|
|
@@ -6750,6 +7086,10 @@ path(path: SurfacePath<C> | SurfacePathBuilder<C>): this
|
|
|
6750
7086
|
section(section: MemberSectionInput): this
|
|
6751
7087
|
```
|
|
6752
7088
|
|
|
7089
|
+
**`MemberSectionInput`**: `width?: number`, `thickness: number`, `edgeRadius?: number`, `direction?: MemberOutwardDirection`, `material?: ProductMaterial`, `stations?: MemberSectionStation[]`
|
|
7090
|
+
|
|
7091
|
+
`MemberSectionStation`: `{ t: number, width?: number, thickness?: number }`
|
|
7092
|
+
|
|
6753
7093
|
#### `cap()`
|
|
6754
7094
|
|
|
6755
7095
|
```ts
|
|
@@ -6762,6 +7102,8 @@ cap(style: SurfaceBandCap): this
|
|
|
6762
7102
|
slot(name: string, feature: MemberFeature | RoundedSlotBuilder): this
|
|
6763
7103
|
```
|
|
6764
7104
|
|
|
7105
|
+
**`MemberFeature`**: `type: MemberFeatureType`, `name?: string`, `length?: number`, `width?: number`, `diameter?: number`, `counterboreDiameter?: number`, `clearanceDiameter?: number`, `height?: number`, `depth?: number`, `count?: number`, `along?: number`, `across?: number`, `verticalTravel?: number`
|
|
7106
|
+
|
|
6765
7107
|
#### `cutout()`
|
|
6766
7108
|
|
|
6767
7109
|
```ts
|
|
@@ -6886,6 +7228,25 @@ toFeature(name?: string): MemberFeature
|
|
|
6886
7228
|
|
|
6887
7229
|
## Constants
|
|
6888
7230
|
|
|
7231
|
+
### `Curve`
|
|
7232
|
+
|
|
7233
|
+
Canonical exact/smooth 3D curve constructors.
|
|
7234
|
+
|
|
7235
|
+
`Curve.*` is the public home for reference curves and route centerlines that feed `sweep`, `variableSweep`, route visualization, and future path consumers. Standalone 3D curve constructors have been collapsed into this namespace.
|
|
7236
|
+
|
|
7237
|
+
- `Blend(start: CurveBlendEndpoint, end: CurveBlendEndpoint): NurbsCurve3D` — Create an exact G1 blend curve between two directed endpoints. The returned curve is a cubic non-rational `NurbsCurve3D`: ForgeCAD converts the endpoint positions and tangents into Bezier control points, so the curve can feed `sweep` and exact surface boundaries through the existing `nurbs` IR rather than a sampled polyline.
|
|
7238
|
+
- `BlendG2(start: CurveBlendG2Endpoint, end: CurveBlendG2Endpoint): NurbsCurve3D` — Create an exact G2 blend curve between two directed endpoints. This is the curvature-aware companion to `Curve.Blend()`. It returns a degree-5 non-rational `NurbsCurve3D` that matches endpoint position, tangent direction, and optional curvature/second-derivative vectors.
|
|
7239
|
+
- `Arc(options: CurveArcOptions): NurbsCurve3D` — Create an exact circular 3D arc from start, end, and start tangent. The returned curve is a rational quadratic `NurbsCurve3D`, split into stable spans when needed, so it can feed `sweep` without sampling the authoring intent away.
|
|
7240
|
+
- `Line(start: Vec3, end: Vec3): NurbsCurve3D` — Create an exact straight 3D NURBS line segment.
|
|
7241
|
+
- `Polyline(points: Vec3[]): Vec3[]` — Create a polyline path as cloned 3D points. Polylines are exact as route/path input to `sweep`. Use `Curve.Route` when the centerline needs bend and endpoint-frame metadata.
|
|
7242
|
+
- `Spline(points: Vec3[], options?: Spline3DOptions): Curve3D` — Create a smooth Catmull-Rom spline path. This is a smooth sampled curve object. Use `Curve.Nurbs` when the path must preserve exact control-point and knot data.
|
|
7243
|
+
- `Nurbs(points: Vec3[], options?: NurbsCurve3DOptions): NurbsCurve3D` — Create an exact NURBS 3D curve from control points, weights, knots, and degree.
|
|
7244
|
+
- `Fit(points: Vec3[], options?: CurveFitOptions): NurbsCurve3D` — Fit a non-rational NURBS curve that interpolates every input point. This is global B-spline interpolation, not approximate curve reduction: ForgeCAD computes chord-length parameters, averaged clamped knots, solves the control points, then verifies the interpolation residual against `tolerance`.
|
|
7245
|
+
- `Trim<T extends CurveTrimInput>(curve: T, start: number, end: number): CurveTrimOutput<T>` — Extract an exact curve segment from normalized parameter `start` to `end`. `NurbsCurve3D` inputs are trimmed with exact knot insertion/subdomain extraction. Polyline point arrays are trimmed by arclength over their exact line segments. Sampled `Curve3D` splines are rejected until ForgeCAD has a tolerance-controlled rebuild path.
|
|
7246
|
+
- `Reverse<T extends CurveTrimInput>(curve: T): CurveTrimOutput<T>` — Reverse an exact curve without changing its geometry. `NurbsCurve3D` inputs reverse control points, weights, and knots. Polyline point arrays are cloned and reversed. Sampled `Curve3D` splines are rejected until ForgeCAD has a tolerance-controlled rebuild path.
|
|
7247
|
+
- `Route: typeof Route3D` — Build analytic 3D line/arc routes for sweeps. `Curve.Route.fromPolyline()` is the canonical route API. It returns a `Route3D` value object, preserving exact route segments, named port frames, and the lowerable `route3d` sweep compile plan.
|
|
7248
|
+
- `Helix: { path(options: HelixOptions): CurveHelixPath; coil: CurveHelixCoil; }` — Build helical paths and swept coils. `Curve.Helix` is the canonical namespace for helical paths and coils. It uses the same sweep-based lowering as other curve paths.
|
|
7249
|
+
|
|
6889
7250
|
### `Surface`
|
|
6890
7251
|
|
|
6891
7252
|
- `Plane(options: SurfacePlaneOptions): Shape` — Create a finite analytic plane sheet that can be trimmed, sewn, thickened, or used as a low-level face.
|
|
@@ -6968,32 +7329,18 @@ toFeature(name?: string): MemberFeature
|
|
|
6968
7329
|
|
|
6969
7330
|
- `repeated(input: { count: number; height: number; }): MemberFeature` — Create repeated ribs that belong to a surface member before lowering.
|
|
6970
7331
|
|
|
6971
|
-
### `Helix`
|
|
6972
|
-
|
|
6973
|
-
Helical curve helpers.
|
|
6974
|
-
|
|
6975
|
-
```ts
|
|
6976
|
-
const guide = Helix.path({ radius: 20, pitch: 6, turns: 4 });
|
|
6977
|
-
const wire = Helix.coil({ radius: 20, pitch: 6, turns: 4, wireRadius: 1 });
|
|
6978
|
-
const rectangular = Helix.coil(rect(2, 1), { radius: 20, height: 30, turns: 5 });
|
|
6979
|
-
```
|
|
6980
|
-
|
|
6981
|
-
- `path(options: HelixOptions): HelixCurve` — Create a metadata-bearing helical centerline around the Z axis.
|
|
6982
|
-
- `coil(options: HelixCoilOptions): Shape` — Create a solid helical coil by sweeping a circular profile through helix-local frames.
|
|
6983
|
-
- `coil(profile: Sketch, options: HelixCoilOptions): Shape` — Create a solid helical coil by sweeping a profile through helix-local frames.
|
|
6984
|
-
|
|
6985
7332
|
---
|
|
6986
7333
|
|
|
6987
7334
|
<!-- generated/assembly.md -->
|
|
6988
7335
|
|
|
6989
7336
|
# Assembly API
|
|
6990
7337
|
|
|
6991
|
-
|
|
7338
|
+
Assembly-owned links, constraints, connectors, solved poses, and robot export.
|
|
6992
7339
|
|
|
6993
7340
|
## Contents
|
|
6994
7341
|
|
|
6995
|
-
- [Assembly & Joints](#assembly-joints) — `bomToCsv`, `assembly
|
|
6996
|
-
- [Assembly](#assembly) — Structure, Connectors, References,
|
|
7342
|
+
- [Assembly & Joints](#assembly-joints) — `bomToCsv`, `assembly`
|
|
7343
|
+
- [Assembly](#assembly) — Kinematics, Structure, Connectors, References, Solving
|
|
6997
7344
|
- [ImportedAssembly](#importedassembly)
|
|
6998
7345
|
- [SolvedAssembly](#solvedassembly)
|
|
6999
7346
|
- [MateBuilder](#matebuilder)
|
|
@@ -7019,110 +7366,219 @@ bomToCsv(rows: BomRow[]): string
|
|
|
7019
7366
|
| `tags?` | `string \| readonly string[]` | Viewport organization tags applied to scene objects produced from this part. |
|
|
7020
7367
|
| `material?`, `process?`, `tolerance?`, `qty?`, `notes?`, `densityKgM3?`, `massKg?` | | — |
|
|
7021
7368
|
|
|
7022
|
-
#### `assembly()` — Create an assembly container with named parts and
|
|
7369
|
+
#### `assembly()` — Create an assembly container with named parts, connectors, and kinematic links.
|
|
7370
|
+
|
|
7371
|
+
**Use this from iteration 1 for any model with moving parts.** Do not build one static pose and retrofit motion later.
|
|
7372
|
+
|
|
7373
|
+
Links are named kinematic markers in the assembly. `edgeBetweenLinks()` records structural distances or visual relationships. `addAngleBetweenLinks()` records measured, limited, or controlled angles. These APIs solve point positions, not rigid-body frames.
|
|
7023
7374
|
|
|
7024
|
-
|
|
7375
|
+
`addPart(..., { mate })` attaches a part connector origin to a solved link point by translation only. It is right for markers and point-following geometry. Use `connect()` / `match()` for physical articulated parts that need full connector frame alignment and deterministic rest orientation.
|
|
7025
7376
|
|
|
7026
|
-
|
|
7377
|
+
Return an `Assembly` directly to expose its connector joints and driven link controls in the editor. Moving those controls re-runs the assembly solve with the new state, so closed-loop link/edge mechanisms move through the real kinematic solver instead of a viewport-only forward-kinematics approximation.
|
|
7027
7378
|
|
|
7028
|
-
|
|
7379
|
+
If no link in a connected kinematic component is fixed, ForgeCAD chooses a deterministic gauge link for solving and reports a floating-component warning.
|
|
7029
7380
|
|
|
7030
|
-
The
|
|
7381
|
+
The legacy joint-chain APIs still exist for compatibility and exporter plumbing. New work should choose between point-link kinematics and connector-frame joints based on whether the part needs orientation.
|
|
7031
7382
|
|
|
7032
7383
|
For multi-file assemblies, a file that returns an `Assembly` is importable via [`require()`](/docs/core#require) and yields an `ImportedAssembly`. Use `mergeInto()` to flatten a sub-assembly into a parent assembly.
|
|
7033
7384
|
|
|
7385
|
+
**Point-link example**
|
|
7386
|
+
|
|
7387
|
+
This snippet mates a marker to the solved `tip` point. It does not orient a bar along `ground -> tip`.
|
|
7388
|
+
|
|
7034
7389
|
```ts
|
|
7035
|
-
const
|
|
7036
|
-
|
|
7037
|
-
|
|
7390
|
+
const marker = box(8, 8, 4).withConnectors({
|
|
7391
|
+
center: connector({ origin: [0, 0, 0], axis: [0, 0, 1] }),
|
|
7392
|
+
});
|
|
7393
|
+
|
|
7394
|
+
const mech = assembly("Linkage")
|
|
7395
|
+
.link("ground", { at: [0, 0, 0], fixed: true })
|
|
7396
|
+
.link("worldX", { at: [10, 0, 0], fixed: true })
|
|
7397
|
+
.link("tip", { at: [40, 0, 0] })
|
|
7398
|
+
.edgeBetweenLinks("ground", "tip", { name: "bar" })
|
|
7399
|
+
.addAngleBetweenLinks("worldX", "ground", "tip", {
|
|
7400
|
+
name: "theta",
|
|
7401
|
+
control: { min: 0, max: 120, default: 30 },
|
|
7038
7402
|
})
|
|
7039
|
-
.addPart("
|
|
7040
|
-
.addRevolute("shoulder", "base", "link", {
|
|
7041
|
-
axis: [0, 1, 0],
|
|
7042
|
-
min: -30, max: 120, default: 25,
|
|
7043
|
-
frame: Transform.identity().translate(0, 0, 20),
|
|
7044
|
-
});
|
|
7403
|
+
.addPart("Tip marker", marker, { mate: { connector: "center", toLink: "tip" } });
|
|
7045
7404
|
|
|
7046
|
-
return mech;
|
|
7405
|
+
return mech;
|
|
7047
7406
|
```
|
|
7048
7407
|
|
|
7049
7408
|
```ts
|
|
7050
7409
|
assembly(name?: string): Assembly
|
|
7051
7410
|
```
|
|
7052
7411
|
|
|
7053
|
-
|
|
7412
|
+
---
|
|
7413
|
+
|
|
7414
|
+
## Classes
|
|
7415
|
+
|
|
7416
|
+
### `Assembly`
|
|
7417
|
+
|
|
7418
|
+
Container for a kinematic mechanism made up of links, relationships, and parts.
|
|
7419
|
+
|
|
7420
|
+
Assembly has two related but different motion tools:
|
|
7421
|
+
|
|
7422
|
+
- **Link graph kinematics** (`link()`, `edgeBetweenLinks()`, `addAngleBetweenLinks()`) solve named point positions. A link is a point, not a rigid-body frame or bone. Connector-to-link mates then place geometry on the solved graph: one mate translates a connector origin onto a link, two mates orient a part so it spans between two solved links, and a third mate pins roll about that span.
|
|
7423
|
+
- **Connector-frame joints** (`connect()` / `match()`) align full connector frames and derive joint frame + axis from `origin`, `axis`, and `up`. Use this for serial articulated geometry such as hips, knees, hinges, drums, sliders, and wheels where the physical part orientation matters.
|
|
7424
|
+
|
|
7425
|
+
Use link graphs when the hard part is solving positions, especially closed loops. Use connector-frame joints when the hard part is a serial tree of explicit rigid-body joint frames such as hinges, sliders, drums, and wheels.
|
|
7426
|
+
|
|
7427
|
+
**Point-link quick start**
|
|
7054
7428
|
|
|
7055
|
-
This
|
|
7429
|
+
This attaches a marker to a solved point. It is intentionally not a bone or oriented part example.
|
|
7056
7430
|
|
|
7057
7431
|
```ts
|
|
7058
|
-
const
|
|
7059
|
-
|
|
7060
|
-
min: -30, max: 120, default: 25,
|
|
7432
|
+
const marker = box(8, 8, 4).withConnectors({
|
|
7433
|
+
center: connector({ origin: [0, 0, 0], axis: [0, 0, 1] }),
|
|
7061
7434
|
});
|
|
7062
|
-
|
|
7435
|
+
|
|
7436
|
+
const mech = assembly("Linkage")
|
|
7437
|
+
.link("ground", { at: [0, 0, 0], fixed: true })
|
|
7438
|
+
.link("worldX", { at: [10, 0, 0], fixed: true })
|
|
7439
|
+
.link("tip", { at: [40, 0, 0] })
|
|
7440
|
+
.edgeBetweenLinks("ground", "tip", { name: "bar" })
|
|
7441
|
+
.addAngleBetweenLinks("worldX", "ground", "tip", {
|
|
7442
|
+
name: "theta",
|
|
7443
|
+
control: { min: 0, max: 120, default: 30 },
|
|
7444
|
+
})
|
|
7445
|
+
.addPart("Tip marker", marker, {
|
|
7446
|
+
mate: { connector: "center", toLink: "tip" },
|
|
7447
|
+
});
|
|
7448
|
+
|
|
7449
|
+
return mech;
|
|
7063
7450
|
```
|
|
7064
7451
|
|
|
7452
|
+
Returning an unsolved `Assembly` keeps the graph available to the runtime. Return a `SolvedAssembly` directly for a specific control state:
|
|
7453
|
+
|
|
7065
7454
|
```ts
|
|
7066
|
-
|
|
7455
|
+
return mech.solve({ theta: 60 });
|
|
7067
7456
|
```
|
|
7068
7457
|
|
|
7069
|
-
|
|
7458
|
+
**Frame-aware serial joint**
|
|
7070
7459
|
|
|
7071
|
-
|
|
7460
|
+
```ts
|
|
7461
|
+
const hip = cylinder(12, 10).withConnectors({
|
|
7462
|
+
socket: connector("hinge", {
|
|
7463
|
+
origin: [0, 0, 6],
|
|
7464
|
+
axis: [0, 0, 1],
|
|
7465
|
+
up: [1, 0, 0],
|
|
7466
|
+
kind: "revolute",
|
|
7467
|
+
}),
|
|
7468
|
+
});
|
|
7469
|
+
|
|
7470
|
+
const upperLeg = box(60, 10, 8).translate(30, 0, -4).withConnectors({
|
|
7471
|
+
hip: connector("hinge", {
|
|
7472
|
+
origin: [0, 0, 0],
|
|
7473
|
+
axis: [0, 0, -1],
|
|
7474
|
+
up: [1, 0, 0],
|
|
7475
|
+
kind: "revolute",
|
|
7476
|
+
}),
|
|
7477
|
+
});
|
|
7478
|
+
|
|
7479
|
+
return assembly("Leg")
|
|
7480
|
+
.addPart("Hip", hip)
|
|
7481
|
+
.addPart("Upper Leg", upperLeg)
|
|
7482
|
+
.connect("Hip.socket", "Upper Leg.hip", { as: "hip", min: -35, max: 55 })
|
|
7483
|
+
.solve({ hip: 20 });
|
|
7484
|
+
```
|
|
7485
|
+
|
|
7486
|
+
**Return types**
|
|
7487
|
+
|
|
7488
|
+
| Return value | Standalone | `require()` result type |
|
|
7489
|
+
|---|---|---|
|
|
7490
|
+
| `Assembly` (unsolved) | yes | `ImportedAssembly` |
|
|
7491
|
+
| `SolvedAssembly` | yes | `SolvedAssembly` |
|
|
7492
|
+
|
|
7493
|
+
**Properties:**
|
|
7494
|
+
|
|
7495
|
+
| Property | Type | Description |
|
|
7496
|
+
|----------|------|-------------|
|
|
7497
|
+
| `name` | `string` | — |
|
|
7498
|
+
|
|
7499
|
+
**Kinematics**
|
|
7500
|
+
|
|
7501
|
+
#### `link()` — Add a named kinematic link to the assembly graph.
|
|
7502
|
+
|
|
7503
|
+
Links are assembly-native solved points. They can exist before any geometry is attached, can be displayed by the viewport, and are solved by link/edge/angle constraints.
|
|
7504
|
+
|
|
7505
|
+
A link is not a rigid-body frame. It has a world position but no orientation basis. Use `connect()` when a physical part must inherit a connector frame and rotate about a real hinge/slider axis.
|
|
7506
|
+
|
|
7507
|
+
```ts
|
|
7508
|
+
link(name: string, options?: AssemblyLinkOptions): Assembly
|
|
7509
|
+
```
|
|
7510
|
+
|
|
7511
|
+
**`AssemblyLinkOptions`**
|
|
7512
|
+
- `at?: [ number, number, number ]` — Initial world-space position of this link before kinematic constraints solve it.
|
|
7513
|
+
- `fixed?: boolean` — Keep the link locked at its authored `at` position during solves.
|
|
7514
|
+
- `metadata?: Record<string, unknown>` — User metadata carried through the kinematic graph for inspection and tooling.
|
|
7515
|
+
|
|
7516
|
+
#### `edgeBetweenLinks()` — Add a relationship edge between two kinematic links.
|
|
7517
|
+
|
|
7518
|
+
By default the edge captures the authored distance between links as a structural length. Pass `{ length: 'free' }` or `{ visualOnly: true }` for a non-structural overlay edge.
|
|
7519
|
+
|
|
7520
|
+
```ts
|
|
7521
|
+
edgeBetweenLinks(a: string, b: string, options?: AssemblyEdgeBetweenLinksOptions): Assembly
|
|
7522
|
+
```
|
|
7523
|
+
|
|
7524
|
+
**`AssemblyEdgeBetweenLinksOptions`**: `name?: string`, `length?: number | "lockCurrent" | "free"`, `min?: number`, `max?: number`, `visualOnly?: boolean`, `control?: AssemblyKinematicControlOptions`, `metadata?: Record<string, unknown>`
|
|
7525
|
+
|
|
7526
|
+
`AssemblyKinematicControlOptions`: `{ min?: number, max?: number, default?: number, unit?: string }`
|
|
7527
|
+
|
|
7528
|
+
#### `addAngleBetweenLinks()` — Add an angle relationship among three kinematic links.
|
|
7529
|
+
|
|
7530
|
+
The middle link is the vertex. When `control` is set, `solve(state)` reads the control value from `state[name]` and solves dependent links from that driven angle.
|
|
7531
|
+
|
|
7532
|
+
```ts
|
|
7533
|
+
addAngleBetweenLinks(a: string, b: string, c: string, options?: AssemblyAngleBetweenLinksOptions): Assembly
|
|
7534
|
+
```
|
|
7072
7535
|
|
|
7073
|
-
|
|
7536
|
+
**`AssemblyAngleBetweenLinksOptions`**: `name?: string`, `value?: number`, `min?: number`, `max?: number`, `control?: boolean | AssemblyKinematicControlOptions`, `limit?: AssemblyKinematicLimitOptions`, `metadata?: Record<string, unknown>`
|
|
7074
7537
|
|
|
7075
|
-
|
|
7538
|
+
`AssemblyKinematicLimitOptions`: `{ min?: number, max?: number }`
|
|
7076
7539
|
|
|
7077
|
-
|
|
7540
|
+
#### `describeKinematics()` — Return the assembly-native kinematic graph definition.
|
|
7078
7541
|
|
|
7079
|
-
|
|
7542
|
+
```ts
|
|
7543
|
+
describeKinematics(): AssemblyKinematicGraphDef
|
|
7544
|
+
```
|
|
7080
7545
|
|
|
7081
|
-
|
|
7546
|
+
**Structure**
|
|
7082
7547
|
|
|
7083
|
-
|
|
7084
|
-
childWorld = parentWorld × frame × motion(value) × childBase
|
|
7085
|
-
```
|
|
7548
|
+
#### `addPart()` — Add a named part to the assembly.
|
|
7086
7549
|
|
|
7087
|
-
|
|
7550
|
+
Connectors declared on the part (via `withConnectors()`) are captured automatically. Parts are positioned at world origin by default unless a `transform` is provided in `options`. For root parts (no incoming joint), `transform` is their final world position.
|
|
7088
7551
|
|
|
7089
|
-
-
|
|
7090
|
-
- **prismatic** — translates the child along an axis by `value` mm
|
|
7091
|
-
- **fixed** — no motion; rigidly attaches the child at `frame`
|
|
7552
|
+
`options.mate` is for point-link attachments. During `solve()`, ForgeCAD translates the part so the named connector origin lands on the solved link position. The part keeps its existing orientation; connector `axis` and `up` are not used for link mating. Use this for markers, sensors, labels, and other geometry that should ride on a solved point. Use `connect()` for oriented physical parts such as limbs, levers, hinges, and wheels.
|
|
7092
7553
|
|
|
7093
|
-
|
|
7554
|
+
When a part is a [`ShapeGroup`](/docs/core#shapegroup), name the group children explicitly to get readable viewport labels (e.g. `"Base Assembly.Body"` instead of `"Base Assembly.1"`):
|
|
7094
7555
|
|
|
7095
7556
|
```ts
|
|
7096
|
-
const
|
|
7097
|
-
|
|
7098
|
-
|
|
7099
|
-
|
|
7100
|
-
|
|
7101
|
-
min: -30, max: 120, default: 25,
|
|
7102
|
-
frame: Transform.identity().translate(0, 0, 20),
|
|
7103
|
-
});
|
|
7104
|
-
|
|
7105
|
-
return mech; // auto-solved at defaults
|
|
7557
|
+
const housing = group(
|
|
7558
|
+
{ name: "Body", shape: body },
|
|
7559
|
+
{ name: "Lid", shape: lid },
|
|
7560
|
+
);
|
|
7561
|
+
assembly.addPart("Base Assembly", housing);
|
|
7106
7562
|
```
|
|
7107
7563
|
|
|
7108
|
-
Returning an unsolved `Assembly` auto-solves at default joint values. Return a `SolvedAssembly` directly for a specific pose:
|
|
7109
|
-
|
|
7110
7564
|
```ts
|
|
7111
|
-
|
|
7565
|
+
addPart(name: string, part: AssemblyPart, options?: PartOptions): Assembly
|
|
7112
7566
|
```
|
|
7113
7567
|
|
|
7114
|
-
|
|
7568
|
+
`PartOptions`: `{ transform?: TransformInput, metadata?: PartMetadata, mate?: AssemblyPartMateInput | AssemblyPartMateInput[] }`
|
|
7115
7569
|
|
|
7116
|
-
|
|
7117
|
-
|
|
7118
|
-
|
|
7119
|
-
|
|
7570
|
+
**`AssemblyPartMateInput`**
|
|
7571
|
+
- `connector: string` — Name of a connector declared on the part (via `withConnectors()`).
|
|
7572
|
+
- `toLink: string` — Name of the link this connector's origin is pinned to.
|
|
7573
|
+
- `aimLink?: string` — Optional second link to orient toward. When set, the part is rotated so the connector's **axis** aims from `toLink` toward `aimLink`, posing an oriented bone instead of only translating it. For full pose without relying on a connector axis, declare a second mate (two connectors → two links).
|
|
7120
7574
|
|
|
7121
|
-
|
|
7575
|
+
#### `addFrame()` — Add a virtual reference frame (no geometry) to the assembly graph.
|
|
7122
7576
|
|
|
7123
|
-
|
|
7124
|
-
|
|
7125
|
-
|
|
7577
|
+
Useful when you need a named pivot point or coordinate frame that has no visual geometry. Acts like a zero-volume part and can be connected to other parts via joints.
|
|
7578
|
+
|
|
7579
|
+
```ts
|
|
7580
|
+
addFrame(name: string, options?: PartOptions): Assembly
|
|
7581
|
+
```
|
|
7126
7582
|
|
|
7127
7583
|
**Connectors**
|
|
7128
7584
|
|
|
@@ -7142,6 +7598,10 @@ Use the single-argument overload to attach assembly-level connectors — these a
|
|
|
7142
7598
|
withConnectors(partName: string, connectors: Record<string, ConnectorInput>): Assembly
|
|
7143
7599
|
```
|
|
7144
7600
|
|
|
7601
|
+
**`PortInput`**: `origin?: [ number, number, number ]`, `axis?: [ number, number, number ]`, `start?: [ number, number, number ]`, `end?: [ number, number, number ]`, `up?: [ number, number, number ]`, `kind?: JointType`, `min?: number`, `max?: number`
|
|
7602
|
+
|
|
7603
|
+
`ConnectorInput`: `{ connectorType?: string, gender?: ConnectorGender, measurements?: Record<string, number | string> }`
|
|
7604
|
+
|
|
7145
7605
|
#### `getConnectors()` — Get connectors declared on a part in part-local space.
|
|
7146
7606
|
|
|
7147
7607
|
```ts
|
|
@@ -7158,23 +7618,35 @@ getConnector(ref: string): { partName: string; connectorName: string; connector:
|
|
|
7158
7618
|
|
|
7159
7619
|
Connector references use `"PartName.connectorName"` format. The system aligns connector origins (child connector lands exactly on parent connector) and derives the joint frame and axis from the connector geometry — no manual `frame` or `axis` math needed.
|
|
7160
7620
|
|
|
7621
|
+
Use `connect()` for frame-aware articulated geometry. A connector's `origin` is the pivot/contact point, `axis` is the hinge line or slide direction, and `up` is the secondary orientation vector that locks the part's zero-angle twist. If `up` is omitted, ForgeCAD chooses a deterministic perpendicular vector, but authored mechanisms should provide `up` whenever rest orientation matters.
|
|
7622
|
+
|
|
7161
7623
|
**Face-to-face convention:** Connectors always meet face-to-face, like a USB plug meeting a socket. Each connector's axis points "outward" from its part. When two connectors mate, the system brings them together so their axes oppose (anti-parallel). This is the same convention used by `matchTo()`.
|
|
7162
7624
|
|
|
7163
7625
|
For a revolute joint (hinge), both connectors' axes should point outward from their respective parts along the hinge line. For a prismatic joint (slider), both axes should point along the slide direction from their part's perspective.
|
|
7164
7626
|
|
|
7627
|
+
**Mirrored revolute axes:** Revolute values follow the right-hand rule around the joint axis. If a bilateral mechanism mirrors a hinge axis, for example `[1, 0, 0]` on the right side and `[-1, 0, 0]` on the left side, the same physical `+theta` value rotates the two sides in opposite fore/aft senses. The mirrored pose uses the negated revolute value; physical limits mirror as `[min, max] -> [-max, -min]`. Prismatic joints do not have this handedness flip. For bilateral mechanisms, prefer side-neutral link controls or an explicit state mapping when you want equal semantic pose values on both sides.
|
|
7628
|
+
|
|
7165
7629
|
The joint type is inferred from the connector's `kind` field if not specified in `options`.
|
|
7166
7630
|
|
|
7167
7631
|
When connectors are defined with `start`/`end`, you can control which point on each connector meets via `align` / `parentAlign` / `childAlign` (`'start'`, `'middle'`, `'end'`).
|
|
7168
7632
|
|
|
7169
|
-
Use `connect()` when connector origins must physically coincide (flange-to-flange, bolt-into-bore).
|
|
7633
|
+
Use `connect()` when connector origins must physically coincide (flange-to-flange, bolt-into-bore).
|
|
7170
7634
|
|
|
7171
7635
|
```ts
|
|
7172
7636
|
// Hinge: both axes point outward along the hinge line
|
|
7173
7637
|
const frame = box(100, 10, 80).withConnectors({
|
|
7174
|
-
hinge: connector("hinge", {
|
|
7638
|
+
hinge: connector("hinge", {
|
|
7639
|
+
origin: [0, 0, 40],
|
|
7640
|
+
axis: [0, 0, 1],
|
|
7641
|
+
up: [1, 0, 0],
|
|
7642
|
+
}),
|
|
7175
7643
|
});
|
|
7176
7644
|
const door = box(60, 4, 80).withConnectors({
|
|
7177
|
-
hinge: connector("hinge", {
|
|
7645
|
+
hinge: connector("hinge", {
|
|
7646
|
+
origin: [0, 0, 40],
|
|
7647
|
+
axis: [0, 0, -1],
|
|
7648
|
+
up: [1, 0, 0],
|
|
7649
|
+
}),
|
|
7178
7650
|
});
|
|
7179
7651
|
assembly("Door")
|
|
7180
7652
|
.addPart("Frame", frame)
|
|
@@ -7186,6 +7658,16 @@ assembly("Door")
|
|
|
7186
7658
|
connect(parentConnectorRef: string, childConnectorRef: string, options?: ConnectOptions): Assembly
|
|
7187
7659
|
```
|
|
7188
7660
|
|
|
7661
|
+
**`ConnectOptions`**
|
|
7662
|
+
|
|
7663
|
+
| Option | Type | Description |
|
|
7664
|
+
|--------|------|-------------|
|
|
7665
|
+
| `flip?` | `boolean` | This parameter is ignored. If your connectors produce wrong orientation, fix the connector axis directions instead of using flip. |
|
|
7666
|
+
| `parentAlign?` | `PortAlign` | Which point on the parent connector to align: 'start', 'middle' (default), or 'end'. |
|
|
7667
|
+
| `childAlign?` | `PortAlign` | Which point on the child connector to align: 'start', 'middle' (default), or 'end'. |
|
|
7668
|
+
| `align?` | `PortAlign` | Shorthand: set both parentAlign and childAlign at once. |
|
|
7669
|
+
| `as?`, `type?`, `min?`, `max?`, `default?`, `unit?`, `effort?`, `velocity?`, `damping?`, `friction?` | | — |
|
|
7670
|
+
|
|
7189
7671
|
#### `match()` — Auto-create a joint by matching typed connectors between two parts.
|
|
7190
7672
|
|
|
7191
7673
|
Connectors can carry a `connectorType` string and a `gender` (`'male'`, `'female'`, or `'neutral'`). `match()` validates type and gender compatibility (use `{ force: true }` to skip validation) and creates the joint automatically from the connector's `kind` metadata.
|
|
@@ -7208,13 +7690,15 @@ const mech = assembly("Door")
|
|
|
7208
7690
|
.addPart("Frame", frame)
|
|
7209
7691
|
.addPart("Door", door)
|
|
7210
7692
|
.match("Door", "Frame", { hinge_top: "hinge_top", hinge_bottom: "hinge_bottom" });
|
|
7211
|
-
//
|
|
7693
|
+
// Matching connectors computes the placement relationship automatically.
|
|
7212
7694
|
```
|
|
7213
7695
|
|
|
7214
7696
|
```ts
|
|
7215
7697
|
match(childPartName: string, parentPartName: string, pairs: Record<string, string>, options?: MatchToOptions & { as?: string; }): Assembly
|
|
7216
7698
|
```
|
|
7217
7699
|
|
|
7700
|
+
`MatchToOptions`: `{ force?: boolean, angle?: number, distance?: number }`
|
|
7701
|
+
|
|
7218
7702
|
**References**
|
|
7219
7703
|
|
|
7220
7704
|
#### `withReferences()` — Attach named placement reference points to this assembly. These are surfaced automatically on the ImportedAssembly when this file is imported via require(), so consumers can use placeReference() without re-declaring them. Returns a new Assembly — does not mutate.
|
|
@@ -7223,22 +7707,28 @@ match(childPartName: string, parentPartName: string, pairs: Record<string, strin
|
|
|
7223
7707
|
withReferences(refs: Pick<PlacementReferenceInput, "points">): Assembly
|
|
7224
7708
|
```
|
|
7225
7709
|
|
|
7226
|
-
|
|
7710
|
+
**`PlacementReferenceInput`**: `points?: Record<string, [ number, number, number ]>`, `edges?: Record<string, PlacementEdgeRef>`, `surfaces?: Record<string, PlacementSurfaceRef>`, `objects?: Record<string, PlacementObjectInput>`
|
|
7227
7711
|
|
|
7228
|
-
|
|
7712
|
+
`PlacementEdgeRef`: `{ start: Vec3, end: Vec3 }`
|
|
7229
7713
|
|
|
7230
|
-
|
|
7714
|
+
`PlacementSurfaceRef`: `{ center: Vec3, normal: Vec3 }`
|
|
7231
7715
|
|
|
7232
|
-
|
|
7716
|
+
**Solving**
|
|
7717
|
+
|
|
7718
|
+
#### `solve()` — Solve the assembly at the given control state and return positioned parts.
|
|
7233
7719
|
|
|
7234
|
-
|
|
7720
|
+
Solves assembly-native kinematic links first. Controlled `addAngleBetweenLinks()` relationships read values from `state` by name, clamp to their declared limits, and expose the solved graph on `SolvedAssembly.kinematics`. Angles solve in the plane of their three authored link positions, so a limb that swings out of the `z = 0` plane poses correctly; structural edges hold their bone lengths so a fully angle-driven serial chain follows forward kinematics.
|
|
7235
7721
|
|
|
7236
|
-
|
|
7722
|
+
Connector mates declared on `addPart(..., { mate })` attach geometry to solved links while preserving part and connector identity:
|
|
7237
7723
|
|
|
7238
|
-
**
|
|
7724
|
+
- one mate **positions** the connector origin on its link;
|
|
7725
|
+
- a mate with `aimLink` (or a second mate to another link) also **orients** the part, rotating an oriented bone to span its links rather than only translating it;
|
|
7726
|
+
- a third mate **pins the roll** about the bone axis (full frame), e.g. a bore or clevis that must face a specific way.
|
|
7727
|
+
|
|
7728
|
+
Connector-frame joints created by `connect()` / `match()` are also evaluated; their values are read from `state` by joint name and clamped to joint limits.
|
|
7239
7729
|
|
|
7240
7730
|
```ts
|
|
7241
|
-
return mech.solve({
|
|
7731
|
+
return mech.solve({ theta: 45 });
|
|
7242
7732
|
```
|
|
7243
7733
|
|
|
7244
7734
|
```ts
|
|
@@ -7253,158 +7743,6 @@ solve(state?: JointState): SolvedAssembly
|
|
|
7253
7743
|
mate(fn: (m: MateBuilder) => void): Assembly
|
|
7254
7744
|
```
|
|
7255
7745
|
|
|
7256
|
-
#### `addFrame()` — Add a virtual reference frame (no geometry) to the assembly graph.
|
|
7257
|
-
|
|
7258
|
-
Useful when you need a named pivot point or coordinate frame that has no visual geometry. Acts like a zero-volume part and can be connected to other parts via joints.
|
|
7259
|
-
|
|
7260
|
-
```ts
|
|
7261
|
-
addFrame(name: string, options?: PartOptions): Assembly
|
|
7262
|
-
```
|
|
7263
|
-
|
|
7264
|
-
#### `addPart()` — Add a named part to the assembly.
|
|
7265
|
-
|
|
7266
|
-
Connectors declared on the part (via `withConnectors()`) are captured automatically. Parts are positioned at world origin by default unless a `transform` is provided in `options`. For root parts (no incoming joint), `transform` is their final world position.
|
|
7267
|
-
|
|
7268
|
-
When a part is a [`ShapeGroup`](/docs/core#shapegroup), name the group children explicitly to get readable viewport labels (e.g. `"Base Assembly.Body"` instead of `"Base Assembly.1"`):
|
|
7269
|
-
|
|
7270
|
-
```ts
|
|
7271
|
-
const housing = group(
|
|
7272
|
-
{ name: "Body", shape: body },
|
|
7273
|
-
{ name: "Lid", shape: lid },
|
|
7274
|
-
);
|
|
7275
|
-
assembly.addPart("Base Assembly", housing);
|
|
7276
|
-
```
|
|
7277
|
-
|
|
7278
|
-
```ts
|
|
7279
|
-
addPart(name: string, part: AssemblyPart, options?: PartOptions): Assembly
|
|
7280
|
-
```
|
|
7281
|
-
|
|
7282
|
-
#### `addJoint()` — Add a kinematic joint between a parent and child part.
|
|
7283
|
-
|
|
7284
|
-
`frame` is a transform from the **parent part frame** to the **joint frame at zero state**. The child's world position is computed as:
|
|
7285
|
-
|
|
7286
|
-
```
|
|
7287
|
-
childWorld = parentWorld × frame × motion(value) × childBase
|
|
7288
|
-
```
|
|
7289
|
-
|
|
7290
|
-
For revolute joints `value` is in degrees; for prismatic joints `value` is in mm. Coupled joints (see `addJointCoupling`) ignore the `state` value passed to `solve()` and compute their value from source joints.
|
|
7291
|
-
|
|
7292
|
-
```ts
|
|
7293
|
-
addJoint(name: string, type: JointType, parent: string, child: string, options?: JointOptions): Assembly
|
|
7294
|
-
```
|
|
7295
|
-
|
|
7296
|
-
#### `addRevolute()` — Shorthand for `addJoint(name, 'revolute', parent, child, options)`.
|
|
7297
|
-
|
|
7298
|
-
```ts
|
|
7299
|
-
addRevolute(name: string, parent: string, child: string, options?: JointOptions): Assembly
|
|
7300
|
-
```
|
|
7301
|
-
|
|
7302
|
-
#### `addPrismatic()` — Shorthand for `addJoint(name, 'prismatic', parent, child, options)`.
|
|
7303
|
-
|
|
7304
|
-
```ts
|
|
7305
|
-
addPrismatic(name: string, parent: string, child: string, options?: JointOptions): Assembly
|
|
7306
|
-
```
|
|
7307
|
-
|
|
7308
|
-
#### `addFixed()` — Shorthand for `addJoint(name, 'fixed', parent, child, options)`.
|
|
7309
|
-
|
|
7310
|
-
Fixed joints rigidly attach a child part to its parent at `frame` with no motion. Before calling `mergeInto()`, use `addFixed()` to collapse multiple root parts into a single root.
|
|
7311
|
-
|
|
7312
|
-
```ts
|
|
7313
|
-
addFixed(name: string, parent: string, child: string, options?: JointOptions): Assembly
|
|
7314
|
-
```
|
|
7315
|
-
|
|
7316
|
-
#### `addJointCoupling()` — Link a joint's value to a linear combination of other joint values.
|
|
7317
|
-
|
|
7318
|
-
The driven joint's value is computed as:
|
|
7319
|
-
|
|
7320
|
-
```
|
|
7321
|
-
driven = offset + Σ(ratio_i × source_i)
|
|
7322
|
-
```
|
|
7323
|
-
|
|
7324
|
-
Coupled joints ignore any value passed in `solve(state)` — a warning is emitted if you try to override one. Coupling cycles are rejected. You cannot sweep a coupled joint directly; sweep one of its source joints instead.
|
|
7325
|
-
|
|
7326
|
-
```ts
|
|
7327
|
-
assembly
|
|
7328
|
-
.addRevolute("Steering", "Base", "Turret", { axis: [0, 0, 1] })
|
|
7329
|
-
.addRevolute("WheelDrive", "Turret", "Wheel", { axis: [1, 0, 0] })
|
|
7330
|
-
.addRevolute("TopGear", "Base", "TopInput", { axis: [0, 0, 1] })
|
|
7331
|
-
.addJointCoupling("TopGear", {
|
|
7332
|
-
terms: [
|
|
7333
|
-
{ joint: "Steering", ratio: 1 },
|
|
7334
|
-
{ joint: "WheelDrive", ratio: 20 / 14 },
|
|
7335
|
-
],
|
|
7336
|
-
});
|
|
7337
|
-
```
|
|
7338
|
-
|
|
7339
|
-
```ts
|
|
7340
|
-
addJointCoupling(jointName: string, options: JointCouplingOptions): Assembly
|
|
7341
|
-
```
|
|
7342
|
-
|
|
7343
|
-
#### `addGearCoupling()` — Link two revolute joints via a gear ratio.
|
|
7344
|
-
|
|
7345
|
-
Choose exactly one ratio source:
|
|
7346
|
-
|
|
7347
|
-
- `ratio` — explicit numeric ratio (driven/driver, negative for external mesh)
|
|
7348
|
-
- `pair` — a `GearRatioLike` from `lib.gearPair`, `lib.bevelGearPair`, etc. (uses `pair.jointRatio`)
|
|
7349
|
-
- `driverTeeth` + `drivenTeeth` — auto-computes ratio; use `mesh` to control sign (`'external'` = negative/opposite rotation, `'internal'` = positive, `'bevel'`/`'face'` = negative)
|
|
7350
|
-
|
|
7351
|
-
When `pair` carries a `phaseDeg`, it is auto-applied as the coupling `offset` to align teeth correctly. Override with `offset: 0` if gear shapes already have the phase baked in.
|
|
7352
|
-
|
|
7353
|
-
```ts
|
|
7354
|
-
const pair = lib.gearPair({ pinion: { module: 1.25, teeth: 14 }, gear: { module: 1.25, teeth: 42 } });
|
|
7355
|
-
assembly
|
|
7356
|
-
.addRevolute("Pinion", "Base", "PinionPart", { axis: [0, 0, 1] })
|
|
7357
|
-
.addRevolute("Driven", "Base", "GearPart", { axis: [0, 0, 1] })
|
|
7358
|
-
.addGearCoupling("Driven", "Pinion", { pair });
|
|
7359
|
-
```
|
|
7360
|
-
|
|
7361
|
-
```ts
|
|
7362
|
-
addGearCoupling(drivenJointName: string, driverJointName: string, options?: GearCouplingOptions): Assembly
|
|
7363
|
-
```
|
|
7364
|
-
|
|
7365
|
-
#### `sweepJoint()` — Sample a joint through its motion range, collecting collision data at each step.
|
|
7366
|
-
|
|
7367
|
-
Divides `[from, to]` into `steps` intervals (producing `steps + 1` frames). At each sample, the assembly is solved with the sweeping joint at that value and `baseState` for all others. Returns one `JointSweepFrame` per sample with the joint value, collision findings, and any solve warnings.
|
|
7368
|
-
|
|
7369
|
-
You cannot sweep a coupled joint — sweep one of its source joints instead.
|
|
7370
|
-
|
|
7371
|
-
```ts
|
|
7372
|
-
const sweep = mech.sweepJoint("elbow", -10, 135, 12, { shoulder: 35 });
|
|
7373
|
-
const hits = sweep.filter(frame => frame.collisions.length > 0);
|
|
7374
|
-
console.log(`Collisions at ${hits.length} of ${sweep.length} poses`);
|
|
7375
|
-
```
|
|
7376
|
-
|
|
7377
|
-
```ts
|
|
7378
|
-
sweepJoint(jointName: string, from: number, to: number, steps: number, baseState?: JointState, collisionOptions?: CollisionOptions): JointSweepFrame[]
|
|
7379
|
-
```
|
|
7380
|
-
|
|
7381
|
-
#### `toJointsView()` — Derive viewport joint controls from the assembly graph and register them.
|
|
7382
|
-
|
|
7383
|
-
Solves the assembly at rest (all joints = default), then converts each joint into a `JointViewInput` with world-space pivot and axis. Fixed joints become hidden zero-range revolute entries so attached parts follow their parent during animation. Joint couplings are forwarded to the viewport automatically.
|
|
7384
|
-
|
|
7385
|
-
This method is optional. Call it only when you want viewport joint sliders, coupled controls, or playback animations. If you only want geometry, return the `Assembly` or `SolvedAssembly` directly and skip `toJointsView()`.
|
|
7386
|
-
|
|
7387
|
-
**Critical pitfall:** Always call `toJointsView()` before solving for display. Then solve at the **rest pose** (no state overrides) and return that solved assembly result directly. Do not flatten it with `.toGroup()` if you want the viewport joint animation to keep working.
|
|
7388
|
-
|
|
7389
|
-
Do not solve at a non-zero angle when using `toJointsView()` — the viewport will apply the same rotation again, double-rotating the part.
|
|
7390
|
-
|
|
7391
|
-
```ts
|
|
7392
|
-
mech.toJointsView({
|
|
7393
|
-
defaults: { J1: 30 },
|
|
7394
|
-
animations: [{
|
|
7395
|
-
name: "Swing", duration: 2, loop: true,
|
|
7396
|
-
keyframes: [{ values: { J1: -45 } }, { values: { J1: 45 } }, { values: { J1: -45 } }],
|
|
7397
|
-
}],
|
|
7398
|
-
});
|
|
7399
|
-
|
|
7400
|
-
// Solve at REST — viewport handles posing
|
|
7401
|
-
return mech.solve();
|
|
7402
|
-
```
|
|
7403
|
-
|
|
7404
|
-
```ts
|
|
7405
|
-
toJointsView(options?: ToJointsViewOptions): void
|
|
7406
|
-
```
|
|
7407
|
-
|
|
7408
7746
|
#### `describe()` — Return the serializable assembly definition used by solve/inspect pipelines.
|
|
7409
7747
|
|
|
7410
7748
|
```ts
|
|
@@ -7422,7 +7760,7 @@ describe(): AssemblyDefinition
|
|
|
7422
7760
|
|
|
7423
7761
|
A wrapper around an imported `Assembly` that provides kinematic access and convenient transform helpers.
|
|
7424
7762
|
|
|
7425
|
-
When a `.forge.js` file returns an unsolved `Assembly`, [`require()`](/docs/core#require) wraps it in an `ImportedAssembly`. This preserves the kinematic structure — you can call `solve()
|
|
7763
|
+
When a `.forge.js` file returns an unsolved `Assembly`, [`require()`](/docs/core#require) wraps it in an `ImportedAssembly`. This preserves the kinematic structure — you can call `solve()` and `mergeInto()` — while also allowing convenience transforms that auto-solve at default values.
|
|
7426
7764
|
|
|
7427
7765
|
**Kinematic access**
|
|
7428
7766
|
|
|
@@ -7451,7 +7789,7 @@ require("./arm.forge.js").mergeInto(robot, {
|
|
|
7451
7789
|
});
|
|
7452
7790
|
```
|
|
7453
7791
|
|
|
7454
|
-
#### `assembly()` — The underlying Assembly
|
|
7792
|
+
#### `assembly()` — The underlying Assembly, for advanced composition and inspection.
|
|
7455
7793
|
|
|
7456
7794
|
```ts
|
|
7457
7795
|
get assembly(): Assembly
|
|
@@ -7469,6 +7807,14 @@ solve(state?: JointState): SolvedAssembly
|
|
|
7469
7807
|
part(name: string, state?: JointState): AssemblyPart
|
|
7470
7808
|
```
|
|
7471
7809
|
|
|
7810
|
+
#### `getPart()` — Return a specific named part positioned at the default solved pose.
|
|
7811
|
+
|
|
7812
|
+
This mirrors `SolvedAssembly.getPart()` for imported assemblies. Use `solve(state).getPart(name)` when inspecting a non-default joint state.
|
|
7813
|
+
|
|
7814
|
+
```ts
|
|
7815
|
+
getPart(partName: string): AssemblyPart
|
|
7816
|
+
```
|
|
7817
|
+
|
|
7472
7818
|
#### `toGroup()` — Convert all assembly parts to a ShapeGroup with named children. Use this for composition, transforms, or child lookup — not as a required render step for assemblies. Child names match the part names used in the assembly. Any stored placement offset and placement references are forwarded to the group.
|
|
7473
7819
|
|
|
7474
7820
|
```ts
|
|
@@ -7547,17 +7893,35 @@ color(hex: string): ShapeGroup
|
|
|
7547
7893
|
child(name: string): Shape | Sketch | ShapeGroup
|
|
7548
7894
|
```
|
|
7549
7895
|
|
|
7550
|
-
#### `
|
|
7896
|
+
#### `collisionReport()` — Detect overlapping part pairs at the default solved pose.
|
|
7897
|
+
|
|
7898
|
+
This mirrors `SolvedAssembly.collisionReport()` for imported assemblies. Use `solve(state).collisionReport(options)` when inspecting a non-default joint state.
|
|
7899
|
+
|
|
7900
|
+
```ts
|
|
7901
|
+
collisionReport(options?: CollisionOptions): CollisionFinding[]
|
|
7902
|
+
```
|
|
7903
|
+
|
|
7904
|
+
`CollisionOptions`: `{ parts?: string[], ignorePairs?: Array<[ string, string ]>, minOverlapVolume?: number }`
|
|
7551
7905
|
|
|
7552
|
-
|
|
7906
|
+
#### `minClearance()` — Compute the minimum gap between two parts at the default solved pose.
|
|
7907
|
+
|
|
7908
|
+
This mirrors `SolvedAssembly.minClearance()` for imported assemblies. Use `solve(state).minClearance(partA, partB, searchLength)` when inspecting a non-default joint state.
|
|
7909
|
+
|
|
7910
|
+
```ts
|
|
7911
|
+
minClearance(partA: string, partB: string, searchLength?: number): number
|
|
7912
|
+
```
|
|
7913
|
+
|
|
7914
|
+
#### `mergeInto()` — Flatten this sub-assembly's parts and relationships into `parent` and wire a mount relationship.
|
|
7915
|
+
|
|
7916
|
+
All part, link, and legacy joint names from the sub-assembly are prefixed with `"${options.prefix}."` to avoid collisions. After the merge, controls are driven from the parent using the prefixed names:
|
|
7553
7917
|
|
|
7554
7918
|
```ts
|
|
7555
|
-
parent.solve({ "Left Arm.
|
|
7919
|
+
parent.solve({ "Left Arm.theta": 45, "Right Arm.theta": -20 })
|
|
7556
7920
|
```
|
|
7557
7921
|
|
|
7558
|
-
|
|
7922
|
+
Connectors from sub-assembly parts are forwarded with the prefix.
|
|
7559
7923
|
|
|
7560
|
-
The sub-assembly must have exactly one root part
|
|
7924
|
+
The sub-assembly must have exactly one root part before it can be merged.
|
|
7561
7925
|
|
|
7562
7926
|
```ts
|
|
7563
7927
|
const robot = assembly("Robot").addPart("Chassis", chassis);
|
|
@@ -7574,6 +7938,25 @@ require("./arm.forge.js").mergeInto(robot, {
|
|
|
7574
7938
|
mergeInto(parent: Assembly, options: MergeIntoOptions): Assembly
|
|
7575
7939
|
```
|
|
7576
7940
|
|
|
7941
|
+
**`MergeIntoOptions`**
|
|
7942
|
+
|
|
7943
|
+
| Option | Type | Description |
|
|
7944
|
+
|--------|------|-------------|
|
|
7945
|
+
| `prefix?` | `string` | Prefix applied to every part name and joint name from the sub-assembly. E.g. prefix "Left Arm" turns part "Base" into "Left Arm.Base". Strongly recommended to avoid name collisions when merging multiple instances. |
|
|
7946
|
+
| `mountParent` | `string` | Part name in the parent assembly to attach the sub-assembly root to. |
|
|
7947
|
+
| `mountJoint` | `string` | Name for the new mount joint in the parent graph. |
|
|
7948
|
+
| `mountType?` | `JointType` | Joint type for the mount connection (default: 'fixed'). |
|
|
7949
|
+
| `mountOptions?` | `JointOptions` | Frame, axis, limits, and other options for the mount joint. |
|
|
7950
|
+
|
|
7951
|
+
**`JointOptions`**
|
|
7952
|
+
|
|
7953
|
+
| Option | Type | Description |
|
|
7954
|
+
|--------|------|-------------|
|
|
7955
|
+
| `connectorRefs?` | `JointConnectorRefs` | Connector refs that define this joint contract. Usually set by `connect()` / `match()`. |
|
|
7956
|
+
| `frame?`, `origin?`, `axis?`, `min?`, `max?`, `default?`, `unit?`, `effort?`, `velocity?`, `damping?`, `friction?` | | — |
|
|
7957
|
+
|
|
7958
|
+
`JointConnectorRefs`: `{ parent: string, child: string, parentAlign?: PortAlign, childAlign?: PortAlign }`
|
|
7959
|
+
|
|
7577
7960
|
### `SolvedAssembly`
|
|
7578
7961
|
|
|
7579
7962
|
The result of solving an assembly at a specific joint state.
|
|
@@ -7582,7 +7965,7 @@ The result of solving an assembly at a specific joint state.
|
|
|
7582
7965
|
|
|
7583
7966
|
**Validation**
|
|
7584
7967
|
|
|
7585
|
-
Call `collisionReport()` to detect overlapping parts
|
|
7968
|
+
Call `collisionReport()` to detect overlapping parts at this solved pose.
|
|
7586
7969
|
|
|
7587
7970
|
```ts
|
|
7588
7971
|
const solved = mech.solve({ shoulder: 45, elbow: -20 });
|
|
@@ -7628,6 +8011,18 @@ get mateDof(): number | null
|
|
|
7628
8011
|
get mateConverged(): boolean | null
|
|
7629
8012
|
```
|
|
7630
8013
|
|
|
8014
|
+
#### `kinematics()` — Solved assembly-native kinematic graph, or null when no links were declared.
|
|
8015
|
+
|
|
8016
|
+
```ts
|
|
8017
|
+
get kinematics(): SolvedAssemblyKinematics | null
|
|
8018
|
+
```
|
|
8019
|
+
|
|
8020
|
+
#### `getLinkPosition()` — Return the solved world position of a kinematic link.
|
|
8021
|
+
|
|
8022
|
+
```ts
|
|
8023
|
+
getLinkPosition(linkName: string): Vec3
|
|
8024
|
+
```
|
|
8025
|
+
|
|
7631
8026
|
#### `getTransform()` — Return the world-space [`Transform`](/docs/core#transform) for the named part at the solved pose.
|
|
7632
8027
|
|
|
7633
8028
|
```ts
|
|
@@ -8061,6 +8456,10 @@ const part = sheetMetal({ panel: { width: 100, height: 60 }, thickness: 1.5, ben
|
|
|
8061
8456
|
flange(edge: SheetMetalEdge, options: SheetMetalFlangeOptions): SheetMetalPart
|
|
8062
8457
|
```
|
|
8063
8458
|
|
|
8459
|
+
**`SheetMetalFlangeOptions`**
|
|
8460
|
+
- `length: number` — Flange leg length in mm, measured from the outside of the bend to the tip.
|
|
8461
|
+
- `angleDeg?: number` — Bend angle in degrees (default: `90`). Only `90°` is supported in v1. Values other than 90 will be rejected at build time.
|
|
8462
|
+
|
|
8064
8463
|
#### `cutout()` — Subtract a 2D sketch cutout from a planar region of the sheet metal part.
|
|
8065
8464
|
|
|
8066
8465
|
`region` must be `'panel'` or one of `'flange-top'`, `'flange-right'`, `'flange-bottom'`, `'flange-left'` (only available once the corresponding flange has been added). Cutouts inside bend regions are **not** supported in v1.
|
|
@@ -8080,6 +8479,11 @@ const part = sheetMetal({ panel: { width: 180, height: 110 }, thickness: 1.5, be
|
|
|
8080
8479
|
cutout(region: SheetMetalPlanarRegionName, sketch: Sketch, options?: SheetMetalCutoutOptions): SheetMetalPart
|
|
8081
8480
|
```
|
|
8082
8481
|
|
|
8482
|
+
**`SheetMetalCutoutOptions`**
|
|
8483
|
+
- `u?: number` — Horizontal offset within the region, measured from the region centre (mm). Default: `0`.
|
|
8484
|
+
- `v?: number` — Vertical offset within the region, measured from the region centre (mm). Default: `0`.
|
|
8485
|
+
- `selfAnchor?: Anchor` — Anchor point on the sketch that aligns to `(u, v)`. Use `'center'` for most cases. For asymmetric profiles, verify orientation by placing one test cutout before committing to the final position. Default: `'center'`.
|
|
8486
|
+
|
|
8083
8487
|
#### `regionNames()` — Return all semantic region names currently available on this part.
|
|
8084
8488
|
|
|
8085
8489
|
The returned list always includes `'panel'`. For every flange that has been added, the list also includes the corresponding `'flange-<edge>'` and `'bend-<edge>'` entries.
|
|
@@ -8349,7 +8753,7 @@ Call `robotExport()` alongside your assembly definition. The CLI commands `forge
|
|
|
8349
8753
|
|
|
8350
8754
|
- Mesh-based inertia tensors (full 6-component, not bounding-box approximations)
|
|
8351
8755
|
- Separate collision meshes (convex hull by default — ~50–80% smaller)
|
|
8352
|
-
- Joint
|
|
8756
|
+
- Joint limits, effort/velocity/damping/friction metadata from assembly joints
|
|
8353
8757
|
|
|
8354
8758
|
**Collision mesh modes** (set per-link via `links["PartName"].collision`):
|
|
8355
8759
|
|
|
@@ -8365,7 +8769,7 @@ Call `robotExport()` alongside your assembly definition. The CLI commands `forge
|
|
|
8365
8769
|
- Revolute `velocity` is in degrees/second in Forge; exporters convert to rad/s.
|
|
8366
8770
|
- Prismatic distances are in mm in Forge; exported in meters.
|
|
8367
8771
|
- `massKg` is preferred; `densityKgM3` is used when mass is unknown.
|
|
8368
|
-
-
|
|
8772
|
+
- Compatibility coupling metadata, when present, maps only the primary term (largest ratio) to `<mimic>` — SDF/URDF support single-leader mimic only. Dropped terms emit a warning.
|
|
8369
8773
|
|
|
8370
8774
|
```ts
|
|
8371
8775
|
const rover = assembly("Scout")
|
|
@@ -8421,9 +8825,9 @@ robotExport(options: RobotExportOptions): CollectedRobotExport
|
|
|
8421
8825
|
|
|
8422
8826
|
**`CollectedRobotExport`**: `modelName: string`, `assembly: AssemblyDefinition`, `state: JointState`, `static: boolean`, `selfCollide: boolean`, `allowAutoDisable: boolean`, `links: Record<string, RobotLinkExportOptions>`, `joints: Record<string, RobotJointExportOptions>`, `plugins: { diffDrive?: RobotDiffDrivePluginOptions; jointStatePublisher?: RobotJointStatePublisherOptions; }`, `world: RobotWorldOptions | null`
|
|
8423
8827
|
|
|
8424
|
-
|
|
8828
|
+
**`AssemblyDefinition`**: `name: string`, `parts: AssemblyPartDef[]`, `joints: AssemblyJointDef[]`, `jointCouplings: AssemblyJointCouplingDef[]`, `kinematics: AssemblyKinematicGraphDef`
|
|
8425
8829
|
|
|
8426
|
-
`AssemblyPartDef`: `{ name: string, part: AssemblyPart, base: Transform, metadata?: PartMetadata }`
|
|
8830
|
+
`AssemblyPartDef`: `{ name: string, part: AssemblyPart, base: Transform, metadata?: PartMetadata, mates: AssemblyPartMateInput[] }`
|
|
8427
8831
|
|
|
8428
8832
|
**`PartMetadata`**
|
|
8429
8833
|
|
|
@@ -8432,6 +8836,11 @@ robotExport(options: RobotExportOptions): CollectedRobotExport
|
|
|
8432
8836
|
| `tags?` | `string \| readonly string[]` | Viewport organization tags applied to scene objects produced from this part. |
|
|
8433
8837
|
| `material?`, `process?`, `tolerance?`, `qty?`, `notes?`, `densityKgM3?`, `massKg?` | | — |
|
|
8434
8838
|
|
|
8839
|
+
**`AssemblyPartMateInput`**
|
|
8840
|
+
- `connector: string` — Name of a connector declared on the part (via `withConnectors()`).
|
|
8841
|
+
- `toLink: string` — Name of the link this connector's origin is pinned to.
|
|
8842
|
+
- `aimLink?: string` — Optional second link to orient toward. When set, the part is rotated so the connector's **axis** aims from `toLink` toward `aimLink`, posing an oriented bone instead of only translating it. For full pose without relying on a connector axis, declare a second mate (two connectors → two links).
|
|
8843
|
+
|
|
8435
8844
|
**`AssemblyJointDef`**: `name: string`, `type: JointType`, `parent: string`, `child: string`, `frame: Transform`, `axis: Vec3`, `min?: number`, `max?: number`, `defaultValue: number`, `unit?: string`, `effort?: number`, `velocity?: number`, `damping?: number`, `friction?: number`, `connectorRefs?: JointConnectorRefs`
|
|
8436
8845
|
|
|
8437
8846
|
`JointConnectorRefs`: `{ parent: string, child: string, parentAlign?: PortAlign, childAlign?: PortAlign }`
|
|
@@ -8440,6 +8849,16 @@ robotExport(options: RobotExportOptions): CollectedRobotExport
|
|
|
8440
8849
|
|
|
8441
8850
|
`JointCouplingTermRecord`: `{ joint: string, ratio: number }`
|
|
8442
8851
|
|
|
8852
|
+
`AssemblyKinematicGraphDef`: `{ links: AssemblyLinkDef[], edges: AssemblyEdgeBetweenLinksDef[], angles: AssemblyAngleBetweenLinksDef[] }`
|
|
8853
|
+
|
|
8854
|
+
`AssemblyLinkDef`: `{ name: string, at: Vec3, fixed: boolean, metadata?: Record<string, unknown> }`
|
|
8855
|
+
|
|
8856
|
+
**`AssemblyEdgeBetweenLinksDef`**: `name: string`, `a: string`, `b: string`, `length: number | null`, `min?: number`, `max?: number`, `visualOnly: boolean`, `control?: AssemblyKinematicControlOptions`, `metadata?: Record<string, unknown>`
|
|
8857
|
+
|
|
8858
|
+
`AssemblyKinematicControlOptions`: `{ min?: number, max?: number, default?: number, unit?: string }`
|
|
8859
|
+
|
|
8860
|
+
**`AssemblyAngleBetweenLinksDef`**: `name: string`, `a: string`, `b: string`, `c: string`, `target?: number`, `min?: number`, `max?: number`, `control?: AssemblyKinematicControlOptions`, `metadata?: Record<string, unknown>`
|
|
8861
|
+
|
|
8443
8862
|
#### `dim()` — Add a dimension annotation between two points.
|
|
8444
8863
|
|
|
8445
8864
|
Dimension annotations are purely visual callouts rendered in the viewport and report export. They do not affect geometry or constrain the model.
|
|
@@ -8587,18 +9006,24 @@ offsetBand(thickness: number): Sketch
|
|
|
8587
9006
|
addSpurTeethBetween(options: DriveWheelSpurTeethRegionOptions): this
|
|
8588
9007
|
```
|
|
8589
9008
|
|
|
9009
|
+
`DriveWheelSpurTeethRegionOptions`: `{ name?: string, teethOnFullCircle: number, toothCount: number, firstTooth?: number, faceWidth?: number }`
|
|
9010
|
+
|
|
8590
9011
|
#### `addSolidArcBetween()` — Add a constant-radius solid arc region such as a dwell, stop, or pusher.
|
|
8591
9012
|
|
|
8592
9013
|
```ts
|
|
8593
9014
|
addSolidArcBetween(options: DriveWheelSolidArcRegionOptions): this
|
|
8594
9015
|
```
|
|
8595
9016
|
|
|
9017
|
+
**`DriveWheelSolidArcRegionOptions`**: `name?: string`, `fromAngleDeg: number`, `toAngleDeg: number`, `innerRadius?: number`, `outerRadius: number`, `faceWidth?: number`, `segments?: number`
|
|
9018
|
+
|
|
8596
9019
|
#### `addShapeRegion()` — Add a fully custom region shape while preserving region metadata.
|
|
8597
9020
|
|
|
8598
9021
|
```ts
|
|
8599
9022
|
addShapeRegion(name: string, shape: Shape, options?: DriveWheelShapeRegionOptions): this
|
|
8600
9023
|
```
|
|
8601
9024
|
|
|
9025
|
+
`DriveWheelShapeRegionOptions`: `{ fromAngleDeg?: number, toAngleDeg?: number, innerRadius?: number, outerRadius?: number }`
|
|
9026
|
+
|
|
8602
9027
|
#### `build()` — Build the final wheel shape with a bore connector and region metadata.
|
|
8603
9028
|
|
|
8604
9029
|
```ts
|
|
@@ -8665,7 +9090,7 @@ Sizes outside the supported ranges will throw at runtime with a descriptive erro
|
|
|
8665
9090
|
- `routedTubeClipAssembly(options: RoutedTubeClipAssemblyOptions): RoutedTubeClipAssemblyResult` — Routed tube or cable retained by saddle clips with real bores, screw holes, and installed screws. **Details** This pattern is for hoses, wires, pump tubes, sensor leads, and appliance cable runs where generated models often draw a cylinder near a wall without clips or strain relief. The base panel has receiving screw envelopes, each saddle clip has a real through-bore around the tube and vertical screw clearances, and the installed screws share those positions. Coordinate convention: the routed tube runs along +X through the world origin. The base panel starts at `z=0`; clips sit on top of the panel, and the tube passes through their bores. **Example** ```ts const route = lib.routedTubeClipAssembly({ tubeDiameter: 6, clipCount: 3, }); verify.notColliding('tube clears clip bores', route.tube, union(...route.clips)); verify.notColliding('clip screws clear retained stack', union(...route.screws), union(route.panel, ...route.clips)); return route.parts; ```
|
|
8666
9091
|
- `pcbTerminalBlockAssembly(options?: PcbTerminalBlockAssemblyOptions): PcbTerminalBlockAssemblyResult` — PCB terminal-block stack with a backplate, standoffs, mounting screws, pin holes, and a seated terminal block. **Details** This pattern is for thermostat backplates, appliance control panels, sensor boards, and small electronics where generated models often place a terminal block, screw heads, and holes as independent decorations. The PCB mounting holes, fused standoffs, installed screws, terminal pins, and PCB pin clearances all come from one shared datum system so the purchased block is mechanically seated and the board is actually mounted. Coordinate convention: X/Y are the board footprint, Z is up. The backplate starts at `z=0`, standoffs rise from the plate, the PCB rests on the standoffs, and the terminal block sits on top of the PCB near the front edge. **Example** ```ts const terminalStack = lib.pcbTerminalBlockAssembly({ terminalCount: 5, screwSize: 'M3', }); verify.notColliding('terminal pins clear PCB holes', terminalStack.terminalBlock, terminalStack.pcb); verify.notColliding('mounting screws clear PCB and standoff holes', union(...terminalStack.screws), union(terminalStack.pcb, terminalStack.backplate)); return terminalStack.parts; ```
|
|
8667
9092
|
- `thumbScrewClampAssembly(options?: ThumbScrewClampAssemblyOptions): ThumbScrewClampAssemblyResult` — Thumb-screw clamp with a C-frame, threaded boss, captive pressure pad, knob, and clamped workpiece. **Details** This pattern is for bench clamps, monitor-arm desk clamps, small vise screws, capo pressure screws, fixture hold-downs, and service brackets where generated models often place a loose screw, knob, or pressure pad near a bracket. The helper creates a one-piece clamp frame with a fixed anvil pad, a bored threaded support and boss, an installed screw with a captive pressure pad and hand knob, and a representative clamped workpiece seated between the pads. Coordinate convention: the clamp screw runs along +X. The fixed anvil is on the -X side, the threaded support and knob are on the +X side, and Z is up from the base bridge. **Example** ```ts const clamp = lib.thumbScrewClampAssembly({ screwSize: 'M6', workpieceThickness: 20, }); verify.notColliding('thumb screw clears threaded boss', clamp.clampScrew, clamp.frame); verify.clearanceBetween('pressure pad is seated on workpiece', clamp.clampScrew, clamp.workpiece, -0.01, 0.05); return clamp.parts; ```
|
|
8668
|
-
- `pipeRoute(points: [ number, number, number ][], radius: number, options?: { bendRadius?: number; wall?: number; segments?: number; }): Shape` — Route a pipe (solid or hollow) through 3D waypoints with smooth bends.
|
|
9093
|
+
- `pipeRoute(points: [ number, number, number ][], radius: number, options?: { bendRadius?: number; wall?: number; segments?: number; }): Shape` — Route a pipe (solid or hollow) through 3D waypoints with smooth bends. This is a convenience recipe over `Curve.Route.fromPolyline()` and [`sweep()`](/docs/curves#sweep). Use `Curve.Route` directly when you need route metadata, named ports, or custom swept profiles.
|
|
8669
9094
|
- `elbow(pipeRadius: number, bendRadius: number, angle?: number | { ... }, options?: { ... }): Shape` — Pipe elbow — a curved pipe section (torus arc) for connecting two pipe directions. By default creates a bend in the XZ plane: incoming along +Z, outgoing rotated by `angle`. The bend starts at the origin, curving away from it.
|
|
8670
9095
|
- `beltDrive(options: BeltDriveOptions): BeltDriveResult` — Create a flat open-belt body around two pulley pitch circles. The belt is generated as a tangent loop in the XY plane and extruded along +Z by `beltWidth`. The result includes the solid belt, the 2D belt profile, a thin pitch-path sketch for visualization, total belt length, tangent spans, and wrap metadata for each pulley. For more than two pulleys, the API intentionally asks for route intent before geometry is created. Use `route: "outer"` for the future outside-envelope mode, or an ordered route for future serpentine/idler layouts. ```ts const drive = lib.beltDrive({ pulleys: [ { name: "motor", center: [0, 0], pitchRadius: 12 }, { name: "output", center: [80, 0], pitchRadius: 28 }, ], beltWidth: 8, beltThickness: 2, }); return drive.belt; ```
|
|
8671
9096
|
- `tangentLoop2d(circles: TangentCircle2D[], options?: TangentLoop2DOptions): TangentLoop2D` — Build a closed 2D route made from common tangent spans and pulley wrap arcs. Use this when you need reusable belt/chain route geometry before creating a solid body. The first implementation supports two circles. `mode: "open"` uses external tangents; `mode: "crossed"` uses internal tangents. ```ts const route = lib.tangentLoop2d([ { center: [0, 0], radius: 12 }, { center: [80, 0], radius: 28 }, ]); const belt = route.offsetBand(2).extrude(8); ```
|
|
@@ -8814,7 +9239,7 @@ Cut planes, exploded views, joint animations, and scene configuration.
|
|
|
8814
9239
|
|
|
8815
9240
|
## Contents
|
|
8816
9241
|
|
|
8817
|
-
- [Viewport & Runtime](#viewport-runtime) — `Viewport.label`, `scene`, `viewConfig`, `explodeView`, `
|
|
9242
|
+
- [Viewport & Runtime](#viewport-runtime) — `Viewport.label`, `scene`, `viewConfig`, `explodeView`, `compareWith`, `cutPlane`, `mock`, `showLabels`, `highlight`
|
|
8818
9243
|
- [RouteBuilder](#routebuilder)
|
|
8819
9244
|
- [route](#route)
|
|
8820
9245
|
|
|
@@ -9001,7 +9426,7 @@ scene(options: SceneOptions): void
|
|
|
9001
9426
|
|
|
9002
9427
|
#### `viewConfig()` — Configure viewport helper visuals for the current script execution.
|
|
9003
9428
|
|
|
9004
|
-
Controls renderer-only overlays that appear in the viewport but are not part of the geometry. Currently supports the joint overlay that renders axis arrows and arc indicators when
|
|
9429
|
+
Controls renderer-only overlays that appear in the viewport but are not part of the geometry. Currently supports the joint overlay that renders axis arrows and arc indicators when joint controls are active. Multiple calls merge — later values override earlier ones per key.
|
|
9005
9430
|
|
|
9006
9431
|
This does **not** trigger a geometry recompute; it only affects the visual helpers drawn on top of the 3D scene.
|
|
9007
9432
|
|
|
@@ -9058,91 +9483,6 @@ explodeView(options?: ExplodeViewOptions): void
|
|
|
9058
9483
|
- `direction?: ExplodeDirection` — Direction mode for this node
|
|
9059
9484
|
- `axisLock?: ExplodeAxis` — Optional axis lock after direction is resolved
|
|
9060
9485
|
|
|
9061
|
-
#### `jointsView()` — Register viewport-only mechanism controls that animate returned objects without re-running the script.
|
|
9062
|
-
|
|
9063
|
-
Defines joints (revolute or prismatic), optional gear/rack couplings, and named animations. The viewport resolves transforms through the joint chain at display time — the script geometry is computed only once at rest pose.
|
|
9064
|
-
|
|
9065
|
-
**Critical:** Solve the assembly at **rest pose** (all animated joints = 0). The viewport applies `jointsView` transforms on top of the returned scene. If geometry is already solved at non-zero angles, animation will double-rotate everything.
|
|
9066
|
-
|
|
9067
|
-
```js
|
|
9068
|
-
// BAD — double rotation
|
|
9069
|
-
const solved = mech.solve({ shoulder: 45, elbow: 30 });
|
|
9070
|
-
jointsView({ joints: [{ name: 'shoulder', ... }] });
|
|
9071
|
-
return solved;
|
|
9072
|
-
|
|
9073
|
-
// GOOD — rest pose, jointsView controls all posing
|
|
9074
|
-
const solved = mech.solve({ shoulder: 0, elbow: 0 });
|
|
9075
|
-
jointsView({
|
|
9076
|
-
joints: [
|
|
9077
|
-
{ name: 'shoulder', child: 'Upper Arm', default: 45, ... },
|
|
9078
|
-
{ name: 'elbow', child: 'Forearm', parent: 'Upper Arm', default: 30, ... },
|
|
9079
|
-
],
|
|
9080
|
-
});
|
|
9081
|
-
return solved;
|
|
9082
|
-
```
|
|
9083
|
-
|
|
9084
|
-
**Pivot coordinates** are world-space positions of each joint origin at rest pose. For `addRevolute('shoulder', 'Base', 'Link', { frame: Transform.identity().translate(0, 0, 20) })` where "Base" is at world origin, the pivot is `[0, 0, 20]`.
|
|
9085
|
-
|
|
9086
|
-
**Fixed attachments** that must follow a parent during animation need a zero-angle revolute joint in the chain:
|
|
9087
|
-
|
|
9088
|
-
```js
|
|
9089
|
-
{ name: 'EE_Follow', child: 'End Effector', parent: 'Last Link',
|
|
9090
|
-
type: 'revolute', axis: [0, 0, 1], pivot: [linkLength, 0, 0],
|
|
9091
|
-
min: 0, max: 0, default: 0 }
|
|
9092
|
-
```
|
|
9093
|
-
|
|
9094
|
-
Animation values are interpolated linearly between keyframes. ForgeCAD does **not** auto-wrap revolute values across `-180/180`. Keep keyframe values continuous — a `-180 -> 171` jump spins the part the long way around. Use `-180 -> -189` instead. Author high-speed multi-turn joints as accumulating angles (`0, 360, 720, ...`) with `continuous: true`.
|
|
9095
|
-
|
|
9096
|
-
**Tick-based keyframes:** Omit `at` from all keyframes to auto-distribute by tick weight:
|
|
9097
|
-
|
|
9098
|
-
```js
|
|
9099
|
-
keyframes: [
|
|
9100
|
-
{ ticks: 3, values: { Shoulder: 20 } }, // slow segment (3x weight)
|
|
9101
|
-
{ ticks: 1, values: { Shoulder: -10 } }, // fast segment (1x weight)
|
|
9102
|
-
{ values: { Shoulder: 20 } }, // last keyframe; ticks ignored
|
|
9103
|
-
]
|
|
9104
|
-
// positions: 0, 0.75, 1.0
|
|
9105
|
-
```
|
|
9106
|
-
|
|
9107
|
-
Mixing explicit `at` and omitted `at` in the same animation is not allowed.
|
|
9108
|
-
|
|
9109
|
-
```js
|
|
9110
|
-
jointsView({
|
|
9111
|
-
joints: [{
|
|
9112
|
-
name: 'Shoulder', child: 'Upper Arm', parent: 'Base',
|
|
9113
|
-
type: 'revolute', axis: [0, -1, 0], pivot: [0, 0, 46],
|
|
9114
|
-
min: -30, max: 110, default: 15,
|
|
9115
|
-
}],
|
|
9116
|
-
animations: [{
|
|
9117
|
-
name: 'Walk Cycle', duration: 1.6, loop: true,
|
|
9118
|
-
keyframes: [
|
|
9119
|
-
{ values: { Shoulder: 20 } },
|
|
9120
|
-
{ values: { Shoulder: -10 } },
|
|
9121
|
-
{ values: { Shoulder: 20 } },
|
|
9122
|
-
],
|
|
9123
|
-
}],
|
|
9124
|
-
});
|
|
9125
|
-
```
|
|
9126
|
-
|
|
9127
|
-
```ts
|
|
9128
|
-
jointsView(options?: JointsViewOptions): void
|
|
9129
|
-
```
|
|
9130
|
-
|
|
9131
|
-
**`JointsViewOptions`**: `enabled?: boolean`, `joints?: JointViewInput[]`, `couplings?: JointViewCouplingInput[]`, `animations?: JointViewAnimationInput[]`, `defaultAnimation?: string`
|
|
9132
|
-
|
|
9133
|
-
**`JointViewInput`**: `name: string`, `child: string`, `parent?: string`, `type?: JointViewType`, `axis?: JointViewAxis`, `pivot?: [ number, number, number ]`, `min?: number`, `max?: number`, `default?: number`, `unit?: string`, `hidden?: boolean`
|
|
9134
|
-
|
|
9135
|
-
`JointViewCouplingInput`: `{ joint: string, terms: JointViewCouplingTermInput[], offset?: number }`
|
|
9136
|
-
|
|
9137
|
-
`JointViewCouplingTermInput`: `{ joint: string, ratio?: number }`
|
|
9138
|
-
|
|
9139
|
-
`JointViewAnimationInput`: `{ name: string, duration?: number, loop?: boolean, continuous?: boolean, keyframes: JointViewAnimationKeyframeInput[] }`
|
|
9140
|
-
|
|
9141
|
-
**`JointViewAnimationKeyframeInput`**
|
|
9142
|
-
- `at?: number` — Timeline position [0, 1]. If omitted from ALL keyframes, positions are auto-computed from tick weights.
|
|
9143
|
-
- `ticks?: number` — Relative weight of the segment from this keyframe to the next (default 1). Only used in tick-based mode (when `at` is omitted). Last keyframe's ticks value is ignored.
|
|
9144
|
-
- Also: `values: Record<string, number>`
|
|
9145
|
-
|
|
9146
9486
|
#### `compareWith()` — Declare a reference model for comparison inspection.
|
|
9147
9487
|
|
|
9148
9488
|
`compareWith()` lets a model carry its own comparison target for inspection workflows. `forgecad inspect compare overlay model.forge.js` uses this reference to render the same Difference Only comparison overlay as the live viewport. Amber marks candidate mismatch evidence, cyan marks reference mismatch evidence, and faint model context keeps the overlay readable. When the CLI can resolve the referenced file, the manifest also includes the same geometric score produced by `forgecad compare 3d`.
|
|
@@ -9475,6 +9815,30 @@ For deeper API coverage, load the relevant generated doc group from the skill so
|
|
|
9475
9815
|
|
|
9476
9816
|
How to build mechanical joints — clevis-tongue hinges, ball-and-socket, dovetails — that actually rotate without binding and stop where they should.
|
|
9477
9817
|
|
|
9818
|
+
## Frame-Aware Connectors First
|
|
9819
|
+
|
|
9820
|
+
If a part must rotate, slide, or point in a specific physical direction, define connectors and use `assembly().connect()`. A connector is a small coordinate frame on the part:
|
|
9821
|
+
|
|
9822
|
+
- `origin` is the pivot, pin center, socket center, or contact point.
|
|
9823
|
+
- `axis` is the hinge line or slide direction.
|
|
9824
|
+
- `up` is the secondary direction that fixes the part's zero-angle twist.
|
|
9825
|
+
|
|
9826
|
+
For a hip -> knee -> wheel chain, the upper leg should be a real part with a hip connector and a knee connector. The hip connector frame defines how the upper leg sits on the hip drum at rest; the knee connector frame defines where the next part attaches. `connect()` aligns those frames and derives the joint axis.
|
|
9827
|
+
|
|
9828
|
+
Do not treat `up` as optional on hinges, wheels, levers, or keyed sliders. If `up` is omitted, ForgeCAD chooses a deterministic perpendicular vector, which keeps the model stable but may not match the intended mechanical rest pose.
|
|
9829
|
+
|
|
9830
|
+
Use `link()`, `edgeBetweenLinks()`, and `addAngleBetweenLinks()` for solved point skeletons and closed-loop relationships. A link is a point, not a bone frame. `addPart(..., { mate: { connector, toLink } })` only moves a connector origin onto a solved link point; it does not rotate the part to aim along an edge.
|
|
9831
|
+
|
|
9832
|
+
## Mirrored Revolute Axes
|
|
9833
|
+
|
|
9834
|
+
Revolute joint values are physical values signed by the right-hand rule around the joint axis. If a bilateral mechanism mirrors a hinge axis, the same numeric joint value does not mean "same pose" on both sides.
|
|
9835
|
+
|
|
9836
|
+
Example: a right ankle hinge with `axis: [1, 0, 0]` and a left ankle hinge with `axis: [-1, 0, 0]` are exact geometric mirrors at rest. But `+20` degrees around those two axes rotates the feet in opposite fore/aft senses. The mirrored pose uses `RightAnkle: 20` and `LeftAnkle: -20`.
|
|
9837
|
+
|
|
9838
|
+
When you expose physical joint limits directly, mirror revolute limits as `[min, max] -> [-max, -min]`. Prismatic joints do not have this angle-handedness flip because their scalar value translates along the mirrored axis.
|
|
9839
|
+
|
|
9840
|
+
For bilateral robots, legs, grippers, and suspension mechanisms, prefer a side-neutral control layer when possible: solve mirrored link positions with `link()`, `edgeBetweenLinks()`, and `addAngleBetweenLinks()`, then attach real parts to the solved skeleton. If you use connector-frame joints directly, make the sign mapping explicit in defaults, keyframes, and verification.
|
|
9841
|
+
|
|
9478
9842
|
## The Cavity Rule
|
|
9479
9843
|
|
|
9480
9844
|
Every mechanical joint has a **cavity** in one part and a **tenon** in the other. The cavity must be a real empty volume — not a gap implied by the absence of two separate solids.
|
|
@@ -9606,7 +9970,7 @@ stable; named scene views are still available through `--view`.
|
|
|
9606
9970
|
The command tree is intentionally job-shaped:
|
|
9607
9971
|
|
|
9608
9972
|
```text
|
|
9609
|
-
inspect visual image|cutaway|depth|normals|objects
|
|
9973
|
+
inspect visual image|cutaway|depth|normals|rig|objects
|
|
9610
9974
|
inspect surface zebra|roughness
|
|
9611
9975
|
inspect physical components|floating|gaps
|
|
9612
9976
|
inspect fit interference
|
|
@@ -9647,6 +10011,7 @@ Use targeted evidence commands for expensive analyses:
|
|
|
9647
10011
|
```bash
|
|
9648
10012
|
forgecad inspect visual depth model.forge.js --camera iso
|
|
9649
10013
|
forgecad inspect visual normals model.forge.js --camera iso
|
|
10014
|
+
forgecad inspect visual rig model.forge.js --camera iso
|
|
9650
10015
|
forgecad inspect surface zebra model.forge.js --camera iso
|
|
9651
10016
|
forgecad inspect surface roughness model.forge.js --camera iso
|
|
9652
10017
|
forgecad inspect visual objects model.forge.js --camera iso
|
|
@@ -10132,12 +10497,33 @@ color(value: string | undefined): SdfShape
|
|
|
10132
10497
|
material(props: ShapeMaterialProps): SdfShape
|
|
10133
10498
|
```
|
|
10134
10499
|
|
|
10500
|
+
**`ShapeMaterialProps`**
|
|
10501
|
+
|
|
10502
|
+
| Option | Type | Description |
|
|
10503
|
+
|--------|------|-------------|
|
|
10504
|
+
| `metalness?` | `number` | Metalness factor (0 = dielectric, 1 = metal). Default: 0.05 |
|
|
10505
|
+
| `roughness?` | `number` | Roughness factor (0 = mirror, 1 = fully diffuse). Default: 0.35 |
|
|
10506
|
+
| `emissive?` | `string` | Emissive glow color (hex string, e.g. "#ff6b35"). |
|
|
10507
|
+
| `emissiveIntensity?` | `number` | Emissive intensity multiplier. Default: 1 |
|
|
10508
|
+
| `opacity?` | `number` | Opacity (0 = fully transparent, 1 = fully opaque). Default: 1 |
|
|
10509
|
+
| `wireframe?` | `boolean` | Render as wireframe. Default: false |
|
|
10510
|
+
| `clearcoat?` | `number` | Clearcoat intensity (0–1). Default: 0.1 |
|
|
10511
|
+
| `clearcoatRoughness?` | `number` | Clearcoat roughness (0–1). Default: 0.4 |
|
|
10512
|
+
| `transmission?` | `number` | Glass/translucency transmission factor (0–1). Renderer support depends on target. |
|
|
10513
|
+
| `ior?` | `number` | Index of refraction for transmissive materials. Typical glass is ~1.45. |
|
|
10514
|
+
| `thickness?` | `number` | Approximate transmissive volume thickness in model units. |
|
|
10515
|
+
| `specularIntensity?` | `number` | Specular highlight intensity (0–1). |
|
|
10516
|
+
| `specularColor?` | `string` | Specular highlight tint. |
|
|
10517
|
+
| `reflectivity?` | `number` | Reflection strength for supported renderers (0–1). |
|
|
10518
|
+
|
|
10135
10519
|
#### `bounds()` — Set explicit preview/meshing bounds for this implicit leaf.
|
|
10136
10520
|
|
|
10137
10521
|
```ts
|
|
10138
10522
|
bounds(bounds: SdfBounds | [ Vec3, Vec3 ]): SdfShape
|
|
10139
10523
|
```
|
|
10140
10524
|
|
|
10525
|
+
`SdfBounds`: `{ min: Vec3, max: Vec3 }`
|
|
10526
|
+
|
|
10141
10527
|
#### `at()` — Sculpt-style alias for translate().
|
|
10142
10528
|
|
|
10143
10529
|
```ts
|
|
@@ -10234,6 +10620,12 @@ fillWith(pattern: SdfShape): SdfShape
|
|
|
10234
10620
|
fillWithGyroid(options: TpmsOptions): SdfShape
|
|
10235
10621
|
```
|
|
10236
10622
|
|
|
10623
|
+
**`TpmsOptions`**
|
|
10624
|
+
- `thickness?: number` — Dimensionless field threshold kept for compatibility. Prefer `wallThickness` for approximate millimeter units.
|
|
10625
|
+
- `wallThickness?: number` — Approximate physical wall thickness in millimeters.
|
|
10626
|
+
- `tpmsThicknessMode?: TpmsThicknessMode` — Override TPMS thickness interpretation. Defaults to metric when `wallThickness` is used, field-threshold when `thickness` is used.
|
|
10627
|
+
- Also: `cellSize: number`
|
|
10628
|
+
|
|
10237
10629
|
#### `fillWithSchwarzP()` — Keep only the Schwarz-P lattice inside this shape.
|
|
10238
10630
|
|
|
10239
10631
|
```ts
|
|
@@ -10384,6 +10776,10 @@ shape.surfaceDisplace((u, v) => -Math.sin(u * 2) * 0.3)
|
|
|
10384
10776
|
surfaceDisplace(pattern: SurfacePattern | ((u: number, v: number) => number), options?: SurfaceDisplaceOptions): SdfShape
|
|
10385
10777
|
```
|
|
10386
10778
|
|
|
10779
|
+
**`SurfaceDisplaceOptions`**
|
|
10780
|
+
- `uv?: "auto" | "sphere" | "cylinder" | "torus" | "triplanar"` — Override auto-detected UV mode. Default: 'auto' (detects from SDF tree).
|
|
10781
|
+
- `triplanarSharpness?: number` — Triplanar blend sharpness — higher = crisper transitions. Default: 4. Only used in triplanar mode.
|
|
10782
|
+
|
|
10387
10783
|
#### `onion()` — Create concentric onion layers.
|
|
10388
10784
|
|
|
10389
10785
|
```ts
|