@metamesh/modeling 0.7.0 → 0.8.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 +24 -0
  2. package/README.md +76 -3
  3. package/dist/api/engine.d.ts +4 -0
  4. package/dist/api/geometry.d.ts +104 -19
  5. package/dist/api/index.d.ts +203 -18
  6. package/dist/api/text.d.ts +21 -7
  7. package/dist/engine/construction-operations.js +52 -10
  8. package/dist/engine/execution.js +55 -2
  9. package/dist/engine/export-3mf.js +17 -1
  10. package/dist/engine/finalization.js +27 -18
  11. package/dist/engine/inspection-operations.js +26 -22
  12. package/dist/engine/loft-operations.js +1 -1
  13. package/dist/engine/mechanical-operations.js +5 -8
  14. package/dist/engine/mesh-view-cache.js +23 -0
  15. package/dist/engine/native/adapter.js +2 -4
  16. package/dist/engine/native/distance.js +42 -0
  17. package/dist/engine/native/status.js +16 -0
  18. package/dist/engine/operation-environment.js +2 -1
  19. package/dist/engine/section-operations.js +3 -3
  20. package/dist/geometry/heightfield.js +101 -42
  21. package/dist/geometry/index.js +2 -0
  22. package/dist/geometry/loft-simple-mesh.js +2 -3
  23. package/dist/geometry/loft-triangulation.js +12 -9
  24. package/dist/geometry/loft.js +2 -6
  25. package/dist/geometry/mechanical/standards.js +2 -3
  26. package/dist/geometry/mechanical/threads.js +2 -3
  27. package/dist/geometry/mesh-centroid.js +36 -0
  28. package/dist/geometry/mesh-solid-intersections.js +123 -0
  29. package/dist/geometry/mesh-solid.js +284 -0
  30. package/dist/geometry/sweeps/sweep-closed.js +4 -4
  31. package/dist/geometry/sweeps/sweep-mesh.js +2 -6
  32. package/dist/geometry/sweeps/sweep-open.js +4 -4
  33. package/dist/geometry/sweeps/validation.js +1 -2
  34. package/dist/model/operations.js +10 -3
  35. package/dist/text/vector-text-layout.js +46 -3
  36. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # @metamesh/modeling
2
2
 
3
+ ## 0.8.0
4
+
5
+ ### Minor Changes
6
+
7
+ - bf1abe4: Add circular baselines to prepared vector text and Generator text services. Shaped glyphs and attached marks rotate rigidly around a millimetre radius, preserving kerning, ligatures, exact curve bounds and logical advances. Circular text is limited to one line, baseline vertical alignment, less than one revolution, and outlines that stay clear of the circle's center axis.
8
+ - bf1abe4: Complete solid splitting, plane clipping, Section hull/component/simplification operations, minimum-distance
9
+ queries and inspection facts. Add optional bounded geometric solid
10
+ validation at build/export boundaries and a pure uniform-density volume centroid helper.
11
+ Reuse immutable native inspection facts within a build and fresh owned topology analysis during
12
+ solid validation; share checks for identical buffer ranges without trusting mutable external facts.
13
+
14
+ Unify SVG default/preset limits and strict preset metadata checks. Preserve structured validation
15
+ codes and nested paths, add physical mesh assertions, and require runtime 2.1 for the current host
16
+ surface and issue metadata.
17
+
18
+ - bf1abe4: Add explicit uneven XY coordinates and optional shorter-edge or centre-error diagonal selection to heightfield and heightfieldMesh. Preserve uniform-grid sampling and fixed triangulation by default, with unchanged mesh allocation counts and strict Float32 coordinate checks.
19
+
20
+ Reject out-of-range section miter limits and inherited names in mechanical standards and thread-fit lookups. Avoid copying section polygons when only their count is needed.
21
+
22
+ ### Patch Changes
23
+
24
+ - bf1abe4: Reject cap triangulation workloads that cannot fit the comparison budget before ear clipping, preserving the existing work ceiling and input vertices.
25
+ - bf1abe4: Detect triangle crossings when an edge endpoint lies within the solid-validation tolerance, and avoid redundant topology analysis during solid validation.
26
+
3
27
  ## 0.7.0
4
28
 
5
29
  ### Patch Changes
package/README.md CHANGED
@@ -102,6 +102,16 @@ work limits remain fixed operation safeguards.
102
102
 
103
103
  `engine.export3mf` consumes selected finalized buffers without preparation or generation. Its lazy ZIP/XML serializer preserves exact selected vertices, triangle indices, semantic part names, and optional RGB display colors without a native or glTF conversion. Hosts own revisions, appearance overrides, filenames, delivery, and persistence.
104
104
 
105
+ Build and 3MF export accept `meshValidation: "solid"` for bounded geometric validation in addition
106
+ to their default indexed topology checks. This rejects intersecting/touching separate shells and
107
+ incorrectly oriented nested boundaries while preserving inward cavity shells. Checks apply to each
108
+ part independently; separate semantic parts may intentionally overlap. It uses the same pure
109
+ `validateSolidMesh` capability described below. Finalization reuses fresh topology facts for its
110
+ privately owned selected buffers; export captures mutable input before fresh analysis. Within one
111
+ operation, identical buffer ranges share checks. No caller-supplied or cross-operation facts are
112
+ trusted. Its additional work and failure ceilings are explicit;
113
+ the default `"topology"` mode retains the existing large-model workflow.
114
+
105
115
  ## Resolution and conventions
106
116
 
107
117
  Primitive and pure loop factories share named options and these conventions:
@@ -323,8 +333,10 @@ All loft routes share `LoftOptions`. Pure `loftMesh` requires an explicit method
323
333
  closed simple-loop mesh without an engine. Selected sampling/output budgets apply before expanded
324
334
  grids and mesh buffers; fixed aggregate safeguards bound correspondence at eight million comparisons
325
335
  and the complete grid at one million logical slots. Simple-loop cap triangulation also has an
326
- eight-million-comparison work ceiling. Complexity rejects instead of reducing fidelity
327
- or substituting another method. Pure counterparts also include `sweepMesh`, `extrudeMesh` and `revolveMesh`.
336
+ eight-million-comparison work ceiling. Caps whose minimum comparison count exceeds that ceiling
337
+ reject before ear clipping; other caps retain the running work check. Complexity rejects instead of
338
+ reducing fidelity or substituting another method. Pure counterparts also include `sweepMesh`,
339
+ `extrudeMesh` and `revolveMesh`.
328
340
 
329
341
  ### Patterns and bounded surfaces
330
342
 
@@ -342,16 +354,64 @@ algorithm ceiling and selected output budgets reject before predictable native c
342
354
  varying factories are checked cumulatively before each transform. Path input and placement counts
343
355
  also obey `maxPathPoints`. Copies use the shared transform and lifetime checks.
344
356
 
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.
357
+ `heightfield` returns a Solid for transforms and booleans; `heightfieldMesh` returns the same
358
+ closed construction as pure indexed data. Supply `size`, `segments`, and optional `origin` for a
359
+ uniform grid, or `coordinates: { x, y }` for absolute, unevenly spaced XY samples. Explicit axes
360
+ must increase strictly and remain distinct in Float32. This lets callers refine narrow borders
361
+ without remapping mesh vertices. Heights remain absolute Z values strictly above `baseZ` (default
362
+ zero); sampling never rescales the field.
363
+
364
+ `triangulation` defaults to `"fixed"`, preserving the lower-left to upper-right diagonal.
365
+ `"min-edge"` chooses the shorter 3D diagonal without extra sampling. `"center-error"` samples the
366
+ physical cell centre once and chooses the diagonal whose interpolated height is closer there.
367
+ Ties retain the fixed diagonal. Every policy uses two top triangles per cell and the same closed
368
+ walls and flat base; centre agreement does not guarantee accuracy elsewhere. Allocation and
369
+ coordinate precision are checked before height callbacks. Each grid point is sampled once, with
370
+ one additional callback per cell for `"center-error"`.
371
+
372
+ ```ts
373
+ heightfield({
374
+ coordinates: { x: [-15, -14.5, 0, 14.5, 15], y: [-10, -9.5, 0, 9.5, 10] },
375
+ triangulation: "min-edge",
376
+ height: ([x, y]) => 3 + Math.cos(x / 5) * Math.cos(y / 5),
377
+ });
378
+ ```
346
379
 
347
380
  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.
348
381
 
382
+ `split(solid, cutter)` returns intersection followed by subtraction as two same-build handles.
383
+ `splitByPlane(solid, { normal, offset })` returns the positive-normal side followed by the negative
384
+ side. Destructure `[positive]` or `[, negative]` to retain just one side. The nonzero normal is
385
+ normalized and `offset` is the signed distance from the world origin in millimetres (default zero).
386
+ Empty results are valid handles
387
+ for `isEmpty`, inspection and further operations, but cannot be finalized as manufacturing parts.
388
+
389
+ `hull` accepts Solid inputs/XYZ points or Section inputs/XY points. `components` and `simplify`
390
+ also preserve either dimension. Solid components are disconnected surface shells: inward cavity
391
+ shells count separately in `inspectSolid().parts` and cannot be finalized alone. Section components
392
+ are filled islands that retain their holes. `inspectSolid` includes surface area, native tolerance and emptiness;
393
+ `inspectSection` includes emptiness. Both return immutable native facts shared by repeated
394
+ inspections of the same shape within one build, including aliases and derived resolution contexts.
395
+ Transforms, Booleans and tolerance changes have their own facts. The execution clears these caches
396
+ on disposal; retained facts contain no live native values.
397
+
398
+ `distance(a, b)` measures the minimum separation between two nonempty solids in millimetres.
399
+ It returns zero for contact or overlapping material. The kernel's search range is managed internally;
400
+ there is no caller-supplied distance cutoff. Queries use native geometry and precision without
401
+ extracting a triangle mesh; empty, expired and foreign-build handles reject.
402
+
403
+ Installed Generators using these added host operations or Section overloads declare minimum runtime
404
+ 2.1.0. Imported operations require the current host context; package metadata declares the runtime
405
+ capabilities the Generator uses.
406
+
349
407
  `implicit` samples a finite positive-inside field within explicit bounds and grid spacing. Grid allocations and field evaluations are bounded. `components` returns same-build handles. `simplify` requires explicit millimetre tolerance and preserves native original-surface cleanup semantics.
350
408
 
351
409
  ## Prepared text and SVG
352
410
 
353
411
  Preparation shapes text once with the host's exact font service and retains immutable `VectorTextLayout`. Imported `textSection(layout, options)` consumes vector paths directly, without font shaping, fetching, or ambient providers. Paths and metrics use Y-up millimetres and a baseline origin. Empty layouts are measurable but cannot create sections. Font preparation remains optional and outside synchronous builds.
354
412
 
413
+ `baseline: { kind: "arc", radius }` places one line on a circle centered at the XY origin, in millimetres. Alignment defaults to `"center"` at `centerAngleDegrees` (90° from +X by default); `"left"` and `"right"` place the logical start and end at that datum. Direction defaults to `"clockwise"`, giving upright top lettering at 90°; `"counterclockwise"` at 270° gives upright bottom lettering. Shaped glyphs and attached zero-advance marks rotate rigidly, preserving their proportions, kerning and ligatures. Exact ink bounds describe the placed curves; `logicalWidth` and `advance[0]` retain the unwrapped arc length including spaces. Radius must be positive and at most 10000 mm. Arcs require baseline vertical alignment and reject multiline text, logical spans of a full revolution, and glyph outlines crossing the circle's center axis. Adjacent glyph collision and fit within a particular face remain consumer policy.
414
+
355
415
  Install `fontkit` to use `prepareVectorFont` from `/text`, and `woff2-encoder` when decoding WOFF2. These optional peers are loaded only when preparing fonts. `sampleTextContours` from `/geometry` and `textSection` share bounded path sampling and need no font dependencies for an already prepared layout.
356
416
 
357
417
  `svgSection` imports bounded painted SVG documents; `svgPathSection` imports filled path data. Y-down coordinates convert to Y-up; nonzero/even-odd filling preserves counters. Sampling applies after transforms and unit conversion. Basic shapes, groups, affine transforms, physical units, viewBox, fills, and strokes are supported. Text must be outlined. External resources, scripts, clipping, masks, filters, and unsupported content fail contextually. Pure parsers are shared with validation and the vector editor.
@@ -360,6 +420,19 @@ Install `fontkit` to use `prepareVectorFont` from `/text`, and `woff2-encoder` w
360
420
 
361
421
  `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.
362
422
 
423
+ Pure `validateSolidMesh(mesh, options)` adds spatial triangle tests, a single closed incident fan at
424
+ every vertex, and shell containment/orientation checks, including cavity boundaries and islands.
425
+ It never repairs or changes input buffers.
426
+ Floating-point contacts within the selected `tolerance` reject conservatively; this is not an exact
427
+ predicate or a manufacturing certification. Its fixed ceilings are 200,000 triangles and 2,000,000
428
+ spatial/containment tests. `maximumTests` may lower the work ceiling. Exhaustion throws
429
+ `COMPLEXITY_LIMIT` and never returns an unchecked result. All position vertices must be referenced.
430
+
431
+ `computeTriangleMeshCentroid(mesh)` returns an immutable uniform-density volume centroid in
432
+ millimetres for consistently oriented closed geometry, accounting for inward cavity shells.
433
+ It checks indexed closure and positive aggregate volume; use solid validation first for untrusted
434
+ geometric shell arrangements.
435
+
363
436
  The optional `meshTopologyMode: "optimized"` build setting runs bounded planar edge flips for either
364
437
  execution lane. It preserves vertices, boundaries, winding, closure, reconstructability, area, and
365
438
  volume. This is local connectivity cleanup, not general remeshing; source buffers never change in place.
@@ -6,6 +6,8 @@ declare const defaultModelingLimits: Readonly<ModelingLimits>;
6
6
 
7
7
  /** Execution settings supplied by the host, independently of generator metadata. */
8
8
  export interface BuildOptions {
9
+ /** Optional bounded intersection and nested-shell checks; defaults to indexed topology validation. */
10
+ readonly meshValidation?: "topology" | "solid";
9
11
  /** Per-build limits, no larger than the configured engine ceilings. */
10
12
  readonly limits?: Partial<ModelingLimits>;
11
13
  /** Physical sampling defaults; model-derived contexts and exact counts override these. */
@@ -37,6 +39,8 @@ export interface ExportMeshPart extends TriangleMesh {
37
39
  * Completed-snapshot serialization settings; filename selection belongs to the host.
38
40
  */
39
41
  export interface Export3mfOptions {
42
+ /** Optional bounded geometric solid checks on captured buffers; defaults to indexed topology validation. */
43
+ readonly meshValidation?: "topology" | "solid";
40
44
  /** Document display name; defaults to MetaMesh model. */
41
45
  readonly name?: string;
42
46
  }
@@ -425,6 +425,17 @@ export declare interface ArcCurve3 {
425
425
  */
426
426
  export declare const computeParallelTransportFrames: (path: readonly Vec3[], initialNormal?: Vec3, normalGuide?: PathNormalGuide, limits?: Partial<GeometrySamplingLimits>) => readonly PathFrame[];
427
427
 
428
+ /**
429
+ * Computes the uniform-density volume centroid of an oriented closed solid, including cavity shells.
430
+ * Coordinates are millimetres. Indexed closure and positive aggregate volume are required; callers
431
+ * may use validateSolidMesh first when geometric intersections and shell orientation are untrusted.
432
+ * Fresh mutable input is inspected on every call, and buffers are never changed.
433
+ *
434
+ * @param mesh - Consistently oriented closed mesh with positive enclosed volume.
435
+ * @returns A fresh immutable XYZ volume centroid.
436
+ */
437
+ export declare function computeTriangleMeshCentroid(mesh: TriangleMesh): Vec3;
438
+
428
439
  /** One sampled component retaining contour identities and logical point correspondence. */
429
440
  export declare interface CorrespondingComponent {
430
441
  /** Stable canonical lowercase identity; unique within the containing topology or output. */
@@ -983,9 +994,10 @@ export declare interface ArcCurve3 {
983
994
  export declare function getMetricFastenerStandard(size: MetricFastenerSize): MetricFastenerStandard;
984
995
 
985
996
  /**
986
- * Builds a closed indexed solid from a regularly sampled top, matched boundary walls, and flat base.
987
- * Heights are evaluated once per grid point after allocation preflight. Physical XY sizes and all
988
- * output coordinates must fit Float32 storage. Shared wall vertices prevent cracks at the base.
997
+ * Builds a closed indexed mesh from a rectilinear top, matched boundary walls, and flat base.
998
+ * Heights run once per grid point after preflight; center-error adds one evaluation per cell.
999
+ * Top winding is +Z, the base is -Z, and sides face outward. Coordinates and heights must fit
1000
+ * Float32 storage. Explicit axes are captured before sampling; shared wall vertices prevent cracks.
989
1001
  *
990
1002
  * @example Sample a closed wavy mesh
991
1003
  * ```ts
@@ -1000,23 +1012,58 @@ export declare interface ArcCurve3 {
1000
1012
  /** Trusted output ceilings for pure construction; omitted fields use engine defaults. */
1001
1013
  export declare type HeightfieldMeshLimits = Partial<Pick<ModelingLimits, "maxVertices" | "maxTriangles" | "maxBytes">>;
1002
1014
 
1003
- /** A regularly sampled, closed heightfield above a flat base, in millimetres. */
1004
- export declare interface HeightfieldOptions {
1005
- /** Positive XY footprint. */
1006
- readonly size: Vec2;
1007
- /** Exact positive integer X/Y interval counts. */
1008
- readonly segments: readonly [x: number, y: number];
1015
+ /**
1016
+ * A closed rectilinear heightfield above a flat base, with uniform or explicit XY samples in millimetres.
1017
+ *
1018
+ * Uniform sampling requires positive `size: [width, depth]` and positive integer `segments: [x, y]`.
1019
+ * Optional `origin` sets the lower-left XY corner; omitting it centres the footprint on XY.
1020
+ * Alternatively, supply `coordinates: { x, y }` with at least two strictly increasing finite
1021
+ * absolute samples per axis, distinct in Float32. This infers the footprint and interval counts;
1022
+ * `size`, `segments`, and `origin` cannot be combined with explicit coordinates.
1023
+ */
1024
+ export declare type HeightfieldOptions = {
1009
1025
  /** Absolute top Z at the supplied XY point. Every sample must be above baseZ. */
1010
1026
  readonly height: (point: Vec2) => number;
1011
1027
  /** Flat bottom Z. Defaults to zero. */
1012
1028
  readonly baseZ?: number;
1029
+ /**
1030
+ * Top-cell diagonal policy. Defaults to fixed (lower-left to upper-right).
1031
+ * min-edge selects the shorter 3D diagonal; center-error selects the diagonal whose endpoint
1032
+ * average is closest to one extra height sample at the physical cell centre. Ties use fixed.
1033
+ * All policies retain two triangles per cell; neither estimates error elsewhere in the cell.
1034
+ */
1035
+ readonly triangulation?: "fixed" | "min-edge" | "center-error";
1036
+ } & ({
1037
+ /** Positive XY footprint for uniform sampling. */
1038
+ readonly size: Vec2;
1039
+ /** Exact positive integer X/Y interval counts. */
1040
+ readonly segments: readonly [x: number, y: number];
1013
1041
  /** Lower-left footprint XY. Omitted centers the footprint on XY. */
1014
1042
  readonly origin?: Vec2;
1015
- }
1016
-
1017
- /** Predictable regular-grid allocation facts; this does not evaluate the height callback. */
1043
+ /** Explicit coordinates cannot be combined with uniform sampling. */
1044
+ readonly coordinates?: never;
1045
+ } | {
1046
+ /**
1047
+ * Absolute XY coordinates, each with at least two strictly increasing finite values that
1048
+ * remain distinct in Float32. Uneven spacing permits caller-owned local refinement.
1049
+ */
1050
+ readonly coordinates: {
1051
+ /** Absolute X samples in ascending order. */
1052
+ readonly x: readonly number[];
1053
+ /** Absolute Y samples in ascending order. */
1054
+ readonly y: readonly number[];
1055
+ };
1056
+ /** The footprint is inferred from explicit coordinates. */
1057
+ readonly size?: never;
1058
+ /** Interval counts are inferred from explicit coordinates. */
1059
+ readonly segments?: never;
1060
+ /** Explicit coordinates already include their origin. */
1061
+ readonly origin?: never;
1062
+ });
1063
+
1064
+ /** Predictable rectilinear-grid allocation facts; this does not evaluate the height callback. */
1018
1065
  export declare interface HeightfieldPreflight {
1019
- /** Number of grid points and height callback evaluations. */
1066
+ /** Number of top grid points; center-error also evaluates one height per cell. */
1020
1067
  readonly sampleCount: number;
1021
1068
  /** Number of allocated XYZ vertices. */
1022
1069
  readonly vertexCount: number;
@@ -1591,7 +1638,7 @@ export declare interface ArcCurve3 {
1591
1638
  readonly closed?: boolean;
1592
1639
  }
1593
1640
 
1594
- /** Checks regular-grid dimensions and bounds allocation before evaluating any height samples. */
1641
+ /** Checks grid dimensions, coordinate precision, and allocation bounds before evaluating heights. */
1595
1642
  export declare function preflightHeightfield(options: HeightfieldOptions, limits?: HeightfieldMeshLimits): HeightfieldPreflight;
1596
1643
 
1597
1644
  /**
@@ -2345,6 +2392,14 @@ export declare interface ArcCurve3 {
2345
2392
  readonly limits?: Partial<GeometrySamplingLimits>;
2346
2393
  }
2347
2394
 
2395
+ /** Bounded floating-point intersection and shell-orientation validation settings. */
2396
+ export declare interface SolidMeshValidationOptions {
2397
+ /** Work ceiling for spatial comparisons and containment tests; defaults to 2,000,000 and may only lower it. */
2398
+ readonly maximumTests?: number;
2399
+ /** Distance comparison policy in millimetres; near contacts reject conservatively. */
2400
+ readonly tolerance?: GeometryToleranceOptions;
2401
+ }
2402
+
2348
2403
  /**
2349
2404
  * Exactly one radius or diameter, placement, and tessellation for an origin-centered sphere.
2350
2405
  */
@@ -3044,6 +3099,22 @@ export declare interface ArcCurve3 {
3044
3099
  readonly outerRadius: number;
3045
3100
  }
3046
3101
 
3102
+ /**
3103
+ * Checks closed indexed topology, triangle intersections, and alternating nested shell orientation.
3104
+ * Outward exterior shells and inward cavity shells are accepted. Separate shells must not touch,
3105
+ * intersect, or overlap as material. Indexed neighbours may share their own vertex or edge.
3106
+ *
3107
+ * Uses bounded floating-point tests, not exact predicates or repair. Contacts within the selected
3108
+ * tolerance reject conservatively. At most 200,000 triangles and 2,000,000 spatial/containment tests
3109
+ * are supported; exhausting a ceiling throws COMPLEXITY_LIMIT rather than claiming validity.
3110
+ * Input buffers are freshly inspected and never modified; units are millimetres.
3111
+ *
3112
+ * @param mesh - Closed triangle mesh to inspect.
3113
+ * @param options - Optional lower work ceiling and numeric comparison policy.
3114
+ * @returns The original mesh after every requested check succeeds.
3115
+ */
3116
+ export declare function validateSolidMesh(mesh: TriangleMesh, options?: SolidMeshValidationOptions): TriangleMesh;
3117
+
3047
3118
  /**
3048
3119
  * Validates that a transform is finite, affine, and non-singular.
3049
3120
  *
@@ -3075,6 +3146,18 @@ export declare interface ArcCurve3 {
3075
3146
  /** A three-dimensional Cartesian vector or point. */
3076
3147
  export declare type Vec3 = readonly [x: number, y: number, z: number];
3077
3148
 
3149
+ /** A circular baseline centered at the XY origin, with rigid shaped glyph groups. */
3150
+ export declare interface VectorTextArcBaseline {
3151
+ /** Selects a circular baseline; glyph outlines rotate without bending or stretching. */
3152
+ readonly kind: "arc";
3153
+ /** Baseline radius in millimetres, greater than zero and at most 10000. */
3154
+ readonly radius: number;
3155
+ /** Alignment datum in degrees counterclockwise from +X; defaults to 90. */
3156
+ readonly centerAngleDegrees?: number;
3157
+ /** Reading direction around the circle; defaults to clockwise. */
3158
+ readonly direction?: "clockwise" | "counterclockwise";
3159
+ }
3160
+
3078
3161
  /** Exact curve bounds in millimetres; positive Y points upward. */
3079
3162
  export declare interface VectorTextBounds {
3080
3163
  /** Minimum X and Y of visible outlines, including accents, descenders, and overhangs. */
@@ -3094,14 +3177,16 @@ export declare interface ArcCurve3 {
3094
3177
  }
3095
3178
 
3096
3179
  /**
3097
- * Horizontal multiline text, bounded to 512 UTF-16 units and 16 lines without wrapping.
3180
+ * Text bounded to 512 UTF-16 units: horizontal lines or one circular line without wrapping.
3098
3181
  */
3099
3182
  export declare interface VectorTextLayoutOptions extends VectorTextOptions {
3100
- /** Place each line's advance at the origin; defaults to left. Ink overhang is preserved. */
3183
+ /** Place the advance's start, midpoint or end at the datum; defaults to center on arcs, otherwise left. */
3101
3184
  readonly align?: "left" | "center" | "right";
3185
+ /** Omitted uses horizontal lines. Arcs require one line and baseline vertical alignment. */
3186
+ readonly baseline?: VectorTextArcBaseline;
3102
3187
  /** Baseline spacing in ems, 0.5–5; omitted uses the selected face's natural vertical metrics. */
3103
3188
  readonly lineHeight?: number;
3104
- /** Place the first baseline or the complete ink bounds at Y=0; defaults to baseline. */
3189
+ /** Place the first baseline or complete ink bounds at Y=0; arcs only accept baseline. */
3105
3190
  readonly verticalAlign?: "baseline" | "top" | "center" | "bottom";
3106
3191
  }
3107
3192
 
@@ -3109,9 +3194,9 @@ export declare interface ArcCurve3 {
3109
3194
  export declare interface VectorTextMetrics {
3110
3195
  /** Visible curve bounds after alignment, or null when the text has no ink. */
3111
3196
  readonly bounds: VectorTextBounds | null;
3112
- /** Unaligned pen displacement: final line advance and negative baseline displacement, in mm. */
3197
+ /** Unaligned logical advance and negative baseline displacement in mm; arcs retain [arc length, 0]. */
3113
3198
  readonly advance: readonly [number, number];
3114
- /** Maximum line advance in millimetres, including spaces; distinct from visible ink width. */
3199
+ /** Maximum logical line advance in millimetres, including spaces; arc length on circular baselines. */
3115
3200
  readonly logicalWidth: number;
3116
3201
  /** Selected font's ascender distance above a baseline in millimetres, independent of the text. */
3117
3202
  readonly ascent: number;