brepjs-bim 0.3.1 → 0.5.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.
@@ -16,7 +16,7 @@ var __copyProps = (to, from, except, desc) => {
16
16
  }
17
17
  return to;
18
18
  };
19
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
20
20
  value: mod,
21
21
  enumerable: true
22
22
  }) : target, mod));
@@ -27,7 +27,7 @@ web_ifc = __toESM(web_ifc, 1);
27
27
  //#region src/identity/ifcGuid.ts
28
28
  var IFC_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_$";
29
29
  function newIfcGuid() {
30
- const bytes = crypto.getRandomValues(new Uint8Array(16));
30
+ const bytes = crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(16));
31
31
  bytes[6] = (bytes[6] ?? 0) & 15 | 64;
32
32
  bytes[8] = (bytes[8] ?? 0) & 63 | 128;
33
33
  return encodeIfcGuid(bytes);
@@ -69,7 +69,7 @@ function fnv1a(text) {
69
69
  }
70
70
  function digest16(stableKey) {
71
71
  const seed = `${NAMESPACE}::${stableKey}`;
72
- const bytes = new Uint8Array(16);
72
+ const bytes = /* @__PURE__ */ new Uint8Array(16);
73
73
  for (let block = 0; block < 4; block++) {
74
74
  const h = fnv1a(`b${block}:${seed}`);
75
75
  bytes[block * 4 + 0] = h >>> 24 & 255;
@@ -206,7 +206,7 @@ function fromBrepError(inner, code, message) {
206
206
  };
207
207
  }
208
208
  //#endregion
209
- //#region \0@oxc-project+runtime@0.133.0/helpers/esm/usingCtx.js
209
+ //#region \0@oxc-project+runtime@0.143.0/helpers/esm/usingCtx.js
210
210
  function _usingCtx() {
211
211
  var r = "function" == typeof SuppressedError ? SuppressedError : function(r, e) {
212
212
  var n = Error();
@@ -293,7 +293,8 @@ function wallToSolid(spec) {
293
293
  ]
294
294
  ]);
295
295
  if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "WALL_PROFILE_FAILED", "Failed to create wall profile"));
296
- const solidResult = (0, brepjs.extrude)(_usingCtx$19.u(profileResult.value), [
296
+ const profile = _usingCtx$19.u(profileResult.value);
297
+ const solidResult = (0, brepjs.extrude)(profile, [
297
298
  length,
298
299
  0,
299
300
  0
@@ -343,7 +344,8 @@ function slabToSolid(spec) {
343
344
  ]
344
345
  ]);
345
346
  if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "SLAB_PROFILE_FAILED", "Failed to create slab profile"));
346
- const solidResult = (0, brepjs.extrude)(_usingCtx$18.u(profileResult.value), [
347
+ const profile = _usingCtx$18.u(profileResult.value);
348
+ const solidResult = (0, brepjs.extrude)(profile, [
347
349
  0,
348
350
  0,
349
351
  thickness
@@ -437,7 +439,6 @@ function cached(getter) {
437
439
  Object.defineProperty(this, "value", { value });
438
440
  return value;
439
441
  }
440
- throw new Error("cached value already set");
441
442
  } };
442
443
  }
443
444
  function nullish(input) {
@@ -483,7 +484,10 @@ function assignProp(target, prop, value) {
483
484
  }
484
485
  function mergeDefs(...defs) {
485
486
  const mergedDescriptors = {};
486
- for (const def of defs) Object.assign(mergedDescriptors, Object.getOwnPropertyDescriptors(def));
487
+ for (const def of defs) {
488
+ const descriptors = Object.getOwnPropertyDescriptors(def);
489
+ Object.assign(mergedDescriptors, descriptors);
490
+ }
487
491
  return Object.defineProperties({}, mergedDescriptors);
488
492
  }
489
493
  function esc(str) {
@@ -805,7 +809,7 @@ var _parse = (_Err) => (schema, value, _ctx, _params) => {
805
809
  }, ctx);
806
810
  if (result instanceof Promise) throw new $ZodAsyncError();
807
811
  if (result.issues.length) {
808
- const e = new (_params?.Err ?? _Err)(result.issues.map((iss) => finalizeIssue(iss, ctx, config())));
812
+ const e = new ((_params?.Err) ?? _Err)(result.issues.map((iss) => finalizeIssue(iss, ctx, config())));
809
813
  captureStackTrace(e, _params?.callee);
810
814
  throw e;
811
815
  }
@@ -822,7 +826,7 @@ var _parseAsync = (_Err) => async (schema, value, _ctx, params) => {
822
826
  }, ctx);
823
827
  if (result instanceof Promise) result = await result;
824
828
  if (result.issues.length) {
825
- const e = new (params?.Err ?? _Err)(result.issues.map((iss) => finalizeIssue(iss, ctx, config())));
829
+ const e = new ((params?.Err) ?? _Err)(result.issues.map((iss) => finalizeIssue(iss, ctx, config())));
826
830
  captureStackTrace(e, params?.callee);
827
831
  throw e;
828
832
  }
@@ -989,8 +993,10 @@ var $ZodCheckLessThan = /*@__PURE__*/ $constructor("$ZodCheckLessThan", (inst, d
989
993
  inst._zod.onattach.push((inst) => {
990
994
  const bag = inst._zod.bag;
991
995
  const curr = (def.inclusive ? bag.maximum : bag.exclusiveMaximum) ?? Number.POSITIVE_INFINITY;
992
- if (def.value < curr) if (def.inclusive) bag.maximum = def.value;
993
- else bag.exclusiveMaximum = def.value;
996
+ if (def.value < curr) {
997
+ if (def.inclusive) bag.maximum = def.value;
998
+ else bag.exclusiveMaximum = def.value;
999
+ }
994
1000
  });
995
1001
  inst._zod.check = (payload) => {
996
1002
  if (def.inclusive ? payload.value <= def.value : payload.value < def.value) return;
@@ -1011,8 +1017,10 @@ var $ZodCheckGreaterThan = /*@__PURE__*/ $constructor("$ZodCheckGreaterThan", (i
1011
1017
  inst._zod.onattach.push((inst) => {
1012
1018
  const bag = inst._zod.bag;
1013
1019
  const curr = (def.inclusive ? bag.minimum : bag.exclusiveMinimum) ?? Number.NEGATIVE_INFINITY;
1014
- if (def.value > curr) if (def.inclusive) bag.minimum = def.value;
1015
- else bag.exclusiveMinimum = def.value;
1020
+ if (def.value > curr) {
1021
+ if (def.inclusive) bag.minimum = def.value;
1022
+ else bag.exclusiveMinimum = def.value;
1023
+ }
1016
1024
  });
1017
1025
  inst._zod.check = (payload) => {
1018
1026
  if (def.inclusive ? payload.value >= def.value : payload.value > def.value) return;
@@ -2545,7 +2553,7 @@ var $ZodOptional = /*@__PURE__*/ $constructor("$ZodOptional", (inst, def) => {
2545
2553
  inst._zod.optin = "optional";
2546
2554
  inst._zod.optout = "optional";
2547
2555
  defineLazy(inst._zod, "values", () => {
2548
- return def.innerType._zod.values ? new Set([...def.innerType._zod.values, void 0]) : void 0;
2556
+ return def.innerType._zod.values ? /* @__PURE__ */ new Set([...def.innerType._zod.values, void 0]) : void 0;
2549
2557
  });
2550
2558
  defineLazy(inst._zod, "pattern", () => {
2551
2559
  const pattern = def.innerType._zod.pattern;
@@ -2579,7 +2587,7 @@ var $ZodNullable = /*@__PURE__*/ $constructor("$ZodNullable", (inst, def) => {
2579
2587
  return pattern ? new RegExp(`^(${cleanRegex(pattern.source)}|null)$`) : void 0;
2580
2588
  });
2581
2589
  defineLazy(inst._zod, "values", () => {
2582
- return def.innerType._zod.values ? new Set([...def.innerType._zod.values, null]) : void 0;
2590
+ return def.innerType._zod.values ? /* @__PURE__ */ new Set([...def.innerType._zod.values, null]) : void 0;
2583
2591
  });
2584
2592
  inst._zod.parse = (payload, ctx) => {
2585
2593
  if (payload.value === null) return payload;
@@ -3513,8 +3521,10 @@ function finalize$1(ctx, schema) {
3513
3521
  defs[seen.defId] = seen.def;
3514
3522
  }
3515
3523
  }
3516
- if (ctx.external) {} else if (Object.keys(defs).length > 0) if (ctx.target === "draft-2020-12") result.$defs = defs;
3517
- else result.definitions = defs;
3524
+ if (ctx.external) {} else if (Object.keys(defs).length > 0) {
3525
+ if (ctx.target === "draft-2020-12") result.$defs = defs;
3526
+ else result.definitions = defs;
3527
+ }
3518
3528
  try {
3519
3529
  const finalized = JSON.parse(JSON.stringify(result));
3520
3530
  Object.defineProperty(finalized, "~standard", {
@@ -3627,16 +3637,18 @@ var numberProcessor = (schema, ctx, _json, _params) => {
3627
3637
  const exMin = typeof exclusiveMinimum === "number" && exclusiveMinimum >= (minimum ?? Number.NEGATIVE_INFINITY);
3628
3638
  const exMax = typeof exclusiveMaximum === "number" && exclusiveMaximum <= (maximum ?? Number.POSITIVE_INFINITY);
3629
3639
  const legacy = ctx.target === "draft-04" || ctx.target === "openapi-3.0";
3630
- if (exMin) if (legacy) {
3631
- json.minimum = exclusiveMinimum;
3632
- json.exclusiveMinimum = true;
3633
- } else json.exclusiveMinimum = exclusiveMinimum;
3634
- else if (typeof minimum === "number") json.minimum = minimum;
3635
- if (exMax) if (legacy) {
3636
- json.maximum = exclusiveMaximum;
3637
- json.exclusiveMaximum = true;
3638
- } else json.exclusiveMaximum = exclusiveMaximum;
3639
- else if (typeof maximum === "number") json.maximum = maximum;
3640
+ if (exMin) {
3641
+ if (legacy) {
3642
+ json.minimum = exclusiveMinimum;
3643
+ json.exclusiveMinimum = true;
3644
+ } else json.exclusiveMinimum = exclusiveMinimum;
3645
+ } else if (typeof minimum === "number") json.minimum = minimum;
3646
+ if (exMax) {
3647
+ if (legacy) {
3648
+ json.maximum = exclusiveMaximum;
3649
+ json.exclusiveMaximum = true;
3650
+ } else json.exclusiveMaximum = exclusiveMaximum;
3651
+ } else if (typeof maximum === "number") json.maximum = maximum;
3640
3652
  if (typeof multipleOf === "number") json.multipleOf = multipleOf;
3641
3653
  };
3642
3654
  var booleanProcessor = (_schema, _ctx, json, _params) => {
@@ -3657,9 +3669,10 @@ var literalProcessor = (schema, ctx, json, _params) => {
3657
3669
  const vals = [];
3658
3670
  for (const val of def.values) if (val === void 0) {
3659
3671
  if (ctx.unrepresentable === "throw") throw new Error("Literal `undefined` cannot be represented in JSON Schema");
3660
- } else if (typeof val === "bigint") if (ctx.unrepresentable === "throw") throw new Error("BigInt literals cannot be represented in JSON Schema");
3661
- else vals.push(Number(val));
3662
- else vals.push(val);
3672
+ } else if (typeof val === "bigint") {
3673
+ if (ctx.unrepresentable === "throw") throw new Error("BigInt literals cannot be represented in JSON Schema");
3674
+ else vals.push(Number(val));
3675
+ } else vals.push(val);
3663
3676
  if (vals.length === 0) {} else if (vals.length === 1) {
3664
3677
  const val = vals[0];
3665
3678
  json.type = val === null ? "null" : typeof val;
@@ -5074,7 +5087,7 @@ function extendedProfileToFace(profile) {
5074
5087
  }
5075
5088
  //#endregion
5076
5089
  //#region src/specs/profile.ts
5077
- var EXTENDED_PROFILE_KIND_SET = new Set([
5090
+ var EXTENDED_PROFILE_KIND_SET = /* @__PURE__ */ new Set([
5078
5091
  "L_SHAPE",
5079
5092
  "T_SHAPE",
5080
5093
  "U_SHAPE",
@@ -5298,7 +5311,7 @@ function profileToPolygon(profile, circleSegments = 32) {
5298
5311
  case "RECTANGULAR": {
5299
5312
  const halfW = profile.width / 2;
5300
5313
  const halfH = profile.height / 2;
5301
- return (0, brepjs.ok)([
5314
+ const pts = [
5302
5315
  [
5303
5316
  -halfW,
5304
5317
  -halfH,
@@ -5319,7 +5332,8 @@ function profileToPolygon(profile, circleSegments = 32) {
5319
5332
  halfH,
5320
5333
  0
5321
5334
  ]
5322
- ]);
5335
+ ];
5336
+ return (0, brepjs.ok)(pts);
5323
5337
  }
5324
5338
  case "CIRCULAR": {
5325
5339
  if (circleSegments < 3) return (0, brepjs.err)(specError("PROFILE_CIRCLE_SEGMENTS", `profileToPolygon: circleSegments must be >= 3, got ${circleSegments}`));
@@ -5354,7 +5368,7 @@ function profileToPolygon(profile, circleSegments = 32) {
5354
5368
  [-halfW, -flangeInnerY]
5355
5369
  ];
5356
5370
  const r = profile.filletRadius ?? 0;
5357
- const rootCorners = new Set([
5371
+ const rootCorners = /* @__PURE__ */ new Set([
5358
5372
  3,
5359
5373
  4,
5360
5374
  9,
@@ -5396,13 +5410,15 @@ function beamToSolid(spec) {
5396
5410
  var _usingCtx3 = _usingCtx();
5397
5411
  const faceResult = extendedProfileToFace(spec.profile);
5398
5412
  if (!faceResult.ok) return (0, brepjs.err)(faceResult.error);
5399
- const prismResult = (0, brepjs.extrude)(_usingCtx3.u(faceResult.value), [
5413
+ const face = _usingCtx3.u(faceResult.value);
5414
+ const prismResult = (0, brepjs.extrude)(face, [
5400
5415
  0,
5401
5416
  0,
5402
5417
  spec.length
5403
5418
  ]);
5404
5419
  if (!prismResult.ok) return (0, brepjs.err)(fromBrepError(prismResult.error, "BEAM_EXTRUDE_FAILED", "Failed to extrude beam profile"));
5405
- const solid = (0, brepjs.rotate)(_usingCtx3.u(prismResult.value), 90, { axis: [
5420
+ const prism = _usingCtx3.u(prismResult.value);
5421
+ const solid = (0, brepjs.rotate)(prism, 90, { axis: [
5406
5422
  0,
5407
5423
  1,
5408
5424
  0
@@ -5419,13 +5435,15 @@ function beamToSolid(spec) {
5419
5435
  }
5420
5436
  const profilePtsResult = profileToPolygon(spec.profile);
5421
5437
  if (!profilePtsResult.ok) return (0, brepjs.err)(profilePtsResult.error);
5422
- const profileResult = (0, brepjs.polygon)(profilePtsResult.value.map(([px, py]) => [
5438
+ const profilePts = profilePtsResult.value.map(([px, py]) => [
5423
5439
  0,
5424
5440
  px,
5425
5441
  py
5426
- ]));
5442
+ ]);
5443
+ const profileResult = (0, brepjs.polygon)(profilePts);
5427
5444
  if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "BEAM_PROFILE_FAILED", "Failed to create beam profile"));
5428
- const solidResult = (0, brepjs.extrude)(_usingCtx$16.u(profileResult.value), [
5445
+ const profile = _usingCtx$16.u(profileResult.value);
5446
+ const solidResult = (0, brepjs.extrude)(profile, [
5429
5447
  spec.length,
5430
5448
  0,
5431
5449
  0
@@ -5453,7 +5471,8 @@ function columnToSolid(spec) {
5453
5471
  var _usingCtx3 = _usingCtx();
5454
5472
  const faceResult = extendedProfileToFace(spec.profile);
5455
5473
  if (!faceResult.ok) return (0, brepjs.err)(faceResult.error);
5456
- const extResult = (0, brepjs.extrude)(_usingCtx3.u(faceResult.value), [
5474
+ const face = _usingCtx3.u(faceResult.value);
5475
+ const extResult = (0, brepjs.extrude)(face, [
5457
5476
  0,
5458
5477
  0,
5459
5478
  spec.height
@@ -5475,7 +5494,8 @@ function columnToSolid(spec) {
5475
5494
  const profilePts = profilePtsResult.value;
5476
5495
  const profileResult = (0, brepjs.polygon)(profilePts);
5477
5496
  if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "COLUMN_PROFILE_FAILED", "Failed to create column profile"));
5478
- const solidResult = (0, brepjs.extrude)(_usingCtx$15.u(profileResult.value), [
5497
+ const profile = _usingCtx$15.u(profileResult.value);
5498
+ const solidResult = (0, brepjs.extrude)(profile, [
5479
5499
  0,
5480
5500
  0,
5481
5501
  spec.height
@@ -5495,7 +5515,7 @@ function columnToSolid(spec) {
5495
5515
  }
5496
5516
  //#endregion
5497
5517
  //#region src/elementFns/openingFns.ts
5498
- var EPSILON_MM$1 = 1;
5518
+ var EPSILON_MM = 1;
5499
5519
  function openingToSolid(spec, wallThickness) {
5500
5520
  try {
5501
5521
  var _usingCtx$14 = _usingCtx();
@@ -5504,7 +5524,7 @@ function openingToSolid(spec, wallThickness) {
5504
5524
  if (wallThickness <= 0) return (0, brepjs.err)(specError("OPENING_ZERO_WALL_THICKNESS", "Wall thickness must be positive"));
5505
5525
  const x0 = spec.offsetAlongWall;
5506
5526
  const yLow = -1;
5507
- const yHigh = wallThickness + EPSILON_MM$1;
5527
+ const yHigh = wallThickness + EPSILON_MM;
5508
5528
  const zLow = spec.offsetFromFloor;
5509
5529
  const zHigh = spec.offsetFromFloor + spec.height;
5510
5530
  const profileResult = (0, brepjs.polygon)([
@@ -5530,7 +5550,8 @@ function openingToSolid(spec, wallThickness) {
5530
5550
  ]
5531
5551
  ]);
5532
5552
  if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "OPENING_PROFILE_FAILED", "Failed to create opening profile"));
5533
- const solidResult = (0, brepjs.extrude)(_usingCtx$14.u(profileResult.value), [
5553
+ const profile = _usingCtx$14.u(profileResult.value);
5554
+ const solidResult = (0, brepjs.extrude)(profile, [
5534
5555
  spec.width,
5535
5556
  0,
5536
5557
  0
@@ -5550,7 +5571,6 @@ function openingToSolid(spec, wallThickness) {
5550
5571
  }
5551
5572
  //#endregion
5552
5573
  //#region src/elementFns/slabOpeningFns.ts
5553
- var EPSILON_MM = 1;
5554
5574
  function slabOpeningToSolid(spec, slabThickness) {
5555
5575
  try {
5556
5576
  var _usingCtx$13 = _usingCtx();
@@ -5585,10 +5605,11 @@ function slabOpeningToSolid(spec, slabThickness) {
5585
5605
  ]
5586
5606
  ]);
5587
5607
  if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "SLAB_OPENING_PROFILE_FAILED", "Failed to create slab opening profile"));
5588
- const solidResult = (0, brepjs.extrude)(_usingCtx$13.u(profileResult.value), [
5608
+ const profile = _usingCtx$13.u(profileResult.value);
5609
+ const solidResult = (0, brepjs.extrude)(profile, [
5589
5610
  0,
5590
5611
  0,
5591
- slabThickness + 2 * EPSILON_MM
5612
+ slabThickness + 2
5592
5613
  ]);
5593
5614
  if (!solidResult.ok) return (0, brepjs.err)(fromBrepError(solidResult.error, "SLAB_OPENING_EXTRUDE_FAILED", "Failed to extrude slab opening profile"));
5594
5615
  const solid = solidResult.value;
@@ -5635,7 +5656,8 @@ function spaceToSolid(spec) {
5635
5656
  ]
5636
5657
  ]);
5637
5658
  if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "SPACE_PROFILE_FAILED", "Failed to create space footprint"));
5638
- const solidResult = (0, brepjs.extrude)(_usingCtx$12.u(profileResult.value), [
5659
+ const profile = _usingCtx$12.u(profileResult.value);
5660
+ const solidResult = (0, brepjs.extrude)(profile, [
5639
5661
  0,
5640
5662
  0,
5641
5663
  height
@@ -5690,7 +5712,8 @@ function flatRoof(spec) {
5690
5712
  ]
5691
5713
  ]);
5692
5714
  if (!face.ok) return (0, brepjs.err)(fromBrepError(face.error, "ROOF_PROFILE_FAILED", "Failed to create roof profile"));
5693
- const solid = (0, brepjs.extrude)(_usingCtx$11.u(face.value), [
5715
+ const profile = _usingCtx$11.u(face.value);
5716
+ const solid = (0, brepjs.extrude)(profile, [
5694
5717
  0,
5695
5718
  0,
5696
5719
  thickness
@@ -5731,7 +5754,8 @@ function shedRoof(spec, pitch) {
5731
5754
  ]
5732
5755
  ]);
5733
5756
  if (!face.ok) return (0, brepjs.err)(fromBrepError(face.error, "ROOF_PROFILE_FAILED", "Failed to create shed profile"));
5734
- const solid = (0, brepjs.extrude)(_usingCtx3.u(face.value), [
5757
+ const profile = _usingCtx3.u(face.value);
5758
+ const solid = (0, brepjs.extrude)(profile, [
5735
5759
  length,
5736
5760
  0,
5737
5761
  0
@@ -5777,7 +5801,8 @@ function gableRoof(spec, pitch) {
5777
5801
  ]
5778
5802
  ]);
5779
5803
  if (!face.ok) return (0, brepjs.err)(fromBrepError(face.error, "ROOF_PROFILE_FAILED", "Failed to create gable profile"));
5780
- const solid = (0, brepjs.extrude)(_usingCtx4.u(face.value), [
5804
+ const profile = _usingCtx4.u(face.value);
5805
+ const solid = (0, brepjs.extrude)(profile, [
5781
5806
  length,
5782
5807
  0,
5783
5808
  0
@@ -5918,7 +5943,8 @@ function boxSolid(sizeX, sizeY, sizeZ) {
5918
5943
  ]
5919
5944
  ]);
5920
5945
  if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "CURTAIN_WALL_PROFILE_FAILED", "Failed to create component profile"));
5921
- const solidResult = (0, brepjs.extrude)(_usingCtx$10.u(profileResult.value), [
5946
+ const profile = _usingCtx$10.u(profileResult.value);
5947
+ const solidResult = (0, brepjs.extrude)(profile, [
5922
5948
  0,
5923
5949
  0,
5924
5950
  sizeZ
@@ -6060,7 +6086,8 @@ function footingToSolid(spec) {
6060
6086
  ]
6061
6087
  ]);
6062
6088
  if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "FOOTING_PROFILE_FAILED", "Failed to create footing profile"));
6063
- const solidResult = (0, brepjs.extrude)(_usingCtx$9.u(profileResult.value), [
6089
+ const profile = _usingCtx$9.u(profileResult.value);
6090
+ const solidResult = (0, brepjs.extrude)(profile, [
6064
6091
  0,
6065
6092
  0,
6066
6093
  thickness
@@ -6086,7 +6113,8 @@ function pileToSolid(spec) {
6086
6113
  var _usingCtx4 = _usingCtx();
6087
6114
  const faceResult = extendedProfileToFace(spec.profile);
6088
6115
  if (!faceResult.ok) return (0, brepjs.err)(faceResult.error);
6089
- const extResult = (0, brepjs.extrude)(_usingCtx4.u(faceResult.value), [
6116
+ const face = _usingCtx4.u(faceResult.value);
6117
+ const extResult = (0, brepjs.extrude)(face, [
6090
6118
  0,
6091
6119
  0,
6092
6120
  spec.length
@@ -6108,7 +6136,8 @@ function pileToSolid(spec) {
6108
6136
  const profilePts = profilePtsResult.value;
6109
6137
  const profileResult = (0, brepjs.polygon)(profilePts);
6110
6138
  if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "PILE_PROFILE_FAILED", "Failed to create pile profile"));
6111
- const solidResult = (0, brepjs.extrude)(_usingCtx3.u(profileResult.value), [
6139
+ const profile = _usingCtx3.u(profileResult.value);
6140
+ const solidResult = (0, brepjs.extrude)(profile, [
6112
6141
  0,
6113
6142
  0,
6114
6143
  spec.length
@@ -6155,7 +6184,8 @@ function panelRailing(spec) {
6155
6184
  ]
6156
6185
  ]);
6157
6186
  if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "RAILING_PROFILE_FAILED", "Failed to create railing profile"));
6158
- const solidResult = (0, brepjs.extrude)(_usingCtx$8.u(profileResult.value), [
6187
+ const profile = _usingCtx$8.u(profileResult.value);
6188
+ const solidResult = (0, brepjs.extrude)(profile, [
6159
6189
  length,
6160
6190
  0,
6161
6191
  0
@@ -6261,7 +6291,8 @@ function coveringToSolid(spec) {
6261
6291
  ]
6262
6292
  ]);
6263
6293
  if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "COVERING_PROFILE_FAILED", "Failed to create covering profile"));
6264
- const solidResult = (0, brepjs.extrude)(_usingCtx$7.u(profileResult.value), [
6294
+ const profile = _usingCtx$7.u(profileResult.value);
6295
+ const solidResult = (0, brepjs.extrude)(profile, [
6265
6296
  0,
6266
6297
  0,
6267
6298
  thickness
@@ -6288,6 +6319,7 @@ var BimModel = class {
6288
6319
  #counter = makeLocalIdCounter();
6289
6320
  #projectId = null;
6290
6321
  #modelScope = "";
6322
+ #usedStableKeys = /* @__PURE__ */ new Set();
6291
6323
  init(spec) {
6292
6324
  if (this.#projectId !== null) return (0, brepjs.err)(specError("DUPLICATE_PROJECT", "BimModel.init() called twice — only one project per model"));
6293
6325
  this.#modelScope = spec.projectId ?? `${spec.name}::${spec.description ?? ""}`;
@@ -6305,24 +6337,43 @@ var BimModel = class {
6305
6337
  addSite(spec) {
6306
6338
  return this.#makeElement("SITE", spec, null);
6307
6339
  }
6308
- addBuilding(spec) {
6309
- return this.#makeElement("BUILDING", spec, null);
6310
- }
6311
- addStorey(spec) {
6312
- return this.#makeElement("STOREY", spec, null);
6313
- }
6314
- addWall(spec) {
6340
+ addBuilding(spec, options) {
6341
+ return this.#makeElement("BUILDING", spec, null, options?.stableKey);
6342
+ }
6343
+ addStorey(spec, options) {
6344
+ return this.#makeElement("STOREY", spec, null, options?.stableKey);
6345
+ }
6346
+ /** Reject a duplicate stableKey BEFORE any geometry is built, so the
6347
+ * Result-returning adders never allocate a solid they cannot store. */
6348
+ #checkStableKey(options) {
6349
+ const key = options?.stableKey;
6350
+ if (key !== void 0 && this.#usedStableKeys.has(key)) return (0, brepjs.err)(specError("DUPLICATE_STABLE_KEY", `BimModel: duplicate stableKey '${key}'`));
6351
+ return (0, brepjs.ok)(void 0);
6352
+ }
6353
+ #checkOpeningKeys(options) {
6354
+ const filler = this.#checkStableKey(options);
6355
+ if (!filler.ok) return filler;
6356
+ const opening = this.#checkStableKey({ stableKey: options?.openingStableKey });
6357
+ if (!opening.ok) return opening;
6358
+ if (options?.stableKey !== void 0 && options.stableKey === options.openingStableKey) return (0, brepjs.err)(specError("DUPLICATE_STABLE_KEY", `BimModel: stableKey and openingStableKey are both '${options.stableKey}'`));
6359
+ return (0, brepjs.ok)(void 0);
6360
+ }
6361
+ addWall(spec, options) {
6362
+ const keyCheck = this.#checkStableKey(options);
6363
+ if (!keyCheck.ok) return keyCheck;
6315
6364
  const geomResult = wallToSolid(spec);
6316
6365
  if (!geomResult.ok) return (0, brepjs.err)(geomResult.error);
6317
- const id = this.#makeElement("WALL", spec, geomResult.value);
6366
+ const id = this.#makeElement("WALL", spec, geomResult.value, options?.stableKey);
6318
6367
  this.#associateMaterial(id, spec);
6319
6368
  this.#associateClassification(id, spec);
6320
6369
  return (0, brepjs.ok)(id);
6321
6370
  }
6322
- addSlab(spec) {
6371
+ addSlab(spec, options) {
6372
+ const keyCheck = this.#checkStableKey(options);
6373
+ if (!keyCheck.ok) return keyCheck;
6323
6374
  const geomResult = slabToSolid(spec);
6324
6375
  if (!geomResult.ok) return (0, brepjs.err)(geomResult.error);
6325
- const id = this.#makeElement("SLAB", spec, geomResult.value);
6376
+ const id = this.#makeElement("SLAB", spec, geomResult.value, options?.stableKey);
6326
6377
  this.#associateMaterial(id, spec);
6327
6378
  this.#associateClassification(id, spec);
6328
6379
  return (0, brepjs.ok)(id);
@@ -6598,7 +6649,9 @@ var BimModel = class {
6598
6649
  });
6599
6650
  return (0, brepjs.ok)(id);
6600
6651
  }
6601
- addDoor(spec) {
6652
+ addDoor(spec, options) {
6653
+ const keyCheck = this.#checkOpeningKeys(options);
6654
+ if (!keyCheck.ok) return keyCheck;
6602
6655
  const wall = this.#elements.get(spec.wallLocalId);
6603
6656
  if (wall === void 0 || wall.category !== "WALL") return (0, brepjs.err)(specError("DOOR_WALL_NOT_FOUND", `No wall found for localId ${spec.wallLocalId}`));
6604
6657
  if (spec.offsetAlongWall + spec.width > wall.spec.length) return (0, brepjs.err)(specError("DOOR_EXCEEDS_WALL_BOUNDS", "Door (offsetAlongWall + width) exceeds wall length"));
@@ -6613,13 +6666,13 @@ var BimModel = class {
6613
6666
  const cutResult = this.#cutWallGeometry(wall, openingSpec);
6614
6667
  if (!cutResult.ok) return (0, brepjs.err)(cutResult.error);
6615
6668
  this.#replaceWallGeometry(wall, cutResult.value);
6616
- const openingId = this.#makeElement("OPENING", openingSpec, null);
6669
+ const openingId = this.#makeElement("OPENING", openingSpec, null, options?.openingStableKey);
6617
6670
  this.#makeRel({
6618
6671
  kind: "VOIDS_WALL",
6619
6672
  wallLocalId: spec.wallLocalId,
6620
6673
  openingLocalId: openingId
6621
6674
  });
6622
- const doorId = this.#makeElement("DOOR", spec, null);
6675
+ const doorId = this.#makeElement("DOOR", spec, null, options?.stableKey);
6623
6676
  this.#makeRel({
6624
6677
  kind: "FILLS_OPENING",
6625
6678
  openingLocalId: openingId,
@@ -6632,7 +6685,9 @@ var BimModel = class {
6632
6685
  });
6633
6686
  return (0, brepjs.ok)(doorId);
6634
6687
  }
6635
- addWindow(spec) {
6688
+ addWindow(spec, options) {
6689
+ const keyCheck = this.#checkOpeningKeys(options);
6690
+ if (!keyCheck.ok) return keyCheck;
6636
6691
  const wall = this.#elements.get(spec.wallLocalId);
6637
6692
  if (wall === void 0 || wall.category !== "WALL") return (0, brepjs.err)(specError("WINDOW_WALL_NOT_FOUND", `No wall found for localId ${spec.wallLocalId}`));
6638
6693
  if (spec.offsetAlongWall + spec.width > wall.spec.length) return (0, brepjs.err)(specError("WINDOW_EXCEEDS_WALL_BOUNDS", "Window (offsetAlongWall + width) exceeds wall length"));
@@ -6647,13 +6702,13 @@ var BimModel = class {
6647
6702
  const cutResult = this.#cutWallGeometry(wall, openingSpec);
6648
6703
  if (!cutResult.ok) return (0, brepjs.err)(cutResult.error);
6649
6704
  this.#replaceWallGeometry(wall, cutResult.value);
6650
- const openingId = this.#makeElement("OPENING", openingSpec, null);
6705
+ const openingId = this.#makeElement("OPENING", openingSpec, null, options?.openingStableKey);
6651
6706
  this.#makeRel({
6652
6707
  kind: "VOIDS_WALL",
6653
6708
  wallLocalId: spec.wallLocalId,
6654
6709
  openingLocalId: openingId
6655
6710
  });
6656
- const windowId = this.#makeElement("WINDOW", spec, null);
6711
+ const windowId = this.#makeElement("WINDOW", spec, null, options?.stableKey);
6657
6712
  this.#makeRel({
6658
6713
  kind: "FILLS_OPENING",
6659
6714
  openingLocalId: openingId,
@@ -6666,7 +6721,9 @@ var BimModel = class {
6666
6721
  });
6667
6722
  return (0, brepjs.ok)(windowId);
6668
6723
  }
6669
- addSlabOpening(input) {
6724
+ addSlabOpening(input, options) {
6725
+ const keyCheck = this.#checkStableKey(options);
6726
+ if (!keyCheck.ok) return keyCheck;
6670
6727
  const slab = this.#elements.get(input.slabLocalId);
6671
6728
  if (slab === void 0 || slab.category !== "SLAB") return (0, brepjs.err)(specError("SLAB_OPENING_SLAB_NOT_FOUND", `No slab found for localId ${input.slabLocalId}`));
6672
6729
  if (input.offsetX + input.sizeX > slab.spec.length) return (0, brepjs.err)(specError("SLAB_OPENING_EXCEEDS_SLAB_BOUNDS", "Opening (offsetX + sizeX) exceeds slab length"));
@@ -6696,7 +6753,7 @@ var BimModel = class {
6696
6753
  const cutResult = this.#cutSlabGeometry(slab, openingSpec);
6697
6754
  if (!cutResult.ok) return (0, brepjs.err)(cutResult.error);
6698
6755
  this.#replaceSlabGeometry(slab, cutResult.value);
6699
- const openingId = this.#makeElement("OPENING", openingSpec, null);
6756
+ const openingId = this.#makeElement("OPENING", openingSpec, null, options?.stableKey);
6700
6757
  this.#makeRel({
6701
6758
  kind: "VOIDS_SLAB",
6702
6759
  slabLocalId: input.slabLocalId,
@@ -6941,10 +6998,14 @@ var BimModel = class {
6941
6998
  getAllRelationships() {
6942
6999
  return [...this.#relationships.values()];
6943
7000
  }
6944
- #makeElement(category, spec, geometry) {
7001
+ #makeElement(category, spec, geometry, stableKey) {
6945
7002
  const localId = this.#counter.next();
7003
+ if (stableKey !== void 0) {
7004
+ if (this.#usedStableKeys.has(stableKey)) throw new Error(`BimModel: duplicate stableKey '${stableKey}'`);
7005
+ this.#usedStableKeys.add(stableKey);
7006
+ }
6946
7007
  const el = {
6947
- guid: deriveIfcGuidSync(makeElementKey(this.#modelScope, category, localId)),
7008
+ guid: deriveIfcGuidSync(stableKey !== void 0 ? `elem:${this.#modelScope}:${stableKey}` : makeElementKey(this.#modelScope, category, localId)),
6948
7009
  localId,
6949
7010
  category,
6950
7011
  spec,
@@ -7191,9 +7252,11 @@ function stairFlightToSolid(spec) {
7191
7252
  if (spec.riserHeight <= 0) return (0, brepjs.err)(specError("STAIR_FLIGHT_ZERO_RISER", "Stair flight riserHeight must be positive"));
7192
7253
  if (spec.treadLength <= 0) return (0, brepjs.err)(specError("STAIR_FLIGHT_ZERO_TREAD", "Stair flight treadLength must be positive"));
7193
7254
  if (!Number.isInteger(spec.numberOfRisers) || spec.numberOfRisers < 1) return (0, brepjs.err)(specError("STAIR_FLIGHT_BAD_RISERS", "Stair flight numberOfRisers must be a positive integer"));
7194
- const profileResult = (0, brepjs.polygon)(buildSilhouette$1(spec.numberOfRisers, spec.riserHeight, spec.treadLength));
7255
+ const silhouette = buildSilhouette$1(spec.numberOfRisers, spec.riserHeight, spec.treadLength);
7256
+ const profileResult = (0, brepjs.polygon)(silhouette);
7195
7257
  if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "STAIR_FLIGHT_PROFILE_FAILED", "Failed to create stair flight silhouette profile"));
7196
- const solidResult = (0, brepjs.extrude)(_usingCtx$5.u(profileResult.value), [
7258
+ const profile = _usingCtx$5.u(profileResult.value);
7259
+ const solidResult = (0, brepjs.extrude)(profile, [
7197
7260
  0,
7198
7261
  spec.width,
7199
7262
  0
@@ -7349,7 +7412,7 @@ function isIfcSchema(value) {
7349
7412
  * linear-infrastructure entities). Entities absent from every per-schema set are
7350
7413
  * treated as schema-agnostic and supported everywhere (open-world default).
7351
7414
  */
7352
- var IFC4X3_ONLY_ENTITIES = new Set([
7415
+ var IFC4X3_ONLY_ENTITIES = /* @__PURE__ */ new Set([
7353
7416
  "IfcAlignment",
7354
7417
  "IfcAlignmentHorizontal",
7355
7418
  "IfcAlignmentVertical",
@@ -7429,7 +7492,8 @@ var IfcWriter = class IfcWriter {
7429
7492
  try {
7430
7493
  const api = new web_ifc.IfcAPI();
7431
7494
  await initIfcApi(api);
7432
- return (0, brepjs.ok)(new IfcWriter(api, api.CreateModel({ schema: fileSchemaString(ifcSchema) }), mvdViewDefinition, header));
7495
+ const modelId = api.CreateModel({ schema: fileSchemaString(ifcSchema) });
7496
+ return (0, brepjs.ok)(new IfcWriter(api, modelId, mvdViewDefinition, header));
7433
7497
  } catch (e) {
7434
7498
  return (0, brepjs.err)(ifcError("IFC_INIT_FAILED", "Failed to initialize web-ifc", e));
7435
7499
  }
@@ -7483,8 +7547,10 @@ var IfcWriter = class IfcWriter {
7483
7547
  let head = new TextDecoder().decode(bytes.subarray(0, HEADER_SCAN));
7484
7548
  if (FILE_NAME_RE.test(head)) head = head.replace(FILE_NAME_RE, (_m, prefix, systems) => `${prefix}(${stepString(this.#author)}),(${stepString(this.#organization)}),${systems},${stepString("")})`);
7485
7549
  else console.warn("IfcWriter: FILE_NAME null-field pattern not found; author/organization/authorization left unpatched");
7486
- if (this.#mvdViewDefinition.length > 0) if (VIEW_DEFINITION_RE.test(head)) head = head.replace(VIEW_DEFINITION_RE, `ViewDefinition [${this.#mvdViewDefinition}]`);
7487
- else console.warn(`IfcWriter: FILE_DESCRIPTION ViewDefinition not found; MVD "${this.#mvdViewDefinition}" not declared`);
7550
+ if (this.#mvdViewDefinition.length > 0) {
7551
+ if (VIEW_DEFINITION_RE.test(head)) head = head.replace(VIEW_DEFINITION_RE, `ViewDefinition [${this.#mvdViewDefinition}]`);
7552
+ else console.warn(`IfcWriter: FILE_DESCRIPTION ViewDefinition not found; MVD "${this.#mvdViewDefinition}" not declared`);
7553
+ }
7488
7554
  const patchedHeadBytes = new TextEncoder().encode(head);
7489
7555
  const tail = bytes.subarray(HEADER_SCAN);
7490
7556
  const out = new Uint8Array(patchedHeadBytes.byteLength + tail.byteLength);
@@ -9403,9 +9469,11 @@ function rampFlightToSolid(spec) {
9403
9469
  if (spec.slope <= 0) return (0, brepjs.err)(specError("RAMP_FLIGHT_ZERO_SLOPE", "Ramp flight slope must be positive"));
9404
9470
  if (spec.thickness <= 0) return (0, brepjs.err)(specError("RAMP_FLIGHT_ZERO_THICKNESS", "Ramp flight thickness must be positive"));
9405
9471
  const rise = spec.length * spec.slope;
9406
- const profileResult = (0, brepjs.polygon)(buildSilhouette(spec.length, rise, spec.thickness));
9472
+ const silhouette = buildSilhouette(spec.length, rise, spec.thickness);
9473
+ const profileResult = (0, brepjs.polygon)(silhouette);
9407
9474
  if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "RAMP_FLIGHT_PROFILE_FAILED", "Failed to create ramp flight silhouette profile"));
9408
- const solidResult = (0, brepjs.extrude)(_usingCtx$3.u(profileResult.value), [
9475
+ const profile = _usingCtx$3.u(profileResult.value);
9476
+ const solidResult = (0, brepjs.extrude)(profile, [
9409
9477
  0,
9410
9478
  spec.width,
9411
9479
  0
@@ -11731,7 +11799,7 @@ function isSlabOpening(spec) {
11731
11799
  * instead, and OPENINGs are tied to their host through VOIDS rels — neither
11732
11800
  * participates in containment, so excluding them here avoids false orphans.
11733
11801
  */
11734
- var CONTAINABLE_CATEGORIES = new Set([
11802
+ var CONTAINABLE_CATEGORIES = /* @__PURE__ */ new Set([
11735
11803
  "WALL",
11736
11804
  "SLAB",
11737
11805
  "BEAM",
@@ -12528,14 +12596,15 @@ async function toIfcValidated(model, meta) {
12528
12596
  const geometry = collectGeometryIssues(model);
12529
12597
  const schema = await checkSchema(bytes);
12530
12598
  const roundTrip = await checkRoundTrip(bytes);
12599
+ const report = { issues: [
12600
+ ...integrity.issues,
12601
+ ...geometry.issues,
12602
+ ...schema.issues,
12603
+ ...roundTrip.issues
12604
+ ] };
12531
12605
  return (0, brepjs.ok)({
12532
12606
  bytes,
12533
- report: { issues: [
12534
- ...integrity.issues,
12535
- ...geometry.issues,
12536
- ...schema.issues,
12537
- ...roundTrip.issues
12538
- ] }
12607
+ report
12539
12608
  });
12540
12609
  }
12541
12610
  /**
@@ -12756,7 +12825,7 @@ var SpfReader = class SpfReader {
12756
12825
  };
12757
12826
  //#endregion
12758
12827
  //#region src/import/spatialTree.ts
12759
- var CATEGORY_BY_TYPE = new Map([
12828
+ var CATEGORY_BY_TYPE = /* @__PURE__ */ new Map([
12760
12829
  [web_ifc.IFCPROJECT, "PROJECT"],
12761
12830
  [web_ifc.IFCSITE, "SITE"],
12762
12831
  [web_ifc.IFCBUILDING, "BUILDING"],
@@ -12938,7 +13007,8 @@ function reconstructExtrusion(reader, extrusionId, scale, worldTransform, diagno
12938
13007
  const solidResult = (() => {
12939
13008
  try {
12940
13009
  var _usingCtx$1 = _usingCtx();
12941
- return (0, brepjs.extrude)(_usingCtx$1.u(faceResult.value), extrudeVec);
13010
+ const face = _usingCtx$1.u(faceResult.value);
13011
+ return (0, brepjs.extrude)(face, extrudeVec);
12942
13012
  } catch (_) {
12943
13013
  _usingCtx$1.e = _;
12944
13014
  } finally {
@@ -12991,10 +13061,12 @@ function reconstructRevolution(reader, revolutionId, scale, worldTransform, diag
12991
13061
  const revolved = (() => {
12992
13062
  try {
12993
13063
  var _usingCtx3 = _usingCtx();
12994
- return (0, brepjs.revolve)(_usingCtx3.u(faceResult.value), {
13064
+ const face = _usingCtx3.u(faceResult.value);
13065
+ const angleDeg = angleRaw * readPlaneAngleScale(reader) * (180 / Math.PI);
13066
+ return (0, brepjs.revolve)(face, {
12995
13067
  at: center,
12996
13068
  axis: direction,
12997
- angle: angleRaw * readPlaneAngleScale(reader) * (180 / Math.PI)
13069
+ angle: angleDeg
12998
13070
  });
12999
13071
  } catch (_) {
13000
13072
  _usingCtx3.e = _;
@@ -13109,7 +13181,7 @@ function packBinaryStl(vertices, indices, scaleToMm) {
13109
13181
  }
13110
13182
  return new Uint8Array(buffer);
13111
13183
  }
13112
- var PARAMETRIC_PROFILE_BUILDERS = new Map([
13184
+ var PARAMETRIC_PROFILE_BUILDERS = /* @__PURE__ */ new Map([
13113
13185
  [web_ifc.IFCRECTANGLEPROFILEDEF, (f) => ({
13114
13186
  kind: "RECTANGULAR",
13115
13187
  width: f("XDim"),
@@ -13877,14 +13949,15 @@ async function fromIfc(bytes, options = {}) {
13877
13949
  }
13878
13950
  const report = appendIssues(emptyReport(), diagnostics);
13879
13951
  const applicationName = readApplicationName(reader);
13880
- return (0, brepjs.ok)({
13952
+ const model = {
13881
13953
  schema: reader.schema,
13882
13954
  spatialTree: spatialRoot === null ? null : mapSpatialNode(spatialRoot),
13883
13955
  elements,
13884
13956
  byExpressId,
13885
13957
  diagnostics: report,
13886
13958
  ...applicationName !== void 0 ? { applicationName } : {}
13887
- });
13959
+ };
13960
+ return (0, brepjs.ok)(model);
13888
13961
  } catch (e) {
13889
13962
  return (0, brepjs.err)(importError("IMPORT_FAILED", "Unexpected failure during IFC import", e));
13890
13963
  } finally {
@@ -14911,7 +14984,7 @@ function parseSlabOpeningInput(input) {
14911
14984
  * containers (PROJECT/SITE/BUILDING/STOREY), abstract groupings, and the SPACE
14912
14985
  * category (which maps to the Space sheet, not Component) are excluded.
14913
14986
  */
14914
- var COMPONENT_CATEGORIES = new Set([
14987
+ var COMPONENT_CATEGORIES = /* @__PURE__ */ new Set([
14915
14988
  "WALL",
14916
14989
  "SLAB",
14917
14990
  "BEAM",
@@ -15536,9 +15609,10 @@ function parseIdsXml(xml) {
15536
15609
  const title = info ? firstChild(info, "title")?.text ?? "" : "";
15537
15610
  const specsContainer = firstChild(root, "specifications");
15538
15611
  if (specsContainer === void 0) return (0, brepjs.err)(idsError("IDS_INVALID_SCHEMA", "IDS document has no <specifications> element"));
15612
+ const specifications = childrenNamed(specsContainer, "specification").map(parseSpecification);
15539
15613
  return (0, brepjs.ok)({
15540
15614
  title,
15541
- specifications: childrenNamed(specsContainer, "specification").map(parseSpecification)
15615
+ specifications
15542
15616
  });
15543
15617
  }
15544
15618
  function parseSpecification(el) {
@@ -15895,10 +15969,7 @@ function restrictionsOf(facet) {
15895
15969
  push(facet.system);
15896
15970
  push(facet.value);
15897
15971
  break;
15898
- case "Material":
15899
- push(facet.value);
15900
- break;
15901
- case "PartOf": break;
15972
+ case "Material": push(facet.value);
15902
15973
  }
15903
15974
  return out;
15904
15975
  }
@@ -16298,7 +16369,8 @@ function parseBcfFiles(files) {
16298
16369
  topics
16299
16370
  });
16300
16371
  } catch (cause) {
16301
- return (0, brepjs.err)(bcfError("BCF_PARSE_FAILED", `Failed to parse BCF container: ${cause instanceof Error ? cause.message : String(cause)}`, cause));
16372
+ const message = cause instanceof Error ? cause.message : String(cause);
16373
+ return (0, brepjs.err)(bcfError("BCF_PARSE_FAILED", `Failed to parse BCF container: ${message}`, cause));
16302
16374
  }
16303
16375
  }
16304
16376
  function parseVersion(xml) {
@@ -16413,6 +16485,210 @@ function assignNum(target, key, value) {
16413
16485
  if (value !== void 0) target[key] = Number(value);
16414
16486
  }
16415
16487
  //#endregion
16488
+ //#region src/familiesAdapter.ts
16489
+ /**
16490
+ * brepjs-families -> BimModel adapter. Consumes a resolved element tree and
16491
+ * feeds each element's PRE-DESUGARED props into the parametric specs — the
16492
+ * spec path stays authoritative for IFC (IfcExtrudedAreaSolid + placement),
16493
+ * while the IR path serves the viewport and dedup. GlobalIds derive from
16494
+ * families key paths (stable under reordering), not insertion order.
16495
+ *
16496
+ * Scope: Storey containers, Wall/Slab elements, and wall openings — a
16497
+ * fill-role void (Door/Window family) maps onto addDoor/addWindow, which cut
16498
+ * the wall and wire IfcRelVoidsElement + IfcRelFillsElement; the opening and
16499
+ * filler GlobalIds derive from the synthesized key paths. Anonymous (non-fill)
16500
+ * voids stay IR-only and never reach the spec path.
16501
+ */
16502
+ var SPEC_DEFAULTS = {
16503
+ origin: [
16504
+ 0,
16505
+ 0,
16506
+ 0
16507
+ ],
16508
+ axisX: [
16509
+ 1,
16510
+ 0,
16511
+ 0
16512
+ ],
16513
+ axisZ: [
16514
+ 0,
16515
+ 0,
16516
+ 1
16517
+ ],
16518
+ materialName: "Default"
16519
+ };
16520
+ var GEOMETRY_PROPS = /* @__PURE__ */ new Set([
16521
+ "voids",
16522
+ "fuse",
16523
+ "transform",
16524
+ "psets"
16525
+ ]);
16526
+ /** Total of the resolved geometry's OUTER literal translate chain. The
16527
+ * transform vocabulary is translate-only, so frame differences are exact
16528
+ * subtractions. Parameter-driven translations stop the peel. */
16529
+ function peelTranslates(node) {
16530
+ const total = [
16531
+ 0,
16532
+ 0,
16533
+ 0
16534
+ ];
16535
+ let moved = false;
16536
+ let cur = node;
16537
+ while (cur.kind === "Translate") {
16538
+ const v = cur.vector;
16539
+ if (v.kind !== "Vec3Lit") break;
16540
+ total[0] += v.value[0];
16541
+ total[1] += v.value[1];
16542
+ total[2] += v.value[2];
16543
+ moved = true;
16544
+ cur = cur.target;
16545
+ }
16546
+ return {
16547
+ total,
16548
+ moved
16549
+ };
16550
+ }
16551
+ /** Fold the resolved geometry's outer translate chain into the spec placement
16552
+ * origin, so IfcLocalPlacement matches the IR world frame no matter where the
16553
+ * transform came from (family-internal or prop-level). */
16554
+ function composedOrigin(el) {
16555
+ const base = el.props["origin"] ?? [
16556
+ 0,
16557
+ 0,
16558
+ 0
16559
+ ];
16560
+ const { total, moved } = peelTranslates(el.geometry);
16561
+ if (!moved && el.props["origin"] === void 0) return void 0;
16562
+ return [
16563
+ base[0] + total[0],
16564
+ base[1] + total[1],
16565
+ base[2] + total[2]
16566
+ ];
16567
+ }
16568
+ function stripGeometryProps(props) {
16569
+ const out = {};
16570
+ for (const [k, v] of Object.entries(props)) if (!GEOMETRY_PROPS.has(k)) out[k] = v;
16571
+ return out;
16572
+ }
16573
+ function specInput(el) {
16574
+ const origin = composedOrigin(el);
16575
+ return {
16576
+ ...SPEC_DEFAULTS,
16577
+ ...stripGeometryProps(el.props),
16578
+ ...origin ? { origin } : {},
16579
+ ...collectSpecProps(el)
16580
+ };
16581
+ }
16582
+ function collectSpecProps(el) {
16583
+ const psets = el.attributes["psets"];
16584
+ const out = {};
16585
+ if (psets && typeof psets === "object") {
16586
+ const common = psets["Pset_WallCommon"];
16587
+ if (common && typeof common === "object") {
16588
+ const c = common;
16589
+ if (typeof c["IsExternal"] === "boolean") out["isExternal"] = c["IsExternal"];
16590
+ if (typeof c["FireRating"] === "string") out["fireRating"] = c["FireRating"];
16591
+ }
16592
+ }
16593
+ delete out["psets"];
16594
+ return out;
16595
+ }
16596
+ function addFill(model, fill, input, identity) {
16597
+ if (fill.type === "Door") {
16598
+ const parsed = parseDoorSpec(input);
16599
+ if (!parsed.ok) return parsed;
16600
+ return model.addDoor(parsed.value, identity);
16601
+ }
16602
+ if (fill.type === "Window") {
16603
+ const parsed = parseWindowSpec(input);
16604
+ if (!parsed.ok) return parsed;
16605
+ return model.addWindow(parsed.value, identity);
16606
+ }
16607
+ return (0, brepjs.err)(specError("FAMILIES_UNSUPPORTED_FILL", `familiesToBim: no fill mapping for element type '${fill.type}' at '${fill.keyPath}'`));
16608
+ }
16609
+ /** Map a wall's synthesized Opening children onto addDoor/addWindow. The
16610
+ * wall-relative offsets come from the void geometry's frame minus the host's:
16611
+ * exact because both carry the same outer host transform. */
16612
+ function addOpenings(model, host, wallId, containerId, idByKeyPath) {
16613
+ const hostT = peelTranslates(host.geometry).total;
16614
+ for (const opening of host.children) {
16615
+ if (opening.type !== "Opening") continue;
16616
+ const fill = opening.children[0];
16617
+ if (fill === void 0) return (0, brepjs.err)(specError("FAMILIES_OPENING_NO_FILL", `familiesToBim: opening '${opening.keyPath}' has no fill element`));
16618
+ const fillT = peelTranslates(opening.geometry).total;
16619
+ const added = addFill(model, fill, {
16620
+ materialName: SPEC_DEFAULTS.materialName,
16621
+ ...stripGeometryProps(fill.props),
16622
+ wallLocalId: wallId,
16623
+ offsetAlongWall: fillT[0] - hostT[0],
16624
+ offsetFromFloor: fillT[2] - hostT[2]
16625
+ }, {
16626
+ stableKey: fill.keyPath,
16627
+ openingStableKey: opening.keyPath
16628
+ });
16629
+ if (!added.ok) return added;
16630
+ model.placeIn(added.value, containerId);
16631
+ idByKeyPath.set(fill.keyPath, added.value);
16632
+ const fillsRel = model.getAllRelationships().find((r) => r.kind === "FILLS_OPENING" && r.fillerLocalId === added.value);
16633
+ if (fillsRel !== void 0) idByKeyPath.set(opening.keyPath, fillsRel.openingLocalId);
16634
+ }
16635
+ return (0, brepjs.ok)(void 0);
16636
+ }
16637
+ /**
16638
+ * Project a resolved families tree into an eager BimModel. The caller owns
16639
+ * the returned model (`using`); families stays domain-neutral — this adapter
16640
+ * is where families types meet the IFC vocabulary.
16641
+ */
16642
+ function familiesToBim(root, options) {
16643
+ const model = new BimModel();
16644
+ const initResult = model.init(options.project);
16645
+ if (!initResult.ok) return initResult;
16646
+ const siteId = model.addSite({ name: options.siteName ?? "Site" });
16647
+ const buildingId = model.addBuilding({ name: options.buildingName ?? "Building" });
16648
+ model.aggregate(siteId, buildingId);
16649
+ const idByKeyPath = /* @__PURE__ */ new Map();
16650
+ const walk = (el, storeyId) => {
16651
+ let containerId = storeyId;
16652
+ if (el.type === "Storey") {
16653
+ const id = model.addStorey({
16654
+ name: el.attributes["name"] ?? el.keyPath,
16655
+ elevation: el.props["elevation"] ?? 0
16656
+ }, { stableKey: el.keyPath });
16657
+ model.aggregate(buildingId, id);
16658
+ idByKeyPath.set(el.keyPath, id);
16659
+ containerId = id;
16660
+ } else if (el.type === "Wall" || el.type === "Slab") {
16661
+ const parsed = el.type === "Wall" ? parseWallSpec(specInput(el)) : parseSlabSpec(specInput(el));
16662
+ if (!parsed.ok) return parsed;
16663
+ const added = el.type === "Wall" ? model.addWall(parsed.value, { stableKey: el.keyPath }) : model.addSlab(parsed.value, { stableKey: el.keyPath });
16664
+ if (!added.ok) return added;
16665
+ idByKeyPath.set(el.keyPath, added.value);
16666
+ if (containerId === null) return (0, brepjs.err)(specError("FAMILIES_NO_STOREY", `familiesToBim: '${el.keyPath}' has no Storey ancestor — IFC elements need spatial containment`));
16667
+ model.placeIn(added.value, containerId);
16668
+ if (el.type === "Wall") {
16669
+ const opened = addOpenings(model, el, added.value, containerId, idByKeyPath);
16670
+ if (!opened.ok) return opened;
16671
+ }
16672
+ } else if (el.type === "Opening") return (0, brepjs.err)(specError("FAMILIES_OPENING_OUTSIDE_WALL", `familiesToBim: opening '${el.keyPath}' is not hosted by a Wall — only wall openings are mapped`));
16673
+ else if (el.type !== "Group" && el.geometry.kind !== "Empty") return (0, brepjs.err)(specError("FAMILIES_UNSUPPORTED_TYPE", `familiesToBim: no spec mapping for element type '${el.type}' at '${el.keyPath}'`));
16674
+ for (const child of el.children) {
16675
+ if (el.type === "Wall" && child.type === "Opening") continue;
16676
+ const r = walk(child, containerId);
16677
+ if (!r.ok) return r;
16678
+ }
16679
+ return (0, brepjs.ok)(void 0);
16680
+ };
16681
+ const walked = walk(root, null);
16682
+ if (!walked.ok) {
16683
+ model[Symbol.dispose]();
16684
+ return walked;
16685
+ }
16686
+ return (0, brepjs.ok)({
16687
+ model,
16688
+ idByKeyPath
16689
+ });
16690
+ }
16691
+ //#endregion
16416
16692
  exports.BimModel = BimModel;
16417
16693
  exports.DEFAULT_IFC_SCHEMA = DEFAULT_IFC_SCHEMA;
16418
16694
  exports.DEFAULT_UNITS = DEFAULT_UNITS;
@@ -16436,6 +16712,7 @@ exports.emptyReport = emptyReport;
16436
16712
  exports.exportCobie = deriveCobieModel;
16437
16713
  exports.extendedProfileArea = extendedProfileArea;
16438
16714
  exports.extendedProfileToFace = extendedProfileToFace;
16715
+ exports.familiesToBim = familiesToBim;
16439
16716
  exports.fileSchemaString = fileSchemaString;
16440
16717
  exports.fromBrepError = fromBrepError;
16441
16718
  exports.fromIfc = fromIfc;