partforge 0.41.0 → 0.45.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 +31 -10
- package/bin/cli.js +138 -27
- package/docs/AUTHORING-PARTS.md +164 -17
- package/docs/ERROR-PATTERNS.md +6 -0
- package/package.json +48 -7
- package/skills/partforge/SKILL.md +17 -3
- package/src/app-embed-test.js +1 -1
- package/src/app-hinged-box.js +12 -0
- package/src/framework/animation-controls.js +254 -0
- package/src/framework/animation.js +271 -0
- package/src/framework/app.css +32 -0
- package/src/framework/assembly.js +1 -1
- package/src/framework/backend-select.js +25 -0
- package/src/framework/camera-tween.js +58 -0
- package/src/framework/capture-build.js +59 -0
- package/src/framework/chrome.css +16 -0
- package/src/framework/controls.js +13 -3
- package/src/framework/cutaway-gizmo-scene.js +244 -0
- package/src/framework/cutaway-gizmo.js +80 -243
- package/src/framework/default-view.js +46 -0
- package/src/framework/download.js +7 -2
- package/src/framework/export-controller.js +13 -2
- package/src/framework/geometry/probe.js +3 -22
- package/src/framework/jobs.js +30 -40
- package/src/framework/lint/finding.js +4 -0
- package/src/framework/lint/index.js +7 -3
- package/src/framework/lint/rules-animations.js +441 -0
- package/src/framework/lint/rules-place.js +76 -0
- package/src/framework/lint/rules-schema.js +22 -0
- package/src/framework/lint/rules-shape.js +12 -0
- package/src/framework/lint/rules-verify.js +2 -2
- package/src/framework/mount.js +147 -20
- package/src/{testing → framework/oracle}/build.js +1 -1
- package/src/{testing → framework/oracle}/bvh.js +1 -1
- package/src/{testing → framework/oracle}/measure.js +1 -1
- package/src/{testing → framework/oracle}/min-wall.js +1 -1
- package/src/{testing → framework/oracle}/verify.js +3 -3
- package/src/framework/param-deps.js +1 -1
- package/src/framework/part-model.js +48 -0
- package/src/framework/pick-request/client.js +11 -3
- package/src/framework/pick-request/endpoint.js +60 -0
- package/src/framework/pick-request/index.js +6 -0
- package/src/framework/pick-request/server.js +222 -34
- package/src/framework/pick-request/token-store.js +31 -0
- package/src/framework/pose-fast-path.js +12 -1
- package/src/framework/pose-probe-core.js +129 -0
- package/src/framework/pose-probe.js +7 -123
- package/src/framework/regen-loop.js +10 -3
- package/src/framework/safe-name.js +26 -0
- package/src/framework/verify-metrics.js +4 -4
- package/src/framework/view-state.js +25 -21
- package/src/framework/view-tabs.js +35 -7
- package/src/framework/viewer-controls.js +5 -26
- package/src/framework/viewer-lighting.js +8 -1
- package/src/framework/viewer.js +139 -20
- package/src/framework/worker.js +5 -1
- package/src/hinged-box-worker.js +3 -0
- package/src/index.js +1 -1
- package/src/parts/hinged-box.js +94 -0
- package/src/testing/render.js +19 -8
- package/src/testing.js +15 -8
- package/types/derive.d.ts +14 -0
- package/types/geometry.d.ts +117 -0
- package/types/index.d.ts +259 -0
- package/types/kernel.d.ts +409 -0
- package/types/lint.d.ts +85 -0
- package/types/part.d.ts +409 -0
- package/types/testing.d.ts +362 -0
- package/types/worker.d.ts +21 -0
- /package/src/{testing → framework/oracle}/assert-dsl.js +0 -0
- /package/src/{testing → framework/oracle}/cases.js +0 -0
- /package/src/{testing → framework/oracle}/dfm-profiles.js +0 -0
- /package/src/{testing → framework/oracle}/gaps.js +0 -0
- /package/src/{testing → framework/oracle}/mesh.js +0 -0
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
// partforge/testing — headless helpers for building, measuring and verifying a
|
|
2
|
+
// part with no browser.
|
|
3
|
+
//
|
|
4
|
+
// Most of what this barrel re-exports is NOT test-only: the oracle
|
|
5
|
+
// (measure/verify/buildView/gaps/BVH/min-wall) also runs inside the browser
|
|
6
|
+
// geometry worker. Only the two kernel booters and the PNG renderer are
|
|
7
|
+
// Node-bound.
|
|
8
|
+
|
|
9
|
+
import type { GeometryKernel, Mesh, Point3, Solid } from "./kernel.js";
|
|
10
|
+
import type { Derived, FontSource, PartDefinition, ResolvedParams } from "./part.js";
|
|
11
|
+
|
|
12
|
+
export type { Derived, GeometryKernel, Mesh, PartDefinition, ResolvedParams, Solid };
|
|
13
|
+
|
|
14
|
+
// --- kernels ----------------------------------------------------------------
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Wrap an already-instantiated Manifold WASM module as a `GeometryKernel`.
|
|
18
|
+
* `wasm` is the value of `await Module()` from `manifold-3d`, after `setup()`.
|
|
19
|
+
*/
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- the opaque manifold-3d WASM module
|
|
21
|
+
export function createManifoldKernel(wasm: any, opts?: { quality?: "preview" | "print" }): GeometryKernel;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Boot the Manifold WASM module in a Node process and return a ready kernel.
|
|
25
|
+
* OCCT must NOT be booted in the same process — keep OCCT tests in their own
|
|
26
|
+
* files (vitest isolates per file).
|
|
27
|
+
*/
|
|
28
|
+
export function bootManifoldKernel(opts?: {
|
|
29
|
+
quality?: "preview" | "print";
|
|
30
|
+
fonts?: Record<string, FontSource>;
|
|
31
|
+
}): Promise<GeometryKernel>;
|
|
32
|
+
|
|
33
|
+
/** Boot OCCT/replicad in a Node process and return a ready kernel. */
|
|
34
|
+
export function bootOcctKernel(opts?: { fonts?: Record<string, FontSource> }): Promise<GeometryKernel>;
|
|
35
|
+
|
|
36
|
+
// --- the job loop -----------------------------------------------------------
|
|
37
|
+
|
|
38
|
+
/** A job the worker loop accepts. */
|
|
39
|
+
export interface WorkerJob {
|
|
40
|
+
type: "generate" | "export-stl" | "export-step" | "export-3mf" | "inspect";
|
|
41
|
+
view?: string;
|
|
42
|
+
params?: ResolvedParams;
|
|
43
|
+
/** `generate`: which sub-parts to build. */
|
|
44
|
+
subparts?: string[];
|
|
45
|
+
/** Export: an explicit sub-part selection, overriding the view's. */
|
|
46
|
+
parts?: string[];
|
|
47
|
+
quality?: "preview" | "print";
|
|
48
|
+
cache?: boolean;
|
|
49
|
+
/** Correlation id echoed on every reply. */
|
|
50
|
+
jobId?: number;
|
|
51
|
+
/** Single-file export name base. */
|
|
52
|
+
name?: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Run one worker job against `kernel`/`part`, posting replies through `post`.
|
|
57
|
+
* The same loop the geometry Web Worker runs, exposed so a test can exercise an
|
|
58
|
+
* export end to end without a Worker.
|
|
59
|
+
*/
|
|
60
|
+
export function handle(
|
|
61
|
+
kernel: GeometryKernel,
|
|
62
|
+
part: PartDefinition,
|
|
63
|
+
msg: WorkerJob,
|
|
64
|
+
post: (message: Record<string, unknown>, transfer?: Transferable[]) => void,
|
|
65
|
+
opts?: { isStale?: () => boolean },
|
|
66
|
+
): Promise<void>;
|
|
67
|
+
|
|
68
|
+
// --- the part model ---------------------------------------------------------
|
|
69
|
+
|
|
70
|
+
/** The sub-parts a view shows, in definition order. */
|
|
71
|
+
export function viewSubParts(part: PartDefinition, view: string, params: ResolvedParams): string[];
|
|
72
|
+
|
|
73
|
+
/** Resolve a part's `derive` into the `d` object builds receive. */
|
|
74
|
+
export function resolveDerived(part: Pick<PartDefinition, "derive">, p: ResolvedParams): Derived;
|
|
75
|
+
|
|
76
|
+
/** Returned instead of a key set when the relevance analysis could not run. */
|
|
77
|
+
export const RELEVANT_ALL: unique symbol;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Which raw parameters affect the parts on screen in `view` — a `Set` of param
|
|
81
|
+
* keys, or `RELEVANT_ALL` when the build could not be analyzed.
|
|
82
|
+
*/
|
|
83
|
+
export function relevantParamKeys(
|
|
84
|
+
part: PartDefinition,
|
|
85
|
+
view: string,
|
|
86
|
+
params: ResolvedParams,
|
|
87
|
+
): Set<string> | typeof RELEVANT_ALL;
|
|
88
|
+
|
|
89
|
+
// --- assembly checks --------------------------------------------------------
|
|
90
|
+
|
|
91
|
+
/** One interpenetrating pair. `location` is the intersection bbox centre. */
|
|
92
|
+
export interface Overlap {
|
|
93
|
+
a: string;
|
|
94
|
+
b: string;
|
|
95
|
+
/** Intersection volume in mm³. */
|
|
96
|
+
volume: number;
|
|
97
|
+
location: Point3 | null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** One measured pair distance. `distance` 0 = touching or interpenetrating. */
|
|
101
|
+
export interface Gap {
|
|
102
|
+
a: string;
|
|
103
|
+
b: string;
|
|
104
|
+
distance: number;
|
|
105
|
+
/** The midpoint between the pair's closest surface points. */
|
|
106
|
+
at: Point3 | null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Build every sub-part of a view in its assembly pose and return the pairs whose
|
|
111
|
+
* solid-intersection volume exceeds `tolerance` mm³. Manifold-only (needs
|
|
112
|
+
* `Solid.intersect`).
|
|
113
|
+
*/
|
|
114
|
+
export function assemblyOverlaps(
|
|
115
|
+
kernel: GeometryKernel,
|
|
116
|
+
part: PartDefinition,
|
|
117
|
+
view: string,
|
|
118
|
+
params?: ResolvedParams,
|
|
119
|
+
opts?: { tolerance?: number },
|
|
120
|
+
): Overlap[];
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* The complement of `assemblyOverlaps`: sub-part pairs that ALMOST touch
|
|
124
|
+
* (0 < distance < `threshold` mm) in the display pose. Pure mesh math, so it
|
|
125
|
+
* runs on both backends.
|
|
126
|
+
*/
|
|
127
|
+
export function assemblyGaps(
|
|
128
|
+
kernel: GeometryKernel,
|
|
129
|
+
part: PartDefinition,
|
|
130
|
+
view: string,
|
|
131
|
+
params?: ResolvedParams,
|
|
132
|
+
opts?: { threshold?: number },
|
|
133
|
+
): Gap[];
|
|
134
|
+
|
|
135
|
+
/** One sub-part built in its display pose, as `buildView` returns it. */
|
|
136
|
+
export interface BuiltSubPart {
|
|
137
|
+
name: string;
|
|
138
|
+
/** LIVE — valid only until `kernel.cleanup()`. */
|
|
139
|
+
solid: Solid;
|
|
140
|
+
/** JS-owned arrays; survives `cleanup()`. */
|
|
141
|
+
mesh: Mesh;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Minimum surface-to-surface distance for every pair of pre-built posed meshes.
|
|
146
|
+
* Pairs involving an empty mesh are skipped. `bvhCache` is a caller-owned `Map`
|
|
147
|
+
* so each mesh is indexed once across passes.
|
|
148
|
+
*/
|
|
149
|
+
export function meshGaps(built: BuiltSubPart[], opts?: { bvhCache?: Map<Mesh, BVH> }): Gap[];
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Build every sub-part of a view in its display pose. Keeps solids LIVE (does
|
|
153
|
+
* NOT call `kernel.cleanup()`) so callers can read exact solid facts first.
|
|
154
|
+
*/
|
|
155
|
+
export function buildView(
|
|
156
|
+
kernel: GeometryKernel,
|
|
157
|
+
part: PartDefinition,
|
|
158
|
+
view: string,
|
|
159
|
+
params?: ResolvedParams,
|
|
160
|
+
): BuiltSubPart[];
|
|
161
|
+
|
|
162
|
+
// --- mesh math --------------------------------------------------------------
|
|
163
|
+
|
|
164
|
+
/** Signed-tetrahedron volume of a triangle mesh. Omit `indices` for a flat soup. */
|
|
165
|
+
export function meshVolume(positions: ArrayLike<number>, indices?: ArrayLike<number>): number;
|
|
166
|
+
|
|
167
|
+
/** `[dx, dy, dz]` extent of a flat position array. */
|
|
168
|
+
export function bboxSize(positions: ArrayLike<number>): [number, number, number];
|
|
169
|
+
|
|
170
|
+
// --- the BVH ----------------------------------------------------------------
|
|
171
|
+
|
|
172
|
+
/** A triangle BVH over one mesh — nearest ray hit, nearest point, exact mesh distance. */
|
|
173
|
+
export interface BVH {
|
|
174
|
+
/** Nearest hit along `dir` from `origin`, or `null`. `t` is the distance, `tri` the mesh triangle id. */
|
|
175
|
+
raycast(
|
|
176
|
+
origin: Point3,
|
|
177
|
+
dir: Point3,
|
|
178
|
+
opts?: { tMin?: number; tMax?: number; skipTri?: number },
|
|
179
|
+
): { t: number; tri: number } | null;
|
|
180
|
+
/** Nearest surface point to `p`. */
|
|
181
|
+
closestPoint(p: Point3): { point: number[] | null; dist: number; tri: number };
|
|
182
|
+
/** Exact minimum surface-to-surface distance to another BVH. */
|
|
183
|
+
distanceTo(other: BVH): {
|
|
184
|
+
distance: number;
|
|
185
|
+
at: number[] | null;
|
|
186
|
+
pointA: number[] | null;
|
|
187
|
+
pointB: number[] | null;
|
|
188
|
+
};
|
|
189
|
+
triangleCount: number;
|
|
190
|
+
/** Flat, 9 coords per triangle, in mesh order. READ-ONLY — writing invalidates the tree. */
|
|
191
|
+
vertices: Float32Array | Float64Array;
|
|
192
|
+
/** The root AABB, `[minx, miny, minz, maxx, maxy, maxz]` (a copy). */
|
|
193
|
+
rootBounds: number[];
|
|
194
|
+
/** Diagnostic self-report of the four typed arrays' byte lengths. */
|
|
195
|
+
bytesAllocated: number;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** Index a mesh (Manifold soup or OCCT indexed form) for spatial queries. */
|
|
199
|
+
export function buildBVH(mesh: Mesh): BVH;
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Min wall thickness by inward ray-casting per surface triangle. `null` only for
|
|
203
|
+
* an EMPTY mesh; a mesh whose rays all miss returns `value: null` WITH the
|
|
204
|
+
* sampling accounting, so "we looked and found nothing" stays distinguishable
|
|
205
|
+
* from "nobody looked". A sampled reading is an upper bound on the true minimum.
|
|
206
|
+
*/
|
|
207
|
+
export function minWall(
|
|
208
|
+
mesh: Mesh,
|
|
209
|
+
opts?: { maxThickness?: number; maxSamples?: number; bvh?: BVH },
|
|
210
|
+
): {
|
|
211
|
+
value: number | null;
|
|
212
|
+
location: Point3 | null;
|
|
213
|
+
sampled: boolean;
|
|
214
|
+
/** The sample BUDGET — triangles selected, not rays actually cast. */
|
|
215
|
+
sampledTriangles: number;
|
|
216
|
+
totalTriangles: number;
|
|
217
|
+
} | null;
|
|
218
|
+
|
|
219
|
+
// --- measure ----------------------------------------------------------------
|
|
220
|
+
|
|
221
|
+
export interface SubPartFacts {
|
|
222
|
+
name: string;
|
|
223
|
+
/** Size only — `[dx, dy, dz]`. */
|
|
224
|
+
bbox: number[];
|
|
225
|
+
/** Where the geometry sits. */
|
|
226
|
+
bounds: { min: number[]; max: number[] };
|
|
227
|
+
/** Volume-weighted centroid, or `null` for a degenerate/zero-volume sub-part. */
|
|
228
|
+
centerOfMass: number[] | null;
|
|
229
|
+
volume: number;
|
|
230
|
+
surfaceArea: number;
|
|
231
|
+
triangleCount: number;
|
|
232
|
+
/** Manifold-only; `null` on OCCT. */
|
|
233
|
+
watertight: boolean | null;
|
|
234
|
+
/** Through-hole count (genus). Manifold-only; `null` on OCCT. */
|
|
235
|
+
holes: number | null;
|
|
236
|
+
/** `null` exactly when no reading exists. */
|
|
237
|
+
minWall: number | null;
|
|
238
|
+
minWallAt: number[] | null;
|
|
239
|
+
minWallSampled: boolean;
|
|
240
|
+
minWallSamples: { sampled: number; total: number } | null;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export interface AggregateFacts {
|
|
244
|
+
bbox: number[];
|
|
245
|
+
bounds: { min: number[]; max: number[] };
|
|
246
|
+
centerOfMass: number[] | null;
|
|
247
|
+
volume: number;
|
|
248
|
+
surfaceArea: number;
|
|
249
|
+
triangleCount: number;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export interface MeasureReport {
|
|
253
|
+
/** `part.meta.title`, falling back to the view name. */
|
|
254
|
+
part: string;
|
|
255
|
+
view: string;
|
|
256
|
+
/** Whether this run cast min-wall rays at all. */
|
|
257
|
+
measuredMinWall: boolean;
|
|
258
|
+
subparts: SubPartFacts[];
|
|
259
|
+
aggregate: AggregateFacts;
|
|
260
|
+
overlaps: Overlap[];
|
|
261
|
+
/** Every sub-part pair's minimum surface distance. */
|
|
262
|
+
gaps: Gap[];
|
|
263
|
+
/** The pairs with an unintended-looking gap under the threshold. */
|
|
264
|
+
nearMisses: Gap[];
|
|
265
|
+
/** Every sub-part watertight and nothing interpenetrating. Near misses never affect it. */
|
|
266
|
+
ok: boolean;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/** Headless geometric report for one view of a part. */
|
|
270
|
+
export function measure(
|
|
271
|
+
kernel: GeometryKernel,
|
|
272
|
+
part: PartDefinition,
|
|
273
|
+
view?: string,
|
|
274
|
+
params?: ResolvedParams,
|
|
275
|
+
opts?: { minWall?: boolean; gapThreshold?: number },
|
|
276
|
+
): MeasureReport;
|
|
277
|
+
|
|
278
|
+
// --- verify -----------------------------------------------------------------
|
|
279
|
+
|
|
280
|
+
export type CheckStatus = "pass" | "fail" | "warn" | "skip";
|
|
281
|
+
|
|
282
|
+
export interface VerifyCheck {
|
|
283
|
+
scope: "view" | "subpart";
|
|
284
|
+
/** The sub-part name, `"a×b"` for a pair check, or `null` for a scalar view metric. */
|
|
285
|
+
subpart: string | null;
|
|
286
|
+
metric: string;
|
|
287
|
+
/** `gate` failures set a non-zero exit code; `warn` never blocks. */
|
|
288
|
+
kind: "gate" | "warn";
|
|
289
|
+
/** The asserted expression, stringified. */
|
|
290
|
+
expr: string;
|
|
291
|
+
actual: unknown;
|
|
292
|
+
status: CheckStatus;
|
|
293
|
+
pass: boolean | null;
|
|
294
|
+
message: string;
|
|
295
|
+
/** One self-contained corrective sentence (part-authored `hint` wins). */
|
|
296
|
+
hint?: string;
|
|
297
|
+
/** A stable ERROR-PATTERNS.md entry id. */
|
|
298
|
+
pattern?: string;
|
|
299
|
+
/** A caveat about HOW the value was measured — today only `minWall` sets one. */
|
|
300
|
+
note?: string;
|
|
301
|
+
/** `[x, y, z]` in mm, for the metrics that have one. */
|
|
302
|
+
location?: number[] | null;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
export interface VerifyCaseResult {
|
|
306
|
+
/** `"defaults"` or a preset name. */
|
|
307
|
+
name: string;
|
|
308
|
+
params: ResolvedParams;
|
|
309
|
+
checks: VerifyCheck[];
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export interface VerifyReport {
|
|
313
|
+
/** True when no check has status `"fail"`. */
|
|
314
|
+
ok: boolean;
|
|
315
|
+
view: string;
|
|
316
|
+
cases: VerifyCaseResult[];
|
|
317
|
+
/** Every failing check, flattened, each tagged with its `case`. */
|
|
318
|
+
failures: Array<VerifyCheck & { case: string }>;
|
|
319
|
+
warnings: Array<VerifyCheck & { case: string }>;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Enforce a part's `verify` block across the default config plus every preset
|
|
324
|
+
* (or its declared `cases`).
|
|
325
|
+
*
|
|
326
|
+
* `seed` lets a caller that has already measured the part hand the result in so
|
|
327
|
+
* verify does not recompute it. It is consulted only when the seed was measured
|
|
328
|
+
* with min wall (or this run needs none) and for the same view.
|
|
329
|
+
*/
|
|
330
|
+
export function verify(
|
|
331
|
+
kernel: GeometryKernel,
|
|
332
|
+
part: PartDefinition,
|
|
333
|
+
opts?: {
|
|
334
|
+
/** Force or override the DFM profile. */
|
|
335
|
+
process?: string | Record<string, unknown>;
|
|
336
|
+
view?: string;
|
|
337
|
+
measureFn?: typeof measure;
|
|
338
|
+
seed?: { params?: ResolvedParams; result: MeasureReport };
|
|
339
|
+
},
|
|
340
|
+
): VerifyReport;
|
|
341
|
+
|
|
342
|
+
// --- rendering --------------------------------------------------------------
|
|
343
|
+
|
|
344
|
+
/** The canonical angle names `renderViews` accepts. */
|
|
345
|
+
export const RENDER_VIEWS: string[];
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Render canonical-angle PNGs of one view with a pure-JS software rasterizer —
|
|
349
|
+
* no native module, no browser. Returns the written file paths.
|
|
350
|
+
*/
|
|
351
|
+
export function renderViews(
|
|
352
|
+
kernel: GeometryKernel,
|
|
353
|
+
part: PartDefinition,
|
|
354
|
+
view?: string,
|
|
355
|
+
opts?: {
|
|
356
|
+
views?: string[];
|
|
357
|
+
out?: string;
|
|
358
|
+
size?: [number, number];
|
|
359
|
+
edges?: boolean;
|
|
360
|
+
params?: ResolvedParams;
|
|
361
|
+
},
|
|
362
|
+
): Promise<string[]>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// partforge/worker — the geometry Web Worker runtime shared by every part.
|
|
2
|
+
//
|
|
3
|
+
// The host spawns this entry twice, named "manifold" (preview + STL + 3MF) and
|
|
4
|
+
// "occt" (STEP), via the Worker `name` option. Each instance lazily imports only
|
|
5
|
+
// its own backend.
|
|
6
|
+
|
|
7
|
+
import type { PartDefinition } from "./part.js";
|
|
8
|
+
|
|
9
|
+
/** The rebind handle `runWorker` returns. */
|
|
10
|
+
export interface WorkerHandle {
|
|
11
|
+
/**
|
|
12
|
+
* Swap the part this worker builds, cancelling stale builds, sweeping idle
|
|
13
|
+
* cache partitions and re-posting `ready` — so a remounting host gates its
|
|
14
|
+
* first generate exactly as on a fresh worker, without losing the warm kernel.
|
|
15
|
+
* The rebind contract is normative in docs/KERNEL-CONTRACT.md.
|
|
16
|
+
*/
|
|
17
|
+
setPart(newPart: PartDefinition): void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Run the worker job loop for `part`. Call once, at worker module top level. */
|
|
21
|
+
export function runWorker(part: PartDefinition): WorkerHandle;
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|