forgecad 0.6.3 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -12
- package/dist/assets/{AdminPage-CeqCUUgu.js → AdminPage-D4bocK4E.js} +250 -151
- package/dist/assets/{BlogPage-P_AJP0v9.js → BlogPage-CJEXL_zJ.js} +94 -70
- package/dist/assets/{DocsPage-CKRV2iq2.js → DocsPage-D3A_g8V3.js} +329 -163
- package/dist/assets/{EditorApp-CnC2k4cW.css → EditorApp-BWYUSpUN.css} +590 -136
- package/dist/assets/EditorApp-Cihhqcsq.js +11692 -0
- package/dist/assets/{EmbedViewer-DBlzmQ5i.js → EmbedViewer-kWjKaC_t.js} +2 -4
- package/dist/assets/LandingPageProofDriven-Bg2IUc3l.css +856 -0
- package/dist/assets/LandingPageProofDriven-DXkKlyhI.js +601 -0
- package/dist/assets/PricingPage-BsU5vzEx.js +232 -0
- package/dist/assets/{SettingsPage-BqCh9JcC.js → SettingsPage-PqvpAKIs.js} +129 -5
- package/dist/assets/{evalWorker-Ql-aKwLA.js → evalWorker-C-hzNUMy.js} +8949 -3161
- package/dist/assets/{Viewport-CoB46f5R.js → index-Pz321YAt.js} +38382 -7501
- package/dist/assets/{index-2hfs_ub0.css → index-ay13WNfa.css} +726 -53
- package/dist/assets/{javascript-DCxGoE5Y.js → javascript-DAl8Gmyo.js} +1 -1
- package/dist/assets/{manifold-CqNMHHKO.js → manifold-BcbjWLIo.js} +4 -3
- package/dist/assets/{manifold-Cce9wRFz.js → manifold-DBckbFgx.js} +1 -1
- package/dist/assets/{manifold-D6BeHIOo.js → manifold-O2AAGXyj.js} +1 -1
- package/dist/assets/{reportWorker-sFEFonXf.js → reportWorker-Dxr-5A7w.js} +8760 -3559
- package/dist/assets/{vendor-react-Dt7-aaJH.js → vendor-react-CG3i_wp0.js} +65 -8
- package/dist/docs/index.html +2 -2
- package/dist/docs-raw/CLI.md +341 -718
- package/dist/docs-raw/generated/assembly.md +699 -112
- package/dist/docs-raw/generated/concepts.md +1834 -1346
- package/dist/docs-raw/generated/core.md +1012 -1059
- package/dist/docs-raw/generated/curves.md +759 -116
- package/dist/docs-raw/generated/lib.md +43 -748
- package/dist/docs-raw/generated/output.md +139 -245
- package/dist/docs-raw/generated/sdf.md +208 -0
- package/dist/docs-raw/generated/sheet-metal.md +473 -21
- package/dist/docs-raw/generated/sketch.md +1518 -362
- package/dist/docs-raw/generated/viewport.md +368 -299
- package/dist/docs-raw/generated/wood.md +104 -0
- package/dist/index.html +2 -2
- package/dist/landing/proof-ams-adapter.png +0 -0
- package/dist/landing/proof-bolt-and-nut.png +0 -0
- package/dist/landing/proof-fillet-enclosure.png +0 -0
- package/dist/landing/proof-glasses.png +0 -0
- package/dist/landing/proof-gyroid.png +0 -0
- package/dist/sitemap.xml +6 -6
- package/dist-cli/forgecad.js +12321 -5700
- package/dist-cli/forgecad.js.map +1 -0
- package/dist-cli/solver-46FFSK2U.js +363 -0
- package/dist-cli/solver-46FFSK2U.js.map +1 -0
- package/dist-skill/CONTEXT.md +4890 -6302
- package/dist-skill/SKILL-dev.md +22 -66
- package/dist-skill/SKILL.md +20 -59
- package/dist-skill/docs/API/core/concepts.md +37 -92
- package/dist-skill/docs/CLI.md +341 -718
- package/dist-skill/docs/generated/assembly.md +699 -112
- package/dist-skill/docs/generated/core.md +1012 -1059
- package/dist-skill/docs/generated/curves.md +759 -116
- package/dist-skill/docs/generated/lib.md +43 -748
- package/dist-skill/docs/generated/output.md +139 -245
- package/dist-skill/docs/generated/sdf.md +208 -0
- package/dist-skill/docs/generated/sheet-metal.md +473 -21
- package/dist-skill/docs/generated/sketch.md +1518 -362
- package/dist-skill/docs/generated/viewport.md +368 -299
- package/dist-skill/docs/generated/wood.md +104 -0
- package/dist-skill/docs/guides/coordinate-system.md +11 -17
- package/dist-skill/docs/guides/geometry-conventions.md +13 -70
- package/dist-skill/docs/guides/joint-design.md +78 -0
- package/dist-skill/docs/guides/modeling-recipes.md +22 -195
- package/dist-skill/docs/guides/positioning.md +88 -147
- package/dist-skill/docs-dev/API/core/concepts.md +78 -0
- package/dist-skill/docs-dev/CLI.md +488 -0
- package/dist-skill/{docs → docs-dev}/blueprint-first.md +5 -0
- package/dist-skill/{docs → docs-dev}/coding-best-practices.md +6 -8
- package/dist-skill/{docs → docs-dev}/coding.md +2 -4
- package/dist-skill/docs-dev/component-model.md +164 -0
- package/dist-skill/docs-dev/generated/assembly.md +779 -0
- package/dist-skill/docs-dev/generated/core.md +1676 -0
- package/dist-skill/docs-dev/generated/curves.md +855 -0
- package/dist-skill/docs-dev/generated/lib.md +55 -0
- package/dist-skill/docs-dev/generated/output.md +234 -0
- package/dist-skill/docs-dev/generated/sdf.md +208 -0
- package/dist-skill/docs-dev/generated/sheet-metal.md +506 -0
- package/dist-skill/docs-dev/generated/sketch.md +1753 -0
- package/dist-skill/docs-dev/generated/viewport.md +513 -0
- package/dist-skill/docs-dev/generated/wood.md +104 -0
- package/dist-skill/docs-dev/guides/coordinate-system.md +46 -0
- package/dist-skill/docs-dev/guides/geometry-conventions.md +52 -0
- package/dist-skill/docs-dev/guides/joint-design.md +78 -0
- package/dist-skill/docs-dev/guides/modeling-recipes.md +77 -0
- package/dist-skill/docs-dev/guides/positioning.md +151 -0
- package/dist-skill/{docs → docs-dev}/guides/skill-maintenance.md +21 -10
- package/dist-skill/{docs → docs-dev}/internals/compiler.md +5 -6
- package/dist-skill/{docs → docs-dev}/internals/constraint-solver-quality.md +0 -1
- package/dist-skill/{docs → docs-dev}/internals/constraint-solver.md +0 -1
- package/dist-skill/{docs → docs-dev}/internals/sketch-2d-pipeline.md +2 -3
- package/examples/api/attachTo-basics.forge.js +8 -8
- package/examples/api/bill-of-materials.forge.js +9 -9
- package/examples/api/bolt-pattern.forge.js +5 -5
- package/examples/api/boolean-operations.forge.js +5 -5
- package/examples/api/bounding-box-visualizer.forge.js +3 -3
- package/examples/api/clone-duplicate.forge.js +2 -2
- package/examples/api/colors-union-vs-array.forge.js +6 -6
- package/examples/api/connector-assembly.forge.js +8 -6
- package/examples/api/connector-basics.forge.js +7 -7
- package/examples/api/constrained-sketch-mechanical.forge.js +4 -4
- package/examples/api/elbow-test.forge.js +3 -3
- package/examples/api/extrude-options.forge.js +8 -14
- package/examples/api/feature-created-faces.forge.js +6 -10
- package/examples/api/fillet-showcase.forge.js +2 -2
- package/examples/api/folded-service-panel-cover.forge.js +2 -2
- package/examples/api/gears-tier1.forge.js +5 -5
- package/examples/api/group-test.forge.js +3 -3
- package/examples/api/group-vs-union.forge.js +1 -1
- package/examples/api/highlight-debug.forge.js +4 -0
- package/examples/api/js-module-pillars.js +1 -1
- package/examples/api/js-module-scene.js +2 -2
- package/examples/api/mesh-import-slats.forge.js +4 -4
- package/examples/api/patterns.forge.js +3 -3
- package/examples/api/pointAlong-orientation.forge.js +3 -3
- package/examples/api/profile-2020-b-slot6.forge.js +4 -5
- package/examples/api/route-perimeter-flange.forge.js +1 -1
- package/examples/api/sdf-rover-demo.forge.js +10 -10
- package/examples/api/sketch-on-face-demo.forge.js +2 -2
- package/examples/api/sketch-regions.forge.js +4 -4
- package/examples/api/sketch-rounding-strategies.forge.js +1 -1
- package/examples/api/smooth-curve-connections.forge.js +1 -1
- package/examples/api/transition-curves.forge.js +4 -4
- package/examples/api/variable-sweep-pure-sdf-test.forge.js +162 -0
- package/examples/api/variable-sweep-test.forge.js +2 -2
- package/examples/api/wood-joinery.forge.js +60 -0
- package/examples/compiler-corpus/enclosure-shell-cuts.forge.js +3 -3
- package/examples/compiler-corpus/fastener-plate-variants.forge.js +2 -2
- package/examples/constraints/01-fully-constrained-rect.forge.js +2 -2
- package/examples/constraints/02-underconstrained-triangle.forge.js +1 -1
- package/examples/constraints/03-redundant-constraints.forge.js +2 -2
- package/examples/constraints/05-parallel-with-linedistance.forge.js +2 -2
- package/examples/constraints/06-complex-spectrogram.forge.js +1 -1
- package/examples/constraints/07-perpendicular-chain.forge.js +4 -4
- package/examples/constraints/08-symmetric-bracket.forge.js +4 -4
- package/examples/constraints/09-stress-spiral.forge.js +1 -1
- package/examples/constraints/10-stress-honeycomb.forge.js +1 -1
- package/examples/constraints/11-surface-grid.forge.js +2 -2
- package/examples/constraints/12-surface-nested.forge.js +4 -4
- package/examples/constraints/13-surface-complex.forge.js +1 -1
- package/examples/exact-arc-housing.forge.js +12 -0
- package/examples/experiments/drone-arm.forge.js +53 -0
- package/examples/furniture/adjustable-table.forge.js +15 -15
- package/examples/furniture/bathroom.forge.js +26 -26
- package/examples/furniture/chair.forge.js +13 -13
- package/examples/furniture/picture-frame.forge.js +6 -6
- package/examples/furniture/shoe-rack-doors.forge.js +8 -8
- package/examples/furniture/shoe-rack.forge.js +7 -7
- package/examples/furniture/table-lamp.forge.js +8 -8
- package/examples/gcode/lissajous-vase.forge.js +4 -4
- package/examples/gcode/math-surface.forge.js +3 -3
- package/examples/gcode/parametric-vase.forge.js +4 -4
- package/examples/gcode/spiral-tower.forge.js +4 -4
- package/examples/generative/crystal-growth.forge.js +9 -9
- package/examples/generative/frost-spires.forge.js +9 -9
- package/examples/generative/golden-spiral-tower.forge.js +11 -11
- package/examples/generative/molten-forge.forge.js +6 -6
- package/examples/generative/neon-coral.forge.js +7 -7
- package/examples/mechanical/3d-printer.forge.js +37 -37
- package/examples/mechanical/5-finger-robot-hand.forge.js +19 -19
- package/examples/mechanical/airplane-propeller.forge.js +9 -9
- package/examples/mechanical/bolt-and-nut.forge.js +10 -10
- package/examples/mechanical/door-with-hinges.forge.js +7 -7
- package/examples/mechanical/fillet-enclosure.forge.js +15 -11
- package/examples/mechanical/headphone-hanger-v2.forge.js +11 -11
- package/examples/mechanical/robot_hand.forge.js +24 -24
- package/examples/mechanical/robot_hand_2.forge.js +26 -26
- package/examples/nurbs-surface.forge.js +8 -0
- package/examples/nurbs-tube.forge.js +7 -0
- package/examples/products/bottle.forge.js +8 -8
- package/examples/products/chess-set.forge.js +25 -25
- package/examples/products/classical-piano.forge.js +20 -20
- package/examples/products/clock.forge.js +33 -33
- package/examples/products/cup.forge.js +5 -5
- package/examples/products/iphone.forge.js +20 -20
- package/examples/products/laptop.forge.js +24 -24
- package/examples/products/laser-cut-box.forge.js +6 -6
- package/examples/products/laser-cut-tray.forge.js +6 -6
- package/examples/products/liquid-soap-dispenser.forge.js +23 -23
- package/examples/products/origami-fish.forge.js +14 -12
- package/examples/products/spiderman-cake.forge.js +6 -6
- package/examples/shelf/container.forge.js +5 -5
- package/examples/shelf/shelf-unit.forge.js +6 -6
- package/examples/toolbox/bolted-joint.forge.js +7 -7
- package/package.json +9 -4
- package/dist/assets/EditorApp-B-vQvgam.js +0 -9888
- package/dist/assets/LandingPage-C5n9hDXI.js +0 -322
- package/dist/assets/PublishedModelPage-Dt7PCVBj.js +0 -146
- package/dist/assets/__vite-browser-external-CURh0WXD.js +0 -8
- package/dist/assets/deserializeRunResult-BLAFoiE0.js +0 -19365
- package/dist/assets/index-1CYp3zUp.js +0 -1455
- package/dist-skill/docs/API/API.md +0 -1666
- package/dist-skill/docs/API/README.md +0 -37
- package/dist-skill/docs/API/assembly/assembly.md +0 -617
- package/dist-skill/docs/API/core/edge-queries.md +0 -130
- package/dist-skill/docs/API/core/parameters.md +0 -122
- package/dist-skill/docs/API/core/reserved-terms.md +0 -137
- package/dist-skill/docs/API/core/sdf.md +0 -326
- package/dist-skill/docs/API/core/skill-cli.md +0 -194
- package/dist-skill/docs/API/core/skill-guide.md +0 -205
- package/dist-skill/docs/API/core/specs.md +0 -186
- package/dist-skill/docs/API/core/topology.md +0 -372
- package/dist-skill/docs/API/entities.md +0 -268
- package/dist-skill/docs/API/output/bom.md +0 -58
- package/dist-skill/docs/API/output/brep-export.md +0 -87
- package/dist-skill/docs/API/output/dimensions.md +0 -67
- package/dist-skill/docs/API/output/export.md +0 -110
- package/dist-skill/docs/API/output/gcode.md +0 -195
- package/dist-skill/docs/API/runtime/viewport.md +0 -420
- package/dist-skill/docs/API/sheet-metal/sheet-metal.md +0 -185
- package/dist-skill/docs/API/sketch/anchor.md +0 -37
- package/dist-skill/docs/API/sketch/booleans.md +0 -91
- package/dist-skill/docs/API/sketch/core.md +0 -73
- package/dist-skill/docs/API/sketch/extrude.md +0 -62
- package/dist-skill/docs/API/sketch/on-face.md +0 -104
- package/dist-skill/docs/API/sketch/operations.md +0 -78
- package/dist-skill/docs/API/sketch/path.md +0 -75
- package/dist-skill/docs/API/sketch/primitives.md +0 -146
- package/dist-skill/docs/API/sketch/regions.md +0 -80
- package/dist-skill/docs/API/sketch/text.md +0 -108
- package/dist-skill/docs/API/sketch/transforms.md +0 -65
- package/dist-skill/docs/API/toolbox/fasteners.md +0 -129
- package/dist-skill/docs/INDEX.md +0 -94
- package/dist-skill/docs/RELEASING.md +0 -55
- package/dist-skill/docs/cli-monetization.md +0 -111
- package/dist-skill/docs/deployment.md +0 -281
- package/dist-skill/docs/generated/concepts.md +0 -2112
- package/dist-skill/docs/internals/shape-from-slices.md +0 -152
- package/dist-skill/docs/platform/admin.md +0 -45
- package/dist-skill/docs/platform/architecture.md +0 -79
- package/dist-skill/docs/platform/auth.md +0 -110
- package/dist-skill/docs/platform/email.md +0 -67
- package/dist-skill/docs/platform/projects.md +0 -111
- package/dist-skill/docs/platform/sharing.md +0 -90
- package/dist-skill/docs/runbook.md +0 -345
|
@@ -5,109 +5,337 @@ skill-order: 100
|
|
|
5
5
|
|
|
6
6
|
# Curves & Surfacing
|
|
7
7
|
|
|
8
|
-
> **Auto-generated** from `src/forge/forge-public-api.ts`. Do not edit by hand — run `npm run gen:docs` to regenerate.
|
|
9
|
-
|
|
10
8
|
Smooth curves, lofted surfaces, swept solids, and splines.
|
|
11
9
|
|
|
10
|
+
## Contents
|
|
11
|
+
|
|
12
|
+
- [Curves & Surfacing](#curves-surfacing) — `hermiteTransitionG2`, `nurbs3d`, `spline2d`, `spline3d`, `loft`, `loftAlongSpine`, `sweep`, `variableSweep`, `nurbsSurface`, `surfacePatch`, `transitionCurve`, `transitionSurface`, `connectEdges`
|
|
13
|
+
- [Curve3D](#curve3d)
|
|
14
|
+
- [NurbsCurve3D](#nurbscurve3d)
|
|
15
|
+
- [NurbsSurface](#nurbssurface)
|
|
16
|
+
- [PathBuilder](#pathbuilder) — Line Segments, Arcs, Curves, Closing & Output
|
|
17
|
+
- [HermiteCurve3D](#hermitecurve3d)
|
|
18
|
+
- [QuinticHermiteCurve3D](#quintichermitecurve3d)
|
|
19
|
+
|
|
12
20
|
## Functions
|
|
13
21
|
|
|
14
22
|
### Curves & Surfacing
|
|
15
23
|
|
|
16
|
-
Create
|
|
24
|
+
#### `hermiteTransitionG2()` — Create a quintic Hermite transition curve between two edge endpoints (G2 continuity).
|
|
17
25
|
|
|
18
|
-
|
|
26
|
+
The curve starts at `a.point` tangent to `a.tangent` with curvature `a.curvature`, and ends at `b.point` tangent to `b.tangent` with curvature `b.curvature`, with smooth G2-continuous interpolation matching position, tangent, and curvature.
|
|
19
27
|
|
|
20
28
|
```ts
|
|
21
|
-
|
|
29
|
+
hermiteTransitionG2(a: QuinticHermiteCurveEndpoint, b: QuinticHermiteCurveEndpoint): QuinticHermiteCurve3D
|
|
22
30
|
```
|
|
23
31
|
|
|
24
|
-
|
|
32
|
+
**`QuinticHermiteCurveEndpoint`**
|
|
33
|
+
|
|
34
|
+
| Option | Type | Description |
|
|
35
|
+
|--------|------|-------------|
|
|
36
|
+
| `point` | `Vec3` | Position |
|
|
37
|
+
| `tangent` | `Vec3` | Tangent direction (will be normalized internally) |
|
|
38
|
+
| `curvature?` | `Vec3` | Second derivative / curvature vector. Default [0, 0, 0]. |
|
|
39
|
+
| `weight?` | `number` | Weight: scales tangent magnitude relative to chord length. Default 1.0. |
|
|
40
|
+
|
|
41
|
+
#### `nurbs3d()` — Create a NURBS curve from control points.
|
|
25
42
|
|
|
26
|
-
|
|
43
|
+
With default options, creates a cubic non-rational B-spline with uniform clamped knots. Set `weights` for rational curves (exact circles, conics). Set `degree` for linear (1), quadratic (2), cubic (3), or higher-order curves.
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
// Simple cubic B-spline through control points
|
|
47
|
+
const curve = nurbs3d([[0,0,0], [10,5,0], [20,-5,10], [30,0,5]]);
|
|
48
|
+
const tube = sweep(circle(2), curve);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```js
|
|
52
|
+
// Rational quadratic — exact circular arc
|
|
53
|
+
const arc = nurbs3d(
|
|
54
|
+
[[10,0,0], [10,10,0], [0,10,0]],
|
|
55
|
+
{ degree: 2, weights: [1, Math.SQRT1_2, 1] }
|
|
56
|
+
);
|
|
57
|
+
```
|
|
27
58
|
|
|
28
59
|
```ts
|
|
29
|
-
|
|
30
|
-
/** Closed loop (default true). */
|
|
31
|
-
closed?: boolean;
|
|
32
|
-
/** Catmull-Rom tension in [0, 1]. 0 = very round, 1 = linear-ish. Default 0.5. */
|
|
33
|
-
tension?: number;
|
|
34
|
-
/** Samples per segment (minimum 3). Default 16. */
|
|
35
|
-
samplesPerSegment?: number;
|
|
36
|
-
/** For open splines, provide stroke width to return a solid Sketch. If omitted for open splines, an error is thrown. */
|
|
37
|
-
strokeWidth?: number;
|
|
38
|
-
/** Stroke join for open splines. Default 'Round'. */
|
|
39
|
-
join?: "Round" | "Square";
|
|
40
|
-
}
|
|
60
|
+
nurbs3d(points: Vec3[], options?: NurbsCurve3DOptions): NurbsCurve3D
|
|
41
61
|
```
|
|
42
62
|
|
|
43
|
-
|
|
63
|
+
**`NurbsCurve3DOptions`**
|
|
64
|
+
|
|
65
|
+
| Option | Type | Description |
|
|
66
|
+
|--------|------|-------------|
|
|
67
|
+
| `degree?` | `number` | Polynomial degree (default 3 = cubic). Must be ≥ 1. |
|
|
68
|
+
| `weights?` | `number[]` | Rational weights, one per control point (default: all 1.0 = non-rational). |
|
|
69
|
+
| `knots?` | `number[]` | Knot vector (default: uniform clamped). Must have length = controlPoints.length + degree + 1. |
|
|
70
|
+
| `closed?` | `boolean` | Whether the curve is closed/periodic (default false). |
|
|
71
|
+
|
|
72
|
+
#### `spline2d()` — Build a smooth Catmull-Rom spline sketch from 2D control points.
|
|
44
73
|
|
|
45
|
-
|
|
74
|
+
A closed spline (default) returns a filled profile. An open spline requires a strokeWidth option to produce a solid sketch. Use tension (0..1, default 0.5) to control curve tightness.
|
|
46
75
|
|
|
47
76
|
```ts
|
|
48
|
-
|
|
77
|
+
spline2d(points: Vec2[], options?: Spline2DOptions): Sketch
|
|
49
78
|
```
|
|
50
79
|
|
|
51
|
-
|
|
80
|
+
**`Spline2DOptions`**
|
|
81
|
+
|
|
82
|
+
| Option | Type | Description |
|
|
83
|
+
|--------|------|-------------|
|
|
84
|
+
| `closed?` | `boolean` | Closed loop (default true). |
|
|
85
|
+
| `tension?` | `number` | Catmull-Rom tension in [0, 1]. 0 = very round, 1 = linear-ish. Default 0.5. |
|
|
86
|
+
| `samplesPerSegment?` | `number` | Samples per segment (minimum 3). Default 16. |
|
|
87
|
+
| `strokeWidth?` | `number` | For open splines, provide stroke width to return a solid Sketch. If omitted for open splines, an error is thrown. |
|
|
88
|
+
| `join?` | `"Round" | "Square"` | Stroke join for open splines. Default 'Round'. |
|
|
89
|
+
|
|
90
|
+
#### `spline3d()` — Create a reusable 3D spline curve object (Catmull-Rom).
|
|
52
91
|
|
|
53
|
-
|
|
92
|
+
The returned Curve3D provides sample(), pointAt(t), tangentAt(t), and length() for downstream use in sweep() or manual path operations.
|
|
54
93
|
|
|
55
94
|
```ts
|
|
56
|
-
|
|
57
|
-
/** Closed loop (default false). */
|
|
58
|
-
closed?: boolean;
|
|
59
|
-
/** Catmull-Rom tension in [0, 1]. 0 = very round, 1 = linear-ish. Default 0.5. */
|
|
60
|
-
tension?: number;
|
|
61
|
-
}
|
|
95
|
+
spline3d(points: Vec3[], options?: Spline3DOptions): Curve3D
|
|
62
96
|
```
|
|
63
97
|
|
|
64
|
-
|
|
98
|
+
**`Spline3DOptions`**
|
|
99
|
+
- `closed?: boolean` — Closed loop (default false).
|
|
100
|
+
- `tension?: number` — Catmull-Rom tension in [0, 1]. 0 = very round, 1 = linear-ish. Default 0.5.
|
|
65
101
|
|
|
66
|
-
#### `loft()`
|
|
102
|
+
#### `loft()` — Loft between multiple sketches along Z stations.
|
|
103
|
+
|
|
104
|
+
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 export through the OCCT exact route.
|
|
105
|
+
|
|
106
|
+
Performance note: loft is significantly heavier than primitive/extrude/revolve. If the part is axis-symmetric (bottles, vases, knobs), prefer revolve().
|
|
67
107
|
|
|
68
108
|
```ts
|
|
69
109
|
loft(profiles: Sketch[], heights: number[], options?: LoftOptions): Shape
|
|
70
110
|
```
|
|
71
111
|
|
|
72
|
-
|
|
112
|
+
**`LoftOptions`**
|
|
113
|
+
- `edgeLength?: number` — Marching-grid edge length for level-set meshing. Smaller = finer.
|
|
114
|
+
- `boundsPadding?: number` — Optional extra bounds padding.
|
|
115
|
+
|
|
116
|
+
#### `loftAlongSpine()` — Loft between multiple profiles positioned along an arbitrary 3D spine curve.
|
|
73
117
|
|
|
74
|
-
|
|
118
|
+
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.
|
|
119
|
+
|
|
120
|
+
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].
|
|
121
|
+
|
|
122
|
+
Internally uses variableSweep infrastructure with SDF interpolation.
|
|
123
|
+
|
|
124
|
+
Performance note: uses level-set meshing, heavier than simple loft().
|
|
75
125
|
|
|
76
126
|
```ts
|
|
77
|
-
|
|
78
|
-
/** Marching-grid edge length for level-set meshing. Smaller = finer. */
|
|
79
|
-
edgeLength?: number;
|
|
80
|
-
/** Optional extra bounds padding. */
|
|
81
|
-
boundsPadding?: number;
|
|
82
|
-
}
|
|
127
|
+
loftAlongSpine(profiles: Sketch[], spine: Curve3D | Vec3[], tValues: number[], options?: LoftAlongSpineOptions): Shape
|
|
83
128
|
```
|
|
84
129
|
|
|
85
|
-
|
|
130
|
+
**`LoftAlongSpineOptions`**
|
|
131
|
+
|
|
132
|
+
| Option | Type | Description |
|
|
133
|
+
|--------|------|-------------|
|
|
134
|
+
| `samples?` | `number` | Number of samples when spine is a Curve3D. Default 48. |
|
|
135
|
+
| `edgeLength?` | `number` | Marching-grid edge length for level-set meshing. Smaller = finer. |
|
|
136
|
+
| `boundsPadding?` | `number` | Optional extra bounds padding. |
|
|
137
|
+
| `up?` | `Vec3` | Preferred "up" vector for local profile frame. Auto fallback is used near parallel segments. |
|
|
86
138
|
|
|
87
139
|
#### `sweep()`
|
|
88
140
|
|
|
89
141
|
```ts
|
|
90
|
-
sweep(profile: Sketch, path:
|
|
142
|
+
sweep(profile: Sketch, path: SweepPathInput, options?: SweepOptions): Shape
|
|
91
143
|
```
|
|
92
144
|
|
|
93
|
-
|
|
145
|
+
**`SweepOptions`**
|
|
146
|
+
|
|
147
|
+
| Option | Type | Description |
|
|
148
|
+
|--------|------|-------------|
|
|
149
|
+
| `samples?` | `number` | Number of samples when path is a Curve3D. Default 48. |
|
|
150
|
+
| `edgeLength?` | `number` | Marching-grid edge length for level-set meshing. Smaller = finer. |
|
|
151
|
+
| `boundsPadding?` | `number` | Optional extra bounds padding. |
|
|
152
|
+
| `up?` | `Vec3` | Preferred "up" vector for local profile frame. Auto fallback is used near parallel segments. |
|
|
153
|
+
|
|
154
|
+
#### `variableSweep()` — Sweep a variable cross-section along a 3D spine curve.
|
|
94
155
|
|
|
95
|
-
|
|
156
|
+
Unlike sweep(), which uses a single constant profile, variableSweep() interpolates between multiple profiles at different stations along the spine. This enables organic shapes like tapering tubes, bone-like structures, and sculptural forms.
|
|
157
|
+
|
|
158
|
+
Each section specifies a t parameter (0 = start, 1 = end of spine) and a 2D profile sketch. The SDF-based level-set mesher smoothly blends between profiles at intermediate positions.
|
|
159
|
+
|
|
160
|
+
Performance note: like sweep(), this uses level-set meshing internally.
|
|
96
161
|
|
|
97
162
|
```ts
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
163
|
+
variableSweep(spine: SweepPathInput, sections: VariableSweepSection[], options?: VariableSweepOptions): Shape
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**`VariableSweepSection`**
|
|
167
|
+
- `t: number` — Parameter along the spine (0 = start, 1 = end).
|
|
168
|
+
- `profile: Sketch` — Cross-section profile at this station.
|
|
169
|
+
|
|
170
|
+
**`VariableSweepOptions`**
|
|
171
|
+
|
|
172
|
+
| Option | Type | Description |
|
|
173
|
+
|--------|------|-------------|
|
|
174
|
+
| `samples?` | `number` | Number of samples when spine is a Curve3D. Default 48. |
|
|
175
|
+
| `edgeLength?` | `number` | Marching-grid edge length for level-set meshing. Smaller = finer. |
|
|
176
|
+
| `boundsPadding?` | `number` | Optional extra bounds padding. |
|
|
177
|
+
| `up?` | `Vec3` | Preferred "up" vector for local profile frame. Auto fallback is used near parallel segments. |
|
|
178
|
+
|
|
179
|
+
#### `nurbsSurface()` — Create a NURBS surface from a grid of control points.
|
|
180
|
+
|
|
181
|
+
The control grid is indexed as `controlGrid[u][v]` — each row is a curve in the V direction, and columns trace curves in the U direction.
|
|
182
|
+
|
|
183
|
+
With default options, creates a bicubic non-rational B-spline surface with uniform clamped knots.
|
|
184
|
+
|
|
185
|
+
```js
|
|
186
|
+
// Simple 4×4 control grid — a gently curved surface
|
|
187
|
+
const grid = [
|
|
188
|
+
[[0,0,0], [10,0,2], [20,0,2], [30,0,0]],
|
|
189
|
+
[[0,10,1], [10,10,5], [20,10,5], [30,10,1]],
|
|
190
|
+
[[0,20,1], [10,20,5], [20,20,5], [30,20,1]],
|
|
191
|
+
[[0,30,0], [10,30,2], [20,30,2], [30,30,0]],
|
|
192
|
+
];
|
|
193
|
+
const surface = nurbsSurface(grid, { thickness: 2 });
|
|
108
194
|
```
|
|
109
195
|
|
|
110
|
-
|
|
196
|
+
```ts
|
|
197
|
+
nurbsSurface(controlGrid: Vec3[][], options?: NurbsSurfaceOptions): Shape
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**`NurbsSurfaceOptions`**
|
|
201
|
+
|
|
202
|
+
| Option | Type | Description |
|
|
203
|
+
|--------|------|-------------|
|
|
204
|
+
| `degreeU?` | `number` | Degree in U direction (default 3). |
|
|
205
|
+
| `degreeV?` | `number` | Degree in V direction (default 3). |
|
|
206
|
+
| `weights?` | `number[][]` | Weights grid — same dimensions as controlGrid (default: all 1.0). |
|
|
207
|
+
| `knotsU?` | `number[]` | Knot vector in U direction (default: uniform clamped). |
|
|
208
|
+
| `knotsV?` | `number[]` | Knot vector in V direction (default: uniform clamped). |
|
|
209
|
+
| `thickness?` | `number` | Sheet thickness — if > 0, thickens the surface into a solid (default 0 = surface only). |
|
|
210
|
+
| `resolution?` | `number` | Tessellation resolution — points per direction (default 32). |
|
|
211
|
+
|
|
212
|
+
#### `surfacePatch()` — Create a smooth surface patch from 4 boundary curves (Coons patch).
|
|
213
|
+
|
|
214
|
+
The four curves form the boundary of a quadrilateral patch:
|
|
215
|
+
|
|
216
|
+
- bottom: u=0..1 at v=0 (from corner00 to corner10)
|
|
217
|
+
- top: u=0..1 at v=1 (from corner01 to corner11)
|
|
218
|
+
- left: v=0..1 at u=0 (from corner00 to corner01)
|
|
219
|
+
- right: v=0..1 at u=1 (from corner10 to corner11)
|
|
220
|
+
|
|
221
|
+
The interior is filled using bilinear Coons patch interpolation: P(u,v) = Lc(u,v) + Ld(u,v) - B(u,v)
|
|
222
|
+
|
|
223
|
+
The result is a thin solid created by offsetting the surface mesh along its normals by the specified thickness.
|
|
224
|
+
|
|
225
|
+
Note: curves should meet at corners. Small gaps are tolerated.
|
|
226
|
+
|
|
227
|
+
```ts
|
|
228
|
+
surfacePatch(curves: { ... }, options?: SurfacePatchOptions): Shape
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**`SurfacePatchOptions`**
|
|
232
|
+
- `resolution?: number` — Number of samples along each direction. Default 24.
|
|
233
|
+
- `thickness?: number` — Thickness of the generated solid. Default 0.5.
|
|
234
|
+
|
|
235
|
+
#### `transitionCurve()` — Create a smooth transition curve between two edges.
|
|
236
|
+
|
|
237
|
+
Returns a `HermiteCurve3D` that starts at `edgeA.point` tangent to `edgeA.tangent` and ends at `edgeB.point` tangent to `edgeB.tangent`.
|
|
238
|
+
|
|
239
|
+
The curve maintains G1 continuity (matching tangent direction) at both endpoints. Weight parameters control the shape of the transition.
|
|
240
|
+
|
|
241
|
+
```js
|
|
242
|
+
```js
|
|
243
|
+
|
|
244
|
+
// Connect two edges with a balanced transition const curve = transitionCurve( { point: [0, 0, 0], tangent: [1, 0, 0] }, { point: [10, 5, 0], tangent: [1, 0, 0] }, );
|
|
245
|
+
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
// Weighted: curve hugs edge A longer
|
|
249
|
+
const weighted = transitionCurve(
|
|
250
|
+
{ point: [0, 0, 0], tangent: [1, 0, 0] },
|
|
251
|
+
{ point: [10, 5, 0], tangent: [1, 0, 0] },
|
|
252
|
+
{ weightA: 2.0, weightB: 0.5 },
|
|
253
|
+
);
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
```ts
|
|
257
|
+
transitionCurve(edgeA: TransitionEdge, edgeB: TransitionEdge, options?: TransitionCurveOptions): HermiteCurve3D
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
**`TransitionEdge`**
|
|
261
|
+
- `point: Vec3` — Connection point on the edge. Can be any point along the edge where the transition should connect.
|
|
262
|
+
- `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).
|
|
263
|
+
- `normal?: Vec3` — Surface normal at the connection point (optional). Used as a hint for the sweep frame's up vector.
|
|
264
|
+
|
|
265
|
+
**`TransitionCurveOptions`**
|
|
266
|
+
- `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
|
|
267
|
+
- `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
|
|
268
|
+
- `samples?: number` — Number of sample points for the output polyline. Default 64. Higher values give smoother curves at the cost of more geometry.
|
|
269
|
+
|
|
270
|
+
#### `transitionSurface()` — Create a solid transition surface between two edges by sweeping a profile along a Hermite transition curve.
|
|
271
|
+
|
|
272
|
+
This produces a watertight solid that smoothly connects the two edges. Works with both Manifold and OCCT backends.
|
|
273
|
+
|
|
274
|
+
```js
|
|
275
|
+
```js
|
|
276
|
+
|
|
277
|
+
// Circular tube connecting two edges const tube = transitionSurface( { point: [0, 0, 0], tangent: [1, 0, 0] }, { point: [10, 5, 3], tangent: [0, 1, 0] }, { radius: 0.5 }, );
|
|
278
|
+
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
// Custom profile with weights
|
|
282
|
+
const custom = transitionSurface(
|
|
283
|
+
{ point: [0, 0, 0], tangent: [1, 0, 0] },
|
|
284
|
+
{ point: [10, 5, 3], tangent: [0, 1, 0] },
|
|
285
|
+
{ profile: mySketch, weightA: 1.5, weightB: 0.8 },
|
|
286
|
+
);
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
```ts
|
|
290
|
+
transitionSurface(edgeA: TransitionEdge, edgeB: TransitionEdge, options?: TransitionSurfaceOptions): Shape
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
**`TransitionSurfaceOptions`** extends TransitionCurveOptions
|
|
295
|
+
|
|
296
|
+
| Option | Type | Description |
|
|
297
|
+
|--------|------|-------------|
|
|
298
|
+
| `profile?` | `Sketch` | Cross-section profile to sweep along the transition curve. If omitted, a circular profile with `radius` is used. |
|
|
299
|
+
| `radius?` | `number` | Radius of circular cross-section (used when `profile` is omitted). Default: 5% of chord length. |
|
|
300
|
+
| `up?` | `Vec3` | Preferred up vector for the sweep frame. Default: auto-detected. |
|
|
301
|
+
| `edgeLength?` | `number` | Edge length for level-set meshing. Smaller = finer. |
|
|
302
|
+
| `boundsPadding?` | `number` | Extra bounds padding for level-set meshing. |
|
|
303
|
+
| `width`, `height` | | — |
|
|
304
|
+
|
|
305
|
+
#### `connectEdges()` — Create a transition surface or solid bridge between two edge segments.
|
|
306
|
+
|
|
307
|
+
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.
|
|
308
|
+
|
|
309
|
+
```ts
|
|
310
|
+
connectEdges(edgeA: EdgeSegment, edgeB: EdgeSegment, options?: ConnectEdgesOptions): Shape
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
**`EdgeSegment`**
|
|
314
|
+
|
|
315
|
+
| Option | Type | Description |
|
|
316
|
+
|--------|------|-------------|
|
|
317
|
+
| `index` | `number` | Stable index within the extraction (deterministic for a given mesh). |
|
|
318
|
+
| `direction` | `Vec3` | Normalized direction from start → end. |
|
|
319
|
+
| `dihedralAngle` | `number` | Dihedral angle in degrees (0 = coplanar, 180 = knife edge). |
|
|
320
|
+
| `convex` | `boolean` | true = outside corner (convex), false = inside corner (concave). |
|
|
321
|
+
| `normalA` | `Vec3` | Normal of first adjacent face. |
|
|
322
|
+
| `normalB` | `Vec3` | Normal of second adjacent face (same as normalA for boundary edges). |
|
|
323
|
+
| `boundary` | `boolean` | true if this is a boundary (unmatched) edge — unusual for closed solids. |
|
|
324
|
+
| `start`, `end`, `midpoint`, `length` | | — |
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
**`ConnectEdgesOptions`** extends TransitionSurfaceOptions
|
|
328
|
+
|
|
329
|
+
| Option | Type | Description |
|
|
330
|
+
|--------|------|-------------|
|
|
331
|
+
| `endA?` | `EdgeEnd` | Which end of edge A to connect. Default: 'start'. |
|
|
332
|
+
| `endB?` | `EdgeEnd` | Which end of edge B to connect. Default: 'start'. |
|
|
333
|
+
| `tangentModeA?` | `TangentMode` | Tangent mode for edge A. Default: 'along'. |
|
|
334
|
+
| `tangentModeB?` | `TangentMode` | Tangent mode for edge B. Default: 'along'. |
|
|
335
|
+
| `tangentA?` | `Vec3` | Explicit tangent for edge A. |
|
|
336
|
+
| `tangentB?` | `Vec3` | Explicit tangent for edge B. |
|
|
337
|
+
| `flipA?` | `boolean` | Flip tangent A. |
|
|
338
|
+
| `flipB?` | `boolean` | Flip tangent B. |
|
|
111
339
|
|
|
112
340
|
---
|
|
113
341
|
|
|
@@ -119,94 +347,509 @@ interface SweepOptions {
|
|
|
119
347
|
|
|
120
348
|
| Property | Type | Description |
|
|
121
349
|
|----------|------|-------------|
|
|
122
|
-
| `points` | `Vec3
|
|
350
|
+
| `points` | `Vec3[]` | — |
|
|
123
351
|
| `closed` | `boolean` | — |
|
|
124
352
|
| `tension` | `number` | — |
|
|
125
353
|
|
|
126
354
|
**Methods:**
|
|
127
355
|
|
|
128
|
-
|
|
129
|
-
- `sample()` — sample(count?: number): Vec3$4[]
|
|
130
|
-
- `pointAt()` — pointAt(t: number): Vec3$4
|
|
131
|
-
- `tangentAt()` — tangentAt(t: number): Vec3$4
|
|
132
|
-
- `length()` — length(samples?: number): number
|
|
356
|
+
#### `sampleBySegment()` — Sample the curve with a fixed number of points per segment.
|
|
133
357
|
|
|
134
|
-
|
|
358
|
+
```ts
|
|
359
|
+
sampleBySegment(samplesPerSegment?: number): Vec3[]
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
#### `sample()` — Sample the curve to an approximate total point count.
|
|
363
|
+
|
|
364
|
+
```ts
|
|
365
|
+
sample(count?: number): Vec3[]
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
#### `pointAt()` — Return the position on the curve at normalized parameter `t` in `[0, 1]`. O(1), no allocations.
|
|
369
|
+
|
|
370
|
+
```ts
|
|
371
|
+
pointAt(t: number): Vec3
|
|
372
|
+
```
|
|
135
373
|
|
|
136
|
-
|
|
374
|
+
#### `tangentAt()` — Return a unit tangent vector at normalized parameter `t` in `[0, 1]`. O(1), analytical derivative.
|
|
375
|
+
|
|
376
|
+
```ts
|
|
377
|
+
tangentAt(t: number): Vec3
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
#### `length()` — Approximate the curve length by polyline sampling.
|
|
381
|
+
|
|
382
|
+
```ts
|
|
383
|
+
length(samples?: number): number
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
### `NurbsCurve3D`
|
|
137
387
|
|
|
138
388
|
**Properties:**
|
|
139
389
|
|
|
140
390
|
| Property | Type | Description |
|
|
141
391
|
|----------|------|-------------|
|
|
142
|
-
| `
|
|
143
|
-
| `
|
|
144
|
-
| `
|
|
145
|
-
| `
|
|
146
|
-
| `
|
|
392
|
+
| `controlPoints` | `Vec3[]` | — |
|
|
393
|
+
| `weights` | `number[]` | — |
|
|
394
|
+
| `knots` | `number[]` | — |
|
|
395
|
+
| `degree` | `number` | — |
|
|
396
|
+
| `closed` | `boolean` | — |
|
|
147
397
|
|
|
148
398
|
**Methods:**
|
|
149
399
|
|
|
150
|
-
|
|
151
|
-
- `tangentAt()` — Evaluate tangent (first derivative) at parameter t ∈ [0, 1]
|
|
152
|
-
- `curvatureAt()` — Evaluate curvature vector (second derivative) at parameter t ∈ [0, 1]
|
|
153
|
-
- `sample()` — Sample the curve as a polyline of evenly-spaced parameter values.
|
|
154
|
-
- `length()` — Approximate arc length by sampling.
|
|
155
|
-
- `sampleAdaptive()` — Sample with adaptive density — more points where curvature is higher. Returns at least `minCount` points, up to `maxCount`.
|
|
156
|
-
- `toPolyline()` — Convert to a format compatible with sweep() path input.
|
|
400
|
+
#### `pointAt()` — Evaluate the curve at parameter t ∈ [0, 1]. Uses De Boor's algorithm — exact, O(degree²).
|
|
157
401
|
|
|
158
|
-
|
|
402
|
+
```ts
|
|
403
|
+
pointAt(t: number): Vec3
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
#### `tangentAt()` — Evaluate the unit tangent vector at parameter t ∈ [0, 1].
|
|
407
|
+
|
|
408
|
+
```ts
|
|
409
|
+
tangentAt(t: number): Vec3
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
#### `sample()` — Sample the curve uniformly at `count` points.
|
|
159
413
|
|
|
160
|
-
|
|
414
|
+
```ts
|
|
415
|
+
sample(count?: number): Vec3[]
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
#### `sampleAdaptive()` — Sample with adaptive density — more points in high-curvature regions.
|
|
419
|
+
|
|
420
|
+
```ts
|
|
421
|
+
sampleAdaptive(minCount?: number, maxCount?: number): Vec3[]
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
#### `length()` — Approximate arc length by summing polyline segment lengths.
|
|
425
|
+
|
|
426
|
+
```ts
|
|
427
|
+
length(samples?: number): number
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
#### `toPolyline()` — Convert to a format compatible with sweep() path input.
|
|
431
|
+
|
|
432
|
+
```ts
|
|
433
|
+
toPolyline(samples?: number): Vec3[]
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
### `NurbsSurface`
|
|
161
437
|
|
|
162
438
|
**Properties:**
|
|
163
439
|
|
|
164
440
|
| Property | Type | Description |
|
|
165
441
|
|----------|------|-------------|
|
|
166
|
-
| `
|
|
167
|
-
| `
|
|
168
|
-
| `
|
|
169
|
-
| `
|
|
170
|
-
| `
|
|
171
|
-
| `
|
|
172
|
-
| `
|
|
442
|
+
| `controlGrid` | `Vec3[][]` | — |
|
|
443
|
+
| `weightsGrid` | `number[][]` | — |
|
|
444
|
+
| `knotsU` | `number[]` | — |
|
|
445
|
+
| `knotsV` | `number[]` | — |
|
|
446
|
+
| `degreeU` | `number` | — |
|
|
447
|
+
| `degreeV` | `number` | — |
|
|
448
|
+
| `nU` | `number` | — |
|
|
449
|
+
| `nV` | `number` | — |
|
|
173
450
|
|
|
174
451
|
**Methods:**
|
|
175
452
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
453
|
+
#### `pointAt()` — Evaluate the surface at parameters (u, v) ∈ [0, 1]². Uses tensor product evaluation: evaluate basis functions in U and V independently.
|
|
454
|
+
|
|
455
|
+
```ts
|
|
456
|
+
pointAt(u: number, v: number): Vec3
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
#### `normalAt()` — Evaluate the surface normal at (u, v) via cross product of partial derivatives.
|
|
460
|
+
|
|
461
|
+
```ts
|
|
462
|
+
normalAt(u: number, v: number): Vec3
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
#### `tessellate()` — Tessellate the surface into a triangle mesh. Returns positions, normals, and triangle indices.
|
|
466
|
+
|
|
467
|
+
```ts
|
|
468
|
+
tessellate(resU?: number, resV?: number): { positions: Vec3[]; normals: Vec3[]; indices: number[]; }
|
|
469
|
+
```
|
|
183
470
|
|
|
184
471
|
### `PathBuilder`
|
|
185
472
|
|
|
473
|
+
**Line Segments**
|
|
474
|
+
|
|
475
|
+
#### `moveTo()` — Move the cursor to an absolute position without drawing a segment.
|
|
476
|
+
|
|
477
|
+
When called after the initial [`path()`](/docs/sketch#path), this establishes the start of the outline. Calling `moveTo` again mid-path starts a new sub-path (hole in `close()`, separate segment for [`stroke()`](/docs/sketch#stroke)).
|
|
478
|
+
|
|
479
|
+
```ts
|
|
480
|
+
moveTo(x: number, y: number): this
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
#### `lineTo()` — Draw a straight line from the current cursor to an absolute position.
|
|
484
|
+
|
|
485
|
+
```ts
|
|
486
|
+
lineTo(x: number, y: number): this
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
#### `lineH()` — Draw a horizontal line segment by `dx` units from the current cursor.
|
|
490
|
+
|
|
491
|
+
Positive `dx` moves right; negative moves left.
|
|
492
|
+
|
|
493
|
+
```ts
|
|
494
|
+
lineH(dx: number): this
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
#### `lineV()` — Draw a vertical line segment by `dy` units from the current cursor.
|
|
498
|
+
|
|
499
|
+
Positive `dy` moves up; negative moves down.
|
|
500
|
+
|
|
501
|
+
```ts
|
|
502
|
+
lineV(dy: number): this
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
#### `lineAngled()` — Draw a line at the given angle and length from the current cursor.
|
|
506
|
+
|
|
507
|
+
Angle convention: `0°` points right (+X), `90°` points up (+Y).
|
|
508
|
+
|
|
509
|
+
```ts
|
|
510
|
+
// L-bracket with angled return
|
|
511
|
+
path().moveTo(0, 0).lineH(50).lineV(-70).lineAngled(20, 235).stroke(4);
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
```ts
|
|
515
|
+
lineAngled(length: number, degrees: number): this
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
**Arcs**
|
|
519
|
+
|
|
520
|
+
#### `arc()` — Draw an arc defined by center, radius, and angle range (no trig needed). If the path has no segments yet, automatically moves to the arc start. Positive sweep (startDeg < endDeg) = CCW, negative = CW.
|
|
521
|
+
|
|
522
|
+
```js
|
|
523
|
+
// Arc centered at (10, 0), radius 50, from -30° to +30°
|
|
524
|
+
path().arc(10, 0, 50, -30, 30).stroke(8, 'Round')
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
```ts
|
|
528
|
+
arc(cx: number, cy: number, radius: number, startDeg: number, endDeg: number): this
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
#### `arcTo()` — Draw a circular arc from the current position to (x, y) with the given radius. `clockwise=true` → arc curves to the right of the start→end direction. `clockwise=false` → arc curves to the left of the start→end direction.
|
|
532
|
+
|
|
533
|
+
```ts
|
|
534
|
+
arcTo(x: number, y: number, radius: number, clockwise?: boolean): this
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
#### `tangentArcTo()` — G1-continuous arc — radius derived from current tangent + endpoint. Throws if endpoint is collinear with current direction.
|
|
538
|
+
|
|
539
|
+
```ts
|
|
540
|
+
tangentArcTo(x: number, y: number): this
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
**Curves**
|
|
544
|
+
|
|
545
|
+
#### `bezierTo()` — Cubic bezier from current position to (x, y) via two control points.
|
|
546
|
+
|
|
547
|
+
```ts
|
|
548
|
+
bezierTo(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number): this
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
**Closing & Output**
|
|
552
|
+
|
|
553
|
+
#### `close()` — Close the path and return a filled [`Sketch`](/docs/sketch#sketch).
|
|
554
|
+
|
|
555
|
+
The winding of the polygon is automatically corrected to CCW (the expected orientation for ForgeCAD sketches). If the path contains multiple sub-paths (started with subsequent `moveTo` calls), the first sub-path is the outer contour and subsequent sub-paths become holes subtracted from it.
|
|
556
|
+
|
|
557
|
+
Edge labels (assigned with `.label('name')`) are transferred to the resulting sketch and propagate through `extrude()`, `revolve()`, `loft()`, and `sweep()` into named faces on the resulting [`Shape`](/docs/core#shape).
|
|
558
|
+
|
|
559
|
+
```ts
|
|
560
|
+
const triangle = path().moveTo(0, 0).lineH(50).lineV(30).close();
|
|
561
|
+
|
|
562
|
+
// With a hole (second sub-path)
|
|
563
|
+
const frame = path()
|
|
564
|
+
.moveTo(0, 0).lineH(40).lineV(30).lineH(-40).close(); // outer
|
|
565
|
+
// (hole would be added with another moveTo and line sequence before close)
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
```ts
|
|
569
|
+
close(): Sketch
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
#### `closeLabel()` — Label the closing segment and close the path. Shorthand for labeling the implicit line from the last point back to the start, then closing.
|
|
573
|
+
|
|
574
|
+
```ts
|
|
575
|
+
closeLabel(name: string): Sketch
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
#### [`stroke()`](/docs/sketch#stroke) — Thicken an open polyline (centerline) into a solid filled profile with uniform width.
|
|
579
|
+
|
|
580
|
+
Expands the path into a closed profile `width` units wide (half-width on each side of the centerline). Use `'Round'` for ribs, wire traces, and organic profiles — it adds semicircular endcaps and rounds joins. Use `'Square'` (default) for sharp miter joins without endcaps.
|
|
581
|
+
|
|
582
|
+
Not the same as rounding corners of a closed polygon — for mixed sharp-and-rounded outlines, build the polygon first and apply [`filletCorners()`](/docs/core#filletcorners).
|
|
583
|
+
|
|
584
|
+
```ts
|
|
585
|
+
// Square-join L-bracket
|
|
586
|
+
const bracket = path().moveTo(0, 0).lineH(50).lineV(-70).lineAngled(20, 235).stroke(4);
|
|
587
|
+
|
|
588
|
+
// Round-join rib
|
|
589
|
+
const rib = path().moveTo(0, 0).lineH(60).stroke(6, 'Round');
|
|
590
|
+
|
|
591
|
+
// Equivalent standalone form
|
|
592
|
+
const wire = stroke([[0, 0], [50, 0], [50, -70]], 4);
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
and semicircular endcaps.
|
|
596
|
+
|
|
597
|
+
```ts
|
|
598
|
+
stroke(width: number, join?: "Round" | "Square"): Sketch
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
#### `label()` — Label the most recently added segment. Labels are born here and grow into face names when the sketch is extruded, lofted, swept, or revolved.
|
|
602
|
+
|
|
603
|
+
Labels must be unique within a path. Each segment can have at most one label.
|
|
604
|
+
|
|
605
|
+
```ts
|
|
606
|
+
label(name: string): this
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
**Other**
|
|
610
|
+
|
|
611
|
+
#### `getX()` — Current cursor X position.
|
|
612
|
+
|
|
613
|
+
```ts
|
|
614
|
+
getX(): number
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
#### `getY()` — Current cursor Y position.
|
|
618
|
+
|
|
619
|
+
```ts
|
|
620
|
+
getY(): number
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
#### `lineBy()` — Draw a line by a relative `(dx, dy)` displacement from the current cursor.
|
|
624
|
+
|
|
625
|
+
```ts
|
|
626
|
+
lineBy(dx: number, dy: number): this
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
#### `arcBy()` — Draw an arc to a point offset from the current cursor.
|
|
630
|
+
|
|
631
|
+
```ts
|
|
632
|
+
arcBy(dx: number, dy: number, radius: number, clockwise?: boolean): this
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
#### `bezierBy()` — Draw a cubic Bezier using control points relative to the current cursor.
|
|
636
|
+
|
|
637
|
+
```ts
|
|
638
|
+
bezierBy(dcp1x: number, dcp1y: number, dcp2x: number, dcp2y: number, dx: number, dy: number): this
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
#### `arcAround()` — Arc around a known center point, sweeping by the given angle. Radius is derived from the distance between the current position and the center. Positive sweep = CCW (math convention), negative = CW.
|
|
642
|
+
|
|
643
|
+
```js
|
|
644
|
+
// Arc 90° CCW around (50, 50)
|
|
645
|
+
path().moveTo(70, 50).arcAround(50, 50, 90)
|
|
646
|
+
// Arc 45° CW around the origin
|
|
647
|
+
path().moveTo(10, 0).arcAround(0, 0, -45)
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
```ts
|
|
651
|
+
arcAround(cx: number, cy: number, sweepDeg: number): this
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
#### `arcAroundRelative()` — Arc around a center point given as an offset from the current position. `(dx, dy)` is the vector from the current point to the center. Positive sweep = CCW (math convention), negative = CW.
|
|
655
|
+
|
|
656
|
+
```js
|
|
657
|
+
// Arc 90° CCW around a center 20 units to the right
|
|
658
|
+
path().moveTo(50, 50).arcAroundRelative(20, 0, 90)
|
|
659
|
+
// Equivalent to: path().moveTo(50, 50).arcAround(70, 50, 90)
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
```ts
|
|
663
|
+
arcAroundRelative(dx: number, dy: number, sweepDeg: number): this
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
#### `smoothCapTo()` — Smooth three-arc end cap from the current position to (endX, endY). Inserts: small corner arc → large cap arc → small corner arc, all G1-continuous.
|
|
667
|
+
|
|
668
|
+
```ts
|
|
669
|
+
smoothCapTo(endX: number, endY: number, cornerRadius: number, capRadius: number): this
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
#### `tangentBezierTo()` — G1-continuous cubic bezier — first control point is auto-derived from the current tangent direction. `weight` controls how far the auto-placed control point extends along the tangent (default: 1/3 of the chord).
|
|
673
|
+
|
|
674
|
+
The second control point `(cp2x, cp2y)` must be provided — it controls the arrival curvature. For a fully automatic smooth curve, see `smoothThrough`.
|
|
675
|
+
|
|
676
|
+
```ts
|
|
677
|
+
tangentBezierTo(cp2x: number, cp2y: number, x: number, y: number, weight?: number): this
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
#### `smoothThrough()` — Catmull-Rom spline through a list of waypoints from the current position. The current position is included as the first point. The last waypoint becomes the new cursor position.
|
|
681
|
+
|
|
682
|
+
```ts
|
|
683
|
+
smoothThrough(waypoints: [ number, number ][], tension?: number): this
|
|
684
|
+
```
|
|
685
|
+
|
|
686
|
+
#### `nurbsTo()` — Rational B-spline edge to (x, y) with explicit control points and weights.
|
|
687
|
+
|
|
688
|
+
The control points define the B-spline shape between the current position and (x, y). The current position is NOT included in `controlPoints` — it is automatically prepended. The endpoint (x, y) is the last control point.
|
|
689
|
+
|
|
690
|
+
```ts
|
|
691
|
+
nurbsTo(controlPoints: [ number, number ][], opts?: { weights?: number[]; degree?: number; }): this
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
#### `exactArcTo()` — Exact circular arc to (x, y) using a rational quadratic NURBS.
|
|
695
|
+
|
|
696
|
+
Unlike `arcTo()` which tessellates to a polyline, this preserves the exact arc definition. When extruded through the OCCT backend, it produces a true cylindrical face — not a faceted approximation.
|
|
697
|
+
|
|
698
|
+
```ts
|
|
699
|
+
exactArcTo(x: number, y: number, opts?: { radius?: number; clockwise?: boolean; }): this
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
#### [`fillet()`](/docs/core#fillet) — Round the last corner (the junction between the previous two segments) with a tangent arc of the given radius.
|
|
703
|
+
|
|
704
|
+
Must be called after at least two line/arc segments that form a corner. The fillet trims back both segments and inserts a tangent arc.
|
|
705
|
+
|
|
706
|
+
```js
|
|
707
|
+
path().moveTo(0,0).lineTo(10,0).lineTo(10,10).fillet(2).lineTo(0,10).close()
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
```ts
|
|
711
|
+
fillet(radius: number): this
|
|
712
|
+
```
|
|
713
|
+
|
|
714
|
+
#### [`chamfer()`](/docs/core#chamfer) — Chamfer the last corner with a straight cut of the given distance.
|
|
715
|
+
|
|
716
|
+
```js
|
|
717
|
+
path().moveTo(0,0).lineTo(10,0).lineTo(10,10).chamfer(2).lineTo(0,10).close()
|
|
718
|
+
```
|
|
719
|
+
|
|
720
|
+
```ts
|
|
721
|
+
chamfer(distance: number): this
|
|
722
|
+
```
|
|
723
|
+
|
|
724
|
+
#### `mirror()` — Mirror all existing segments across an axis and append the mirrored copy in reverse order, creating a symmetric path. The axis passes through the current cursor position.
|
|
725
|
+
|
|
726
|
+
'y' mirrors across the local Y-axis (flips X), or `[nx, ny]` for an arbitrary axis direction.
|
|
727
|
+
|
|
728
|
+
```js
|
|
729
|
+
// Build right half, mirror to get full symmetric profile
|
|
730
|
+
path().moveTo(0,0).lineTo(10,0).lineTo(10,5).mirror('x').close()
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
```ts
|
|
734
|
+
mirror(axis: "x" | "y" | [ number, number ]): this
|
|
735
|
+
```
|
|
736
|
+
|
|
737
|
+
#### `closeOffset()` — Close the path and return an offset version of the filled Sketch. Positive delta expands outward, negative shrinks inward.
|
|
738
|
+
|
|
739
|
+
```ts
|
|
740
|
+
closeOffset(delta: number, join?: "Round" | "Square" | "Miter"): Sketch
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
### `HermiteCurve3D`
|
|
744
|
+
|
|
745
|
+
**Properties:**
|
|
746
|
+
|
|
747
|
+
| Property | Type | Description |
|
|
748
|
+
|----------|------|-------------|
|
|
749
|
+
| `p0` | `Vec3` | Start position |
|
|
750
|
+
| `p1` | `Vec3` | End position |
|
|
751
|
+
| `t0` | `Vec3` | Scaled tangent at start (direction * weight * chordLength) |
|
|
752
|
+
| `t1` | `Vec3` | Scaled tangent at end (direction * weight * chordLength) |
|
|
753
|
+
| `chordLength` | `number` | Chord length (straight-line distance between endpoints) |
|
|
754
|
+
|
|
755
|
+
**Methods:**
|
|
756
|
+
|
|
757
|
+
#### `pointAt()` — Evaluate position at parameter t ∈ [0, 1]
|
|
758
|
+
|
|
759
|
+
```ts
|
|
760
|
+
pointAt(t: number): Vec3
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
#### `tangentAt()` — Evaluate tangent (first derivative) at parameter t ∈ [0, 1]
|
|
764
|
+
|
|
765
|
+
```ts
|
|
766
|
+
tangentAt(t: number): Vec3
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
#### `curvatureAt()` — Evaluate curvature vector (second derivative) at parameter t ∈ [0, 1]
|
|
770
|
+
|
|
771
|
+
```ts
|
|
772
|
+
curvatureAt(t: number): Vec3
|
|
773
|
+
```
|
|
774
|
+
|
|
775
|
+
#### `sample()` — Sample the curve as a polyline of evenly-spaced parameter values.
|
|
776
|
+
|
|
777
|
+
```ts
|
|
778
|
+
sample(count?: number): Vec3[]
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
#### `length()` — Approximate arc length by sampling.
|
|
782
|
+
|
|
783
|
+
```ts
|
|
784
|
+
length(samples?: number): number
|
|
785
|
+
```
|
|
786
|
+
|
|
787
|
+
#### `sampleAdaptive()` — Sample with adaptive density — more points where curvature is higher. Returns at least `minCount` points, up to `maxCount`.
|
|
788
|
+
|
|
789
|
+
```ts
|
|
790
|
+
sampleAdaptive(minCount?: number, maxCount?: number): Vec3[]
|
|
791
|
+
```
|
|
792
|
+
|
|
793
|
+
#### `toPolyline()` — Convert to a format compatible with sweep() path input.
|
|
794
|
+
|
|
795
|
+
```ts
|
|
796
|
+
toPolyline(samples?: number): Vec3[]
|
|
797
|
+
```
|
|
798
|
+
|
|
799
|
+
### `QuinticHermiteCurve3D`
|
|
800
|
+
|
|
801
|
+
**Properties:**
|
|
802
|
+
|
|
803
|
+
| Property | Type | Description |
|
|
804
|
+
|----------|------|-------------|
|
|
805
|
+
| `p0` | `Vec3` | Start position |
|
|
806
|
+
| `p1` | `Vec3` | End position |
|
|
807
|
+
| `t0` | `Vec3` | Scaled tangent at start (direction * weight * chordLength) |
|
|
808
|
+
| `t1` | `Vec3` | Scaled tangent at end (direction * weight * chordLength) |
|
|
809
|
+
| `c0` | `Vec3` | Scaled second derivative at start (curvature * weight² * chordLength²) |
|
|
810
|
+
| `c1` | `Vec3` | Scaled second derivative at end (curvature * weight² * chordLength²) |
|
|
811
|
+
| `chordLength` | `number` | Chord length (straight-line distance between endpoints) |
|
|
812
|
+
|
|
186
813
|
**Methods:**
|
|
187
814
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
815
|
+
#### `pointAt()` — Evaluate position at parameter t ∈ [0, 1]
|
|
816
|
+
|
|
817
|
+
```ts
|
|
818
|
+
pointAt(t: number): Vec3
|
|
819
|
+
```
|
|
820
|
+
|
|
821
|
+
#### `tangentAt()` — Evaluate tangent (first derivative, normalized) at parameter t ∈ [0, 1]
|
|
822
|
+
|
|
823
|
+
```ts
|
|
824
|
+
tangentAt(t: number): Vec3
|
|
825
|
+
```
|
|
826
|
+
|
|
827
|
+
#### `curvatureAt()` — Evaluate curvature vector (second derivative) at parameter t ∈ [0, 1]
|
|
828
|
+
|
|
829
|
+
```ts
|
|
830
|
+
curvatureAt(t: number): Vec3
|
|
831
|
+
```
|
|
832
|
+
|
|
833
|
+
#### `sample()` — Sample the curve as a polyline of evenly-spaced parameter values.
|
|
834
|
+
|
|
835
|
+
```ts
|
|
836
|
+
sample(count?: number): Vec3[]
|
|
837
|
+
```
|
|
838
|
+
|
|
839
|
+
#### `length()` — Approximate arc length by sampling.
|
|
840
|
+
|
|
841
|
+
```ts
|
|
842
|
+
length(samples?: number): number
|
|
843
|
+
```
|
|
844
|
+
|
|
845
|
+
#### `sampleAdaptive()` — Sample with adaptive density — more points where curvature is higher. Returns at least `minCount` points, up to `maxCount`.
|
|
846
|
+
|
|
847
|
+
```ts
|
|
848
|
+
sampleAdaptive(minCount?: number, maxCount?: number): Vec3[]
|
|
849
|
+
```
|
|
850
|
+
|
|
851
|
+
#### `toPolyline()` — Convert to a format compatible with sweep() path input.
|
|
852
|
+
|
|
853
|
+
```ts
|
|
854
|
+
toPolyline(samples?: number): Vec3[]
|
|
855
|
+
```
|