@metamesh/modeling 0.6.0 → 0.7.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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @metamesh/modeling
2
2
 
3
+ ## 0.7.0
4
+
5
+ ### Patch Changes
6
+
7
+ - b1b97b9: Reduce large-mesh finalization time and topology-analysis allocations without changing geometry, validation, or export output.
8
+
3
9
  ## 0.6.0
4
10
 
5
11
  ### Minor Changes
package/README.md CHANGED
@@ -4,7 +4,9 @@ Standalone geometry foundations and a modeling engine for Node applications, bro
4
4
 
5
5
  > **Preview:** `0.x` minor releases may break compatibility. Patches remain compatible within the same minor line. Stable support requires an explicit `1.0.0` review.
6
6
 
7
- Applications initialize one engine and build models with imported functions. Generator hosts provide that same execution scope; generators do not initialize WASM or dispose native values. No SDK or Testkit is required to use Modeling.
7
+ Applications initialize one engine and build models with imported functions. Generator hosts supply
8
+ the same execution scope and own WASM initialization and native cleanup. Modeling requires no SDK
9
+ or Testkit.
8
10
 
9
11
  ## API discovery and ownership
10
12
 
@@ -15,7 +17,10 @@ Applications initialize one engine and build models with imported functions. Gen
15
17
  | `@metamesh/modeling/engine` | Host initialization, scoped modeling, finalization, resource limits, and snapshot export |
16
18
  | `@metamesh/modeling/text` | Optional font-byte preparation and synchronous vector layout |
17
19
 
18
- These four entrypoints are the complete workspace and published API. Pure helpers cannot import the engine, native kernel, browser APIs, Studio, or Generators. Fontkit and WOFF2 decoding remain optional peers used only by `/text`. Hosts own acquisition, worker lifecycle, and loading. API reports define supported symbols; consumer fixtures verify published use.
20
+ These four entry points are the complete workspace and published API. Pure helpers cannot import
21
+ the engine, native kernel, browser APIs, Studio, or Generators. Fontkit and WOFF2 decoding are optional
22
+ peers used only by `/text`. Hosts own acquisition, worker lifecycle, and loading. API reports define
23
+ supported symbols; consumer fixtures verify published use.
19
24
 
20
25
  ## Modeling and lifecycle
21
26
 
@@ -51,11 +56,36 @@ const engine = await createModelEngine({ locateFile: () => modelingWasmUrl });
51
56
  const snapshot = engine.build(() => box({ size: [10, 20, 30] }));
52
57
  ```
53
58
 
54
- Imported functions and explicitly supplied `ModelingContext` methods execute the same operations. Imports contain no kernel initialization. The host binds the active context through a versioned symbol shared across bundles, then clears it in `finally`. Calls outside `build` throw. Builds are synchronous and cannot nest, including across different engines in one JavaScript realm; asynchronous preparation belongs before `build`. Separate Workers have independent execution scopes. An engine also rejects builds or exports while its asynchronous exporter is busy.
55
-
56
- `Solid` and `Section` are opaque immutable handles belonging to one synchronous build. Sections are native filled XY shapes, including holes and disconnected components. `union`, `subtract`, and `intersect` accept either Solid or Section operands and reject mixed dimensions. Offsets and Boolean operations retain native sections; `fromRegion` and `toRegion` are explicit conversion boundaries. Repeated sweeps and loft stations extract each Section once per execution; `toRegion` returns fresh immutable snapshots. Handles cannot cross builds or survive disposal. All native values dispose in reverse construction order from `finally`, including on failure. Installed bundles call host-owned methods, avoiding package-local native class or module identity.
57
-
58
- Builds return a bare solid (part ID `model`) or named `{ id, solid, color? }` / `{ id, mesh, color? }` descriptors. IDs are unique lowercase kebab-case strings of at most 64 characters; colors are six-digit hex RGB. Direct meshes are copied and checked for finite, nondegenerate, oriented closed topology and kernel reconstructability without copying them back from the kernel. Native extraction preserves one bounded retry for collapsed Float32 stitches. Descriptors for the same Solid share one extraction, copied manufacturing buffers, and immutable facts. Public `toTriangleMesh` inspection returns independent copies; mutable mesh inputs are captured and validated afresh for every build. Finalized parts retain shared bounds, counts, signed volume, area, and topology facts. Render normals remain outside manufacturing data.
59
+ Imported functions and `ModelingContext` methods execute the same operations. Imports do not
60
+ initialize the kernel. The host binds the active context through a versioned symbol shared across
61
+ bundles, then clears it in `finally`. Calls outside `build` throw.
62
+
63
+ Builds are synchronous and cannot nest, including across engines in one JavaScript realm.
64
+ Hosts finish asynchronous loading and service preparation before `build`; separate Workers have
65
+ independent execution scopes.
66
+ An engine rejects builds or exports while its asynchronous exporter is busy.
67
+
68
+ `Solid` and `Section` are opaque immutable handles belonging to one synchronous build. Sections are
69
+ native filled XY shapes, including holes and disconnected components. `union`, `subtract`, and
70
+ `intersect` accept either Solid or Section operands and reject mixed dimensions. Offsets and Booleans
71
+ retain native sections; `fromRegion` and `toRegion` are explicit conversion boundaries. Repeated
72
+ sweeps and loft stations extract each Section once per execution; `toRegion` returns fresh immutable
73
+ snapshots.
74
+
75
+ Handles cannot cross builds or survive disposal. All native values dispose in reverse construction
76
+ order from `finally`, including on failure. Installed bundles call host-owned methods, avoiding
77
+ package-local native class or module identity.
78
+
79
+ Builds return a bare solid (part ID `model`) or named `{ id, solid, color? }` / `{ id, mesh, color? }`
80
+ descriptors. IDs are unique lowercase kebab-case strings of at most 64 characters; colors are
81
+ six-digit hex RGB. Direct meshes are copied and checked for finite, nondegenerate, oriented closed
82
+ topology and kernel reconstructability without copying them back from the kernel. Native extraction
83
+ allows one bounded retry for collapsed Float32 stitches.
84
+
85
+ Descriptors for the same Solid share one extraction, copied manufacturing buffers, and immutable
86
+ facts. `toTriangleMesh` inspection returns independent copies; mutable mesh inputs are captured and
87
+ validated afresh for every build. Finalized parts retain shared bounds, counts, signed volume, area,
88
+ and topology facts. Render normals remain outside manufacturing data.
59
89
 
60
90
  Default limits are 64 parts, 1,000,000 vertices, 2,000,000 triangles, and 96 MiB of unique backing storage. Trusted applications may configure higher or lower ceilings through `createModelEngine({ limits })`. Per-build limits may only lower those configured ceilings, and snapshot export uses them too. Generator hosts own ceilings; generators receive no engine. Predictable allocations preflight safe-integer counts. Algorithm-specific numeric limits remain documented by their operations. Complexity fails explicitly instead of reducing fidelity.
61
91
 
@@ -74,7 +104,7 @@ work limits remain fixed operation safeguards.
74
104
 
75
105
  ## Resolution and conventions
76
106
 
77
- Primitive and pure loop factories share named options. These conventions apply across the applicable operations:
107
+ Primitive and pure loop factories share named options and these conventions:
78
108
 
79
109
  | Concept | Convention |
80
110
  | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -143,26 +173,48 @@ Boolean and other geometry construction results require deliberate definitions r
143
173
  inheriting anchors that may no longer describe their surfaces. Custom rigid frames remain useful;
144
174
  pure `boxAnchor3` and `cylinderAnchor3` calculate dimension-based frames without a native shape.
145
175
 
146
- `GeometryResolution` defaults to 0.05 mm maximum chord error and 16–512 segments per complete circle. Precedence is exact local segments, operation resolution, context-derived resolution, then engine defaults. `model.withResolution(policy)` and imported `modelingFunctions(policy)` return a local method set for the same build. Destructure those methods to apply the local policy; other imported operations retain the original build policy. There is no global resolution or font state. Exact counts express deliberate faceting or fixed correspondence; adaptive curves retain physical deviation and edge-length constraints.
176
+ `GeometryResolution` defaults to 0.05 mm maximum chord error and 16–512 segments per complete circle.
177
+ Precedence is exact local segments, operation resolution, context-derived resolution, then engine
178
+ defaults. Exact counts express deliberate faceting or fixed correspondence; adaptive curves retain
179
+ physical deviation and edge-length constraints.
180
+
181
+ `model.withResolution(policy)` and imported `modelingFunctions(policy)` return a local method set
182
+ for the same build. Destructure those methods to apply the local policy; other imports retain the
183
+ original build policy. There is no global resolution or font state.
147
184
 
148
185
  Revolution `segments` describes a complete circle; partial revolutions scale that count to the
149
186
  angular span and round up. Native filled revolutions need at least three intervals: adaptive
150
187
  sampling refines to that minimum, while exact counts below it fail explicitly. Pure `revolveMesh`
151
188
  supports one interval.
152
189
 
153
- `resamplePolyline2` and `resamplePolyline3` share physical sampling options and return immutable points with directed unit tangents. Open paths retain both endpoints; `closed: true` includes the closing edge and omits a repeated seam vertex. Exact closed counts require at least three segments. `regularPolygonLoop({ radius, sides, rotationDegrees, sizing })` creates a centered CCW point loop with an implicit seam. Shared `RadialSizing` is `"vertex"` by default or `"face"` to compensate polygon faceting so the requested radius reaches the faces; `CylinderOptions` owns that same sizing option.
190
+ `resamplePolyline2` and `resamplePolyline3` share physical sampling options and return immutable
191
+ points with directed unit tangents. Open paths retain both endpoints; `closed: true` includes the
192
+ closing edge and omits a repeated seam vertex. Exact closed counts require at least three segments.
193
+
194
+ `regularPolygonLoop({ radius, sides, rotationDegrees, sizing })` creates a centered CCW point loop
195
+ with an implicit seam. `RadialSizing` defaults to `"vertex"`; `"face"` compensates polygon faceting
196
+ so the requested radius reaches the faces. `CylinderOptions` uses the same sizing option.
154
197
 
155
198
  `normalizeLoop2`, `normalizePolygon2`, and `normalizeRegion2` return immutable geometry and may reuse internally validated results. External objects are validated even when frozen. Mutable mesh buffers are analyzed freshly at external boundaries; engine facts are reused inside their owner.
156
199
 
157
200
  ## Profiles and mechanical construction
158
201
 
159
- `sampleOpenProfile` samples upward radius/height line and cubic chains. Consumers own closure and product constraints. Point factories such as `circleLoop` and `rectangleLoop` return ordered loops; `Profile` owns identified components, contours and landmarks, and `CorrespondingProfile` retains their shared sampled slots. `rectangle`, `rectangleLoop` and `rectangleProfile` share `size`, `center` and `treatment` options. Use the root primitive for ordinary modeling; pure loop/profile construction serves custom data and explicit correspondence. Native filled sections do not promise stable vertex ordering.
202
+ `sampleOpenProfile` samples upward radius/height line and cubic chains. Consumers own closure and
203
+ product constraints. `circleLoop`, `rectangleLoop`, and other point factories return ordered loops.
204
+ `Profile` owns identified components, contours, and landmarks; `CorrespondingProfile` retains their
205
+ shared sampled slots.
206
+
207
+ `rectangle`, `rectangleLoop`, and `rectangleProfile` share `size`, `center`, and `treatment` options.
208
+ Use root primitives for ordinary modeling and pure loops/profiles for custom data and explicit
209
+ correspondence. Native filled sections do not promise stable vertex ordering.
160
210
 
161
211
  For a closed planar XYZ path, map the canonical loop with
162
212
  `rectangleLoop(options).map(([x, y]) => [x, y, 0] as const)`. Before sampling, cap its
163
213
  `limits.maxProfilePoints` at the smaller selected profile and path point ceilings. This keeps
164
214
  path allocation bounded and preserves all corner tangent endpoints and the implicit seam.
165
215
 
216
+ ### Extrusion and sweep
217
+
166
218
  `extrude(shape, { height, ... })`, `revolve(shape, options)` and `sweep(shape, path, options)` accept a filled Section or CorrespondingProfile, including holes and disconnected components. Open sweeps have watertight end caps; `closed: true` joins the path back to its beginning. Sweep options share degree twist, positive uniform or XY scale, and an XY scale pair per normalized path station. Closed scales interpolate along the implicit closing edge. A rotational symmetry seam uses `seamProfileOffset`, a counterclockwise logical point advance that reverses for clockwise hole contours; every contour must geometrically match the total twist. Incompatible seams fail explicitly.
167
219
 
168
220
  Adaptive extrusion sampling bounds the entire side triangles, including the interaction between
@@ -194,6 +246,8 @@ policies. For a planar closed path whose profile X should stay vertical, request
194
246
  `orientation: { kind: "transport", initialNormal: [0, 0, 1] }`. Closed policies use cyclic tangents
195
247
  and the same first frame on their closing gap.
196
248
 
249
+ ### Loft correspondence
250
+
197
251
  `loft(stations, options)` accepts a `profile` and exactly one horizontal `z` height or explicit
198
252
  right-handed `frame` per station. Stations stay in input order. Ordinary primitives keep the
199
253
  simple case at the root entry point:
@@ -213,7 +267,7 @@ With omitted `correspondence`, compatible rectangle stations, circle/ellipse rin
213
267
  regular polygons and equal-pointed stars use their retained authored landmarks. Rectangle corners
214
268
  can change among square, round and chamfer treatments, including collapsed or saturated rounds.
215
269
  Circle/ellipse and rectangle stations also share those quadrant/corner landmarks: a circular
216
- inlet becomes a rounded rectangle directly, without an authored saturated-square workaround.
270
+ inlet can become a rounded rectangle directly.
217
271
  Exact circular counts for this mixed family must divide by four; other deliberately faceted counts
218
272
  need an explicit geometric method. Adaptive counts align upward within the selected ceilings.
219
273
  XY affine transforms preserve these identities; this semantic path requires consistent handedness.
@@ -250,6 +304,13 @@ then next-only advances. Its objective sums connection lengths with the closing
250
304
  No tangent/supporting-plane association is exposed: general concave and spatial stations do not
251
305
  provide the convexity contract that such a method needs.
252
306
 
307
+ Identified `ProfileSpan2` supports lines, arcs, and cubic Béziers with `start`, `control1`, `control2`,
308
+ and `end`. Cubics reuse the curve evaluator and physical subdivision policy, selecting one uniform
309
+ parameter grid for corresponding spans. Fully collapsed spans retain logical slots without moving
310
+ endpoints. Sampled contours and components use `CorrespondingContour` and `CorrespondingComponent`.
311
+ Pass the complete family to `matchProfiles` with optional `limits.maxProfilePoints`; its default is
312
+ 100,000 points per profile, while a separate one-million-slot family work bound remains in force.
313
+
253
314
  For precise custom correspondence, prepare `matchProfiles` over explicit `Profile` specifications.
254
315
  It chooses the maximum physical interval count for each paired span across the family, including
255
316
  span-local `resolution`; identified line, arc and cubic kinds may change while IDs, order and
@@ -265,6 +326,8 @@ and the complete grid at one million logical slots. Simple-loop cap triangulatio
265
326
  eight-million-comparison work ceiling. Complexity rejects instead of reducing fidelity
266
327
  or substituting another method. Pure counterparts also include `sweepMesh`, `extrudeMesh` and `revolveMesh`.
267
328
 
329
+ ### Patterns and bounded surfaces
330
+
268
331
  `linearPattern(shape, { count, length })` determines equal intervals from exactly two of `count`,
269
332
  signed `spacing`, and signed total `length`; its direction defaults to +X. Length and spacing must
270
333
  fit a whole interval count. `gridPattern(shape, { count: [3, 2], spacing: [10, 8] })` supports XY
@@ -279,13 +342,6 @@ algorithm ceiling and selected output budgets reject before predictable native c
279
342
  varying factories are checked cumulatively before each transform. Path input and placement counts
280
343
  also obey `maxPathPoints`. Copies use the shared transform and lifetime checks.
281
344
 
282
- Identified `ProfileSpan2` supports lines, arcs, and cubic Béziers with `start`, `control1`, `control2`,
283
- and `end`. Cubics reuse the curve evaluator and physical subdivision policy, selecting one uniform
284
- parameter grid for corresponding spans. Fully collapsed spans retain logical slots without moving
285
- endpoints. Sampled contours and components use `CorrespondingContour` and `CorrespondingComponent`.
286
- Pass the complete family to `matchProfiles` with optional `limits.maxProfilePoints`; its default is
287
- 100,000 points per profile, while a separate one-million-slot family work bound remains in force.
288
-
289
345
  `heightfieldMesh` and the modeling `heightfield` create a closed grid surface with explicit XY size, segment counts, and base Z. Heights are absolute Z values strictly above the base. Allocation is checked before sampling, and the height callback runs once per grid point.
290
346
 
291
347
  Pure mechanical modules own thread profiles, sampling, printable fit, meshes, and allocation-free `preflightThreadedRod`, `preflightInternalThreadCutter`, and `preflightThreadedNut`. Engine methods own native construction. `pitch` is adjacent axial spacing; `lead = pitch × starts` is advance per turn. Internal cutters extend beyond requested faces and compensate faceting while preserving end treatments. Printable modeling forms are not certified thread classes or structural ratings.
@@ -304,7 +360,13 @@ Install `fontkit` to use `prepareVectorFont` from `/text`, and `woff2-encoder` w
304
360
 
305
361
  `analyzeTriangleMesh` validates and supplies bounds, counts, area, and signed volume in one traversal, with optional edge/component facts. Measurements, bounds, and signatures are independent immutable snapshots; external mutable buffers are inspected freshly on each call. Closed-edge checks require two oppositely wound faces per edge; they do not detect every geometric self-intersection. `createTriangleMeshSignature` adds quantized bounds, volume, topology, and a regression fingerprint, not a cryptographic digest.
306
362
 
307
- The optional `meshTopologyMode: "optimized"` build setting runs bounded planar edge flips for either execution lane. It preserves vertices, boundaries, winding, closure, reconstructability, area, and volume. Models above 200,000 triangles skip optimization; unsupported or failed candidates preserve their original part. Equal shared mesh views reuse one optimized candidate. The selected buffers must still fit all execution ceilings; optimization that would exceed them preserves the original model. This is local connectivity cleanup, not general remeshing. Source buffers never change in place.
363
+ The optional `meshTopologyMode: "optimized"` build setting runs bounded planar edge flips for either
364
+ execution lane. It preserves vertices, boundaries, winding, closure, reconstructability, area, and
365
+ volume. This is local connectivity cleanup, not general remeshing; source buffers never change in place.
366
+
367
+ Models above 200,000 triangles skip optimization. Unsupported or failed candidates retain their
368
+ original part, as do models whose optimized buffers would exceed an execution ceiling. Equal shared
369
+ mesh views reuse one optimized candidate.
308
370
 
309
371
  ## Quality gates
310
372
 
@@ -13,7 +13,9 @@ export interface BuildOptions {
13
13
  /** Optional bounded planar cleanup; unsupported candidates preserve the original part. */
14
14
  readonly meshTopologyMode?: "original" | "optimized";
15
15
  }
16
- /** Host engine. No native values or manually disposable handles are exposed. */
16
+ /**
17
+ * Host engine. No native values or manually disposable handles are exposed.
18
+ */
17
19
  export interface ModelEngine {
18
20
  /** Immutable resource ceilings selected when this engine was initialized. */
19
21
  readonly limits: ModelingLimits;
@@ -22,14 +24,18 @@ export interface ModelEngine {
22
24
  /** Serializes already finalized mesh parts; never prepares or builds a generator again. */
23
25
  export3mf(parts: readonly ExportMeshPart[], options?: Export3mfOptions): Promise<ArrayBuffer>;
24
26
  }
25
- /** Manufacturing buffers accepted by the snapshot exporter. */
27
+ /**
28
+ * Manufacturing buffers accepted by the snapshot exporter.
29
+ */
26
30
  export interface ExportMeshPart extends TriangleMesh {
27
31
  /** Stable canonical lowercase identity; unique within the containing topology or output. */
28
32
  readonly id: string;
29
33
  /** Optional six-digit hex RGB display color. */
30
34
  readonly color?: string;
31
35
  }
32
- /** Completed-snapshot serialization settings; filename selection belongs to the host. */
36
+ /**
37
+ * Completed-snapshot serialization settings; filename selection belongs to the host.
38
+ */
33
39
  export interface Export3mfOptions {
34
40
  /** Document display name; defaults to MetaMesh model. */
35
41
  readonly name?: string;
@@ -49,6 +55,17 @@ export { defaultModelingLimits };
49
55
  /** URL of the package's pinned WASM asset; browser bundlers may replace it with their emitted URL. */
50
56
  export declare const modelingWasmUrl: string;
51
57
 
52
- /** Initializes one host kernel and returns a lifecycle-safe engine. No generator initializes WASM. */
58
+ /**
59
+ * Initializes one host kernel and returns a lifecycle-safe engine. No generator initializes WASM.
60
+ *
61
+ * @example Build a box in a host application
62
+ * ```ts
63
+ * import { box } from "@metamesh/modeling";
64
+ * import { createModelEngine } from "@metamesh/modeling/engine";
65
+ *
66
+ * const engine = await createModelEngine();
67
+ * const snapshot = engine.build(() => box({ size: [30, 20, 8] }));
68
+ * ```
69
+ */
53
70
  export declare function createModelEngine(options?: ModelEngineOptions): Promise<ModelEngine>;
54
71
  export type { BuildOutput, FinalizedModel, FinalizedPart, ModelingContext, ModelingLimits, GeometryResolution, GeometrySamplingLimits, };