brepjs-bim 0.24.4 → 0.25.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.
@@ -266,7 +266,7 @@ function _usingCtx() {
266
266
  //#region src/elementFns/wallFns.ts
267
267
  function wallToSolid(spec) {
268
268
  try {
269
- var _usingCtx$20 = _usingCtx();
269
+ var _usingCtx$21 = _usingCtx();
270
270
  if (spec.length <= 0) return (0, brepjs.err)(specError("WALL_ZERO_LENGTH", "Wall length must be positive"));
271
271
  if (spec.height <= 0) return (0, brepjs.err)(specError("WALL_ZERO_HEIGHT", "Wall height must be positive"));
272
272
  if (spec.thickness <= 0) return (0, brepjs.err)(specError("WALL_ZERO_THICKNESS", "Wall thickness must be positive"));
@@ -294,7 +294,7 @@ function wallToSolid(spec) {
294
294
  ]
295
295
  ]);
296
296
  if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "WALL_PROFILE_FAILED", "Failed to create wall profile"));
297
- const profile = _usingCtx$20.u(profileResult.value);
297
+ const profile = _usingCtx$21.u(profileResult.value);
298
298
  const solidResult = (0, brepjs.extrude)(profile, [
299
299
  length,
300
300
  0,
@@ -308,16 +308,16 @@ function wallToSolid(spec) {
308
308
  }
309
309
  return (0, brepjs.ok)(solid);
310
310
  } catch (_) {
311
- _usingCtx$20.e = _;
311
+ _usingCtx$21.e = _;
312
312
  } finally {
313
- _usingCtx$20.d();
313
+ _usingCtx$21.d();
314
314
  }
315
315
  }
316
316
  //#endregion
317
317
  //#region src/elementFns/slabFns.ts
318
318
  function slabToSolid(spec) {
319
319
  try {
320
- var _usingCtx$19 = _usingCtx();
320
+ var _usingCtx$20 = _usingCtx();
321
321
  if (spec.length <= 0) return (0, brepjs.err)(specError("SLAB_ZERO_LENGTH", "Slab length must be positive"));
322
322
  if (spec.width <= 0) return (0, brepjs.err)(specError("SLAB_ZERO_WIDTH", "Slab width must be positive"));
323
323
  if (spec.thickness <= 0) return (0, brepjs.err)(specError("SLAB_ZERO_THICKNESS", "Slab thickness must be positive"));
@@ -345,7 +345,7 @@ function slabToSolid(spec) {
345
345
  ]
346
346
  ]);
347
347
  if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "SLAB_PROFILE_FAILED", "Failed to create slab profile"));
348
- const profile = _usingCtx$19.u(profileResult.value);
348
+ const profile = _usingCtx$20.u(profileResult.value);
349
349
  const solidResult = (0, brepjs.extrude)(profile, [
350
350
  0,
351
351
  0,
@@ -359,9 +359,9 @@ function slabToSolid(spec) {
359
359
  }
360
360
  return (0, brepjs.ok)(solid);
361
361
  } catch (_) {
362
- _usingCtx$19.e = _;
362
+ _usingCtx$20.e = _;
363
363
  } finally {
364
- _usingCtx$19.d();
364
+ _usingCtx$20.d();
365
365
  }
366
366
  }
367
367
  //#endregion
@@ -522,7 +522,7 @@ var propertyKeyTypes = /* @__PURE__*/ new Set([
522
522
  function escapeRegex(str) {
523
523
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
524
524
  }
525
- function clone$3(inst, def, params) {
525
+ function clone$4(inst, def, params) {
526
526
  const cl = new inst._zod.constr(def ?? inst._zod.def);
527
527
  if (!def || params?.parent) cl._zod.parent = inst;
528
528
  return cl;
@@ -569,7 +569,7 @@ function pick(schema, mask) {
569
569
  if (checks && checks.length > 0) throw new Error(".pick() cannot be used on object schemas containing refinements");
570
570
  const newShape = {};
571
571
  mirrorShape(newShape, schema, maskedKeys(schema, mask));
572
- return clone$3(schema, mergeDefs(currDef, {
572
+ return clone$4(schema, mergeDefs(currDef, {
573
573
  shape: newShape,
574
574
  checks: []
575
575
  }));
@@ -590,7 +590,7 @@ function omit(schema, mask) {
590
590
  const omitted = new Set(maskedKeys(schema, mask));
591
591
  const newShape = {};
592
592
  mirrorShape(newShape, schema, Reflect.ownKeys(sourceShape(schema)).filter((key) => !omitted.has(key)));
593
- return clone$3(schema, mergeDefs(currDef, {
593
+ return clone$4(schema, mergeDefs(currDef, {
594
594
  shape: newShape,
595
595
  checks: []
596
596
  }));
@@ -602,7 +602,7 @@ function extend(schema, shape) {
602
602
  const existingShape = sourceShape(schema);
603
603
  for (const key of Reflect.ownKeys(shape)) if (Object.getOwnPropertyDescriptor(existingShape, key) !== void 0) throw new Error("Cannot overwrite keys on object schemas containing refinements. Use `.safeExtend()` instead.");
604
604
  }
605
- return clone$3(schema, mergeDefs(schema._zod.def, { shape: extended(schema, shape) }));
605
+ return clone$4(schema, mergeDefs(schema._zod.def, { shape: extended(schema, shape) }));
606
606
  }
607
607
  function extended(schema, shape) {
608
608
  const newShape = {};
@@ -612,7 +612,7 @@ function extended(schema, shape) {
612
612
  }
613
613
  function safeExtend(schema, shape) {
614
614
  if (!isPlainObject(shape)) throw new Error("Invalid input to safeExtend: expected a plain object");
615
- return clone$3(schema, mergeDefs(schema._zod.def, { shape: extended(schema, shape) }));
615
+ return clone$4(schema, mergeDefs(schema._zod.def, { shape: extended(schema, shape) }));
616
616
  }
617
617
  function merge(a, b) {
618
618
  if (!b?._zod?.def) throw new Error("Invalid input to merge: expected an object schema. To merge a plain shape, use `.extend()`.");
@@ -620,7 +620,7 @@ function merge(a, b) {
620
620
  const newShape = {};
621
621
  mirrorShape(newShape, a, Reflect.ownKeys(sourceShape(a)));
622
622
  mirrorShape(newShape, b, Reflect.ownKeys(sourceShape(b)));
623
- return clone$3(a, mergeDefs(a._zod.def, {
623
+ return clone$4(a, mergeDefs(a._zod.def, {
624
624
  shape: newShape,
625
625
  get catchall() {
626
626
  return b._zod.def.catchall;
@@ -637,7 +637,7 @@ function partial(Class, schema, mask, name = "partial") {
637
637
  type: "optional",
638
638
  innerType: value
639
639
  })));
640
- return clone$3(schema, mergeDefs(schema._zod.def, {
640
+ return clone$4(schema, mergeDefs(schema._zod.def, {
641
641
  shape: newShape,
642
642
  checks: []
643
643
  }));
@@ -649,7 +649,7 @@ function required(Class, schema, mask) {
649
649
  type: "nonoptional",
650
650
  innerType: value
651
651
  }));
652
- return clone$3(schema, mergeDefs(schema._zod.def, { shape: newShape }));
652
+ return clone$4(schema, mergeDefs(schema._zod.def, { shape: newShape }));
653
653
  }
654
654
  function aborted(x, startIndex = 0) {
655
655
  if (x.aborted === true) return true;
@@ -1719,7 +1719,7 @@ var Doc = class {
1719
1719
  var version = {
1720
1720
  major: 4,
1721
1721
  minor: 6,
1722
- patch: 4
1722
+ patch: 5
1723
1723
  };
1724
1724
  //#endregion
1725
1725
  //#region ../../node_modules/zod/v4/core/schemas.js
@@ -5215,7 +5215,7 @@ var ZodType = /*@__PURE__*/ $constructor("ZodType", (inst, def) => {
5215
5215
  return this.check(...chks);
5216
5216
  },
5217
5217
  clone(def, params) {
5218
- return clone$3(this, def, params);
5218
+ return clone$4(this, def, params);
5219
5219
  },
5220
5220
  brand() {
5221
5221
  return this;
@@ -6358,14 +6358,14 @@ function to3D(points) {
6358
6358
  }
6359
6359
  function extendedProfileToFace(profile) {
6360
6360
  try {
6361
- var _usingCtx$18 = _usingCtx();
6361
+ var _usingCtx$19 = _usingCtx();
6362
6362
  const invalid = validateProfile(profile);
6363
6363
  if (invalid !== null) return (0, brepjs.err)(invalid);
6364
6364
  const outerResult = (0, brepjs.polygon)(to3D(outerLoop(profile)));
6365
6365
  if (!outerResult.ok) return (0, brepjs.err)(fromBrepError(outerResult.error, "PROFILE_FACE_FAILED", "Failed to build profile outer face"));
6366
6366
  const holes = holeLoops(profile);
6367
6367
  if (holes.length === 0) return (0, brepjs.ok)(outerResult.value);
6368
- const outerFace = _usingCtx$18.u(outerResult.value);
6368
+ const outerFace = _usingCtx$19.u(outerResult.value);
6369
6369
  const holeFaces = [];
6370
6370
  const holeWires = [];
6371
6371
  const disposeHoleFaces = () => {
@@ -6390,9 +6390,9 @@ function extendedProfileToFace(profile) {
6390
6390
  disposeHoleFaces();
6391
6391
  return (0, brepjs.ok)(faceWithHoles);
6392
6392
  } catch (_) {
6393
- _usingCtx$18.e = _;
6393
+ _usingCtx$19.e = _;
6394
6394
  } finally {
6395
- _usingCtx$18.d();
6395
+ _usingCtx$19.d();
6396
6396
  }
6397
6397
  }
6398
6398
  //#endregion
@@ -6714,7 +6714,7 @@ function profileToPolygon(profile, circleSegments = 32) {
6714
6714
  //#region src/elementFns/beamFns.ts
6715
6715
  function beamToSolid(spec) {
6716
6716
  try {
6717
- var _usingCtx$17 = _usingCtx();
6717
+ var _usingCtx$18 = _usingCtx();
6718
6718
  if (spec.length <= 0) return (0, brepjs.err)(specError("BEAM_ZERO_LENGTH", "Beam length must be positive"));
6719
6719
  if (isExtendedProfile(spec.profile)) try {
6720
6720
  var _usingCtx3 = _usingCtx();
@@ -6752,7 +6752,7 @@ function beamToSolid(spec) {
6752
6752
  ]);
6753
6753
  const profileResult = (0, brepjs.polygon)(profilePts);
6754
6754
  if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "BEAM_PROFILE_FAILED", "Failed to create beam profile"));
6755
- const profile = _usingCtx$17.u(profileResult.value);
6755
+ const profile = _usingCtx$18.u(profileResult.value);
6756
6756
  const solidResult = (0, brepjs.extrude)(profile, [
6757
6757
  spec.length,
6758
6758
  0,
@@ -6766,16 +6766,16 @@ function beamToSolid(spec) {
6766
6766
  }
6767
6767
  return (0, brepjs.ok)(solid);
6768
6768
  } catch (_) {
6769
- _usingCtx$17.e = _;
6769
+ _usingCtx$18.e = _;
6770
6770
  } finally {
6771
- _usingCtx$17.d();
6771
+ _usingCtx$18.d();
6772
6772
  }
6773
6773
  }
6774
6774
  //#endregion
6775
6775
  //#region src/elementFns/columnFns.ts
6776
6776
  function columnToSolid(spec) {
6777
6777
  try {
6778
- var _usingCtx$16 = _usingCtx();
6778
+ var _usingCtx$17 = _usingCtx();
6779
6779
  if (spec.height <= 0) return (0, brepjs.err)(specError("COLUMN_ZERO_HEIGHT", "Column height must be positive"));
6780
6780
  if (isExtendedProfile(spec.profile)) try {
6781
6781
  var _usingCtx3 = _usingCtx();
@@ -6804,7 +6804,7 @@ function columnToSolid(spec) {
6804
6804
  const profilePts = profilePtsResult.value;
6805
6805
  const profileResult = (0, brepjs.polygon)(profilePts);
6806
6806
  if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "COLUMN_PROFILE_FAILED", "Failed to create column profile"));
6807
- const profile = _usingCtx$16.u(profileResult.value);
6807
+ const profile = _usingCtx$17.u(profileResult.value);
6808
6808
  const solidResult = (0, brepjs.extrude)(profile, [
6809
6809
  0,
6810
6810
  0,
@@ -6818,9 +6818,9 @@ function columnToSolid(spec) {
6818
6818
  }
6819
6819
  return (0, brepjs.ok)(solid);
6820
6820
  } catch (_) {
6821
- _usingCtx$16.e = _;
6821
+ _usingCtx$17.e = _;
6822
6822
  } finally {
6823
- _usingCtx$16.d();
6823
+ _usingCtx$17.d();
6824
6824
  }
6825
6825
  }
6826
6826
  //#endregion
@@ -6828,7 +6828,7 @@ function columnToSolid(spec) {
6828
6828
  var EPSILON_MM = 1;
6829
6829
  function openingToSolid(spec, wallThickness) {
6830
6830
  try {
6831
- var _usingCtx$15 = _usingCtx();
6831
+ var _usingCtx$16 = _usingCtx();
6832
6832
  if (spec.width <= 0) return (0, brepjs.err)(specError("OPENING_ZERO_WIDTH", "Opening width must be positive"));
6833
6833
  if (spec.height <= 0) return (0, brepjs.err)(specError("OPENING_ZERO_HEIGHT", "Opening height must be positive"));
6834
6834
  if (wallThickness <= 0) return (0, brepjs.err)(specError("OPENING_ZERO_WALL_THICKNESS", "Wall thickness must be positive"));
@@ -6860,7 +6860,7 @@ function openingToSolid(spec, wallThickness) {
6860
6860
  ]
6861
6861
  ]);
6862
6862
  if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "OPENING_PROFILE_FAILED", "Failed to create opening profile"));
6863
- const profile = _usingCtx$15.u(profileResult.value);
6863
+ const profile = _usingCtx$16.u(profileResult.value);
6864
6864
  const solidResult = (0, brepjs.extrude)(profile, [
6865
6865
  spec.width,
6866
6866
  0,
@@ -6874,16 +6874,16 @@ function openingToSolid(spec, wallThickness) {
6874
6874
  }
6875
6875
  return (0, brepjs.ok)(solid);
6876
6876
  } catch (_) {
6877
- _usingCtx$15.e = _;
6877
+ _usingCtx$16.e = _;
6878
6878
  } finally {
6879
- _usingCtx$15.d();
6879
+ _usingCtx$16.d();
6880
6880
  }
6881
6881
  }
6882
6882
  //#endregion
6883
6883
  //#region src/elementFns/slabOpeningFns.ts
6884
6884
  function slabOpeningToSolid(spec, slabThickness) {
6885
6885
  try {
6886
- var _usingCtx$14 = _usingCtx();
6886
+ var _usingCtx$15 = _usingCtx();
6887
6887
  if (spec.sizeX <= 0) return (0, brepjs.err)(specError("SLAB_OPENING_ZERO_SIZE_X", "Slab opening sizeX must be positive"));
6888
6888
  if (spec.sizeY <= 0) return (0, brepjs.err)(specError("SLAB_OPENING_ZERO_SIZE_Y", "Slab opening sizeY must be positive"));
6889
6889
  if (slabThickness <= 0) return (0, brepjs.err)(specError("SLAB_OPENING_ZERO_SLAB_THICKNESS", "Slab thickness must be positive"));
@@ -6915,7 +6915,7 @@ function slabOpeningToSolid(spec, slabThickness) {
6915
6915
  ]
6916
6916
  ]);
6917
6917
  if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "SLAB_OPENING_PROFILE_FAILED", "Failed to create slab opening profile"));
6918
- const profile = _usingCtx$14.u(profileResult.value);
6918
+ const profile = _usingCtx$15.u(profileResult.value);
6919
6919
  const solidResult = (0, brepjs.extrude)(profile, [
6920
6920
  0,
6921
6921
  0,
@@ -6929,16 +6929,16 @@ function slabOpeningToSolid(spec, slabThickness) {
6929
6929
  }
6930
6930
  return (0, brepjs.ok)(solid);
6931
6931
  } catch (_) {
6932
- _usingCtx$14.e = _;
6932
+ _usingCtx$15.e = _;
6933
6933
  } finally {
6934
- _usingCtx$14.d();
6934
+ _usingCtx$15.d();
6935
6935
  }
6936
6936
  }
6937
6937
  //#endregion
6938
6938
  //#region src/elementFns/spaceFns.ts
6939
6939
  function spaceToSolid(spec) {
6940
6940
  try {
6941
- var _usingCtx$13 = _usingCtx();
6941
+ var _usingCtx$14 = _usingCtx();
6942
6942
  if (spec.length <= 0) return (0, brepjs.err)(specError("SPACE_ZERO_LENGTH", "Space length must be positive"));
6943
6943
  if (spec.width <= 0) return (0, brepjs.err)(specError("SPACE_ZERO_WIDTH", "Space width must be positive"));
6944
6944
  if (spec.height <= 0) return (0, brepjs.err)(specError("SPACE_ZERO_HEIGHT", "Space height must be positive"));
@@ -6966,7 +6966,7 @@ function spaceToSolid(spec) {
6966
6966
  ]
6967
6967
  ]);
6968
6968
  if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "SPACE_PROFILE_FAILED", "Failed to create space footprint"));
6969
- const profile = _usingCtx$13.u(profileResult.value);
6969
+ const profile = _usingCtx$14.u(profileResult.value);
6970
6970
  const solidResult = (0, brepjs.extrude)(profile, [
6971
6971
  0,
6972
6972
  0,
@@ -6980,9 +6980,9 @@ function spaceToSolid(spec) {
6980
6980
  }
6981
6981
  return (0, brepjs.ok)(solid);
6982
6982
  } catch (_) {
6983
- _usingCtx$13.e = _;
6983
+ _usingCtx$14.e = _;
6984
6984
  } finally {
6985
- _usingCtx$13.d();
6985
+ _usingCtx$14.d();
6986
6986
  }
6987
6987
  }
6988
6988
  //#endregion
@@ -6997,7 +6997,7 @@ function gate(solid, code) {
6997
6997
  }
6998
6998
  function flatRoof(spec) {
6999
6999
  try {
7000
- var _usingCtx$12 = _usingCtx();
7000
+ var _usingCtx$13 = _usingCtx();
7001
7001
  const { length, width, thickness } = spec;
7002
7002
  const face = (0, brepjs.polygon)([
7003
7003
  [
@@ -7022,7 +7022,7 @@ function flatRoof(spec) {
7022
7022
  ]
7023
7023
  ]);
7024
7024
  if (!face.ok) return (0, brepjs.err)(fromBrepError(face.error, "ROOF_PROFILE_FAILED", "Failed to create roof profile"));
7025
- const profile = _usingCtx$12.u(face.value);
7025
+ const profile = _usingCtx$13.u(face.value);
7026
7026
  const solid = (0, brepjs.extrude)(profile, [
7027
7027
  0,
7028
7028
  0,
@@ -7031,9 +7031,9 @@ function flatRoof(spec) {
7031
7031
  if (!solid.ok) return (0, brepjs.err)(fromBrepError(solid.error, "ROOF_EXTRUDE_FAILED", "Failed to extrude roof profile"));
7032
7032
  return gate(solid.value, "ROOF_INVALID_SOLID");
7033
7033
  } catch (_) {
7034
- _usingCtx$12.e = _;
7034
+ _usingCtx$13.e = _;
7035
7035
  } finally {
7036
- _usingCtx$12.d();
7036
+ _usingCtx$13.d();
7037
7037
  }
7038
7038
  }
7039
7039
  function shedRoof(spec, pitch) {
@@ -7229,7 +7229,7 @@ function roofToSolid(spec) {
7229
7229
  //#region src/elementFns/curtainWallFns.ts
7230
7230
  function boxSolid(sizeX, sizeY, sizeZ) {
7231
7231
  try {
7232
- var _usingCtx$11 = _usingCtx();
7232
+ var _usingCtx$12 = _usingCtx();
7233
7233
  const profileResult = (0, brepjs.polygon)([
7234
7234
  [
7235
7235
  0,
@@ -7253,7 +7253,7 @@ function boxSolid(sizeX, sizeY, sizeZ) {
7253
7253
  ]
7254
7254
  ]);
7255
7255
  if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "CURTAIN_WALL_PROFILE_FAILED", "Failed to create component profile"));
7256
- const profile = _usingCtx$11.u(profileResult.value);
7256
+ const profile = _usingCtx$12.u(profileResult.value);
7257
7257
  const solidResult = (0, brepjs.extrude)(profile, [
7258
7258
  0,
7259
7259
  0,
@@ -7267,9 +7267,9 @@ function boxSolid(sizeX, sizeY, sizeZ) {
7267
7267
  }
7268
7268
  return (0, brepjs.ok)(solid);
7269
7269
  } catch (_) {
7270
- _usingCtx$11.e = _;
7270
+ _usingCtx$12.e = _;
7271
7271
  } finally {
7272
- _usingCtx$11.d();
7272
+ _usingCtx$12.d();
7273
7273
  }
7274
7274
  }
7275
7275
  function disposeComponents(components) {
@@ -7368,7 +7368,7 @@ function curtainWallToGrid(spec) {
7368
7368
  //#region src/elementFns/foundationFns.ts
7369
7369
  function footingToSolid(spec) {
7370
7370
  try {
7371
- var _usingCtx$10 = _usingCtx();
7371
+ var _usingCtx$11 = _usingCtx();
7372
7372
  if (spec.length <= 0) return (0, brepjs.err)(specError("FOOTING_ZERO_LENGTH", "Footing length must be positive"));
7373
7373
  if (spec.width <= 0) return (0, brepjs.err)(specError("FOOTING_ZERO_WIDTH", "Footing width must be positive"));
7374
7374
  if (spec.thickness <= 0) return (0, brepjs.err)(specError("FOOTING_ZERO_THICKNESS", "Footing thickness must be positive"));
@@ -7396,7 +7396,7 @@ function footingToSolid(spec) {
7396
7396
  ]
7397
7397
  ]);
7398
7398
  if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "FOOTING_PROFILE_FAILED", "Failed to create footing profile"));
7399
- const profile = _usingCtx$10.u(profileResult.value);
7399
+ const profile = _usingCtx$11.u(profileResult.value);
7400
7400
  const solidResult = (0, brepjs.extrude)(profile, [
7401
7401
  0,
7402
7402
  0,
@@ -7410,9 +7410,9 @@ function footingToSolid(spec) {
7410
7410
  }
7411
7411
  return (0, brepjs.ok)(solid);
7412
7412
  } catch (_) {
7413
- _usingCtx$10.e = _;
7413
+ _usingCtx$11.e = _;
7414
7414
  } finally {
7415
- _usingCtx$10.d();
7415
+ _usingCtx$11.d();
7416
7416
  }
7417
7417
  }
7418
7418
  function pileToSolid(spec) {
@@ -7469,7 +7469,7 @@ function pileToSolid(spec) {
7469
7469
  //#region src/elementFns/railingFns.ts
7470
7470
  function panelRailing(spec) {
7471
7471
  try {
7472
- var _usingCtx$9 = _usingCtx();
7472
+ var _usingCtx$10 = _usingCtx();
7473
7473
  const { length, height, thickness } = spec;
7474
7474
  const profileResult = (0, brepjs.polygon)([
7475
7475
  [
@@ -7494,7 +7494,7 @@ function panelRailing(spec) {
7494
7494
  ]
7495
7495
  ]);
7496
7496
  if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "RAILING_PROFILE_FAILED", "Failed to create railing profile"));
7497
- const profile = _usingCtx$9.u(profileResult.value);
7497
+ const profile = _usingCtx$10.u(profileResult.value);
7498
7498
  const solidResult = (0, brepjs.extrude)(profile, [
7499
7499
  length,
7500
7500
  0,
@@ -7508,9 +7508,9 @@ function panelRailing(spec) {
7508
7508
  }
7509
7509
  return (0, brepjs.ok)(solid);
7510
7510
  } catch (_) {
7511
- _usingCtx$9.e = _;
7511
+ _usingCtx$10.e = _;
7512
7512
  } finally {
7513
- _usingCtx$9.d();
7513
+ _usingCtx$10.d();
7514
7514
  }
7515
7515
  }
7516
7516
  function postedRailing(spec) {
@@ -7573,7 +7573,7 @@ function railingToSolid(spec) {
7573
7573
  //#region src/elementFns/coveringFns.ts
7574
7574
  function coveringToSolid(spec) {
7575
7575
  try {
7576
- var _usingCtx$8 = _usingCtx();
7576
+ var _usingCtx$9 = _usingCtx();
7577
7577
  if (spec.length <= 0) return (0, brepjs.err)(specError("COVERING_ZERO_LENGTH", "Covering length must be positive"));
7578
7578
  if (spec.width <= 0) return (0, brepjs.err)(specError("COVERING_ZERO_WIDTH", "Covering width must be positive"));
7579
7579
  if (spec.thickness <= 0) return (0, brepjs.err)(specError("COVERING_ZERO_THICKNESS", "Covering thickness must be positive"));
@@ -7601,7 +7601,7 @@ function coveringToSolid(spec) {
7601
7601
  ]
7602
7602
  ]);
7603
7603
  if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "COVERING_PROFILE_FAILED", "Failed to create covering profile"));
7604
- const profile = _usingCtx$8.u(profileResult.value);
7604
+ const profile = _usingCtx$9.u(profileResult.value);
7605
7605
  const solidResult = (0, brepjs.extrude)(profile, [
7606
7606
  0,
7607
7607
  0,
@@ -7615,9 +7615,9 @@ function coveringToSolid(spec) {
7615
7615
  }
7616
7616
  return (0, brepjs.ok)(solid);
7617
7617
  } catch (_) {
7618
- _usingCtx$8.e = _;
7618
+ _usingCtx$9.e = _;
7619
7619
  } finally {
7620
- _usingCtx$8.d();
7620
+ _usingCtx$9.d();
7621
7621
  }
7622
7622
  }
7623
7623
  //#endregion
@@ -7805,6 +7805,31 @@ function measureProductBodyMaterial(solids) {
7805
7805
  return measured;
7806
7806
  });
7807
7807
  }
7808
+ /** Borrow all items and query their tight bounds in their existing coordinates. */
7809
+ function productBodyBounds(solids) {
7810
+ const items = validateItems(solids, "productBodyBounds");
7811
+ if (!items.ok) return items;
7812
+ return ownedOperation("productBodyBounds", (scope) => {
7813
+ let bounds;
7814
+ for (const [itemIndex, solid] of items.value.entries()) {
7815
+ scope.itemIndex = itemIndex;
7816
+ const ready = before("bounds", itemIndex);
7817
+ if (!ready.ok) return ready;
7818
+ const current = (productBodyTestHooks()?.bounds ?? brepjs.getBounds)(solid);
7819
+ if (!Object.values(current).every(Number.isFinite) || current.xMin > current.xMax || current.yMin > current.yMax || current.zMin > current.zMax) return (0, brepjs.err)(bodyError("productBodyBounds", "BODY_INVALID_BOUNDS", "Expected finite ordered bounds", current, itemIndex));
7820
+ bounds = bounds === void 0 ? { ...current } : {
7821
+ xMin: Math.min(bounds.xMin, current.xMin),
7822
+ xMax: Math.max(bounds.xMax, current.xMax),
7823
+ yMin: Math.min(bounds.yMin, current.yMin),
7824
+ yMax: Math.max(bounds.yMax, current.yMax),
7825
+ zMin: Math.min(bounds.zMin, current.zMin),
7826
+ zMax: Math.max(bounds.zMax, current.zMax)
7827
+ };
7828
+ }
7829
+ if (bounds === void 0) throw new Error("Validated Body produced no bounds");
7830
+ return (0, brepjs.ok)(Object.freeze({ bounds: Object.freeze(bounds) }));
7831
+ });
7832
+ }
7808
7833
  //#endregion
7809
7834
  //#region src/model/bimModel.ts
7810
7835
  function exactWallBodyImmutable() {
@@ -8360,18 +8385,18 @@ var BimModel = class {
8360
8385
  }
8361
8386
  #cutWallGeometry(wall, openingSpec) {
8362
8387
  try {
8363
- var _usingCtx$7 = _usingCtx();
8388
+ var _usingCtx$8 = _usingCtx();
8364
8389
  if (wall.geometry.kind === "EXACT") return exactWallBodyImmutable();
8365
8390
  const toolResult = openingToSolid(openingSpec, wall.spec.thickness);
8366
8391
  if (!toolResult.ok) return (0, brepjs.err)(toolResult.error);
8367
- const tool = _usingCtx$7.u(toolResult.value);
8392
+ const tool = _usingCtx$8.u(toolResult.value);
8368
8393
  const cutResult = (0, brepjs.cut)(wall.geometry.solid, tool);
8369
8394
  if (!cutResult.ok) return (0, brepjs.err)(fromBrepError(cutResult.error, "WALL_CUT_FAILED", "Boolean cut of wall with opening failed"));
8370
8395
  return (0, brepjs.ok)(cutResult.value);
8371
8396
  } catch (_) {
8372
- _usingCtx$7.e = _;
8397
+ _usingCtx$8.e = _;
8373
8398
  } finally {
8374
- _usingCtx$7.d();
8399
+ _usingCtx$8.d();
8375
8400
  }
8376
8401
  }
8377
8402
  #replaceWallGeometry(wall, newGeometry) {
@@ -8986,7 +9011,7 @@ function buildSilhouette$1(numberOfRisers, riserHeight, treadLength) {
8986
9011
  }
8987
9012
  function stairFlightToSolid(spec) {
8988
9013
  try {
8989
- var _usingCtx$6 = _usingCtx();
9014
+ var _usingCtx$7 = _usingCtx();
8990
9015
  if (spec.width <= 0) return (0, brepjs.err)(specError("STAIR_FLIGHT_ZERO_WIDTH", "Stair flight width must be positive"));
8991
9016
  if (spec.riserHeight <= 0) return (0, brepjs.err)(specError("STAIR_FLIGHT_ZERO_RISER", "Stair flight riserHeight must be positive"));
8992
9017
  if (spec.treadLength <= 0) return (0, brepjs.err)(specError("STAIR_FLIGHT_ZERO_TREAD", "Stair flight treadLength must be positive"));
@@ -8994,7 +9019,7 @@ function stairFlightToSolid(spec) {
8994
9019
  const silhouette = buildSilhouette$1(spec.numberOfRisers, spec.riserHeight, spec.treadLength);
8995
9020
  const profileResult = (0, brepjs.polygon)(silhouette);
8996
9021
  if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "STAIR_FLIGHT_PROFILE_FAILED", "Failed to create stair flight silhouette profile"));
8997
- const profile = _usingCtx$6.u(profileResult.value);
9022
+ const profile = _usingCtx$7.u(profileResult.value);
8998
9023
  const solidResult = (0, brepjs.extrude)(profile, [
8999
9024
  0,
9000
9025
  spec.width,
@@ -9011,9 +9036,9 @@ function stairFlightToSolid(spec) {
9011
9036
  geometrySimplified: false
9012
9037
  });
9013
9038
  } catch (_) {
9014
- _usingCtx$6.e = _;
9039
+ _usingCtx$7.e = _;
9015
9040
  } finally {
9016
- _usingCtx$6.d();
9041
+ _usingCtx$7.d();
9017
9042
  }
9018
9043
  }
9019
9044
  //#endregion
@@ -9044,7 +9069,7 @@ function buildSilhouette(length, rise, thickness) {
9044
9069
  }
9045
9070
  function rampFlightToSolid(spec) {
9046
9071
  try {
9047
- var _usingCtx$5 = _usingCtx();
9072
+ var _usingCtx$6 = _usingCtx();
9048
9073
  if (spec.width <= 0) return (0, brepjs.err)(specError("RAMP_FLIGHT_ZERO_WIDTH", "Ramp flight width must be positive"));
9049
9074
  if (spec.length <= 0) return (0, brepjs.err)(specError("RAMP_FLIGHT_ZERO_LENGTH", "Ramp flight length must be positive"));
9050
9075
  if (spec.slope <= 0) return (0, brepjs.err)(specError("RAMP_FLIGHT_ZERO_SLOPE", "Ramp flight slope must be positive"));
@@ -9053,7 +9078,7 @@ function rampFlightToSolid(spec) {
9053
9078
  const silhouette = buildSilhouette(spec.length, rise, spec.thickness);
9054
9079
  const profileResult = (0, brepjs.polygon)(silhouette);
9055
9080
  if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "RAMP_FLIGHT_PROFILE_FAILED", "Failed to create ramp flight silhouette profile"));
9056
- const profile = _usingCtx$5.u(profileResult.value);
9081
+ const profile = _usingCtx$6.u(profileResult.value);
9057
9082
  const solidResult = (0, brepjs.extrude)(profile, [
9058
9083
  0,
9059
9084
  spec.width,
@@ -9070,9 +9095,9 @@ function rampFlightToSolid(spec) {
9070
9095
  geometrySimplified: true
9071
9096
  });
9072
9097
  } catch (_) {
9073
- _usingCtx$5.e = _;
9098
+ _usingCtx$6.e = _;
9074
9099
  } finally {
9075
- _usingCtx$5.d();
9100
+ _usingCtx$6.d();
9076
9101
  }
9077
9102
  }
9078
9103
  //#endregion
@@ -9215,22 +9240,22 @@ function placedSolids(el, options = {}) {
9215
9240
  case "STAIR": {
9216
9241
  const out = [];
9217
9242
  for (const flight of el.spec.flights) try {
9218
- var _usingCtx$4 = _usingCtx();
9243
+ var _usingCtx$5 = _usingCtx();
9219
9244
  const built = stairFlightToSolid(flight);
9220
9245
  if (!built.ok) {
9221
9246
  disposeAll$1(out);
9222
9247
  return (0, brepjs.err)(built.error);
9223
9248
  }
9224
- const placed = placeWithinParent(_usingCtx$4.u(built.value.solid), flight, parentFrame);
9249
+ const placed = placeWithinParent(_usingCtx$5.u(built.value.solid), flight, parentFrame);
9225
9250
  if (!placed.ok) {
9226
9251
  disposeAll$1(out);
9227
9252
  return placed;
9228
9253
  }
9229
9254
  out.push(placed.value);
9230
9255
  } catch (_) {
9231
- _usingCtx$4.e = _;
9256
+ _usingCtx$5.e = _;
9232
9257
  } finally {
9233
- _usingCtx$4.d();
9258
+ _usingCtx$5.d();
9234
9259
  }
9235
9260
  return (0, brepjs.ok)(out);
9236
9261
  }
@@ -9740,11 +9765,21 @@ function writeHeader(w, meta) {
9740
9765
  WorldCoordinateSystem: w.ref(writeAxis2Placement3D(w)),
9741
9766
  TrueNorth: null
9742
9767
  });
9768
+ return {
9769
+ ownerHistoryId,
9770
+ geomContextId,
9771
+ geomSubContextId: writeGeometricSubContext(w, geomContextId, "Body"),
9772
+ unitAssignmentId,
9773
+ lengthUnitId
9774
+ };
9775
+ }
9776
+ /** Separates display Body geometry from non-subtractive Reference geometry. */
9777
+ function writeGeometricSubContext(w, geomContextId, identifier) {
9743
9778
  const geomSubContextId = w.nextId();
9744
9779
  w.writeLine({
9745
9780
  expressID: geomSubContextId,
9746
9781
  type: web_ifc.IFCGEOMETRICREPRESENTATIONSUBCONTEXT,
9747
- ContextIdentifier: w.mkType(web_ifc.IFCLABEL, "Body"),
9782
+ ContextIdentifier: w.mkType(web_ifc.IFCLABEL, identifier),
9748
9783
  ContextType: w.mkType(web_ifc.IFCLABEL, "Model"),
9749
9784
  CoordinateSpaceDimension: null,
9750
9785
  Precision: null,
@@ -9758,13 +9793,7 @@ function writeHeader(w, meta) {
9758
9793
  },
9759
9794
  UserDefinedTargetView: null
9760
9795
  });
9761
- return {
9762
- ownerHistoryId,
9763
- geomContextId,
9764
- geomSubContextId,
9765
- unitAssignmentId,
9766
- lengthUnitId
9767
- };
9796
+ return geomSubContextId;
9768
9797
  }
9769
9798
  /**
9770
9799
  * Writes IfcProjectedCRS + IfcMapConversion georeferencing the model context
@@ -10172,7 +10201,7 @@ function writePreparedTessellationBody(w, items, geomSubContextId) {
10172
10201
  bodyItemIds
10173
10202
  };
10174
10203
  }
10175
- function writeExactBodyGeometry(w, placement, items, geomSubContextId, parentPlacementId) {
10204
+ function writeProductBodyGeometry(w, placement, items, geomSubContextId, parentPlacementId) {
10176
10205
  const placement3DId = writeAxis2Placement3D(w, [
10177
10206
  toIfcLengthM(placement.origin[0]),
10178
10207
  toIfcLengthM(placement.origin[1]),
@@ -10218,47 +10247,6 @@ function writePreparedTessellationItem(w, prepared) {
10218
10247
  });
10219
10248
  return faceSetId;
10220
10249
  }
10221
- /**
10222
- * Builds a wall 'Axis' IfcShapeRepresentation: an IfcPolyline from (0,0) to
10223
- * (lengthM, 0) in the wall's local XY plane, wrapped in an IfcShapeRepresentation
10224
- * with RepresentationIdentifier='Axis', RepresentationType='Curve2D'. Returns
10225
- * the IfcShapeRepresentation express ID so callers can add it alongside the Body
10226
- * representation in an IfcProductDefinitionShape.
10227
- *
10228
- * wallLengthMm is the wall length in mm (brepjs native units); it is converted
10229
- * to metres for IFC.
10230
- */
10231
- function writeWallAxisRepresentation(w, wallLengthMm, geomSubContextId) {
10232
- const lengthM = toIfcLengthM(wallLengthMm);
10233
- const startId = w.nextId();
10234
- w.writeLine({
10235
- expressID: startId,
10236
- type: web_ifc.IFCCARTESIANPOINT,
10237
- Coordinates: [w.mkType(web_ifc.IFCLENGTHMEASURE, 0), w.mkType(web_ifc.IFCLENGTHMEASURE, 0)]
10238
- });
10239
- const endId = w.nextId();
10240
- w.writeLine({
10241
- expressID: endId,
10242
- type: web_ifc.IFCCARTESIANPOINT,
10243
- Coordinates: [w.mkType(web_ifc.IFCLENGTHMEASURE, lengthM), w.mkType(web_ifc.IFCLENGTHMEASURE, 0)]
10244
- });
10245
- const polylineId = w.nextId();
10246
- w.writeLine({
10247
- expressID: polylineId,
10248
- type: web_ifc.IFCPOLYLINE,
10249
- Points: [w.ref(startId), w.ref(endId)]
10250
- });
10251
- const shapeRepId = w.nextId();
10252
- w.writeLine({
10253
- expressID: shapeRepId,
10254
- type: web_ifc.IFCSHAPEREPRESENTATION,
10255
- ContextOfItems: w.ref(geomSubContextId),
10256
- RepresentationIdentifier: w.mkType(web_ifc.IFCLABEL, "Axis"),
10257
- RepresentationType: w.mkType(web_ifc.IFCLABEL, "Curve2D"),
10258
- Items: [w.ref(polylineId)]
10259
- });
10260
- return shapeRepId;
10261
- }
10262
10250
  function writeFacetedBrepFallback(w, geomSubContextId, reason) {
10263
10251
  const originId = w.nextId();
10264
10252
  w.writeLine({
@@ -10589,81 +10577,6 @@ function writePolyline(w, points) {
10589
10577
  }
10590
10578
  //#endregion
10591
10579
  //#region src/ifc-writer/geometryWriter.ts
10592
- function writeWallGeometry(w, spec, geomSubContextId, parentPlacementId) {
10593
- const placement3DId = writeAxis2Placement3D(w, spec.origin.map(toIfcLengthM), spec.axisZ, spec.axisX);
10594
- const localPlacementId = w.nextId();
10595
- w.writeLine({
10596
- expressID: localPlacementId,
10597
- type: web_ifc.IFCLOCALPLACEMENT,
10598
- PlacementRelTo: parentPlacementId !== null ? w.ref(parentPlacementId) : null,
10599
- RelativePlacement: w.ref(placement3DId)
10600
- });
10601
- const thicknessM = toIfcLengthM(spec.thickness);
10602
- const heightM = toIfcLengthM(spec.height);
10603
- const lengthM = toIfcLengthM(spec.length);
10604
- const profileId = w.nextId();
10605
- w.writeLine({
10606
- expressID: profileId,
10607
- type: web_ifc.IFCRECTANGLEPROFILEDEF,
10608
- ProfileType: {
10609
- type: 3,
10610
- value: "AREA"
10611
- },
10612
- ProfileName: null,
10613
- Position: w.ref(writeAxis2Placement2D$1(w, [thicknessM / 2, heightM / 2])),
10614
- XDim: w.mkType(web_ifc.IFCPOSITIVELENGTHMEASURE, thicknessM),
10615
- YDim: w.mkType(web_ifc.IFCPOSITIVELENGTHMEASURE, heightM)
10616
- });
10617
- const extrusionPosId = writeAxis2Placement3D(w, [
10618
- 0,
10619
- 0,
10620
- 0
10621
- ], [
10622
- 1,
10623
- 0,
10624
- 0
10625
- ], [
10626
- 0,
10627
- 1,
10628
- 0
10629
- ]);
10630
- const extrusionDirId = writeDirection(w, [
10631
- 0,
10632
- 0,
10633
- 1
10634
- ]);
10635
- const extrusionId = w.nextId();
10636
- w.writeLine({
10637
- expressID: extrusionId,
10638
- type: web_ifc.IFCEXTRUDEDAREASOLID,
10639
- SweptArea: w.ref(profileId),
10640
- Position: w.ref(extrusionPosId),
10641
- ExtrudedDirection: w.ref(extrusionDirId),
10642
- Depth: w.mkType(web_ifc.IFCPOSITIVELENGTHMEASURE, lengthM)
10643
- });
10644
- const shapeRepId = w.nextId();
10645
- w.writeLine({
10646
- expressID: shapeRepId,
10647
- type: web_ifc.IFCSHAPEREPRESENTATION,
10648
- ContextOfItems: w.ref(geomSubContextId),
10649
- RepresentationIdentifier: w.mkType(web_ifc.IFCLABEL, "Body"),
10650
- RepresentationType: w.mkType(web_ifc.IFCLABEL, "SweptSolid"),
10651
- Items: [w.ref(extrusionId)]
10652
- });
10653
- const axisRepId = writeWallAxisRepresentation(w, spec.length, geomSubContextId);
10654
- const productDefinitionShapeId = w.nextId();
10655
- w.writeLine({
10656
- expressID: productDefinitionShapeId,
10657
- type: web_ifc.IFCPRODUCTDEFINITIONSHAPE,
10658
- Name: null,
10659
- Description: null,
10660
- Representations: [w.ref(axisRepId), w.ref(shapeRepId)]
10661
- });
10662
- return {
10663
- localPlacementId,
10664
- productDefinitionShapeId
10665
- };
10666
- }
10667
10580
  function writeSlabGeometry(w, spec, geomSubContextId, parentPlacementId) {
10668
10581
  const placement3DId = writeAxis2Placement3D(w, spec.origin.map(toIfcLengthM), spec.axisZ, spec.axisX);
10669
10582
  const localPlacementId = w.nextId();
@@ -11714,22 +11627,22 @@ function writeStairAssembly(w, spec, stairKey, assemblyGuid, ownerHistoryId, geo
11714
11627
  const flightExpressIds = [];
11715
11628
  let geometrySimplified = false;
11716
11629
  for (const [i, flight] of spec.flights.entries()) try {
11717
- var _usingCtx$3 = _usingCtx();
11630
+ var _usingCtx$4 = _usingCtx();
11718
11631
  const built = stairFlightToSolid(flight);
11719
11632
  if (!built.ok) return {
11720
11633
  ok: false,
11721
11634
  error: built.error
11722
11635
  };
11723
- const flightSolid = _usingCtx$3.u(built.value.solid);
11636
+ const flightSolid = _usingCtx$4.u(built.value.solid);
11724
11637
  const flightPlacementId = writeLocalPlacement(w, flight.origin, flight.axisZ, flight.axisX, stairPlacementId);
11725
11638
  const tess = writeTessellation(w, flightSolid, geomSubContextId, flightPlacementId);
11726
11639
  if (tess.usedFallback) geometrySimplified = true;
11727
11640
  const flightExpressId = writeStairFlightEntity(w, deriveIfcGuidSync(`elem:STAIR_FLIGHT:${stairKey}:${i}`), `${stairName} Flight ${i + 1}`, flight, ownerHistoryId, flightPlacementId, tess.productDefinitionShapeId);
11728
11641
  flightExpressIds.push(flightExpressId);
11729
11642
  } catch (_) {
11730
- _usingCtx$3.e = _;
11643
+ _usingCtx$4.e = _;
11731
11644
  } finally {
11732
- _usingCtx$3.d();
11645
+ _usingCtx$4.d();
11733
11646
  }
11734
11647
  writeRelAggregates(w, deriveIfcGuidSync(`rel:AGGREGATES:STAIR:${stairKey}`), ownerHistoryId, assemblyExpressId, flightExpressIds);
11735
11648
  writeAssemblyType(w, web_ifc.IFCSTAIRTYPE, "Stair Type", deriveIfcGuidSync(`type:STAIR:${stairKey}`), deriveIfcGuidSync(`rel-type:STAIR:${stairKey}`), predefinedType, ownerHistoryId, [assemblyExpressId]);
@@ -11806,104 +11719,6 @@ function writeRampAssembly(w, spec, rampKey, assemblyGuid, ownerHistoryId, geomS
11806
11719
  }
11807
11720
  //#endregion
11808
11721
  //#region src/ifc-writer/railingWriter.ts
11809
- function writeRailingGeometry(w, spec, solid, geomSubContextId, parentPlacementId) {
11810
- const placement3DId = writeAxis2Placement3D(w, spec.origin.map(toIfcLengthM), spec.axisZ, spec.axisX);
11811
- const localPlacementId = w.nextId();
11812
- w.writeLine({
11813
- expressID: localPlacementId,
11814
- type: web_ifc.IFCLOCALPLACEMENT,
11815
- PlacementRelTo: parentPlacementId !== null ? w.ref(parentPlacementId) : null,
11816
- RelativePlacement: w.ref(placement3DId)
11817
- });
11818
- if (spec.infill === "POSTED") {
11819
- const tess = writeTessellation(w, solid, geomSubContextId, localPlacementId);
11820
- return {
11821
- localPlacementId,
11822
- productDefinitionShapeId: tess.productDefinitionShapeId,
11823
- bodyItemId: null,
11824
- usedFallback: tess.usedFallback
11825
- };
11826
- }
11827
- const thicknessM = toIfcLengthM(spec.thickness);
11828
- const heightM = toIfcLengthM(spec.height);
11829
- const lengthM = toIfcLengthM(spec.length);
11830
- const profileOriginId = w.nextId();
11831
- w.writeLine({
11832
- expressID: profileOriginId,
11833
- type: web_ifc.IFCCARTESIANPOINT,
11834
- Coordinates: [w.mkType(web_ifc.IFCLENGTHMEASURE, thicknessM / 2), w.mkType(web_ifc.IFCLENGTHMEASURE, heightM / 2)]
11835
- });
11836
- const profilePosId = w.nextId();
11837
- w.writeLine({
11838
- expressID: profilePosId,
11839
- type: web_ifc.IFCAXIS2PLACEMENT2D,
11840
- Location: w.ref(profileOriginId),
11841
- RefDirection: null
11842
- });
11843
- const profileId = w.nextId();
11844
- w.writeLine({
11845
- expressID: profileId,
11846
- type: web_ifc.IFCRECTANGLEPROFILEDEF,
11847
- ProfileType: {
11848
- type: 3,
11849
- value: "AREA"
11850
- },
11851
- ProfileName: null,
11852
- Position: w.ref(profilePosId),
11853
- XDim: w.mkType(web_ifc.IFCPOSITIVELENGTHMEASURE, thicknessM),
11854
- YDim: w.mkType(web_ifc.IFCPOSITIVELENGTHMEASURE, heightM)
11855
- });
11856
- const extrusionPosId = writeAxis2Placement3D(w, [
11857
- 0,
11858
- 0,
11859
- 0
11860
- ], [
11861
- 1,
11862
- 0,
11863
- 0
11864
- ], [
11865
- 0,
11866
- 1,
11867
- 0
11868
- ]);
11869
- const extrusionDirId = writeDirection(w, [
11870
- 0,
11871
- 0,
11872
- 1
11873
- ]);
11874
- const extrusionId = w.nextId();
11875
- w.writeLine({
11876
- expressID: extrusionId,
11877
- type: web_ifc.IFCEXTRUDEDAREASOLID,
11878
- SweptArea: w.ref(profileId),
11879
- Position: w.ref(extrusionPosId),
11880
- ExtrudedDirection: w.ref(extrusionDirId),
11881
- Depth: w.mkType(web_ifc.IFCPOSITIVELENGTHMEASURE, lengthM)
11882
- });
11883
- const shapeRepId = w.nextId();
11884
- w.writeLine({
11885
- expressID: shapeRepId,
11886
- type: web_ifc.IFCSHAPEREPRESENTATION,
11887
- ContextOfItems: w.ref(geomSubContextId),
11888
- RepresentationIdentifier: w.mkType(web_ifc.IFCLABEL, "Body"),
11889
- RepresentationType: w.mkType(web_ifc.IFCLABEL, "SweptSolid"),
11890
- Items: [w.ref(extrusionId)]
11891
- });
11892
- const productDefinitionShapeId = w.nextId();
11893
- w.writeLine({
11894
- expressID: productDefinitionShapeId,
11895
- type: web_ifc.IFCPRODUCTDEFINITIONSHAPE,
11896
- Name: null,
11897
- Description: null,
11898
- Representations: [w.ref(shapeRepId)]
11899
- });
11900
- return {
11901
- localPlacementId,
11902
- productDefinitionShapeId,
11903
- bodyItemId: extrusionId,
11904
- usedFallback: false
11905
- };
11906
- }
11907
11722
  function writeRailingEntity(w, guid, name, _predefinedType, ownerHistoryId, localPlacementId, productDefinitionShapeId) {
11908
11723
  const id = w.nextId();
11909
11724
  w.writeLine({
@@ -13342,7 +13157,7 @@ function writeAxis2Placement2D(w) {
13342
13157
  });
13343
13158
  return id;
13344
13159
  }
13345
- function writeOpeningGeometry(w, guid, openingSpec, wallSpec, wallPlacementId, geomSubContextId, ownerHistoryId) {
13160
+ function writeOpeningGeometry(w, guid, openingSpec, wallSpec, wallPlacementId, geomSubContextId, ownerHistoryId, representationIdentifier) {
13346
13161
  const widthM = toIfcLengthM(openingSpec.width);
13347
13162
  const heightM = toIfcLengthM(openingSpec.height);
13348
13163
  const offsetAlongWallM = toIfcLengthM(openingSpec.offsetAlongWall);
@@ -13405,7 +13220,7 @@ function writeOpeningGeometry(w, guid, openingSpec, wallSpec, wallPlacementId, g
13405
13220
  expressID: shapeRepId,
13406
13221
  type: web_ifc.IFCSHAPEREPRESENTATION,
13407
13222
  ContextOfItems: w.ref(geomSubContextId),
13408
- RepresentationIdentifier: w.mkType(web_ifc.IFCLABEL, "Body"),
13223
+ RepresentationIdentifier: w.mkType(web_ifc.IFCLABEL, representationIdentifier),
13409
13224
  RepresentationType: w.mkType(web_ifc.IFCLABEL, "SweptSolid"),
13410
13225
  Items: [w.ref(extrusionId)]
13411
13226
  });
@@ -15162,18 +14977,22 @@ function checkGeoreferencing(reader) {
15162
14977
  return [issue("warning", "NO_GEOREFERENCING", "Model contains a facility but no IfcProjectedCRS/IfcGeographicCRS (GRF003) — set ProjectSpec.crs")];
15163
14978
  }
15164
14979
  //#endregion
15165
- //#region src/serialize/exactBodyPreflight.ts
14980
+ //#region src/serialize/productBodyPreflight.ts
15166
14981
  var testItemPreparer = null;
15167
- /** Prepares every exact Body item without writing IFC lines. Source solids remain borrowed. */
15168
- function preflightExactBody(input) {
14982
+ /** Prepares every retained Body item without writing IFC lines. Source solids remain borrowed. */
14983
+ function preflightProductBody(input) {
15169
14984
  const prepareItem = input.prepareItem ?? testItemPreparer ?? prepareTessellation;
14985
+ const failed = (itemIndex, reason, cause) => (0, brepjs.err)(ifcError("BODY_TESSELLATION_FAILED", `Product Body item ${itemIndex} for ${input.localId} could not be tessellated: ${reason}`, cause, {
14986
+ localId: input.localId,
14987
+ itemIndex
14988
+ }));
15170
14989
  const prepareAt = (solid, itemIndex) => {
15171
- const item = prepareItem(solid);
15172
- if (!item.ok) return (0, brepjs.err)(ifcError("EXACT_BODY_TESSELLATION_FAILED", `Exact Product Body item ${itemIndex} for ${input.localId} could not be tessellated: ${item.reason}`, item.cause, {
15173
- localId: input.localId,
15174
- itemIndex
15175
- }));
15176
- return (0, brepjs.ok)(item.value);
14990
+ try {
14991
+ const item = prepareItem(solid);
14992
+ return item.ok ? (0, brepjs.ok)(item.value) : failed(itemIndex, item.reason, item.cause);
14993
+ } catch (cause) {
14994
+ return failed(itemIndex, cause instanceof Error ? cause.message : String(cause), cause);
14995
+ }
15177
14996
  };
15178
14997
  const [firstSolid, ...remainingSolids] = input.solids;
15179
14998
  const first = prepareAt(firstSolid, 0);
@@ -15241,24 +15060,13 @@ function wallVolumeError(cause) {
15241
15060
  function preflightProductBodies(elements) {
15242
15061
  const preflighted = [];
15243
15062
  for (const element of elements) {
15244
- const body = element.geometry;
15245
- if (body.kind === "PARAMETRIC") {
15246
- preflighted.push({
15247
- kind: "PARAMETRIC",
15248
- element,
15249
- solid: body.solid
15250
- });
15251
- continue;
15252
- }
15253
- const prepared = preflightExactBody({
15063
+ const prepared = preflightProductBody({
15254
15064
  localId: element.localId,
15255
- solids: body.solids
15065
+ solids: bodySolids(element.geometry)
15256
15066
  });
15257
15067
  if (!prepared.ok) return (0, brepjs.err)(prepared.error);
15258
15068
  preflighted.push({
15259
- kind: "EXACT",
15260
15069
  element,
15261
- solids: body.solids,
15262
15070
  items: prepared.value
15263
15071
  });
15264
15072
  }
@@ -15270,7 +15078,7 @@ async function toIfc(model, meta) {
15270
15078
  }
15271
15079
  async function serializeIfc(model, meta) {
15272
15080
  try {
15273
- var _usingCtx$2 = _usingCtx();
15081
+ var _usingCtx$3 = _usingCtx();
15274
15082
  const issues = [];
15275
15083
  const project = model.getProject();
15276
15084
  if (!project) return (0, brepjs.err)(ifcError("NO_PROJECT", "BimModel has no project — call model.init() first"));
@@ -15281,7 +15089,7 @@ async function serializeIfc(model, meta) {
15281
15089
  organization: meta.organizationName
15282
15090
  });
15283
15091
  if (!writerResult.ok) return writerResult;
15284
- const w = _usingCtx$2.u(writerResult.value);
15092
+ const w = _usingCtx$3.u(writerResult.value);
15285
15093
  w.setModelScope(project.guid);
15286
15094
  const elements = model.getAllElements();
15287
15095
  const relationships = model.getAllRelationships();
@@ -15393,13 +15201,14 @@ async function serializeIfc(model, meta) {
15393
15201
  spec: wall.spec,
15394
15202
  solids: bodySolids(wall.geometry)
15395
15203
  });
15396
- const { localPlacementId, productDefinitionShapeId } = preflighted.kind === "EXACT" ? writeExactBodyGeometry(w, wall.spec, preflighted.items, geomSubContextId, storeyPlacementId) : writeWallGeometry(w, wall.spec, geomSubContextId, storeyPlacementId);
15204
+ const { localPlacementId, productDefinitionShapeId, bodyItemIds } = writeProductBodyGeometry(w, wall.spec, preflighted.items, geomSubContextId, storeyPlacementId);
15397
15205
  const wallExpressId = writeWallEntity(w, wall.guid, `Wall ${i + 1}`, ownerHistoryId, localPlacementId, productDefinitionShapeId);
15398
15206
  idMap.set(wall.localId, wallExpressId);
15399
15207
  placementMap.set(wall.localId, localPlacementId);
15400
15208
  writeWallCommonPset(w, ownerHistoryId, wallExpressId, wall.spec);
15401
15209
  writeManufacturerPset(w, ownerHistoryId, wallExpressId, wall.spec);
15402
15210
  if (wall.spec.customProperties !== void 0) writeCustomPsets(w, ownerHistoryId, wallExpressId, wall.spec.customProperties);
15211
+ for (const itemId of bodyItemIds) applySurfaceStyle(w, model, wall.localId, itemId);
15403
15212
  if (!bodyQuantities.ok) issues.push(issue("warning", "WALL_QUANTITY_OMITTED", "Wall quantities omitted because occupied material volume could not be measured", wall.localId, {
15404
15213
  guid: wall.guid,
15405
15214
  cause: bodyQuantities.error
@@ -15549,27 +15358,14 @@ async function serializeIfc(model, meta) {
15549
15358
  const railing = preflighted.element;
15550
15359
  const containingId = findContainerOf(railing.localId, relationships);
15551
15360
  const storeyPlacementId = containingId !== null ? placementMap.get(containingId) ?? null : null;
15552
- let representation;
15553
- let exactBodyItemIds = [];
15554
- if (preflighted.kind === "EXACT") {
15555
- const exact = writeExactBodyGeometry(w, railing.spec, preflighted.items, geomSubContextId, storeyPlacementId);
15556
- exactBodyItemIds = exact.bodyItemIds;
15557
- representation = {
15558
- ...exact,
15559
- bodyItemId: null,
15560
- usedFallback: false
15561
- };
15562
- } else representation = writeRailingGeometry(w, railing.spec, preflighted.solid, geomSubContextId, storeyPlacementId);
15563
- const { localPlacementId, productDefinitionShapeId, bodyItemId, usedFallback } = representation;
15564
- if (usedFallback) console.warn(`Railing ${i + 1} tessellation failed; IFC body is a degenerate fallback.`);
15361
+ const { localPlacementId, productDefinitionShapeId, bodyItemIds } = writeProductBodyGeometry(w, railing.spec, preflighted.items, geomSubContextId, storeyPlacementId);
15565
15362
  const railingExpressId = writeRailingEntity(w, railing.guid, `Railing ${i + 1}`, railing.spec.predefinedType ?? "NOTDEFINED", ownerHistoryId, localPlacementId, productDefinitionShapeId);
15566
15363
  idMap.set(railing.localId, railingExpressId);
15567
15364
  placementMap.set(railing.localId, localPlacementId);
15568
15365
  writeRailingCommonPset(w, ownerHistoryId, railingExpressId, railing.spec);
15569
15366
  writeManufacturerPset(w, ownerHistoryId, railingExpressId, railing.spec);
15570
15367
  if (railing.spec.customProperties !== void 0) writeCustomPsets(w, ownerHistoryId, railingExpressId, railing.spec.customProperties);
15571
- if (preflighted.kind === "EXACT") for (const itemId of exactBodyItemIds) applySurfaceStyle(w, model, railing.localId, itemId);
15572
- else if (bodyItemId !== null) applySurfaceStyle(w, model, railing.localId, bodyItemId);
15368
+ for (const itemId of bodyItemIds) applySurfaceStyle(w, model, railing.localId, itemId);
15573
15369
  }
15574
15370
  for (const [i, covering] of coverings.entries()) {
15575
15371
  const containingId = findContainerOf(covering.localId, relationships);
@@ -15585,6 +15381,7 @@ async function serializeIfc(model, meta) {
15585
15381
  }
15586
15382
  const openingPlacementMap = /* @__PURE__ */ new Map();
15587
15383
  const openingEntityMap = /* @__PURE__ */ new Map();
15384
+ let referenceContextId;
15588
15385
  for (const rel of relationships) {
15589
15386
  if (rel.kind !== "VOIDS_WALL") continue;
15590
15387
  const wallElement = elements.find((el) => el.category === "WALL" && el.localId === rel.wallLocalId);
@@ -15595,7 +15392,8 @@ async function serializeIfc(model, meta) {
15595
15392
  const openingElement = elements.find((el) => el.localId === rel.openingLocalId);
15596
15393
  if (openingElement === void 0 || openingElement.category !== "OPENING") continue;
15597
15394
  if (!isWallOpening(openingElement.spec)) continue;
15598
- const { openingEntityId, openingPlacementId } = writeOpeningGeometry(w, openingElement.guid, openingElement.spec, wallElement.spec, wallPlacementId, geomSubContextId, ownerHistoryId);
15395
+ referenceContextId ??= writeGeometricSubContext(w, geomContextId, "Reference");
15396
+ const { openingEntityId, openingPlacementId } = writeOpeningGeometry(w, openingElement.guid, openingElement.spec, wallElement.spec, wallPlacementId, referenceContextId, ownerHistoryId, "Reference");
15599
15397
  idMap.set(rel.openingLocalId, openingEntityId);
15600
15398
  openingPlacementMap.set(rel.openingLocalId, openingPlacementId);
15601
15399
  openingEntityMap.set(rel.openingLocalId, openingEntityId);
@@ -15755,9 +15553,9 @@ async function serializeIfc(model, meta) {
15755
15553
  issues
15756
15554
  }) : saved;
15757
15555
  } catch (_) {
15758
- _usingCtx$2.e = _;
15556
+ _usingCtx$3.e = _;
15759
15557
  } finally {
15760
- _usingCtx$2.d();
15558
+ _usingCtx$3.d();
15761
15559
  }
15762
15560
  }
15763
15561
  /**
@@ -16475,13 +16273,13 @@ function reconstructExtrusion(reader, extrusionId, scale, worldTransform, diagno
16475
16273
  ];
16476
16274
  const solidResult = (() => {
16477
16275
  try {
16478
- var _usingCtx$1 = _usingCtx();
16479
- const face = _usingCtx$1.u(faceResult.value);
16276
+ var _usingCtx$2 = _usingCtx();
16277
+ const face = _usingCtx$2.u(faceResult.value);
16480
16278
  return (0, brepjs.extrude)(face, extrudeVec);
16481
16279
  } catch (_) {
16482
- _usingCtx$1.e = _;
16280
+ _usingCtx$2.e = _;
16483
16281
  } finally {
16484
- _usingCtx$1.d();
16282
+ _usingCtx$2.d();
16485
16283
  }
16486
16284
  })();
16487
16285
  if (!solidResult.ok) {
@@ -17203,6 +17001,161 @@ function errMsg$1(e) {
17203
17001
  return e instanceof Error ? e.message : String(e);
17204
17002
  }
17205
17003
  //#endregion
17004
+ //#region src/geometryCleanupDiagnostics.ts
17005
+ var diagnosticSchema = object({
17006
+ operation: string(),
17007
+ itemIndex: number().int().nonnegative(),
17008
+ resourceKind: _enum(["SHAPE", "TRANSFORM"]),
17009
+ localId: number().optional(),
17010
+ cause: unknown()
17011
+ });
17012
+ var failedCleanup = object({
17013
+ kind: literal("FAILED"),
17014
+ diagnostics: array(unknown()).nonempty()
17015
+ });
17016
+ var errorMetadata = object({ cleanup: unknown().optional() });
17017
+ var cleanupAttempt = Symbol("geometryCleanupAttempt");
17018
+ var bodyOperation = _enum(["productBodyBounds", "measureProductBodyMaterial"]);
17019
+ function attemptIdentity(source) {
17020
+ const identity = cleanupAttempt in source ? source[cleanupAttempt] : source;
17021
+ return typeof identity === "object" && identity !== null ? identity : source;
17022
+ }
17023
+ function errorChildren(value) {
17024
+ const children = [];
17025
+ if (value instanceof AggregateError) {
17026
+ const errors = value.errors;
17027
+ children.push(...errors);
17028
+ }
17029
+ if ("cause" in value) children.push(value.cause);
17030
+ if ("error" in value && "suppressed" in value) children.push(value.error, value.suppressed);
17031
+ return children;
17032
+ }
17033
+ /** Keep the actual failed-attempt markers; their causes need not be unique. */
17034
+ function nativeCleanupAttempts(error) {
17035
+ const attempts = [];
17036
+ const seen = /* @__PURE__ */ new Set();
17037
+ const visit = (value) => {
17038
+ if (typeof value !== "object" || value === null || seen.has(value)) return;
17039
+ seen.add(value);
17040
+ if (value instanceof brepjs.GeometryCleanupError) attempts.push(value);
17041
+ else errorChildren(value).forEach(visit);
17042
+ };
17043
+ visit(error);
17044
+ return attempts;
17045
+ }
17046
+ /** Read prior owner attempts without retrying releases or merging distinct failures. */
17047
+ function reportedGeometryCleanup(error, operation, context = {}) {
17048
+ const diagnostics = [];
17049
+ const seenErrors = /* @__PURE__ */ new Set();
17050
+ const seenAttempts = /* @__PURE__ */ new Set();
17051
+ const add = (diagnostic, identity) => {
17052
+ if (seenAttempts.has(identity)) return;
17053
+ seenAttempts.add(identity);
17054
+ diagnostics.push(Object.freeze({
17055
+ operation: diagnostic.operation,
17056
+ itemIndex: diagnostic.itemIndex,
17057
+ resourceKind: diagnostic.resourceKind,
17058
+ cause: diagnostic.cause,
17059
+ ...diagnostic.localId === void 0 ? {} : { localId: diagnostic.localId },
17060
+ [cleanupAttempt]: identity
17061
+ }));
17062
+ };
17063
+ const visit = (value) => {
17064
+ if (typeof value !== "object" || value === null || seenErrors.has(value)) return;
17065
+ seenErrors.add(value);
17066
+ const metadata = errorMetadata.safeParse("metadata" in value ? value.metadata : void 0);
17067
+ const reports = [metadata.success ? metadata.data.cleanup : void 0, "cleanup" in value ? value.cleanup : void 0];
17068
+ const nativeAttempts = bodyOperation.safeParse("operation" in value ? value.operation : void 0).success ? nativeCleanupAttempts(value) : [];
17069
+ const matched = /* @__PURE__ */ new Set();
17070
+ const seenEntries = /* @__PURE__ */ new Set();
17071
+ for (const report of reports) {
17072
+ const parsed = failedCleanup.safeParse(report);
17073
+ if (!parsed.success) continue;
17074
+ for (const source of parsed.data.diagnostics) {
17075
+ if (typeof source !== "object" || source === null || seenEntries.has(source)) continue;
17076
+ const diagnostic = diagnosticSchema.safeParse(source);
17077
+ if (!diagnostic.success) continue;
17078
+ seenEntries.add(source);
17079
+ let identity = attemptIdentity(source);
17080
+ if (identity === source) {
17081
+ const native = nativeAttempts.find((attempt) => !matched.has(attempt) && attempt.resourceKind === diagnostic.data.resourceKind && attempt.cause === diagnostic.data.cause);
17082
+ if (native !== void 0) identity = native;
17083
+ }
17084
+ if (identity instanceof brepjs.GeometryCleanupError) matched.add(identity);
17085
+ add(diagnostic.data, identity);
17086
+ }
17087
+ }
17088
+ if (value instanceof brepjs.GeometryCleanupError) add({
17089
+ operation,
17090
+ itemIndex: context.itemIndex ?? 0,
17091
+ resourceKind: value.resourceKind,
17092
+ cause: value.cause
17093
+ }, value);
17094
+ else errorChildren(value).forEach(visit);
17095
+ };
17096
+ visit(error);
17097
+ return diagnostics;
17098
+ }
17099
+ //#endregion
17100
+ //#region src/import/cutImportedSolids.ts
17101
+ /** Borrow World-placed inputs; own every disconnected result independently of the cut's cache. */
17102
+ function cutImportedSolids(host, opening) {
17103
+ const context = { operation: "cutImportedSolids" };
17104
+ const temporaries = [];
17105
+ const outputs = [];
17106
+ const build = () => {
17107
+ const cutResult = (0, brepjs.cut)(host, opening);
17108
+ if (!cutResult.ok) return (0, brepjs.err)(fromBrepError(cutResult.error, "VOID_CUT_FAILED", "Opening cut failed"));
17109
+ temporaries.push({
17110
+ resource: cutResult.value,
17111
+ itemIndex: 0
17112
+ });
17113
+ const children = (0, brepjs.getKernel)().iterShapes(cutResult.value.wrapped, "solid").map(brepjs.createSolid);
17114
+ temporaries.push(...children.map((resource, itemIndex) => ({
17115
+ resource,
17116
+ itemIndex
17117
+ })));
17118
+ for (const solid of children) {
17119
+ const copied = (0, brepjs.clone)(solid);
17120
+ if (!copied.ok) return (0, brepjs.err)(fromBrepError(copied.error, "VOID_COPY_FAILED", "Could not retain opening result"));
17121
+ const candidate = copied.value;
17122
+ temporaries.push({
17123
+ resource: candidate,
17124
+ itemIndex: temporaries.length
17125
+ });
17126
+ const validated = (0, brepjs.validSolid)(candidate);
17127
+ if (!validated.ok) return (0, brepjs.err)(importError("VOID_INVALID_SOLID", "Opening result is not valid", validated.error));
17128
+ outputs.push(validated.value);
17129
+ }
17130
+ return (0, brepjs.ok)(outputs);
17131
+ };
17132
+ let result;
17133
+ try {
17134
+ result = build();
17135
+ } catch (cause) {
17136
+ result = (0, brepjs.err)(importError("VOID_CUT_FAILED", "Opening cut failed", cause));
17137
+ }
17138
+ const survivors = new Set(result.ok ? outputs : []);
17139
+ const cleanup = cleanupOwnedResources(temporaries.filter(({ resource }) => !survivors.has(resource)).reverse(), context);
17140
+ const diagnostics = [...result.ok ? [] : reportedGeometryCleanup(result.error, context.operation), ...cleanup.kind === "FAILED" ? cleanup.diagnostics : []];
17141
+ if (result.ok && diagnostics.length === 0) return result;
17142
+ if (result.ok) {
17143
+ const outputCleanup = cleanupOwnedResources(outputs.map((resource, itemIndex) => ({
17144
+ resource,
17145
+ itemIndex
17146
+ })), context);
17147
+ if (outputCleanup.kind === "FAILED") diagnostics.push(...outputCleanup.diagnostics);
17148
+ }
17149
+ const failure = result.ok ? importError("VOID_CLEANUP_FAILED", "Opening result cleanup failed") : result.error;
17150
+ return (0, brepjs.err)({
17151
+ ...failure,
17152
+ metadata: {
17153
+ ...failure.metadata,
17154
+ cleanup: cleanupReport(diagnostics)
17155
+ }
17156
+ });
17157
+ }
17158
+ //#endregion
17206
17159
  //#region src/import/dataRead.ts
17207
17160
  /** Narrows an unknown line attribute to the `{ type, value }` wrapper shape. */
17208
17161
  function asTyped(v) {
@@ -17558,7 +17511,7 @@ var ELEMENT_TYPES = [
17558
17511
  * fatal failures — bad bytes, unsupported schema, WASM open failure — return
17559
17512
  * `err`. Inspect {@link ImportedModel.diagnostics} for per-element quality.
17560
17513
  *
17561
- * The web-ifc model handle is always closed in a `finally` block.
17514
+ * The web-ifc model handle is closed before handing reconstructed owners to the caller.
17562
17515
  */
17563
17516
  async function fromIfc(bytes, options = {}) {
17564
17517
  const settings = { coordinateToOrigin: options.coordinateToOrigin ?? false };
@@ -17566,6 +17519,7 @@ async function fromIfc(bytes, options = {}) {
17566
17519
  if (!readerResult.ok) return (0, brepjs.err)(readerResult.error);
17567
17520
  const reader = readerResult.value;
17568
17521
  const elements = [];
17522
+ let result;
17569
17523
  try {
17570
17524
  reader.buildGuidMap();
17571
17525
  const diagnostics = [];
@@ -17592,13 +17546,23 @@ async function fromIfc(bytes, options = {}) {
17592
17546
  diagnostics: report,
17593
17547
  ...applicationName !== void 0 ? { applicationName } : {}
17594
17548
  };
17595
- return (0, brepjs.ok)(model);
17549
+ result = (0, brepjs.ok)(model);
17596
17550
  } catch (e) {
17597
- disposeElements(elements);
17598
- return (0, brepjs.err)(importError("IMPORT_FAILED", "Unexpected failure during IFC import", e));
17599
- } finally {
17551
+ result = (0, brepjs.err)(importError("IMPORT_FAILED", "Unexpected failure during IFC import", e));
17552
+ }
17553
+ try {
17600
17554
  reader.close();
17555
+ } catch (cause) {
17556
+ result = (0, brepjs.err)(result.ok ? importError("IMPORT_CLOSE_FAILED", "Failed to close IFC reader", cause) : {
17557
+ ...result.error,
17558
+ metadata: {
17559
+ ...result.error.metadata,
17560
+ readerCloseCause: cause
17561
+ }
17562
+ });
17601
17563
  }
17564
+ if (!result.ok) return (0, brepjs.err)(withCleanup(result.error, disposeElements(elements), "fromIfc"));
17565
+ return result;
17602
17566
  }
17603
17567
  /**
17604
17568
  * Reads one product into an {@link ImportedElement}. Returns null only when the
@@ -17649,8 +17613,8 @@ function readElement(reader, expressId, category, scale, containment, typeEnums,
17649
17613
  ...fills !== void 0 ? { fills } : {}
17650
17614
  };
17651
17615
  } catch (e) {
17652
- if (geometry !== null) disposeGeometry(geometry);
17653
- diagnostics.push(issue("error", "ELEMENT_READ_FAILED", `Element ${expressId} reconstruction threw: ${errMsg(e)}`, expressId));
17616
+ const failure = geometry === null ? e : withCleanup(importError("ELEMENT_READ_FAILED", "Element read failed", e), disposeGeometry(geometry), "readElement");
17617
+ diagnostics.push(issue("error", "ELEMENT_READ_FAILED", `Element ${expressId} reconstruction threw: ${errMsg(e)}`, expressId, { cause: failure }));
17654
17618
  return null;
17655
17619
  }
17656
17620
  }
@@ -17666,36 +17630,79 @@ function findFills(reader, elementExpressId) {
17666
17630
  }
17667
17631
  /**
17668
17632
  * Reconstructs a host element's body geometry and, per IFC `IfcRelVoidsElement`
17669
- * semantics, subtracts the reconstructed solid of every opening that voids it —
17633
+ * semantics, subtracts each opening's Body representation from a parametric host —
17670
17634
  * so a wall with a door hole comes back as the cut solid, matching the source
17671
17635
  * model. Falls back to the uncut solid (with a diagnostic) if a cut fails.
17672
17636
  */
17673
17637
  function reconstructGeometry(reader, expressId, scale, voidedBy, diagnostics) {
17674
17638
  const base = toImportedGeometry(reader, expressId, scale, diagnostics);
17675
17639
  if (base.fidelity !== "PARAMETRIC" || base.completeness !== "COMPLETE" || voidedBy.length === 0) return base;
17676
- const firstHost = base.solids[0];
17677
- if (firstHost === void 0) return base;
17678
- const hosts = [firstHost, ...base.solids.slice(1)];
17679
- for (const openingId of voidedBy) {
17680
- const opening = readBodyGeometry(reader, openingId, scale, diagnostics);
17681
- if (opening.kind !== "SOLID") continue;
17682
- try {
17683
- for (let hostIndex = 0; hostIndex < hosts.length; hostIndex++) {
17640
+ if (base.solids[0] === void 0) return base;
17641
+ const hosts = [...base.solids];
17642
+ try {
17643
+ for (const openingId of voidedBy) try {
17644
+ var _usingCtx$1 = _usingCtx();
17645
+ const opening = readBodyGeometry(reader, openingId, scale, diagnostics);
17646
+ if (opening.kind !== "SOLID") continue;
17647
+ const openingOwner = _usingCtx$1.u(ownOpening(opening.solid, openingId));
17648
+ for (let hostIndex = 0; hostIndex < hosts.length;) {
17684
17649
  const host = hosts[hostIndex];
17685
- if (host === void 0) continue;
17686
- const cutResult = (0, brepjs.cut)(host, opening.solid);
17650
+ if (host === void 0) break;
17651
+ const cutResult = cutImportedSolids(host, openingOwner.solid);
17687
17652
  if (!cutResult.ok) {
17688
- diagnostics.push(issue("warning", "VOID_SUBTRACTION_FAILED", `Opening ${openingId} could not be subtracted from element ${expressId}: ${cutResult.error.message}`, expressId));
17653
+ diagnostics.push(issue("warning", "VOID_SUBTRACTION_FAILED", `Opening ${openingId} could not be subtracted from element ${expressId}: ${cutResult.error.message}`, expressId, { cause: cutResult.error }));
17654
+ hostIndex++;
17689
17655
  continue;
17690
17656
  }
17691
- host[Symbol.dispose]();
17692
- hosts[hostIndex] = cutResult.value;
17657
+ hosts.splice(hostIndex, 1, ...cutResult.value);
17658
+ const cleanup = cleanupOwnedResources([{
17659
+ resource: host,
17660
+ itemIndex: hostIndex
17661
+ }], { operation: "importOpening" });
17662
+ if (cleanup.kind === "FAILED") diagnostics.push(issue("warning", "VOID_HOST_CLEANUP_FAILED", "Opening result retained, but previous host release failed", expressId, { cleanup }));
17663
+ hostIndex += cutResult.value.length;
17693
17664
  }
17665
+ } catch (_) {
17666
+ _usingCtx$1.e = _;
17694
17667
  } finally {
17695
- opening.solid[Symbol.dispose]();
17668
+ _usingCtx$1.d();
17696
17669
  }
17670
+ return completeImportedGeometry("PARAMETRIC", hosts, expressId, diagnostics);
17671
+ } catch (cause) {
17672
+ const failure = importError("GEOMETRY_RECONSTRUCTION_FAILED", "Opening reconstruction failed", cause);
17673
+ const cleanup = cleanupOwnedResources(hosts.map((resource, itemIndex) => ({
17674
+ resource,
17675
+ itemIndex
17676
+ })), { operation: "reconstructGeometry" });
17677
+ throw Object.assign(new Error(failure.message, { cause }), {
17678
+ code: failure.code,
17679
+ metadata: withCleanup(failure, cleanup, "reconstructGeometry").metadata
17680
+ });
17697
17681
  }
17698
- return completeImportedGeometry("PARAMETRIC", hosts, expressId, diagnostics);
17682
+ }
17683
+ function ownOpening(solid, openingId) {
17684
+ return {
17685
+ solid,
17686
+ [Symbol.dispose]() {
17687
+ const cleanup = cleanupOwnedResources([{
17688
+ resource: solid,
17689
+ itemIndex: 0
17690
+ }], { operation: "importOpeningTool" });
17691
+ if (cleanup.kind === "FAILED") throw new Error(`Opening ${openingId} cleanup failed`, { cause: {
17692
+ ...importError("OPENING_CLEANUP_FAILED", "Temporary opening release failed"),
17693
+ metadata: { cleanup }
17694
+ } });
17695
+ }
17696
+ };
17697
+ }
17698
+ function withCleanup(error, cleanup, operation) {
17699
+ return {
17700
+ ...error,
17701
+ metadata: {
17702
+ ...error.metadata,
17703
+ cleanup: cleanupReport([...reportedGeometryCleanup(error, operation), ...cleanup.kind === "FAILED" ? cleanup.diagnostics : []])
17704
+ }
17705
+ };
17699
17706
  }
17700
17707
  function toImportedGeometry(reader, expressId, scale, diagnostics) {
17701
17708
  const body = readBodyItems(reader, expressId, scale, diagnostics);
@@ -17743,61 +17750,48 @@ function completeImportedGeometry(fidelity, solids, expressId, diagnostics) {
17743
17750
  fidelity,
17744
17751
  completeness: "COMPLETE",
17745
17752
  solids,
17746
- solid: solids.length === 1 ? solids[0] : null,
17753
+ solid: solids.length === 1 ? solids[0] ?? null : null,
17747
17754
  ...measureCompleteBody(solids, expressId, diagnostics)
17748
17755
  };
17749
17756
  }
17750
17757
  function measureCompleteBody(solids, expressId, diagnostics) {
17758
+ const unavailable = (cause, message = errMsg(cause)) => {
17759
+ diagnostics.push(issue("warning", "BODY_AGGREGATE_MEASUREMENT_FAILED", `Complete Body aggregate measurement failed: ${message}`, expressId, { cause }));
17760
+ return {
17761
+ bounds: null,
17762
+ volumeMm3: null
17763
+ };
17764
+ };
17751
17765
  try {
17752
- const first = solids[0];
17766
+ const [first, ...rest] = solids;
17753
17767
  if (first === void 0) return {
17754
17768
  bounds: null,
17755
17769
  volumeMm3: null
17756
17770
  };
17757
- const initial = (0, brepjs.getBounds)(first);
17758
- let xMin = initial.xMin;
17759
- let xMax = initial.xMax;
17760
- let yMin = initial.yMin;
17761
- let yMax = initial.yMax;
17762
- let zMin = initial.zMin;
17763
- let zMax = initial.zMax;
17764
- let volumeMm3 = 0;
17765
- for (const solid of solids) {
17766
- const measured = (0, brepjs.measureVolume)(solid);
17767
- if (!measured.ok) throw new Error(measured.error.message);
17768
- volumeMm3 += measured.value;
17769
- const itemBounds = (0, brepjs.getBounds)(solid);
17770
- xMin = Math.min(xMin, itemBounds.xMin);
17771
- xMax = Math.max(xMax, itemBounds.xMax);
17772
- yMin = Math.min(yMin, itemBounds.yMin);
17773
- yMax = Math.max(yMax, itemBounds.yMax);
17774
- zMin = Math.min(zMin, itemBounds.zMin);
17775
- zMax = Math.max(zMax, itemBounds.zMax);
17776
- }
17771
+ const items = [first, ...rest];
17772
+ const bounds = productBodyBounds(items);
17773
+ if (!bounds.ok) return unavailable(bounds.error, bounds.error.message);
17774
+ const volume = measureProductBodyMaterial(items);
17775
+ if (!volume.ok) return unavailable(volume.error, volume.error.message);
17777
17776
  return {
17778
- bounds: {
17779
- xMin,
17780
- xMax,
17781
- yMin,
17782
- yMax,
17783
- zMin,
17784
- zMax
17785
- },
17786
- volumeMm3
17777
+ bounds: bounds.value.bounds,
17778
+ volumeMm3: volume.value
17787
17779
  };
17788
17780
  } catch (cause) {
17789
- diagnostics.push(issue("warning", "BODY_AGGREGATE_MEASUREMENT_FAILED", `Complete Body aggregate measurement failed: ${errMsg(cause)}`, expressId));
17790
- return {
17791
- bounds: null,
17792
- volumeMm3: null
17793
- };
17781
+ return unavailable(cause);
17794
17782
  }
17795
17783
  }
17796
17784
  function disposeGeometry(geometry) {
17797
- for (const solid of geometry.solids) solid[Symbol.dispose]();
17785
+ return cleanupOwnedResources(geometry.solids.map((resource, itemIndex) => ({
17786
+ resource,
17787
+ itemIndex
17788
+ })), { operation: "disposeImportedGeometry" });
17798
17789
  }
17799
17790
  function disposeElements(elements) {
17800
- for (const element of elements) disposeGeometry(element.geometry);
17791
+ return cleanupOwnedResources(elements.flatMap(({ geometry }) => geometry.solids).map((resource, itemIndex) => ({
17792
+ resource,
17793
+ itemIndex
17794
+ })), { operation: "disposeImportedElements" });
17801
17795
  }
17802
17796
  function toImportedPset(pset) {
17803
17797
  return {