partforge 0.55.0 → 0.56.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/docs/AUTHORING-PARTS.md +110 -2
- package/docs/ERROR-PATTERNS.md +28 -2
- package/docs/KERNEL-CONTRACT.md +107 -30
- package/package.json +1 -1
- package/src/app-gasket.js +14 -0
- package/src/framework/backend-select.js +5 -2
- package/src/framework/geometry/contour-ops.js +1090 -0
- package/src/framework/geometry/curve-fill.js +1 -59
- package/src/framework/geometry/kernel.js +24 -6
- package/src/framework/geometry/manifold-backend.js +72 -62
- package/src/framework/geometry/occt-backend.js +121 -84
- package/src/framework/geometry/paper-bridge.js +224 -0
- package/src/framework/geometry/polygon.js +8 -0
- package/src/framework/geometry/probe.js +41 -16
- package/src/framework/geometry/profile.js +51 -0
- package/src/framework/geometry/shape2d-regions.js +100 -0
- package/src/framework/geometry/shape2d.js +91 -0
- package/src/framework/jobs.js +16 -3
- package/src/framework/lint/rules-build.js +5 -2
- package/src/gasket-worker.js +3 -0
- package/src/parts/gasket.js +111 -0
- package/types/geometry.d.ts +121 -2
- package/types/kernel.d.ts +66 -6
|
@@ -28,7 +28,7 @@ export const BUILD_RULES = [
|
|
|
28
28
|
{
|
|
29
29
|
id: "unknown-solid-op",
|
|
30
30
|
run: ({ probe }) => unique(probe().issues
|
|
31
|
-
.filter((i) => i.kind === "unknown-op" && i.scope
|
|
31
|
+
.filter((i) => i.kind === "unknown-op" && i.scope !== "kernel").map((i) => i.op))
|
|
32
32
|
.map((op) => err("unknown-solid-op", `\`.${op}(…)\` is not a Solid or Shape2D method`,
|
|
33
33
|
`Remove the call or correct the name — see the Solid and Shape2D method tables in docs/AUTHORING-PARTS.md.`,
|
|
34
34
|
"parts")),
|
|
@@ -52,7 +52,10 @@ export const BUILD_RULES = [
|
|
|
52
52
|
id: "manifold-backend-uses-occt-op",
|
|
53
53
|
run: ({ part, probe }) => {
|
|
54
54
|
if (part?.meta?.backend !== "manifold") return [];
|
|
55
|
-
|
|
55
|
+
// solidUsed, not used: `Shape2D.fillet`/`.chamfer` are backend-identical pure
|
|
56
|
+
// JS and are fine under a pinned Manifold backend; only Solid-handle uses of
|
|
57
|
+
// these names are CAD-only.
|
|
58
|
+
return [...probe().solidUsed].filter((op) => OCCT_ONLY.has(op))
|
|
56
59
|
.map((op) => err("manifold-backend-uses-occt-op",
|
|
57
60
|
`\`meta.backend\` pins Manifold, but the build calls \`${op}\`, which only OCCT implements`,
|
|
58
61
|
`Remove \`meta.backend: "manifold"\` and let the probe route this part to OCCT, or replace \`${op}\` with a mesh-friendly construction — as written the build throws KernelCapabilityError.`,
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// Reference part for the 2-D editing surface (docs/AUTHORING-PARTS.md "Editing
|
|
2
|
+
// profiles"): a gasket plate whose outline is a curve-native `pathProfile` (one cubic
|
|
3
|
+
// bulge edge), with two bolt bosses unioned onto the bottom edge — their circular tabs
|
|
4
|
+
// centered exactly ON that edge, the coincident-edge boolean case paper.js has known
|
|
5
|
+
// rough edges around (see the design doc's "two carve-outs"). The corners are then
|
|
6
|
+
// rounded with `.fillet(…, { corners: "convex" })`, the bolt holes cut, an optional
|
|
7
|
+
// print-clearance offset applied, and the whole profile extruded.
|
|
8
|
+
//
|
|
9
|
+
// No `meta.backend` override needed: the probe tracks which handle kind an op ran
|
|
10
|
+
// on, so this build's `Shape2D.fillet` (backend-identical pure JS — KERNEL-CONTRACT.md
|
|
11
|
+
// "One shared implementation") does not read as a CAD-only op, and the part
|
|
12
|
+
// auto-routes to fast Manifold like any other non-B-rep part.
|
|
13
|
+
import { pathProfile, circleProfile } from "partforge/geometry";
|
|
14
|
+
|
|
15
|
+
// Pure dimension math shared with test/gasket-part.test.js, which re-derives the same
|
|
16
|
+
// 2-D profile with the free contour-ops/paper-bridge functions (no kernel) to check
|
|
17
|
+
// validateProfile/profileCorners/toContours-shaped output independent of the build.
|
|
18
|
+
// Exported alongside the default PartDefinition — still DOM-free and side-effect-free.
|
|
19
|
+
export function gasketGeometry(p) {
|
|
20
|
+
const w2 = p.w / 2;
|
|
21
|
+
const dep = p.w * 0.6; // plate depth (top edge sits at y = dep)
|
|
22
|
+
const bulge = p.w * 0.18; // how far the top edge's cubic bulges outward
|
|
23
|
+
const tabR = p.boltR + 1.5; // boss radius — enough wall around the hole
|
|
24
|
+
const tabX = p.w * 0.25; // boss centers, symmetric about x = 0
|
|
25
|
+
const tabSegs = 12; // low-poly boss facets — a fillet needs a chord it can fit inside
|
|
26
|
+
// Deterministic backoff (AUTHORING-PARTS "Editing profiles" — fillet throws rather
|
|
27
|
+
// than clamping) so cornerR never overruns a boss's own facets or the margins/gap
|
|
28
|
+
// between the outline and a boss, at any width/bolt-radius in the sliders' range.
|
|
29
|
+
// The 0.5 factor covers `fillet`'s "soft cap" halving an edge's share whenever BOTH
|
|
30
|
+
// its neighboring corners are selected too — true here, since `corners: "convex"`
|
|
31
|
+
// selects every convex corner at once, including each boss's own facet corners.
|
|
32
|
+
const marginX = w2 - tabX - tabR; // material outboard of each boss
|
|
33
|
+
const gapX = tabX - tabR; // half the gap between the two bosses
|
|
34
|
+
const filletR = Math.max(0, Math.min(p.cornerR, (marginX - 0.3) / 2, (gapX - 0.3) / 2, dep * 0.2, tabR * 0.4));
|
|
35
|
+
return { w2, dep, bulge, tabR, tabX, tabSegs, filletR };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export default {
|
|
39
|
+
meta: { title: "Gasket", units: "mm", background: 0x15181d },
|
|
40
|
+
parameters: [
|
|
41
|
+
{
|
|
42
|
+
id: "gasket",
|
|
43
|
+
title: "Gasket",
|
|
44
|
+
description: "A curvy gasket plate with two bolt bosses. Demonstrates `pathProfile`, `Shape2D.union`/`.fillet`/`.cut`/`.offset`, and a coincident-edge boolean (the bosses straddle the outline's bottom edge).",
|
|
45
|
+
controls: [
|
|
46
|
+
{ key: "w", label: "Width", unit: "mm", min: 24, max: 80, step: 1,
|
|
47
|
+
description: "Overall plate width. The plate's depth, bulge, and boss spacing all scale from this one dimension." },
|
|
48
|
+
{ key: "h", label: "Thickness", unit: "mm", min: 1, max: 8, step: 0.5,
|
|
49
|
+
description: "Plate thickness (the extrusion height)." },
|
|
50
|
+
{ key: "boltR", label: "Bolt hole radius", unit: "mm", min: 1, max: 2.5, step: 0.1,
|
|
51
|
+
description: "Radius of each mounting hole, cut through the boss at the end." },
|
|
52
|
+
{ key: "cornerR", label: "Corner radius", unit: "mm", min: 0, max: 4, step: 0.25,
|
|
53
|
+
description: "Fillet applied to every convex corner (`Shape2D.fillet`). Automatically backed off if the plate/bosses are too small to fit it — see the build's `filletR` clamp." },
|
|
54
|
+
{ key: "clearance", label: "Print-clearance offset", unit: "mm", min: 0, max: 1, step: 0.1,
|
|
55
|
+
description: "Grows the whole outline outward (round corners) for a looser slip fit. 0 = none." },
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
defaults: { w: 44, h: 3, boltR: 1.6, cornerR: 2, clearance: 0 },
|
|
60
|
+
parts: {
|
|
61
|
+
gasket: {
|
|
62
|
+
label: "Gasket",
|
|
63
|
+
views: ["gasket"],
|
|
64
|
+
export: { name: "gasket" },
|
|
65
|
+
build: (k, p) => {
|
|
66
|
+
const { w2, dep, bulge, tabR, tabX, tabSegs, filletR } = gasketGeometry(p);
|
|
67
|
+
|
|
68
|
+
// Curve-native outline: straight sides and bottom, one cubic bulge across the
|
|
69
|
+
// top. `close()` leaves the bottom-left→start edge implicit; contour-ops
|
|
70
|
+
// re-closes it explicitly wherever that matters (corner/fillet math).
|
|
71
|
+
const outline = pathProfile([-w2, 0])
|
|
72
|
+
.lineTo([w2, 0])
|
|
73
|
+
.lineTo([w2, dep])
|
|
74
|
+
.cubicTo([-w2, dep], [w2 * 0.5, dep + bulge], [-w2 * 0.5, dep + bulge])
|
|
75
|
+
.close();
|
|
76
|
+
|
|
77
|
+
// Boss centers sit exactly ON the bottom edge (y = 0) — the coincident-edge
|
|
78
|
+
// union case: each tab straddles the existing straight edge rather than
|
|
79
|
+
// merely touching it at a point.
|
|
80
|
+
const tabs = [[-tabX, 0], [tabX, 0]].map(([cx, cy]) => circleProfile(tabR, [cx, cy], tabSegs));
|
|
81
|
+
|
|
82
|
+
// Fillet the outer convex corners, THEN cut the bolt holes. Note this ordering
|
|
83
|
+
// trades away the STEP-CIRCLE fidelity the "fillet after booleans" rule buys
|
|
84
|
+
// (AUTHORING-PARTS.md "Editing profiles"): the hole cuts are themselves paper.js
|
|
85
|
+
// booleans, so they degrade the fillet's true `{to,via}` arcs back to cubic
|
|
86
|
+
// approximations before export. That's fine here — the fillet still reads as a
|
|
87
|
+
// rounded corner (curve-exact until the cut, faceted-cubic after) — but a part
|
|
88
|
+
// that needs an exact STEP CIRCLE on a filleted corner should cut first and
|
|
89
|
+
// fillet last instead.
|
|
90
|
+
let plate = k.shape2d(outline).union(tabs[0]).union(tabs[1]);
|
|
91
|
+
if (filletR > 0) plate = plate.fillet(filletR, { corners: "convex" });
|
|
92
|
+
plate = plate.cut(circleProfile(p.boltR, [-tabX, 0])).cut(circleProfile(p.boltR, [tabX, 0]));
|
|
93
|
+
if (p.clearance) plate = plate.offset(p.clearance);
|
|
94
|
+
|
|
95
|
+
return plate.extrude({ h: p.h });
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
views: { gasket: { label: "Gasket" } },
|
|
100
|
+
// Now that Shape2D.fillet no longer routes the part to OCCT, this builds on
|
|
101
|
+
// Manifold and can gate `holes` directly (Manifold-only topology —
|
|
102
|
+
// ERROR-PATTERNS.md#occt-holes-watertight-na); test/gasket-part.test.js's
|
|
103
|
+
// genus() === 2 check pins the same fact against a directly-booted kernel.
|
|
104
|
+
verify: {
|
|
105
|
+
process: "fdm-pla",
|
|
106
|
+
expect: {
|
|
107
|
+
gasket: { bbox: "<=[60,50,6]", holes: 2 },
|
|
108
|
+
_view: { overlaps: 0 },
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
};
|
package/types/geometry.d.ts
CHANGED
|
@@ -3,9 +3,29 @@
|
|
|
3
3
|
// DOM-free and kernel-free: this is the entry a part's build functions import
|
|
4
4
|
// (importing "partforge" inside a worker throws `document is not defined`).
|
|
5
5
|
|
|
6
|
-
import type {
|
|
6
|
+
import type {
|
|
7
|
+
ArcContour,
|
|
8
|
+
Contour,
|
|
9
|
+
Corner2D,
|
|
10
|
+
CornerSelector,
|
|
11
|
+
MirrorAxis2,
|
|
12
|
+
Point2,
|
|
13
|
+
Point3,
|
|
14
|
+
PointsContour,
|
|
15
|
+
Region2D,
|
|
16
|
+
Solid,
|
|
17
|
+
} from "./kernel.js";
|
|
18
|
+
|
|
19
|
+
export type { ArcContour, Contour, Corner2D, CornerSelector, MirrorAxis2, Point2, Point3, PointsContour, Region2D, Solid };
|
|
7
20
|
|
|
8
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Anything the 2-D editing ops below accept: a plain point list, a curve-native
|
|
23
|
+
* contour, a `{outer, holes}` region, or a region array. Every op returns the
|
|
24
|
+
* same shape of input it was given (a bare point list stays a point list, unless
|
|
25
|
+
* the op introduces curves — e.g. a non-uniform `scaleProfile` on an arc — in
|
|
26
|
+
* which case it upgrades to a `Contour`).
|
|
27
|
+
*/
|
|
28
|
+
export type ProfileInput = number[][] | Contour | Region2D | Region2D[];
|
|
9
29
|
|
|
10
30
|
/** A pie/sector wedge with its tip at the origin. */
|
|
11
31
|
export function piePolygon(tipR: number, arcDeg: number, segs?: number): PointsContour;
|
|
@@ -115,3 +135,102 @@ export function circularPattern(
|
|
|
115
135
|
rotateCopies?: boolean;
|
|
116
136
|
},
|
|
117
137
|
): Solid[];
|
|
138
|
+
|
|
139
|
+
// ── 2-D editing ops ──────────────────────────────────────────────────────────
|
|
140
|
+
// Polymorphic input contract: every op below accepts a point list, a curve-native
|
|
141
|
+
// contour, a region, or a region array, and returns the same shape of input it
|
|
142
|
+
// was given — except the arc-length queries (profileLength/profilePointAt/
|
|
143
|
+
// profileTangentAt), which are single-contour by nature and throw on a region.
|
|
144
|
+
|
|
145
|
+
/** Translate every contour by `[dx, dy]`. Exact on all segment types. */
|
|
146
|
+
export function translateProfile(input: ProfileInput, delta: Point2): ProfileInput;
|
|
147
|
+
|
|
148
|
+
/** Rotate `deg` degrees about `center` (default the origin). Arcs stay arcs. */
|
|
149
|
+
export function rotateProfile(input: ProfileInput, deg: number, center?: Point2): ProfileInput;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Scale by a uniform or per-axis factor about `center` (default the origin).
|
|
153
|
+
* A non-uniform `[sx, sy]` converts `{to, via}` arcs to cubics (an ellipse is
|
|
154
|
+
* not a circular arc). Scale factors must be finite and non-zero.
|
|
155
|
+
*/
|
|
156
|
+
export function scaleProfile(input: ProfileInput, s: number | Point2, center?: Point2): ProfileInput;
|
|
157
|
+
|
|
158
|
+
/** Reflect across `"x"`, `"y"`, or an arbitrary `{point, dir}` line. */
|
|
159
|
+
export function mirrorProfile(input: ProfileInput, axis: MirrorAxis2): ProfileInput;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Round selected corners with true arcs. `r` may be an array paired
|
|
163
|
+
* positionally with `{indices}`. Throws if no corner matches, or if `r`
|
|
164
|
+
* does not fit against its neighboring segments.
|
|
165
|
+
*/
|
|
166
|
+
export function filletProfile(input: ProfileInput, r: number | number[], opts?: { corners?: CornerSelector }): ProfileInput;
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Bevel selected corners with a straight chord (symmetric setback). `dist`
|
|
170
|
+
* may be an array paired positionally with `{indices}`. Throws if no corner
|
|
171
|
+
* matches, or if `dist` does not fit against its neighboring segments.
|
|
172
|
+
*/
|
|
173
|
+
export function chamferProfile(input: ProfileInput, dist: number | number[], opts?: { corners?: CornerSelector }): ProfileInput;
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* The corner list — `{index, point, interiorAngleDeg, convex, segTypes}[]`,
|
|
177
|
+
* plus `{regionIndex, ring}` for region/regions input. This positional order
|
|
178
|
+
* is what `filletProfile`/`chamferProfile`'s `{indices}` selects into.
|
|
179
|
+
*/
|
|
180
|
+
export function profileCorners(input: ProfileInput): Corner2D[];
|
|
181
|
+
|
|
182
|
+
/** Total arc length of a single contour, in mm. */
|
|
183
|
+
export function profileLength(contour: Contour): number;
|
|
184
|
+
|
|
185
|
+
/** The point at normalized position `t` (0..1) or absolute arc `length` along a single contour. */
|
|
186
|
+
export function profilePointAt(contour: Contour, opts: { t: number } | { length: number }): Point2;
|
|
187
|
+
|
|
188
|
+
/** The unit tangent at normalized position `t` (0..1) or absolute arc `length` along a single contour. */
|
|
189
|
+
export function profileTangentAt(contour: Contour, opts: { t: number } | { length: number }): Point2;
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* The closest point on `input` to `[x, y]` — the pick-resolution primitive.
|
|
193
|
+
* Accepts regions (unlike the arc-length queries above); `contourIndex`/
|
|
194
|
+
* `segmentIndex` follow the same flattened outer-then-holes-per-region order
|
|
195
|
+
* `validateProfile` uses. `t` is normalized 0..1, but for a `{to, via}` arc
|
|
196
|
+
* segment (expanded internally into ≤90° cubic pieces that share one
|
|
197
|
+
* `segmentIndex`) it is local to whichever piece the nearest point landed
|
|
198
|
+
* on, not a position along the arc's full sweep.
|
|
199
|
+
*/
|
|
200
|
+
export function profileNearestPoint(
|
|
201
|
+
input: ProfileInput,
|
|
202
|
+
p: Point2,
|
|
203
|
+
): { point: Point2; distance: number; contourIndex: number; segmentIndex: number; t: number };
|
|
204
|
+
|
|
205
|
+
/** Curve-exact axis-aligned bounds across every contour in `input`. */
|
|
206
|
+
export function profileBounds(input: ProfileInput): { min: Point2; max: Point2 };
|
|
207
|
+
|
|
208
|
+
/** Net curve-exact area (Σ|outers| − Σ|holes|), mm². */
|
|
209
|
+
export function profileArea(input: ProfileInput): number;
|
|
210
|
+
|
|
211
|
+
/** Curve-aware point-in-shape test (inside an outer, not inside a hole). */
|
|
212
|
+
export function profileContains(input: ProfileInput, p: Point2): boolean;
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Corner-preserving decimation/refit within `tolerance` mm: splits at corners,
|
|
216
|
+
* then reduces or refits each run independently, reassembling with corner
|
|
217
|
+
* points bit-exact preserved. A run that gains curves upgrades a point-list
|
|
218
|
+
* input to a `Contour`.
|
|
219
|
+
*/
|
|
220
|
+
export function simplifyProfile(input: ProfileInput, tolerance: number): ProfileInput;
|
|
221
|
+
|
|
222
|
+
/** One issue `validateProfile` reports. Never thrown — only returned. */
|
|
223
|
+
export interface ProfileIssue {
|
|
224
|
+
type: "degenerate" | "self-intersection" | "winding" | "nesting";
|
|
225
|
+
contourIndex: number;
|
|
226
|
+
segmentIndex?: number;
|
|
227
|
+
point?: Point2;
|
|
228
|
+
message: string;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Geometric sanity checks (degenerate segments/area, self-intersection,
|
|
233
|
+
* winding, nesting) against `input`'s sampled approximation. Never throws on
|
|
234
|
+
* geometric badness — only an unrecognized `input` shape throws.
|
|
235
|
+
*/
|
|
236
|
+
export function validateProfile(input: ProfileInput): { ok: boolean; issues: ProfileIssue[] };
|
package/types/kernel.d.ts
CHANGED
|
@@ -95,6 +95,39 @@ export type AxisName = "X" | "Y" | "Z";
|
|
|
95
95
|
/** Convex-corner style for an offset. */
|
|
96
96
|
export type OffsetCorners = "round" | "chamfer" | "sharp";
|
|
97
97
|
|
|
98
|
+
/** A stored contour-IR region: curve-native contours, nothing flattened. */
|
|
99
|
+
export interface ContourRegion {
|
|
100
|
+
outer: ArcContour;
|
|
101
|
+
holes: ArcContour[];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** One corner of a `Shape2D`, as `Shape2D.corners()` reports it. */
|
|
105
|
+
export interface Corner2D {
|
|
106
|
+
/** Index of the joint within its own contour. */
|
|
107
|
+
index: number;
|
|
108
|
+
point: Point2;
|
|
109
|
+
interiorAngleDeg: number;
|
|
110
|
+
convex: boolean;
|
|
111
|
+
/** Segment kinds meeting here, incoming then outgoing. */
|
|
112
|
+
segTypes: Array<"line" | "arc" | "cubic">;
|
|
113
|
+
/** Present only for multi-region shapes. */
|
|
114
|
+
regionIndex?: number;
|
|
115
|
+
/** Present only for multi-region shapes. */
|
|
116
|
+
ring?: "outer" | { hole: number };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Which corners a `Shape2D` `fillet`/`chamfer` applies to. Default `"all"`. */
|
|
120
|
+
export type CornerSelector =
|
|
121
|
+
| "all"
|
|
122
|
+
| "convex"
|
|
123
|
+
| "concave"
|
|
124
|
+
/** Positional indices into `corners()`; a per-corner `r`/`d` array pairs with these. */
|
|
125
|
+
| { indices: number[] }
|
|
126
|
+
| { near: Point2; count?: number };
|
|
127
|
+
|
|
128
|
+
/** A mirror line for `Shape2D.mirror`. */
|
|
129
|
+
export type MirrorAxis2 = "x" | "y" | { point: Point2; dir: Point2 };
|
|
130
|
+
|
|
98
131
|
// --- edge / face selectors (OCCT-only ops) ---------------------------------
|
|
99
132
|
|
|
100
133
|
/**
|
|
@@ -115,8 +148,13 @@ export type FaceSelector = EdgeSelector;
|
|
|
115
148
|
// --- Shape2D ----------------------------------------------------------------
|
|
116
149
|
|
|
117
150
|
/**
|
|
118
|
-
*
|
|
119
|
-
*
|
|
151
|
+
* A 2-D sketch value: booleans, transforms, corner ops and queries. ONE shared
|
|
152
|
+
* implementation on both backends — storage is the curve-native contour IR, so
|
|
153
|
+
* arcs and béziers survive every op and results are backend-identical except
|
|
154
|
+
* `offset`. No backend geometry exists until the shape is extruded/revolved or
|
|
155
|
+
* materialized via `toRegions()`. `_`-prefixed keys are internals, not declared.
|
|
156
|
+
*
|
|
157
|
+
* Every op returns a NEW `Shape2D`; no operand is ever mutated.
|
|
120
158
|
*/
|
|
121
159
|
export interface Shape2D {
|
|
122
160
|
union(other: Shape2D | Contour): Shape2D;
|
|
@@ -125,20 +163,42 @@ export interface Shape2D {
|
|
|
125
163
|
cutAll(others: Array<Shape2D | Contour>): Shape2D;
|
|
126
164
|
intersect(other: Shape2D | Contour): Shape2D;
|
|
127
165
|
/**
|
|
128
|
-
* Grow (`delta > 0`) or inset (`delta < 0`).
|
|
129
|
-
* faceted at mesh LOD on Manifold. Throws if the
|
|
166
|
+
* Grow (`delta > 0`) or inset (`delta < 0`). The one backend-specific op:
|
|
167
|
+
* curve-preserving on OCCT, faceted at mesh LOD on Manifold. Throws if the
|
|
168
|
+
* offset collapses the shape.
|
|
130
169
|
*/
|
|
131
170
|
offset(delta: number, opts?: { corners?: OffsetCorners; segs?: number }): Shape2D;
|
|
132
|
-
/** Net area (outers minus holes), mm². */
|
|
171
|
+
/** Net area (outers minus holes), mm². Curve-exact — not measured off a tessellation. */
|
|
133
172
|
area(): number;
|
|
173
|
+
/** Axis-aligned 2-D bounds, curve-exact. */
|
|
134
174
|
boundingBox(): BoundingBox2;
|
|
135
|
-
/** Materialize into region arrays. */
|
|
175
|
+
/** Materialize into point-ring region arrays, tessellating curves at the backend's LOD. */
|
|
136
176
|
toRegions(): MaterializedRegion[];
|
|
177
|
+
/** The stored contour IR — curve-native and lossless. A deep copy, safe to mutate. */
|
|
178
|
+
toContours(): ContourRegion[];
|
|
137
179
|
/** `toRegions()` unwrapped — throws unless there is exactly one region. */
|
|
138
180
|
simple(): MaterializedRegion;
|
|
139
181
|
/** Scission: each disjoint region as its own live `Shape2D`. */
|
|
140
182
|
regions(): Shape2D[];
|
|
141
183
|
clone(): Shape2D;
|
|
184
|
+
/** Translate by `[dx, dy]`. */
|
|
185
|
+
translate(v: Point2): Shape2D;
|
|
186
|
+
/** Rotate `deg` about `center` (default the origin). */
|
|
187
|
+
rotate(deg: number, center?: Point2): Shape2D;
|
|
188
|
+
/** Scale about `center` (default the origin). A single `number` scales uniformly; `[sx, sy]` scales each axis independently. */
|
|
189
|
+
scale(factor: number | [number, number], center?: Point2): Shape2D;
|
|
190
|
+
/** Reflect across an axis line. */
|
|
191
|
+
mirror(axis: MirrorAxis2): Shape2D;
|
|
192
|
+
/** Round selected corners with true arcs. `r` may be an array paired with `{ indices }`. */
|
|
193
|
+
fillet(r: number | number[], opts?: { corners?: CornerSelector }): Shape2D;
|
|
194
|
+
/** Bevel selected corners with straight chords. `d` may be an array paired with `{ indices }`. */
|
|
195
|
+
chamfer(d: number | number[], opts?: { corners?: CornerSelector }): Shape2D;
|
|
196
|
+
/** Corner-preserving decimation/refit within `tolerance` mm. */
|
|
197
|
+
simplify(tolerance: number): Shape2D;
|
|
198
|
+
/** The corner list — the positional order `fillet`/`chamfer`'s `{ indices }` selects into. */
|
|
199
|
+
corners(): Corner2D[];
|
|
200
|
+
/** Is `[x, y]` inside the shape (inside an outer, not inside a hole)? */
|
|
201
|
+
contains(p: Point2): boolean;
|
|
142
202
|
/** Sugar for `k.extrude({ profile: this, ... })`. */
|
|
143
203
|
extrude(opts: { h: number; twist?: number; scaleTop?: number }): Solid;
|
|
144
204
|
/** Sugar for `k.revolve({ profile: this, ... })`. */
|