partforge 0.63.0 → 0.64.1

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.
Files changed (39) hide show
  1. package/bin/cli.js +2 -3
  2. package/docs/AUTHORING-PARTS.md +135 -0
  3. package/docs/ERROR-PATTERNS.md +29 -1
  4. package/docs/KERNEL-CONTRACT.md +15 -1
  5. package/package.json +1 -1
  6. package/src/app-import-demo.js +18 -0
  7. package/src/framework/app.css +8 -1
  8. package/src/framework/asset-resolve.js +71 -0
  9. package/src/framework/capture-build.js +12 -4
  10. package/src/framework/export-controller.js +12 -0
  11. package/src/framework/fonts.js +12 -30
  12. package/src/framework/geometry/contour-offset.js +18 -6
  13. package/src/framework/geometry/contour-ops.js +28 -1
  14. package/src/framework/geometry/kernel.js +2 -1
  15. package/src/framework/geometry/manifold-backend.js +41 -0
  16. package/src/framework/geometry/mesh-repair.js +87 -0
  17. package/src/framework/geometry/occt-backend.js +33 -1
  18. package/src/framework/geometry/profile.js +45 -3
  19. package/src/framework/geometry/stl-parse.js +45 -0
  20. package/src/framework/geometry/threemf-parse.js +87 -0
  21. package/src/framework/geometry-service.js +3 -1
  22. package/src/framework/imports.js +84 -0
  23. package/src/framework/jobs.js +20 -1
  24. package/src/framework/lint/index.js +2 -1
  25. package/src/framework/lint/rules-imports.js +115 -0
  26. package/src/framework/mount.js +94 -1
  27. package/src/framework/oracle/measure.js +28 -2
  28. package/src/framework/verify-metrics.js +10 -0
  29. package/src/framework/worker.js +11 -2
  30. package/src/import-demo-worker.js +3 -0
  31. package/src/parts/assets/import-demo-scan.stl +86 -0
  32. package/src/parts/import-demo.js +134 -0
  33. package/src/testing/assets.js +19 -0
  34. package/src/testing/manifold.js +14 -2
  35. package/src/testing/occt.js +5 -2
  36. package/src/testing/step-mesh-thread.js +15 -0
  37. package/src/testing/step-mesh.js +17 -0
  38. package/types/kernel.d.ts +6 -0
  39. package/types/part.d.ts +14 -0
@@ -0,0 +1,15 @@
1
+ // src/testing/step-mesh-thread.js — runs INSIDE the worker_thread only.
2
+ import { parentPort, workerData } from "node:worker_threads";
3
+ import { bootOcctKernel } from "./occt.js";
4
+
5
+ const kernel = await bootOcctKernel();
6
+ const out = [];
7
+ const transfer = [];
8
+ for (const { name, bytes, digest } of workerData) {
9
+ await kernel._registerImport({ name, digest, step: bytes });
10
+ const { positions, indices } = kernel.import(name).toIndexedMesh({ quality: "print" });
11
+ out.push({ name, digest, positions, indices });
12
+ transfer.push(positions.buffer, indices.buffer);
13
+ }
14
+ parentPort.postMessage(out, transfer);
15
+ process.exit(0);
@@ -0,0 +1,17 @@
1
+ // STEP → triangle mesh for the Node crossover (Manifold part importing STEP).
2
+ // OCCT boots in a worker_thread — a separate isolate is a separate WASM world,
3
+ // so the "never both kernels in one process" invariant holds by construction.
4
+ import { Worker } from "node:worker_threads";
5
+
6
+ export function tessellateStepAssets(entries) {
7
+ return new Promise((resolve, reject) => {
8
+ const w = new Worker(new URL("./step-mesh-thread.js", import.meta.url),
9
+ { workerData: entries.map(({ name, bytes, digest }) => ({ name, bytes, digest })) });
10
+ w.once("message", (out) => {
11
+ resolve(new Map(out.map((m) => [m.name, { digest: m.digest, positions: m.positions, indices: m.indices }])));
12
+ w.terminate();
13
+ });
14
+ w.once("error", reject);
15
+ w.once("exit", (code) => { if (code !== 0) reject(new Error(`step tessellation thread exited ${code}`)); });
16
+ });
17
+ }
package/types/kernel.d.ts CHANGED
@@ -477,6 +477,12 @@ export interface GeometryKernel {
477
477
  hullChain(inputs: HullInput[]): Shape2D;
478
478
  /** STEP bytes — OCCT only (Manifold throws `KernelCapabilityError`). */
479
479
  toSTEP(named: Array<{ name: string; solid: Solid }>): Promise<ArrayBuffer>;
480
+ /**
481
+ * Imported geometry declared in the part's `imports` field, registered
482
+ * pre-build by the framework via the underscore-prefixed `_registerImport`
483
+ * side-channel (not a part author's calling surface).
484
+ */
485
+ import(name: string): Solid;
480
486
 
481
487
  // Backend-optional: the sub-part cache brackets and WASM lifetime hooks. Every
482
488
  // framework caller reaches these through `?.`, so a third-party backend may
package/types/part.d.ts CHANGED
@@ -300,6 +300,14 @@ export interface SubPartDefinition<P = ResolvedParams, D = Derived> {
300
300
  enabled?: (p: P) => unknown;
301
301
  /** `false` = reference/preview-only: shown in the viewer, never exported. */
302
302
  exportable?: boolean;
303
+ /**
304
+ * Name of a declared `imports` entry this sub-part is held to. When set,
305
+ * `measure()` computes a `deviation` fact (symmetric-difference volume,
306
+ * volume delta %, bbox-corner drift) against that import's posed solid, and
307
+ * `verify.expect.<subpart>` may use the `refXorVolume` / `refVolumeDeltaPct`
308
+ * / `refBboxDelta` gate metrics.
309
+ */
310
+ reference?: string;
303
311
  /** Viewer-only override — `color` is `0xRRGGBB`, `opacity` is 0..1. */
304
312
  display?: { color?: number; opacity?: number };
305
313
  /** Filename / object name on export; defaults to the key. */
@@ -362,6 +370,12 @@ export interface SubPartExpectations {
362
370
  boundsMin?: Expectation;
363
371
  boundsMax?: Expectation;
364
372
  minWall?: Expectation;
373
+ /** Symmetric-difference volume vs. the sub-part's declared `reference` import. */
374
+ refXorVolume?: Expectation;
375
+ /** Percent volume delta vs. the sub-part's declared `reference` import. */
376
+ refVolumeDeltaPct?: Expectation;
377
+ /** Bounding-box corner drift `[dx, dy, dz]` vs. the sub-part's declared `reference` import. */
378
+ refBboxDelta?: Expectation;
365
379
  }
366
380
 
367
381
  /**