@ifc-lite/wasm 4.2.2 → 4.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "IFC-Lite Contributors"
6
6
  ],
7
7
  "description": "WebAssembly bindings for IFC-Lite",
8
- "version": "4.2.2",
8
+ "version": "4.3.1",
9
9
  "license": "MPL-2.0",
10
10
  "repository": {
11
11
  "type": "git",
package/pkg/ifc-lite.d.ts CHANGED
@@ -276,6 +276,13 @@ export class IfcAPI {
276
276
  * taken in order from the concatenated `positions`/`normals`/`indices`; `colors` is
277
277
  * RGBA per mesh, `origins` xyz per mesh, `express_ids` labels each mesh (indices are
278
278
  * per-mesh local). The caller passes exactly the meshes it wants emitted.
279
+ *
280
+ * Fails CLOSED: if the declared vertex/index counts run past the flattened
281
+ * `positions` / `indices`, there are fewer `index_counts` than meshes, or `normals`
282
+ * is empty or too short to cover every vertex, this throws an `Error` whose message
283
+ * starts with `MALFORMED_MESH_INPUT` — instead of silently emitting a GLB with those
284
+ * meshes dropped. (The viewer always passes fully-backed, normal-covered arrays, so
285
+ * this only fires on a caller bug.)
279
286
  */
280
287
  exportGlbFromMeshes(positions: Float32Array, normals: Float32Array, indices: Uint32Array, vertex_counts: Uint32Array, index_counts: Uint32Array, colors: Float32Array, origins: Float64Array, express_ids: Uint32Array, include_metadata: boolean, lit?: boolean | null, emissive?: boolean | null): Uint8Array;
281
288
  /**
@@ -374,6 +381,12 @@ export class IfcAPI {
374
381
  * property-set synthesis); empty ⇒ none. See `export_step_json` for the shape.
375
382
  */
376
383
  exportStep(content: Uint8Array, schema: string, included: Uint32Array, mutations_json: string): Uint8Array;
384
+ /**
385
+ * Export **OpenUSD** (`.usda` ASCII): a real Z-up USD stage — spatial hierarchy of
386
+ * `Xform` prims, `UsdGeomMesh` geometry, `UsdPreviewSurface` materials, IFC
387
+ * metadata as custom attributes. Whole-model (geometry-backed).
388
+ */
389
+ exportUsd(content: Uint8Array): Uint8Array;
377
390
  /**
378
391
  * Extract raw profile polygons from all building elements with `IfcExtrudedAreaSolid`
379
392
  * representations.
@@ -547,15 +560,22 @@ export class IfcAPI {
547
560
  * range_end)` shard; the main thread stitches the returned columns into the
548
561
  * full entity index (byte-identical to the single-threaded
549
562
  * `build_entity_index`) by binary-searching each shard for the previous
550
- * shard's `handoff`. Delegates to `ifc_lite_processing::scan_shard`, the
551
- * exact per-chunk primitive the native `build_entity_index_parallel` fans
552
- * across cores so the sharded merge cannot drift from the serial builder.
563
+ * shard's `handoff`. Delegates to `ifc_lite_processing::scan_shard_classified`
564
+ * a separately-maintained loop over the same `EntityScanner` primitive as
565
+ * `scan_shard` (the one the native `build_entity_index_parallel` fans across
566
+ * cores), plus a per-record class column this sharded path also needs. The
567
+ * two loops' records/handoff are kept in parity by a dedicated test
568
+ * (`rust/processing/tests/issue_2053_shard_scan_parity.rs`), not by
569
+ * delegation — edit one without the other and that test catches the drift.
553
570
  *
554
571
  * Byte offsets returned are GLOBAL (relative to file start), so shards
555
572
  * concatenate without rewriting. Returns a plain object:
556
573
  * `{ ids: Uint32Array, starts: Uint32Array, lengths: Uint32Array,
557
- * handoff: number }`
558
- * where `handoff` is the global start of the first entity at/after
574
+ * classes: Uint8Array, handoff: number }`
575
+ * where `classes` is the parallel per-record prepass class byte
576
+ * (`PREPASS_CLASS_*`: named code in the low bits plus the geometry-job /
577
+ * type-candidate flags) the host filters on to rebuild pre-pass span
578
+ * lists, and `handoff` is the global start of the first entity at/after
559
579
  * `range_end` (the next shard's first real entity), or `-1` at EOF.
560
580
  */
561
581
  scanEntityIndexShard(data: Uint8Array, range_start: number, range_end: number): any;
@@ -789,6 +809,47 @@ export class MeshCollection {
789
809
  * nested shape as a single FFI crossing instead of dozens of getters.
790
810
  */
791
811
  readonly diagnostics: any;
812
+ /**
813
+ * Per-entity world-space AABBs as a `Float64Array`, SIX values per entry
814
+ * (`minx, miny, minz, maxx, maxy, maxz`), in the same order as
815
+ * [`Self::geometry_hash_ids`] — entry `i` spans `[6*i, 6*i+6)`. Empty
816
+ * unless geometry hashing was enabled; the same
817
+ * `IfcAPI.setComputeGeometryHashes` switch gates both, so nothing is
818
+ * computed when the diff feature is off.
819
+ *
820
+ * Unquantized world `f64` (the file's RTC folded back in), so two
821
+ * revisions that chose different RTC offsets report the same box. This is
822
+ * what lets a consumer say "MOVED" honestly instead of inferring it from a
823
+ * changed hash, which also fires on reshape and on retriangulation.
824
+ *
825
+ * **Frame: WebGL Y-up**, like every other box, position, origin and
826
+ * placement that crosses this boundary (see `MeshDataJs::local_bounds`).
827
+ * The hasher accumulates in the producer's IFC Z-up frame, so the swap
828
+ * `(x,y,z) -> (x,z,-y)` is applied here, on the way out. Unconverted, the
829
+ * boxes would not enclose the very meshes `processGeometryBatch` returns
830
+ * alongside them. Positions are RTC-relative and this box is absolute, so
831
+ * a consumer comparing the two folds `rtcOffset*` in — itself Y-up-swapped.
832
+ *
833
+ * Present for every hashed entity. Its companion
834
+ * [`Self::geometry_volume_values`] is not — see there.
835
+ */
836
+ readonly geometryAabbValues: Float64Array;
837
+ /**
838
+ * Per-entity topology verdict as a `Uint8Array`, one packed byte per entry
839
+ * in [`Self::geometry_hash_ids`] order:
840
+ *
841
+ * * bit 0 (`1`) — every segment closed (no boundary / non-manifold edge)
842
+ * * bit 1 (`2`) — every segment orientable
843
+ * * bit 2 (`4`) — every segment a single connected component
844
+ * * bit 3 (`8`) — the entity produced exactly one segment
845
+ *
846
+ * `0x0F` is exactly the set that carries a volume in
847
+ * [`Self::geometry_volume_values`]. The individual bits are the diagnosis:
848
+ * a model checker can distinguish "this wall is an open shell" (bit 0
849
+ * clear) from "this door is a multi-item assembly whose parts may overlap"
850
+ * (bit 3 clear), which are different findings with different fixes.
851
+ */
852
+ readonly geometryClosureFlags: Uint8Array;
792
853
  /**
793
854
  * Number of per-entity geometry fingerprints recorded.
794
855
  */
@@ -806,6 +867,28 @@ export class MeshCollection {
806
867
  * geometry hashing was enabled.
807
868
  */
808
869
  readonly geometryHashValues: BigUint64Array;
870
+ /**
871
+ * Per-entity enclosed volume in CUBIC METRES as a `Float64Array`, one
872
+ * value per entry in [`Self::geometry_hash_ids`] order. `NaN` means NO
873
+ * TRUSTWORTHY VOLUME — the same absent convention as
874
+ * [`Self::geometry_aabb_values`] — and it is `NaN` for roughly a third of
875
+ * entities by design, not by failure.
876
+ *
877
+ * A value is emitted only when that entity's produced geometry was
878
+ * PROVABLY a single closed, orientable, single-component solid, as decided
879
+ * by the mesher's own orientation pass. Read
880
+ * `ifc_lite_geometry::GeometryHasher::volume` before treating a `NaN` as a
881
+ * bug: an open `SurfaceModel`, a material-layered wall (whose slices are
882
+ * open bands by construction), and any element assembled from more than one
883
+ * representation item all correctly report nothing rather than a plausible
884
+ * wrong number. [`Self::geometry_closure_flags`] says which.
885
+ *
886
+ * This is NOT a substitute for an IFC `BaseQuantities` `GrossVolume`: it is
887
+ * the volume of the geometry that was actually meshed, after opening cuts,
888
+ * and it says nothing about whether a CSG degradation left the host uncut
889
+ * (see the `diagnostics` getter).
890
+ */
891
+ readonly geometryVolumeValues: Float64Array;
809
892
  /**
810
893
  * Get number of meshes
811
894
  */
@@ -1583,6 +1666,7 @@ export interface InitOutput {
1583
1666
  readonly ifcapi_exportMerged: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => void;
1584
1667
  readonly ifcapi_exportObj: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number) => void;
1585
1668
  readonly ifcapi_exportStep: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number) => void;
1669
+ readonly ifcapi_exportUsd: (a: number, b: number, c: number, d: number) => void;
1586
1670
  readonly ifcapi_extractProfiles: (a: number, b: number, c: number, d: number) => number;
1587
1671
  readonly ifcapi_finalizePrepassStyles: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number, l: number, m: number, n: number, o: number, p: number, q: number, r: number, s: number, t: number, u: number, v: number, w: number, x: number, y: number) => void;
1588
1672
  readonly ifcapi_getMemory: (a: number) => number;
@@ -1620,9 +1704,12 @@ export interface InitOutput {
1620
1704
  readonly meshOutline2d: (a: number, b: number, c: number, d: number, e: number, f: number) => number;
1621
1705
  readonly meshcollection_buildingRotation: (a: number, b: number) => void;
1622
1706
  readonly meshcollection_diagnostics: (a: number) => number;
1707
+ readonly meshcollection_geometryAabbValues: (a: number) => number;
1708
+ readonly meshcollection_geometryClosureFlags: (a: number) => number;
1623
1709
  readonly meshcollection_geometryHashCount: (a: number) => number;
1624
1710
  readonly meshcollection_geometryHashIds: (a: number) => number;
1625
1711
  readonly meshcollection_geometryHashValues: (a: number) => number;
1712
+ readonly meshcollection_geometryVolumeValues: (a: number) => number;
1626
1713
  readonly meshcollection_get: (a: number, b: number) => number;
1627
1714
  readonly meshcollection_hasRtcOffset: (a: number) => number;
1628
1715
  readonly meshcollection_length: (a: number) => number;
package/pkg/ifc-lite.js CHANGED
@@ -715,6 +715,13 @@ export class IfcAPI {
715
715
  * taken in order from the concatenated `positions`/`normals`/`indices`; `colors` is
716
716
  * RGBA per mesh, `origins` xyz per mesh, `express_ids` labels each mesh (indices are
717
717
  * per-mesh local). The caller passes exactly the meshes it wants emitted.
718
+ *
719
+ * Fails CLOSED: if the declared vertex/index counts run past the flattened
720
+ * `positions` / `indices`, there are fewer `index_counts` than meshes, or `normals`
721
+ * is empty or too short to cover every vertex, this throws an `Error` whose message
722
+ * starts with `MALFORMED_MESH_INPUT` — instead of silently emitting a GLB with those
723
+ * meshes dropped. (The viewer always passes fully-backed, normal-covered arrays, so
724
+ * this only fires on a caller bug.)
718
725
  * @param {Float32Array} positions
719
726
  * @param {Float32Array} normals
720
727
  * @param {Uint32Array} indices
@@ -750,6 +757,11 @@ export class IfcAPI {
750
757
  wasm.ifcapi_exportGlbFromMeshes(retptr, this.__wbg_ptr, ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3, ptr4, len4, ptr5, len5, ptr6, len6, ptr7, len7, include_metadata, isLikeNone(lit) ? 0xFFFFFF : lit ? 1 : 0, isLikeNone(emissive) ? 0xFFFFFF : emissive ? 1 : 0);
751
758
  var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
752
759
  var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
760
+ var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
761
+ var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
762
+ if (r3) {
763
+ throw takeObject(r2);
764
+ }
753
765
  var v9 = getArrayU8FromWasm0(r0, r1).slice();
754
766
  wasm.__wbindgen_export4(r0, r1 * 1, 1);
755
767
  return v9;
@@ -1076,6 +1088,28 @@ export class IfcAPI {
1076
1088
  wasm.__wbindgen_add_to_stack_pointer(16);
1077
1089
  }
1078
1090
  }
1091
+ /**
1092
+ * Export **OpenUSD** (`.usda` ASCII): a real Z-up USD stage — spatial hierarchy of
1093
+ * `Xform` prims, `UsdGeomMesh` geometry, `UsdPreviewSurface` materials, IFC
1094
+ * metadata as custom attributes. Whole-model (geometry-backed).
1095
+ * @param {Uint8Array} content
1096
+ * @returns {Uint8Array}
1097
+ */
1098
+ exportUsd(content) {
1099
+ try {
1100
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
1101
+ const ptr0 = passArray8ToWasm0(content, wasm.__wbindgen_export);
1102
+ const len0 = WASM_VECTOR_LEN;
1103
+ wasm.ifcapi_exportUsd(retptr, this.__wbg_ptr, ptr0, len0);
1104
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
1105
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
1106
+ var v2 = getArrayU8FromWasm0(r0, r1).slice();
1107
+ wasm.__wbindgen_export4(r0, r1 * 1, 1);
1108
+ return v2;
1109
+ } finally {
1110
+ wasm.__wbindgen_add_to_stack_pointer(16);
1111
+ }
1112
+ }
1079
1113
  /**
1080
1114
  * Extract raw profile polygons from all building elements with `IfcExtrudedAreaSolid`
1081
1115
  * representations.
@@ -1593,15 +1627,22 @@ export class IfcAPI {
1593
1627
  * range_end)` shard; the main thread stitches the returned columns into the
1594
1628
  * full entity index (byte-identical to the single-threaded
1595
1629
  * `build_entity_index`) by binary-searching each shard for the previous
1596
- * shard's `handoff`. Delegates to `ifc_lite_processing::scan_shard`, the
1597
- * exact per-chunk primitive the native `build_entity_index_parallel` fans
1598
- * across cores so the sharded merge cannot drift from the serial builder.
1630
+ * shard's `handoff`. Delegates to `ifc_lite_processing::scan_shard_classified`
1631
+ * a separately-maintained loop over the same `EntityScanner` primitive as
1632
+ * `scan_shard` (the one the native `build_entity_index_parallel` fans across
1633
+ * cores), plus a per-record class column this sharded path also needs. The
1634
+ * two loops' records/handoff are kept in parity by a dedicated test
1635
+ * (`rust/processing/tests/issue_2053_shard_scan_parity.rs`), not by
1636
+ * delegation — edit one without the other and that test catches the drift.
1599
1637
  *
1600
1638
  * Byte offsets returned are GLOBAL (relative to file start), so shards
1601
1639
  * concatenate without rewriting. Returns a plain object:
1602
1640
  * `{ ids: Uint32Array, starts: Uint32Array, lengths: Uint32Array,
1603
- * handoff: number }`
1604
- * where `handoff` is the global start of the first entity at/after
1641
+ * classes: Uint8Array, handoff: number }`
1642
+ * where `classes` is the parallel per-record prepass class byte
1643
+ * (`PREPASS_CLASS_*`: named code in the low bits plus the geometry-job /
1644
+ * type-candidate flags) the host filters on to rebuild pre-pass span
1645
+ * lists, and `handoff` is the global start of the first entity at/after
1605
1646
  * `range_end` (the next shard's first real entity), or `-1` at EOF.
1606
1647
  * @param {Uint8Array} data
1607
1648
  * @param {number} range_start
@@ -2005,6 +2046,55 @@ export class MeshCollection {
2005
2046
  const ret = wasm.meshcollection_diagnostics(this.__wbg_ptr);
2006
2047
  return takeObject(ret);
2007
2048
  }
2049
+ /**
2050
+ * Per-entity world-space AABBs as a `Float64Array`, SIX values per entry
2051
+ * (`minx, miny, minz, maxx, maxy, maxz`), in the same order as
2052
+ * [`Self::geometry_hash_ids`] — entry `i` spans `[6*i, 6*i+6)`. Empty
2053
+ * unless geometry hashing was enabled; the same
2054
+ * `IfcAPI.setComputeGeometryHashes` switch gates both, so nothing is
2055
+ * computed when the diff feature is off.
2056
+ *
2057
+ * Unquantized world `f64` (the file's RTC folded back in), so two
2058
+ * revisions that chose different RTC offsets report the same box. This is
2059
+ * what lets a consumer say "MOVED" honestly instead of inferring it from a
2060
+ * changed hash, which also fires on reshape and on retriangulation.
2061
+ *
2062
+ * **Frame: WebGL Y-up**, like every other box, position, origin and
2063
+ * placement that crosses this boundary (see `MeshDataJs::local_bounds`).
2064
+ * The hasher accumulates in the producer's IFC Z-up frame, so the swap
2065
+ * `(x,y,z) -> (x,z,-y)` is applied here, on the way out. Unconverted, the
2066
+ * boxes would not enclose the very meshes `processGeometryBatch` returns
2067
+ * alongside them. Positions are RTC-relative and this box is absolute, so
2068
+ * a consumer comparing the two folds `rtcOffset*` in — itself Y-up-swapped.
2069
+ *
2070
+ * Present for every hashed entity. Its companion
2071
+ * [`Self::geometry_volume_values`] is not — see there.
2072
+ * @returns {Float64Array}
2073
+ */
2074
+ get geometryAabbValues() {
2075
+ const ret = wasm.meshcollection_geometryAabbValues(this.__wbg_ptr);
2076
+ return takeObject(ret);
2077
+ }
2078
+ /**
2079
+ * Per-entity topology verdict as a `Uint8Array`, one packed byte per entry
2080
+ * in [`Self::geometry_hash_ids`] order:
2081
+ *
2082
+ * * bit 0 (`1`) — every segment closed (no boundary / non-manifold edge)
2083
+ * * bit 1 (`2`) — every segment orientable
2084
+ * * bit 2 (`4`) — every segment a single connected component
2085
+ * * bit 3 (`8`) — the entity produced exactly one segment
2086
+ *
2087
+ * `0x0F` is exactly the set that carries a volume in
2088
+ * [`Self::geometry_volume_values`]. The individual bits are the diagnosis:
2089
+ * a model checker can distinguish "this wall is an open shell" (bit 0
2090
+ * clear) from "this door is a multi-item assembly whose parts may overlap"
2091
+ * (bit 3 clear), which are different findings with different fixes.
2092
+ * @returns {Uint8Array}
2093
+ */
2094
+ get geometryClosureFlags() {
2095
+ const ret = wasm.meshcollection_geometryClosureFlags(this.__wbg_ptr);
2096
+ return takeObject(ret);
2097
+ }
2008
2098
  /**
2009
2099
  * Number of per-entity geometry fingerprints recorded.
2010
2100
  * @returns {number}
@@ -2034,6 +2124,32 @@ export class MeshCollection {
2034
2124
  const ret = wasm.meshcollection_geometryHashValues(this.__wbg_ptr);
2035
2125
  return takeObject(ret);
2036
2126
  }
2127
+ /**
2128
+ * Per-entity enclosed volume in CUBIC METRES as a `Float64Array`, one
2129
+ * value per entry in [`Self::geometry_hash_ids`] order. `NaN` means NO
2130
+ * TRUSTWORTHY VOLUME — the same absent convention as
2131
+ * [`Self::geometry_aabb_values`] — and it is `NaN` for roughly a third of
2132
+ * entities by design, not by failure.
2133
+ *
2134
+ * A value is emitted only when that entity's produced geometry was
2135
+ * PROVABLY a single closed, orientable, single-component solid, as decided
2136
+ * by the mesher's own orientation pass. Read
2137
+ * `ifc_lite_geometry::GeometryHasher::volume` before treating a `NaN` as a
2138
+ * bug: an open `SurfaceModel`, a material-layered wall (whose slices are
2139
+ * open bands by construction), and any element assembled from more than one
2140
+ * representation item all correctly report nothing rather than a plausible
2141
+ * wrong number. [`Self::geometry_closure_flags`] says which.
2142
+ *
2143
+ * This is NOT a substitute for an IFC `BaseQuantities` `GrossVolume`: it is
2144
+ * the volume of the geometry that was actually meshed, after opening cuts,
2145
+ * and it says nothing about whether a CSG degradation left the host uncut
2146
+ * (see the `diagnostics` getter).
2147
+ * @returns {Float64Array}
2148
+ */
2149
+ get geometryVolumeValues() {
2150
+ const ret = wasm.meshcollection_geometryVolumeValues(this.__wbg_ptr);
2151
+ return takeObject(ret);
2152
+ }
2037
2153
  /**
2038
2154
  * Get mesh at index (clones — non-destructive). Prefer `takeMesh` on the
2039
2155
  * hot streaming path; this stays for callers that read meshes more than once.
Binary file