brepjs-bim 0.24.4 → 0.24.5

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.
@@ -1,4 +1,4 @@
1
- import { GeometryCleanupError, addHoles, applyMatrix, autoHeal, box, castShape, clone, convexHull, cut, err, extrude, fuse, fuseAll, getBounds, getKernel, getSolids, isClosedWire, isOk, isPlanarWire, isSolid, isValid, isValidSolid, locate, measureVolume, mesh, ok, outerWire, polygon, revolve, rotate, translate, validSolid } from "brepjs";
1
+ import { GeometryCleanupError, addHoles, applyMatrix, autoHeal, box, castShape, clone, convexHull, createSolid, cut, err, extrude, fuse, fuseAll, getBounds, getKernel, getSolids, isClosedWire, isOk, isPlanarWire, isSolid, isValid, isValidSolid, locate, measureVolume, mesh, ok, outerWire, polygon, revolve, rotate, translate, validSolid } from "brepjs";
2
2
  import * as WebIFC from "web-ifc";
3
3
  import { Handle, IFCROOT, IfcAPI } from "web-ifc";
4
4
  //#region src/identity/ifcGuid.ts
@@ -243,7 +243,7 @@ function _usingCtx() {
243
243
  //#region src/elementFns/wallFns.ts
244
244
  function wallToSolid(spec) {
245
245
  try {
246
- var _usingCtx$20 = _usingCtx();
246
+ var _usingCtx$21 = _usingCtx();
247
247
  if (spec.length <= 0) return err(specError("WALL_ZERO_LENGTH", "Wall length must be positive"));
248
248
  if (spec.height <= 0) return err(specError("WALL_ZERO_HEIGHT", "Wall height must be positive"));
249
249
  if (spec.thickness <= 0) return err(specError("WALL_ZERO_THICKNESS", "Wall thickness must be positive"));
@@ -271,7 +271,7 @@ function wallToSolid(spec) {
271
271
  ]
272
272
  ]);
273
273
  if (!profileResult.ok) return err(fromBrepError(profileResult.error, "WALL_PROFILE_FAILED", "Failed to create wall profile"));
274
- const profile = _usingCtx$20.u(profileResult.value);
274
+ const profile = _usingCtx$21.u(profileResult.value);
275
275
  const solidResult = extrude(profile, [
276
276
  length,
277
277
  0,
@@ -285,16 +285,16 @@ function wallToSolid(spec) {
285
285
  }
286
286
  return ok(solid);
287
287
  } catch (_) {
288
- _usingCtx$20.e = _;
288
+ _usingCtx$21.e = _;
289
289
  } finally {
290
- _usingCtx$20.d();
290
+ _usingCtx$21.d();
291
291
  }
292
292
  }
293
293
  //#endregion
294
294
  //#region src/elementFns/slabFns.ts
295
295
  function slabToSolid(spec) {
296
296
  try {
297
- var _usingCtx$19 = _usingCtx();
297
+ var _usingCtx$20 = _usingCtx();
298
298
  if (spec.length <= 0) return err(specError("SLAB_ZERO_LENGTH", "Slab length must be positive"));
299
299
  if (spec.width <= 0) return err(specError("SLAB_ZERO_WIDTH", "Slab width must be positive"));
300
300
  if (spec.thickness <= 0) return err(specError("SLAB_ZERO_THICKNESS", "Slab thickness must be positive"));
@@ -322,7 +322,7 @@ function slabToSolid(spec) {
322
322
  ]
323
323
  ]);
324
324
  if (!profileResult.ok) return err(fromBrepError(profileResult.error, "SLAB_PROFILE_FAILED", "Failed to create slab profile"));
325
- const profile = _usingCtx$19.u(profileResult.value);
325
+ const profile = _usingCtx$20.u(profileResult.value);
326
326
  const solidResult = extrude(profile, [
327
327
  0,
328
328
  0,
@@ -336,9 +336,9 @@ function slabToSolid(spec) {
336
336
  }
337
337
  return ok(solid);
338
338
  } catch (_) {
339
- _usingCtx$19.e = _;
339
+ _usingCtx$20.e = _;
340
340
  } finally {
341
- _usingCtx$19.d();
341
+ _usingCtx$20.d();
342
342
  }
343
343
  }
344
344
  //#endregion
@@ -6335,14 +6335,14 @@ function to3D(points) {
6335
6335
  }
6336
6336
  function extendedProfileToFace(profile) {
6337
6337
  try {
6338
- var _usingCtx$18 = _usingCtx();
6338
+ var _usingCtx$19 = _usingCtx();
6339
6339
  const invalid = validateProfile(profile);
6340
6340
  if (invalid !== null) return err(invalid);
6341
6341
  const outerResult = polygon(to3D(outerLoop(profile)));
6342
6342
  if (!outerResult.ok) return err(fromBrepError(outerResult.error, "PROFILE_FACE_FAILED", "Failed to build profile outer face"));
6343
6343
  const holes = holeLoops(profile);
6344
6344
  if (holes.length === 0) return ok(outerResult.value);
6345
- const outerFace = _usingCtx$18.u(outerResult.value);
6345
+ const outerFace = _usingCtx$19.u(outerResult.value);
6346
6346
  const holeFaces = [];
6347
6347
  const holeWires = [];
6348
6348
  const disposeHoleFaces = () => {
@@ -6367,9 +6367,9 @@ function extendedProfileToFace(profile) {
6367
6367
  disposeHoleFaces();
6368
6368
  return ok(faceWithHoles);
6369
6369
  } catch (_) {
6370
- _usingCtx$18.e = _;
6370
+ _usingCtx$19.e = _;
6371
6371
  } finally {
6372
- _usingCtx$18.d();
6372
+ _usingCtx$19.d();
6373
6373
  }
6374
6374
  }
6375
6375
  //#endregion
@@ -6691,7 +6691,7 @@ function profileToPolygon(profile, circleSegments = 32) {
6691
6691
  //#region src/elementFns/beamFns.ts
6692
6692
  function beamToSolid(spec) {
6693
6693
  try {
6694
- var _usingCtx$17 = _usingCtx();
6694
+ var _usingCtx$18 = _usingCtx();
6695
6695
  if (spec.length <= 0) return err(specError("BEAM_ZERO_LENGTH", "Beam length must be positive"));
6696
6696
  if (isExtendedProfile(spec.profile)) try {
6697
6697
  var _usingCtx3 = _usingCtx();
@@ -6729,7 +6729,7 @@ function beamToSolid(spec) {
6729
6729
  ]);
6730
6730
  const profileResult = polygon(profilePts);
6731
6731
  if (!profileResult.ok) return err(fromBrepError(profileResult.error, "BEAM_PROFILE_FAILED", "Failed to create beam profile"));
6732
- const profile = _usingCtx$17.u(profileResult.value);
6732
+ const profile = _usingCtx$18.u(profileResult.value);
6733
6733
  const solidResult = extrude(profile, [
6734
6734
  spec.length,
6735
6735
  0,
@@ -6743,16 +6743,16 @@ function beamToSolid(spec) {
6743
6743
  }
6744
6744
  return ok(solid);
6745
6745
  } catch (_) {
6746
- _usingCtx$17.e = _;
6746
+ _usingCtx$18.e = _;
6747
6747
  } finally {
6748
- _usingCtx$17.d();
6748
+ _usingCtx$18.d();
6749
6749
  }
6750
6750
  }
6751
6751
  //#endregion
6752
6752
  //#region src/elementFns/columnFns.ts
6753
6753
  function columnToSolid(spec) {
6754
6754
  try {
6755
- var _usingCtx$16 = _usingCtx();
6755
+ var _usingCtx$17 = _usingCtx();
6756
6756
  if (spec.height <= 0) return err(specError("COLUMN_ZERO_HEIGHT", "Column height must be positive"));
6757
6757
  if (isExtendedProfile(spec.profile)) try {
6758
6758
  var _usingCtx3 = _usingCtx();
@@ -6781,7 +6781,7 @@ function columnToSolid(spec) {
6781
6781
  const profilePts = profilePtsResult.value;
6782
6782
  const profileResult = polygon(profilePts);
6783
6783
  if (!profileResult.ok) return err(fromBrepError(profileResult.error, "COLUMN_PROFILE_FAILED", "Failed to create column profile"));
6784
- const profile = _usingCtx$16.u(profileResult.value);
6784
+ const profile = _usingCtx$17.u(profileResult.value);
6785
6785
  const solidResult = extrude(profile, [
6786
6786
  0,
6787
6787
  0,
@@ -6795,9 +6795,9 @@ function columnToSolid(spec) {
6795
6795
  }
6796
6796
  return ok(solid);
6797
6797
  } catch (_) {
6798
- _usingCtx$16.e = _;
6798
+ _usingCtx$17.e = _;
6799
6799
  } finally {
6800
- _usingCtx$16.d();
6800
+ _usingCtx$17.d();
6801
6801
  }
6802
6802
  }
6803
6803
  //#endregion
@@ -6805,7 +6805,7 @@ function columnToSolid(spec) {
6805
6805
  var EPSILON_MM = 1;
6806
6806
  function openingToSolid(spec, wallThickness) {
6807
6807
  try {
6808
- var _usingCtx$15 = _usingCtx();
6808
+ var _usingCtx$16 = _usingCtx();
6809
6809
  if (spec.width <= 0) return err(specError("OPENING_ZERO_WIDTH", "Opening width must be positive"));
6810
6810
  if (spec.height <= 0) return err(specError("OPENING_ZERO_HEIGHT", "Opening height must be positive"));
6811
6811
  if (wallThickness <= 0) return err(specError("OPENING_ZERO_WALL_THICKNESS", "Wall thickness must be positive"));
@@ -6837,7 +6837,7 @@ function openingToSolid(spec, wallThickness) {
6837
6837
  ]
6838
6838
  ]);
6839
6839
  if (!profileResult.ok) return err(fromBrepError(profileResult.error, "OPENING_PROFILE_FAILED", "Failed to create opening profile"));
6840
- const profile = _usingCtx$15.u(profileResult.value);
6840
+ const profile = _usingCtx$16.u(profileResult.value);
6841
6841
  const solidResult = extrude(profile, [
6842
6842
  spec.width,
6843
6843
  0,
@@ -6851,16 +6851,16 @@ function openingToSolid(spec, wallThickness) {
6851
6851
  }
6852
6852
  return ok(solid);
6853
6853
  } catch (_) {
6854
- _usingCtx$15.e = _;
6854
+ _usingCtx$16.e = _;
6855
6855
  } finally {
6856
- _usingCtx$15.d();
6856
+ _usingCtx$16.d();
6857
6857
  }
6858
6858
  }
6859
6859
  //#endregion
6860
6860
  //#region src/elementFns/slabOpeningFns.ts
6861
6861
  function slabOpeningToSolid(spec, slabThickness) {
6862
6862
  try {
6863
- var _usingCtx$14 = _usingCtx();
6863
+ var _usingCtx$15 = _usingCtx();
6864
6864
  if (spec.sizeX <= 0) return err(specError("SLAB_OPENING_ZERO_SIZE_X", "Slab opening sizeX must be positive"));
6865
6865
  if (spec.sizeY <= 0) return err(specError("SLAB_OPENING_ZERO_SIZE_Y", "Slab opening sizeY must be positive"));
6866
6866
  if (slabThickness <= 0) return err(specError("SLAB_OPENING_ZERO_SLAB_THICKNESS", "Slab thickness must be positive"));
@@ -6892,7 +6892,7 @@ function slabOpeningToSolid(spec, slabThickness) {
6892
6892
  ]
6893
6893
  ]);
6894
6894
  if (!profileResult.ok) return err(fromBrepError(profileResult.error, "SLAB_OPENING_PROFILE_FAILED", "Failed to create slab opening profile"));
6895
- const profile = _usingCtx$14.u(profileResult.value);
6895
+ const profile = _usingCtx$15.u(profileResult.value);
6896
6896
  const solidResult = extrude(profile, [
6897
6897
  0,
6898
6898
  0,
@@ -6906,16 +6906,16 @@ function slabOpeningToSolid(spec, slabThickness) {
6906
6906
  }
6907
6907
  return ok(solid);
6908
6908
  } catch (_) {
6909
- _usingCtx$14.e = _;
6909
+ _usingCtx$15.e = _;
6910
6910
  } finally {
6911
- _usingCtx$14.d();
6911
+ _usingCtx$15.d();
6912
6912
  }
6913
6913
  }
6914
6914
  //#endregion
6915
6915
  //#region src/elementFns/spaceFns.ts
6916
6916
  function spaceToSolid(spec) {
6917
6917
  try {
6918
- var _usingCtx$13 = _usingCtx();
6918
+ var _usingCtx$14 = _usingCtx();
6919
6919
  if (spec.length <= 0) return err(specError("SPACE_ZERO_LENGTH", "Space length must be positive"));
6920
6920
  if (spec.width <= 0) return err(specError("SPACE_ZERO_WIDTH", "Space width must be positive"));
6921
6921
  if (spec.height <= 0) return err(specError("SPACE_ZERO_HEIGHT", "Space height must be positive"));
@@ -6943,7 +6943,7 @@ function spaceToSolid(spec) {
6943
6943
  ]
6944
6944
  ]);
6945
6945
  if (!profileResult.ok) return err(fromBrepError(profileResult.error, "SPACE_PROFILE_FAILED", "Failed to create space footprint"));
6946
- const profile = _usingCtx$13.u(profileResult.value);
6946
+ const profile = _usingCtx$14.u(profileResult.value);
6947
6947
  const solidResult = extrude(profile, [
6948
6948
  0,
6949
6949
  0,
@@ -6957,9 +6957,9 @@ function spaceToSolid(spec) {
6957
6957
  }
6958
6958
  return ok(solid);
6959
6959
  } catch (_) {
6960
- _usingCtx$13.e = _;
6960
+ _usingCtx$14.e = _;
6961
6961
  } finally {
6962
- _usingCtx$13.d();
6962
+ _usingCtx$14.d();
6963
6963
  }
6964
6964
  }
6965
6965
  //#endregion
@@ -6974,7 +6974,7 @@ function gate(solid, code) {
6974
6974
  }
6975
6975
  function flatRoof(spec) {
6976
6976
  try {
6977
- var _usingCtx$12 = _usingCtx();
6977
+ var _usingCtx$13 = _usingCtx();
6978
6978
  const { length, width, thickness } = spec;
6979
6979
  const face = polygon([
6980
6980
  [
@@ -6999,7 +6999,7 @@ function flatRoof(spec) {
6999
6999
  ]
7000
7000
  ]);
7001
7001
  if (!face.ok) return err(fromBrepError(face.error, "ROOF_PROFILE_FAILED", "Failed to create roof profile"));
7002
- const profile = _usingCtx$12.u(face.value);
7002
+ const profile = _usingCtx$13.u(face.value);
7003
7003
  const solid = extrude(profile, [
7004
7004
  0,
7005
7005
  0,
@@ -7008,9 +7008,9 @@ function flatRoof(spec) {
7008
7008
  if (!solid.ok) return err(fromBrepError(solid.error, "ROOF_EXTRUDE_FAILED", "Failed to extrude roof profile"));
7009
7009
  return gate(solid.value, "ROOF_INVALID_SOLID");
7010
7010
  } catch (_) {
7011
- _usingCtx$12.e = _;
7011
+ _usingCtx$13.e = _;
7012
7012
  } finally {
7013
- _usingCtx$12.d();
7013
+ _usingCtx$13.d();
7014
7014
  }
7015
7015
  }
7016
7016
  function shedRoof(spec, pitch) {
@@ -7206,7 +7206,7 @@ function roofToSolid(spec) {
7206
7206
  //#region src/elementFns/curtainWallFns.ts
7207
7207
  function boxSolid(sizeX, sizeY, sizeZ) {
7208
7208
  try {
7209
- var _usingCtx$11 = _usingCtx();
7209
+ var _usingCtx$12 = _usingCtx();
7210
7210
  const profileResult = polygon([
7211
7211
  [
7212
7212
  0,
@@ -7230,7 +7230,7 @@ function boxSolid(sizeX, sizeY, sizeZ) {
7230
7230
  ]
7231
7231
  ]);
7232
7232
  if (!profileResult.ok) return err(fromBrepError(profileResult.error, "CURTAIN_WALL_PROFILE_FAILED", "Failed to create component profile"));
7233
- const profile = _usingCtx$11.u(profileResult.value);
7233
+ const profile = _usingCtx$12.u(profileResult.value);
7234
7234
  const solidResult = extrude(profile, [
7235
7235
  0,
7236
7236
  0,
@@ -7244,9 +7244,9 @@ function boxSolid(sizeX, sizeY, sizeZ) {
7244
7244
  }
7245
7245
  return ok(solid);
7246
7246
  } catch (_) {
7247
- _usingCtx$11.e = _;
7247
+ _usingCtx$12.e = _;
7248
7248
  } finally {
7249
- _usingCtx$11.d();
7249
+ _usingCtx$12.d();
7250
7250
  }
7251
7251
  }
7252
7252
  function disposeComponents(components) {
@@ -7345,7 +7345,7 @@ function curtainWallToGrid(spec) {
7345
7345
  //#region src/elementFns/foundationFns.ts
7346
7346
  function footingToSolid(spec) {
7347
7347
  try {
7348
- var _usingCtx$10 = _usingCtx();
7348
+ var _usingCtx$11 = _usingCtx();
7349
7349
  if (spec.length <= 0) return err(specError("FOOTING_ZERO_LENGTH", "Footing length must be positive"));
7350
7350
  if (spec.width <= 0) return err(specError("FOOTING_ZERO_WIDTH", "Footing width must be positive"));
7351
7351
  if (spec.thickness <= 0) return err(specError("FOOTING_ZERO_THICKNESS", "Footing thickness must be positive"));
@@ -7373,7 +7373,7 @@ function footingToSolid(spec) {
7373
7373
  ]
7374
7374
  ]);
7375
7375
  if (!profileResult.ok) return err(fromBrepError(profileResult.error, "FOOTING_PROFILE_FAILED", "Failed to create footing profile"));
7376
- const profile = _usingCtx$10.u(profileResult.value);
7376
+ const profile = _usingCtx$11.u(profileResult.value);
7377
7377
  const solidResult = extrude(profile, [
7378
7378
  0,
7379
7379
  0,
@@ -7387,9 +7387,9 @@ function footingToSolid(spec) {
7387
7387
  }
7388
7388
  return ok(solid);
7389
7389
  } catch (_) {
7390
- _usingCtx$10.e = _;
7390
+ _usingCtx$11.e = _;
7391
7391
  } finally {
7392
- _usingCtx$10.d();
7392
+ _usingCtx$11.d();
7393
7393
  }
7394
7394
  }
7395
7395
  function pileToSolid(spec) {
@@ -7446,7 +7446,7 @@ function pileToSolid(spec) {
7446
7446
  //#region src/elementFns/railingFns.ts
7447
7447
  function panelRailing(spec) {
7448
7448
  try {
7449
- var _usingCtx$9 = _usingCtx();
7449
+ var _usingCtx$10 = _usingCtx();
7450
7450
  const { length, height, thickness } = spec;
7451
7451
  const profileResult = polygon([
7452
7452
  [
@@ -7471,7 +7471,7 @@ function panelRailing(spec) {
7471
7471
  ]
7472
7472
  ]);
7473
7473
  if (!profileResult.ok) return err(fromBrepError(profileResult.error, "RAILING_PROFILE_FAILED", "Failed to create railing profile"));
7474
- const profile = _usingCtx$9.u(profileResult.value);
7474
+ const profile = _usingCtx$10.u(profileResult.value);
7475
7475
  const solidResult = extrude(profile, [
7476
7476
  length,
7477
7477
  0,
@@ -7485,9 +7485,9 @@ function panelRailing(spec) {
7485
7485
  }
7486
7486
  return ok(solid);
7487
7487
  } catch (_) {
7488
- _usingCtx$9.e = _;
7488
+ _usingCtx$10.e = _;
7489
7489
  } finally {
7490
- _usingCtx$9.d();
7490
+ _usingCtx$10.d();
7491
7491
  }
7492
7492
  }
7493
7493
  function postedRailing(spec) {
@@ -7550,7 +7550,7 @@ function railingToSolid(spec) {
7550
7550
  //#region src/elementFns/coveringFns.ts
7551
7551
  function coveringToSolid(spec) {
7552
7552
  try {
7553
- var _usingCtx$8 = _usingCtx();
7553
+ var _usingCtx$9 = _usingCtx();
7554
7554
  if (spec.length <= 0) return err(specError("COVERING_ZERO_LENGTH", "Covering length must be positive"));
7555
7555
  if (spec.width <= 0) return err(specError("COVERING_ZERO_WIDTH", "Covering width must be positive"));
7556
7556
  if (spec.thickness <= 0) return err(specError("COVERING_ZERO_THICKNESS", "Covering thickness must be positive"));
@@ -7578,7 +7578,7 @@ function coveringToSolid(spec) {
7578
7578
  ]
7579
7579
  ]);
7580
7580
  if (!profileResult.ok) return err(fromBrepError(profileResult.error, "COVERING_PROFILE_FAILED", "Failed to create covering profile"));
7581
- const profile = _usingCtx$8.u(profileResult.value);
7581
+ const profile = _usingCtx$9.u(profileResult.value);
7582
7582
  const solidResult = extrude(profile, [
7583
7583
  0,
7584
7584
  0,
@@ -7592,9 +7592,9 @@ function coveringToSolid(spec) {
7592
7592
  }
7593
7593
  return ok(solid);
7594
7594
  } catch (_) {
7595
- _usingCtx$8.e = _;
7595
+ _usingCtx$9.e = _;
7596
7596
  } finally {
7597
- _usingCtx$8.d();
7597
+ _usingCtx$9.d();
7598
7598
  }
7599
7599
  }
7600
7600
  //#endregion
@@ -7782,6 +7782,31 @@ function measureProductBodyMaterial(solids) {
7782
7782
  return measured;
7783
7783
  });
7784
7784
  }
7785
+ /** Borrow all items and query their tight bounds in their existing coordinates. */
7786
+ function productBodyBounds(solids) {
7787
+ const items = validateItems(solids, "productBodyBounds");
7788
+ if (!items.ok) return items;
7789
+ return ownedOperation("productBodyBounds", (scope) => {
7790
+ let bounds;
7791
+ for (const [itemIndex, solid] of items.value.entries()) {
7792
+ scope.itemIndex = itemIndex;
7793
+ const ready = before("bounds", itemIndex);
7794
+ if (!ready.ok) return ready;
7795
+ const current = (productBodyTestHooks()?.bounds ?? getBounds)(solid);
7796
+ if (!Object.values(current).every(Number.isFinite) || current.xMin > current.xMax || current.yMin > current.yMax || current.zMin > current.zMax) return err(bodyError("productBodyBounds", "BODY_INVALID_BOUNDS", "Expected finite ordered bounds", current, itemIndex));
7797
+ bounds = bounds === void 0 ? { ...current } : {
7798
+ xMin: Math.min(bounds.xMin, current.xMin),
7799
+ xMax: Math.max(bounds.xMax, current.xMax),
7800
+ yMin: Math.min(bounds.yMin, current.yMin),
7801
+ yMax: Math.max(bounds.yMax, current.yMax),
7802
+ zMin: Math.min(bounds.zMin, current.zMin),
7803
+ zMax: Math.max(bounds.zMax, current.zMax)
7804
+ };
7805
+ }
7806
+ if (bounds === void 0) throw new Error("Validated Body produced no bounds");
7807
+ return ok(Object.freeze({ bounds: Object.freeze(bounds) }));
7808
+ });
7809
+ }
7785
7810
  //#endregion
7786
7811
  //#region src/model/bimModel.ts
7787
7812
  function exactWallBodyImmutable() {
@@ -8337,18 +8362,18 @@ var BimModel = class {
8337
8362
  }
8338
8363
  #cutWallGeometry(wall, openingSpec) {
8339
8364
  try {
8340
- var _usingCtx$7 = _usingCtx();
8365
+ var _usingCtx$8 = _usingCtx();
8341
8366
  if (wall.geometry.kind === "EXACT") return exactWallBodyImmutable();
8342
8367
  const toolResult = openingToSolid(openingSpec, wall.spec.thickness);
8343
8368
  if (!toolResult.ok) return err(toolResult.error);
8344
- const tool = _usingCtx$7.u(toolResult.value);
8369
+ const tool = _usingCtx$8.u(toolResult.value);
8345
8370
  const cutResult = cut(wall.geometry.solid, tool);
8346
8371
  if (!cutResult.ok) return err(fromBrepError(cutResult.error, "WALL_CUT_FAILED", "Boolean cut of wall with opening failed"));
8347
8372
  return ok(cutResult.value);
8348
8373
  } catch (_) {
8349
- _usingCtx$7.e = _;
8374
+ _usingCtx$8.e = _;
8350
8375
  } finally {
8351
- _usingCtx$7.d();
8376
+ _usingCtx$8.d();
8352
8377
  }
8353
8378
  }
8354
8379
  #replaceWallGeometry(wall, newGeometry) {
@@ -8963,7 +8988,7 @@ function buildSilhouette$1(numberOfRisers, riserHeight, treadLength) {
8963
8988
  }
8964
8989
  function stairFlightToSolid(spec) {
8965
8990
  try {
8966
- var _usingCtx$6 = _usingCtx();
8991
+ var _usingCtx$7 = _usingCtx();
8967
8992
  if (spec.width <= 0) return err(specError("STAIR_FLIGHT_ZERO_WIDTH", "Stair flight width must be positive"));
8968
8993
  if (spec.riserHeight <= 0) return err(specError("STAIR_FLIGHT_ZERO_RISER", "Stair flight riserHeight must be positive"));
8969
8994
  if (spec.treadLength <= 0) return err(specError("STAIR_FLIGHT_ZERO_TREAD", "Stair flight treadLength must be positive"));
@@ -8971,7 +8996,7 @@ function stairFlightToSolid(spec) {
8971
8996
  const silhouette = buildSilhouette$1(spec.numberOfRisers, spec.riserHeight, spec.treadLength);
8972
8997
  const profileResult = polygon(silhouette);
8973
8998
  if (!profileResult.ok) return err(fromBrepError(profileResult.error, "STAIR_FLIGHT_PROFILE_FAILED", "Failed to create stair flight silhouette profile"));
8974
- const profile = _usingCtx$6.u(profileResult.value);
8999
+ const profile = _usingCtx$7.u(profileResult.value);
8975
9000
  const solidResult = extrude(profile, [
8976
9001
  0,
8977
9002
  spec.width,
@@ -8988,9 +9013,9 @@ function stairFlightToSolid(spec) {
8988
9013
  geometrySimplified: false
8989
9014
  });
8990
9015
  } catch (_) {
8991
- _usingCtx$6.e = _;
9016
+ _usingCtx$7.e = _;
8992
9017
  } finally {
8993
- _usingCtx$6.d();
9018
+ _usingCtx$7.d();
8994
9019
  }
8995
9020
  }
8996
9021
  //#endregion
@@ -9021,7 +9046,7 @@ function buildSilhouette(length, rise, thickness) {
9021
9046
  }
9022
9047
  function rampFlightToSolid(spec) {
9023
9048
  try {
9024
- var _usingCtx$5 = _usingCtx();
9049
+ var _usingCtx$6 = _usingCtx();
9025
9050
  if (spec.width <= 0) return err(specError("RAMP_FLIGHT_ZERO_WIDTH", "Ramp flight width must be positive"));
9026
9051
  if (spec.length <= 0) return err(specError("RAMP_FLIGHT_ZERO_LENGTH", "Ramp flight length must be positive"));
9027
9052
  if (spec.slope <= 0) return err(specError("RAMP_FLIGHT_ZERO_SLOPE", "Ramp flight slope must be positive"));
@@ -9030,7 +9055,7 @@ function rampFlightToSolid(spec) {
9030
9055
  const silhouette = buildSilhouette(spec.length, rise, spec.thickness);
9031
9056
  const profileResult = polygon(silhouette);
9032
9057
  if (!profileResult.ok) return err(fromBrepError(profileResult.error, "RAMP_FLIGHT_PROFILE_FAILED", "Failed to create ramp flight silhouette profile"));
9033
- const profile = _usingCtx$5.u(profileResult.value);
9058
+ const profile = _usingCtx$6.u(profileResult.value);
9034
9059
  const solidResult = extrude(profile, [
9035
9060
  0,
9036
9061
  spec.width,
@@ -9047,9 +9072,9 @@ function rampFlightToSolid(spec) {
9047
9072
  geometrySimplified: true
9048
9073
  });
9049
9074
  } catch (_) {
9050
- _usingCtx$5.e = _;
9075
+ _usingCtx$6.e = _;
9051
9076
  } finally {
9052
- _usingCtx$5.d();
9077
+ _usingCtx$6.d();
9053
9078
  }
9054
9079
  }
9055
9080
  //#endregion
@@ -9192,22 +9217,22 @@ function placedSolids(el, options = {}) {
9192
9217
  case "STAIR": {
9193
9218
  const out = [];
9194
9219
  for (const flight of el.spec.flights) try {
9195
- var _usingCtx$4 = _usingCtx();
9220
+ var _usingCtx$5 = _usingCtx();
9196
9221
  const built = stairFlightToSolid(flight);
9197
9222
  if (!built.ok) {
9198
9223
  disposeAll$1(out);
9199
9224
  return err(built.error);
9200
9225
  }
9201
- const placed = placeWithinParent(_usingCtx$4.u(built.value.solid), flight, parentFrame);
9226
+ const placed = placeWithinParent(_usingCtx$5.u(built.value.solid), flight, parentFrame);
9202
9227
  if (!placed.ok) {
9203
9228
  disposeAll$1(out);
9204
9229
  return placed;
9205
9230
  }
9206
9231
  out.push(placed.value);
9207
9232
  } catch (_) {
9208
- _usingCtx$4.e = _;
9233
+ _usingCtx$5.e = _;
9209
9234
  } finally {
9210
- _usingCtx$4.d();
9235
+ _usingCtx$5.d();
9211
9236
  }
9212
9237
  return ok(out);
9213
9238
  }
@@ -11691,22 +11716,22 @@ function writeStairAssembly(w, spec, stairKey, assemblyGuid, ownerHistoryId, geo
11691
11716
  const flightExpressIds = [];
11692
11717
  let geometrySimplified = false;
11693
11718
  for (const [i, flight] of spec.flights.entries()) try {
11694
- var _usingCtx$3 = _usingCtx();
11719
+ var _usingCtx$4 = _usingCtx();
11695
11720
  const built = stairFlightToSolid(flight);
11696
11721
  if (!built.ok) return {
11697
11722
  ok: false,
11698
11723
  error: built.error
11699
11724
  };
11700
- const flightSolid = _usingCtx$3.u(built.value.solid);
11725
+ const flightSolid = _usingCtx$4.u(built.value.solid);
11701
11726
  const flightPlacementId = writeLocalPlacement(w, flight.origin, flight.axisZ, flight.axisX, stairPlacementId);
11702
11727
  const tess = writeTessellation(w, flightSolid, geomSubContextId, flightPlacementId);
11703
11728
  if (tess.usedFallback) geometrySimplified = true;
11704
11729
  const flightExpressId = writeStairFlightEntity(w, deriveIfcGuidSync(`elem:STAIR_FLIGHT:${stairKey}:${i}`), `${stairName} Flight ${i + 1}`, flight, ownerHistoryId, flightPlacementId, tess.productDefinitionShapeId);
11705
11730
  flightExpressIds.push(flightExpressId);
11706
11731
  } catch (_) {
11707
- _usingCtx$3.e = _;
11732
+ _usingCtx$4.e = _;
11708
11733
  } finally {
11709
- _usingCtx$3.d();
11734
+ _usingCtx$4.d();
11710
11735
  }
11711
11736
  writeRelAggregates(w, deriveIfcGuidSync(`rel:AGGREGATES:STAIR:${stairKey}`), ownerHistoryId, assemblyExpressId, flightExpressIds);
11712
11737
  writeAssemblyType(w, WebIFC.IFCSTAIRTYPE, "Stair Type", deriveIfcGuidSync(`type:STAIR:${stairKey}`), deriveIfcGuidSync(`rel-type:STAIR:${stairKey}`), predefinedType, ownerHistoryId, [assemblyExpressId]);
@@ -15247,7 +15272,7 @@ async function toIfc(model, meta) {
15247
15272
  }
15248
15273
  async function serializeIfc(model, meta) {
15249
15274
  try {
15250
- var _usingCtx$2 = _usingCtx();
15275
+ var _usingCtx$3 = _usingCtx();
15251
15276
  const issues = [];
15252
15277
  const project = model.getProject();
15253
15278
  if (!project) return err(ifcError("NO_PROJECT", "BimModel has no project — call model.init() first"));
@@ -15258,7 +15283,7 @@ async function serializeIfc(model, meta) {
15258
15283
  organization: meta.organizationName
15259
15284
  });
15260
15285
  if (!writerResult.ok) return writerResult;
15261
- const w = _usingCtx$2.u(writerResult.value);
15286
+ const w = _usingCtx$3.u(writerResult.value);
15262
15287
  w.setModelScope(project.guid);
15263
15288
  const elements = model.getAllElements();
15264
15289
  const relationships = model.getAllRelationships();
@@ -15732,9 +15757,9 @@ async function serializeIfc(model, meta) {
15732
15757
  issues
15733
15758
  }) : saved;
15734
15759
  } catch (_) {
15735
- _usingCtx$2.e = _;
15760
+ _usingCtx$3.e = _;
15736
15761
  } finally {
15737
- _usingCtx$2.d();
15762
+ _usingCtx$3.d();
15738
15763
  }
15739
15764
  }
15740
15765
  /**
@@ -16452,13 +16477,13 @@ function reconstructExtrusion(reader, extrusionId, scale, worldTransform, diagno
16452
16477
  ];
16453
16478
  const solidResult = (() => {
16454
16479
  try {
16455
- var _usingCtx$1 = _usingCtx();
16456
- const face = _usingCtx$1.u(faceResult.value);
16480
+ var _usingCtx$2 = _usingCtx();
16481
+ const face = _usingCtx$2.u(faceResult.value);
16457
16482
  return extrude(face, extrudeVec);
16458
16483
  } catch (_) {
16459
- _usingCtx$1.e = _;
16484
+ _usingCtx$2.e = _;
16460
16485
  } finally {
16461
- _usingCtx$1.d();
16486
+ _usingCtx$2.d();
16462
16487
  }
16463
16488
  })();
16464
16489
  if (!solidResult.ok) {
@@ -17180,6 +17205,161 @@ function errMsg$1(e) {
17180
17205
  return e instanceof Error ? e.message : String(e);
17181
17206
  }
17182
17207
  //#endregion
17208
+ //#region src/geometryCleanupDiagnostics.ts
17209
+ var diagnosticSchema = object({
17210
+ operation: string(),
17211
+ itemIndex: number().int().nonnegative(),
17212
+ resourceKind: _enum(["SHAPE", "TRANSFORM"]),
17213
+ localId: number().optional(),
17214
+ cause: unknown()
17215
+ });
17216
+ var failedCleanup = object({
17217
+ kind: literal("FAILED"),
17218
+ diagnostics: array(unknown()).nonempty()
17219
+ });
17220
+ var errorMetadata = object({ cleanup: unknown().optional() });
17221
+ var cleanupAttempt = Symbol("geometryCleanupAttempt");
17222
+ var bodyOperation = _enum(["productBodyBounds", "measureProductBodyMaterial"]);
17223
+ function attemptIdentity(source) {
17224
+ const identity = cleanupAttempt in source ? source[cleanupAttempt] : source;
17225
+ return typeof identity === "object" && identity !== null ? identity : source;
17226
+ }
17227
+ function errorChildren(value) {
17228
+ const children = [];
17229
+ if (value instanceof AggregateError) {
17230
+ const errors = value.errors;
17231
+ children.push(...errors);
17232
+ }
17233
+ if ("cause" in value) children.push(value.cause);
17234
+ if ("error" in value && "suppressed" in value) children.push(value.error, value.suppressed);
17235
+ return children;
17236
+ }
17237
+ /** Keep the actual failed-attempt markers; their causes need not be unique. */
17238
+ function nativeCleanupAttempts(error) {
17239
+ const attempts = [];
17240
+ const seen = /* @__PURE__ */ new Set();
17241
+ const visit = (value) => {
17242
+ if (typeof value !== "object" || value === null || seen.has(value)) return;
17243
+ seen.add(value);
17244
+ if (value instanceof GeometryCleanupError) attempts.push(value);
17245
+ else errorChildren(value).forEach(visit);
17246
+ };
17247
+ visit(error);
17248
+ return attempts;
17249
+ }
17250
+ /** Read prior owner attempts without retrying releases or merging distinct failures. */
17251
+ function reportedGeometryCleanup(error, operation, context = {}) {
17252
+ const diagnostics = [];
17253
+ const seenErrors = /* @__PURE__ */ new Set();
17254
+ const seenAttempts = /* @__PURE__ */ new Set();
17255
+ const add = (diagnostic, identity) => {
17256
+ if (seenAttempts.has(identity)) return;
17257
+ seenAttempts.add(identity);
17258
+ diagnostics.push(Object.freeze({
17259
+ operation: diagnostic.operation,
17260
+ itemIndex: diagnostic.itemIndex,
17261
+ resourceKind: diagnostic.resourceKind,
17262
+ cause: diagnostic.cause,
17263
+ ...diagnostic.localId === void 0 ? {} : { localId: diagnostic.localId },
17264
+ [cleanupAttempt]: identity
17265
+ }));
17266
+ };
17267
+ const visit = (value) => {
17268
+ if (typeof value !== "object" || value === null || seenErrors.has(value)) return;
17269
+ seenErrors.add(value);
17270
+ const metadata = errorMetadata.safeParse("metadata" in value ? value.metadata : void 0);
17271
+ const reports = [metadata.success ? metadata.data.cleanup : void 0, "cleanup" in value ? value.cleanup : void 0];
17272
+ const nativeAttempts = bodyOperation.safeParse("operation" in value ? value.operation : void 0).success ? nativeCleanupAttempts(value) : [];
17273
+ const matched = /* @__PURE__ */ new Set();
17274
+ const seenEntries = /* @__PURE__ */ new Set();
17275
+ for (const report of reports) {
17276
+ const parsed = failedCleanup.safeParse(report);
17277
+ if (!parsed.success) continue;
17278
+ for (const source of parsed.data.diagnostics) {
17279
+ if (typeof source !== "object" || source === null || seenEntries.has(source)) continue;
17280
+ const diagnostic = diagnosticSchema.safeParse(source);
17281
+ if (!diagnostic.success) continue;
17282
+ seenEntries.add(source);
17283
+ let identity = attemptIdentity(source);
17284
+ if (identity === source) {
17285
+ const native = nativeAttempts.find((attempt) => !matched.has(attempt) && attempt.resourceKind === diagnostic.data.resourceKind && attempt.cause === diagnostic.data.cause);
17286
+ if (native !== void 0) identity = native;
17287
+ }
17288
+ if (identity instanceof GeometryCleanupError) matched.add(identity);
17289
+ add(diagnostic.data, identity);
17290
+ }
17291
+ }
17292
+ if (value instanceof GeometryCleanupError) add({
17293
+ operation,
17294
+ itemIndex: context.itemIndex ?? 0,
17295
+ resourceKind: value.resourceKind,
17296
+ cause: value.cause
17297
+ }, value);
17298
+ else errorChildren(value).forEach(visit);
17299
+ };
17300
+ visit(error);
17301
+ return diagnostics;
17302
+ }
17303
+ //#endregion
17304
+ //#region src/import/cutImportedSolids.ts
17305
+ /** Borrow World-placed inputs; own every disconnected result independently of the cut's cache. */
17306
+ function cutImportedSolids(host, opening) {
17307
+ const context = { operation: "cutImportedSolids" };
17308
+ const temporaries = [];
17309
+ const outputs = [];
17310
+ const build = () => {
17311
+ const cutResult = cut(host, opening);
17312
+ if (!cutResult.ok) return err(fromBrepError(cutResult.error, "VOID_CUT_FAILED", "Opening cut failed"));
17313
+ temporaries.push({
17314
+ resource: cutResult.value,
17315
+ itemIndex: 0
17316
+ });
17317
+ const children = getKernel().iterShapes(cutResult.value.wrapped, "solid").map(createSolid);
17318
+ temporaries.push(...children.map((resource, itemIndex) => ({
17319
+ resource,
17320
+ itemIndex
17321
+ })));
17322
+ for (const solid of children) {
17323
+ const copied = clone(solid);
17324
+ if (!copied.ok) return err(fromBrepError(copied.error, "VOID_COPY_FAILED", "Could not retain opening result"));
17325
+ const candidate = copied.value;
17326
+ temporaries.push({
17327
+ resource: candidate,
17328
+ itemIndex: temporaries.length
17329
+ });
17330
+ const validated = validSolid(candidate);
17331
+ if (!validated.ok) return err(importError("VOID_INVALID_SOLID", "Opening result is not valid", validated.error));
17332
+ outputs.push(validated.value);
17333
+ }
17334
+ return ok(outputs);
17335
+ };
17336
+ let result;
17337
+ try {
17338
+ result = build();
17339
+ } catch (cause) {
17340
+ result = err(importError("VOID_CUT_FAILED", "Opening cut failed", cause));
17341
+ }
17342
+ const survivors = new Set(result.ok ? outputs : []);
17343
+ const cleanup = cleanupOwnedResources(temporaries.filter(({ resource }) => !survivors.has(resource)).reverse(), context);
17344
+ const diagnostics = [...result.ok ? [] : reportedGeometryCleanup(result.error, context.operation), ...cleanup.kind === "FAILED" ? cleanup.diagnostics : []];
17345
+ if (result.ok && diagnostics.length === 0) return result;
17346
+ if (result.ok) {
17347
+ const outputCleanup = cleanupOwnedResources(outputs.map((resource, itemIndex) => ({
17348
+ resource,
17349
+ itemIndex
17350
+ })), context);
17351
+ if (outputCleanup.kind === "FAILED") diagnostics.push(...outputCleanup.diagnostics);
17352
+ }
17353
+ const failure = result.ok ? importError("VOID_CLEANUP_FAILED", "Opening result cleanup failed") : result.error;
17354
+ return err({
17355
+ ...failure,
17356
+ metadata: {
17357
+ ...failure.metadata,
17358
+ cleanup: cleanupReport(diagnostics)
17359
+ }
17360
+ });
17361
+ }
17362
+ //#endregion
17183
17363
  //#region src/import/dataRead.ts
17184
17364
  /** Narrows an unknown line attribute to the `{ type, value }` wrapper shape. */
17185
17365
  function asTyped(v) {
@@ -17535,7 +17715,7 @@ var ELEMENT_TYPES = [
17535
17715
  * fatal failures — bad bytes, unsupported schema, WASM open failure — return
17536
17716
  * `err`. Inspect {@link ImportedModel.diagnostics} for per-element quality.
17537
17717
  *
17538
- * The web-ifc model handle is always closed in a `finally` block.
17718
+ * The web-ifc model handle is closed before handing reconstructed owners to the caller.
17539
17719
  */
17540
17720
  async function fromIfc(bytes, options = {}) {
17541
17721
  const settings = { coordinateToOrigin: options.coordinateToOrigin ?? false };
@@ -17543,6 +17723,7 @@ async function fromIfc(bytes, options = {}) {
17543
17723
  if (!readerResult.ok) return err(readerResult.error);
17544
17724
  const reader = readerResult.value;
17545
17725
  const elements = [];
17726
+ let result;
17546
17727
  try {
17547
17728
  reader.buildGuidMap();
17548
17729
  const diagnostics = [];
@@ -17569,13 +17750,23 @@ async function fromIfc(bytes, options = {}) {
17569
17750
  diagnostics: report,
17570
17751
  ...applicationName !== void 0 ? { applicationName } : {}
17571
17752
  };
17572
- return ok(model);
17753
+ result = ok(model);
17573
17754
  } catch (e) {
17574
- disposeElements(elements);
17575
- return err(importError("IMPORT_FAILED", "Unexpected failure during IFC import", e));
17576
- } finally {
17755
+ result = err(importError("IMPORT_FAILED", "Unexpected failure during IFC import", e));
17756
+ }
17757
+ try {
17577
17758
  reader.close();
17759
+ } catch (cause) {
17760
+ result = err(result.ok ? importError("IMPORT_CLOSE_FAILED", "Failed to close IFC reader", cause) : {
17761
+ ...result.error,
17762
+ metadata: {
17763
+ ...result.error.metadata,
17764
+ readerCloseCause: cause
17765
+ }
17766
+ });
17578
17767
  }
17768
+ if (!result.ok) return err(withCleanup(result.error, disposeElements(elements), "fromIfc"));
17769
+ return result;
17579
17770
  }
17580
17771
  /**
17581
17772
  * Reads one product into an {@link ImportedElement}. Returns null only when the
@@ -17626,8 +17817,8 @@ function readElement(reader, expressId, category, scale, containment, typeEnums,
17626
17817
  ...fills !== void 0 ? { fills } : {}
17627
17818
  };
17628
17819
  } catch (e) {
17629
- if (geometry !== null) disposeGeometry(geometry);
17630
- diagnostics.push(issue("error", "ELEMENT_READ_FAILED", `Element ${expressId} reconstruction threw: ${errMsg(e)}`, expressId));
17820
+ const failure = geometry === null ? e : withCleanup(importError("ELEMENT_READ_FAILED", "Element read failed", e), disposeGeometry(geometry), "readElement");
17821
+ diagnostics.push(issue("error", "ELEMENT_READ_FAILED", `Element ${expressId} reconstruction threw: ${errMsg(e)}`, expressId, { cause: failure }));
17631
17822
  return null;
17632
17823
  }
17633
17824
  }
@@ -17643,36 +17834,79 @@ function findFills(reader, elementExpressId) {
17643
17834
  }
17644
17835
  /**
17645
17836
  * Reconstructs a host element's body geometry and, per IFC `IfcRelVoidsElement`
17646
- * semantics, subtracts the reconstructed solid of every opening that voids it —
17837
+ * semantics, subtracts each opening's Body representation from a parametric host —
17647
17838
  * so a wall with a door hole comes back as the cut solid, matching the source
17648
17839
  * model. Falls back to the uncut solid (with a diagnostic) if a cut fails.
17649
17840
  */
17650
17841
  function reconstructGeometry(reader, expressId, scale, voidedBy, diagnostics) {
17651
17842
  const base = toImportedGeometry(reader, expressId, scale, diagnostics);
17652
17843
  if (base.fidelity !== "PARAMETRIC" || base.completeness !== "COMPLETE" || voidedBy.length === 0) return base;
17653
- const firstHost = base.solids[0];
17654
- if (firstHost === void 0) return base;
17655
- const hosts = [firstHost, ...base.solids.slice(1)];
17656
- for (const openingId of voidedBy) {
17657
- const opening = readBodyGeometry(reader, openingId, scale, diagnostics);
17658
- if (opening.kind !== "SOLID") continue;
17659
- try {
17660
- for (let hostIndex = 0; hostIndex < hosts.length; hostIndex++) {
17844
+ if (base.solids[0] === void 0) return base;
17845
+ const hosts = [...base.solids];
17846
+ try {
17847
+ for (const openingId of voidedBy) try {
17848
+ var _usingCtx$1 = _usingCtx();
17849
+ const opening = readBodyGeometry(reader, openingId, scale, diagnostics);
17850
+ if (opening.kind !== "SOLID") continue;
17851
+ const openingOwner = _usingCtx$1.u(ownOpening(opening.solid, openingId));
17852
+ for (let hostIndex = 0; hostIndex < hosts.length;) {
17661
17853
  const host = hosts[hostIndex];
17662
- if (host === void 0) continue;
17663
- const cutResult = cut(host, opening.solid);
17854
+ if (host === void 0) break;
17855
+ const cutResult = cutImportedSolids(host, openingOwner.solid);
17664
17856
  if (!cutResult.ok) {
17665
- diagnostics.push(issue("warning", "VOID_SUBTRACTION_FAILED", `Opening ${openingId} could not be subtracted from element ${expressId}: ${cutResult.error.message}`, expressId));
17857
+ diagnostics.push(issue("warning", "VOID_SUBTRACTION_FAILED", `Opening ${openingId} could not be subtracted from element ${expressId}: ${cutResult.error.message}`, expressId, { cause: cutResult.error }));
17858
+ hostIndex++;
17666
17859
  continue;
17667
17860
  }
17668
- host[Symbol.dispose]();
17669
- hosts[hostIndex] = cutResult.value;
17861
+ hosts.splice(hostIndex, 1, ...cutResult.value);
17862
+ const cleanup = cleanupOwnedResources([{
17863
+ resource: host,
17864
+ itemIndex: hostIndex
17865
+ }], { operation: "importOpening" });
17866
+ if (cleanup.kind === "FAILED") diagnostics.push(issue("warning", "VOID_HOST_CLEANUP_FAILED", "Opening result retained, but previous host release failed", expressId, { cleanup }));
17867
+ hostIndex += cutResult.value.length;
17670
17868
  }
17869
+ } catch (_) {
17870
+ _usingCtx$1.e = _;
17671
17871
  } finally {
17672
- opening.solid[Symbol.dispose]();
17872
+ _usingCtx$1.d();
17673
17873
  }
17874
+ return completeImportedGeometry("PARAMETRIC", hosts, expressId, diagnostics);
17875
+ } catch (cause) {
17876
+ const failure = importError("GEOMETRY_RECONSTRUCTION_FAILED", "Opening reconstruction failed", cause);
17877
+ const cleanup = cleanupOwnedResources(hosts.map((resource, itemIndex) => ({
17878
+ resource,
17879
+ itemIndex
17880
+ })), { operation: "reconstructGeometry" });
17881
+ throw Object.assign(new Error(failure.message, { cause }), {
17882
+ code: failure.code,
17883
+ metadata: withCleanup(failure, cleanup, "reconstructGeometry").metadata
17884
+ });
17674
17885
  }
17675
- return completeImportedGeometry("PARAMETRIC", hosts, expressId, diagnostics);
17886
+ }
17887
+ function ownOpening(solid, openingId) {
17888
+ return {
17889
+ solid,
17890
+ [Symbol.dispose]() {
17891
+ const cleanup = cleanupOwnedResources([{
17892
+ resource: solid,
17893
+ itemIndex: 0
17894
+ }], { operation: "importOpeningTool" });
17895
+ if (cleanup.kind === "FAILED") throw new Error(`Opening ${openingId} cleanup failed`, { cause: {
17896
+ ...importError("OPENING_CLEANUP_FAILED", "Temporary opening release failed"),
17897
+ metadata: { cleanup }
17898
+ } });
17899
+ }
17900
+ };
17901
+ }
17902
+ function withCleanup(error, cleanup, operation) {
17903
+ return {
17904
+ ...error,
17905
+ metadata: {
17906
+ ...error.metadata,
17907
+ cleanup: cleanupReport([...reportedGeometryCleanup(error, operation), ...cleanup.kind === "FAILED" ? cleanup.diagnostics : []])
17908
+ }
17909
+ };
17676
17910
  }
17677
17911
  function toImportedGeometry(reader, expressId, scale, diagnostics) {
17678
17912
  const body = readBodyItems(reader, expressId, scale, diagnostics);
@@ -17720,61 +17954,48 @@ function completeImportedGeometry(fidelity, solids, expressId, diagnostics) {
17720
17954
  fidelity,
17721
17955
  completeness: "COMPLETE",
17722
17956
  solids,
17723
- solid: solids.length === 1 ? solids[0] : null,
17957
+ solid: solids.length === 1 ? solids[0] ?? null : null,
17724
17958
  ...measureCompleteBody(solids, expressId, diagnostics)
17725
17959
  };
17726
17960
  }
17727
17961
  function measureCompleteBody(solids, expressId, diagnostics) {
17962
+ const unavailable = (cause, message = errMsg(cause)) => {
17963
+ diagnostics.push(issue("warning", "BODY_AGGREGATE_MEASUREMENT_FAILED", `Complete Body aggregate measurement failed: ${message}`, expressId, { cause }));
17964
+ return {
17965
+ bounds: null,
17966
+ volumeMm3: null
17967
+ };
17968
+ };
17728
17969
  try {
17729
- const first = solids[0];
17970
+ const [first, ...rest] = solids;
17730
17971
  if (first === void 0) return {
17731
17972
  bounds: null,
17732
17973
  volumeMm3: null
17733
17974
  };
17734
- const initial = getBounds(first);
17735
- let xMin = initial.xMin;
17736
- let xMax = initial.xMax;
17737
- let yMin = initial.yMin;
17738
- let yMax = initial.yMax;
17739
- let zMin = initial.zMin;
17740
- let zMax = initial.zMax;
17741
- let volumeMm3 = 0;
17742
- for (const solid of solids) {
17743
- const measured = measureVolume(solid);
17744
- if (!measured.ok) throw new Error(measured.error.message);
17745
- volumeMm3 += measured.value;
17746
- const itemBounds = getBounds(solid);
17747
- xMin = Math.min(xMin, itemBounds.xMin);
17748
- xMax = Math.max(xMax, itemBounds.xMax);
17749
- yMin = Math.min(yMin, itemBounds.yMin);
17750
- yMax = Math.max(yMax, itemBounds.yMax);
17751
- zMin = Math.min(zMin, itemBounds.zMin);
17752
- zMax = Math.max(zMax, itemBounds.zMax);
17753
- }
17975
+ const items = [first, ...rest];
17976
+ const bounds = productBodyBounds(items);
17977
+ if (!bounds.ok) return unavailable(bounds.error, bounds.error.message);
17978
+ const volume = measureProductBodyMaterial(items);
17979
+ if (!volume.ok) return unavailable(volume.error, volume.error.message);
17754
17980
  return {
17755
- bounds: {
17756
- xMin,
17757
- xMax,
17758
- yMin,
17759
- yMax,
17760
- zMin,
17761
- zMax
17762
- },
17763
- volumeMm3
17981
+ bounds: bounds.value.bounds,
17982
+ volumeMm3: volume.value
17764
17983
  };
17765
17984
  } catch (cause) {
17766
- diagnostics.push(issue("warning", "BODY_AGGREGATE_MEASUREMENT_FAILED", `Complete Body aggregate measurement failed: ${errMsg(cause)}`, expressId));
17767
- return {
17768
- bounds: null,
17769
- volumeMm3: null
17770
- };
17985
+ return unavailable(cause);
17771
17986
  }
17772
17987
  }
17773
17988
  function disposeGeometry(geometry) {
17774
- for (const solid of geometry.solids) solid[Symbol.dispose]();
17989
+ return cleanupOwnedResources(geometry.solids.map((resource, itemIndex) => ({
17990
+ resource,
17991
+ itemIndex
17992
+ })), { operation: "disposeImportedGeometry" });
17775
17993
  }
17776
17994
  function disposeElements(elements) {
17777
- for (const element of elements) disposeGeometry(element.geometry);
17995
+ return cleanupOwnedResources(elements.flatMap(({ geometry }) => geometry.solids).map((resource, itemIndex) => ({
17996
+ resource,
17997
+ itemIndex
17998
+ })), { operation: "disposeImportedElements" });
17778
17999
  }
17779
18000
  function toImportedPset(pset) {
17780
18001
  return {