@metamesh/modeling 0.10.0 → 0.12.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.
Files changed (36) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/README.md +70 -17
  3. package/dist/api/engine.d.ts +105 -8
  4. package/dist/api/index.d.ts +755 -771
  5. package/dist/engine/construction-operations.js +3 -1
  6. package/dist/engine/deferred-context.js +182 -0
  7. package/dist/engine/execution.js +38 -4
  8. package/dist/engine/finalization.js +65 -133
  9. package/dist/engine/geometry-recipes.js +12 -0
  10. package/dist/engine/immutable-generation-data.js +216 -0
  11. package/dist/engine/index.js +57 -35
  12. package/dist/engine/kernel/manifold.wasm +0 -0
  13. package/dist/engine/model-dependencies.js +46 -0
  14. package/dist/engine/model-summary.js +43 -0
  15. package/dist/engine/native/ownership.js +11 -5
  16. package/dist/engine/placement.js +24 -0
  17. package/dist/engine/recipe-realization.js +119 -0
  18. package/dist/engine/session-graph.js +447 -0
  19. package/dist/engine/session-native.js +90 -0
  20. package/dist/engine/session-snapshot.js +153 -0
  21. package/dist/engine/session.js +184 -0
  22. package/dist/engine/shape-anchors.js +5 -0
  23. package/dist/geometry/mesh-centroid.js +7 -6
  24. package/dist/geometry/mesh-planar.js +11 -10
  25. package/dist/geometry/mesh-quality.js +15 -14
  26. package/dist/geometry/mesh-signature.js +8 -7
  27. package/dist/geometry/mesh-solid.js +10 -4
  28. package/dist/geometry/mesh.js +36 -31
  29. package/dist/index.js +2 -0
  30. package/dist/model/execution-binding.js +5 -5
  31. package/dist/model/generation.js +27 -0
  32. package/dist/model/immutable-buffer.js +111 -0
  33. package/dist/model/modeling-context.js +1 -0
  34. package/dist/model/operations.js +521 -160
  35. package/dist/model/shape-handles.js +44 -0
  36. package/package.json +4 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,57 @@
1
1
  # @metamesh/modeling
2
2
 
3
+ ## 0.12.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 45d2617: Replace named computed bindings with an optional dependency array as the third argument:
8
+ `computed(id, callback, dependencies?)`. Automatic parameter and computation tracking remains
9
+ enabled; the array supplements it for values captured outside the callback, including destructured
10
+ parameters. Update existing named-binding calls to close over those values and list their dependencies.
11
+
12
+ Compare dependency entries by identity, preserve computed reader chaining and cache verification,
13
+ and reject mutable object or array dependencies without adding deep comparisons to generation.
14
+
15
+ ## 0.11.0
16
+
17
+ ### Minor Changes
18
+
19
+ - b5da778: Replace prepare/build with typed generation programs, optional computed values and authoritative
20
+ part placement. Add isolated session caches and deferred geometry with explicit snapshot ownership.
21
+ Expose immutable fluent Solid and Section methods for transforms, subtraction, finishing, and
22
+ Section-to-Solid construction, replacing the corresponding standalone operations and public
23
+ context facade. Generator packages require format 4 and runtime 4; previous executable contracts are unsupported.
24
+ - bfb7a63: Keep computed dependencies correct after conditional removal and re-registration, and prevent
25
+ caught evaluation failures from returning earlier cached values. Reduce immutable data copying,
26
+ centralize geometry finalization, and validate newly delivered resources once while preserving
27
+ per-revision part and aggregate resource checks.
28
+
29
+ Replace transparent cached typed arrays with explicit immutableBuffer and immutableMesh capture
30
+ helpers. Cached numeric data exposes length, at(), and copy(); uncaptured typed arrays in computed
31
+ results or bindings now reject. Snapshot and Worker output buffers remain ordinary typed arrays.
32
+
33
+ ### Patch Changes
34
+
35
+ - 88f5336: Correct immutable cache comparisons, recapture mutable mesh inputs on every run, and preserve
36
+ session ownership during concurrent Testkit operations. Reject unsupported hidden data fields
37
+ instead of silently dropping them, and reuse native operation contexts during recipe evaluation.
38
+ - 6c90e9d: Capture computed data in one validated traversal, reuse unchanged bindings and repeated dependency
39
+ analysis, and share recorded operation calls across result handles. Derive internal fluent dispatch
40
+ from the public shape contracts and skip cache retention work when caching is disabled.
41
+
42
+ Keep failed assertions attached to their revision even when author code catches them, preventing
43
+ invalid snapshots and cache commits.
44
+
45
+ - 60c458f: Validate placed manufacturing geometry under the requested solid policy after Float32 rounding.
46
+ Share world buffers between identical placed instances while still counting each instance toward
47
+ output limits, and include retained object storage and field names in optional cache estimates.
48
+ - a1f5aba: Reuse evaluated computed geometry as native operands across session runs without changing the public
49
+ API. Preserve anchor and profile metadata, bound optional retention, and release native allocations
50
+ on failed candidate generation, eviction, cache clearing, and session disposal.
51
+ - d4ffce7: Release obsolete computation callbacks and prior revision graphs when a revision closes, keeping
52
+ cached numerical recipes from retaining unrelated work. Consolidate recipe traversal and revision
53
+ cleanup without changing the public Modeling API.
54
+
3
55
  ## 0.10.0
4
56
 
5
57
  ### Minor Changes
package/README.md CHANGED
@@ -15,33 +15,86 @@ pnpm add @metamesh/modeling
15
15
  Initialize an engine, then build and export a plate with a hole:
16
16
 
17
17
  ```ts
18
- import { box, cylinder, subtract } from "@metamesh/modeling";
18
+ import { box, cylinder, generate } from "@metamesh/modeling";
19
19
  import { createModelEngine } from "@metamesh/modeling/engine";
20
20
 
21
21
  const engine = await createModelEngine();
22
- const snapshot = engine.build(
23
- () => {
24
- const body = box({ size: [30, 20, 6] });
25
- const bore = cylinder({ height: 8, radius: 3, sizing: "face" });
26
- return [{ id: "plate", solid: subtract(body, [bore]), color: "#67e8f9" }];
27
- },
28
- { resolution: { maxChordError: 0.025 } }
29
- );
22
+ const model = generate<Record<never, never>>(({ part }) => {
23
+ const body = box({ size: [30, 20, 6] });
24
+ const bore = cylinder({ height: 8, radius: 3, sizing: "face" });
25
+ return [part("plate", { geometry: body.subtract([bore]), color: "#67e8f9" })];
26
+ });
27
+ const session = engine.createSession(model, { resolution: { maxChordError: 0.025 } });
28
+ const snapshot = session.run({});
30
29
  const bytes = await engine.export3mf(snapshot.parts, { name: "Plate" });
30
+ snapshot.dispose();
31
+ session.dispose();
31
32
  ```
32
33
 
33
- Modeling calls run inside a synchronous build; handles belong to that build. Keep the finalized
34
- snapshot for rendering or export. For browser WASM setup and host lifecycle, follow
34
+ Constructors and fluent shape methods declare immutable recipes inside synchronous generation.
35
+ Use shape methods for transforms, subtraction, and finishing; collection operations such as
36
+ `union`, patterns, and observations remain functions. Calls apply in written order about the origin.
37
+ A method preserves its input; no-op operations may reuse a handle. A session owns optional
38
+ computed caches; captured snapshots stay usable after the session is disposed. Keep the finalized
39
+ snapshot for rendering or export, then dispose it. For browser WASM setup and host lifecycle, follow
35
40
  [Run Modeling in your application](https://github.com/JasonKoolman/metamesh/blob/main/apps/web/content/docs/engine/hosting.mdx).
36
41
 
42
+ ## Reuse unchanged work
43
+
44
+ Caching is optional. A named computation tracks parameter and other computation reads; part
45
+ placement and color stay separate from its geometry:
46
+
47
+ ```ts
48
+ const model = generate<{ height: number; gap: number }>(({ params, computed, part }) => {
49
+ const body = computed("body", () => box({ size: [10, 10, params.height] }));
50
+ return [part("left", body), part("right", { geometry: body, position: [10 + params.gap, 0, 0] })];
51
+ });
52
+ ```
53
+
54
+ Changing `gap` reuses the body's validated geometry. Changing `height` recomputes it. Read parameters
55
+ and computed readers inside the callback; locals calculated there keep those reads tracked.
56
+ For changing values prepared outside, add a dependency array:
57
+
58
+ ```ts
59
+ const { height } = params;
60
+ const body = computed("body", () => box({ size: [10, 10, height] }), [height]);
61
+ ```
62
+
63
+ Both forms track synchronous parameter and reader calls inside the callback. An earlier parameter
64
+ value or computed result carries no dependency into a later callback unless listed explicitly.
65
+ The array supplements automatic tracking; even `[]` still tracks reads inside the callback.
66
+ Entries compare with `Object.is`, without deep comparisons. Objects and arrays must be deeply
67
+ immutable; parameter snapshots and computed results already satisfy this contract. Fresh objects
68
+ still cause cache misses, so prefer scalar inputs and create temporary options inside the callback.
69
+ Computations are pure and synchronous; ordinary generation without caching remains supported.
70
+
71
+ Capture cached numeric arrays with `immutableBuffer(values)`: read `length` or `at(index)`, and
72
+ call `copy()` for an owned mutable typed array. Capture cached meshes with `immutableMesh(mesh)`
73
+ and pass their readers directly to `part`. Computed results and dependencies reject raw typed arrays;
74
+ ordinary transient mesh inputs, snapshots, and Worker output still use normal typed arrays.
75
+
76
+ The optional session setting `verifyCache: true` reruns callbacks on cache hits and compares bounded
77
+ data and recipes to diagnose observable missing dependencies. It can reject unsupported or large
78
+ comparisons and cannot prove arbitrary JavaScript; production caching leaves this probe disabled.
79
+
80
+ For expensive geometry followed by adjustable cuts or Booleans, put the base and finishing stage
81
+ in separate computations. Sessions can retain the evaluated base privately, preserving native
82
+ precision when it becomes an operand in a later run. Retention is bounded and optional; eviction
83
+ rebuilds the original recipe. Clear or dispose the session to release retained native geometry.
84
+
85
+ Use `assert(condition, message)` for a constraint that stops the revision and `warn(message)` for
86
+ nonblocking guidance. Hosts bind prepared deterministic capabilities through session `services`.
87
+ `validateModel` checks declarations and reached assertions without initializing a kernel; checks
88
+ that observe geometry use an initialized session's `validate` method.
89
+
37
90
  ## API discovery and ownership
38
91
 
39
- | Import | Use it for |
40
- | ------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------- |
41
- | [`@metamesh/modeling`](https://github.com/JasonKoolman/metamesh/blob/main/apps/web/content/reference/generated/model/index.mdx) | Solids, sections, transforms, and modeling operations |
42
- | [`@metamesh/modeling/geometry`](https://github.com/JasonKoolman/metamesh/blob/main/apps/web/content/reference/generated/geometry/index.mdx) | Pure paths, profiles, meshes, and calculations |
43
- | [`@metamesh/modeling/engine`](https://github.com/JasonKoolman/metamesh/blob/main/apps/web/content/reference/generated/engine/index.mdx) | Initialization, builds, limits, and 3MF export |
44
- | [`@metamesh/modeling/text`](https://github.com/JasonKoolman/metamesh/blob/main/apps/web/content/reference/generated/text/index.mdx) | Optional font preparation and vector layouts |
92
+ | Import | Use it for |
93
+ | ------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
94
+ | [`@metamesh/modeling`](https://github.com/JasonKoolman/metamesh/blob/main/apps/web/content/reference/generated/model/index.mdx) | Fluent solids and sections, constructors, and collection operations |
95
+ | [`@metamesh/modeling/geometry`](https://github.com/JasonKoolman/metamesh/blob/main/apps/web/content/reference/generated/geometry/index.mdx) | Pure paths, profiles, meshes, and calculations |
96
+ | [`@metamesh/modeling/engine`](https://github.com/JasonKoolman/metamesh/blob/main/apps/web/content/reference/generated/engine/index.mdx) | Sessions, snapshots, limits, and 3MF export |
97
+ | [`@metamesh/modeling/text`](https://github.com/JasonKoolman/metamesh/blob/main/apps/web/content/reference/generated/text/index.mdx) | Optional font preparation and vector layouts |
45
98
 
46
99
  ## Documentation
47
100
 
@@ -1,16 +1,16 @@
1
1
  import type { TriangleMesh } from "@metamesh/modeling/geometry";
2
- import type { BuildOutput, FinalizedModel, GeometryResolution, ModelingContext, ModelingLimits, FinalizedPart, GeometrySamplingLimits } from "@metamesh/modeling";
2
+ import type { FinalizedModel, FinalizedPart, GenerationDiagnostic, GeometryResolution, ModelingLimits, ModelProgram, Vec3 } from "@metamesh/modeling";
3
3
  /** Default host resource ceilings for parts, vertices, triangles, and transferable bytes. */
4
4
  declare const defaultModelingLimits: Readonly<ModelingLimits>;
5
5
 
6
6
 
7
7
  /** Execution settings supplied by the host, independently of generator metadata. */
8
- export interface BuildOptions {
8
+ export interface GenerationOptions {
9
9
  /** Optional bounded intersection and nested-shell checks; defaults to indexed topology validation. */
10
10
  readonly meshValidation?: "topology" | "solid";
11
11
  /** Per-build limits, no larger than the configured engine ceilings. */
12
12
  readonly limits?: Partial<ModelingLimits>;
13
- /** Physical sampling defaults; model-derived contexts and exact counts override these. */
13
+ /** Physical sampling defaults; operation options and exact counts override these. */
14
14
  readonly resolution?: GeometryResolution;
15
15
  /** Optional bounded planar cleanup; unsupported candidates preserve the original part. */
16
16
  readonly meshTopologyMode?: "original" | "optimized";
@@ -21,11 +21,90 @@ export interface BuildOptions {
21
21
  export interface ModelEngine {
22
22
  /** Immutable resource ceilings selected when this engine was initialized. */
23
23
  readonly limits: ModelingLimits;
24
- /** Runs once, finalizes copied parts, and disposes all native values on every exit. */
25
- build(build: (model: ModelingContext) => BuildOutput, options?: BuildOptions): FinalizedModel;
24
+ /** Creates isolated caches; prepared service capabilities remain fixed for its lifetime. */
25
+ createSession<P, S>(program: ModelProgram<P, S>, ...options: keyof S extends never ? [options?: SessionOptions<S>] : [options: SessionOptions<S> & {
26
+ readonly services: S;
27
+ }]): ModelSession<P>;
26
28
  /** Serializes already finalized mesh parts; never prepares or builds a generator again. */
27
29
  export3mf(parts: readonly ExportMeshPart[], options?: Export3mfOptions): Promise<ArrayBuffer>;
28
30
  }
31
+ /** Fixed generation policy and deterministic host capabilities for one model session. */
32
+ export interface SessionOptions<S = Record<never, never>> extends GenerationOptions {
33
+ /** Host-owned synchronous capabilities, required when declared by the program. The original
34
+ * environment is retained; its behavior and assets must remain deterministic and fixed for
35
+ * this session. Equivalent function wrappers do not constitute an asset revision.
36
+ */
37
+ readonly services?: S;
38
+ /** Enables reuse; defaults to true. Set false for uncached output equivalence checks. */
39
+ readonly cache?: boolean;
40
+ /** Development-only cache-hit probe: reruns pure callbacks and compares bounded data/recipes.
41
+ * Unsupported closures or large results fail explicitly; this cannot prove arbitrary JavaScript.
42
+ */
43
+ readonly verifyCache?: boolean;
44
+ }
45
+ /** Validated local geometry resource; accessors return owned buffer copies. */
46
+ export interface GeometryResource extends TriangleMesh {
47
+ /** Session-scoped immutable resource identity, stable while geometry is reused. */
48
+ readonly id: string;
49
+ /** Immutable resource revision; a new resource receives a new identity. */
50
+ readonly revision: number;
51
+ /** Facts for the exact local manufacturing buffers. */
52
+ readonly measurements: FinalizedPart["measurements"];
53
+ }
54
+ /** A world-space manufacturing part plus its reusable local renderer resource. */
55
+ export interface SnapshotPart extends FinalizedPart {
56
+ /** Validated geometry in local coordinates, independent of placement and appearance. */
57
+ readonly geometry: GeometryResource;
58
+ /** XYZ translation in millimetres after rotation. */
59
+ readonly position: Vec3;
60
+ /** Fixed global X, then Y, then Z rotation in degrees. */
61
+ readonly rotation: Vec3;
62
+ }
63
+ /** Captured immutable revision; disposal never changes another snapshot or an in-flight export. */
64
+ export interface ModelSnapshot extends FinalizedModel {
65
+ /** Ordered world-space parts, each referring to reusable local geometry. */
66
+ readonly parts: readonly SnapshotPart[];
67
+ /** Nonblocking diagnostics emitted for this revision. */
68
+ readonly diagnostics: readonly GenerationDiagnostic[];
69
+ /** Known dependency graph; outer reads remain conservative model-wide dependencies. */
70
+ readonly dependencies: ModelDependencies;
71
+ /** Releases this snapshot's references; idempotent, further access is invalid. */
72
+ dispose(): void;
73
+ }
74
+ /** Known dependencies for inspection; plain JavaScript bindings do not reveal their parameter origin. */
75
+ export interface ModelDependencies {
76
+ /** Parameter keys read outside computed callbacks, affecting structure or ordinary code. */
77
+ readonly modelParameters: readonly string[];
78
+ /** Direct dependencies recorded by successfully evaluated computations. */
79
+ readonly computations: readonly {
80
+ /** Session-local computation identity. */
81
+ readonly id: string;
82
+ /** Direct top-level parameter reads. */
83
+ readonly parameters: readonly string[];
84
+ /** Direct computed-reader dependencies. */
85
+ readonly computations: readonly string[];
86
+ }[];
87
+ /** Part reader roots and known transitive parameter dependencies. */
88
+ readonly parts: readonly {
89
+ /** Stable semantic output part identity. */
90
+ readonly id: string;
91
+ /** Computed readers supplied as geometry or placement roots. */
92
+ readonly computations: readonly string[];
93
+ /** Known transitive parameter dependencies through these roots. */
94
+ readonly parameters: readonly string[];
95
+ }[];
96
+ }
97
+ /** Stateful execution owner; a failed revision cannot change previously returned snapshots. */
98
+ export interface ModelSession<P> {
99
+ /** Generates an atomic manufacturing snapshot or throws; parameters are copied and frozen. */
100
+ run(params: Readonly<P>): ModelSnapshot;
101
+ /** Runs declaration and reached assertions without realizing returned parts. */
102
+ validate(params: Readonly<P>): readonly GenerationDiagnostic[];
103
+ /** Clears optional caches without invalidating captured snapshots. */
104
+ clearCache(): void;
105
+ /** Releases session ownership; idempotent, captured snapshots remain usable. */
106
+ dispose(): void;
107
+ }
29
108
  /**
30
109
  * Manufacturing buffers accepted by the snapshot exporter.
31
110
  */
@@ -59,17 +138,35 @@ export { defaultModelingLimits };
59
138
  /** URL of the package's pinned WASM asset; browser bundlers may replace it with their emitted URL. */
60
139
  export declare const modelingWasmUrl: string;
61
140
 
141
+ /**
142
+ * Runs declaration and immediate assertions without initializing a kernel or finalizing parts.
143
+ * Geometry observations require an initialized session's `validate` method instead.
144
+ */
145
+ export declare function validateModel<P, S>(program: ModelProgram<P, S>, params: Readonly<P>, ...settings: keyof S extends never ? [options?: SessionOptions<S>] : [options: SessionOptions<S> & {
146
+ readonly services: S;
147
+ }]): readonly GenerationDiagnostic[];
62
148
  /**
63
149
  * Initializes one host kernel and returns a lifecycle-safe engine. No generator initializes WASM.
64
150
  *
65
151
  * @example Build a box in a host application
66
152
  * ```ts
67
- * import { box } from "@metamesh/modeling";
153
+ * import { box, generate } from "@metamesh/modeling";
68
154
  * import { createModelEngine } from "@metamesh/modeling/engine";
69
155
  *
70
156
  * const engine = await createModelEngine();
71
- * const snapshot = engine.build(() => box({ size: [30, 20, 8] }));
157
+ * const model = generate<Record<never, never>>(({ part }) => [part("body", box({ size: [30, 20, 8] }))]);
158
+ * const session = engine.createSession(model);
159
+ * try {
160
+ * const snapshot = session.run({});
161
+ * try {
162
+ * await engine.export3mf(snapshot.parts);
163
+ * } finally {
164
+ * snapshot.dispose();
165
+ * }
166
+ * } finally {
167
+ * session.dispose();
168
+ * }
72
169
  * ```
73
170
  */
74
171
  export declare function createModelEngine(options?: ModelEngineOptions): Promise<ModelEngine>;
75
- export type { BuildOutput, FinalizedModel, FinalizedPart, ModelingContext, ModelingLimits, GeometryResolution, GeometrySamplingLimits, };
172
+ export type { ModelingLimits };