forgecad 0.9.14 → 0.9.16
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-CXvls4-J.js} +2 -2
- package/dist/assets/{BenchmarkPage-CTrLKfpo.js → BenchmarkPage-B27zk8xL.js} +4 -15
- package/dist/assets/{BlogPage-5nPesyds.js → BlogPage-CMAVvgQL.js} +2 -2
- package/dist/assets/{DocsPage-C4Y3nbYc.js → DocsPage-knf4I4h7.js} +9 -3
- package/dist/assets/EditorApp-BHMQlJ-D.js +14686 -0
- package/dist/assets/{EditorApp-BAnckbsk.css → EditorApp-BpjZgzk0.css} +846 -0
- package/dist/assets/{EmbedViewer-C8fB4n5U.js → EmbedViewer-D7ZGlFjx.js} +3 -3
- package/dist/assets/{LandingPageProofDriven-jSz0LaMM.js → LandingPageProofDriven-CnevhTE8.js} +36 -38
- package/dist/assets/LegalPage-BPTUmqeg.js +39 -0
- package/dist/assets/LegalPage-BRlScr9A.css +91 -0
- package/dist/assets/{PricingPage-B83B90zh.js → PricingPage-B0D4goG_.js} +19 -19
- package/dist/assets/{PricingPage-BMedqFef.css → PricingPage-BPF6HKyO.css} +25 -0
- package/dist/assets/{SettingsPage-DY889pcu.js → SettingsPage-CFF-UgjI.js} +2 -2
- package/dist/assets/app-CE3sYcV7.css +3890 -0
- package/dist/assets/{app-bEww1ic4.js → app-T0pDcSX4.js} +3382 -1069
- package/dist/assets/cli/{render-Cho2uKG_.js → render-C5pcIISc.js} +477 -29
- package/dist/assets/{constructionHistoryWorker-HYwzJY4m.js → constructionHistoryWorker-Ba2Hm58b.js} +928 -243
- package/dist/assets/{evalWorker-CjQwJSE-.js → evalWorker-vkx310U2.js} +8883 -6040
- 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-BuTJDVX6.js} +1179 -273
- package/dist/assets/{javascript-70-4uGcz.js → javascript-1kQXfVaz.js} +1 -1
- package/dist/assets/{targets-D6PWsv6X.js → jointPose-B_Cgedn9.js} +71 -3
- 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-rmfAcdwF.js → manifold-BWgsjmAM.js} +1 -1
- package/dist/assets/{manifold-uRzgk5O8.js → manifold-D6IFSkhH.js} +2 -2
- package/dist/assets/{manifold-CG9Fokx-.js → manifold-rZexZI0G.js} +1 -1
- package/dist/assets/{reportWorker-4cW_ZpoS.js → reportWorker-0AGij1Ru.js} +8659 -12771
- package/dist/assets/{scalar-sampling-budget-CfDiFvh7.js → scalar-sampling-budget-J5cuzxT1.js} +8050 -6203
- package/dist/assets/{scanProxyWorker-Bs2TDgLw.js → scanProxyWorker-Vl4Wxa1y.js} +50 -6
- package/dist/assets/{solver-DuJAO8S6.js → solver-BZ9LPTHs.js} +1 -1
- package/dist/assets/solver_bg-DAHZJ_rw.wasm +0 -0
- 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 +5 -12
- package/dist/docs-raw/CLI.md +34 -10
- package/dist/docs-raw/component-model.md +27 -11
- package/dist/docs-raw/generated/assembly.md +374 -187
- package/dist/docs-raw/generated/concepts.md +245 -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 +9 -19
- package/dist/docs-raw/generated/output.md +29 -4
- package/dist/docs-raw/generated/runtime-names.md +49 -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 +40 -39
- 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 +3 -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/docs-raw/welcome.md +2 -0
- 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-SYQ2PWOB.js} +1 -2
- package/dist-cli/{check-query-propagation-XOKNSSYU.js → check-query-propagation-HIAGV62W.js} +1 -2
- package/dist-cli/{chunk-EXWGNL6K.js → chunk-SPZE3DUY.js} +20659 -17930
- package/dist-cli/forgecad.js +3568 -1250
- 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 +1192 -725
- package/dist-skill/SKILL.md +3 -2
- package/dist-skill/docs/API/core/concepts.md +64 -1
- package/dist-skill/docs/CLI.md +34 -10
- package/dist-skill/docs/generated/assembly.md +339 -213
- 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 +9 -19
- package/dist-skill/docs/generated/output.md +29 -4
- package/dist-skill/docs/generated/runtime-names.md +40 -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-make-a-model/SKILL.md +39 -38
- 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-project/SKILL.md +2 -0
- 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 +8 -8
- 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 +71 -0
- package/examples/api/transition-curves.forge.js +44 -15
- package/examples/api/variable-sweep-test.forge.js +3 -1
- 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 +17 -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/examples/api/bolted-service-cover.forge.js +0 -17
- package/examples/api/cable-gland-anchor.forge.js +0 -14
- package/examples/api/captured-cartridge-guide.forge.js +0 -14
- package/examples/api/captured-linear-slide.forge.js +0 -13
- package/examples/api/clevis-pin-joint.forge.js +0 -13
- package/examples/api/datum-enclosure.forge.js +0 -16
- package/examples/api/hose-barb-port.forge.js +0 -14
- package/examples/api/knuckled-hinge-assembly.forge.js +0 -15
- package/examples/api/living-hinge-cover.forge.js +0 -14
- package/examples/api/pcb-terminal-block.forge.js +0 -22
- package/examples/api/pinned-lever-pivot-stack.forge.js +0 -14
- package/examples/api/retained-shaft-knob-stack.forge.js +0 -15
- package/examples/api/routed-tube-clip.forge.js +0 -15
- package/examples/api/seated-bearing-stack.forge.js +0 -30
- package/examples/api/snap-latch-cover.forge.js +0 -14
- package/examples/api/thumb-screw-clamp.forge.js +0 -15
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,46 @@ 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, connector, console, constrainedSketch, Constraint
|
|
297
|
+
Counterbore, Curve, Curve3D, cutPlane, cylinder, degrees, difference, difference2d
|
|
298
|
+
dim, dimLine, draft, ellipse, explodeView, faceProfile, fillet, filletCorners
|
|
299
|
+
filletTrackedEdge, fingerJoint, flatPanel, flatPart, formatInstructions, Function, gcode, GCodeBuilder
|
|
300
|
+
getActiveBackend, global, globalThis, group, highlight, Import, ImportedAssembly, importMesh
|
|
301
|
+
importStep, importSvgSketch, initKernel, intersection, intersection2d, intersectWithPlane, joint, jointsView
|
|
302
|
+
laserKit, lib, line, Line2D, linearPattern, linearPattern2d, listParam, loadFont
|
|
303
|
+
loft, Loft, lookupKerf, mirrorCopy, mock, ngon, NurbsCurve3D, nurbsSurface
|
|
304
|
+
NurbsSurface, offsetSolid, param, Param, path, point, Point2D, Points
|
|
305
|
+
polygon, polygonVertices, port, Product, ProductHandleBuilder, ProductHandleFeature, ProductPanelBuilder, ProductRibbonBuilder
|
|
306
|
+
ProductSkin, ProductSkinBuilder, ProductSpoutBuilder, ProductStationBuilder, ProductSurfaceBuilder, ProductSurfaceRef, projectToPlane, queueMicrotask
|
|
307
|
+
radians, rect, Rectangle2D, Ribs, robotExport, roundedRect, Route3D, scene
|
|
308
|
+
Sculpt, sdf, SdfShape, selectEdge, selectEdges, self, setActiveBackend, setImmediate
|
|
309
|
+
setInterval, setTimeout, Shape, ShapeGroup, sheetMetal, SheetMetalPart, sheetStock, Sketch
|
|
310
|
+
sketchToDxf, sketchToSvg, slot, Slot, SolvedAssembly, spec, sphere, spline2d
|
|
311
|
+
star, stroke, Surface, SurfaceBody, SurfaceMembers, surfacePatch, sweep, tabSlot
|
|
312
|
+
text2d, textWidth, torus, toShape, Transform, union, union2d, variableSweep
|
|
313
|
+
verify, viewConfig, Viewport, window, Wood
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
`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.
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
217
320
|
<!-- generated/core.md -->
|
|
218
321
|
|
|
219
322
|
# Core API
|
|
@@ -238,6 +341,8 @@ For organic shapes, smooth blending, TPMS lattices, and surface deformations. Re
|
|
|
238
341
|
- [SurfacePattern](#surfacepattern)
|
|
239
342
|
- [Pattern2D](#pattern2d)
|
|
240
343
|
- [Pattern2DBuilder](#pattern2dbuilder)
|
|
344
|
+
- [HermiteCurve3D](#hermitecurve3d)
|
|
345
|
+
- [QuinticHermiteCurve3D](#quintichermitecurve3d)
|
|
241
346
|
- [ShapeRef](#shaperef)
|
|
242
347
|
- [ANCHOR3D_NAMES](#anchor3d-names)
|
|
243
348
|
- [verify](#verify)
|
|
@@ -577,9 +682,9 @@ selectEdges(shape: Shape, query?: EdgeQuery): EdgeSegment[]
|
|
|
577
682
|
| `minLength?` | `number` | Filter: minimum edge length. |
|
|
578
683
|
| `maxLength?` | `number` | Filter: maximum edge length. |
|
|
579
684
|
| `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
|
|
685
|
+
| `atZ?` | `number` | Shorthand: edge midpoint Z is approximately this value within `tolerance`. |
|
|
686
|
+
| `tolerance?` | `number` | Position tolerance for approximate matches. Used by `atZ` and `near`. Default: `1.0`. |
|
|
687
|
+
| `angleTolerance?` | `number` | Angular tolerance in degrees for `parallel`/`perpendicular` filters. Default: `10`. |
|
|
583
688
|
|
|
584
689
|
`BoundingRegion`: `{ xMin?: number, xMax?: number, yMin?: number, yMax?: number, zMin?: number, zMax?: number }`
|
|
585
690
|
|
|
@@ -631,7 +736,16 @@ coalesceEdges(segments: EdgeSegment[], tolerance?: number): EdgeSegment[]
|
|
|
631
736
|
|
|
632
737
|
#### `require()` — Import a module with optional ForgeCAD parameter overrides. Returns the module's exports.
|
|
633
738
|
|
|
634
|
-
When importing a `.forge.js` file,
|
|
739
|
+
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.
|
|
740
|
+
|
|
741
|
+
**Assembly return contract**
|
|
742
|
+
|
|
743
|
+
| `.forge.js` return value | `require()` result |
|
|
744
|
+
|---|---|
|
|
745
|
+
| `Assembly` | `ImportedAssembly` |
|
|
746
|
+
| `SolvedAssembly` | `SolvedAssembly` |
|
|
747
|
+
|
|
748
|
+
[`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
749
|
|
|
636
750
|
**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
751
|
|
|
@@ -662,6 +776,26 @@ mount.bolts.pos // access the metadata
|
|
|
662
776
|
mount.shape // access the geometry
|
|
663
777
|
```
|
|
664
778
|
|
|
779
|
+
**Forge-aware builder module pattern** — use `.forge.js` modules for reusable sketch, profile, shape, or assembly builders that need ForgeCAD runtime APIs:
|
|
780
|
+
|
|
781
|
+
```js
|
|
782
|
+
// profiles.forge.js — inspectable on its own, reusable through require()
|
|
783
|
+
function wheelProfile() {
|
|
784
|
+
return circle2d(40).subtract(circle2d(18));
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
return {
|
|
788
|
+
preview: [{ name: 'Wheel profile', sketch: wheelProfile() }],
|
|
789
|
+
make: { wheelProfile },
|
|
790
|
+
};
|
|
791
|
+
|
|
792
|
+
// main.forge.js
|
|
793
|
+
const profiles = require('./profiles.forge.js');
|
|
794
|
+
const wheel = profiles.make.wheelProfile().extrude(8);
|
|
795
|
+
```
|
|
796
|
+
|
|
797
|
+
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.
|
|
798
|
+
|
|
665
799
|
```ts
|
|
666
800
|
require(path: string, paramOverrides?: Record<string, number | string>): any
|
|
667
801
|
```
|
|
@@ -690,10 +824,18 @@ importSvgSketch(fileName: string, options?: SvgImportOptions): Sketch
|
|
|
690
824
|
| `simplify?` | `number` | Simplification tolerance for final sketch cleanup. |
|
|
691
825
|
| `invertY?` | `boolean` | Flip SVG Y-down coordinates to CAD Y-up. Enabled by default. |
|
|
692
826
|
|
|
693
|
-
#### `importMesh()` — Import an external mesh file (STL, OBJ, 3MF)
|
|
827
|
+
#### `importMesh()` — Import an external mesh file (STL, OBJ, 3MF).
|
|
828
|
+
|
|
829
|
+
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`.
|
|
830
|
+
|
|
831
|
+
```js
|
|
832
|
+
const all = importMesh("./assembly.3mf", { separateObjects: true });
|
|
833
|
+
const pin = all.child("Pin #001");
|
|
834
|
+
const plate = importMesh("./assembly.3mf", { object: "3mf:build:001:object:7" });
|
|
835
|
+
```
|
|
694
836
|
|
|
695
837
|
```ts
|
|
696
|
-
importMesh(fileName: string, options?: { scale?: number; center?: boolean; }): Shape
|
|
838
|
+
importMesh(fileName: string, options?: { scale?: number; center?: boolean; object?: string; separateObjects?: boolean; }): Shape | ShapeGroup
|
|
697
839
|
```
|
|
698
840
|
|
|
699
841
|
#### `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 +1308,25 @@ box(100, 100, 10).color('#gold').material({ metalness: 0.95, roughness: 0.05 }).
|
|
|
1166
1308
|
material(props: ShapeMaterialProps): Shape
|
|
1167
1309
|
```
|
|
1168
1310
|
|
|
1311
|
+
**`ShapeMaterialProps`**
|
|
1312
|
+
|
|
1313
|
+
| Option | Type | Description |
|
|
1314
|
+
|--------|------|-------------|
|
|
1315
|
+
| `metalness?` | `number` | Metalness factor (0 = dielectric, 1 = metal). Default: 0.05 |
|
|
1316
|
+
| `roughness?` | `number` | Roughness factor (0 = mirror, 1 = fully diffuse). Default: 0.35 |
|
|
1317
|
+
| `emissive?` | `string` | Emissive glow color (hex string, e.g. "#ff6b35"). |
|
|
1318
|
+
| `emissiveIntensity?` | `number` | Emissive intensity multiplier. Default: 1 |
|
|
1319
|
+
| `opacity?` | `number` | Opacity (0 = fully transparent, 1 = fully opaque). Default: 1 |
|
|
1320
|
+
| `wireframe?` | `boolean` | Render as wireframe. Default: false |
|
|
1321
|
+
| `clearcoat?` | `number` | Clearcoat intensity (0–1). Default: 0.1 |
|
|
1322
|
+
| `clearcoatRoughness?` | `number` | Clearcoat roughness (0–1). Default: 0.4 |
|
|
1323
|
+
| `transmission?` | `number` | Glass/translucency transmission factor (0–1). Renderer support depends on target. |
|
|
1324
|
+
| `ior?` | `number` | Index of refraction for transmissive materials. Typical glass is ~1.45. |
|
|
1325
|
+
| `thickness?` | `number` | Approximate transmissive volume thickness in model units. |
|
|
1326
|
+
| `specularIntensity?` | `number` | Specular highlight intensity (0–1). |
|
|
1327
|
+
| `specularColor?` | `string` | Specular highlight tint. |
|
|
1328
|
+
| `reflectivity?` | `number` | Reflection strength for supported renderers (0–1). |
|
|
1329
|
+
|
|
1169
1330
|
**Face Topology**
|
|
1170
1331
|
|
|
1171
1332
|
#### `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 +1447,15 @@ body.edgesOf('top', { concave: true })
|
|
|
1286
1447
|
edgesOf(faceLabel: string, options?: EdgesOfOptions): EdgeSegment[]
|
|
1287
1448
|
```
|
|
1288
1449
|
|
|
1450
|
+
**`EdgesOfOptions`**
|
|
1451
|
+
|
|
1452
|
+
| Option | Type | Description |
|
|
1453
|
+
|--------|------|-------------|
|
|
1454
|
+
| `exclude?` | `string \| string[]` | Exclude edges shared with these named faces. |
|
|
1455
|
+
| `convex?` | `boolean` | Additional geometric filter: only convex edges. |
|
|
1456
|
+
| `concave?` | `boolean` | Additional geometric filter: only concave edges. |
|
|
1457
|
+
| `minLength?` | `number` | Minimum edge length filter. |
|
|
1458
|
+
|
|
1289
1459
|
#### `edgesBetween()` — Return edges shared between two named faces.
|
|
1290
1460
|
|
|
1291
1461
|
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 +1537,8 @@ rotateZ(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): Sh
|
|
|
1367
1537
|
rotateAroundTo(axis: [ number, number, number ], pivot: [ number, number, number ], movingPoint: RotationPointLike, targetPoint: RotationPointLike, options?: RotateAroundToOptions): Shape
|
|
1368
1538
|
```
|
|
1369
1539
|
|
|
1540
|
+
`RotateAroundToOptions`: `{ mode?: RotateAroundToMode }`
|
|
1541
|
+
|
|
1370
1542
|
#### `transform()` — Apply a 4x4 affine transform matrix (column-major) or a Transform object.
|
|
1371
1543
|
|
|
1372
1544
|
```ts
|
|
@@ -1463,6 +1635,11 @@ box(100, 100, 20).pocket('top', 8, { scale: 0.8 })
|
|
|
1463
1635
|
pocket(face: FaceSelector, depth: number, opts?: PocketOptions): Shape
|
|
1464
1636
|
```
|
|
1465
1637
|
|
|
1638
|
+
**`PocketOptions`**
|
|
1639
|
+
- `inset?: number` — Shrink the face boundary inward by this many mm before extruding. Produces angled walls when combined with depth. Default: 0 (full face).
|
|
1640
|
+
- `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.
|
|
1641
|
+
- `join?: "Square" | "Round" | "Miter"` — Corner join style when using `inset`. Default: 'Round'.
|
|
1642
|
+
|
|
1466
1643
|
#### `boss()` — Add a boss (protrusion) from the named face.
|
|
1467
1644
|
|
|
1468
1645
|
```js
|
|
@@ -1485,6 +1662,30 @@ box(50, 50, 20).hole('top', { diameter: 6, counterbore: { diameter: 12, depth: 3
|
|
|
1485
1662
|
hole(faceOrRef: SketchFaceTarget | FaceRef, opts: ShapeHoleOptions): Shape
|
|
1486
1663
|
```
|
|
1487
1664
|
|
|
1665
|
+
**`FaceRef`**
|
|
1666
|
+
|
|
1667
|
+
| Option | Type | Description |
|
|
1668
|
+
|--------|------|-------------|
|
|
1669
|
+
| `normal` | `[ number, number, number ]` | Normal direction of the face |
|
|
1670
|
+
| `center` | `[ number, number, number ]` | Center point of the face |
|
|
1671
|
+
| `query?` | `FaceQueryRef` | Compiler-owned face query when available. |
|
|
1672
|
+
| `planar?` | `boolean` | True when the face can host a 2D sketch placement frame |
|
|
1673
|
+
| `uAxis?` | `[ number, number, number ]` | Face-local horizontal axis for planar faces |
|
|
1674
|
+
| `vAxis?` | `[ number, number, number ]` | Face-local vertical axis for planar faces |
|
|
1675
|
+
| `surface?` | `FaceSurface` | Analytic surface family when the backend can identify one. |
|
|
1676
|
+
| `descendant?` | `FaceDescendantMetadata` | Shared descendant-resolution metadata when this face is a semantic region/set. |
|
|
1677
|
+
| `name` | | — |
|
|
1678
|
+
|
|
1679
|
+
**`FaceDescendantMetadata`**: `kind: "single" | "face-set"`, `semantic: FaceDescendantSemantic`, `memberCount: number`, `memberNames: string[]`, `coplanar: boolean`
|
|
1680
|
+
|
|
1681
|
+
**`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`
|
|
1682
|
+
|
|
1683
|
+
`ShapeFeatureExtentOptions`: `{ forward: ShapeFeatureExtentSideOptions, reverse?: ShapeFeatureExtentSideOptions }`
|
|
1684
|
+
|
|
1685
|
+
`ShapeFeatureExtentSideOptions`: `{ depth?: number, upToFace?: SketchFaceTarget | FaceRef, through?: boolean }`
|
|
1686
|
+
|
|
1687
|
+
**`ShapeHoleThreadOptions`**: `designation?: string`, `pitch?: number`, `class?: string`, `handedness?: "right" | "left"`, `depth?: number`, `modeled?: boolean`
|
|
1688
|
+
|
|
1488
1689
|
#### `cutout()` — Cut a profile-shaped pocket through a face using a placed sketch.
|
|
1489
1690
|
|
|
1490
1691
|
The sketch must be placed on a face with `Sketch.onFace(...)`. The cut follows the sketch's 2D profile.
|
|
@@ -1498,6 +1699,8 @@ body.cutout(profile, { depth: 5 })
|
|
|
1498
1699
|
cutout(sketch: Sketch, opts?: ShapeCutoutOptions): Shape
|
|
1499
1700
|
```
|
|
1500
1701
|
|
|
1702
|
+
**`ShapeCutoutOptions`**: `depth?: number`, `upToFace?: SketchFaceTarget | FaceRef`, `extent?: ShapeFeatureExtentOptions`, `taperScale?: number | [ number, number ]`
|
|
1703
|
+
|
|
1501
1704
|
**Placement**
|
|
1502
1705
|
|
|
1503
1706
|
#### `placeReference()` — Translate the shape so the given anchor or reference lands on the target coordinate.
|
|
@@ -1561,6 +1764,11 @@ mast.translate(0, station, radius + 50).seatInto(fuselage, 'mount', { depth: 'fl
|
|
|
1561
1764
|
seatInto(target: Shape, surface: string, options?: SeatIntoOptions): Shape
|
|
1562
1765
|
```
|
|
1563
1766
|
|
|
1767
|
+
**`SeatIntoOptions`**
|
|
1768
|
+
- `along?: [ number, number, number ]` — Movement axis. Default: inverted face normal (points into target).
|
|
1769
|
+
- `depth?: "full" | "flush" | number` — How deep to embed. 'full' = entire face inside. 'flush' = nearest point touches. number = mm past flush. Default: 'full'.
|
|
1770
|
+
- `gap?: number` — Standoff gap in mm. Positive = gap between face and target. Negative = extra penetration. Default: 0.
|
|
1771
|
+
|
|
1564
1772
|
#### `seatOver()` — Slide this shape until a target's labeled face is fully covered (inside this shape).
|
|
1565
1773
|
|
|
1566
1774
|
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 +1793,10 @@ seatOver(target: Shape, targetSurface: string, options?: SeatIntoOptions): Shape
|
|
|
1585
1793
|
withConnectors(connectors: Record<string, ConnectorInput>): Shape
|
|
1586
1794
|
```
|
|
1587
1795
|
|
|
1796
|
+
**`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`
|
|
1797
|
+
|
|
1798
|
+
`ConnectorInput`: `{ connectorType?: string, gender?: ConnectorGender, measurements?: Record<string, number | string> }`
|
|
1799
|
+
|
|
1588
1800
|
#### `connectorNames()` — List all connector names on this shape.
|
|
1589
1801
|
|
|
1590
1802
|
```ts
|
|
@@ -1621,6 +1833,8 @@ Overloads:
|
|
|
1621
1833
|
matchTo(targetOrPairs: Shape | MatchTarget | Array<[ Shape | MatchTarget, string, string ]>, selfConnOrDict?: string | Record<string, string>, targetConnOrOptions?: string | MatchToOptions, maybeOptions?: MatchToOptions): Shape
|
|
1622
1834
|
```
|
|
1623
1835
|
|
|
1836
|
+
`MatchToOptions`: `{ force?: boolean, angle?: number, distance?: number }`
|
|
1837
|
+
|
|
1624
1838
|
**References**
|
|
1625
1839
|
|
|
1626
1840
|
#### `withReferences()` — Attach named placement references that survive normal transforms and imports.
|
|
@@ -1629,6 +1843,12 @@ matchTo(targetOrPairs: Shape | MatchTarget | Array<[ Shape | MatchTarget, string
|
|
|
1629
1843
|
withReferences(refs: PlacementReferenceInput): Shape
|
|
1630
1844
|
```
|
|
1631
1845
|
|
|
1846
|
+
**`PlacementReferenceInput`**: `points?: Record<string, [ number, number, number ]>`, `edges?: Record<string, PlacementEdgeRef>`, `surfaces?: Record<string, PlacementSurfaceRef>`, `objects?: Record<string, PlacementObjectInput>`
|
|
1847
|
+
|
|
1848
|
+
`PlacementEdgeRef`: `{ start: Vec3, end: Vec3 }`
|
|
1849
|
+
|
|
1850
|
+
`PlacementSurfaceRef`: `{ center: Vec3, normal: Vec3 }`
|
|
1851
|
+
|
|
1632
1852
|
#### `referenceNames()` — List named placement references carried by this shape.
|
|
1633
1853
|
|
|
1634
1854
|
```ts
|
|
@@ -1790,6 +2010,24 @@ translate(x: number, y: number, z: number): Transform
|
|
|
1790
2010
|
rotateAxis(axis: Vec3, angleDeg: number, pivot?: Vec3): Transform
|
|
1791
2011
|
```
|
|
1792
2012
|
|
|
2013
|
+
#### `rotateX()` — Rotate about the X axis after the current transform (parity with `Shape.rotateX`).
|
|
2014
|
+
|
|
2015
|
+
```ts
|
|
2016
|
+
rotateX(angleDeg: number, pivot?: Vec3): Transform
|
|
2017
|
+
```
|
|
2018
|
+
|
|
2019
|
+
#### `rotateY()` — Rotate about the Y axis after the current transform (parity with `Shape.rotateY`).
|
|
2020
|
+
|
|
2021
|
+
```ts
|
|
2022
|
+
rotateY(angleDeg: number, pivot?: Vec3): Transform
|
|
2023
|
+
```
|
|
2024
|
+
|
|
2025
|
+
#### `rotateZ()` — Rotate about the Z axis after the current transform (parity with `Shape.rotateZ`).
|
|
2026
|
+
|
|
2027
|
+
```ts
|
|
2028
|
+
rotateZ(angleDeg: number, pivot?: Vec3): Transform
|
|
2029
|
+
```
|
|
2030
|
+
|
|
1793
2031
|
#### `inverse()` — Return the inverse transform.
|
|
1794
2032
|
|
|
1795
2033
|
```ts
|
|
@@ -2133,18 +2371,160 @@ constant(value?: number): Pattern2D
|
|
|
2133
2371
|
sineWave(options: Pattern2DSineWaveOptions): Pattern2D
|
|
2134
2372
|
```
|
|
2135
2373
|
|
|
2374
|
+
**`Pattern2DSineWaveOptions`**
|
|
2375
|
+
|
|
2376
|
+
| Option | Type | Description |
|
|
2377
|
+
|--------|------|-------------|
|
|
2378
|
+
| `direction?` | `Vec2` | Direction the wave advances in UV space. Default: [1, 0]. |
|
|
2379
|
+
| `wavelength` | `number` | Distance between wave peaks in surface millimeters. |
|
|
2380
|
+
| `amplitude?` | `number` | Height amplitude in millimeters. Default: 1. |
|
|
2381
|
+
| `phase?` | `number` | Phase offset in radians. Default: 0. |
|
|
2382
|
+
| `bias?` | `number` | Constant height offset in millimeters. Default: 0. |
|
|
2383
|
+
|
|
2136
2384
|
#### `stripes()` — Create recessed stripe bands in UV space.
|
|
2137
2385
|
|
|
2138
2386
|
```ts
|
|
2139
2387
|
stripes(options: Pattern2DStripesOptions): Pattern2D
|
|
2140
2388
|
```
|
|
2141
2389
|
|
|
2390
|
+
**`Pattern2DStripesOptions`**
|
|
2391
|
+
|
|
2392
|
+
| Option | Type | Description |
|
|
2393
|
+
|--------|------|-------------|
|
|
2394
|
+
| `direction?` | `Vec2` | Direction perpendicular to the stripe bands in UV space. Default: [1, 0]. |
|
|
2395
|
+
| `spacing` | `number` | Center-to-center spacing in surface millimeters. |
|
|
2396
|
+
| `width` | `number` | Stripe width in surface millimeters. |
|
|
2397
|
+
| `depth?` | `number` | Stripe groove depth in millimeters. Default: 1. |
|
|
2398
|
+
|
|
2142
2399
|
#### `overUnderWeave()` — Create an over-under woven relief pattern in UV space.
|
|
2143
2400
|
|
|
2144
2401
|
```ts
|
|
2145
2402
|
overUnderWeave(options: Pattern2DOverUnderWeaveOptions): Pattern2D
|
|
2146
2403
|
```
|
|
2147
2404
|
|
|
2405
|
+
**`Pattern2DOverUnderWeaveOptions`**
|
|
2406
|
+
|
|
2407
|
+
| Option | Type | Description |
|
|
2408
|
+
|--------|------|-------------|
|
|
2409
|
+
| `spacing` | `number \| Vec2` | Thread center-to-center spacing. A number uses the same spacing for U and V. |
|
|
2410
|
+
| `threadWidth` | `number \| Vec2` | Thread width. A number uses the same width for U and V. |
|
|
2411
|
+
| `depth?` | `number` | Thread groove depth in millimeters. Default: 0.8. |
|
|
2412
|
+
| `underScale?` | `number` | Relative height of the under-crossing thread. Default: 0.15. |
|
|
2413
|
+
|
|
2414
|
+
### `HermiteCurve3D`
|
|
2415
|
+
|
|
2416
|
+
**Properties:**
|
|
2417
|
+
|
|
2418
|
+
| Property | Type | Description |
|
|
2419
|
+
|----------|------|-------------|
|
|
2420
|
+
| `p0` | `Vec3` | Start position |
|
|
2421
|
+
| `p1` | `Vec3` | End position |
|
|
2422
|
+
| `t0` | `Vec3` | Scaled tangent at start (direction * weight * chordLength) |
|
|
2423
|
+
| `t1` | `Vec3` | Scaled tangent at end (direction * weight * chordLength) |
|
|
2424
|
+
| `chordLength` | `number` | Chord length (straight-line distance between endpoints) |
|
|
2425
|
+
|
|
2426
|
+
**Methods:**
|
|
2427
|
+
|
|
2428
|
+
#### `pointAt()` — Evaluate position at parameter t ∈ [0, 1]
|
|
2429
|
+
|
|
2430
|
+
```ts
|
|
2431
|
+
pointAt(t: number): Vec3
|
|
2432
|
+
```
|
|
2433
|
+
|
|
2434
|
+
#### `tangentAt()` — Evaluate tangent (first derivative) at parameter t ∈ [0, 1]
|
|
2435
|
+
|
|
2436
|
+
```ts
|
|
2437
|
+
tangentAt(t: number): Vec3
|
|
2438
|
+
```
|
|
2439
|
+
|
|
2440
|
+
#### `curvatureAt()` — Evaluate curvature vector (second derivative) at parameter t ∈ [0, 1]
|
|
2441
|
+
|
|
2442
|
+
```ts
|
|
2443
|
+
curvatureAt(t: number): Vec3
|
|
2444
|
+
```
|
|
2445
|
+
|
|
2446
|
+
#### `sample()` — Sample the curve as a polyline of evenly-spaced parameter values.
|
|
2447
|
+
|
|
2448
|
+
```ts
|
|
2449
|
+
sample(count?: number): Vec3[]
|
|
2450
|
+
```
|
|
2451
|
+
|
|
2452
|
+
#### `length()` — Approximate arc length by sampling.
|
|
2453
|
+
|
|
2454
|
+
```ts
|
|
2455
|
+
length(samples?: number): number
|
|
2456
|
+
```
|
|
2457
|
+
|
|
2458
|
+
#### `sampleAdaptive()` — Sample with adaptive density — more points where curvature is higher. Returns at least `minCount` points, up to `maxCount`.
|
|
2459
|
+
|
|
2460
|
+
```ts
|
|
2461
|
+
sampleAdaptive(minCount?: number, maxCount?: number): Vec3[]
|
|
2462
|
+
```
|
|
2463
|
+
|
|
2464
|
+
#### `toPolyline()` — Convert to a format compatible with sweep() path input.
|
|
2465
|
+
|
|
2466
|
+
```ts
|
|
2467
|
+
toPolyline(samples?: number): Vec3[]
|
|
2468
|
+
```
|
|
2469
|
+
|
|
2470
|
+
### `QuinticHermiteCurve3D`
|
|
2471
|
+
|
|
2472
|
+
**Properties:**
|
|
2473
|
+
|
|
2474
|
+
| Property | Type | Description |
|
|
2475
|
+
|----------|------|-------------|
|
|
2476
|
+
| `p0` | `Vec3` | Start position |
|
|
2477
|
+
| `p1` | `Vec3` | End position |
|
|
2478
|
+
| `t0` | `Vec3` | Scaled tangent at start (direction * weight * chordLength) |
|
|
2479
|
+
| `t1` | `Vec3` | Scaled tangent at end (direction * weight * chordLength) |
|
|
2480
|
+
| `c0` | `Vec3` | Scaled second derivative at start (curvature * weight² * chordLength²) |
|
|
2481
|
+
| `c1` | `Vec3` | Scaled second derivative at end (curvature * weight² * chordLength²) |
|
|
2482
|
+
| `chordLength` | `number` | Chord length (straight-line distance between endpoints) |
|
|
2483
|
+
|
|
2484
|
+
**Methods:**
|
|
2485
|
+
|
|
2486
|
+
#### `pointAt()` — Evaluate position at parameter t ∈ [0, 1]
|
|
2487
|
+
|
|
2488
|
+
```ts
|
|
2489
|
+
pointAt(t: number): Vec3
|
|
2490
|
+
```
|
|
2491
|
+
|
|
2492
|
+
#### `tangentAt()` — Evaluate tangent (first derivative, normalized) at parameter t ∈ [0, 1]
|
|
2493
|
+
|
|
2494
|
+
```ts
|
|
2495
|
+
tangentAt(t: number): Vec3
|
|
2496
|
+
```
|
|
2497
|
+
|
|
2498
|
+
#### `curvatureAt()` — Evaluate curvature vector (second derivative) at parameter t ∈ [0, 1]
|
|
2499
|
+
|
|
2500
|
+
```ts
|
|
2501
|
+
curvatureAt(t: number): Vec3
|
|
2502
|
+
```
|
|
2503
|
+
|
|
2504
|
+
#### `sample()` — Sample the curve as a polyline of evenly-spaced parameter values.
|
|
2505
|
+
|
|
2506
|
+
```ts
|
|
2507
|
+
sample(count?: number): Vec3[]
|
|
2508
|
+
```
|
|
2509
|
+
|
|
2510
|
+
#### `length()` — Approximate arc length by sampling.
|
|
2511
|
+
|
|
2512
|
+
```ts
|
|
2513
|
+
length(samples?: number): number
|
|
2514
|
+
```
|
|
2515
|
+
|
|
2516
|
+
#### `sampleAdaptive()` — Sample with adaptive density — more points where curvature is higher. Returns at least `minCount` points, up to `maxCount`.
|
|
2517
|
+
|
|
2518
|
+
```ts
|
|
2519
|
+
sampleAdaptive(minCount?: number, maxCount?: number): Vec3[]
|
|
2520
|
+
```
|
|
2521
|
+
|
|
2522
|
+
#### `toPolyline()` — Convert to a format compatible with sweep() path input.
|
|
2523
|
+
|
|
2524
|
+
```ts
|
|
2525
|
+
toPolyline(samples?: number): Vec3[]
|
|
2526
|
+
```
|
|
2527
|
+
|
|
2148
2528
|
### `ShapeRef`
|
|
2149
2529
|
|
|
2150
2530
|
A first-class reference path over a shape's semantic faces and face relationships.
|
|
@@ -2337,39 +2717,43 @@ Namespaced file import helpers for formats that should not add new lowercase glo
|
|
|
2337
2717
|
|
|
2338
2718
|
ForgeCAD uses a **Z-up** right-handed coordinate system.
|
|
2339
2719
|
|
|
2720
|
+
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]`.
|
|
2721
|
+
|
|
2340
2722
|
## Axes
|
|
2341
2723
|
|
|
2342
2724
|
| Axis | Direction | Positive |
|
|
2343
2725
|
|------|-----------------|----------|
|
|
2344
2726
|
| X | Left / Right | Right |
|
|
2345
|
-
| Y |
|
|
2727
|
+
| Y | Front / Back | Back |
|
|
2346
2728
|
| Z | Up / Down | Up |
|
|
2347
2729
|
|
|
2348
2730
|
## Standard Views
|
|
2349
2731
|
|
|
2732
|
+
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.
|
|
2733
|
+
|
|
2350
2734
|
| View | Camera position direction | Sees plane |
|
|
2351
|
-
|
|
2352
|
-
| Front |
|
|
2353
|
-
| Back | +Y
|
|
2735
|
+
|--------|---------------------------|------------|
|
|
2736
|
+
| Front | -Y | XZ |
|
|
2737
|
+
| Back | +Y | XZ |
|
|
2354
2738
|
| Right | +X | YZ |
|
|
2355
|
-
| Left |
|
|
2739
|
+
| Left | -X | YZ |
|
|
2356
2740
|
| Top | +Z | XY |
|
|
2357
|
-
| Bottom |
|
|
2741
|
+
| Bottom | -Z | XY |
|
|
2358
2742
|
|
|
2359
2743
|
## GizmoViewcube Face Mapping
|
|
2360
2744
|
|
|
2361
|
-
|
|
2745
|
+
Renderer/view-cube internals may have their own material ordering. Map any view-cube labels to the ForgeCAD directions below:
|
|
2362
2746
|
|
|
2363
|
-
|
|
|
2364
|
-
|
|
2365
|
-
|
|
|
2366
|
-
|
|
|
2367
|
-
|
|
|
2368
|
-
|
|
|
2369
|
-
|
|
|
2370
|
-
|
|
|
2747
|
+
| Direction | ForgeCAD label |
|
|
2748
|
+
|-----------|----------------|
|
|
2749
|
+
| +X | Right |
|
|
2750
|
+
| -X | Left |
|
|
2751
|
+
| +Y | Back |
|
|
2752
|
+
| -Y | Front |
|
|
2753
|
+
| +Z | Top |
|
|
2754
|
+
| -Z | Bottom |
|
|
2371
2755
|
|
|
2372
|
-
|
|
2756
|
+
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
2757
|
|
|
2374
2758
|
## Grid
|
|
2375
2759
|
|
|
@@ -2397,7 +2781,7 @@ Three.js is Y-up; ForgeCAD is Z-up. Fix applied at camera level (`camera.up = (0
|
|
|
2397
2781
|
|
|
2398
2782
|
## Revolution Axis
|
|
2399
2783
|
|
|
2400
|
-
`
|
|
2784
|
+
`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
2785
|
|
|
2402
2786
|
## Boolean Winding (3D)
|
|
2403
2787
|
|
|
@@ -2423,7 +2807,7 @@ Prefer `composeChain(...)` over manual `.mul(...).mul(...)` in kinematics code t
|
|
|
2423
2807
|
|---|---|---|---|
|
|
2424
2808
|
| Winding | Any point order | CCW | `polygon()`, `path().close()` |
|
|
2425
2809
|
| Up axis | Z-up | Y-up (Three.js) | `camera.up`, gizmo labels |
|
|
2426
|
-
| Revolution | "revolve this profile" | Profile
|
|
2810
|
+
| Revolution | "revolve this profile" | Profile X = radius, Profile Y = world Z height | Documented and regression-tested |
|
|
2427
2811
|
| Face normals | Doesn't think about it | Outward-pointing | Manifold constructors |
|
|
2428
2812
|
| Transform order | Left-to-right chain | Post-multiply | Native match |
|
|
2429
2813
|
|
|
@@ -2439,6 +2823,16 @@ For any fixed assembly where parts are meant to stay in contact in the final mod
|
|
|
2439
2823
|
|
|
2440
2824
|
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
2825
|
|
|
2826
|
+
## Mechanisms: connector frames vs link points
|
|
2827
|
+
|
|
2828
|
+
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.
|
|
2829
|
+
|
|
2830
|
+
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.
|
|
2831
|
+
|
|
2832
|
+
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.
|
|
2833
|
+
|
|
2834
|
+
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.
|
|
2835
|
+
|
|
2442
2836
|
## Primitive origin convention
|
|
2443
2837
|
|
|
2444
2838
|
All 3D primitives are **centered on XY, base at Z=0**:
|
|
@@ -2462,14 +2856,14 @@ Most positioning bugs come from manual coordinate arithmetic. Use these methods
|
|
|
2462
2856
|
|
|
2463
2857
|
## 1. Connectors + `matchTo()` — default for mating interfaces
|
|
2464
2858
|
|
|
2465
|
-
Define connectors on parts; `matchTo()` provides automatic
|
|
2859
|
+
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
2860
|
|
|
2467
2861
|
```javascript
|
|
2468
2862
|
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] }),
|
|
2863
|
+
left_tab: connector.male("dovetail", { origin: [-100, 0, 0], axis: [-1, 0, 0], up: [0, 0, 1] }),
|
|
2470
2864
|
});
|
|
2471
2865
|
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] }),
|
|
2866
|
+
shelf_0: connector.female("dovetail", { origin: [6, 0, -50], axis: [1, 0, 0], up: [0, 0, 1] }),
|
|
2473
2867
|
});
|
|
2474
2868
|
const placed = shelf.matchTo(panel, "left_tab", "shelf_0");
|
|
2475
2869
|
// Dictionary form for multiple pairs on same target:
|
|
@@ -3089,7 +3483,6 @@ addRegularPolygon(sk: ConstrainedSketchBuilder, options: RegularPolygonOptions):
|
|
|
3089
3483
|
| `startAngle?` | `number` | Angle (in degrees) of vertex[0] measured from the +X axis (CCW positive). Default: 0 (rightmost vertex). |
|
|
3090
3484
|
| `blockRotation?` | `boolean` | Prevent 180° rotation (ensures first edge maintains its initial direction). Default: false. |
|
|
3091
3485
|
|
|
3092
|
-
|
|
3093
3486
|
**`ConstrainedRegularPolygon`** extends ConstrainedPolygon
|
|
3094
3487
|
- `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
3488
|
|
|
@@ -3329,7 +3722,7 @@ region(seed: [ number, number ]): Sketch
|
|
|
3329
3722
|
extrude(height: number, opts?: { twist?: number; divisions?: number; scaleTop?: number | [ number, number ]; }): Shape
|
|
3330
3723
|
```
|
|
3331
3724
|
|
|
3332
|
-
#### `revolve()` — Revolve this 2D sketch around the
|
|
3725
|
+
#### `revolve()` — Revolve this 2D sketch around the world Z axis. Sketch X is radius; sketch Y becomes world Z height.
|
|
3333
3726
|
|
|
3334
3727
|
```ts
|
|
3335
3728
|
revolve(degrees?: number, segments?: number): Shape
|
|
@@ -3360,9 +3753,25 @@ Use this when a 2D profile should be oriented onto a 3D face before extrusion or
|
|
|
3360
3753
|
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
3754
|
```
|
|
3362
3755
|
|
|
3363
|
-
|
|
3756
|
+
**`FaceRef`**
|
|
3364
3757
|
|
|
3365
|
-
|
|
3758
|
+
| Option | Type | Description |
|
|
3759
|
+
|--------|------|-------------|
|
|
3760
|
+
| `normal` | `[ number, number, number ]` | Normal direction of the face |
|
|
3761
|
+
| `center` | `[ number, number, number ]` | Center point of the face |
|
|
3762
|
+
| `query?` | `FaceQueryRef` | Compiler-owned face query when available. |
|
|
3763
|
+
| `planar?` | `boolean` | True when the face can host a 2D sketch placement frame |
|
|
3764
|
+
| `uAxis?` | `[ number, number, number ]` | Face-local horizontal axis for planar faces |
|
|
3765
|
+
| `vAxis?` | `[ number, number, number ]` | Face-local vertical axis for planar faces |
|
|
3766
|
+
| `surface?` | `FaceSurface` | Analytic surface family when the backend can identify one. |
|
|
3767
|
+
| `descendant?` | `FaceDescendantMetadata` | Shared descendant-resolution metadata when this face is a semantic region/set. |
|
|
3768
|
+
| `name` | | — |
|
|
3769
|
+
|
|
3770
|
+
**`FaceDescendantMetadata`**: `kind: "single" | "face-set"`, `semantic: FaceDescendantSemantic`, `memberCount: number`, `memberNames: string[]`, `coplanar: boolean`
|
|
3771
|
+
|
|
3772
|
+
**Labels**
|
|
3773
|
+
|
|
3774
|
+
#### `labelEdge()` — Label the single boundary edge (for circles, single-loop profiles). Returns a new sketch.
|
|
3366
3775
|
|
|
3367
3776
|
```ts
|
|
3368
3777
|
labelEdge(name: string): Sketch
|
|
@@ -3658,6 +4067,16 @@ regularPolygon(options: RegularPolygonOptions): ConstrainedRegularPolygon
|
|
|
3658
4067
|
groupRect(options: GroupRectOptions): ConstrainedGroupRect
|
|
3659
4068
|
```
|
|
3660
4069
|
|
|
4070
|
+
**`GroupRectOptions`**
|
|
4071
|
+
|
|
4072
|
+
| Option | Type | Description |
|
|
4073
|
+
|--------|------|-------------|
|
|
4074
|
+
| `x?` | `number` | Bottom-left x coordinate (world). Default: 0. |
|
|
4075
|
+
| `y?` | `number` | Bottom-left y coordinate (world). Default: 0. |
|
|
4076
|
+
| `width` | `number` | Width (along x in local coords). Required. |
|
|
4077
|
+
| `height` | `number` | Height (along y in local coords). Required. |
|
|
4078
|
+
| `allowRotation?` | `boolean` | Allow the solver to rotate this rectangle. Default: false. |
|
|
4079
|
+
|
|
3661
4080
|
**Geometric Constraints**
|
|
3662
4081
|
|
|
3663
4082
|
#### `horizontal()` — Constrain a line to be horizontal (parallel to the X axis).
|
|
@@ -4001,6 +4420,23 @@ result.withUpdatedConstraint('cst-5', 120); // update a dimension without rebuil
|
|
|
4001
4420
|
solve(options?: SolveOptions): ConstraintSketch | Sketch
|
|
4002
4421
|
```
|
|
4003
4422
|
|
|
4423
|
+
**`SolveOptions`**
|
|
4424
|
+
|
|
4425
|
+
| Option | Type | Description |
|
|
4426
|
+
|--------|------|-------------|
|
|
4427
|
+
| `iterations?` | `number` | Maximum number of LM outer iterations per restart. |
|
|
4428
|
+
| `tolerance?` | `number` | Infinity-norm residual tolerance for declaring convergence. |
|
|
4429
|
+
| `restarts?` | `number` | Number of deterministic restart seeds used by the global solver. |
|
|
4430
|
+
| `warmStartIterations?` | `number` | Optional projector iterations used only for initialisation, not as the main solver. |
|
|
4431
|
+
| `maxScaledStep?` | `number` | Maximum LM step length in scaled variable space. Larger = bolder, smaller = safer. |
|
|
4432
|
+
| `skipRedundancyCheck?` | `boolean` | Skip redundancy detection (safe when topology is unchanged and previous DOF >= 0). |
|
|
4433
|
+
| `presolveConstraintId?` | `string` | Run the targeted presolve hook for this constraint before the main solve. |
|
|
4434
|
+
| `fallbackRestarts?` | `number` | When set and the first solve exceeds tolerance*5, retry with this many restarts. |
|
|
4435
|
+
| `progressive?` | `boolean` | Add constraints progressively with short LM solves, all in one WASM call. |
|
|
4436
|
+
| `timeBudgetMs?` | `number` | Wall-clock time budget in ms for the entire solve. 0 = no limit. |
|
|
4437
|
+
| `debugConstructiveTranscript?` | `boolean` | Capture a readable constructive transcript in `constraintMeta.debug`. |
|
|
4438
|
+
| `debugSvgSnapshots?` | `boolean` | Capture SVG snapshots for constructive steps in `constraintMeta.debug`. |
|
|
4439
|
+
|
|
4004
4440
|
#### `solveConstraintsOnly()` — Run the solver without building a full `ConstraintSketch`.
|
|
4005
4441
|
|
|
4006
4442
|
Lighter than `solve()` — skips profile and DOF analysis. Useful for lightweight constraint validation or progress monitoring mid-construction.
|
|
@@ -4409,15 +4845,13 @@ Smooth curves, lofted surfaces, swept solids, splines, and high-level product sk
|
|
|
4409
4845
|
|
|
4410
4846
|
## Contents
|
|
4411
4847
|
|
|
4412
|
-
- [Curves & Surfacing](#curves-surfacing) — `
|
|
4848
|
+
- [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
4849
|
- [Surface Members](#surface-members) — `surfaceBand`, `SurfaceBody`
|
|
4414
4850
|
- [Curve3D](#curve3d)
|
|
4415
|
-
- [
|
|
4851
|
+
- [Route3D](#route3d)
|
|
4416
4852
|
- [NurbsCurve3D](#nurbscurve3d)
|
|
4417
4853
|
- [NurbsSurface](#nurbssurface)
|
|
4418
4854
|
- [PathBuilder](#pathbuilder) — Line Segments, Arcs, Curves, Closing & Output
|
|
4419
|
-
- [HermiteCurve3D](#hermitecurve3d)
|
|
4420
|
-
- [QuinticHermiteCurve3D](#quintichermitecurve3d)
|
|
4421
4855
|
- [ProductSkin](#productskin)
|
|
4422
4856
|
- [ProductSurfaceRef](#productsurfaceref)
|
|
4423
4857
|
- [ProductSurfaceBuilder](#productsurfacebuilder)
|
|
@@ -4439,6 +4873,7 @@ Smooth curves, lofted surfaces, swept solids, splines, and high-level product sk
|
|
|
4439
4873
|
- [SurfaceJoinBuilder](#surfacejoinbuilder)
|
|
4440
4874
|
- [CounterboreBuilder](#counterborebuilder)
|
|
4441
4875
|
- [RoundedSlotBuilder](#roundedslotbuilder)
|
|
4876
|
+
- [Curve](#curve)
|
|
4442
4877
|
- [Surface](#surface)
|
|
4443
4878
|
- [Blend](#blend)
|
|
4444
4879
|
- [Analysis](#analysis)
|
|
@@ -4448,97 +4883,186 @@ Smooth curves, lofted surfaces, swept solids, splines, and high-level product sk
|
|
|
4448
4883
|
- [Slot](#slot)
|
|
4449
4884
|
- [Counterbore](#counterbore)
|
|
4450
4885
|
- [Ribs](#ribs)
|
|
4451
|
-
- [Helix](#helix)
|
|
4452
4886
|
|
|
4453
4887
|
## Functions
|
|
4454
4888
|
|
|
4455
4889
|
### Curves & Surfacing
|
|
4456
4890
|
|
|
4457
|
-
#### `
|
|
4891
|
+
#### `Curve.Blend()` — Create an exact G1 blend curve between two directed endpoints.
|
|
4892
|
+
|
|
4893
|
+
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.
|
|
4894
|
+
|
|
4895
|
+
```js
|
|
4896
|
+
const rail = Curve.Blend(
|
|
4897
|
+
{ point: [0, 0, 0], tangent: [1, 0, 0], weight: 0.8 },
|
|
4898
|
+
{ point: [40, 20, 8], tangent: [0, 1, 0], weight: 0.8 },
|
|
4899
|
+
);
|
|
4900
|
+
const tube = sweep(circle2d(2), rail);
|
|
4901
|
+
```
|
|
4458
4902
|
|
|
4459
4903
|
```ts
|
|
4460
|
-
|
|
4904
|
+
Curve.Blend(start: CurveBlendEndpoint, end: CurveBlendEndpoint): NurbsCurve3D
|
|
4461
4905
|
```
|
|
4462
4906
|
|
|
4463
|
-
|
|
4907
|
+
**`CurveBlendEndpoint`**
|
|
4908
|
+
- `point: Vec3` — Endpoint position.
|
|
4909
|
+
- `tangent: Vec3` — Tangent direction at this endpoint. Magnitude is ignored.
|
|
4910
|
+
- `weight?: number` — Tangent reach relative to the endpoint chord length. Default 1.
|
|
4464
4911
|
|
|
4465
|
-
#### `
|
|
4912
|
+
#### `Curve.BlendG2()` — Create an exact G2 blend curve between two directed endpoints.
|
|
4913
|
+
|
|
4914
|
+
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.
|
|
4915
|
+
|
|
4916
|
+
```js
|
|
4917
|
+
const rail = Curve.BlendG2(
|
|
4918
|
+
{ point: [0, 0, 0], tangent: [1, 0, 0], curvature: [0, 0.02, 0] },
|
|
4919
|
+
{ point: [50, 20, 0], tangent: [0, 1, 0], curvature: [-0.02, 0, 0] },
|
|
4920
|
+
);
|
|
4921
|
+
```
|
|
4466
4922
|
|
|
4467
4923
|
```ts
|
|
4468
|
-
|
|
4924
|
+
Curve.BlendG2(start: CurveBlendG2Endpoint, end: CurveBlendG2Endpoint): NurbsCurve3D
|
|
4469
4925
|
```
|
|
4470
4926
|
|
|
4471
|
-
|
|
4927
|
+
**`CurveBlendG2Endpoint`** extends CurveBlendEndpoint
|
|
4928
|
+
- `curvature?: Vec3` — Optional endpoint curvature/second-derivative vector. Default is zero.
|
|
4472
4929
|
|
|
4473
|
-
#### `
|
|
4930
|
+
#### `Curve.Arc()` — Create an exact circular 3D arc from start, end, and start tangent.
|
|
4931
|
+
|
|
4932
|
+
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.
|
|
4933
|
+
|
|
4934
|
+
```js
|
|
4935
|
+
const rail = Curve.Arc({
|
|
4936
|
+
start: [40, 0, 0],
|
|
4937
|
+
end: [0, 40, 0],
|
|
4938
|
+
tangent: [0, 1, 0],
|
|
4939
|
+
});
|
|
4940
|
+
const tube = sweep(circle2d(2), rail);
|
|
4941
|
+
```
|
|
4474
4942
|
|
|
4475
4943
|
```ts
|
|
4476
|
-
|
|
4944
|
+
Curve.Arc(options: CurveArcOptions): NurbsCurve3D
|
|
4477
4945
|
```
|
|
4478
4946
|
|
|
4479
|
-
|
|
4947
|
+
**`CurveArcOptions`**
|
|
4948
|
+
- `start: Vec3` — Arc start point.
|
|
4949
|
+
- `end: Vec3` — Arc end point.
|
|
4950
|
+
- `tangent: Vec3` — Tangent direction at the start point. Magnitude is ignored.
|
|
4951
|
+
|
|
4952
|
+
#### `Curve.Line()` — Create an exact straight 3D NURBS line segment.
|
|
4953
|
+
|
|
4954
|
+
```js
|
|
4955
|
+
const rail = Curve.Line([0, 0, 0], [80, 0, 15]);
|
|
4956
|
+
const rib = sweep(circle2d(2), rail);
|
|
4957
|
+
```
|
|
4480
4958
|
|
|
4481
4959
|
```ts
|
|
4482
|
-
|
|
4960
|
+
Curve.Line(start: Vec3, end: Vec3): NurbsCurve3D
|
|
4483
4961
|
```
|
|
4484
4962
|
|
|
4485
|
-
#### `
|
|
4963
|
+
#### `Curve.Polyline()` — Create a polyline path as cloned 3D points.
|
|
4964
|
+
|
|
4965
|
+
Polylines are exact as route/path input to `sweep`. Use `Curve.Route` when the centerline needs bend and endpoint-frame metadata.
|
|
4486
4966
|
|
|
4487
4967
|
```ts
|
|
4488
|
-
|
|
4968
|
+
Curve.Polyline(points: Vec3[]): Vec3[]
|
|
4489
4969
|
```
|
|
4490
4970
|
|
|
4491
|
-
#### `
|
|
4971
|
+
#### `Curve.Spline()` — Create a smooth Catmull-Rom spline path.
|
|
4972
|
+
|
|
4973
|
+
This is a smooth sampled curve object. Use `Curve.Nurbs` when the path must preserve exact control-point and knot data.
|
|
4492
4974
|
|
|
4493
4975
|
```ts
|
|
4494
|
-
|
|
4976
|
+
Curve.Spline(points: Vec3[], options?: Spline3DOptions): Curve3D
|
|
4495
4977
|
```
|
|
4496
4978
|
|
|
4497
|
-
|
|
4979
|
+
**`Spline3DOptions`**
|
|
4980
|
+
- `closed?: boolean` — Closed loop (default false).
|
|
4981
|
+
- `tension?: number` — Catmull-Rom tension in [0, 1]. 0 = very round, 1 = linear-ish. Default 0.5.
|
|
4498
4982
|
|
|
4499
|
-
|
|
4983
|
+
#### `Curve.Nurbs()` — Create an exact NURBS 3D curve from control points, weights, knots, and degree.
|
|
4984
|
+
|
|
4985
|
+
```js
|
|
4986
|
+
const rail = Curve.Nurbs([[0, 0, 0], [30, 4, 12], [60, -4, 12], [90, 0, 0]]);
|
|
4987
|
+
const tube = sweep(circle2d(2), rail);
|
|
4988
|
+
```
|
|
4500
4989
|
|
|
4501
4990
|
```ts
|
|
4502
|
-
|
|
4991
|
+
Curve.Nurbs(points: Vec3[], options?: NurbsCurve3DOptions): NurbsCurve3D
|
|
4503
4992
|
```
|
|
4504
4993
|
|
|
4505
|
-
|
|
4994
|
+
**`NurbsCurve3DOptions`**
|
|
4506
4995
|
|
|
4507
|
-
|
|
4996
|
+
| Option | Type | Description |
|
|
4997
|
+
|--------|------|-------------|
|
|
4998
|
+
| `degree?` | `number` | Polynomial degree (default 3 = cubic). Must be ≥ 1. |
|
|
4999
|
+
| `weights?` | `number[]` | Rational weights, one per control point (default: all 1.0 = non-rational). |
|
|
5000
|
+
| `knots?` | `number[]` | Knot vector (default: uniform clamped). Must have length = controlPoints.length + degree + 1. |
|
|
5001
|
+
| `closed?` | `boolean` | Whether the curve is closed/periodic (default false). |
|
|
5002
|
+
|
|
5003
|
+
#### `Curve.Fit()` — Fit a non-rational NURBS curve that interpolates every input point.
|
|
5004
|
+
|
|
5005
|
+
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`.
|
|
5006
|
+
|
|
5007
|
+
```js
|
|
5008
|
+
const rail = Curve.Fit(
|
|
5009
|
+
[[0, 0, 0], [20, 8, 12], [50, -4, 18], [80, 0, 0]],
|
|
5010
|
+
{ degree: 3, tolerance: 0.001 },
|
|
5011
|
+
);
|
|
5012
|
+
const tube = sweep(circle2d(2), rail);
|
|
5013
|
+
```
|
|
4508
5014
|
|
|
4509
5015
|
```ts
|
|
4510
|
-
|
|
5016
|
+
Curve.Fit(points: Vec3[], options?: CurveFitOptions): NurbsCurve3D
|
|
4511
5017
|
```
|
|
4512
5018
|
|
|
4513
|
-
|
|
5019
|
+
**`CurveFitOptions`**
|
|
5020
|
+
- `degree?: number` — Polynomial degree. Default is cubic, reduced automatically for short point lists.
|
|
5021
|
+
- `tolerance?: number` — Maximum allowed interpolation residual in model units. Default 1e-7.
|
|
4514
5022
|
|
|
4515
|
-
|
|
5023
|
+
#### `Curve.Trim()` — Extract an exact curve segment from normalized parameter `start` to `end`.
|
|
5024
|
+
|
|
5025
|
+
`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
5026
|
|
|
4517
5027
|
```ts
|
|
4518
|
-
|
|
5028
|
+
Curve.Trim<T extends CurveTrimInput>(curve: T, start: number, end: number): CurveTrimOutput<T>
|
|
4519
5029
|
```
|
|
4520
5030
|
|
|
4521
|
-
#### `
|
|
5031
|
+
#### `Curve.Reverse()` — Reverse an exact curve without changing its geometry.
|
|
4522
5032
|
|
|
4523
|
-
|
|
5033
|
+
`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
5034
|
|
|
4525
5035
|
```ts
|
|
4526
|
-
|
|
5036
|
+
Curve.Reverse<T extends CurveTrimInput>(curve: T): CurveTrimOutput<T>
|
|
4527
5037
|
```
|
|
4528
5038
|
|
|
4529
|
-
|
|
4530
|
-
- `edgeLength?: number` — Marching-grid edge length for level-set meshing. Smaller = finer.
|
|
4531
|
-
- `boundsPadding?: number` — Optional extra bounds padding.
|
|
5039
|
+
#### `Curve.Route()` — Build analytic 3D line/arc routes for sweeps.
|
|
4532
5040
|
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
5041
|
+
`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.
|
|
5042
|
+
|
|
5043
|
+
```js
|
|
5044
|
+
const route = Curve.Route.fromPolyline(
|
|
5045
|
+
[[0, 0, 0], [0, 0, 50], [40, 0, 50]],
|
|
5046
|
+
{ cornerRadius: 12, startPort: 'inlet', endPort: 'outlet' },
|
|
5047
|
+
);
|
|
5048
|
+
const tube = sweep(circle2d(4), route);
|
|
5049
|
+
```
|
|
5050
|
+
|
|
5051
|
+
```ts
|
|
5052
|
+
Curve.Route: typeof Route3D
|
|
5053
|
+
```
|
|
4537
5054
|
|
|
4538
|
-
#### `Helix
|
|
5055
|
+
#### `Curve.Helix()` — Build helical paths and swept coils.
|
|
5056
|
+
|
|
5057
|
+
`Curve.Helix` is the canonical namespace for helical paths and coils. It uses the same sweep-based lowering as other curve paths.
|
|
5058
|
+
|
|
5059
|
+
```js
|
|
5060
|
+
const guide = Curve.Helix.path({ radius: 20, pitch: 6, turns: 4 });
|
|
5061
|
+
const spring = Curve.Helix.coil({ radius: 20, pitch: 6, turns: 4, wireRadius: 1 });
|
|
5062
|
+
```
|
|
4539
5063
|
|
|
4540
5064
|
```ts
|
|
4541
|
-
Helix
|
|
5065
|
+
Curve.Helix: { path(options: HelixOptions): CurveHelixPath; coil: CurveHelixCoil; }
|
|
4542
5066
|
```
|
|
4543
5067
|
|
|
4544
5068
|
**`HelixOptions`**
|
|
@@ -4553,66 +5077,88 @@ Helix.path(options: HelixOptions): HelixCurve
|
|
|
4553
5077
|
| `clockwise?` | `boolean` | Reverse winding direction when viewed from +Z. |
|
|
4554
5078
|
| `samplesPerTurn?` | `number` | Point samples per turn for the metadata path. Default 32. |
|
|
4555
5079
|
|
|
4556
|
-
|
|
5080
|
+
`CurveHelixPath`: `{ radius: number, pitch: number, turns: number, height: number, startAngle: number, clockwise: boolean }`
|
|
4557
5081
|
|
|
4558
|
-
|
|
5082
|
+
#### `Loft.station()` — Create a loft station from a 2D profile and an axis position.
|
|
5083
|
+
|
|
5084
|
+
```ts
|
|
5085
|
+
Loft.station(profile: Sketch, position: number): LoftStation
|
|
5086
|
+
```
|
|
4559
5087
|
|
|
4560
|
-
|
|
4561
|
-
- `Helix.coil(profile: Sketch, options: HelixCoilOptions): Shape`
|
|
5088
|
+
`LoftStation`: `{ profile: Sketch, position: number }`
|
|
4562
5089
|
|
|
5090
|
+
#### `Loft.leftRail()` — Create a guide rail that constrains the section-local negative-X side.
|
|
4563
5091
|
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
4567
|
-
- `divisionsPerTurn?: number` — Sweep path samples per turn. Default 32.
|
|
5092
|
+
```ts
|
|
5093
|
+
Loft.leftRail(path: LoftGuideRailPath): LoftGuideRail
|
|
5094
|
+
```
|
|
4568
5095
|
|
|
4569
|
-
|
|
5096
|
+
`LoftGuideRail`: `{ side: LoftGuideRailSide, path: LoftGuideRailPath }`
|
|
4570
5097
|
|
|
4571
|
-
|
|
5098
|
+
#### `Loft.rightRail()` — Create a guide rail that constrains the section-local positive-X side.
|
|
4572
5099
|
|
|
4573
5100
|
```ts
|
|
4574
|
-
|
|
5101
|
+
Loft.rightRail(path: LoftGuideRailPath): LoftGuideRail
|
|
4575
5102
|
```
|
|
4576
5103
|
|
|
4577
|
-
|
|
5104
|
+
#### `Loft.frontRail()` — Create a guide rail that constrains the section-local positive-Y side.
|
|
4578
5105
|
|
|
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. |
|
|
5106
|
+
```ts
|
|
5107
|
+
Loft.frontRail(path: LoftGuideRailPath): LoftGuideRail
|
|
5108
|
+
```
|
|
4585
5109
|
|
|
4586
|
-
#### `
|
|
5110
|
+
#### `Loft.backRail()` — Create a guide rail that constrains the section-local negative-Y side.
|
|
5111
|
+
|
|
5112
|
+
```ts
|
|
5113
|
+
Loft.backRail(path: LoftGuideRailPath): LoftGuideRail
|
|
5114
|
+
```
|
|
4587
5115
|
|
|
4588
|
-
|
|
5116
|
+
#### `Loft.centerRail()` — Create a guide rail that moves section centers along the loft.
|
|
4589
5117
|
|
|
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);
|
|
5118
|
+
```ts
|
|
5119
|
+
Loft.centerRail(path: LoftGuideRailPath): LoftGuideRail
|
|
4594
5120
|
```
|
|
4595
5121
|
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
)
|
|
5122
|
+
#### `Loft.pathOnXz()` — Place a 2D guide path onto the XZ plane.
|
|
5123
|
+
|
|
5124
|
+
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).
|
|
5125
|
+
|
|
5126
|
+
```ts
|
|
5127
|
+
Loft.pathOnXz(path: LoftPath2D, y?: number): Vec3[]
|
|
4602
5128
|
```
|
|
4603
5129
|
|
|
5130
|
+
#### `Loft.pathOnYz()` — Place a 2D guide path onto the YZ plane.
|
|
5131
|
+
|
|
5132
|
+
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).
|
|
5133
|
+
|
|
4604
5134
|
```ts
|
|
4605
|
-
|
|
5135
|
+
Loft.pathOnYz(path: LoftPath2D, x?: number): Vec3[]
|
|
4606
5136
|
```
|
|
4607
5137
|
|
|
4608
|
-
|
|
5138
|
+
#### `Loft.pathOnXy()` — Place a 2D guide path onto the XY plane.
|
|
4609
5139
|
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
5140
|
+
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.
|
|
5141
|
+
|
|
5142
|
+
```ts
|
|
5143
|
+
Loft.pathOnXy(path: LoftPath2D, z?: number): Vec3[]
|
|
5144
|
+
```
|
|
5145
|
+
|
|
5146
|
+
#### `Loft.withGuideRails()` — Loft through profile stations while forcing generated sections to follow guide rails.
|
|
5147
|
+
|
|
5148
|
+
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.
|
|
5149
|
+
|
|
5150
|
+
```ts
|
|
5151
|
+
Loft.withGuideRails(stations: LoftStation[], rails: LoftGuideRail[], options?: LoftWithGuideRailsOptions): Shape
|
|
5152
|
+
```
|
|
5153
|
+
|
|
5154
|
+
**`LoftOptions`**
|
|
5155
|
+
- `edgeLength?: number` — Marching-grid edge length for level-set meshing. Smaller = finer.
|
|
5156
|
+
- `boundsPadding?: number` — Optional extra bounds padding.
|
|
5157
|
+
|
|
5158
|
+
**`LoftWithGuideRailsOptions`** extends LoftOptions
|
|
5159
|
+
- `axis?: LoftAxis` — Primary station axis. Default Z.
|
|
5160
|
+
- `samples?: number` — Number of generated loft stations including ends. Default scales with station count.
|
|
5161
|
+
- `railSamples?: number` — Number of points sampled from curve-backed rails before axis interpolation. Default 64.
|
|
4616
5162
|
|
|
4617
5163
|
#### `spline2d()` — Build a smooth Catmull-Rom spline sketch from 2D control points.
|
|
4618
5164
|
|
|
@@ -4632,18 +5178,6 @@ spline2d(points: Vec2[], options?: Spline2DOptions): Sketch
|
|
|
4632
5178
|
| `strokeWidth?` | `number` | For open splines, provide stroke width to return a solid Sketch. If omitted for open splines, an error is thrown. |
|
|
4633
5179
|
| `join?` | `"Round" \| "Square"` | Stroke join for open splines. Default 'Round'. |
|
|
4634
5180
|
|
|
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
5181
|
#### `loft()` — Loft between multiple sketches along Z stations.
|
|
4648
5182
|
|
|
4649
5183
|
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 +5188,6 @@ Performance note: loft is significantly heavier than primitive/extrude/revolve.
|
|
|
4654
5188
|
loft(profiles: Sketch[], heights: number[], options?: LoftOptions): Shape
|
|
4655
5189
|
```
|
|
4656
5190
|
|
|
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
5191
|
#### `sweep()`
|
|
4681
5192
|
|
|
4682
5193
|
```ts
|
|
@@ -4800,106 +5311,6 @@ surfacePatch(curves: { ... }, options?: SurfacePatchOptions): Shape
|
|
|
4800
5311
|
- `thickness?: number` — Thickness of the generated solid. Default 0 for an open exact sheet.
|
|
4801
5312
|
- `approximate?: boolean` — Allow explicit approximation for non-exact curve inputs such as Curve3D samples.
|
|
4802
5313
|
|
|
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
5314
|
### Surface Members
|
|
4904
5315
|
|
|
4905
5316
|
#### `surfaceBand()`
|
|
@@ -4979,47 +5390,76 @@ tangentAt(t: number): Vec3
|
|
|
4979
5390
|
length(samples?: number): number
|
|
4980
5391
|
```
|
|
4981
5392
|
|
|
4982
|
-
### `
|
|
5393
|
+
### `Route3D`
|
|
4983
5394
|
|
|
4984
|
-
Metadata-bearing
|
|
5395
|
+
Metadata-bearing analytic 3D route made from line and arc segments.
|
|
4985
5396
|
|
|
4986
|
-
|
|
5397
|
+
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
5398
|
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
5399
|
+
```js
|
|
5400
|
+
const route = Curve.Route.fromPolyline(
|
|
5401
|
+
[[0, 0, 0], [0, 0, 80], [60, 0, 80]],
|
|
5402
|
+
{ cornerRadius: 24, startPort: "inlet", endPort: "outlet" },
|
|
5403
|
+
);
|
|
5404
|
+
const pipe = sweep(difference2d(circle2d(8), circle2d(6)), route);
|
|
5405
|
+
const outlet = route.port("outlet");
|
|
5406
|
+
```
|
|
4996
5407
|
|
|
4997
|
-
|
|
5408
|
+
#### `fromPolyline()` — Build a line/arc route from virtual polyline corner points.
|
|
4998
5409
|
|
|
4999
|
-
|
|
5410
|
+
```ts
|
|
5411
|
+
static fromPolyline(points: Route3DVec3[], options?: Route3DFromPolylineOptions): Route3D
|
|
5412
|
+
```
|
|
5413
|
+
|
|
5414
|
+
**`Route3DFromPolylineOptions`**
|
|
5415
|
+
|
|
5416
|
+
| Option | Type | Description |
|
|
5417
|
+
|--------|------|-------------|
|
|
5418
|
+
| `cornerRadius?` | `number` | Bend radius applied to every virtual interior corner. Default 0 keeps sharp polyline corners. |
|
|
5419
|
+
| `startPort?` | `string` | Name for the start port. Default "start". |
|
|
5420
|
+
| `endPort?` | `string` | Name for the end port. Default "end". |
|
|
5421
|
+
| `up?` | `Vec3` | Preferred up vector for deterministic port frames. Default [0, 0, 1]. |
|
|
5422
|
+
|
|
5423
|
+
#### `length()` — Total centerline length, including line and bend arc segments.
|
|
5000
5424
|
|
|
5001
5425
|
```ts
|
|
5002
|
-
|
|
5426
|
+
get length(): number
|
|
5003
5427
|
```
|
|
5004
5428
|
|
|
5005
|
-
#### `
|
|
5429
|
+
#### `segments()` — Exact line and arc segments that make up this route.
|
|
5006
5430
|
|
|
5007
5431
|
```ts
|
|
5008
|
-
|
|
5432
|
+
get segments(): Route3DSegment[]
|
|
5009
5433
|
```
|
|
5010
5434
|
|
|
5011
|
-
#### `
|
|
5435
|
+
#### `ports()` — Named port frames, keyed by port name.
|
|
5012
5436
|
|
|
5013
5437
|
```ts
|
|
5014
|
-
|
|
5438
|
+
get ports(): Record<string, RoutePortFrame>
|
|
5015
5439
|
```
|
|
5016
5440
|
|
|
5017
|
-
#### `
|
|
5441
|
+
#### `port()` — Return one named route port frame.
|
|
5442
|
+
|
|
5443
|
+
```ts
|
|
5444
|
+
port(name: string): RoutePortFrame
|
|
5445
|
+
```
|
|
5446
|
+
|
|
5447
|
+
#### `toSweepPathPlan()` — Convert this route to the compile plan consumed by sweep().
|
|
5448
|
+
|
|
5449
|
+
```ts
|
|
5450
|
+
toSweepPathPlan(): SweepPathCompilePlan
|
|
5451
|
+
```
|
|
5452
|
+
|
|
5453
|
+
#### `toPolyline()` — Sample this analytic route as a polyline for inspection or backend lowering.
|
|
5018
5454
|
|
|
5019
5455
|
```ts
|
|
5020
|
-
|
|
5456
|
+
toPolyline(options?: number | Route3DToPolylineOptions): Route3DVec3[]
|
|
5021
5457
|
```
|
|
5022
5458
|
|
|
5459
|
+
**`Route3DToPolylineOptions`**
|
|
5460
|
+
- `samples?: number` — Approximate target point count for the full route.
|
|
5461
|
+
- `maxAngleDeg?: number` — Maximum angular spacing on arc segments. Default 6 degrees.
|
|
5462
|
+
|
|
5023
5463
|
### `NurbsCurve3D`
|
|
5024
5464
|
|
|
5025
5465
|
**Properties:**
|
|
@@ -5393,120 +5833,6 @@ toPolyline(): [ number, number ][]
|
|
|
5393
5833
|
closeOffset(delta: number, join?: "Round" | "Square" | "Miter"): Sketch
|
|
5394
5834
|
```
|
|
5395
5835
|
|
|
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
5836
|
### `ProductSkin`
|
|
5511
5837
|
|
|
5512
5838
|
**Properties:**
|
|
@@ -5939,7 +6265,6 @@ attachTo(ref: ProductRefInput, options?: ProductPanelAttachOptions): Shape
|
|
|
5939
6265
|
|
|
5940
6266
|
`ProductSurfaceRef`
|
|
5941
6267
|
|
|
5942
|
-
|
|
5943
6268
|
`ProductPanelAttachOptions`: `{ at?: Partial<ProductSkinRefQuery>, thickness?: number, material?: ProductMaterial, color?: string }`
|
|
5944
6269
|
|
|
5945
6270
|
### `ProductRibbonBuilder`
|
|
@@ -6263,6 +6588,8 @@ bottom(options?: { angle?: number; offset?: number; }): SurfaceAnchor<CylinderSu
|
|
|
6263
6588
|
pointAt(coordinate: CylinderSurfaceCoordinate): Vec3
|
|
6264
6589
|
```
|
|
6265
6590
|
|
|
6591
|
+
`CylinderSurfaceCoordinate`: `{ kind?: "cylinder", angle: number, z: number, offset?: number }`
|
|
6592
|
+
|
|
6266
6593
|
#### `mirrorPoint()`
|
|
6267
6594
|
|
|
6268
6595
|
```ts
|
|
@@ -6382,6 +6709,8 @@ bottom(options?: { x?: number; offset?: number; }): SurfaceAnchor<PlaneSurfaceCo
|
|
|
6382
6709
|
pointAt(coordinate: PlaneSurfaceCoordinate): Vec3
|
|
6383
6710
|
```
|
|
6384
6711
|
|
|
6712
|
+
`PlaneSurfaceCoordinate`: `{ kind?: "plane", x: number, y: number, offset?: number }`
|
|
6713
|
+
|
|
6385
6714
|
#### `mirrorPoint()`
|
|
6386
6715
|
|
|
6387
6716
|
```ts
|
|
@@ -6666,6 +6995,8 @@ boundaries(samples?: number): SurfaceBandBoundarySample[]
|
|
|
6666
6995
|
withHole(name: string, input: SurfaceBandHoleInput): SurfaceBand<C>
|
|
6667
6996
|
```
|
|
6668
6997
|
|
|
6998
|
+
`SurfaceBandHoleInput`: `{ length: number, width: number, along?: number, across?: number }`
|
|
6999
|
+
|
|
6669
7000
|
#### `holes()` — Resolve recorded hole regions into member-local across/along loops.
|
|
6670
7001
|
|
|
6671
7002
|
```ts
|
|
@@ -6688,6 +7019,8 @@ holes(): SurfaceBandHoleRegion[]
|
|
|
6688
7019
|
carrier(carrier: CarrierSurface): this
|
|
6689
7020
|
```
|
|
6690
7021
|
|
|
7022
|
+
`CarrierSurface`: `{ name: string, kind: SurfaceCarrierKind }`
|
|
7023
|
+
|
|
6691
7024
|
#### `member()`
|
|
6692
7025
|
|
|
6693
7026
|
```ts
|
|
@@ -6732,6 +7065,8 @@ band(): this
|
|
|
6732
7065
|
at(anchor: SurfaceAnchor<C>): this
|
|
6733
7066
|
```
|
|
6734
7067
|
|
|
7068
|
+
`SurfaceAnchor`: `{ carrier: CarrierSurface<C>, coordinate: C }`
|
|
7069
|
+
|
|
6735
7070
|
#### `size()`
|
|
6736
7071
|
|
|
6737
7072
|
```ts
|
|
@@ -6750,6 +7085,10 @@ path(path: SurfacePath<C> | SurfacePathBuilder<C>): this
|
|
|
6750
7085
|
section(section: MemberSectionInput): this
|
|
6751
7086
|
```
|
|
6752
7087
|
|
|
7088
|
+
**`MemberSectionInput`**: `width?: number`, `thickness: number`, `edgeRadius?: number`, `direction?: MemberOutwardDirection`, `material?: ProductMaterial`, `stations?: MemberSectionStation[]`
|
|
7089
|
+
|
|
7090
|
+
`MemberSectionStation`: `{ t: number, width?: number, thickness?: number }`
|
|
7091
|
+
|
|
6753
7092
|
#### `cap()`
|
|
6754
7093
|
|
|
6755
7094
|
```ts
|
|
@@ -6762,6 +7101,8 @@ cap(style: SurfaceBandCap): this
|
|
|
6762
7101
|
slot(name: string, feature: MemberFeature | RoundedSlotBuilder): this
|
|
6763
7102
|
```
|
|
6764
7103
|
|
|
7104
|
+
**`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`
|
|
7105
|
+
|
|
6765
7106
|
#### `cutout()`
|
|
6766
7107
|
|
|
6767
7108
|
```ts
|
|
@@ -6886,6 +7227,25 @@ toFeature(name?: string): MemberFeature
|
|
|
6886
7227
|
|
|
6887
7228
|
## Constants
|
|
6888
7229
|
|
|
7230
|
+
### `Curve`
|
|
7231
|
+
|
|
7232
|
+
Canonical exact/smooth 3D curve constructors.
|
|
7233
|
+
|
|
7234
|
+
`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.
|
|
7235
|
+
|
|
7236
|
+
- `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.
|
|
7237
|
+
- `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.
|
|
7238
|
+
- `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.
|
|
7239
|
+
- `Line(start: Vec3, end: Vec3): NurbsCurve3D` — Create an exact straight 3D NURBS line segment.
|
|
7240
|
+
- `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.
|
|
7241
|
+
- `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.
|
|
7242
|
+
- `Nurbs(points: Vec3[], options?: NurbsCurve3DOptions): NurbsCurve3D` — Create an exact NURBS 3D curve from control points, weights, knots, and degree.
|
|
7243
|
+
- `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`.
|
|
7244
|
+
- `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.
|
|
7245
|
+
- `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.
|
|
7246
|
+
- `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.
|
|
7247
|
+
- `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.
|
|
7248
|
+
|
|
6889
7249
|
### `Surface`
|
|
6890
7250
|
|
|
6891
7251
|
- `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 +7328,18 @@ toFeature(name?: string): MemberFeature
|
|
|
6968
7328
|
|
|
6969
7329
|
- `repeated(input: { count: number; height: number; }): MemberFeature` — Create repeated ribs that belong to a surface member before lowering.
|
|
6970
7330
|
|
|
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
7331
|
---
|
|
6986
7332
|
|
|
6987
7333
|
<!-- generated/assembly.md -->
|
|
6988
7334
|
|
|
6989
7335
|
# Assembly API
|
|
6990
7336
|
|
|
6991
|
-
|
|
7337
|
+
Assembly-owned links, constraints, connectors, solved poses, and robot export.
|
|
6992
7338
|
|
|
6993
7339
|
## Contents
|
|
6994
7340
|
|
|
6995
|
-
- [Assembly & Joints](#assembly-joints) — `bomToCsv`, `assembly
|
|
6996
|
-
- [Assembly](#assembly) — Structure, Connectors, References,
|
|
7341
|
+
- [Assembly & Joints](#assembly-joints) — `bomToCsv`, `assembly`
|
|
7342
|
+
- [Assembly](#assembly) — Kinematics, Structure, Connectors, References, Solving
|
|
6997
7343
|
- [ImportedAssembly](#importedassembly)
|
|
6998
7344
|
- [SolvedAssembly](#solvedassembly)
|
|
6999
7345
|
- [MateBuilder](#matebuilder)
|
|
@@ -7019,110 +7365,259 @@ bomToCsv(rows: BomRow[]): string
|
|
|
7019
7365
|
| `tags?` | `string \| readonly string[]` | Viewport organization tags applied to scene objects produced from this part. |
|
|
7020
7366
|
| `material?`, `process?`, `tolerance?`, `qty?`, `notes?`, `densityKgM3?`, `massKg?` | | — |
|
|
7021
7367
|
|
|
7022
|
-
#### `assembly()` — Create an assembly container with named parts and
|
|
7368
|
+
#### `assembly()` — Create an assembly container with named parts, connectors, and kinematic links.
|
|
7369
|
+
|
|
7370
|
+
**Use this from iteration 1 for any model with moving parts.** Do not build one static pose and retrofit motion later.
|
|
7023
7371
|
|
|
7024
|
-
|
|
7372
|
+
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.
|
|
7025
7373
|
|
|
7026
|
-
|
|
7374
|
+
`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.
|
|
7027
7375
|
|
|
7028
|
-
|
|
7376
|
+
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.
|
|
7029
7377
|
|
|
7030
|
-
|
|
7378
|
+
If no link in a connected kinematic component is fixed, ForgeCAD chooses a deterministic gauge link for solving and reports a floating-component warning.
|
|
7379
|
+
|
|
7380
|
+
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
7381
|
|
|
7032
7382
|
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
7383
|
|
|
7384
|
+
**Point-link example**
|
|
7385
|
+
|
|
7386
|
+
This snippet mates a marker to the solved `tip` point. It does not orient a bar along `ground -> tip`.
|
|
7387
|
+
|
|
7034
7388
|
```ts
|
|
7035
|
-
const
|
|
7036
|
-
|
|
7037
|
-
|
|
7389
|
+
const marker = box(8, 8, 4).withConnectors({
|
|
7390
|
+
center: connector({ origin: [0, 0, 0], axis: [0, 0, 1] }),
|
|
7391
|
+
});
|
|
7392
|
+
|
|
7393
|
+
const mech = assembly("Linkage")
|
|
7394
|
+
.link("ground", { at: [0, 0, 0], fixed: true })
|
|
7395
|
+
.link("worldX", { at: [10, 0, 0], fixed: true })
|
|
7396
|
+
.link("tip", { at: [40, 0, 0] })
|
|
7397
|
+
.edgeBetweenLinks("ground", "tip", { name: "bar" })
|
|
7398
|
+
.addAngleBetweenLinks("worldX", "ground", "tip", {
|
|
7399
|
+
name: "theta",
|
|
7400
|
+
control: { min: 0, max: 120, default: 30 },
|
|
7038
7401
|
})
|
|
7039
|
-
.addPart("
|
|
7040
|
-
|
|
7041
|
-
|
|
7042
|
-
|
|
7043
|
-
|
|
7402
|
+
.addPart("Tip marker", marker, { mate: { connector: "center", toLink: "tip" } });
|
|
7403
|
+
|
|
7404
|
+
return mech;
|
|
7405
|
+
```
|
|
7406
|
+
|
|
7407
|
+
```ts
|
|
7408
|
+
assembly(name?: string): Assembly
|
|
7409
|
+
```
|
|
7410
|
+
|
|
7411
|
+
---
|
|
7412
|
+
|
|
7413
|
+
## Classes
|
|
7414
|
+
|
|
7415
|
+
### `Assembly`
|
|
7416
|
+
|
|
7417
|
+
Container for a kinematic mechanism made up of links, relationships, and parts.
|
|
7418
|
+
|
|
7419
|
+
Assembly has two related but different motion tools:
|
|
7420
|
+
|
|
7421
|
+
- **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.
|
|
7422
|
+
- **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.
|
|
7423
|
+
|
|
7424
|
+
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.
|
|
7425
|
+
|
|
7426
|
+
**Point-link quick start**
|
|
7427
|
+
|
|
7428
|
+
This attaches a marker to a solved point. It is intentionally not a bone or oriented part example.
|
|
7429
|
+
|
|
7430
|
+
```ts
|
|
7431
|
+
const marker = box(8, 8, 4).withConnectors({
|
|
7432
|
+
center: connector({ origin: [0, 0, 0], axis: [0, 0, 1] }),
|
|
7433
|
+
});
|
|
7434
|
+
|
|
7435
|
+
const mech = assembly("Linkage")
|
|
7436
|
+
.link("ground", { at: [0, 0, 0], fixed: true })
|
|
7437
|
+
.link("worldX", { at: [10, 0, 0], fixed: true })
|
|
7438
|
+
.link("tip", { at: [40, 0, 0] })
|
|
7439
|
+
.edgeBetweenLinks("ground", "tip", { name: "bar" })
|
|
7440
|
+
.addAngleBetweenLinks("worldX", "ground", "tip", {
|
|
7441
|
+
name: "theta",
|
|
7442
|
+
control: { min: 0, max: 120, default: 30 },
|
|
7443
|
+
})
|
|
7444
|
+
.addPart("Tip marker", marker, {
|
|
7445
|
+
mate: { connector: "center", toLink: "tip" },
|
|
7044
7446
|
});
|
|
7045
7447
|
|
|
7046
|
-
return mech;
|
|
7448
|
+
return mech;
|
|
7449
|
+
```
|
|
7450
|
+
|
|
7451
|
+
Returning an unsolved `Assembly` keeps the graph available to the runtime. Return a `SolvedAssembly` directly for a specific control state:
|
|
7452
|
+
|
|
7453
|
+
```ts
|
|
7454
|
+
return mech.solve({ theta: 60 });
|
|
7455
|
+
```
|
|
7456
|
+
|
|
7457
|
+
**Frame-aware serial joint**
|
|
7458
|
+
|
|
7459
|
+
```ts
|
|
7460
|
+
const hip = cylinder(12, 10).withConnectors({
|
|
7461
|
+
socket: connector("hinge", {
|
|
7462
|
+
origin: [0, 0, 6],
|
|
7463
|
+
axis: [0, 0, 1],
|
|
7464
|
+
up: [1, 0, 0],
|
|
7465
|
+
kind: "revolute",
|
|
7466
|
+
}),
|
|
7467
|
+
});
|
|
7468
|
+
|
|
7469
|
+
const upperLeg = box(60, 10, 8).translate(30, 0, -4).withConnectors({
|
|
7470
|
+
hip: connector("hinge", {
|
|
7471
|
+
origin: [0, 0, 0],
|
|
7472
|
+
axis: [0, 0, -1],
|
|
7473
|
+
up: [1, 0, 0],
|
|
7474
|
+
kind: "revolute",
|
|
7475
|
+
}),
|
|
7476
|
+
});
|
|
7477
|
+
|
|
7478
|
+
return assembly("Leg")
|
|
7479
|
+
.addPart("Hip", hip)
|
|
7480
|
+
.addPart("Upper Leg", upperLeg)
|
|
7481
|
+
.connect("Hip.socket", "Upper Leg.hip", { as: "hip", min: -35, max: 55 })
|
|
7482
|
+
.solve({ hip: 20 });
|
|
7483
|
+
```
|
|
7484
|
+
|
|
7485
|
+
**Return types**
|
|
7486
|
+
|
|
7487
|
+
| Return value | Standalone | `require()` result type |
|
|
7488
|
+
|---|---|---|
|
|
7489
|
+
| `Assembly` (unsolved) | yes | `ImportedAssembly` |
|
|
7490
|
+
| `SolvedAssembly` | yes | `SolvedAssembly` |
|
|
7491
|
+
|
|
7492
|
+
**Properties:**
|
|
7493
|
+
|
|
7494
|
+
| Property | Type | Description |
|
|
7495
|
+
|----------|------|-------------|
|
|
7496
|
+
| `name` | `string` | — |
|
|
7497
|
+
|
|
7498
|
+
**Kinematics**
|
|
7499
|
+
|
|
7500
|
+
#### `link()` — Add a named kinematic link to the assembly graph.
|
|
7501
|
+
|
|
7502
|
+
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.
|
|
7503
|
+
|
|
7504
|
+
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.
|
|
7505
|
+
|
|
7506
|
+
```ts
|
|
7507
|
+
link(name: string, options?: AssemblyLinkOptions): Assembly
|
|
7508
|
+
```
|
|
7509
|
+
|
|
7510
|
+
**`AssemblyLinkOptions`**
|
|
7511
|
+
- `at?: [ number, number, number ]` — Initial world-space position of this link before kinematic constraints solve it.
|
|
7512
|
+
- `fixed?: boolean` — Keep the link locked at its authored `at` position during solves.
|
|
7513
|
+
- `metadata?: Record<string, unknown>` — User metadata carried through the kinematic graph for inspection and tooling.
|
|
7514
|
+
|
|
7515
|
+
#### `edgeBetweenLinks()` — Add a relationship edge between two kinematic links.
|
|
7516
|
+
|
|
7517
|
+
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.
|
|
7518
|
+
|
|
7519
|
+
```ts
|
|
7520
|
+
edgeBetweenLinks(a: string, b: string, options?: AssemblyEdgeBetweenLinksOptions): Assembly
|
|
7521
|
+
```
|
|
7522
|
+
|
|
7523
|
+
**`AssemblyEdgeBetweenLinksOptions`**: `name?: string`, `length?: number | "lockCurrent" | "free"`, `min?: number`, `max?: number`, `visualOnly?: boolean`, `control?: AssemblyKinematicControlOptions`, `metadata?: Record<string, unknown>`
|
|
7524
|
+
|
|
7525
|
+
`AssemblyKinematicControlOptions`: `{ min?: number, max?: number, default?: number, unit?: string }`
|
|
7526
|
+
|
|
7527
|
+
#### `addAngleBetweenLinks()` — Add an angle relationship among three kinematic links.
|
|
7528
|
+
|
|
7529
|
+
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.
|
|
7530
|
+
|
|
7531
|
+
```ts
|
|
7532
|
+
addAngleBetweenLinks(a: string, b: string, c: string, options?: AssemblyAngleBetweenLinksOptions): Assembly
|
|
7533
|
+
```
|
|
7534
|
+
|
|
7535
|
+
**`AssemblyAngleBetweenLinksOptions`**: `name?: string`, `value?: number`, `min?: number`, `max?: number`, `control?: boolean | AssemblyKinematicControlOptions`, `limit?: AssemblyKinematicLimitOptions`, `metadata?: Record<string, unknown>`
|
|
7536
|
+
|
|
7537
|
+
`AssemblyKinematicLimitOptions`: `{ min?: number, max?: number }`
|
|
7538
|
+
|
|
7539
|
+
#### `addAngleBetweenLinkSegmentAndWorldDirection()` — Add an absolute angle relationship from a world direction to a link segment.
|
|
7540
|
+
|
|
7541
|
+
The first link is the vertex/pivot and the second link is the moving point. A value of `0` places `fromLink -> toLink` along `direction` in the mechanism plane; positive angles rotate counter-clockwise in that plane.
|
|
7542
|
+
|
|
7543
|
+
Use `Points.polar(1, angleDeg)` when the reference direction is planar and angle-based instead of axis-aligned.
|
|
7544
|
+
|
|
7545
|
+
```ts
|
|
7546
|
+
addAngleBetweenLinkSegmentAndWorldDirection(fromLink: string, toLink: string, direction: Vec3, options?: AssemblyAngleBetweenLinksOptions): Assembly
|
|
7047
7547
|
```
|
|
7048
7548
|
|
|
7549
|
+
#### `describeKinematics()` — Return the assembly-native kinematic graph definition.
|
|
7550
|
+
|
|
7049
7551
|
```ts
|
|
7050
|
-
|
|
7552
|
+
describeKinematics(): AssemblyKinematicGraphDef
|
|
7051
7553
|
```
|
|
7052
7554
|
|
|
7053
|
-
|
|
7555
|
+
**Structure**
|
|
7556
|
+
|
|
7557
|
+
#### `addPart()` — Add a named part to the assembly.
|
|
7558
|
+
|
|
7559
|
+
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.
|
|
7560
|
+
|
|
7561
|
+
`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.
|
|
7054
7562
|
|
|
7055
|
-
|
|
7563
|
+
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"`):
|
|
7056
7564
|
|
|
7057
7565
|
```ts
|
|
7058
|
-
const
|
|
7059
|
-
|
|
7060
|
-
|
|
7061
|
-
|
|
7062
|
-
|
|
7566
|
+
const housing = group(
|
|
7567
|
+
{ name: "Body", shape: body },
|
|
7568
|
+
{ name: "Lid", shape: lid },
|
|
7569
|
+
);
|
|
7570
|
+
assembly.addPart("Base Assembly", housing);
|
|
7063
7571
|
```
|
|
7064
7572
|
|
|
7065
7573
|
```ts
|
|
7066
|
-
|
|
7574
|
+
addPart(name: string, part: AssemblyPart, options?: PartOptions): Assembly
|
|
7067
7575
|
```
|
|
7068
7576
|
|
|
7069
|
-
|
|
7577
|
+
**`PartOptions`**: `transform?: TransformInput`, `metadata?: PartMetadata`, `mate?: AssemblyPartMateInput | AssemblyPartMateInput[]`, `bindToFrame?: string`
|
|
7070
7578
|
|
|
7071
|
-
|
|
7072
|
-
|
|
7073
|
-
|
|
7074
|
-
|
|
7075
|
-
### `Assembly`
|
|
7076
|
-
|
|
7077
|
-
Container for a kinematic mechanism made up of named parts and joints.
|
|
7579
|
+
**`AssemblyPartMateInput`**
|
|
7580
|
+
- `connector: string` — Name of a connector declared on the part (via `withConnectors()`).
|
|
7581
|
+
- `toLink: string` — Name of the link this connector's origin is pinned to.
|
|
7582
|
+
- `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).
|
|
7078
7583
|
|
|
7079
|
-
|
|
7584
|
+
#### `frame()` — Add a named rig frame to the assembly.
|
|
7080
7585
|
|
|
7081
|
-
|
|
7586
|
+
A frame is a solved pose: `origin` plus orientation. `axis` is the frame's primary direction and `up` fixes roll around that axis. Use frames for robot links, joint axes, and parts that must carry orientation. Use `link()` for solved points in distance/angle graphs.
|
|
7082
7587
|
|
|
7083
|
-
```
|
|
7084
|
-
|
|
7588
|
+
```ts
|
|
7589
|
+
frame(name: string, options: AssemblyFrameOptions): Assembly
|
|
7085
7590
|
```
|
|
7086
7591
|
|
|
7087
|
-
|
|
7592
|
+
**`AssemblyFrameOptions`**: `origin: [ number, number, number ]`, `axis: [ number, number, number ]`, `up: [ number, number, number ]`, `fixed?: boolean`, `metadata?: Record<string, unknown>`
|
|
7088
7593
|
|
|
7089
|
-
|
|
7090
|
-
- **prismatic** — translates the child along an axis by `value` mm
|
|
7091
|
-
- **fixed** — no motion; rigidly attaches the child at `frame`
|
|
7594
|
+
#### `fixedJoint()` — Rigidly attach a child rig frame to a parent rig frame.
|
|
7092
7595
|
|
|
7093
|
-
|
|
7596
|
+
Fixed joints carry frame hierarchy but do not expose a Motion control.
|
|
7094
7597
|
|
|
7095
7598
|
```ts
|
|
7096
|
-
|
|
7097
|
-
.addPart("base", box(80, 80, 20).translate(0, 0, -10))
|
|
7098
|
-
.addPart("link", box(140, 24, 24).translate(0, -12, -12))
|
|
7099
|
-
.addJoint("shoulder", "revolute", "base", "link", {
|
|
7100
|
-
axis: [0, 1, 0],
|
|
7101
|
-
min: -30, max: 120, default: 25,
|
|
7102
|
-
frame: Transform.identity().translate(0, 0, 20),
|
|
7103
|
-
});
|
|
7104
|
-
|
|
7105
|
-
return mech; // auto-solved at defaults
|
|
7599
|
+
fixedJoint(name: string, options: AssemblyFixedFrameJointOptions): Assembly
|
|
7106
7600
|
```
|
|
7107
7601
|
|
|
7108
|
-
|
|
7602
|
+
`AssemblyFixedFrameJointOptions`: `{ parent: string, child: string, metadata?: Record<string, unknown> }`
|
|
7603
|
+
|
|
7604
|
+
#### `revoluteJoint()` — Add a revolute rig-frame joint.
|
|
7605
|
+
|
|
7606
|
+
The child frame rotates around the parent frame's `axis` direction. Moving frame joints appear in Motion by default; pass `control: false` to keep the joint solved at its default value without showing a Motion control.
|
|
7109
7607
|
|
|
7110
7608
|
```ts
|
|
7111
|
-
|
|
7609
|
+
revoluteJoint(name: string, options: AssemblyMovingFrameJointOptions): Assembly
|
|
7112
7610
|
```
|
|
7113
7611
|
|
|
7114
|
-
|
|
7612
|
+
**`AssemblyMovingFrameJointOptions`**: `parent: string`, `child: string`, `min?: number`, `max?: number`, `default?: number`, `unit?: string`, `control?: boolean`, `metadata?: Record<string, unknown>`
|
|
7115
7613
|
|
|
7116
|
-
|
|
7117
|
-
|---|---|---|
|
|
7118
|
-
| `Assembly` (unsolved) | yes | `ImportedAssembly` |
|
|
7119
|
-
| `SolvedAssembly` | yes | `SolvedAssembly` |
|
|
7614
|
+
#### `prismaticJoint()` — Add a prismatic rig-frame joint.
|
|
7120
7615
|
|
|
7121
|
-
|
|
7616
|
+
The child frame translates along the parent frame's `axis` direction. Moving frame joints appear in Motion by default; pass `control: false` to keep the joint solved at its default value without showing a Motion control.
|
|
7122
7617
|
|
|
7123
|
-
|
|
7124
|
-
|
|
7125
|
-
|
|
7618
|
+
```ts
|
|
7619
|
+
prismaticJoint(name: string, options: AssemblyMovingFrameJointOptions): Assembly
|
|
7620
|
+
```
|
|
7126
7621
|
|
|
7127
7622
|
**Connectors**
|
|
7128
7623
|
|
|
@@ -7142,6 +7637,10 @@ Use the single-argument overload to attach assembly-level connectors — these a
|
|
|
7142
7637
|
withConnectors(partName: string, connectors: Record<string, ConnectorInput>): Assembly
|
|
7143
7638
|
```
|
|
7144
7639
|
|
|
7640
|
+
**`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`
|
|
7641
|
+
|
|
7642
|
+
`ConnectorInput`: `{ connectorType?: string, gender?: ConnectorGender, measurements?: Record<string, number | string> }`
|
|
7643
|
+
|
|
7145
7644
|
#### `getConnectors()` — Get connectors declared on a part in part-local space.
|
|
7146
7645
|
|
|
7147
7646
|
```ts
|
|
@@ -7158,23 +7657,35 @@ getConnector(ref: string): { partName: string; connectorName: string; connector:
|
|
|
7158
7657
|
|
|
7159
7658
|
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
7659
|
|
|
7660
|
+
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.
|
|
7661
|
+
|
|
7161
7662
|
**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
7663
|
|
|
7163
7664
|
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
7665
|
|
|
7666
|
+
**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.
|
|
7667
|
+
|
|
7165
7668
|
The joint type is inferred from the connector's `kind` field if not specified in `options`.
|
|
7166
7669
|
|
|
7167
7670
|
When connectors are defined with `start`/`end`, you can control which point on each connector meets via `align` / `parentAlign` / `childAlign` (`'start'`, `'middle'`, `'end'`).
|
|
7168
7671
|
|
|
7169
|
-
Use `connect()` when connector origins must physically coincide (flange-to-flange, bolt-into-bore).
|
|
7672
|
+
Use `connect()` when connector origins must physically coincide (flange-to-flange, bolt-into-bore).
|
|
7170
7673
|
|
|
7171
7674
|
```ts
|
|
7172
7675
|
// Hinge: both axes point outward along the hinge line
|
|
7173
7676
|
const frame = box(100, 10, 80).withConnectors({
|
|
7174
|
-
hinge: connector("hinge", {
|
|
7677
|
+
hinge: connector("hinge", {
|
|
7678
|
+
origin: [0, 0, 40],
|
|
7679
|
+
axis: [0, 0, 1],
|
|
7680
|
+
up: [1, 0, 0],
|
|
7681
|
+
}),
|
|
7175
7682
|
});
|
|
7176
7683
|
const door = box(60, 4, 80).withConnectors({
|
|
7177
|
-
hinge: connector("hinge", {
|
|
7684
|
+
hinge: connector("hinge", {
|
|
7685
|
+
origin: [0, 0, 40],
|
|
7686
|
+
axis: [0, 0, -1],
|
|
7687
|
+
up: [1, 0, 0],
|
|
7688
|
+
}),
|
|
7178
7689
|
});
|
|
7179
7690
|
assembly("Door")
|
|
7180
7691
|
.addPart("Frame", frame)
|
|
@@ -7186,6 +7697,16 @@ assembly("Door")
|
|
|
7186
7697
|
connect(parentConnectorRef: string, childConnectorRef: string, options?: ConnectOptions): Assembly
|
|
7187
7698
|
```
|
|
7188
7699
|
|
|
7700
|
+
**`ConnectOptions`**
|
|
7701
|
+
|
|
7702
|
+
| Option | Type | Description |
|
|
7703
|
+
|--------|------|-------------|
|
|
7704
|
+
| `flip?` | `boolean` | This parameter is ignored. If your connectors produce wrong orientation, fix the connector axis directions instead of using flip. |
|
|
7705
|
+
| `parentAlign?` | `PortAlign` | Which point on the parent connector to align: 'start', 'middle' (default), or 'end'. |
|
|
7706
|
+
| `childAlign?` | `PortAlign` | Which point on the child connector to align: 'start', 'middle' (default), or 'end'. |
|
|
7707
|
+
| `align?` | `PortAlign` | Shorthand: set both parentAlign and childAlign at once. |
|
|
7708
|
+
| `as?`, `type?`, `min?`, `max?`, `default?`, `unit?`, `effort?`, `velocity?`, `damping?`, `friction?` | | — |
|
|
7709
|
+
|
|
7189
7710
|
#### `match()` — Auto-create a joint by matching typed connectors between two parts.
|
|
7190
7711
|
|
|
7191
7712
|
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 +7729,15 @@ const mech = assembly("Door")
|
|
|
7208
7729
|
.addPart("Frame", frame)
|
|
7209
7730
|
.addPart("Door", door)
|
|
7210
7731
|
.match("Door", "Frame", { hinge_top: "hinge_top", hinge_bottom: "hinge_bottom" });
|
|
7211
|
-
//
|
|
7732
|
+
// Matching connectors computes the placement relationship automatically.
|
|
7212
7733
|
```
|
|
7213
7734
|
|
|
7214
7735
|
```ts
|
|
7215
7736
|
match(childPartName: string, parentPartName: string, pairs: Record<string, string>, options?: MatchToOptions & { as?: string; }): Assembly
|
|
7216
7737
|
```
|
|
7217
7738
|
|
|
7739
|
+
`MatchToOptions`: `{ force?: boolean, angle?: number, distance?: number }`
|
|
7740
|
+
|
|
7218
7741
|
**References**
|
|
7219
7742
|
|
|
7220
7743
|
#### `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 +7746,28 @@ match(childPartName: string, parentPartName: string, pairs: Record<string, strin
|
|
|
7223
7746
|
withReferences(refs: Pick<PlacementReferenceInput, "points">): Assembly
|
|
7224
7747
|
```
|
|
7225
7748
|
|
|
7226
|
-
|
|
7749
|
+
**`PlacementReferenceInput`**: `points?: Record<string, [ number, number, number ]>`, `edges?: Record<string, PlacementEdgeRef>`, `surfaces?: Record<string, PlacementSurfaceRef>`, `objects?: Record<string, PlacementObjectInput>`
|
|
7750
|
+
|
|
7751
|
+
`PlacementEdgeRef`: `{ start: Vec3, end: Vec3 }`
|
|
7752
|
+
|
|
7753
|
+
`PlacementSurfaceRef`: `{ center: Vec3, normal: Vec3 }`
|
|
7227
7754
|
|
|
7228
|
-
|
|
7755
|
+
**Solving**
|
|
7229
7756
|
|
|
7230
|
-
|
|
7757
|
+
#### `solve()` — Solve the assembly at the given control state and return positioned parts.
|
|
7231
7758
|
|
|
7232
|
-
|
|
7759
|
+
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.
|
|
7233
7760
|
|
|
7234
|
-
|
|
7761
|
+
Connector mates declared on `addPart(..., { mate })` attach geometry to solved links while preserving part and connector identity:
|
|
7235
7762
|
|
|
7236
|
-
|
|
7763
|
+
- one mate **positions** the connector origin on its link;
|
|
7764
|
+
- 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;
|
|
7765
|
+
- a third mate **pins the roll** about the bone axis (full frame), e.g. a bore or clevis that must face a specific way.
|
|
7237
7766
|
|
|
7238
|
-
|
|
7767
|
+
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
7768
|
|
|
7240
7769
|
```ts
|
|
7241
|
-
return mech.solve({
|
|
7770
|
+
return mech.solve({ theta: 45 });
|
|
7242
7771
|
```
|
|
7243
7772
|
|
|
7244
7773
|
```ts
|
|
@@ -7253,156 +7782,30 @@ solve(state?: JointState): SolvedAssembly
|
|
|
7253
7782
|
mate(fn: (m: MateBuilder) => void): Assembly
|
|
7254
7783
|
```
|
|
7255
7784
|
|
|
7256
|
-
#### `
|
|
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:
|
|
7785
|
+
#### `edgeBetweenFrames()` — Add a visual skeleton edge between two rig frame origins.
|
|
7346
7786
|
|
|
7347
|
-
|
|
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
|
-
```
|
|
7787
|
+
Frame edges follow the solved frame poses produced by `fixedJoint()`, `revoluteJoint()`, and `prismaticJoint()`. They do not add constraints, degrees of freedom, parts, or geometry; use them to make a frame-only rig readable in the Motion/rig inspection overlay.
|
|
7360
7788
|
|
|
7361
7789
|
```ts
|
|
7362
|
-
|
|
7790
|
+
edgeBetweenFrames(a: string, b: string, options?: AssemblyFrameEdgeOptions): Assembly
|
|
7363
7791
|
```
|
|
7364
7792
|
|
|
7365
|
-
|
|
7793
|
+
`AssemblyFrameEdgeOptions`: `{ name?: string, metadata?: Record<string, unknown> }`
|
|
7366
7794
|
|
|
7367
|
-
|
|
7795
|
+
#### `linkToward()` — Create a derived link at a fixed distance from `fromLink` toward `towardLink`.
|
|
7368
7796
|
|
|
7369
|
-
|
|
7797
|
+
Derived links are trace/reference points. They are recomputed after the primary link solve and cannot participate in structural edges or angle constraints.
|
|
7370
7798
|
|
|
7371
7799
|
```ts
|
|
7372
|
-
|
|
7373
|
-
const hits = sweep.filter(frame => frame.collisions.length > 0);
|
|
7374
|
-
console.log(`Collisions at ${hits.length} of ${sweep.length} poses`);
|
|
7800
|
+
linkToward(name: string, fromLink: string, towardLink: string, distance: number): Assembly
|
|
7375
7801
|
```
|
|
7376
7802
|
|
|
7377
|
-
|
|
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.
|
|
7803
|
+
#### `linkAwayFrom()` — Create a derived link at a fixed distance from `fromLink` away from `awayFromLink`.
|
|
7384
7804
|
|
|
7385
|
-
|
|
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
|
-
```
|
|
7805
|
+
Use this for coupler trace/extension points such as the Chebyshev lambda linkage's point beyond the rocker joint.
|
|
7403
7806
|
|
|
7404
7807
|
```ts
|
|
7405
|
-
|
|
7808
|
+
linkAwayFrom(name: string, fromLink: string, awayFromLink: string, distance: number): Assembly
|
|
7406
7809
|
```
|
|
7407
7810
|
|
|
7408
7811
|
#### `describe()` — Return the serializable assembly definition used by solve/inspect pipelines.
|
|
@@ -7422,7 +7825,7 @@ describe(): AssemblyDefinition
|
|
|
7422
7825
|
|
|
7423
7826
|
A wrapper around an imported `Assembly` that provides kinematic access and convenient transform helpers.
|
|
7424
7827
|
|
|
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()
|
|
7828
|
+
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
7829
|
|
|
7427
7830
|
**Kinematic access**
|
|
7428
7831
|
|
|
@@ -7451,7 +7854,7 @@ require("./arm.forge.js").mergeInto(robot, {
|
|
|
7451
7854
|
});
|
|
7452
7855
|
```
|
|
7453
7856
|
|
|
7454
|
-
#### `assembly()` — The underlying Assembly
|
|
7857
|
+
#### `assembly()` — The underlying Assembly, for advanced composition and inspection.
|
|
7455
7858
|
|
|
7456
7859
|
```ts
|
|
7457
7860
|
get assembly(): Assembly
|
|
@@ -7469,6 +7872,14 @@ solve(state?: JointState): SolvedAssembly
|
|
|
7469
7872
|
part(name: string, state?: JointState): AssemblyPart
|
|
7470
7873
|
```
|
|
7471
7874
|
|
|
7875
|
+
#### `getPart()` — Return a specific named part positioned at the default solved pose.
|
|
7876
|
+
|
|
7877
|
+
This mirrors `SolvedAssembly.getPart()` for imported assemblies. Use `solve(state).getPart(name)` when inspecting a non-default joint state.
|
|
7878
|
+
|
|
7879
|
+
```ts
|
|
7880
|
+
getPart(partName: string): AssemblyPart
|
|
7881
|
+
```
|
|
7882
|
+
|
|
7472
7883
|
#### `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
7884
|
|
|
7474
7885
|
```ts
|
|
@@ -7547,17 +7958,35 @@ color(hex: string): ShapeGroup
|
|
|
7547
7958
|
child(name: string): Shape | Sketch | ShapeGroup
|
|
7548
7959
|
```
|
|
7549
7960
|
|
|
7550
|
-
#### `
|
|
7961
|
+
#### `collisionReport()` — Detect overlapping part pairs at the default solved pose.
|
|
7962
|
+
|
|
7963
|
+
This mirrors `SolvedAssembly.collisionReport()` for imported assemblies. Use `solve(state).collisionReport(options)` when inspecting a non-default joint state.
|
|
7964
|
+
|
|
7965
|
+
```ts
|
|
7966
|
+
collisionReport(options?: CollisionOptions): CollisionFinding[]
|
|
7967
|
+
```
|
|
7968
|
+
|
|
7969
|
+
`CollisionOptions`: `{ parts?: string[], ignorePairs?: Array<[ string, string ]>, minOverlapVolume?: number }`
|
|
7970
|
+
|
|
7971
|
+
#### `minClearance()` — Compute the minimum gap between two parts at the default solved pose.
|
|
7972
|
+
|
|
7973
|
+
This mirrors `SolvedAssembly.minClearance()` for imported assemblies. Use `solve(state).minClearance(partA, partB, searchLength)` when inspecting a non-default joint state.
|
|
7974
|
+
|
|
7975
|
+
```ts
|
|
7976
|
+
minClearance(partA: string, partB: string, searchLength?: number): number
|
|
7977
|
+
```
|
|
7978
|
+
|
|
7979
|
+
#### `mergeInto()` — Flatten this sub-assembly's parts and relationships into `parent` and wire a mount relationship.
|
|
7551
7980
|
|
|
7552
|
-
All part and joint names from the sub-assembly are prefixed with `"${options.prefix}."` to avoid collisions. After the merge,
|
|
7981
|
+
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
7982
|
|
|
7554
7983
|
```ts
|
|
7555
|
-
parent.solve({ "Left Arm.
|
|
7984
|
+
parent.solve({ "Left Arm.theta": 45, "Right Arm.theta": -20 })
|
|
7556
7985
|
```
|
|
7557
7986
|
|
|
7558
|
-
|
|
7987
|
+
Connectors from sub-assembly parts are forwarded with the prefix.
|
|
7559
7988
|
|
|
7560
|
-
The sub-assembly must have exactly one root part
|
|
7989
|
+
The sub-assembly must have exactly one root part before it can be merged.
|
|
7561
7990
|
|
|
7562
7991
|
```ts
|
|
7563
7992
|
const robot = assembly("Robot").addPart("Chassis", chassis);
|
|
@@ -7574,6 +8003,25 @@ require("./arm.forge.js").mergeInto(robot, {
|
|
|
7574
8003
|
mergeInto(parent: Assembly, options: MergeIntoOptions): Assembly
|
|
7575
8004
|
```
|
|
7576
8005
|
|
|
8006
|
+
**`MergeIntoOptions`**
|
|
8007
|
+
|
|
8008
|
+
| Option | Type | Description |
|
|
8009
|
+
|--------|------|-------------|
|
|
8010
|
+
| `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. |
|
|
8011
|
+
| `mountParent` | `string` | Part name in the parent assembly to attach the sub-assembly root to. |
|
|
8012
|
+
| `mountJoint` | `string` | Name for the new mount joint in the parent graph. |
|
|
8013
|
+
| `mountType?` | `JointType` | Joint type for the mount connection (default: 'fixed'). |
|
|
8014
|
+
| `mountOptions?` | `JointOptions` | Frame, axis, limits, and other options for the mount joint. |
|
|
8015
|
+
|
|
8016
|
+
**`JointOptions`**
|
|
8017
|
+
|
|
8018
|
+
| Option | Type | Description |
|
|
8019
|
+
|--------|------|-------------|
|
|
8020
|
+
| `connectorRefs?` | `JointConnectorRefs` | Connector refs that define this joint contract. Usually set by `connect()` / `match()`. |
|
|
8021
|
+
| `frame?`, `origin?`, `axis?`, `min?`, `max?`, `default?`, `unit?`, `effort?`, `velocity?`, `damping?`, `friction?` | | — |
|
|
8022
|
+
|
|
8023
|
+
`JointConnectorRefs`: `{ parent: string, child: string, parentAlign?: PortAlign, childAlign?: PortAlign }`
|
|
8024
|
+
|
|
7577
8025
|
### `SolvedAssembly`
|
|
7578
8026
|
|
|
7579
8027
|
The result of solving an assembly at a specific joint state.
|
|
@@ -7582,7 +8030,7 @@ The result of solving an assembly at a specific joint state.
|
|
|
7582
8030
|
|
|
7583
8031
|
**Validation**
|
|
7584
8032
|
|
|
7585
|
-
Call `collisionReport()` to detect overlapping parts
|
|
8033
|
+
Call `collisionReport()` to detect overlapping parts at this solved pose.
|
|
7586
8034
|
|
|
7587
8035
|
```ts
|
|
7588
8036
|
const solved = mech.solve({ shoulder: 45, elbow: -20 });
|
|
@@ -7628,6 +8076,30 @@ get mateDof(): number | null
|
|
|
7628
8076
|
get mateConverged(): boolean | null
|
|
7629
8077
|
```
|
|
7630
8078
|
|
|
8079
|
+
#### `kinematics()` — Solved assembly-native kinematic or frame-edge overlay data, or null when no rig overlay data was declared.
|
|
8080
|
+
|
|
8081
|
+
```ts
|
|
8082
|
+
get kinematics(): SolvedAssemblyKinematics | null
|
|
8083
|
+
```
|
|
8084
|
+
|
|
8085
|
+
#### `getLinkPosition()` — Return the solved world position of a kinematic link.
|
|
8086
|
+
|
|
8087
|
+
```ts
|
|
8088
|
+
getLinkPosition(linkName: string): Vec3
|
|
8089
|
+
```
|
|
8090
|
+
|
|
8091
|
+
#### `getFrame()` — Return the solved world transform for a named rig frame.
|
|
8092
|
+
|
|
8093
|
+
```ts
|
|
8094
|
+
getFrame(frameName: string): Transform
|
|
8095
|
+
```
|
|
8096
|
+
|
|
8097
|
+
#### `frames()` — Return solved rig frames, including origin, axis, up, and transform.
|
|
8098
|
+
|
|
8099
|
+
```ts
|
|
8100
|
+
get frames(): SolvedAssemblyFrameDef[]
|
|
8101
|
+
```
|
|
8102
|
+
|
|
7631
8103
|
#### `getTransform()` — Return the world-space [`Transform`](/docs/core#transform) for the named part at the solved pose.
|
|
7632
8104
|
|
|
7633
8105
|
```ts
|
|
@@ -8061,6 +8533,10 @@ const part = sheetMetal({ panel: { width: 100, height: 60 }, thickness: 1.5, ben
|
|
|
8061
8533
|
flange(edge: SheetMetalEdge, options: SheetMetalFlangeOptions): SheetMetalPart
|
|
8062
8534
|
```
|
|
8063
8535
|
|
|
8536
|
+
**`SheetMetalFlangeOptions`**
|
|
8537
|
+
- `length: number` — Flange leg length in mm, measured from the outside of the bend to the tip.
|
|
8538
|
+
- `angleDeg?: number` — Bend angle in degrees (default: `90`). Only `90°` is supported in v1. Values other than 90 will be rejected at build time.
|
|
8539
|
+
|
|
8064
8540
|
#### `cutout()` — Subtract a 2D sketch cutout from a planar region of the sheet metal part.
|
|
8065
8541
|
|
|
8066
8542
|
`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 +8556,11 @@ const part = sheetMetal({ panel: { width: 180, height: 110 }, thickness: 1.5, be
|
|
|
8080
8556
|
cutout(region: SheetMetalPlanarRegionName, sketch: Sketch, options?: SheetMetalCutoutOptions): SheetMetalPart
|
|
8081
8557
|
```
|
|
8082
8558
|
|
|
8559
|
+
**`SheetMetalCutoutOptions`**
|
|
8560
|
+
- `u?: number` — Horizontal offset within the region, measured from the region centre (mm). Default: `0`.
|
|
8561
|
+
- `v?: number` — Vertical offset within the region, measured from the region centre (mm). Default: `0`.
|
|
8562
|
+
- `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'`.
|
|
8563
|
+
|
|
8083
8564
|
#### `regionNames()` — Return all semantic region names currently available on this part.
|
|
8084
8565
|
|
|
8085
8566
|
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 +8830,7 @@ Call `robotExport()` alongside your assembly definition. The CLI commands `forge
|
|
|
8349
8830
|
|
|
8350
8831
|
- Mesh-based inertia tensors (full 6-component, not bounding-box approximations)
|
|
8351
8832
|
- Separate collision meshes (convex hull by default — ~50–80% smaller)
|
|
8352
|
-
- Joint
|
|
8833
|
+
- Joint limits, effort/velocity/damping/friction metadata from assembly joints
|
|
8353
8834
|
|
|
8354
8835
|
**Collision mesh modes** (set per-link via `links["PartName"].collision`):
|
|
8355
8836
|
|
|
@@ -8365,7 +8846,7 @@ Call `robotExport()` alongside your assembly definition. The CLI commands `forge
|
|
|
8365
8846
|
- Revolute `velocity` is in degrees/second in Forge; exporters convert to rad/s.
|
|
8366
8847
|
- Prismatic distances are in mm in Forge; exported in meters.
|
|
8367
8848
|
- `massKg` is preferred; `densityKgM3` is used when mass is unknown.
|
|
8368
|
-
-
|
|
8849
|
+
- 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
8850
|
|
|
8370
8851
|
```ts
|
|
8371
8852
|
const rover = assembly("Scout")
|
|
@@ -8421,9 +8902,9 @@ robotExport(options: RobotExportOptions): CollectedRobotExport
|
|
|
8421
8902
|
|
|
8422
8903
|
**`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
8904
|
|
|
8424
|
-
|
|
8905
|
+
**`AssemblyDefinition`**: `name: string`, `parts: AssemblyPartDef[]`, `joints: AssemblyJointDef[]`, `jointCouplings: AssemblyJointCouplingDef[]`, `kinematics: AssemblyKinematicGraphDef`, `frames: AssemblyFrameDef[]`, `frameJoints: AssemblyFrameJointDef[]`, `frameEdges: AssemblyFrameEdgeDef[]`
|
|
8425
8906
|
|
|
8426
|
-
|
|
8907
|
+
**`AssemblyPartDef`**: `name: string`, `part: AssemblyPart`, `base: Transform`, `metadata?: PartMetadata`, `mates: AssemblyPartMateInput[]`, `bindToFrame?: string`
|
|
8427
8908
|
|
|
8428
8909
|
**`PartMetadata`**
|
|
8429
8910
|
|
|
@@ -8432,6 +8913,11 @@ robotExport(options: RobotExportOptions): CollectedRobotExport
|
|
|
8432
8913
|
| `tags?` | `string \| readonly string[]` | Viewport organization tags applied to scene objects produced from this part. |
|
|
8433
8914
|
| `material?`, `process?`, `tolerance?`, `qty?`, `notes?`, `densityKgM3?`, `massKg?` | | — |
|
|
8434
8915
|
|
|
8916
|
+
**`AssemblyPartMateInput`**
|
|
8917
|
+
- `connector: string` — Name of a connector declared on the part (via `withConnectors()`).
|
|
8918
|
+
- `toLink: string` — Name of the link this connector's origin is pinned to.
|
|
8919
|
+
- `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).
|
|
8920
|
+
|
|
8435
8921
|
**`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
8922
|
|
|
8437
8923
|
`JointConnectorRefs`: `{ parent: string, child: string, parentAlign?: PortAlign, childAlign?: PortAlign }`
|
|
@@ -8440,6 +8926,26 @@ robotExport(options: RobotExportOptions): CollectedRobotExport
|
|
|
8440
8926
|
|
|
8441
8927
|
`JointCouplingTermRecord`: `{ joint: string, ratio: number }`
|
|
8442
8928
|
|
|
8929
|
+
**`AssemblyKinematicGraphDef`**: `links: AssemblyLinkDef[]`, `edges: AssemblyEdgeBetweenLinksDef[]`, `angles: AssemblyAngleBetweenLinksDef[]`, `derivedLinks: AssemblyDerivedLinkDef[]`
|
|
8930
|
+
|
|
8931
|
+
`AssemblyLinkDef`: `{ name: string, at: Vec3, fixed: boolean, metadata?: Record<string, unknown> }`
|
|
8932
|
+
|
|
8933
|
+
**`AssemblyEdgeBetweenLinksDef`**: `name: string`, `a: string`, `b: string`, `length: number | null`, `min?: number`, `max?: number`, `visualOnly: boolean`, `control?: AssemblyKinematicControlOptions`, `metadata?: Record<string, unknown>`
|
|
8934
|
+
|
|
8935
|
+
`AssemblyKinematicControlOptions`: `{ min?: number, max?: number, default?: number, unit?: string }`
|
|
8936
|
+
|
|
8937
|
+
**`AssemblyAngleBetweenLinksDef`**: `name: string`, `a?: string`, `b: string`, `c: string`, `reference?: AssemblyAngleReferenceDef`, `target?: number`, `min?: number`, `max?: number`, `control?: AssemblyKinematicControlOptions`, `metadata?: Record<string, unknown>`
|
|
8938
|
+
|
|
8939
|
+
`AssemblyAngleReferenceDef`: `{ kind: "worldDirection", direction: Vec3 }`
|
|
8940
|
+
|
|
8941
|
+
`AssemblyDerivedLinkDef`: `{ name: string, fromLink: string, referenceLink: string, distance: number, direction: AssemblyDerivedLinkDirection }`
|
|
8942
|
+
|
|
8943
|
+
`AssemblyFrameDef`: `{ name: string, origin: Vec3, axis: Vec3, up: Vec3, fixed: boolean, metadata?: Record<string, unknown> }`
|
|
8944
|
+
|
|
8945
|
+
**`AssemblyFrameJointDef`**: `name: string`, `type: AssemblyFrameJointType`, `parent: string`, `child: string`, `rest: Transform`, `min?: number`, `max?: number`, `defaultValue: number`, `unit?: string`, `control: boolean`, `metadata?: Record<string, unknown>`
|
|
8946
|
+
|
|
8947
|
+
`AssemblyFrameEdgeDef`: `{ name: string, a: string, b: string, metadata?: Record<string, unknown> }`
|
|
8948
|
+
|
|
8443
8949
|
#### `dim()` — Add a dimension annotation between two points.
|
|
8444
8950
|
|
|
8445
8951
|
Dimension annotations are purely visual callouts rendered in the viewport and report export. They do not affect geometry or constrain the model.
|
|
@@ -8587,18 +9093,24 @@ offsetBand(thickness: number): Sketch
|
|
|
8587
9093
|
addSpurTeethBetween(options: DriveWheelSpurTeethRegionOptions): this
|
|
8588
9094
|
```
|
|
8589
9095
|
|
|
9096
|
+
`DriveWheelSpurTeethRegionOptions`: `{ name?: string, teethOnFullCircle: number, toothCount: number, firstTooth?: number, faceWidth?: number }`
|
|
9097
|
+
|
|
8590
9098
|
#### `addSolidArcBetween()` — Add a constant-radius solid arc region such as a dwell, stop, or pusher.
|
|
8591
9099
|
|
|
8592
9100
|
```ts
|
|
8593
9101
|
addSolidArcBetween(options: DriveWheelSolidArcRegionOptions): this
|
|
8594
9102
|
```
|
|
8595
9103
|
|
|
9104
|
+
**`DriveWheelSolidArcRegionOptions`**: `name?: string`, `fromAngleDeg: number`, `toAngleDeg: number`, `innerRadius?: number`, `outerRadius: number`, `faceWidth?: number`, `segments?: number`
|
|
9105
|
+
|
|
8596
9106
|
#### `addShapeRegion()` — Add a fully custom region shape while preserving region metadata.
|
|
8597
9107
|
|
|
8598
9108
|
```ts
|
|
8599
9109
|
addShapeRegion(name: string, shape: Shape, options?: DriveWheelShapeRegionOptions): this
|
|
8600
9110
|
```
|
|
8601
9111
|
|
|
9112
|
+
`DriveWheelShapeRegionOptions`: `{ fromAngleDeg?: number, toAngleDeg?: number, innerRadius?: number, outerRadius?: number }`
|
|
9113
|
+
|
|
8602
9114
|
#### `build()` — Build the final wheel shape with a bore connector and region metadata.
|
|
8603
9115
|
|
|
8604
9116
|
```ts
|
|
@@ -8615,7 +9127,7 @@ Pre-built parametric parts available in user scripts as `lib.*`.
|
|
|
8615
9127
|
|
|
8616
9128
|
Every key in this object becomes a method or namespace on the `lib` object exposed to `.forge.js` scripts. The catalog includes:
|
|
8617
9129
|
|
|
8618
|
-
**Fasteners and hardware patterns:** `bolt`, `nut`, `washer`, `fastenerSet`, `
|
|
9130
|
+
**Fasteners and hardware patterns:** `bolt`, `nut`, `washer`, `fastenerSet`, `fastenerHole`, `boltHole`, `counterbore`, `hexNut`, `holePattern`
|
|
8619
9131
|
|
|
8620
9132
|
**Structure:** `tube`, `pipe`, `bracket`, `pipeRoute`, `elbow`, `tSlotProfile`, `tSlotExtrusion`, `profile2020BSlot6Profile`, `profile2020BSlot6`
|
|
8621
9133
|
|
|
@@ -8649,24 +9161,8 @@ Sizes outside the supported ranges will throw at runtime with a descriptive erro
|
|
|
8649
9161
|
- `nut(diameter: number, options?: { pitch?: number; height?: number; acrossFlats?: number; segments?: number; }): Shape` — ISO-style hex nut with a threaded bore. **Details** Constructed from the intersection of three rotated slabs with a cylindrical bore subtracted. The nut is centered at the origin, height along Z. Default proportions follow ISO 4032 loosely: height ≈ 0.8×diameter, across-flats ≈ 1.6×diameter. The bore is a clearance bore (not modelled with helical threads) for rendering efficiency. For standard M-size nuts pre-configured for a complete joint, use { **Example** ```ts const n = lib.nut(5); // M5 nut ```
|
|
8650
9162
|
- `washer(size: MetricSize, options?: { standard?: WasherStandard; segments?: number; }): Shape` — ISO metric flat washer (DIN 125-A). **Details** Returns a flat ring centered at the origin, thickness along Z. Dimensions are taken from { **Example** ```ts const w = lib.washer('M5'); // DIN 125-A M5 washer ```
|
|
8651
9163
|
- `fastenerSet(size: MetricSize, boltLength: number, options?: FastenerSetOptions): FastenerSetResult` — Complete ISO metric fastener set — bolt, nut, optional washers, and matching hole cutters. **Details** Returns all geometry for one bolted joint: the bolt, nut, up to two washers, a clearance-hole cutter, and a tap-drill cutter. All shapes are returned **un-positioned** (each on the Z-axis). Place them with `.translate()`. Sizes outside M4–M10 are supported for the washer (M2–M10); unsupported combinations will throw. **Example** ```ts const hw = lib.fastenerSet('M5', 20); const topPlate = box(60, 40, 8).translate(0, 0, 12) .subtract(hw.clearanceHole.translate(15, 10, 12)); const botPlate = box(60, 40, 8) .subtract(hw.clearanceHole.translate(15, 10, 0)); return [ { name: 'Top Plate', shape: topPlate }, { name: 'Bot Plate', shape: botPlate }, { name: 'Bolt', shape: hw.bolt.translate(15, 10, 20) }, { name: 'Nut', shape: hw.nut.translate(15, 10, -4) }, ]; ```
|
|
8652
|
-
- `
|
|
8653
|
-
- `
|
|
8654
|
-
- `snapLatchCoverAssembly(options: SnapLatchCoverAssemblyOptions): SnapLatchCoverAssemblyResult` — Snap-retained cover with a receiver frame, latch windows, underside catch lands, and fused snap hooks. **Details** This pattern is for covers, cartridges, clasps, and small housings where agents often add decorative tabs without a catch. The receiver has a real service opening plus two clearance latch windows. The cover is one fused part with two flexible-looking snap fingers that pass through the windows and barb under the receiver underside. Nothing intersects in the final assembly; the hook geometry sits close enough to the catch lands to prove retention intent. Coordinate convention: the receiver frame sits from `z=0` to `parentThickness`; the cover is seated just above the receiver on +Z. Two snap hooks sit on the +/-Y ledges and tuck under the receiver. **Example** ```ts const snapCover = lib.snapLatchCoverAssembly({ width: 72, depth: 44, }); verify.notColliding('snap hooks clear receiver windows', snapCover.cover, snapCover.parent); verify.inRange('snap cover has small seating clearance', snapCover.dims.faceClearance, 0.01, 0.08); return snapCover.parts; ```
|
|
8655
|
-
- `pinnedLeverAssembly(options: PinnedLeverAssemblyOptions): PinnedLeverAssemblyResult` — Retained pinned lever stack with a fused hub/arm/grip, low stop land, pivot pin, bore cutters, and thrust washers. **Details** This pattern is for the common handle/lever failure mode where a visual arm, hub, washer, and pin are placed near each other but never form a credible mechanism. The lever body is one fused part, the pin runs through aligned bores, washers sit on both sides of the lever, and the support includes a bearing land plus an optional low stop land beside the lever path. Coordinate convention: pivot axis is +Z at the XY origin. The support starts at `z=0`, the lower washer sits on top of the support, the lever sits on the lower washer, the upper washer sits on the lever, and the retained pin spans the full stack. **Example** ```ts const lever = lib.pinnedLeverAssembly({ armLength: 54, armWidth: 10, pinDiameter: 5, }); verify.equal('lever stack has five retained parts', lever.parts.length, 5); return lever.parts; ```
|
|
8656
|
-
- `retainedShaftAssembly(options: RetainedShaftAssemblyOptions): RetainedShaftAssemblyResult` — Retained shaft, washer, knob, and support-cheek stack for trunnions, pivots, and adjustable clamps. **Details** This pattern replaces the common "pin, washers, and knob are near each other" visual shortcut with a mechanically accountable shaft stack. The two support cheeks get matching clearance bores, the through shaft spans the whole stack, washers and knobs share the same axis, and retaining heads keep the knobs from reading as loose floating cylinders. Coordinate convention: the shaft axis is +X through the world origin. Support cheeks are centered at `x = +/- supportSpacing / 2`. The supports are bored for clearance, so collision inspection should report no support/shaft overlap while the connectivity audit still sees one retained stack. **Example** ```ts const trunnion = lib.retainedShaftAssembly({ supportSpacing: 96, shaftDiameter: 8, supportHeight: 42, }); verify.equal('retained shaft stack has seven parts', trunnion.parts.length, 7); return trunnion.parts; ```
|
|
8657
|
-
- `capturedLinearSlide(options: CapturedLinearSlideOptions): CapturedLinearSlideResult` — Captured linear slide with a U-channel rail, return lips, end stops, and a carriage posed inside the guide. **Details** This pattern is for drawer-slide, quick-release plate, and guided-carriage models where agents often place rail details and a moving block near each other without a capture relationship. The rail is one fused part with side walls, inward lips, and end stops; the carriage is wider than the lip throat but narrower than the inner rail width, so it is mechanically captured while retaining explicit clearance. Coordinate convention: rail length is along X, width is along Y, and Z is up. The rail base starts at `z=0`; the carriage sits above the base and below the return lips. `travel=0` places the carriage at the negative-X end of travel, and `travel=maxTravel` places it at the positive-X end. **Example** ```ts const slide = lib.capturedLinearSlide({ length: 160, carriageLength: 52, travel: 42, }); verify.greaterThan('carriage is captured by return lips', slide.dims.carriageWidth, slide.dims.throatWidth); return slide.parts; ```
|
|
8658
|
-
- `capturedCartridgeGuideAssembly(options: CapturedCartridgeGuideAssemblyOptions): CapturedCartridgeGuideAssemblyResult` — Captured removable cartridge guide with return lips, rear stop, wide cartridge flange, and pull tab. **Details** This pattern is for pump cartridges, filter cassettes, skeg cassettes, battery cartridges, and slide-in service modules where generated models often place a tray and a loose block near each other. The guide is one fused part with side walls, inward return lips, and a rear stop. The cartridge has a wide lower flange captured under the lips and a narrower body that passes through the throat, so the model has a real retention contract without manual coordinate tuning. Coordinate convention: insertion travel is along +X. The open entry is at −X, the rear stop is at +X, the guide base starts at `z=0`, and `insertion=0` places the cartridge at the front travel limit. **Example** ```ts const cassette = lib.capturedCartridgeGuideAssembly({ length: 150, cartridgeLength: 72, }); verify.notColliding('cartridge clears guide rails', cassette.cartridge, cassette.guide); verify.greaterThan('cartridge flange is captured by lips', cassette.dims.cartridgeWidth, cassette.dims.throatWidth); return cassette.parts; ```
|
|
8659
|
-
- `livingHingeCoverAssembly(options: LivingHingeCoverAssemblyOptions): LivingHingeCoverAssemblyResult` — One-piece molded living-hinge cover strip with a fixed leaf, thin flexible web, cover leaf, pull lip, snap barb, and catch land. **Details** This pattern is for small polypropylene-style lids, battery doors, sample covers, blister latches, and molded service flaps where generated models often draw a decorative hinge strip between two disconnected plates. It returns one fused molded part in its as-molded flat state: fixed mounting leaf, thin hinge web, moving cover leaf, pull lip, raised snap barb, and catch land. The flexible web is intentionally much thinner than the rigid leaves and shares material with both leaves. Coordinate convention: X is hinge length/part width, Y runs from fixed leaf through hinge web to cover leaf, and Z is thickness. The hinge web is centered on `y=0`; the fixed leaf lies at −Y and the cover leaf at +Y. **Example** ```ts const livingCover = lib.livingHingeCoverAssembly({ width: 64, coverDepth: 42, }); verify.greaterThan('living hinge is much thinner than rigid leaves', livingCover.dims.flexRatio, 3); return livingCover.parts; ```
|
|
8660
|
-
- `knuckledHingeAssembly(options: KnuckledHingeAssemblyOptions): KnuckledHingeAssemblyResult` — Alternating knuckle hinge with two fused leaves and a retained pin. **Details** This pattern replaces hand-placed hinge barrels and pin ghosts with a mechanically accountable hinge. The fixed leaf owns every other knuckle, the moving leaf owns the alternating knuckles, all knuckles share one bore size, and the retained pin spans the full stack with heads outside the barrels. Coordinate convention: the hinge pin axis is +X through the world origin. The fixed leaf extends toward +Y. The moving leaf extends toward -Y and rotates about +X by `openAngleDeg`. **Example** ```ts const hinge = lib.knuckledHingeAssembly({ length: 70, leafLength: 28, openAngleDeg: 45, }); verify.equal('hinge has two leaves and one retained pin', hinge.parts.length, 3); return hinge.parts; ```
|
|
8661
|
-
- `clevisPinJointAssembly(options?: ClevisPinJointAssemblyOptions): ClevisPinJointAssemblyResult` — Clevis-style pin joint with bored yoke ears, a center link eye, and a retained pin. **Details** This pattern is for crank links, damper rod ends, pump crossheads, capo/cam pivots, and small mechanism joints where agents often place an eyelet and a pin near a bracket without modeling the captured load path. The clevis is one fused part with two bored ears and a rear bridge, the center link has a real eye and arm, and the retained pin spans the full stack with heads outside the ears. Coordinate convention: the pin axis is +Y through the world origin. The center link arm extends toward +X. The clevis bridge sits behind the eye on -X, leaving the link eye clear inside the yoke. **Example** ```ts const clevis = lib.clevisPinJointAssembly({ pinDiameter: 4, linkArmLength: 38, }); verify.equal('clevis joint has three retained parts', clevis.parts.length, 3); return clevis.parts; ```
|
|
8662
|
-
- `seatedBearingAssembly(options: SeatedBearingAssemblyOptions): SeatedBearingAssemblyResult` — Seated radial-bearing support with a real counterbore, shoulder, through shaft, and retaining collars. **Details** This pattern is for purchased bearings, rollers, burr-cartridge shafts, and small spindle supports where agents often place a ring and a shaft near a block without modelling the pocket that locates the bearing. The housing includes a through-bore and a larger counterbore that leaves a shoulder for the bearing outer race. The shaft is smaller than the bearing bore and carries collars outside the housing, so collision checks can distinguish intended clearance from impossible overlap. Coordinate convention: the shaft axis is +Z through the world origin. The housing block starts at `z=0`, the raised boss is on top of the block, the bearing is seated from the top counterbore, and the shaft extends above and below the housing. **Example** ```ts const bearingStack = lib.seatedBearingAssembly({ bearingOuterDiameter: 22, bearingInnerDiameter: 8, bearingWidth: 7, }); verify.greaterThan('housing has wall around bearing pocket', bearingStack.dims.bossOuterDiameter - bearingStack.dims.pocketDiameter, 4); return bearingStack.parts; ```
|
|
8663
|
-
- `cableGlandAnchorAssembly(options: CableGlandAnchorAssemblyOptions): CableGlandAnchorAssemblyResult` — Cable, wire, or tube gland anchor with a real panel hole, hollow gland body, compression nut, and routed cable. **Details** This pattern is for pumps, filters, electronics boxes, vents, monitors, and fixtures where generated models often leave hoses or cables terminating in space. It creates the receiving panel hole, a hollow gland body with a panel-side flange seated in a shallow pocket, a hollow compression nut, and a cable/tube that runs through the gland bore with explicit clearance. Coordinate convention: the cable axis is +X through the world origin. The panel is centered around `x=0` with thickness along X; the flange sits on the +X side of the panel and the compression nut sits on the −X side. The cable spans the full anchor. **Example** ```ts const anchor = lib.cableGlandAnchorAssembly({ cableDiameter: 6, panelThickness: 3, }); verify.notColliding('cable clears gland bore', anchor.cable, anchor.gland); verify.clearanceBetween('gland flange is seated at panel pocket', anchor.gland, anchor.panel, 0.01, 0.2); return anchor.parts; ```
|
|
8664
|
-
- `hoseBarbPortAssembly(options: HoseBarbPortAssemblyOptions): HoseBarbPortAssemblyResult` — Hose-barb pump/filter port with a bored receiver, shoulder, barb ridges, installed hose, and clamp band. **Details** This pattern is for pump heads, filters, vents, lab cartridges, and fluid fittings where generated models often leave tubes ending near a block. The receiver has a real through-port and raised boss, the fitting is hollow with a shoulder and multiple barb ridges, and the hose is modeled as an installed tube over the barb envelope with a clamp band. The hose bore is sized for the deformed installed hose, so collision checks distinguish the retained interface from impossible solid overlap. Coordinate convention: the fluid axis is +X through the world origin. The receiver block is centered around `x=0`; the raised boss and hose are on the +X side. **Example** ```ts const hosePort = lib.hoseBarbPortAssembly({ hoseInnerDiameter: 6, hoseOuterDiameter: 10, }); verify.notColliding('hose clears barb peaks', hosePort.hose, hosePort.fitting); verify.inRange('fitting shoulder seats near boss face', hosePort.dims.faceClearance, 0.01, 0.08); return hosePort.parts; ```
|
|
8665
|
-
- `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
|
-
- `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
|
-
- `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. Each interior waypoint gets a torus-section bend. Straight segments connect them. Returns a single unioned Shape.
|
|
8669
|
-
- `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.
|
|
9164
|
+
- `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.
|
|
9165
|
+
- `elbow(pipeRadius: number, bendRadius: number, angle?: number | { ... }, options?: { ... }): Shape` — Pipe elbow — a curved pipe section for connecting two pipe directions. This is a convenience recipe over `Curve.Arc()` and [`sweep()`](/docs/curves#sweep). Use `Curve.Route.fromPolyline()` directly when you need named route ports, segment metadata, or multi-bend pipe runs.
|
|
8670
9166
|
- `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
9167
|
- `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); ```
|
|
8672
9168
|
- `tSlotProfile(options?: TSlotProfileOptions): Sketch` — Build a 2D T-slot cross-section sketch. Default parameters describe a 20x20 B-type profile with slot 6. Use this when you want a drawing-ready profile sketch before extrusion.
|
|
@@ -8814,7 +9310,7 @@ Cut planes, exploded views, joint animations, and scene configuration.
|
|
|
8814
9310
|
|
|
8815
9311
|
## Contents
|
|
8816
9312
|
|
|
8817
|
-
- [Viewport & Runtime](#viewport-runtime) — `Viewport.label`, `scene`, `viewConfig`, `explodeView`, `
|
|
9313
|
+
- [Viewport & Runtime](#viewport-runtime) — `Viewport.label`, `scene`, `viewConfig`, `explodeView`, `compareWith`, `cutPlane`, `mock`, `showLabels`, `highlight`
|
|
8818
9314
|
- [RouteBuilder](#routebuilder)
|
|
8819
9315
|
- [route](#route)
|
|
8820
9316
|
|
|
@@ -9001,7 +9497,7 @@ scene(options: SceneOptions): void
|
|
|
9001
9497
|
|
|
9002
9498
|
#### `viewConfig()` — Configure viewport helper visuals for the current script execution.
|
|
9003
9499
|
|
|
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
|
|
9500
|
+
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
9501
|
|
|
9006
9502
|
This does **not** trigger a geometry recompute; it only affects the visual helpers drawn on top of the 3D scene.
|
|
9007
9503
|
|
|
@@ -9058,91 +9554,6 @@ explodeView(options?: ExplodeViewOptions): void
|
|
|
9058
9554
|
- `direction?: ExplodeDirection` — Direction mode for this node
|
|
9059
9555
|
- `axisLock?: ExplodeAxis` — Optional axis lock after direction is resolved
|
|
9060
9556
|
|
|
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
9557
|
#### `compareWith()` — Declare a reference model for comparison inspection.
|
|
9147
9558
|
|
|
9148
9559
|
`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 +9886,30 @@ For deeper API coverage, load the relevant generated doc group from the skill so
|
|
|
9475
9886
|
|
|
9476
9887
|
How to build mechanical joints — clevis-tongue hinges, ball-and-socket, dovetails — that actually rotate without binding and stop where they should.
|
|
9477
9888
|
|
|
9889
|
+
## Frame-Aware Connectors First
|
|
9890
|
+
|
|
9891
|
+
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:
|
|
9892
|
+
|
|
9893
|
+
- `origin` is the pivot, pin center, socket center, or contact point.
|
|
9894
|
+
- `axis` is the hinge line or slide direction.
|
|
9895
|
+
- `up` is the secondary direction that fixes the part's zero-angle twist.
|
|
9896
|
+
|
|
9897
|
+
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.
|
|
9898
|
+
|
|
9899
|
+
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.
|
|
9900
|
+
|
|
9901
|
+
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.
|
|
9902
|
+
|
|
9903
|
+
## Mirrored Revolute Axes
|
|
9904
|
+
|
|
9905
|
+
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.
|
|
9906
|
+
|
|
9907
|
+
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`.
|
|
9908
|
+
|
|
9909
|
+
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.
|
|
9910
|
+
|
|
9911
|
+
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.
|
|
9912
|
+
|
|
9478
9913
|
## The Cavity Rule
|
|
9479
9914
|
|
|
9480
9915
|
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 +10041,7 @@ stable; named scene views are still available through `--view`.
|
|
|
9606
10041
|
The command tree is intentionally job-shaped:
|
|
9607
10042
|
|
|
9608
10043
|
```text
|
|
9609
|
-
inspect visual image|cutaway|depth|normals|objects
|
|
10044
|
+
inspect visual image|cutaway|depth|normals|rig|objects
|
|
9610
10045
|
inspect surface zebra|roughness
|
|
9611
10046
|
inspect physical components|floating|gaps
|
|
9612
10047
|
inspect fit interference
|
|
@@ -9647,6 +10082,7 @@ Use targeted evidence commands for expensive analyses:
|
|
|
9647
10082
|
```bash
|
|
9648
10083
|
forgecad inspect visual depth model.forge.js --camera iso
|
|
9649
10084
|
forgecad inspect visual normals model.forge.js --camera iso
|
|
10085
|
+
forgecad inspect visual rig model.forge.js --camera iso
|
|
9650
10086
|
forgecad inspect surface zebra model.forge.js --camera iso
|
|
9651
10087
|
forgecad inspect surface roughness model.forge.js --camera iso
|
|
9652
10088
|
forgecad inspect visual objects model.forge.js --camera iso
|
|
@@ -10132,12 +10568,33 @@ color(value: string | undefined): SdfShape
|
|
|
10132
10568
|
material(props: ShapeMaterialProps): SdfShape
|
|
10133
10569
|
```
|
|
10134
10570
|
|
|
10571
|
+
**`ShapeMaterialProps`**
|
|
10572
|
+
|
|
10573
|
+
| Option | Type | Description |
|
|
10574
|
+
|--------|------|-------------|
|
|
10575
|
+
| `metalness?` | `number` | Metalness factor (0 = dielectric, 1 = metal). Default: 0.05 |
|
|
10576
|
+
| `roughness?` | `number` | Roughness factor (0 = mirror, 1 = fully diffuse). Default: 0.35 |
|
|
10577
|
+
| `emissive?` | `string` | Emissive glow color (hex string, e.g. "#ff6b35"). |
|
|
10578
|
+
| `emissiveIntensity?` | `number` | Emissive intensity multiplier. Default: 1 |
|
|
10579
|
+
| `opacity?` | `number` | Opacity (0 = fully transparent, 1 = fully opaque). Default: 1 |
|
|
10580
|
+
| `wireframe?` | `boolean` | Render as wireframe. Default: false |
|
|
10581
|
+
| `clearcoat?` | `number` | Clearcoat intensity (0–1). Default: 0.1 |
|
|
10582
|
+
| `clearcoatRoughness?` | `number` | Clearcoat roughness (0–1). Default: 0.4 |
|
|
10583
|
+
| `transmission?` | `number` | Glass/translucency transmission factor (0–1). Renderer support depends on target. |
|
|
10584
|
+
| `ior?` | `number` | Index of refraction for transmissive materials. Typical glass is ~1.45. |
|
|
10585
|
+
| `thickness?` | `number` | Approximate transmissive volume thickness in model units. |
|
|
10586
|
+
| `specularIntensity?` | `number` | Specular highlight intensity (0–1). |
|
|
10587
|
+
| `specularColor?` | `string` | Specular highlight tint. |
|
|
10588
|
+
| `reflectivity?` | `number` | Reflection strength for supported renderers (0–1). |
|
|
10589
|
+
|
|
10135
10590
|
#### `bounds()` — Set explicit preview/meshing bounds for this implicit leaf.
|
|
10136
10591
|
|
|
10137
10592
|
```ts
|
|
10138
10593
|
bounds(bounds: SdfBounds | [ Vec3, Vec3 ]): SdfShape
|
|
10139
10594
|
```
|
|
10140
10595
|
|
|
10596
|
+
`SdfBounds`: `{ min: Vec3, max: Vec3 }`
|
|
10597
|
+
|
|
10141
10598
|
#### `at()` — Sculpt-style alias for translate().
|
|
10142
10599
|
|
|
10143
10600
|
```ts
|
|
@@ -10234,6 +10691,12 @@ fillWith(pattern: SdfShape): SdfShape
|
|
|
10234
10691
|
fillWithGyroid(options: TpmsOptions): SdfShape
|
|
10235
10692
|
```
|
|
10236
10693
|
|
|
10694
|
+
**`TpmsOptions`**
|
|
10695
|
+
- `thickness?: number` — Dimensionless field threshold kept for compatibility. Prefer `wallThickness` for approximate millimeter units.
|
|
10696
|
+
- `wallThickness?: number` — Approximate physical wall thickness in millimeters.
|
|
10697
|
+
- `tpmsThicknessMode?: TpmsThicknessMode` — Override TPMS thickness interpretation. Defaults to metric when `wallThickness` is used, field-threshold when `thickness` is used.
|
|
10698
|
+
- Also: `cellSize: number`
|
|
10699
|
+
|
|
10237
10700
|
#### `fillWithSchwarzP()` — Keep only the Schwarz-P lattice inside this shape.
|
|
10238
10701
|
|
|
10239
10702
|
```ts
|
|
@@ -10384,6 +10847,10 @@ shape.surfaceDisplace((u, v) => -Math.sin(u * 2) * 0.3)
|
|
|
10384
10847
|
surfaceDisplace(pattern: SurfacePattern | ((u: number, v: number) => number), options?: SurfaceDisplaceOptions): SdfShape
|
|
10385
10848
|
```
|
|
10386
10849
|
|
|
10850
|
+
**`SurfaceDisplaceOptions`**
|
|
10851
|
+
- `uv?: "auto" | "sphere" | "cylinder" | "torus" | "triplanar"` — Override auto-detected UV mode. Default: 'auto' (detects from SDF tree).
|
|
10852
|
+
- `triplanarSharpness?: number` — Triplanar blend sharpness — higher = crisper transitions. Default: 4. Only used in triplanar mode.
|
|
10853
|
+
|
|
10387
10854
|
#### `onion()` — Create concentric onion layers.
|
|
10388
10855
|
|
|
10389
10856
|
```ts
|