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.
@@ -4,7 +4,7 @@ import { Handle, IfcAPI } from "web-ifc";
4
4
  //#region src/identity/ifcGuid.ts
5
5
  var IFC_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_$";
6
6
  function newIfcGuid() {
7
- const bytes = crypto.getRandomValues(new Uint8Array(16));
7
+ const bytes = crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(16));
8
8
  bytes[6] = (bytes[6] ?? 0) & 15 | 64;
9
9
  bytes[8] = (bytes[8] ?? 0) & 63 | 128;
10
10
  return encodeIfcGuid(bytes);
@@ -46,7 +46,7 @@ function fnv1a(text) {
46
46
  }
47
47
  function digest16(stableKey) {
48
48
  const seed = `${NAMESPACE}::${stableKey}`;
49
- const bytes = new Uint8Array(16);
49
+ const bytes = /* @__PURE__ */ new Uint8Array(16);
50
50
  for (let block = 0; block < 4; block++) {
51
51
  const h = fnv1a(`b${block}:${seed}`);
52
52
  bytes[block * 4 + 0] = h >>> 24 & 255;
@@ -183,7 +183,7 @@ function fromBrepError(inner, code, message) {
183
183
  };
184
184
  }
185
185
  //#endregion
186
- //#region \0@oxc-project+runtime@0.133.0/helpers/esm/usingCtx.js
186
+ //#region \0@oxc-project+runtime@0.143.0/helpers/esm/usingCtx.js
187
187
  function _usingCtx() {
188
188
  var r = "function" == typeof SuppressedError ? SuppressedError : function(r, e) {
189
189
  var n = Error();
@@ -270,7 +270,8 @@ function wallToSolid(spec) {
270
270
  ]
271
271
  ]);
272
272
  if (!profileResult.ok) return err(fromBrepError(profileResult.error, "WALL_PROFILE_FAILED", "Failed to create wall profile"));
273
- const solidResult = extrude(_usingCtx$19.u(profileResult.value), [
273
+ const profile = _usingCtx$19.u(profileResult.value);
274
+ const solidResult = extrude(profile, [
274
275
  length,
275
276
  0,
276
277
  0
@@ -320,7 +321,8 @@ function slabToSolid(spec) {
320
321
  ]
321
322
  ]);
322
323
  if (!profileResult.ok) return err(fromBrepError(profileResult.error, "SLAB_PROFILE_FAILED", "Failed to create slab profile"));
323
- const solidResult = extrude(_usingCtx$18.u(profileResult.value), [
324
+ const profile = _usingCtx$18.u(profileResult.value);
325
+ const solidResult = extrude(profile, [
324
326
  0,
325
327
  0,
326
328
  thickness
@@ -414,7 +416,6 @@ function cached(getter) {
414
416
  Object.defineProperty(this, "value", { value });
415
417
  return value;
416
418
  }
417
- throw new Error("cached value already set");
418
419
  } };
419
420
  }
420
421
  function nullish(input) {
@@ -460,7 +461,10 @@ function assignProp(target, prop, value) {
460
461
  }
461
462
  function mergeDefs(...defs) {
462
463
  const mergedDescriptors = {};
463
- for (const def of defs) Object.assign(mergedDescriptors, Object.getOwnPropertyDescriptors(def));
464
+ for (const def of defs) {
465
+ const descriptors = Object.getOwnPropertyDescriptors(def);
466
+ Object.assign(mergedDescriptors, descriptors);
467
+ }
464
468
  return Object.defineProperties({}, mergedDescriptors);
465
469
  }
466
470
  function esc(str) {
@@ -782,7 +786,7 @@ var _parse = (_Err) => (schema, value, _ctx, _params) => {
782
786
  }, ctx);
783
787
  if (result instanceof Promise) throw new $ZodAsyncError();
784
788
  if (result.issues.length) {
785
- const e = new (_params?.Err ?? _Err)(result.issues.map((iss) => finalizeIssue(iss, ctx, config())));
789
+ const e = new ((_params?.Err) ?? _Err)(result.issues.map((iss) => finalizeIssue(iss, ctx, config())));
786
790
  captureStackTrace(e, _params?.callee);
787
791
  throw e;
788
792
  }
@@ -799,7 +803,7 @@ var _parseAsync = (_Err) => async (schema, value, _ctx, params) => {
799
803
  }, ctx);
800
804
  if (result instanceof Promise) result = await result;
801
805
  if (result.issues.length) {
802
- const e = new (params?.Err ?? _Err)(result.issues.map((iss) => finalizeIssue(iss, ctx, config())));
806
+ const e = new ((params?.Err) ?? _Err)(result.issues.map((iss) => finalizeIssue(iss, ctx, config())));
803
807
  captureStackTrace(e, params?.callee);
804
808
  throw e;
805
809
  }
@@ -966,8 +970,10 @@ var $ZodCheckLessThan = /*@__PURE__*/ $constructor("$ZodCheckLessThan", (inst, d
966
970
  inst._zod.onattach.push((inst) => {
967
971
  const bag = inst._zod.bag;
968
972
  const curr = (def.inclusive ? bag.maximum : bag.exclusiveMaximum) ?? Number.POSITIVE_INFINITY;
969
- if (def.value < curr) if (def.inclusive) bag.maximum = def.value;
970
- else bag.exclusiveMaximum = def.value;
973
+ if (def.value < curr) {
974
+ if (def.inclusive) bag.maximum = def.value;
975
+ else bag.exclusiveMaximum = def.value;
976
+ }
971
977
  });
972
978
  inst._zod.check = (payload) => {
973
979
  if (def.inclusive ? payload.value <= def.value : payload.value < def.value) return;
@@ -988,8 +994,10 @@ var $ZodCheckGreaterThan = /*@__PURE__*/ $constructor("$ZodCheckGreaterThan", (i
988
994
  inst._zod.onattach.push((inst) => {
989
995
  const bag = inst._zod.bag;
990
996
  const curr = (def.inclusive ? bag.minimum : bag.exclusiveMinimum) ?? Number.NEGATIVE_INFINITY;
991
- if (def.value > curr) if (def.inclusive) bag.minimum = def.value;
992
- else bag.exclusiveMinimum = def.value;
997
+ if (def.value > curr) {
998
+ if (def.inclusive) bag.minimum = def.value;
999
+ else bag.exclusiveMinimum = def.value;
1000
+ }
993
1001
  });
994
1002
  inst._zod.check = (payload) => {
995
1003
  if (def.inclusive ? payload.value >= def.value : payload.value > def.value) return;
@@ -2522,7 +2530,7 @@ var $ZodOptional = /*@__PURE__*/ $constructor("$ZodOptional", (inst, def) => {
2522
2530
  inst._zod.optin = "optional";
2523
2531
  inst._zod.optout = "optional";
2524
2532
  defineLazy(inst._zod, "values", () => {
2525
- return def.innerType._zod.values ? new Set([...def.innerType._zod.values, void 0]) : void 0;
2533
+ return def.innerType._zod.values ? /* @__PURE__ */ new Set([...def.innerType._zod.values, void 0]) : void 0;
2526
2534
  });
2527
2535
  defineLazy(inst._zod, "pattern", () => {
2528
2536
  const pattern = def.innerType._zod.pattern;
@@ -2556,7 +2564,7 @@ var $ZodNullable = /*@__PURE__*/ $constructor("$ZodNullable", (inst, def) => {
2556
2564
  return pattern ? new RegExp(`^(${cleanRegex(pattern.source)}|null)$`) : void 0;
2557
2565
  });
2558
2566
  defineLazy(inst._zod, "values", () => {
2559
- return def.innerType._zod.values ? new Set([...def.innerType._zod.values, null]) : void 0;
2567
+ return def.innerType._zod.values ? /* @__PURE__ */ new Set([...def.innerType._zod.values, null]) : void 0;
2560
2568
  });
2561
2569
  inst._zod.parse = (payload, ctx) => {
2562
2570
  if (payload.value === null) return payload;
@@ -3490,8 +3498,10 @@ function finalize$1(ctx, schema) {
3490
3498
  defs[seen.defId] = seen.def;
3491
3499
  }
3492
3500
  }
3493
- if (ctx.external) {} else if (Object.keys(defs).length > 0) if (ctx.target === "draft-2020-12") result.$defs = defs;
3494
- else result.definitions = defs;
3501
+ if (ctx.external) {} else if (Object.keys(defs).length > 0) {
3502
+ if (ctx.target === "draft-2020-12") result.$defs = defs;
3503
+ else result.definitions = defs;
3504
+ }
3495
3505
  try {
3496
3506
  const finalized = JSON.parse(JSON.stringify(result));
3497
3507
  Object.defineProperty(finalized, "~standard", {
@@ -3604,16 +3614,18 @@ var numberProcessor = (schema, ctx, _json, _params) => {
3604
3614
  const exMin = typeof exclusiveMinimum === "number" && exclusiveMinimum >= (minimum ?? Number.NEGATIVE_INFINITY);
3605
3615
  const exMax = typeof exclusiveMaximum === "number" && exclusiveMaximum <= (maximum ?? Number.POSITIVE_INFINITY);
3606
3616
  const legacy = ctx.target === "draft-04" || ctx.target === "openapi-3.0";
3607
- if (exMin) if (legacy) {
3608
- json.minimum = exclusiveMinimum;
3609
- json.exclusiveMinimum = true;
3610
- } else json.exclusiveMinimum = exclusiveMinimum;
3611
- else if (typeof minimum === "number") json.minimum = minimum;
3612
- if (exMax) if (legacy) {
3613
- json.maximum = exclusiveMaximum;
3614
- json.exclusiveMaximum = true;
3615
- } else json.exclusiveMaximum = exclusiveMaximum;
3616
- else if (typeof maximum === "number") json.maximum = maximum;
3617
+ if (exMin) {
3618
+ if (legacy) {
3619
+ json.minimum = exclusiveMinimum;
3620
+ json.exclusiveMinimum = true;
3621
+ } else json.exclusiveMinimum = exclusiveMinimum;
3622
+ } else if (typeof minimum === "number") json.minimum = minimum;
3623
+ if (exMax) {
3624
+ if (legacy) {
3625
+ json.maximum = exclusiveMaximum;
3626
+ json.exclusiveMaximum = true;
3627
+ } else json.exclusiveMaximum = exclusiveMaximum;
3628
+ } else if (typeof maximum === "number") json.maximum = maximum;
3617
3629
  if (typeof multipleOf === "number") json.multipleOf = multipleOf;
3618
3630
  };
3619
3631
  var booleanProcessor = (_schema, _ctx, json, _params) => {
@@ -3634,9 +3646,10 @@ var literalProcessor = (schema, ctx, json, _params) => {
3634
3646
  const vals = [];
3635
3647
  for (const val of def.values) if (val === void 0) {
3636
3648
  if (ctx.unrepresentable === "throw") throw new Error("Literal `undefined` cannot be represented in JSON Schema");
3637
- } else if (typeof val === "bigint") if (ctx.unrepresentable === "throw") throw new Error("BigInt literals cannot be represented in JSON Schema");
3638
- else vals.push(Number(val));
3639
- else vals.push(val);
3649
+ } else if (typeof val === "bigint") {
3650
+ if (ctx.unrepresentable === "throw") throw new Error("BigInt literals cannot be represented in JSON Schema");
3651
+ else vals.push(Number(val));
3652
+ } else vals.push(val);
3640
3653
  if (vals.length === 0) {} else if (vals.length === 1) {
3641
3654
  const val = vals[0];
3642
3655
  json.type = val === null ? "null" : typeof val;
@@ -5051,7 +5064,7 @@ function extendedProfileToFace(profile) {
5051
5064
  }
5052
5065
  //#endregion
5053
5066
  //#region src/specs/profile.ts
5054
- var EXTENDED_PROFILE_KIND_SET = new Set([
5067
+ var EXTENDED_PROFILE_KIND_SET = /* @__PURE__ */ new Set([
5055
5068
  "L_SHAPE",
5056
5069
  "T_SHAPE",
5057
5070
  "U_SHAPE",
@@ -5275,7 +5288,7 @@ function profileToPolygon(profile, circleSegments = 32) {
5275
5288
  case "RECTANGULAR": {
5276
5289
  const halfW = profile.width / 2;
5277
5290
  const halfH = profile.height / 2;
5278
- return ok([
5291
+ const pts = [
5279
5292
  [
5280
5293
  -halfW,
5281
5294
  -halfH,
@@ -5296,7 +5309,8 @@ function profileToPolygon(profile, circleSegments = 32) {
5296
5309
  halfH,
5297
5310
  0
5298
5311
  ]
5299
- ]);
5312
+ ];
5313
+ return ok(pts);
5300
5314
  }
5301
5315
  case "CIRCULAR": {
5302
5316
  if (circleSegments < 3) return err(specError("PROFILE_CIRCLE_SEGMENTS", `profileToPolygon: circleSegments must be >= 3, got ${circleSegments}`));
@@ -5331,7 +5345,7 @@ function profileToPolygon(profile, circleSegments = 32) {
5331
5345
  [-halfW, -flangeInnerY]
5332
5346
  ];
5333
5347
  const r = profile.filletRadius ?? 0;
5334
- const rootCorners = new Set([
5348
+ const rootCorners = /* @__PURE__ */ new Set([
5335
5349
  3,
5336
5350
  4,
5337
5351
  9,
@@ -5373,13 +5387,15 @@ function beamToSolid(spec) {
5373
5387
  var _usingCtx3 = _usingCtx();
5374
5388
  const faceResult = extendedProfileToFace(spec.profile);
5375
5389
  if (!faceResult.ok) return err(faceResult.error);
5376
- const prismResult = extrude(_usingCtx3.u(faceResult.value), [
5390
+ const face = _usingCtx3.u(faceResult.value);
5391
+ const prismResult = extrude(face, [
5377
5392
  0,
5378
5393
  0,
5379
5394
  spec.length
5380
5395
  ]);
5381
5396
  if (!prismResult.ok) return err(fromBrepError(prismResult.error, "BEAM_EXTRUDE_FAILED", "Failed to extrude beam profile"));
5382
- const solid = rotate(_usingCtx3.u(prismResult.value), 90, { axis: [
5397
+ const prism = _usingCtx3.u(prismResult.value);
5398
+ const solid = rotate(prism, 90, { axis: [
5383
5399
  0,
5384
5400
  1,
5385
5401
  0
@@ -5396,13 +5412,15 @@ function beamToSolid(spec) {
5396
5412
  }
5397
5413
  const profilePtsResult = profileToPolygon(spec.profile);
5398
5414
  if (!profilePtsResult.ok) return err(profilePtsResult.error);
5399
- const profileResult = polygon(profilePtsResult.value.map(([px, py]) => [
5415
+ const profilePts = profilePtsResult.value.map(([px, py]) => [
5400
5416
  0,
5401
5417
  px,
5402
5418
  py
5403
- ]));
5419
+ ]);
5420
+ const profileResult = polygon(profilePts);
5404
5421
  if (!profileResult.ok) return err(fromBrepError(profileResult.error, "BEAM_PROFILE_FAILED", "Failed to create beam profile"));
5405
- const solidResult = extrude(_usingCtx$16.u(profileResult.value), [
5422
+ const profile = _usingCtx$16.u(profileResult.value);
5423
+ const solidResult = extrude(profile, [
5406
5424
  spec.length,
5407
5425
  0,
5408
5426
  0
@@ -5430,7 +5448,8 @@ function columnToSolid(spec) {
5430
5448
  var _usingCtx3 = _usingCtx();
5431
5449
  const faceResult = extendedProfileToFace(spec.profile);
5432
5450
  if (!faceResult.ok) return err(faceResult.error);
5433
- const extResult = extrude(_usingCtx3.u(faceResult.value), [
5451
+ const face = _usingCtx3.u(faceResult.value);
5452
+ const extResult = extrude(face, [
5434
5453
  0,
5435
5454
  0,
5436
5455
  spec.height
@@ -5452,7 +5471,8 @@ function columnToSolid(spec) {
5452
5471
  const profilePts = profilePtsResult.value;
5453
5472
  const profileResult = polygon(profilePts);
5454
5473
  if (!profileResult.ok) return err(fromBrepError(profileResult.error, "COLUMN_PROFILE_FAILED", "Failed to create column profile"));
5455
- const solidResult = extrude(_usingCtx$15.u(profileResult.value), [
5474
+ const profile = _usingCtx$15.u(profileResult.value);
5475
+ const solidResult = extrude(profile, [
5456
5476
  0,
5457
5477
  0,
5458
5478
  spec.height
@@ -5472,7 +5492,7 @@ function columnToSolid(spec) {
5472
5492
  }
5473
5493
  //#endregion
5474
5494
  //#region src/elementFns/openingFns.ts
5475
- var EPSILON_MM$1 = 1;
5495
+ var EPSILON_MM = 1;
5476
5496
  function openingToSolid(spec, wallThickness) {
5477
5497
  try {
5478
5498
  var _usingCtx$14 = _usingCtx();
@@ -5481,7 +5501,7 @@ function openingToSolid(spec, wallThickness) {
5481
5501
  if (wallThickness <= 0) return err(specError("OPENING_ZERO_WALL_THICKNESS", "Wall thickness must be positive"));
5482
5502
  const x0 = spec.offsetAlongWall;
5483
5503
  const yLow = -1;
5484
- const yHigh = wallThickness + EPSILON_MM$1;
5504
+ const yHigh = wallThickness + EPSILON_MM;
5485
5505
  const zLow = spec.offsetFromFloor;
5486
5506
  const zHigh = spec.offsetFromFloor + spec.height;
5487
5507
  const profileResult = polygon([
@@ -5507,7 +5527,8 @@ function openingToSolid(spec, wallThickness) {
5507
5527
  ]
5508
5528
  ]);
5509
5529
  if (!profileResult.ok) return err(fromBrepError(profileResult.error, "OPENING_PROFILE_FAILED", "Failed to create opening profile"));
5510
- const solidResult = extrude(_usingCtx$14.u(profileResult.value), [
5530
+ const profile = _usingCtx$14.u(profileResult.value);
5531
+ const solidResult = extrude(profile, [
5511
5532
  spec.width,
5512
5533
  0,
5513
5534
  0
@@ -5527,7 +5548,6 @@ function openingToSolid(spec, wallThickness) {
5527
5548
  }
5528
5549
  //#endregion
5529
5550
  //#region src/elementFns/slabOpeningFns.ts
5530
- var EPSILON_MM = 1;
5531
5551
  function slabOpeningToSolid(spec, slabThickness) {
5532
5552
  try {
5533
5553
  var _usingCtx$13 = _usingCtx();
@@ -5562,10 +5582,11 @@ function slabOpeningToSolid(spec, slabThickness) {
5562
5582
  ]
5563
5583
  ]);
5564
5584
  if (!profileResult.ok) return err(fromBrepError(profileResult.error, "SLAB_OPENING_PROFILE_FAILED", "Failed to create slab opening profile"));
5565
- const solidResult = extrude(_usingCtx$13.u(profileResult.value), [
5585
+ const profile = _usingCtx$13.u(profileResult.value);
5586
+ const solidResult = extrude(profile, [
5566
5587
  0,
5567
5588
  0,
5568
- slabThickness + 2 * EPSILON_MM
5589
+ slabThickness + 2
5569
5590
  ]);
5570
5591
  if (!solidResult.ok) return err(fromBrepError(solidResult.error, "SLAB_OPENING_EXTRUDE_FAILED", "Failed to extrude slab opening profile"));
5571
5592
  const solid = solidResult.value;
@@ -5612,7 +5633,8 @@ function spaceToSolid(spec) {
5612
5633
  ]
5613
5634
  ]);
5614
5635
  if (!profileResult.ok) return err(fromBrepError(profileResult.error, "SPACE_PROFILE_FAILED", "Failed to create space footprint"));
5615
- const solidResult = extrude(_usingCtx$12.u(profileResult.value), [
5636
+ const profile = _usingCtx$12.u(profileResult.value);
5637
+ const solidResult = extrude(profile, [
5616
5638
  0,
5617
5639
  0,
5618
5640
  height
@@ -5667,7 +5689,8 @@ function flatRoof(spec) {
5667
5689
  ]
5668
5690
  ]);
5669
5691
  if (!face.ok) return err(fromBrepError(face.error, "ROOF_PROFILE_FAILED", "Failed to create roof profile"));
5670
- const solid = extrude(_usingCtx$11.u(face.value), [
5692
+ const profile = _usingCtx$11.u(face.value);
5693
+ const solid = extrude(profile, [
5671
5694
  0,
5672
5695
  0,
5673
5696
  thickness
@@ -5708,7 +5731,8 @@ function shedRoof(spec, pitch) {
5708
5731
  ]
5709
5732
  ]);
5710
5733
  if (!face.ok) return err(fromBrepError(face.error, "ROOF_PROFILE_FAILED", "Failed to create shed profile"));
5711
- const solid = extrude(_usingCtx3.u(face.value), [
5734
+ const profile = _usingCtx3.u(face.value);
5735
+ const solid = extrude(profile, [
5712
5736
  length,
5713
5737
  0,
5714
5738
  0
@@ -5754,7 +5778,8 @@ function gableRoof(spec, pitch) {
5754
5778
  ]
5755
5779
  ]);
5756
5780
  if (!face.ok) return err(fromBrepError(face.error, "ROOF_PROFILE_FAILED", "Failed to create gable profile"));
5757
- const solid = extrude(_usingCtx4.u(face.value), [
5781
+ const profile = _usingCtx4.u(face.value);
5782
+ const solid = extrude(profile, [
5758
5783
  length,
5759
5784
  0,
5760
5785
  0
@@ -5895,7 +5920,8 @@ function boxSolid(sizeX, sizeY, sizeZ) {
5895
5920
  ]
5896
5921
  ]);
5897
5922
  if (!profileResult.ok) return err(fromBrepError(profileResult.error, "CURTAIN_WALL_PROFILE_FAILED", "Failed to create component profile"));
5898
- const solidResult = extrude(_usingCtx$10.u(profileResult.value), [
5923
+ const profile = _usingCtx$10.u(profileResult.value);
5924
+ const solidResult = extrude(profile, [
5899
5925
  0,
5900
5926
  0,
5901
5927
  sizeZ
@@ -6037,7 +6063,8 @@ function footingToSolid(spec) {
6037
6063
  ]
6038
6064
  ]);
6039
6065
  if (!profileResult.ok) return err(fromBrepError(profileResult.error, "FOOTING_PROFILE_FAILED", "Failed to create footing profile"));
6040
- const solidResult = extrude(_usingCtx$9.u(profileResult.value), [
6066
+ const profile = _usingCtx$9.u(profileResult.value);
6067
+ const solidResult = extrude(profile, [
6041
6068
  0,
6042
6069
  0,
6043
6070
  thickness
@@ -6063,7 +6090,8 @@ function pileToSolid(spec) {
6063
6090
  var _usingCtx4 = _usingCtx();
6064
6091
  const faceResult = extendedProfileToFace(spec.profile);
6065
6092
  if (!faceResult.ok) return err(faceResult.error);
6066
- const extResult = extrude(_usingCtx4.u(faceResult.value), [
6093
+ const face = _usingCtx4.u(faceResult.value);
6094
+ const extResult = extrude(face, [
6067
6095
  0,
6068
6096
  0,
6069
6097
  spec.length
@@ -6085,7 +6113,8 @@ function pileToSolid(spec) {
6085
6113
  const profilePts = profilePtsResult.value;
6086
6114
  const profileResult = polygon(profilePts);
6087
6115
  if (!profileResult.ok) return err(fromBrepError(profileResult.error, "PILE_PROFILE_FAILED", "Failed to create pile profile"));
6088
- const solidResult = extrude(_usingCtx3.u(profileResult.value), [
6116
+ const profile = _usingCtx3.u(profileResult.value);
6117
+ const solidResult = extrude(profile, [
6089
6118
  0,
6090
6119
  0,
6091
6120
  spec.length
@@ -6132,7 +6161,8 @@ function panelRailing(spec) {
6132
6161
  ]
6133
6162
  ]);
6134
6163
  if (!profileResult.ok) return err(fromBrepError(profileResult.error, "RAILING_PROFILE_FAILED", "Failed to create railing profile"));
6135
- const solidResult = extrude(_usingCtx$8.u(profileResult.value), [
6164
+ const profile = _usingCtx$8.u(profileResult.value);
6165
+ const solidResult = extrude(profile, [
6136
6166
  length,
6137
6167
  0,
6138
6168
  0
@@ -6238,7 +6268,8 @@ function coveringToSolid(spec) {
6238
6268
  ]
6239
6269
  ]);
6240
6270
  if (!profileResult.ok) return err(fromBrepError(profileResult.error, "COVERING_PROFILE_FAILED", "Failed to create covering profile"));
6241
- const solidResult = extrude(_usingCtx$7.u(profileResult.value), [
6271
+ const profile = _usingCtx$7.u(profileResult.value);
6272
+ const solidResult = extrude(profile, [
6242
6273
  0,
6243
6274
  0,
6244
6275
  thickness
@@ -6265,6 +6296,7 @@ var BimModel = class {
6265
6296
  #counter = makeLocalIdCounter();
6266
6297
  #projectId = null;
6267
6298
  #modelScope = "";
6299
+ #usedStableKeys = /* @__PURE__ */ new Set();
6268
6300
  init(spec) {
6269
6301
  if (this.#projectId !== null) return err(specError("DUPLICATE_PROJECT", "BimModel.init() called twice — only one project per model"));
6270
6302
  this.#modelScope = spec.projectId ?? `${spec.name}::${spec.description ?? ""}`;
@@ -6282,24 +6314,43 @@ var BimModel = class {
6282
6314
  addSite(spec) {
6283
6315
  return this.#makeElement("SITE", spec, null);
6284
6316
  }
6285
- addBuilding(spec) {
6286
- return this.#makeElement("BUILDING", spec, null);
6287
- }
6288
- addStorey(spec) {
6289
- return this.#makeElement("STOREY", spec, null);
6290
- }
6291
- addWall(spec) {
6317
+ addBuilding(spec, options) {
6318
+ return this.#makeElement("BUILDING", spec, null, options?.stableKey);
6319
+ }
6320
+ addStorey(spec, options) {
6321
+ return this.#makeElement("STOREY", spec, null, options?.stableKey);
6322
+ }
6323
+ /** Reject a duplicate stableKey BEFORE any geometry is built, so the
6324
+ * Result-returning adders never allocate a solid they cannot store. */
6325
+ #checkStableKey(options) {
6326
+ const key = options?.stableKey;
6327
+ if (key !== void 0 && this.#usedStableKeys.has(key)) return err(specError("DUPLICATE_STABLE_KEY", `BimModel: duplicate stableKey '${key}'`));
6328
+ return ok(void 0);
6329
+ }
6330
+ #checkOpeningKeys(options) {
6331
+ const filler = this.#checkStableKey(options);
6332
+ if (!filler.ok) return filler;
6333
+ const opening = this.#checkStableKey({ stableKey: options?.openingStableKey });
6334
+ if (!opening.ok) return opening;
6335
+ if (options?.stableKey !== void 0 && options.stableKey === options.openingStableKey) return err(specError("DUPLICATE_STABLE_KEY", `BimModel: stableKey and openingStableKey are both '${options.stableKey}'`));
6336
+ return ok(void 0);
6337
+ }
6338
+ addWall(spec, options) {
6339
+ const keyCheck = this.#checkStableKey(options);
6340
+ if (!keyCheck.ok) return keyCheck;
6292
6341
  const geomResult = wallToSolid(spec);
6293
6342
  if (!geomResult.ok) return err(geomResult.error);
6294
- const id = this.#makeElement("WALL", spec, geomResult.value);
6343
+ const id = this.#makeElement("WALL", spec, geomResult.value, options?.stableKey);
6295
6344
  this.#associateMaterial(id, spec);
6296
6345
  this.#associateClassification(id, spec);
6297
6346
  return ok(id);
6298
6347
  }
6299
- addSlab(spec) {
6348
+ addSlab(spec, options) {
6349
+ const keyCheck = this.#checkStableKey(options);
6350
+ if (!keyCheck.ok) return keyCheck;
6300
6351
  const geomResult = slabToSolid(spec);
6301
6352
  if (!geomResult.ok) return err(geomResult.error);
6302
- const id = this.#makeElement("SLAB", spec, geomResult.value);
6353
+ const id = this.#makeElement("SLAB", spec, geomResult.value, options?.stableKey);
6303
6354
  this.#associateMaterial(id, spec);
6304
6355
  this.#associateClassification(id, spec);
6305
6356
  return ok(id);
@@ -6575,7 +6626,9 @@ var BimModel = class {
6575
6626
  });
6576
6627
  return ok(id);
6577
6628
  }
6578
- addDoor(spec) {
6629
+ addDoor(spec, options) {
6630
+ const keyCheck = this.#checkOpeningKeys(options);
6631
+ if (!keyCheck.ok) return keyCheck;
6579
6632
  const wall = this.#elements.get(spec.wallLocalId);
6580
6633
  if (wall === void 0 || wall.category !== "WALL") return err(specError("DOOR_WALL_NOT_FOUND", `No wall found for localId ${spec.wallLocalId}`));
6581
6634
  if (spec.offsetAlongWall + spec.width > wall.spec.length) return err(specError("DOOR_EXCEEDS_WALL_BOUNDS", "Door (offsetAlongWall + width) exceeds wall length"));
@@ -6590,13 +6643,13 @@ var BimModel = class {
6590
6643
  const cutResult = this.#cutWallGeometry(wall, openingSpec);
6591
6644
  if (!cutResult.ok) return err(cutResult.error);
6592
6645
  this.#replaceWallGeometry(wall, cutResult.value);
6593
- const openingId = this.#makeElement("OPENING", openingSpec, null);
6646
+ const openingId = this.#makeElement("OPENING", openingSpec, null, options?.openingStableKey);
6594
6647
  this.#makeRel({
6595
6648
  kind: "VOIDS_WALL",
6596
6649
  wallLocalId: spec.wallLocalId,
6597
6650
  openingLocalId: openingId
6598
6651
  });
6599
- const doorId = this.#makeElement("DOOR", spec, null);
6652
+ const doorId = this.#makeElement("DOOR", spec, null, options?.stableKey);
6600
6653
  this.#makeRel({
6601
6654
  kind: "FILLS_OPENING",
6602
6655
  openingLocalId: openingId,
@@ -6609,7 +6662,9 @@ var BimModel = class {
6609
6662
  });
6610
6663
  return ok(doorId);
6611
6664
  }
6612
- addWindow(spec) {
6665
+ addWindow(spec, options) {
6666
+ const keyCheck = this.#checkOpeningKeys(options);
6667
+ if (!keyCheck.ok) return keyCheck;
6613
6668
  const wall = this.#elements.get(spec.wallLocalId);
6614
6669
  if (wall === void 0 || wall.category !== "WALL") return err(specError("WINDOW_WALL_NOT_FOUND", `No wall found for localId ${spec.wallLocalId}`));
6615
6670
  if (spec.offsetAlongWall + spec.width > wall.spec.length) return err(specError("WINDOW_EXCEEDS_WALL_BOUNDS", "Window (offsetAlongWall + width) exceeds wall length"));
@@ -6624,13 +6679,13 @@ var BimModel = class {
6624
6679
  const cutResult = this.#cutWallGeometry(wall, openingSpec);
6625
6680
  if (!cutResult.ok) return err(cutResult.error);
6626
6681
  this.#replaceWallGeometry(wall, cutResult.value);
6627
- const openingId = this.#makeElement("OPENING", openingSpec, null);
6682
+ const openingId = this.#makeElement("OPENING", openingSpec, null, options?.openingStableKey);
6628
6683
  this.#makeRel({
6629
6684
  kind: "VOIDS_WALL",
6630
6685
  wallLocalId: spec.wallLocalId,
6631
6686
  openingLocalId: openingId
6632
6687
  });
6633
- const windowId = this.#makeElement("WINDOW", spec, null);
6688
+ const windowId = this.#makeElement("WINDOW", spec, null, options?.stableKey);
6634
6689
  this.#makeRel({
6635
6690
  kind: "FILLS_OPENING",
6636
6691
  openingLocalId: openingId,
@@ -6643,7 +6698,9 @@ var BimModel = class {
6643
6698
  });
6644
6699
  return ok(windowId);
6645
6700
  }
6646
- addSlabOpening(input) {
6701
+ addSlabOpening(input, options) {
6702
+ const keyCheck = this.#checkStableKey(options);
6703
+ if (!keyCheck.ok) return keyCheck;
6647
6704
  const slab = this.#elements.get(input.slabLocalId);
6648
6705
  if (slab === void 0 || slab.category !== "SLAB") return err(specError("SLAB_OPENING_SLAB_NOT_FOUND", `No slab found for localId ${input.slabLocalId}`));
6649
6706
  if (input.offsetX + input.sizeX > slab.spec.length) return err(specError("SLAB_OPENING_EXCEEDS_SLAB_BOUNDS", "Opening (offsetX + sizeX) exceeds slab length"));
@@ -6673,7 +6730,7 @@ var BimModel = class {
6673
6730
  const cutResult = this.#cutSlabGeometry(slab, openingSpec);
6674
6731
  if (!cutResult.ok) return err(cutResult.error);
6675
6732
  this.#replaceSlabGeometry(slab, cutResult.value);
6676
- const openingId = this.#makeElement("OPENING", openingSpec, null);
6733
+ const openingId = this.#makeElement("OPENING", openingSpec, null, options?.stableKey);
6677
6734
  this.#makeRel({
6678
6735
  kind: "VOIDS_SLAB",
6679
6736
  slabLocalId: input.slabLocalId,
@@ -6918,10 +6975,14 @@ var BimModel = class {
6918
6975
  getAllRelationships() {
6919
6976
  return [...this.#relationships.values()];
6920
6977
  }
6921
- #makeElement(category, spec, geometry) {
6978
+ #makeElement(category, spec, geometry, stableKey) {
6922
6979
  const localId = this.#counter.next();
6980
+ if (stableKey !== void 0) {
6981
+ if (this.#usedStableKeys.has(stableKey)) throw new Error(`BimModel: duplicate stableKey '${stableKey}'`);
6982
+ this.#usedStableKeys.add(stableKey);
6983
+ }
6923
6984
  const el = {
6924
- guid: deriveIfcGuidSync(makeElementKey(this.#modelScope, category, localId)),
6985
+ guid: deriveIfcGuidSync(stableKey !== void 0 ? `elem:${this.#modelScope}:${stableKey}` : makeElementKey(this.#modelScope, category, localId)),
6925
6986
  localId,
6926
6987
  category,
6927
6988
  spec,
@@ -7168,9 +7229,11 @@ function stairFlightToSolid(spec) {
7168
7229
  if (spec.riserHeight <= 0) return err(specError("STAIR_FLIGHT_ZERO_RISER", "Stair flight riserHeight must be positive"));
7169
7230
  if (spec.treadLength <= 0) return err(specError("STAIR_FLIGHT_ZERO_TREAD", "Stair flight treadLength must be positive"));
7170
7231
  if (!Number.isInteger(spec.numberOfRisers) || spec.numberOfRisers < 1) return err(specError("STAIR_FLIGHT_BAD_RISERS", "Stair flight numberOfRisers must be a positive integer"));
7171
- const profileResult = polygon(buildSilhouette$1(spec.numberOfRisers, spec.riserHeight, spec.treadLength));
7232
+ const silhouette = buildSilhouette$1(spec.numberOfRisers, spec.riserHeight, spec.treadLength);
7233
+ const profileResult = polygon(silhouette);
7172
7234
  if (!profileResult.ok) return err(fromBrepError(profileResult.error, "STAIR_FLIGHT_PROFILE_FAILED", "Failed to create stair flight silhouette profile"));
7173
- const solidResult = extrude(_usingCtx$5.u(profileResult.value), [
7235
+ const profile = _usingCtx$5.u(profileResult.value);
7236
+ const solidResult = extrude(profile, [
7174
7237
  0,
7175
7238
  spec.width,
7176
7239
  0
@@ -7326,7 +7389,7 @@ function isIfcSchema(value) {
7326
7389
  * linear-infrastructure entities). Entities absent from every per-schema set are
7327
7390
  * treated as schema-agnostic and supported everywhere (open-world default).
7328
7391
  */
7329
- var IFC4X3_ONLY_ENTITIES = new Set([
7392
+ var IFC4X3_ONLY_ENTITIES = /* @__PURE__ */ new Set([
7330
7393
  "IfcAlignment",
7331
7394
  "IfcAlignmentHorizontal",
7332
7395
  "IfcAlignmentVertical",
@@ -7406,7 +7469,8 @@ var IfcWriter = class IfcWriter {
7406
7469
  try {
7407
7470
  const api = new IfcAPI();
7408
7471
  await initIfcApi(api);
7409
- return ok(new IfcWriter(api, api.CreateModel({ schema: fileSchemaString(ifcSchema) }), mvdViewDefinition, header));
7472
+ const modelId = api.CreateModel({ schema: fileSchemaString(ifcSchema) });
7473
+ return ok(new IfcWriter(api, modelId, mvdViewDefinition, header));
7410
7474
  } catch (e) {
7411
7475
  return err(ifcError("IFC_INIT_FAILED", "Failed to initialize web-ifc", e));
7412
7476
  }
@@ -7460,8 +7524,10 @@ var IfcWriter = class IfcWriter {
7460
7524
  let head = new TextDecoder().decode(bytes.subarray(0, HEADER_SCAN));
7461
7525
  if (FILE_NAME_RE.test(head)) head = head.replace(FILE_NAME_RE, (_m, prefix, systems) => `${prefix}(${stepString(this.#author)}),(${stepString(this.#organization)}),${systems},${stepString("")})`);
7462
7526
  else console.warn("IfcWriter: FILE_NAME null-field pattern not found; author/organization/authorization left unpatched");
7463
- if (this.#mvdViewDefinition.length > 0) if (VIEW_DEFINITION_RE.test(head)) head = head.replace(VIEW_DEFINITION_RE, `ViewDefinition [${this.#mvdViewDefinition}]`);
7464
- else console.warn(`IfcWriter: FILE_DESCRIPTION ViewDefinition not found; MVD "${this.#mvdViewDefinition}" not declared`);
7527
+ if (this.#mvdViewDefinition.length > 0) {
7528
+ if (VIEW_DEFINITION_RE.test(head)) head = head.replace(VIEW_DEFINITION_RE, `ViewDefinition [${this.#mvdViewDefinition}]`);
7529
+ else console.warn(`IfcWriter: FILE_DESCRIPTION ViewDefinition not found; MVD "${this.#mvdViewDefinition}" not declared`);
7530
+ }
7465
7531
  const patchedHeadBytes = new TextEncoder().encode(head);
7466
7532
  const tail = bytes.subarray(HEADER_SCAN);
7467
7533
  const out = new Uint8Array(patchedHeadBytes.byteLength + tail.byteLength);
@@ -9380,9 +9446,11 @@ function rampFlightToSolid(spec) {
9380
9446
  if (spec.slope <= 0) return err(specError("RAMP_FLIGHT_ZERO_SLOPE", "Ramp flight slope must be positive"));
9381
9447
  if (spec.thickness <= 0) return err(specError("RAMP_FLIGHT_ZERO_THICKNESS", "Ramp flight thickness must be positive"));
9382
9448
  const rise = spec.length * spec.slope;
9383
- const profileResult = polygon(buildSilhouette(spec.length, rise, spec.thickness));
9449
+ const silhouette = buildSilhouette(spec.length, rise, spec.thickness);
9450
+ const profileResult = polygon(silhouette);
9384
9451
  if (!profileResult.ok) return err(fromBrepError(profileResult.error, "RAMP_FLIGHT_PROFILE_FAILED", "Failed to create ramp flight silhouette profile"));
9385
- const solidResult = extrude(_usingCtx$3.u(profileResult.value), [
9452
+ const profile = _usingCtx$3.u(profileResult.value);
9453
+ const solidResult = extrude(profile, [
9386
9454
  0,
9387
9455
  spec.width,
9388
9456
  0
@@ -11708,7 +11776,7 @@ function isSlabOpening(spec) {
11708
11776
  * instead, and OPENINGs are tied to their host through VOIDS rels — neither
11709
11777
  * participates in containment, so excluding them here avoids false orphans.
11710
11778
  */
11711
- var CONTAINABLE_CATEGORIES = new Set([
11779
+ var CONTAINABLE_CATEGORIES = /* @__PURE__ */ new Set([
11712
11780
  "WALL",
11713
11781
  "SLAB",
11714
11782
  "BEAM",
@@ -12505,14 +12573,15 @@ async function toIfcValidated(model, meta) {
12505
12573
  const geometry = collectGeometryIssues(model);
12506
12574
  const schema = await checkSchema(bytes);
12507
12575
  const roundTrip = await checkRoundTrip(bytes);
12576
+ const report = { issues: [
12577
+ ...integrity.issues,
12578
+ ...geometry.issues,
12579
+ ...schema.issues,
12580
+ ...roundTrip.issues
12581
+ ] };
12508
12582
  return ok({
12509
12583
  bytes,
12510
- report: { issues: [
12511
- ...integrity.issues,
12512
- ...geometry.issues,
12513
- ...schema.issues,
12514
- ...roundTrip.issues
12515
- ] }
12584
+ report
12516
12585
  });
12517
12586
  }
12518
12587
  /**
@@ -12733,7 +12802,7 @@ var SpfReader = class SpfReader {
12733
12802
  };
12734
12803
  //#endregion
12735
12804
  //#region src/import/spatialTree.ts
12736
- var CATEGORY_BY_TYPE = new Map([
12805
+ var CATEGORY_BY_TYPE = /* @__PURE__ */ new Map([
12737
12806
  [WebIFC.IFCPROJECT, "PROJECT"],
12738
12807
  [WebIFC.IFCSITE, "SITE"],
12739
12808
  [WebIFC.IFCBUILDING, "BUILDING"],
@@ -12915,7 +12984,8 @@ function reconstructExtrusion(reader, extrusionId, scale, worldTransform, diagno
12915
12984
  const solidResult = (() => {
12916
12985
  try {
12917
12986
  var _usingCtx$1 = _usingCtx();
12918
- return extrude(_usingCtx$1.u(faceResult.value), extrudeVec);
12987
+ const face = _usingCtx$1.u(faceResult.value);
12988
+ return extrude(face, extrudeVec);
12919
12989
  } catch (_) {
12920
12990
  _usingCtx$1.e = _;
12921
12991
  } finally {
@@ -12968,10 +13038,12 @@ function reconstructRevolution(reader, revolutionId, scale, worldTransform, diag
12968
13038
  const revolved = (() => {
12969
13039
  try {
12970
13040
  var _usingCtx3 = _usingCtx();
12971
- return revolve(_usingCtx3.u(faceResult.value), {
13041
+ const face = _usingCtx3.u(faceResult.value);
13042
+ const angleDeg = angleRaw * readPlaneAngleScale(reader) * (180 / Math.PI);
13043
+ return revolve(face, {
12972
13044
  at: center,
12973
13045
  axis: direction,
12974
- angle: angleRaw * readPlaneAngleScale(reader) * (180 / Math.PI)
13046
+ angle: angleDeg
12975
13047
  });
12976
13048
  } catch (_) {
12977
13049
  _usingCtx3.e = _;
@@ -13086,7 +13158,7 @@ function packBinaryStl(vertices, indices, scaleToMm) {
13086
13158
  }
13087
13159
  return new Uint8Array(buffer);
13088
13160
  }
13089
- var PARAMETRIC_PROFILE_BUILDERS = new Map([
13161
+ var PARAMETRIC_PROFILE_BUILDERS = /* @__PURE__ */ new Map([
13090
13162
  [WebIFC.IFCRECTANGLEPROFILEDEF, (f) => ({
13091
13163
  kind: "RECTANGULAR",
13092
13164
  width: f("XDim"),
@@ -13854,14 +13926,15 @@ async function fromIfc(bytes, options = {}) {
13854
13926
  }
13855
13927
  const report = appendIssues(emptyReport(), diagnostics);
13856
13928
  const applicationName = readApplicationName(reader);
13857
- return ok({
13929
+ const model = {
13858
13930
  schema: reader.schema,
13859
13931
  spatialTree: spatialRoot === null ? null : mapSpatialNode(spatialRoot),
13860
13932
  elements,
13861
13933
  byExpressId,
13862
13934
  diagnostics: report,
13863
13935
  ...applicationName !== void 0 ? { applicationName } : {}
13864
- });
13936
+ };
13937
+ return ok(model);
13865
13938
  } catch (e) {
13866
13939
  return err(importError("IMPORT_FAILED", "Unexpected failure during IFC import", e));
13867
13940
  } finally {
@@ -14888,7 +14961,7 @@ function parseSlabOpeningInput(input) {
14888
14961
  * containers (PROJECT/SITE/BUILDING/STOREY), abstract groupings, and the SPACE
14889
14962
  * category (which maps to the Space sheet, not Component) are excluded.
14890
14963
  */
14891
- var COMPONENT_CATEGORIES = new Set([
14964
+ var COMPONENT_CATEGORIES = /* @__PURE__ */ new Set([
14892
14965
  "WALL",
14893
14966
  "SLAB",
14894
14967
  "BEAM",
@@ -15513,9 +15586,10 @@ function parseIdsXml(xml) {
15513
15586
  const title = info ? firstChild(info, "title")?.text ?? "" : "";
15514
15587
  const specsContainer = firstChild(root, "specifications");
15515
15588
  if (specsContainer === void 0) return err(idsError("IDS_INVALID_SCHEMA", "IDS document has no <specifications> element"));
15589
+ const specifications = childrenNamed(specsContainer, "specification").map(parseSpecification);
15516
15590
  return ok({
15517
15591
  title,
15518
- specifications: childrenNamed(specsContainer, "specification").map(parseSpecification)
15592
+ specifications
15519
15593
  });
15520
15594
  }
15521
15595
  function parseSpecification(el) {
@@ -15872,10 +15946,7 @@ function restrictionsOf(facet) {
15872
15946
  push(facet.system);
15873
15947
  push(facet.value);
15874
15948
  break;
15875
- case "Material":
15876
- push(facet.value);
15877
- break;
15878
- case "PartOf": break;
15949
+ case "Material": push(facet.value);
15879
15950
  }
15880
15951
  return out;
15881
15952
  }
@@ -16275,7 +16346,8 @@ function parseBcfFiles(files) {
16275
16346
  topics
16276
16347
  });
16277
16348
  } catch (cause) {
16278
- return err(bcfError("BCF_PARSE_FAILED", `Failed to parse BCF container: ${cause instanceof Error ? cause.message : String(cause)}`, cause));
16349
+ const message = cause instanceof Error ? cause.message : String(cause);
16350
+ return err(bcfError("BCF_PARSE_FAILED", `Failed to parse BCF container: ${message}`, cause));
16279
16351
  }
16280
16352
  }
16281
16353
  function parseVersion(xml) {
@@ -16390,4 +16462,208 @@ function assignNum(target, key, value) {
16390
16462
  if (value !== void 0) target[key] = Number(value);
16391
16463
  }
16392
16464
  //#endregion
16393
- export { BimModel, DEFAULT_IFC_SCHEMA, DEFAULT_UNITS, IFC_SCHEMAS, PSET_PROPERTY_TYPE_TABLE, PSET_TEMPLATES, SpfReader, bcfError, checkGeometryValidity, checkModelAgainstIds as checkIds, checkModelAgainstIds, checkReferentialIntegrity, checkRoundTrip, checkSchema, countBySeverity, deriveCobieModel, deriveCobieModel as exportCobie, deriveIfcGuid, deriveIfcGuidSync, disposeImportedModel, emptyReport, extendedProfileArea, extendedProfileToFace, fileSchemaString, fromBrepError, fromIfc, geometryError, hasErrors, idsError, ifcError, importError, isExtendedProfile, isIfcSchema, isSlabOpening, isValidIfcGuid, isWallOpening, issue, makeLocalIdCounter, measureTypeFor, newIfcGuid, parseBcfFiles, parseBeamSpec, parseColumnSpec, parseCoveringSpec, parseCurtainWallSpec, parseDoorSpec, parseElementAssemblySpec, parseFootingSpec, parseIdsXml, parsePileSpec, parseProfile, parseRailingSpec, parseRampFlightSpec, parseRampSpec, parseRoofSpec, parseSlabOpeningInput, parseSlabSpec, parseSpaceSpec, parseStairFlightSpec, parseStairSpec, parseSurfaceStyleSpec, parseSystemSpec, parseWallSpec, parseWindowSpec, parseZoneSpec, placedSolids, schemaSupports, serializeBcfFiles, serializeCobieToCsv, serializeCobieToJson, setIfcWasmLocateFile, specError, templateFor, toIfc, toIfcLengthM, toIfcValidated, toLengthMm, writeClassificationRefs, writeElementAssemblyEntity, writeIfcType, writeMaterialLayerSet, writeMaterialProfileSet, writeMaterialSimple, writePresentationLayer, writeRelAggregatesElements, writeRelAssignsToGroup, writeRelConnectsElements, writeRelConnectsPathElements, writeRelNests, writeStyledItem, writeSurfaceStyle, writeSystemEntity, writeZoneEntity };
16465
+ //#region src/familiesAdapter.ts
16466
+ /**
16467
+ * brepjs-families -> BimModel adapter. Consumes a resolved element tree and
16468
+ * feeds each element's PRE-DESUGARED props into the parametric specs — the
16469
+ * spec path stays authoritative for IFC (IfcExtrudedAreaSolid + placement),
16470
+ * while the IR path serves the viewport and dedup. GlobalIds derive from
16471
+ * families key paths (stable under reordering), not insertion order.
16472
+ *
16473
+ * Scope: Storey containers, Wall/Slab elements, and wall openings — a
16474
+ * fill-role void (Door/Window family) maps onto addDoor/addWindow, which cut
16475
+ * the wall and wire IfcRelVoidsElement + IfcRelFillsElement; the opening and
16476
+ * filler GlobalIds derive from the synthesized key paths. Anonymous (non-fill)
16477
+ * voids stay IR-only and never reach the spec path.
16478
+ */
16479
+ var SPEC_DEFAULTS = {
16480
+ origin: [
16481
+ 0,
16482
+ 0,
16483
+ 0
16484
+ ],
16485
+ axisX: [
16486
+ 1,
16487
+ 0,
16488
+ 0
16489
+ ],
16490
+ axisZ: [
16491
+ 0,
16492
+ 0,
16493
+ 1
16494
+ ],
16495
+ materialName: "Default"
16496
+ };
16497
+ var GEOMETRY_PROPS = /* @__PURE__ */ new Set([
16498
+ "voids",
16499
+ "fuse",
16500
+ "transform",
16501
+ "psets"
16502
+ ]);
16503
+ /** Total of the resolved geometry's OUTER literal translate chain. The
16504
+ * transform vocabulary is translate-only, so frame differences are exact
16505
+ * subtractions. Parameter-driven translations stop the peel. */
16506
+ function peelTranslates(node) {
16507
+ const total = [
16508
+ 0,
16509
+ 0,
16510
+ 0
16511
+ ];
16512
+ let moved = false;
16513
+ let cur = node;
16514
+ while (cur.kind === "Translate") {
16515
+ const v = cur.vector;
16516
+ if (v.kind !== "Vec3Lit") break;
16517
+ total[0] += v.value[0];
16518
+ total[1] += v.value[1];
16519
+ total[2] += v.value[2];
16520
+ moved = true;
16521
+ cur = cur.target;
16522
+ }
16523
+ return {
16524
+ total,
16525
+ moved
16526
+ };
16527
+ }
16528
+ /** Fold the resolved geometry's outer translate chain into the spec placement
16529
+ * origin, so IfcLocalPlacement matches the IR world frame no matter where the
16530
+ * transform came from (family-internal or prop-level). */
16531
+ function composedOrigin(el) {
16532
+ const base = el.props["origin"] ?? [
16533
+ 0,
16534
+ 0,
16535
+ 0
16536
+ ];
16537
+ const { total, moved } = peelTranslates(el.geometry);
16538
+ if (!moved && el.props["origin"] === void 0) return void 0;
16539
+ return [
16540
+ base[0] + total[0],
16541
+ base[1] + total[1],
16542
+ base[2] + total[2]
16543
+ ];
16544
+ }
16545
+ function stripGeometryProps(props) {
16546
+ const out = {};
16547
+ for (const [k, v] of Object.entries(props)) if (!GEOMETRY_PROPS.has(k)) out[k] = v;
16548
+ return out;
16549
+ }
16550
+ function specInput(el) {
16551
+ const origin = composedOrigin(el);
16552
+ return {
16553
+ ...SPEC_DEFAULTS,
16554
+ ...stripGeometryProps(el.props),
16555
+ ...origin ? { origin } : {},
16556
+ ...collectSpecProps(el)
16557
+ };
16558
+ }
16559
+ function collectSpecProps(el) {
16560
+ const psets = el.attributes["psets"];
16561
+ const out = {};
16562
+ if (psets && typeof psets === "object") {
16563
+ const common = psets["Pset_WallCommon"];
16564
+ if (common && typeof common === "object") {
16565
+ const c = common;
16566
+ if (typeof c["IsExternal"] === "boolean") out["isExternal"] = c["IsExternal"];
16567
+ if (typeof c["FireRating"] === "string") out["fireRating"] = c["FireRating"];
16568
+ }
16569
+ }
16570
+ delete out["psets"];
16571
+ return out;
16572
+ }
16573
+ function addFill(model, fill, input, identity) {
16574
+ if (fill.type === "Door") {
16575
+ const parsed = parseDoorSpec(input);
16576
+ if (!parsed.ok) return parsed;
16577
+ return model.addDoor(parsed.value, identity);
16578
+ }
16579
+ if (fill.type === "Window") {
16580
+ const parsed = parseWindowSpec(input);
16581
+ if (!parsed.ok) return parsed;
16582
+ return model.addWindow(parsed.value, identity);
16583
+ }
16584
+ return err(specError("FAMILIES_UNSUPPORTED_FILL", `familiesToBim: no fill mapping for element type '${fill.type}' at '${fill.keyPath}'`));
16585
+ }
16586
+ /** Map a wall's synthesized Opening children onto addDoor/addWindow. The
16587
+ * wall-relative offsets come from the void geometry's frame minus the host's:
16588
+ * exact because both carry the same outer host transform. */
16589
+ function addOpenings(model, host, wallId, containerId, idByKeyPath) {
16590
+ const hostT = peelTranslates(host.geometry).total;
16591
+ for (const opening of host.children) {
16592
+ if (opening.type !== "Opening") continue;
16593
+ const fill = opening.children[0];
16594
+ if (fill === void 0) return err(specError("FAMILIES_OPENING_NO_FILL", `familiesToBim: opening '${opening.keyPath}' has no fill element`));
16595
+ const fillT = peelTranslates(opening.geometry).total;
16596
+ const added = addFill(model, fill, {
16597
+ materialName: SPEC_DEFAULTS.materialName,
16598
+ ...stripGeometryProps(fill.props),
16599
+ wallLocalId: wallId,
16600
+ offsetAlongWall: fillT[0] - hostT[0],
16601
+ offsetFromFloor: fillT[2] - hostT[2]
16602
+ }, {
16603
+ stableKey: fill.keyPath,
16604
+ openingStableKey: opening.keyPath
16605
+ });
16606
+ if (!added.ok) return added;
16607
+ model.placeIn(added.value, containerId);
16608
+ idByKeyPath.set(fill.keyPath, added.value);
16609
+ const fillsRel = model.getAllRelationships().find((r) => r.kind === "FILLS_OPENING" && r.fillerLocalId === added.value);
16610
+ if (fillsRel !== void 0) idByKeyPath.set(opening.keyPath, fillsRel.openingLocalId);
16611
+ }
16612
+ return ok(void 0);
16613
+ }
16614
+ /**
16615
+ * Project a resolved families tree into an eager BimModel. The caller owns
16616
+ * the returned model (`using`); families stays domain-neutral — this adapter
16617
+ * is where families types meet the IFC vocabulary.
16618
+ */
16619
+ function familiesToBim(root, options) {
16620
+ const model = new BimModel();
16621
+ const initResult = model.init(options.project);
16622
+ if (!initResult.ok) return initResult;
16623
+ const siteId = model.addSite({ name: options.siteName ?? "Site" });
16624
+ const buildingId = model.addBuilding({ name: options.buildingName ?? "Building" });
16625
+ model.aggregate(siteId, buildingId);
16626
+ const idByKeyPath = /* @__PURE__ */ new Map();
16627
+ const walk = (el, storeyId) => {
16628
+ let containerId = storeyId;
16629
+ if (el.type === "Storey") {
16630
+ const id = model.addStorey({
16631
+ name: el.attributes["name"] ?? el.keyPath,
16632
+ elevation: el.props["elevation"] ?? 0
16633
+ }, { stableKey: el.keyPath });
16634
+ model.aggregate(buildingId, id);
16635
+ idByKeyPath.set(el.keyPath, id);
16636
+ containerId = id;
16637
+ } else if (el.type === "Wall" || el.type === "Slab") {
16638
+ const parsed = el.type === "Wall" ? parseWallSpec(specInput(el)) : parseSlabSpec(specInput(el));
16639
+ if (!parsed.ok) return parsed;
16640
+ const added = el.type === "Wall" ? model.addWall(parsed.value, { stableKey: el.keyPath }) : model.addSlab(parsed.value, { stableKey: el.keyPath });
16641
+ if (!added.ok) return added;
16642
+ idByKeyPath.set(el.keyPath, added.value);
16643
+ if (containerId === null) return err(specError("FAMILIES_NO_STOREY", `familiesToBim: '${el.keyPath}' has no Storey ancestor — IFC elements need spatial containment`));
16644
+ model.placeIn(added.value, containerId);
16645
+ if (el.type === "Wall") {
16646
+ const opened = addOpenings(model, el, added.value, containerId, idByKeyPath);
16647
+ if (!opened.ok) return opened;
16648
+ }
16649
+ } else if (el.type === "Opening") return err(specError("FAMILIES_OPENING_OUTSIDE_WALL", `familiesToBim: opening '${el.keyPath}' is not hosted by a Wall — only wall openings are mapped`));
16650
+ else if (el.type !== "Group" && el.geometry.kind !== "Empty") return err(specError("FAMILIES_UNSUPPORTED_TYPE", `familiesToBim: no spec mapping for element type '${el.type}' at '${el.keyPath}'`));
16651
+ for (const child of el.children) {
16652
+ if (el.type === "Wall" && child.type === "Opening") continue;
16653
+ const r = walk(child, containerId);
16654
+ if (!r.ok) return r;
16655
+ }
16656
+ return ok(void 0);
16657
+ };
16658
+ const walked = walk(root, null);
16659
+ if (!walked.ok) {
16660
+ model[Symbol.dispose]();
16661
+ return walked;
16662
+ }
16663
+ return ok({
16664
+ model,
16665
+ idByKeyPath
16666
+ });
16667
+ }
16668
+ //#endregion
16669
+ export { BimModel, DEFAULT_IFC_SCHEMA, DEFAULT_UNITS, IFC_SCHEMAS, PSET_PROPERTY_TYPE_TABLE, PSET_TEMPLATES, SpfReader, bcfError, checkGeometryValidity, checkModelAgainstIds as checkIds, checkModelAgainstIds, checkReferentialIntegrity, checkRoundTrip, checkSchema, countBySeverity, deriveCobieModel, deriveCobieModel as exportCobie, deriveIfcGuid, deriveIfcGuidSync, disposeImportedModel, emptyReport, extendedProfileArea, extendedProfileToFace, familiesToBim, fileSchemaString, fromBrepError, fromIfc, geometryError, hasErrors, idsError, ifcError, importError, isExtendedProfile, isIfcSchema, isSlabOpening, isValidIfcGuid, isWallOpening, issue, makeLocalIdCounter, measureTypeFor, newIfcGuid, parseBcfFiles, parseBeamSpec, parseColumnSpec, parseCoveringSpec, parseCurtainWallSpec, parseDoorSpec, parseElementAssemblySpec, parseFootingSpec, parseIdsXml, parsePileSpec, parseProfile, parseRailingSpec, parseRampFlightSpec, parseRampSpec, parseRoofSpec, parseSlabOpeningInput, parseSlabSpec, parseSpaceSpec, parseStairFlightSpec, parseStairSpec, parseSurfaceStyleSpec, parseSystemSpec, parseWallSpec, parseWindowSpec, parseZoneSpec, placedSolids, schemaSupports, serializeBcfFiles, serializeCobieToCsv, serializeCobieToJson, setIfcWasmLocateFile, specError, templateFor, toIfc, toIfcLengthM, toIfcValidated, toLengthMm, writeClassificationRefs, writeElementAssemblyEntity, writeIfcType, writeMaterialLayerSet, writeMaterialProfileSet, writeMaterialSimple, writePresentationLayer, writeRelAggregatesElements, writeRelAssignsToGroup, writeRelConnectsElements, writeRelConnectsPathElements, writeRelNests, writeStyledItem, writeSurfaceStyle, writeSystemEntity, writeZoneEntity };