@meshioplusplus/wasm 11.0.0 → 12.1.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/index.d.ts CHANGED
@@ -20,6 +20,40 @@ export interface SurfaceQualityInfo {
20
20
  watertight: boolean;
21
21
  }
22
22
 
23
+ /**
24
+ * A `point_data`/`cell_data`/`field_data` array's JS type: it carries its
25
+ * source dtype crossing the WASM boundary instead of always widening to
26
+ * `Float64Array` (roadmap §1 "WASM parity": dtype carry). `BigInt64Array`/
27
+ * `BigUint64Array` elements are JS `bigint`, not `number` -- see
28
+ * {@link XdmfTimeSeriesWriter.writeDataArrays} if you need to feed one to an
29
+ * API that still expects `number`s.
30
+ */
31
+ export type DataArray =
32
+ | Float32Array
33
+ | Float64Array
34
+ | Int8Array
35
+ | Int16Array
36
+ | Int32Array
37
+ | BigInt64Array
38
+ | Uint8Array
39
+ | Uint16Array
40
+ | Uint32Array
41
+ | BigUint64Array;
42
+
43
+ /**
44
+ * The index maps an op that prunes/renumbers points and cells returns when
45
+ * called with `returnMaps: true`, alongside its `mesh`: `pointMap` is input
46
+ * point index -> output point index (-1 if pruned), `cellMaps` is one array
47
+ * per **input** cell block, input cell -> output index within the
48
+ * corresponding output block (-1 if dropped). See doc/wasm.md's "Index maps"
49
+ * section for the per-op semantics (e.g. what a collapsed/welded point's
50
+ * -1-or-survivor value means).
51
+ */
52
+ export interface PointCellMaps {
53
+ pointMap: Int32Array;
54
+ cellMaps: Int32Array[];
55
+ }
56
+
23
57
  export interface RectangularCellBlock {
24
58
  /** meshio++ cell type name, e.g. "triangle", "tetra10", "hexahedron". */
25
59
  type: string;
@@ -50,10 +84,11 @@ export interface PolygonCellBlock {
50
84
  * start index into the face list (length numCells + 1, so cell `c`'s faces
51
85
  * are `faceOffsets[cellOffsets[c]] .. faceOffsets[cellOffsets[c + 1]]`).
52
86
  *
53
- * No C++ format writer accepts a polyhedron block yet (a documented,
54
- * pre-existing gap) -- this shape crosses the JS boundary correctly (e.g.
55
- * through {@link MeshioPlusPlusModule.clean} or any other operation) but
56
- * `writeMesh` will throw naming the format when asked to write one.
87
+ * `writeMesh` accepts a polyhedron block for the formats that can hold one
88
+ * (`vtu`, `ensight` nfaced, `cgns` NFACE_n, `med` POE, `openfoam`); a format
89
+ * that cannot (legacy `vtk`, `vtp`, ...) throws naming the format. The shape
90
+ * also crosses the JS boundary unchanged through operations such as
91
+ * {@link MeshioPlusPlusModule.clean}.
57
92
  */
58
93
  export interface PolyhedronCellBlock {
59
94
  type: string;
@@ -81,9 +116,10 @@ export interface Mesh {
81
116
  /**
82
117
  * name -> flat, row-major per-point data. A multi-component (vector/tensor)
83
118
  * array is stored interleaved, `numPoints * components` long, with its width
84
- * declared in {@link Mesh.point_data_components}.
119
+ * declared in {@link Mesh.point_data_components}. Each array's JS type is
120
+ * its source dtype (see {@link DataArray}), not always `Float64Array`.
85
121
  */
86
- point_data?: Record<string, Float64Array>;
122
+ point_data?: Record<string, DataArray>;
87
123
  /**
88
124
  * Per-entity width of any `point_data` array that is not a scalar, since a
89
125
  * flat typed array carries no shape. A name absent here has one component.
@@ -91,8 +127,13 @@ export interface Mesh {
91
127
  * scalar-only mesh gets an empty object.
92
128
  */
93
129
  point_data_components?: Record<string, number>;
94
- /** name -> one flat array per cell block, same order as `cells`. */
95
- cell_data?: Record<string, Float64Array[]>;
130
+ /**
131
+ * name -> one flat array per cell block, same order as `cells`. Every
132
+ * block of one named array shares the same {@link DataArray} class --
133
+ * `writeMesh`/`convert`-side callers that mix classes across blocks of the
134
+ * same name get a thrown Error naming the array.
135
+ */
136
+ cell_data?: Record<string, DataArray[]>;
96
137
  /**
97
138
  * Per-entity width of any `cell_data` array that is not a scalar. One value
98
139
  * per *array*, not per block: every block of a named cell_data array must
@@ -100,11 +141,141 @@ export interface Mesh {
100
141
  */
101
142
  cell_data_components?: Record<string, number>;
102
143
  /** name -> scalar/small metadata arrays (e.g. material ids). */
103
- field_data?: Record<string, Float64Array>;
144
+ field_data?: Record<string, DataArray>;
104
145
  /** Per-entity width of any `field_data` array that is not a scalar. */
105
146
  field_data_components?: Record<string, number>;
106
147
  /** Named groups of points / cells / cell facets (see {@link Region}). */
107
148
  regions?: Region[];
149
+ /** `Begin Properties` blocks -- Kratos material data (currently MDPA only). See {@link PropertySet}. */
150
+ propertySets?: PropertySet[];
151
+ /**
152
+ * Format-specific side-channel metadata a generic `Mesh` cannot represent,
153
+ * attached by `readMeshSelective(path, {info: true})`. Present only for
154
+ * formats with one (openfoam/med/mdpa/ansysinp/unv/gmsh/exodus); absent
155
+ * otherwise, even when `info: true` was requested. Its own `format` field
156
+ * is what `writeMesh` checks before reusing it on a write with no explicit
157
+ * `options.info`. See doc/wasm.md's "Side channel (info)" section.
158
+ */
159
+ info?: MeshInfo;
160
+ }
161
+
162
+ /** The union of every format's side-channel `info` shape. Discriminate on `format`. */
163
+ export type MeshInfo =
164
+ | OpenFoamInfo
165
+ | MedInfo
166
+ | MdpaInfo
167
+ | AnsysInfo
168
+ | UnvInfo
169
+ | GmshInfo
170
+ | ExodusInfo;
171
+
172
+ /** OpenFOAM's side channel: `readMeshSelective`'s cell_tags/patch-type map, reshaped per patch. */
173
+ export interface OpenFoamInfo {
174
+ format: 'openfoam';
175
+ patches: Array<{
176
+ /** The negative, MED-style family id `cell_tags` uses for this patch. */
177
+ familyId: number;
178
+ /** Names this family id is known by (usually one; more than one on a collision). */
179
+ names: string[];
180
+ /** The patch `type` (`patch`/`wall`/`symmetry`/...), when known. */
181
+ type?: string;
182
+ }>;
183
+ }
184
+
185
+ /** MED's side channel: family/group names, units, and per-field step/time metadata. */
186
+ export interface MedInfo {
187
+ format: 'med';
188
+ /** Point family id -> subset name(s), as `point_tags`/`cell_tags` key them. */
189
+ pointTags: Record<string, string[]>;
190
+ cellTags: Record<string, string[]>;
191
+ meshName: string;
192
+ description: string;
193
+ unitTime: string;
194
+ unitCoords: string;
195
+ /** Family id -> its `FAM_<id>...` group link name. */
196
+ pointTagGroups: Record<string, string>;
197
+ cellTagGroups: Record<string, string>;
198
+ /** Lenient-mode only: constructs this read could not represent, verbatim. */
199
+ skippedConstructs: string[];
200
+ /** Lenient-mode only: field name -> `[UNI, UNT]` unit strings. */
201
+ fieldUnits: Record<string, [string, string]>;
202
+ /** Lenient-mode only: field name -> `{ndt, nor, pdt}` (MED's own step/order/time triple). */
203
+ stepMeta: Record<string, { ndt: number; nor: number; pdt: number }>;
204
+ /** Field name -> every step's time value (always filled, not lenient-only). */
205
+ fieldTimeValues: Record<string, number[]>;
206
+ }
207
+
208
+ /** MDPA's side channel: per-block entity names. Properties ride on `mesh.propertySets` instead (see {@link PropertySet}), not here. */
209
+ export interface MdpaInfo {
210
+ format: 'mdpa';
211
+ /** One entry per cell block, mesh block order. */
212
+ entityNames: Array<{ name: string; isCondition: boolean }>;
213
+ /** Lenient-mode only: constructs this read could not represent, verbatim. */
214
+ skippedConstructs: string[];
215
+ }
216
+
217
+ /**
218
+ * The shared shape of Ansys (`.cdb`/`.inp`, MAPDL) and UNV's side channel:
219
+ * named point/cell sets the generic {@link Region} shape does not carry.
220
+ */
221
+ export interface AnsysUnvInfoShape {
222
+ /** Set name -> 0-based point/node indices. */
223
+ pointSets: Record<string, number[]>;
224
+ /** Set name -> one array of 0-based local indices per cell block. */
225
+ cellSets: Record<string, number[][]>;
226
+ }
227
+
228
+ export interface AnsysInfo extends AnsysUnvInfoShape {
229
+ format: 'ansysinp';
230
+ }
231
+
232
+ export interface UnvInfo extends AnsysUnvInfoShape {
233
+ format: 'unv';
234
+ }
235
+
236
+ /** Gmsh's side channel: `$Entities` bounding-entity tags, one array per cell block. */
237
+ export interface GmshInfo {
238
+ format: 'gmsh';
239
+ boundingEntities: number[][];
240
+ }
241
+
242
+ /** Exodus's side channel: info/QA records. Read-only -- there is no Info-bearing Exodus writer. */
243
+ export interface ExodusInfo {
244
+ format: 'exodus';
245
+ infoRecords: string[];
246
+ }
247
+
248
+ /**
249
+ * One `Begin Properties <id>` block: an id plus its entries, in file order.
250
+ * Carried on the {@link Mesh} object itself (like {@link Region}), keyed by
251
+ * id rather than entity index, so operations that renumber cells/points
252
+ * cannot invalidate them. Shape-preserving operations (`clean`, `smooth`,
253
+ * `transform`, `attachQuality`, the `data*` ops) carry them through;
254
+ * restructuring and multi-input ones (`merge`, `cropBbox`/`cropPlane`/
255
+ * `cropPredicate`, `split`, `partition`, `diff`) do not.
256
+ */
257
+ export interface PropertySet {
258
+ id: number;
259
+ values: PropertyValue[];
260
+ }
261
+
262
+ /**
263
+ * One `KEY value` entry of a properties block. Exactly one of `values` and
264
+ * `text` carries the value: a plain number is a one-element `values`; an
265
+ * inline `Begin Table` is an `(n, k)` `values` (flat, row-major -- `k` given
266
+ * by `components` when `k > 1`) with `isTable` set and `key` holding the
267
+ * table header's arguments verbatim; anything else (a constitutive-law name,
268
+ * a bracketed vector/matrix) is kept verbatim in `text`, which is what makes
269
+ * an unrecognized value lossless.
270
+ */
271
+ export interface PropertyValue {
272
+ key: string;
273
+ values: Float64Array;
274
+ /** Per-entity width of `values` when `isTable` and `k > 1`. Absent means 1. */
275
+ components?: number;
276
+ /** The value verbatim, when it is not numeric (`values` is then empty). */
277
+ text: string;
278
+ isTable: boolean;
108
279
  }
109
280
 
110
281
  /**
@@ -150,7 +321,34 @@ export interface RegionSummary {
150
321
  numEntries: number;
151
322
  }
152
323
 
153
- export interface ConvertOptions {
324
+ /**
325
+ * Parameterized-write options for `writeMesh`/`convert`, all optional --
326
+ * unset/empty reproduces the exact write from before v11.2.0. There is
327
+ * deliberately no gzip level or VTK 4.2/5.1 selector: neither exists as a
328
+ * `WriteOptions` field on the C++ side (gzip level 4 is a fixed registry
329
+ * default; `vtk42`/`vtk51` are separate format keys, not a `vtk` option).
330
+ */
331
+ export interface MeshWriteOptions {
332
+ /** ASCII vs binary. Errors for a format with only one variant. */
333
+ encoding?: "ascii" | "binary";
334
+ /** Block-compression codec, for the VTK-XML formats (vtu/vtp) only. */
335
+ codec?: "none" | "zlib" | "lz4" | "zstd";
336
+ /** `printf`-style float format for ASCII writers that take one (e.g. `".16e"`, the default). */
337
+ floatFormat?: string;
338
+ }
339
+
340
+ /**
341
+ * `writeMesh`'s own options: `MeshWriteOptions` plus `info`, a format's
342
+ * side-channel metadata to write (see {@link MeshInfo}) -- wins over a
343
+ * `mesh.info` whose own `format` matches this write's. Given for a format
344
+ * with no side-channel writer (openfoam/mdpa/ansysinp/unv/gmsh/med are the
345
+ * writable ones; exodus is read-only) throws naming it.
346
+ */
347
+ export interface MeshWriteOptionsWithInfo extends MeshWriteOptions {
348
+ info?: MeshInfo;
349
+ }
350
+
351
+ export interface ConvertOptions extends MeshWriteOptions {
154
352
  /** Explicit input format key, or omit to infer from inPath's extension. */
155
353
  inFormat?: string;
156
354
  /** Explicit output format key, or omit to infer from outPath's extension. */
@@ -680,13 +878,16 @@ export interface XdmfTimeSeriesWriter {
680
878
  * Append one step from raw arrays instead of a mesh -- the granularity a
681
879
  * solver has once `writePointsCells` has fixed the geometry. Arrays are
682
880
  * emitted in key order; `components` gives the per-entity width of any array
683
- * that is not a scalar, since a flat typed array carries no shape.
881
+ * that is not a scalar, since a flat typed array carries no shape. A
882
+ * `BigInt64Array`/`BigUint64Array` value (e.g. taken from a mesh's
883
+ * `cell_data` unchanged) is widened to `number`s internally -- the XDMF
884
+ * data path is double-precision regardless of the source dtype.
684
885
  * @throws {Error} if an array's length does not match the static grid.
685
886
  */
686
887
  writeDataArrays(
687
888
  time: number,
688
- pointData: Record<string, Float64Array | number[]>,
689
- cellData?: Record<string, Float64Array | number[]>,
889
+ pointData: Record<string, DataArray | number[]>,
890
+ cellData?: Record<string, DataArray | number[]>,
690
891
  components?: Record<string, number>
691
892
  ): void;
692
893
 
@@ -719,6 +920,41 @@ export interface XdmfTimeSeriesWriter {
719
920
  close(): void;
720
921
  }
721
922
 
923
+ /** One entry of a sequence plan: one step of one file. See {@link MeshioPlusPlusModule.sequenceEntries} and {@link SequenceReader}. */
924
+ export interface SequenceEntry {
925
+ path: string;
926
+ step: number;
927
+ time: number;
928
+ timeSource: 'explicit' | 'file' | 'filename' | 'index';
929
+ }
930
+
931
+ /**
932
+ * A stateful sequence reader, opened by {@link MeshioPlusPlusModule.openSequence}.
933
+ * Plans the sequence once (no heavy data read) and reads one step's mesh at a
934
+ * time -- at most one mesh alive, whatever the step count. See
935
+ * `doc/sequences.md`.
936
+ */
937
+ export interface SequenceReader {
938
+ /** How many entries this sequence has. */
939
+ count: number;
940
+ path(i: number): string;
941
+ step(i: number): number;
942
+ time(i: number): number;
943
+ timeSource(i: number): 'explicit' | 'file' | 'filename' | 'index';
944
+ entry(i: number): SequenceEntry;
945
+ entries(): SequenceEntry[];
946
+ /**
947
+ * Read entry `i`'s mesh. `pointsOnly`/`arrays`/`lenient` are `readMeshSelective`'s.
948
+ * @throws {Error} if `i` is out of `[0, count)`.
949
+ */
950
+ read(
951
+ i: number,
952
+ options?: { pointsOnly?: boolean; arrays?: string[] | null; lenient?: boolean },
953
+ ): Mesh;
954
+ /** Release the handle. Safe to call twice; after this every other method throws. */
955
+ close(): void;
956
+ }
957
+
722
958
  /**
723
959
  * The instantiated module returned by `loadMeshioPlusPlus()`. `FS` is
724
960
  * Emscripten's virtual filesystem (MEMFS by default) -- write the bytes of a
@@ -764,6 +1000,13 @@ export interface MeshioPlusPlusModule {
764
1000
  * truncated block or a bad node reference still throws, because continuing
765
1001
  * past those would return a mesh that is quietly wrong.
766
1002
  *
1003
+ * `info: true` attaches the format's side channel as the result's `.info`
1004
+ * (see {@link MeshInfo}) for the formats that have one
1005
+ * (openfoam/med/mdpa/ansysinp/unv/gmsh/exodus); silently has no effect for
1006
+ * any other format, so a caller can always pass it and check `.info`
1007
+ * itself. `pointsOnly`/`arrays` reach the read only for the formats whose
1008
+ * info-bearing reader takes selective-read options (med/mdpa/gmsh/exodus).
1009
+ *
767
1010
  * @throws {Error} on an out-of-range `timeStep`.
768
1011
  */
769
1012
  readMeshSelective(
@@ -774,6 +1017,7 @@ export interface MeshioPlusPlusModule {
774
1017
  arrays?: string[] | null;
775
1018
  timeStep?: number;
776
1019
  lenient?: boolean;
1020
+ info?: boolean;
777
1021
  }
778
1022
  ): Mesh;
779
1023
 
@@ -791,18 +1035,26 @@ export interface MeshioPlusPlusModule {
791
1035
  readerSupportsOptions(format: string): boolean;
792
1036
 
793
1037
  /**
794
- * Write a mesh to the virtual filesystem.
795
- * @throws {Error} on an unknown/write-unsupported format or malformed input
796
- * (e.g. a points/connectivity array length not divisible by its
797
- * declared dim/nodesPerCell).
1038
+ * Write a mesh to the virtual filesystem. See {@link MeshWriteOptionsWithInfo.info}
1039
+ * for writing a format's side channel.
1040
+ * @returns every virtual-FS path this write touched (new or changed),
1041
+ * sorted -- more than one for a multi-file writer (`.xdmf` + its `.h5`
1042
+ * companion, an OpenFOAM `polyMesh` directory's files, ...).
1043
+ * @throws {Error} on an unknown/write-unsupported format, an `options`
1044
+ * field the format cannot honour, `info` given for a format with no
1045
+ * side-channel writer, or malformed input (e.g. a points/connectivity
1046
+ * array length not divisible by its declared dim/nodesPerCell).
798
1047
  */
799
- writeMesh(path: string, mesh: Mesh, format?: string): void;
1048
+ writeMesh(
1049
+ path: string, mesh: Mesh, format?: string, options?: MeshWriteOptionsWithInfo
1050
+ ): string[];
800
1051
 
801
1052
  /**
802
1053
  * Read `inPath` and write it to `outPath` directly (no intermediate JS
803
1054
  * mesh object). Mirrors the CLI's `convert` subcommand.
1055
+ * @returns every virtual-FS path the write touched (new or changed), sorted.
804
1056
  */
805
- convert(inPath: string, outPath: string, options?: ConvertOptions): void;
1057
+ convert(inPath: string, outPath: string, options?: ConvertOptions): string[];
806
1058
 
807
1059
  /**
808
1060
  * Like {@link convert}, but writes a *renderable surface*: a mesh with
@@ -898,12 +1150,24 @@ export interface MeshioPlusPlusModule {
898
1150
  timeFrom?: 'auto' | 'file' | 'filename' | 'index';
899
1151
  sort?: boolean;
900
1152
  },
901
- ): Array<{
902
- path: string;
903
- step: number;
904
- time: number;
905
- timeSource: 'explicit' | 'file' | 'filename' | 'index';
906
- }>;
1153
+ ): SequenceEntry[];
1154
+
1155
+ /**
1156
+ * Open a **stateful** sequence reader: plans the sequence once (`source`/
1157
+ * `options` exactly as {@link sequenceEntries}), then lets you read one
1158
+ * step's mesh at a time via {@link SequenceReader.read} without holding
1159
+ * more than one mesh alive -- the lazy-read counterpart to
1160
+ * `sequenceEntries` + `readMesh` in a loop.
1161
+ */
1162
+ openSequence(
1163
+ source: string | string[],
1164
+ options?: {
1165
+ format?: string;
1166
+ times?: number[];
1167
+ timeFrom?: 'auto' | 'file' | 'filename' | 'index';
1168
+ sort?: boolean;
1169
+ },
1170
+ ): SequenceReader;
907
1171
 
908
1172
  /**
909
1173
  * **Fan-in**: write every step of `source` into one multi-step file.
@@ -1010,7 +1274,13 @@ export interface MeshioPlusPlusModule {
1010
1274
  /** Whether two meshes are equal within tolerance. */
1011
1275
  meshesEqual(a: Mesh, b: Mesh, atol?: number, rtol?: number, unordered?: boolean): boolean;
1012
1276
 
1013
- /** Combine several meshes into one, optionally welding coincident points. */
1277
+ /**
1278
+ * Combine several meshes into one, optionally welding coincident points.
1279
+ * With `returnMaps: true`, also returns `pointMaps`/`cellMaps`: one array
1280
+ * per INPUT MESH (not per input block, unlike every other op's maps), each
1281
+ * input's local point/cell index -> the output index (cellMaps: -1 if
1282
+ * dropped as a duplicate).
1283
+ */
1014
1284
  merge(
1015
1285
  meshes: Mesh[],
1016
1286
  weld?: boolean,
@@ -1018,12 +1288,23 @@ export interface MeshioPlusPlusModule {
1018
1288
  sourceTag?: boolean,
1019
1289
  dataPolicy?: MergeDataPolicy,
1020
1290
  dropDuplicateCells?: boolean,
1291
+ returnMaps?: false,
1021
1292
  ): Mesh;
1293
+ merge(
1294
+ meshes: Mesh[],
1295
+ weld?: boolean,
1296
+ atol?: number,
1297
+ sourceTag?: boolean,
1298
+ dataPolicy?: MergeDataPolicy,
1299
+ dropDuplicateCells?: boolean,
1300
+ returnMaps?: true,
1301
+ ): { mesh: Mesh; pointMaps: Int32Array[]; cellMaps: Int32Array[] };
1022
1302
 
1023
1303
  /** Apply a row-major 4x4 affine transform to the point coordinates. */
1024
1304
  transform(mesh: Mesh, matrix: number[], rotateVectorData?: boolean): Mesh;
1025
1305
 
1026
- /** Weld / prune / de-duplicate in one pass. */
1306
+ /** Weld / prune / de-duplicate in one pass. With `returnMaps: true`, the
1307
+ * result also carries `pointMap`/`cellMaps` (see {@link PointCellMaps}). */
1027
1308
  clean(
1028
1309
  mesh: Mesh,
1029
1310
  weld?: boolean,
@@ -1031,12 +1312,15 @@ export interface MeshioPlusPlusModule {
1031
1312
  removeOrphans?: boolean,
1032
1313
  dropDegenerate?: boolean,
1033
1314
  dropDuplicateCells?: boolean,
1315
+ returnMaps?: boolean,
1034
1316
  ): {
1035
1317
  mesh: Mesh;
1036
1318
  pointsWelded: number;
1037
1319
  pointsRemovedOrphan: number;
1038
1320
  cellsDroppedDegenerate: number;
1039
1321
  cellsDroppedDuplicate: number;
1322
+ pointMap?: Int32Array;
1323
+ cellMaps?: Int32Array[];
1040
1324
  };
1041
1325
 
1042
1326
  /**
@@ -1046,9 +1330,12 @@ export interface MeshioPlusPlusModule {
1046
1330
  * `"laplacian"` is stronger per pass but contracts the mesh. A **negative**
1047
1331
  * `lambda` means "this method's own default" (0.5 Laplacian, 0.33 Taubin).
1048
1332
  * Boundary and feature nodes are pinned by default, and `guardInversion`
1049
- * rejects any move that would flip an incident cell.
1333
+ * rejects any move that would flip an incident cell. `frozen` is an
1334
+ * optional array of 0-based point ids to pin outright, unioned with any
1335
+ * boundary/feature pins.
1050
1336
  * @throws {Error} on an unknown `method`, a non-negative `lambda` outside
1051
- * `(0, 1)`, or a `"taubin"` `mu` that does not satisfy `mu < -lambda < 0`.
1337
+ * `(0, 1)`, a `"taubin"` `mu` that does not satisfy `mu < -lambda < 0`, or
1338
+ * a `frozen` id outside `[0, numPoints)`.
1052
1339
  */
1053
1340
  smooth(
1054
1341
  mesh: Mesh,
@@ -1060,6 +1347,7 @@ export interface MeshioPlusPlusModule {
1060
1347
  preserveFeatures?: boolean,
1061
1348
  featureAngle?: number,
1062
1349
  guardInversion?: boolean,
1350
+ frozen?: number[] | Int32Array | null,
1063
1351
  ): { mesh: Mesh; numNodesMoved: number; maxDisplacement: number; numSkippedInversion: number };
1064
1352
 
1065
1353
  /**
@@ -1127,17 +1415,37 @@ export interface MeshioPlusPlusModule {
1127
1415
  fine: Mesh,
1128
1416
  ): { mesh: Mesh; numGroupsUndone: number; numCellsRemoved: number };
1129
1417
 
1130
- /** Subset a mesh to an axis-aligned bounding box. */
1131
- cropBbox(mesh: Mesh, lo: number[], hi: number[], mode?: CropMode, recordIds?: boolean): Mesh;
1132
-
1133
- /** Subset a mesh to the half-space `(p - point) . normal >= 0`. */
1418
+ /** Subset a mesh to an axis-aligned bounding box. With `returnMaps: true`,
1419
+ * returns `{mesh, pointMap, cellMaps}` (see {@link PointCellMaps}) instead
1420
+ * of a bare mesh. */
1421
+ cropBbox(
1422
+ mesh: Mesh, lo: number[], hi: number[], mode?: CropMode, recordIds?: boolean,
1423
+ returnMaps?: false,
1424
+ ): Mesh;
1425
+ cropBbox(
1426
+ mesh: Mesh, lo: number[], hi: number[], mode?: CropMode, recordIds?: boolean,
1427
+ returnMaps?: true,
1428
+ ): { mesh: Mesh } & PointCellMaps;
1429
+
1430
+ /** Subset a mesh to the half-space `(p - point) . normal >= 0`. With
1431
+ * `returnMaps: true`, returns `{mesh, pointMap, cellMaps}` instead of a
1432
+ * bare mesh. */
1134
1433
  cropPlane(
1135
1434
  mesh: Mesh,
1136
1435
  point: number[],
1137
1436
  normal: number[],
1138
1437
  mode?: CropMode,
1139
1438
  recordIds?: boolean,
1439
+ returnMaps?: false,
1140
1440
  ): Mesh;
1441
+ cropPlane(
1442
+ mesh: Mesh,
1443
+ point: number[],
1444
+ normal: number[],
1445
+ mode?: CropMode,
1446
+ recordIds?: boolean,
1447
+ returnMaps?: true,
1448
+ ): { mesh: Mesh } & PointCellMaps;
1141
1449
 
1142
1450
  /**
1143
1451
  * Subset a mesh to the cells whose value in a scalar `cell_data` array
@@ -1158,6 +1466,9 @@ export interface MeshioPlusPlusModule {
1158
1466
  * @throws {Error} when `array` is not a scalar `cell_data` array covering
1159
1467
  * every block, or the comparison is not one of `<`, `<=`, `>`, `>=`, `==`,
1160
1468
  * `!=`.
1469
+ *
1470
+ * With `returnMaps: true`, returns `{mesh, pointMap, cellMaps}` instead of
1471
+ * a bare mesh.
1161
1472
  */
1162
1473
  cropPredicate(
1163
1474
  mesh: Mesh,
@@ -1165,7 +1476,16 @@ export interface MeshioPlusPlusModule {
1165
1476
  compare?: CropCompare,
1166
1477
  value?: number,
1167
1478
  recordIds?: boolean,
1479
+ returnMaps?: false,
1168
1480
  ): Mesh;
1481
+ cropPredicate(
1482
+ mesh: Mesh,
1483
+ array: string,
1484
+ compare?: CropCompare,
1485
+ value?: number,
1486
+ recordIds?: boolean,
1487
+ returnMaps?: true,
1488
+ ): { mesh: Mesh } & PointCellMaps;
1169
1489
 
1170
1490
  /**
1171
1491
  * Planar cross-section of a mesh (marching tetrahedra on a simplexified
@@ -1671,8 +1991,12 @@ export interface MeshioPlusPlusModule {
1671
1991
  maxDisplacement: number;
1672
1992
  };
1673
1993
 
1674
- /** Partition a mesh into submeshes by type, connected component, or tag. */
1675
- split(mesh: Mesh, by: SplitBy, tagName?: string): { key: string; mesh: Mesh }[];
1994
+ /** Partition a mesh into submeshes by type, connected component, or tag.
1995
+ * With `returnMaps: true`, each piece also carries `pointMap`/`cellMaps`
1996
+ * (see {@link PointCellMaps}). */
1997
+ split(
1998
+ mesh: Mesh, by: SplitBy, tagName?: string, returnMaps?: boolean,
1999
+ ): ({ key: string; mesh: Mesh } & Partial<PointCellMaps>)[];
1676
2000
 
1677
2001
  /**
1678
2002
  * Convert the element representation: drop higher-order nodes
@@ -1680,8 +2004,16 @@ export interface MeshioPlusPlusModule {
1680
2004
  * or promote linear cells to serendipity quadratic (`"elevate"`).
1681
2005
  * @throws {Error} on a polyhedron block under `"simplexify"`, or a
1682
2006
  * full-Lagrange target (quad9/hexahedron27) under `"elevate"`.
2007
+ *
2008
+ * With `returnMaps: true`, returns `{mesh, pointMap, cellMaps}` instead of
2009
+ * a bare mesh.
1683
2010
  */
1684
- convertCells(mesh: Mesh, mode?: ConvertCellsMode, recordParentIds?: boolean): Mesh;
2011
+ convertCells(
2012
+ mesh: Mesh, mode?: ConvertCellsMode, recordParentIds?: boolean, returnMaps?: false,
2013
+ ): Mesh;
2014
+ convertCells(
2015
+ mesh: Mesh, mode?: ConvertCellsMode, recordParentIds?: boolean, returnMaps?: true,
2016
+ ): { mesh: Mesh } & PointCellMaps;
1685
2017
 
1686
2018
  /**
1687
2019
  * Polyhedrally refine a mesh: one polyhedral child per face of every
@@ -1693,8 +2025,11 @@ export interface MeshioPlusPlusModule {
1693
2025
  * `convertCells`, there is no point map -- subdivide never prunes or
1694
2026
  * renumbers an original point.
1695
2027
  * @throws {Error} when a cell's faces are not a closed orientable surface.
2028
+ *
2029
+ * With `returnMaps: true`, returns `{mesh, cellMaps}` instead of a bare mesh.
1696
2030
  */
1697
- subdivide(mesh: Mesh, recordParentIds?: boolean): Mesh;
2031
+ subdivide(mesh: Mesh, recordParentIds?: boolean, returnMaps?: false): Mesh;
2032
+ subdivide(mesh: Mesh, recordParentIds?: boolean, returnMaps?: true): { mesh: Mesh; cellMaps: Int32Array[] };
1698
2033
 
1699
2034
  /**
1700
2035
  * Polyhedrally coarsen a mesh: merge groups of cells into single larger
@@ -1707,8 +2042,13 @@ export interface MeshioPlusPlusModule {
1707
2042
  * for a minimal point set).
1708
2043
  * @throws {Error} when targetGroupSize is 0, or the mesh contains a face
1709
2044
  * shared by three or more cells (non-manifold).
2045
+ *
2046
+ * With `returnMaps: true`, returns `{mesh, cellMap}` instead of a bare
2047
+ * mesh -- a single FLAT array (global input cell index -> global output
2048
+ * cell index), unlike the other ops' per-block `cellMaps`.
1710
2049
  */
1711
- agglomerate(mesh: Mesh, targetGroupSize?: number): Mesh;
2050
+ agglomerate(mesh: Mesh, targetGroupSize?: number, returnMaps?: false): Mesh;
2051
+ agglomerate(mesh: Mesh, targetGroupSize?: number, returnMaps?: true): { mesh: Mesh; cellMap: Int32Array };
1712
2052
 
1713
2053
  /**
1714
2054
  * Refine a mesh, subdividing cells into same-type children (`line` → 2,
@@ -1723,13 +2063,24 @@ export interface MeshioPlusPlusModule {
1723
2063
  * @throws {Error} on a higher-order cell, a `pyramid`, or a ragged
1724
2064
  * polygon/polyhedron block — none has a same-type subdivision — and on more
1725
2065
  * than one selector, an unknown region, or an unusable predicate array.
2066
+ *
2067
+ * With `returnMaps: true`, returns `{mesh, pointMap, cellMaps}` instead of
2068
+ * a bare mesh.
1726
2069
  */
1727
2070
  refine(
1728
2071
  mesh: Mesh,
1729
2072
  levels?: number,
1730
2073
  recordParentIds?: boolean,
1731
- options?: RefineOptions
2074
+ options?: RefineOptions,
2075
+ returnMaps?: false,
1732
2076
  ): Mesh;
2077
+ refine(
2078
+ mesh: Mesh,
2079
+ levels?: number,
2080
+ recordParentIds?: boolean,
2081
+ options?: RefineOptions,
2082
+ returnMaps?: true,
2083
+ ): { mesh: Mesh } & PointCellMaps;
1733
2084
 
1734
2085
  /**
1735
2086
  * Decimate a SURFACE mesh by quadric-error-metric (Garland-Heckbert) edge
@@ -1740,11 +2091,13 @@ export interface MeshioPlusPlusModule {
1740
2091
  * (once-used-edge test) and feature vertices (face normals differing by
1741
2092
  * more than `featureAngle` degrees) are pinned by default, and the link
1742
2093
  * condition plus a normal-flip guard reject any collapse that would change
1743
- * topology or fold the surface. The index maps and the frozen mask are not
1744
- * carried across the JS boundary, as on the other flat bindings.
2094
+ * topology or fold the surface. `frozen` is an optional array of 0-based
2095
+ * point ids to pin outright. With `returnMaps: true`, the result also
2096
+ * carries `pointMap`/`cellMaps`.
1745
2097
  * @throws {Error} on a 3D volume mesh (extract the surface first),
1746
2098
  * higher-order or ragged blocks, `line`/`vertex` blocks, an unknown
1747
- * `placement`, or a criterion count other than one.
2099
+ * `placement`, a criterion count other than one, or a `frozen` id outside
2100
+ * `[0, numPoints)`.
1748
2101
  */
1749
2102
  decimate(
1750
2103
  mesh: Mesh,
@@ -1755,24 +2108,66 @@ export interface MeshioPlusPlusModule {
1755
2108
  preserveBoundary?: boolean,
1756
2109
  preserveFeatures?: boolean,
1757
2110
  featureAngle?: number,
2111
+ frozen?: number[] | Int32Array | null,
2112
+ returnMaps?: boolean,
1758
2113
  ): {
1759
2114
  mesh: Mesh;
1760
2115
  facesRemoved: number;
1761
2116
  pointsRemoved: number;
1762
2117
  collapsesRejected: number;
1763
2118
  maxErrorApplied: number;
2119
+ pointMap?: Int32Array;
2120
+ cellMaps?: Int32Array[];
2121
+ };
2122
+
2123
+ /**
2124
+ * Decimate a tetrahedral VOLUME mesh by quadric-error-metric tet-edge
2125
+ * collapse — `decimate`'s volume sibling. Exactly one of `ratio` (fraction
2126
+ * of tets to KEEP, in (0, 1]), `targetCells` and `maxError` must be
2127
+ * non-negative. The output is all-tetra with the block structure kept 1:1.
2128
+ * `preserveBoundary` defaults to `false` here (unlike `decimate`): the
2129
+ * mesh's outer surface is usually interior geometry a solver still wants
2130
+ * simplified, not a boundary to protect. `frozen` is an optional array of
2131
+ * 0-based point ids to pin outright, unioned with any boundary/feature
2132
+ * pins. With `returnMaps: true`, the result also carries
2133
+ * `pointMap`/`cellMaps`.
2134
+ * @throws {Error} on a non-manifold boundary face, a non-tetra 3D cell,
2135
+ * higher-order tets, ragged/polyhedron blocks, a non-3D block, an unknown
2136
+ * `placement`, a criterion count other than one, or a `frozen` id outside
2137
+ * `[0, numPoints)`.
2138
+ */
2139
+ decimateVolume(
2140
+ mesh: Mesh,
2141
+ ratio?: number,
2142
+ targetCells?: number,
2143
+ maxError?: number,
2144
+ placement?: DecimatePlacement,
2145
+ preserveBoundary?: boolean,
2146
+ preserveFeatures?: boolean,
2147
+ featureAngle?: number,
2148
+ frozen?: number[] | Int32Array | null,
2149
+ returnMaps?: boolean,
2150
+ ): {
2151
+ mesh: Mesh;
2152
+ tetsRemoved: number;
2153
+ pointsRemoved: number;
2154
+ collapsesRejected: number;
2155
+ maxErrorApplied: number;
2156
+ pointMap?: Int32Array;
2157
+ cellMaps?: Int32Array[];
1764
2158
  };
1765
2159
 
1766
2160
  /**
1767
2161
  * Decompose a mesh into exactly `nparts` balanced pieces for domain
1768
2162
  * decomposition (the count-driven complement to `split`). Pieces keep the
1769
2163
  * input's cell-block structure 1:1 (empty blocks included, unlike `split`),
1770
- * so concatenating them reproduces the input. The index maps are not
1771
- * carried across the JS boundary — use `recordIds` for the
2164
+ * so concatenating them reproduces the input. Use `recordIds` for the
1772
2165
  * `partition:original_*_id` arrays, or `partitionLabels` for the raw
1773
- * assignment. `weightsKey` names a scalar `cell_data` array of per-cell
1774
- * weights. `ghostLayers > 0` grows each piece by that many shared-node BFS
1775
- * layers of other parts' cells (a halo), tagged `partition:ghost`.
2166
+ * assignment; with `returnMaps: true`, each piece also carries
2167
+ * `pointMap`/`cellMaps` (see {@link PointCellMaps}). `weightsKey` names a
2168
+ * scalar `cell_data` array of per-cell weights. `ghostLayers > 0` grows
2169
+ * each piece by that many shared-node BFS layers of other parts' cells (a
2170
+ * halo), tagged `partition:ghost`.
1776
2171
  * @throws {Error} on `method: 'kahip'` (KaHIP is never part of the WASM
1777
2172
  * build; the message names `MESHIOPLUSPLUS_WITH_KAHIP`), `nparts < 1`,
1778
2173
  * `ghostLayers < 0`, or a bad weights array.
@@ -1787,7 +2182,8 @@ export interface MeshioPlusPlusModule {
1787
2182
  recordIds?: boolean,
1788
2183
  ghostLayers?: number,
1789
2184
  weightsKey?: string,
1790
- ): { partId: number; mesh: Mesh }[];
2185
+ returnMaps?: boolean,
2186
+ ): ({ partId: number; mesh: Mesh } & Partial<PointCellMaps>)[];
1791
2187
 
1792
2188
  /**
1793
2189
  * The per-cell part assignment only: one array per cell block
@@ -1936,16 +2332,54 @@ export interface MeshioPlusPlusModule {
1936
2332
  * instantiation otherwise. `variant: 'auto'` (default) picks the threaded build
1937
2333
  * under Node and in a cross-origin-isolated browser, else the sequential one.
1938
2334
  *
1939
- * @param moduleOverrides forwarded as-is to the Emscripten module factory
1940
- * (e.g. `{ locateFile }` to relocate the `.wasm` binary for a bundler/CDN).
1941
- * `locateFile` receives the requested filename, so return the URL matching the
1942
- * loaded variant (`meshioplusplus_wasm.wasm` or `meshioplusplus_wasm_mt.wasm`).
2335
+ * @param moduleOverrides forwarded to the Emscripten module factory, with
2336
+ * `locateFile`/`onAbort` wrapped for diagnostics (your own overrides still
2337
+ * run first) -- see {@link MeshioPlusPlusLoadError}. `locateFile` receives
2338
+ * the requested filename, so return the URL matching the loaded variant
2339
+ * (`meshioplusplus_wasm.wasm` or `meshioplusplus_wasm_mt.wasm`).
1943
2340
  * @param options.variant which native artifact to load: `'auto'` (default),
1944
2341
  * `'mt'` (force threaded), or `'seq'` (force sequential).
2342
+ * @throws {MeshioPlusPlusLoadError} if the WASM module fails to instantiate.
1945
2343
  */
1946
2344
  export function loadMeshioPlusPlus(
1947
- moduleOverrides?: object,
2345
+ moduleOverrides?: ModuleOverrides,
1948
2346
  options?: { variant?: 'auto' | 'mt' | 'seq' },
1949
2347
  ): Promise<MeshioPlusPlusModule>;
1950
2348
 
2349
+ /** Overrides forwarded to the underlying Emscripten module factory. */
2350
+ export interface ModuleOverrides {
2351
+ /**
2352
+ * Resolve the URL for a native artifact Emscripten wants to fetch (the
2353
+ * `.wasm` binary, and under a threaded build its worker script). Receives
2354
+ * the requested filename and Emscripten's own default prefix; return the
2355
+ * URL to actually load. Must return the file matching the loaded variant
2356
+ * (`meshioplusplus_wasm.wasm` vs `meshioplusplus_wasm_mt.wasm`) -- a
2357
+ * mismatch either fails instantiation or, in the case of the sequential
2358
+ * binary handed to the threaded glue, loads successfully but reports the
2359
+ * wrong {@link MeshioPlusPlusModule.parallelBackend}, which
2360
+ * `loadMeshioPlusPlus` detects and rejects with a {@link MeshioPlusPlusLoadError}.
2361
+ */
2362
+ locateFile?(path: string, prefix: string): string;
2363
+ /** Called by Emscripten when the module aborts during instantiation. */
2364
+ onAbort?(reason: unknown): void;
2365
+ [key: string]: unknown;
2366
+ }
2367
+
2368
+ /**
2369
+ * Thrown by `loadMeshioPlusPlus()` when a WASM module fails to instantiate.
2370
+ * `cause` is the underlying error (or abort reason) that triggered it.
2371
+ */
2372
+ export interface MeshioPlusPlusLoadError extends Error {
2373
+ name: 'MeshioPlusPlusLoadError';
2374
+ /** Which native artifact was being loaded. */
2375
+ variant: 'mt' | 'seq';
2376
+ /** The glue module specifier (e.g. '../dist/meshioplusplus_wasm_mt.mjs'). */
2377
+ glue: string;
2378
+ /** The filename Emscripten asked `locateFile` to resolve, if it got that far. */
2379
+ requestedFile?: string;
2380
+ /** What `locateFile` returned for `requestedFile`. */
2381
+ resolvedUrl?: string;
2382
+ cause?: unknown;
2383
+ }
2384
+
1951
2385
  export default loadMeshioPlusPlus;