brepjs-bim 0.16.0 → 0.17.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.
@@ -7314,6 +7314,65 @@ function stairFlightToSolid(spec) {
7314
7314
  }
7315
7315
  }
7316
7316
  //#endregion
7317
+ //#region src/elementFns/rampFns.ts
7318
+ function buildSilhouette(length, rise, thickness) {
7319
+ return [
7320
+ [
7321
+ 0,
7322
+ 0,
7323
+ -thickness
7324
+ ],
7325
+ [
7326
+ length,
7327
+ 0,
7328
+ rise - thickness
7329
+ ],
7330
+ [
7331
+ length,
7332
+ 0,
7333
+ rise
7334
+ ],
7335
+ [
7336
+ 0,
7337
+ 0,
7338
+ 0
7339
+ ]
7340
+ ];
7341
+ }
7342
+ function rampFlightToSolid(spec) {
7343
+ try {
7344
+ var _usingCtx$4 = _usingCtx();
7345
+ if (spec.width <= 0) return (0, brepjs.err)(specError("RAMP_FLIGHT_ZERO_WIDTH", "Ramp flight width must be positive"));
7346
+ if (spec.length <= 0) return (0, brepjs.err)(specError("RAMP_FLIGHT_ZERO_LENGTH", "Ramp flight length must be positive"));
7347
+ if (spec.slope <= 0) return (0, brepjs.err)(specError("RAMP_FLIGHT_ZERO_SLOPE", "Ramp flight slope must be positive"));
7348
+ if (spec.thickness <= 0) return (0, brepjs.err)(specError("RAMP_FLIGHT_ZERO_THICKNESS", "Ramp flight thickness must be positive"));
7349
+ const rise = spec.length * spec.slope;
7350
+ const silhouette = buildSilhouette(spec.length, rise, spec.thickness);
7351
+ const profileResult = (0, brepjs.polygon)(silhouette);
7352
+ if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "RAMP_FLIGHT_PROFILE_FAILED", "Failed to create ramp flight silhouette profile"));
7353
+ const profile = _usingCtx$4.u(profileResult.value);
7354
+ const solidResult = (0, brepjs.extrude)(profile, [
7355
+ 0,
7356
+ spec.width,
7357
+ 0
7358
+ ]);
7359
+ if (!solidResult.ok) return (0, brepjs.err)(fromBrepError(solidResult.error, "RAMP_FLIGHT_EXTRUDE_FAILED", "Failed to extrude ramp flight silhouette"));
7360
+ const solid = solidResult.value;
7361
+ if (!(0, brepjs.isValidSolid)(solid)) {
7362
+ solid[Symbol.dispose]();
7363
+ return (0, brepjs.err)(geometryError("RAMP_FLIGHT_INVALID_SOLID", "Ramp flight solid failed validity check"));
7364
+ }
7365
+ return (0, brepjs.ok)({
7366
+ solid,
7367
+ geometrySimplified: true
7368
+ });
7369
+ } catch (_) {
7370
+ _usingCtx$4.e = _;
7371
+ } finally {
7372
+ _usingCtx$4.d();
7373
+ }
7374
+ }
7375
+ //#endregion
7317
7376
  //#region src/elementFns/placedGeometry.ts
7318
7377
  function place(solid, frame) {
7319
7378
  const result = (0, brepjs.applyMatrix)(solid, placementToMatrix(frame));
@@ -7332,10 +7391,10 @@ function disposeAll(solids) {
7332
7391
  * failure the solids already built for this call are disposed before the error is
7333
7392
  * returned, so no partial array is leaked.
7334
7393
  *
7335
- * Stairs carry no element solid (`.geometry` is null), so flight solids are built
7336
- * from `spec.flights` and placed per flight. Curtain walls return placed panels +
7337
- * mullions. Elements with no solid geometry (doors/windows/ramps/groups/spatial)
7338
- * return an empty array.
7394
+ * Stairs and ramps carry no element solid (`.geometry` is null), so flight
7395
+ * solids are built from `spec.flights` and placed per flight. Curtain walls
7396
+ * return placed panels + mullions. Elements with no solid geometry
7397
+ * (doors/windows/groups/spatial) return an empty array.
7339
7398
  */
7340
7399
  function placedSolids(el) {
7341
7400
  switch (el.category) {
@@ -7347,6 +7406,7 @@ function placedSolids(el) {
7347
7406
  case "ROOF":
7348
7407
  case "FOOTING":
7349
7408
  case "PILE":
7409
+ case "COVERING":
7350
7410
  case "RAILING": {
7351
7411
  const placed = place(el.geometry, el.spec);
7352
7412
  if (!placed.ok) return placed;
@@ -7355,29 +7415,51 @@ function placedSolids(el) {
7355
7415
  case "STAIR": {
7356
7416
  const out = [];
7357
7417
  for (const flight of el.spec.flights) try {
7358
- var _usingCtx$4 = _usingCtx();
7418
+ var _usingCtx$3 = _usingCtx();
7359
7419
  const built = stairFlightToSolid(flight);
7360
7420
  if (!built.ok) {
7361
7421
  disposeAll(out);
7362
7422
  return (0, brepjs.err)(built.error);
7363
7423
  }
7364
- const placed = place(_usingCtx$4.u(built.value.solid), flight);
7424
+ const placed = place(_usingCtx$3.u(built.value.solid), flight);
7425
+ if (!placed.ok) {
7426
+ disposeAll(out);
7427
+ return placed;
7428
+ }
7429
+ out.push(placed.value);
7430
+ } catch (_) {
7431
+ _usingCtx$3.e = _;
7432
+ } finally {
7433
+ _usingCtx$3.d();
7434
+ }
7435
+ return (0, brepjs.ok)(out);
7436
+ }
7437
+ case "RAMP": {
7438
+ const out = [];
7439
+ for (const flight of el.spec.flights) try {
7440
+ var _usingCtx3 = _usingCtx();
7441
+ const built = rampFlightToSolid(flight);
7442
+ if (!built.ok) {
7443
+ disposeAll(out);
7444
+ return (0, brepjs.err)(built.error);
7445
+ }
7446
+ const placed = place(_usingCtx3.u(built.value.solid), flight);
7365
7447
  if (!placed.ok) {
7366
7448
  disposeAll(out);
7367
7449
  return placed;
7368
7450
  }
7369
7451
  out.push(placed.value);
7370
7452
  } catch (_) {
7371
- _usingCtx$4.e = _;
7453
+ _usingCtx3.e = _;
7372
7454
  } finally {
7373
- _usingCtx$4.d();
7455
+ _usingCtx3.d();
7374
7456
  }
7375
7457
  return (0, brepjs.ok)(out);
7376
7458
  }
7377
7459
  case "CURTAIN_WALL": {
7378
7460
  const out = [];
7379
7461
  for (const c of [...el.geometry.panels, ...el.geometry.mullions]) try {
7380
- var _usingCtx3 = _usingCtx();
7462
+ var _usingCtx4 = _usingCtx();
7381
7463
  const componentLocal = place(c.solid, {
7382
7464
  origin: c.origin,
7383
7465
  axisX: [
@@ -7395,16 +7477,16 @@ function placedSolids(el) {
7395
7477
  disposeAll(out);
7396
7478
  return componentLocal;
7397
7479
  }
7398
- const placed = place(_usingCtx3.u(componentLocal.value), el.spec);
7480
+ const placed = place(_usingCtx4.u(componentLocal.value), el.spec);
7399
7481
  if (!placed.ok) {
7400
7482
  disposeAll(out);
7401
7483
  return placed;
7402
7484
  }
7403
7485
  out.push(placed.value);
7404
7486
  } catch (_) {
7405
- _usingCtx3.e = _;
7487
+ _usingCtx4.e = _;
7406
7488
  } finally {
7407
- _usingCtx3.d();
7489
+ _usingCtx4.d();
7408
7490
  }
7409
7491
  return (0, brepjs.ok)(out);
7410
7492
  }
@@ -7804,9 +7886,42 @@ function writeHeader(w, meta) {
7804
7886
  ownerHistoryId,
7805
7887
  geomContextId,
7806
7888
  geomSubContextId,
7807
- unitAssignmentId
7889
+ unitAssignmentId,
7890
+ lengthUnitId
7808
7891
  };
7809
7892
  }
7893
+ /**
7894
+ * Writes IfcProjectedCRS + IfcMapConversion georeferencing the model context
7895
+ * (GRF003). Coordinates are metres; rotation defaults to identity.
7896
+ */
7897
+ function writeMapConversion(w, crs, geomContextId, lengthUnitId) {
7898
+ const crsId = w.nextId();
7899
+ w.writeLine({
7900
+ expressID: crsId,
7901
+ type: web_ifc.IFCPROJECTEDCRS,
7902
+ Name: w.mkType(web_ifc.IFCLABEL, crs.name),
7903
+ Description: crs.description !== void 0 ? w.mkType(web_ifc.IFCTEXT, crs.description) : null,
7904
+ GeodeticDatum: crs.geodeticDatum !== void 0 ? w.mkType(web_ifc.IFCIDENTIFIER, crs.geodeticDatum) : null,
7905
+ VerticalDatum: crs.verticalDatum !== void 0 ? w.mkType(web_ifc.IFCIDENTIFIER, crs.verticalDatum) : null,
7906
+ MapProjection: crs.mapProjection !== void 0 ? w.mkType(web_ifc.IFCIDENTIFIER, crs.mapProjection) : null,
7907
+ MapZone: crs.mapZone !== void 0 ? w.mkType(web_ifc.IFCIDENTIFIER, crs.mapZone) : null,
7908
+ MapUnit: w.ref(lengthUnitId)
7909
+ });
7910
+ const conversionId = w.nextId();
7911
+ w.writeLine({
7912
+ expressID: conversionId,
7913
+ type: web_ifc.IFCMAPCONVERSION,
7914
+ SourceCRS: w.ref(geomContextId),
7915
+ TargetCRS: w.ref(crsId),
7916
+ Eastings: w.mkType(web_ifc.IFCLENGTHMEASURE, crs.eastings ?? 0),
7917
+ Northings: w.mkType(web_ifc.IFCLENGTHMEASURE, crs.northings ?? 0),
7918
+ OrthogonalHeight: w.mkType(web_ifc.IFCLENGTHMEASURE, crs.orthogonalHeight ?? 0),
7919
+ XAxisAbscissa: crs.xAxisAbscissa !== void 0 ? w.mkType(web_ifc.IFCREAL, crs.xAxisAbscissa) : null,
7920
+ XAxisOrdinate: crs.xAxisOrdinate !== void 0 ? w.mkType(web_ifc.IFCREAL, crs.xAxisOrdinate) : null,
7921
+ Scale: crs.scale !== void 0 ? w.mkType(web_ifc.IFCREAL, crs.scale) : null
7922
+ });
7923
+ return crsId;
7924
+ }
7810
7925
  function writeAxis2Placement3D(w, origin = [
7811
7926
  0,
7812
7927
  0,
@@ -9458,65 +9573,6 @@ function writeRelContainedInSpatialStructure(w, guid, ownerHistoryId, relatingSt
9458
9573
  });
9459
9574
  }
9460
9575
  //#endregion
9461
- //#region src/elementFns/rampFns.ts
9462
- function buildSilhouette(length, rise, thickness) {
9463
- return [
9464
- [
9465
- 0,
9466
- 0,
9467
- -thickness
9468
- ],
9469
- [
9470
- length,
9471
- 0,
9472
- rise - thickness
9473
- ],
9474
- [
9475
- length,
9476
- 0,
9477
- rise
9478
- ],
9479
- [
9480
- 0,
9481
- 0,
9482
- 0
9483
- ]
9484
- ];
9485
- }
9486
- function rampFlightToSolid(spec) {
9487
- try {
9488
- var _usingCtx$3 = _usingCtx();
9489
- if (spec.width <= 0) return (0, brepjs.err)(specError("RAMP_FLIGHT_ZERO_WIDTH", "Ramp flight width must be positive"));
9490
- if (spec.length <= 0) return (0, brepjs.err)(specError("RAMP_FLIGHT_ZERO_LENGTH", "Ramp flight length must be positive"));
9491
- if (spec.slope <= 0) return (0, brepjs.err)(specError("RAMP_FLIGHT_ZERO_SLOPE", "Ramp flight slope must be positive"));
9492
- if (spec.thickness <= 0) return (0, brepjs.err)(specError("RAMP_FLIGHT_ZERO_THICKNESS", "Ramp flight thickness must be positive"));
9493
- const rise = spec.length * spec.slope;
9494
- const silhouette = buildSilhouette(spec.length, rise, spec.thickness);
9495
- const profileResult = (0, brepjs.polygon)(silhouette);
9496
- if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "RAMP_FLIGHT_PROFILE_FAILED", "Failed to create ramp flight silhouette profile"));
9497
- const profile = _usingCtx$3.u(profileResult.value);
9498
- const solidResult = (0, brepjs.extrude)(profile, [
9499
- 0,
9500
- spec.width,
9501
- 0
9502
- ]);
9503
- if (!solidResult.ok) return (0, brepjs.err)(fromBrepError(solidResult.error, "RAMP_FLIGHT_EXTRUDE_FAILED", "Failed to extrude ramp flight silhouette"));
9504
- const solid = solidResult.value;
9505
- if (!(0, brepjs.isValidSolid)(solid)) {
9506
- solid[Symbol.dispose]();
9507
- return (0, brepjs.err)(geometryError("RAMP_FLIGHT_INVALID_SOLID", "Ramp flight solid failed validity check"));
9508
- }
9509
- return (0, brepjs.ok)({
9510
- solid,
9511
- geometrySimplified: true
9512
- });
9513
- } catch (_) {
9514
- _usingCtx$3.e = _;
9515
- } finally {
9516
- _usingCtx$3.d();
9517
- }
9518
- }
9519
- //#endregion
9520
9576
  //#region src/ifc-writer/stairWriter.ts
9521
9577
  function writeLocalPlacement(w, origin, axisZ, axisX, parentPlacementId) {
9522
9578
  const placement3DId = writeAxis2Placement3D(w, origin.map(toIfcLengthM), axisZ, axisX);
@@ -9572,12 +9628,56 @@ function writeStairFlightEntity(w, guid, name, flight, ownerHistoryId, localPlac
9572
9628
  ObjectPlacement: w.ref(localPlacementId),
9573
9629
  Representation: w.ref(productDefinitionShapeId),
9574
9630
  Tag: null,
9575
- NumberOfRisers: w.mkType(web_ifc.IFCINTEGER, flight.numberOfRisers),
9576
- NumberOfTreads: w.mkType(web_ifc.IFCINTEGER, flight.numberOfRisers - 1),
9577
- RiserHeight: w.mkType(web_ifc.IFCPOSITIVELENGTHMEASURE, toIfcLengthM(flight.riserHeight)),
9578
- TreadLength: w.mkType(web_ifc.IFCPOSITIVELENGTHMEASURE, toIfcLengthM(flight.treadLength)),
9631
+ NumberOfRisers: null,
9632
+ NumberOfTreads: null,
9633
+ RiserHeight: null,
9634
+ TreadLength: null,
9579
9635
  PredefinedType: null
9580
9636
  });
9637
+ writeStairFlightCommonPset(w, ownerHistoryId, id, flight);
9638
+ return id;
9639
+ }
9640
+ /** Pset_StairFlightCommon: the standard home for the flight geometry data the
9641
+ * deprecated IfcStairFlight attributes used to carry. Note the standard's
9642
+ * singular `NumberOfRiser`. */
9643
+ function writeStairFlightCommonPset(w, ownerHistoryId, flightExpressId, flight) {
9644
+ const props = [
9645
+ writeSingleValue(w, "NumberOfRiser", w.mkType(web_ifc.IFCCOUNTMEASURE, flight.numberOfRisers)),
9646
+ writeSingleValue(w, "NumberOfTreads", w.mkType(web_ifc.IFCCOUNTMEASURE, flight.numberOfRisers - 1)),
9647
+ writeSingleValue(w, "RiserHeight", w.mkType(web_ifc.IFCPOSITIVELENGTHMEASURE, toIfcLengthM(flight.riserHeight))),
9648
+ writeSingleValue(w, "TreadLength", w.mkType(web_ifc.IFCPOSITIVELENGTHMEASURE, toIfcLengthM(flight.treadLength)))
9649
+ ];
9650
+ const psetId = w.nextId();
9651
+ w.writeLine({
9652
+ expressID: psetId,
9653
+ type: web_ifc.IFCPROPERTYSET,
9654
+ GlobalId: w.mkType(web_ifc.IFCGLOBALLYUNIQUEID, w.guidFor(psetId)),
9655
+ OwnerHistory: w.ref(ownerHistoryId),
9656
+ Name: w.mkType(web_ifc.IFCLABEL, "Pset_StairFlightCommon"),
9657
+ Description: null,
9658
+ HasProperties: props.map((id) => w.ref(id))
9659
+ });
9660
+ w.writeLine({
9661
+ expressID: w.nextId(),
9662
+ type: web_ifc.IFCRELDEFINESBYPROPERTIES,
9663
+ GlobalId: w.mkType(web_ifc.IFCGLOBALLYUNIQUEID, w.guidFor(psetId + 1e5)),
9664
+ OwnerHistory: w.ref(ownerHistoryId),
9665
+ Name: null,
9666
+ Description: null,
9667
+ RelatedObjects: [w.ref(flightExpressId)],
9668
+ RelatingPropertyDefinition: w.ref(psetId)
9669
+ });
9670
+ }
9671
+ function writeSingleValue(w, name, value) {
9672
+ const id = w.nextId();
9673
+ w.writeLine({
9674
+ expressID: id,
9675
+ type: web_ifc.IFCPROPERTYSINGLEVALUE,
9676
+ Name: w.mkType(web_ifc.IFCIDENTIFIER, name),
9677
+ Description: null,
9678
+ NominalValue: value,
9679
+ Unit: null
9680
+ });
9581
9681
  return id;
9582
9682
  }
9583
9683
  function writeStairEntity(w, guid, name, _predefinedType, ownerHistoryId, localPlacementId) {
@@ -11088,12 +11188,11 @@ function writeRoofBaseQuantities(w, ownerHistoryId, roofExpressId, spec) {
11088
11188
  const widthM = toIfcLengthM(spec.width);
11089
11189
  const thicknessM = toIfcLengthM(spec.thickness);
11090
11190
  const grossAreaM2 = lengthM * widthM;
11091
- const grossVolumeM3 = grossAreaM2 * thicknessM;
11191
+ grossAreaM2 * thicknessM;
11092
11192
  writeRelDefinesByProperties(w, ownerHistoryId, roofExpressId, writeElementQuantity(w, ownerHistoryId, "Qto_RoofBaseQuantities", [
11093
11193
  writeQtyArea(w, "GrossArea", grossAreaM2),
11094
11194
  writeQtyArea(w, "NetArea", grossAreaM2),
11095
- writeQtyVolume(w, "GrossVolume", grossVolumeM3),
11096
- writeQtyVolume(w, "NetVolume", grossVolumeM3)
11195
+ writeQtyArea(w, "ProjectedArea", grossAreaM2)
11097
11196
  ]));
11098
11197
  }
11099
11198
  function writeCurtainWallCommonPset(w, ownerHistoryId, curtainWallExpressId, spec) {
@@ -11120,7 +11219,7 @@ function writeFootingBaseQuantities(w, ownerHistoryId, footingExpressId, spec) {
11120
11219
  writeRelDefinesByProperties(w, ownerHistoryId, footingExpressId, writeElementQuantity(w, ownerHistoryId, "Qto_FootingBaseQuantities", [
11121
11220
  writeQtyLength(w, "Length", lengthM),
11122
11221
  writeQtyLength(w, "Width", widthM),
11123
- writeQtyLength(w, "Depth", depthM),
11222
+ writeQtyLength(w, "Height", depthM),
11124
11223
  writeQtyVolume(w, "GrossVolume", grossVolumeM3),
11125
11224
  writeQtyVolume(w, "NetVolume", grossVolumeM3)
11126
11225
  ]));
@@ -12068,71 +12167,984 @@ async function checkRoundTrip(bytes) {
12068
12167
  };
12069
12168
  }
12070
12169
  //#endregion
12071
- //#region src/serialize/toIfc.ts
12072
- async function toIfc(model, meta) {
12073
- const project = model.getProject();
12074
- if (!project) return (0, brepjs.err)(ifcError("NO_PROJECT", "BimModel has no project — call model.init() first"));
12075
- const authorName = [meta.author?.givenName, meta.author?.familyName].filter((p) => Boolean(p)).join(" ");
12076
- const writerResult = await IfcWriter.create(meta.mvdViewDefinition, meta.ifcSchema, {
12077
- author: authorName,
12078
- organization: meta.organizationName
12079
- });
12080
- if (!writerResult.ok) return writerResult;
12081
- const w = writerResult.value;
12082
- w.setModelScope(project.guid);
12083
- const elements = model.getAllElements();
12084
- const relationships = model.getAllRelationships();
12085
- const walls = model.getWalls();
12086
- const slabs = model.getSlabs();
12087
- const beams = model.getBeams();
12088
- const columns = model.getColumns();
12089
- const doors = model.getDoors();
12090
- const windows = model.getWindows();
12091
- const proxies = model.getProxies();
12092
- const spaces = model.getSpaces();
12093
- const roofs = model.getRoofs();
12094
- const curtainWalls = model.getCurtainWalls();
12095
- const footings = model.getFootings();
12096
- const piles = model.getPiles();
12097
- const stairs = model.getStairs();
12098
- const ramps = model.getRamps();
12099
- const railings = model.getRailings();
12100
- const coverings = model.getCoverings();
12101
- const assemblies = model.getElementAssemblies();
12102
- const zones = model.getZones();
12103
- const systems = model.getSystems();
12104
- const { ownerHistoryId, geomContextId, geomSubContextId, unitAssignmentId } = writeHeader(w, meta);
12105
- const densityByElement = buildDensityMap(relationships);
12106
- const idMap = /* @__PURE__ */ new Map();
12107
- const placementMap = /* @__PURE__ */ new Map();
12108
- const projectExpressId = writeProject$1(w, project.guid, project.spec.name, ownerHistoryId, unitAssignmentId, geomContextId);
12109
- idMap.set(project.localId, projectExpressId);
12110
- for (const el of elements) {
12111
- if (el.category !== "SITE") continue;
12112
- const { entityId, placementId } = writeSite(w, el.guid, el.spec.name, ownerHistoryId);
12113
- idMap.set(el.localId, entityId);
12114
- placementMap.set(el.localId, placementId);
12115
- }
12116
- for (const el of elements) {
12117
- if (el.category !== "BUILDING") continue;
12118
- const parentSiteId = findParentOf(el.localId, relationships);
12119
- const parentPlacementId = parentSiteId !== null ? placementMap.get(parentSiteId) ?? null : null;
12120
- const { entityId, placementId } = writeBuilding(w, el.guid, el.spec.name, ownerHistoryId, parentPlacementId);
12121
- idMap.set(el.localId, entityId);
12122
- placementMap.set(el.localId, placementId);
12123
- }
12124
- for (const el of elements) {
12125
- if (el.category !== "STOREY") continue;
12126
- const parentBuildingId = findParentOf(el.localId, relationships);
12127
- const parentPlacementId = parentBuildingId !== null ? placementMap.get(parentBuildingId) ?? null : null;
12128
- const { entityId, placementId } = writeStorey(w, el.guid, el.spec.name, el.spec.elevation, ownerHistoryId, parentPlacementId);
12129
- idMap.set(el.localId, entityId);
12130
- placementMap.set(el.localId, placementId);
12170
+ //#region src/import/spfReader.ts
12171
+ var SUPPORTED_SCHEMAS = [
12172
+ "IFC2X3",
12173
+ "IFC4",
12174
+ "IFC4X3"
12175
+ ];
12176
+ /**
12177
+ * Foundation layer of the IFC reader: owns the web-ifc WASM model handle and
12178
+ * exposes typed line access, GUID↔expressId lookup, world-transform composition,
12179
+ * geometry-engine access and STEP string decoding.
12180
+ *
12181
+ * Lifecycle: created via the static async {@link SpfReader.create}; the caller
12182
+ * MUST call {@link SpfReader.close} (it always issues `CloseModel`, preventing
12183
+ * WASM handle leaks). The handle is `using`-friendly via `Symbol.dispose`.
12184
+ */
12185
+ function deleteVector(vec) {
12186
+ vec.delete?.();
12187
+ }
12188
+ var SpfReader = class SpfReader {
12189
+ schema;
12190
+ modelId;
12191
+ #api;
12192
+ #closed = false;
12193
+ #linesByType = /* @__PURE__ */ new Map();
12194
+ constructor(api, modelId, schema) {
12195
+ this.#api = api;
12196
+ this.modelId = modelId;
12197
+ this.schema = schema;
12131
12198
  }
12132
- const openingsByWall = /* @__PURE__ */ new Map();
12133
- for (const rel of relationships) {
12134
- if (rel.kind !== "VOIDS_WALL") continue;
12135
- const opening = elements.find((el) => el.localId === rel.openingLocalId);
12199
+ static async create(bytes, settings = {}) {
12200
+ let api;
12201
+ try {
12202
+ api = new web_ifc.IfcAPI();
12203
+ await initIfcApi(api);
12204
+ } catch (e) {
12205
+ return (0, brepjs.err)(importError("OPEN_MODEL_FAILED", "Failed to initialize web-ifc", e));
12206
+ }
12207
+ let modelId;
12208
+ try {
12209
+ modelId = api.OpenModel(bytes, { COORDINATE_TO_ORIGIN: settings.coordinateToOrigin ?? false });
12210
+ } catch (e) {
12211
+ return (0, brepjs.err)(importError("OPEN_MODEL_FAILED", "web-ifc OpenModel threw", e));
12212
+ }
12213
+ if (modelId < 0 || !api.IsModelOpen(modelId)) {
12214
+ if (modelId >= 0) api.CloseModel(modelId);
12215
+ return (0, brepjs.err)(importError("OPEN_MODEL_FAILED", "web-ifc OpenModel returned an invalid model id"));
12216
+ }
12217
+ const schemaRaw = api.GetModelSchema(modelId);
12218
+ if (!SUPPORTED_SCHEMAS.includes(schemaRaw)) {
12219
+ api.CloseModel(modelId);
12220
+ return (0, brepjs.err)(importError("SCHEMA_UNSUPPORTED", `Unsupported IFC schema "${schemaRaw}"`));
12221
+ }
12222
+ return (0, brepjs.ok)(new SpfReader(api, modelId, schemaRaw));
12223
+ }
12224
+ /** Always issues CloseModel; safe to call more than once. */
12225
+ close() {
12226
+ if (this.#closed) return;
12227
+ this.#closed = true;
12228
+ this.#api.CloseModel(this.modelId);
12229
+ }
12230
+ [Symbol.dispose]() {
12231
+ this.close();
12232
+ }
12233
+ /**
12234
+ * Raw line object for an express id. With `flatten=false` (default) references
12235
+ * appear as `{ type, value }` wrappers and nested entities are not resolved;
12236
+ * pass `flatten=true` to recursively inline referenced lines.
12237
+ */
12238
+ getLine(expressId, flatten = false) {
12239
+ return this.#api.GetLine(this.modelId, expressId, flatten) ?? null;
12240
+ }
12241
+ /** Express ids of every line whose type equals `type` (no inherited types). */
12242
+ getLinesOfType(type) {
12243
+ const cached = this.#linesByType.get(type);
12244
+ if (cached !== void 0) return cached;
12245
+ const vec = this.#api.GetLineIDsWithType(this.modelId, type);
12246
+ const out = [];
12247
+ for (let i = 0; i < vec.size(); i++) out.push(vec.get(i));
12248
+ deleteVector(vec);
12249
+ this.#linesByType.set(type, out);
12250
+ return out;
12251
+ }
12252
+ /** Express ids of every line in the model. */
12253
+ getAllLines() {
12254
+ const vec = this.#api.GetAllLines(this.modelId);
12255
+ const out = [];
12256
+ for (let i = 0; i < vec.size(); i++) out.push(vec.get(i));
12257
+ deleteVector(vec);
12258
+ return out;
12259
+ }
12260
+ /** IFC type code for an express id. */
12261
+ /** Uppercased IFC entity name of the instance (e.g. `IFCWALL`). */
12262
+ typeNameOf(expressId) {
12263
+ return this.#api.GetNameFromTypeCode(this.getLineType(expressId)).toUpperCase();
12264
+ }
12265
+ getLineType(expressId) {
12266
+ const t = this.#api.GetLineType(this.modelId, expressId);
12267
+ return typeof t === "number" ? t : Number(t?.value ?? t);
12268
+ }
12269
+ /** Builds web-ifc's internal GUID→expressId index; call before guid lookups. */
12270
+ buildGuidMap() {
12271
+ this.#api.CreateIfcGuidToExpressIdMapping(this.modelId);
12272
+ }
12273
+ /** expressId for a GlobalId, or undefined. Requires {@link buildGuidMap} first. */
12274
+ expressIdFromGuid(guid) {
12275
+ const id = this.#api.GetExpressIdFromGuid(this.modelId, guid);
12276
+ return typeof id === "number" ? id : void 0;
12277
+ }
12278
+ /** GlobalId for an express id, or undefined. Requires {@link buildGuidMap} first. */
12279
+ guidFromExpressId(expressId) {
12280
+ const guid = this.#api.GetGuidFromExpressId(this.modelId, expressId);
12281
+ return typeof guid === "string" ? guid : void 0;
12282
+ }
12283
+ /**
12284
+ * Composed world transform (column-major 16-float matrix) for a placement
12285
+ * express id, resolving the full IfcLocalPlacement chain.
12286
+ */
12287
+ getWorldTransform(placementExpressId) {
12288
+ return this.#api.GetWorldTransformMatrix(this.modelId, placementExpressId);
12289
+ }
12290
+ /** Streams placed meshes for the given product express ids. */
12291
+ streamMeshes(expressIds, cb) {
12292
+ this.#api.StreamMeshes(this.modelId, expressIds, cb);
12293
+ }
12294
+ /** Geometry buffers for a geometry express id; caller MUST call `.delete()`. */
12295
+ getGeometry(geometryExpressId) {
12296
+ return this.#api.GetGeometry(this.modelId, geometryExpressId);
12297
+ }
12298
+ /** Reads a Float32Array view of vertex data from a WASM pointer. */
12299
+ getVertexArray(ptr, size) {
12300
+ return this.#api.GetVertexArray(ptr, size);
12301
+ }
12302
+ /** Reads a Uint32Array view of index data from a WASM pointer. */
12303
+ getIndexArray(ptr, size) {
12304
+ return this.#api.GetIndexArray(ptr, size);
12305
+ }
12306
+ /** Decodes IFC STEP string escapes (`\X2\`, `\S\`, `\X\`) in a raw value. */
12307
+ decodeText(s) {
12308
+ const decoded = this.#api.DecodeText(s);
12309
+ return typeof decoded === "string" ? decoded : s;
12310
+ }
12311
+ };
12312
+ //#endregion
12313
+ //#region src/validation/qtoDefinitions.generated.ts
12314
+ var QTO_DEFINITIONS = {
12315
+ "Qto_ActuatorBaseQuantities": {
12316
+ "entities": ["IFCACTUATOR"],
12317
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12318
+ },
12319
+ "Qto_AirTerminalBaseQuantities": {
12320
+ "entities": ["IFCAIRTERMINAL"],
12321
+ "quantities": {
12322
+ "GrossWeight": "IFCQUANTITYWEIGHT",
12323
+ "Perimeter": "IFCQUANTITYLENGTH",
12324
+ "TotalSurfaceArea": "IFCQUANTITYAREA"
12325
+ }
12326
+ },
12327
+ "Qto_AirTerminalBoxTypeBaseQuantities": {
12328
+ "entities": ["IFCAIRTERMINALBOX"],
12329
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12330
+ },
12331
+ "Qto_AirToAirHeatRecoveryBaseQuantities": {
12332
+ "entities": ["IFCAIRTOAIRHEATRECOVERY"],
12333
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12334
+ },
12335
+ "Qto_AlarmBaseQuantities": {
12336
+ "entities": ["IFCALARM"],
12337
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12338
+ },
12339
+ "Qto_AudioVisualApplianceBaseQuantities": {
12340
+ "entities": ["IFCAUDIOVISUALAPPLIANCE"],
12341
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12342
+ },
12343
+ "Qto_BeamBaseQuantities": {
12344
+ "entities": ["IFCBEAM"],
12345
+ "quantities": {
12346
+ "CrossSectionArea": "IFCQUANTITYAREA",
12347
+ "GrossSurfaceArea": "IFCQUANTITYAREA",
12348
+ "GrossVolume": "IFCQUANTITYVOLUME",
12349
+ "GrossWeight": "IFCQUANTITYWEIGHT",
12350
+ "Length": "IFCQUANTITYLENGTH",
12351
+ "NetSurfaceArea": "IFCQUANTITYAREA",
12352
+ "NetVolume": "IFCQUANTITYVOLUME",
12353
+ "NetWeight": "IFCQUANTITYWEIGHT",
12354
+ "OuterSurfaceArea": "IFCQUANTITYAREA"
12355
+ }
12356
+ },
12357
+ "Qto_BoilerBaseQuantities": {
12358
+ "entities": ["IFCBOILER"],
12359
+ "quantities": {
12360
+ "GrossWeight": "IFCQUANTITYWEIGHT",
12361
+ "NetWeight": "IFCQUANTITYWEIGHT",
12362
+ "TotalSurfaceArea": "IFCQUANTITYAREA"
12363
+ }
12364
+ },
12365
+ "Qto_BuildingBaseQuantities": {
12366
+ "entities": ["IFCBUILDING"],
12367
+ "quantities": {
12368
+ "EavesHeight": "IFCQUANTITYLENGTH",
12369
+ "FootprintArea": "IFCQUANTITYAREA",
12370
+ "GrossFloorArea": "IFCQUANTITYAREA",
12371
+ "GrossVolume": "IFCQUANTITYVOLUME",
12372
+ "Height": "IFCQUANTITYLENGTH",
12373
+ "NetFloorArea": "IFCQUANTITYAREA",
12374
+ "NetVolume": "IFCQUANTITYVOLUME"
12375
+ }
12376
+ },
12377
+ "Qto_BuildingElementProxyQuantities": {
12378
+ "entities": ["IFCBUILDINGELEMENTPROXY"],
12379
+ "quantities": {
12380
+ "NetSurfaceArea": "IFCQUANTITYCOUNT",
12381
+ "NetVolume": "IFCQUANTITYCOUNT"
12382
+ }
12383
+ },
12384
+ "Qto_BuildingStoreyBaseQuantities": {
12385
+ "entities": ["IFCBUILDINGSTOREY"],
12386
+ "quantities": {
12387
+ "GrossFloorArea": "IFCQUANTITYAREA",
12388
+ "GrossHeight": "IFCQUANTITYLENGTH",
12389
+ "GrossPerimeter": "IFCQUANTITYLENGTH",
12390
+ "GrossVolume": "IFCQUANTITYVOLUME",
12391
+ "NetFloorArea": "IFCQUANTITYAREA",
12392
+ "NetHeigtht": "IFCQUANTITYLENGTH",
12393
+ "NetVolume": "IFCQUANTITYVOLUME"
12394
+ }
12395
+ },
12396
+ "Qto_BurnerBaseQuantities": {
12397
+ "entities": ["IFCBURNER"],
12398
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12399
+ },
12400
+ "Qto_CableCarrierFittingBaseQuantities": {
12401
+ "entities": ["IFCCABLECARRIERFITTING"],
12402
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12403
+ },
12404
+ "Qto_CableCarrierSegmentBaseQuantities": {
12405
+ "entities": ["IFCCABLECARRIERSEGMENT"],
12406
+ "quantities": {
12407
+ "CrossSectionArea": "IFCQUANTITYAREA",
12408
+ "GrossWeight": "IFCQUANTITYWEIGHT",
12409
+ "Length": "IFCQUANTITYLENGTH",
12410
+ "OuterSurfaceArea": "IFCQUANTITYAREA"
12411
+ }
12412
+ },
12413
+ "Qto_CableFittingBaseQuantities": {
12414
+ "entities": ["IFCCABLEFITTING"],
12415
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12416
+ },
12417
+ "Qto_CableSegmentBaseQuantities": {
12418
+ "entities": ["IFCCABLESEGMENT"],
12419
+ "quantities": {
12420
+ "CrossSectionArea": "IFCQUANTITYAREA",
12421
+ "GrossWeight": "IFCQUANTITYWEIGHT",
12422
+ "Length": "IFCQUANTITYLENGTH",
12423
+ "OuterSurfaceArea": "IFCQUANTITYAREA"
12424
+ }
12425
+ },
12426
+ "Qto_ChillerBaseQuantities": {
12427
+ "entities": ["IFCCHILLER"],
12428
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12429
+ },
12430
+ "Qto_ChimneyBaseQuantities": {
12431
+ "entities": ["IFCCHIMNEY"],
12432
+ "quantities": { "Length": "IFCQUANTITYLENGTH" }
12433
+ },
12434
+ "Qto_CoilBaseQuantities": {
12435
+ "entities": ["IFCCOIL"],
12436
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12437
+ },
12438
+ "Qto_ColumnBaseQuantities": {
12439
+ "entities": ["IFCCOLUMN"],
12440
+ "quantities": {
12441
+ "CrossSectionArea": "IFCQUANTITYAREA",
12442
+ "GrossSurfaceArea": "IFCQUANTITYAREA",
12443
+ "GrossVolume": "IFCQUANTITYVOLUME",
12444
+ "GrossWeight": "IFCQUANTITYWEIGHT",
12445
+ "Length": "IFCQUANTITYLENGTH",
12446
+ "NetSurfaceArea": "IFCQUANTITYAREA",
12447
+ "NetVolume": "IFCQUANTITYVOLUME",
12448
+ "NetWeight": "IFCQUANTITYWEIGHT",
12449
+ "OuterSurfaceArea": "IFCQUANTITYAREA"
12450
+ }
12451
+ },
12452
+ "Qto_CommunicationsApplianceBaseQuantities": {
12453
+ "entities": ["IFCCOMMUNICATIONSAPPLIANCE"],
12454
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12455
+ },
12456
+ "Qto_CompressorBaseQuantities": {
12457
+ "entities": ["IFCCOMPRESSOR"],
12458
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12459
+ },
12460
+ "Qto_CondenserBaseQuantities": {
12461
+ "entities": ["IFCCONDENSER"],
12462
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12463
+ },
12464
+ "Qto_ConstructionEquipmentResourceBaseQuantities": {
12465
+ "entities": ["IFCCONSTRUCTIONEQUIPMENTRESOURCE"],
12466
+ "quantities": {
12467
+ "OperatingTime": "IFCQUANTITYTIME",
12468
+ "UsageTime": "IFCQUANTITYTIME"
12469
+ }
12470
+ },
12471
+ "Qto_ConstructionMaterialResourceBaseQuantities": {
12472
+ "entities": ["IFCCONSTRUCTIONMATERIALRESOURCE"],
12473
+ "quantities": {
12474
+ "GrossVolume": "IFCQUANTITYVOLUME",
12475
+ "GrossWeight": "IFCQUANTITYWEIGHT",
12476
+ "NetVolume": "IFCQUANTITYVOLUME",
12477
+ "NetWeight": "IFCQUANTITYWEIGHT"
12478
+ }
12479
+ },
12480
+ "Qto_ControllerBaseQuantities": {
12481
+ "entities": ["IFCCONTROLLER"],
12482
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12483
+ },
12484
+ "Qto_CooledBeamBaseQuantities": {
12485
+ "entities": ["IFCCOOLEDBEAM"],
12486
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12487
+ },
12488
+ "Qto_CoolingTowerBaseQuantities": {
12489
+ "entities": ["IFCCOOLINGTOWER"],
12490
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12491
+ },
12492
+ "Qto_CoveringBaseQuantities": {
12493
+ "entities": ["IFCCOVERING"],
12494
+ "quantities": {
12495
+ "GrossArea": "IFCQUANTITYAREA",
12496
+ "NetArea": "IFCQUANTITYAREA",
12497
+ "Width": "IFCQUANTITYLENGTH"
12498
+ }
12499
+ },
12500
+ "Qto_CurtainWallQuantities": {
12501
+ "entities": ["IFCCURTAINWALL"],
12502
+ "quantities": {
12503
+ "GrossSideArea": "IFCQUANTITYAREA",
12504
+ "Height": "IFCQUANTITYLENGTH",
12505
+ "Length": "IFCQUANTITYLENGTH",
12506
+ "NetSideArea": "IFCQUANTITYAREA",
12507
+ "Width": "IFCQUANTITYLENGTH"
12508
+ }
12509
+ },
12510
+ "Qto_DamperBaseQuantities": {
12511
+ "entities": ["IFCDAMPER"],
12512
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12513
+ },
12514
+ "Qto_DistributionChamberElementBaseQuantities": {
12515
+ "entities": ["IFCDISTRIBUTIONCHAMBERELEMENT"],
12516
+ "quantities": {
12517
+ "GrossSurfaceArea": "IFCQUANTITYAREA",
12518
+ "GrossVolume": "IFCQUANTITYVOLUME",
12519
+ "NetSurfaceArea": "IFCQUANTITYAREA",
12520
+ "NetVolume": "IFCQUANTITYVOLUME"
12521
+ }
12522
+ },
12523
+ "Qto_DoorBaseQuantities": {
12524
+ "entities": ["IFCDOOR"],
12525
+ "quantities": {
12526
+ "Area": "IFCQUANTITYAREA",
12527
+ "Height": "IFCQUANTITYLENGTH",
12528
+ "Perimeter": "IFCQUANTITYLENGTH",
12529
+ "Width": "IFCQUANTITYLENGTH"
12530
+ }
12531
+ },
12532
+ "Qto_DuctFittingBaseQuantities": {
12533
+ "entities": ["IFCDUCTFITTING"],
12534
+ "quantities": {
12535
+ "GrossCrossSectionArea": "IFCQUANTITYAREA",
12536
+ "GrossWeight": "IFCQUANTITYWEIGHT",
12537
+ "Length": "IFCQUANTITYLENGTH",
12538
+ "NetCrossSectionArea": "IFCQUANTITYAREA",
12539
+ "OuterSurfaceArea": "IFCQUANTITYAREA"
12540
+ }
12541
+ },
12542
+ "Qto_DuctSegmentBaseQuantities": {
12543
+ "entities": ["IFCDUCTSEGMENT"],
12544
+ "quantities": {
12545
+ "GrossCrossSectionArea": "IFCQUANTITYAREA",
12546
+ "GrossWeight": "IFCQUANTITYWEIGHT",
12547
+ "Length": "IFCQUANTITYLENGTH",
12548
+ "NetCrossSectionArea": "IFCQUANTITYAREA",
12549
+ "OuterSurfaceArea": "IFCQUANTITYAREA"
12550
+ }
12551
+ },
12552
+ "Qto_DuctSilencerBaseQuantities": {
12553
+ "entities": ["IFCDUCTSILENCER"],
12554
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12555
+ },
12556
+ "Qto_ElectricApplianceBaseQuantities": {
12557
+ "entities": ["IFCELECTRICAPPLIANCE"],
12558
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12559
+ },
12560
+ "Qto_ElectricDistributionBoardBaseQuantities": {
12561
+ "entities": ["IFCELECTRICDISTRIBUTIONBOARD"],
12562
+ "quantities": {
12563
+ "GrossWeight": "IFCQUANTITYWEIGHT",
12564
+ "NumberOfCircuits": "IFCQUANTITYCOUNT"
12565
+ }
12566
+ },
12567
+ "Qto_ElectricFlowStorageDeviceBaseQuantities": {
12568
+ "entities": ["IFCELECTRICFLOWSTORAGEDEVICE"],
12569
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12570
+ },
12571
+ "Qto_ElectricGeneratorBaseQuantities": {
12572
+ "entities": ["IFCELECTRICGENERATOR"],
12573
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12574
+ },
12575
+ "Qto_ElectricMotorBaseQuantities": {
12576
+ "entities": ["IFCELECTRICMOTOR"],
12577
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12578
+ },
12579
+ "Qto_ElectricTimeControlBaseQuantities": {
12580
+ "entities": ["IFCELECTRICTIMECONTROL"],
12581
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12582
+ },
12583
+ "Qto_EvaporativeCoolerBaseQuantities": {
12584
+ "entities": ["IFCEVAPORATIVECOOLER"],
12585
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12586
+ },
12587
+ "Qto_EvaporatorBaseQuantities": {
12588
+ "entities": ["IFCEVAPORATOR"],
12589
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12590
+ },
12591
+ "Qto_FanBaseQuantities": {
12592
+ "entities": ["IFCFAN"],
12593
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12594
+ },
12595
+ "Qto_FilterBaseQuantities": {
12596
+ "entities": ["IFCFILTER"],
12597
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12598
+ },
12599
+ "Qto_FireSuppressionTerminalBaseQuantities": {
12600
+ "entities": ["IFCFIRESUPPRESSIONTERMINAL"],
12601
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12602
+ },
12603
+ "Qto_FlowInstrumentBaseQuantities": {
12604
+ "entities": ["IFCFLOWINSTRUMENT"],
12605
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12606
+ },
12607
+ "Qto_FlowMeterBaseQuantities": {
12608
+ "entities": ["IFCFLOWMETER"],
12609
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12610
+ },
12611
+ "Qto_FootingBaseQuantities": {
12612
+ "entities": ["IFCFOOTING"],
12613
+ "quantities": {
12614
+ "CrossSectionArea": "IFCQUANTITYAREA",
12615
+ "GrossSurfaceArea": "IFCQUANTITYAREA",
12616
+ "GrossVolume": "IFCQUANTITYVOLUME",
12617
+ "GrossWeight": "IFCQUANTITYWEIGHT",
12618
+ "Height": "IFCQUANTITYLENGTH",
12619
+ "Length": "IFCQUANTITYLENGTH",
12620
+ "NetVolume": "IFCQUANTITYVOLUME",
12621
+ "NetWeight": "IFCQUANTITYWEIGHT",
12622
+ "OuterSurfaceArea": "IFCQUANTITYAREA",
12623
+ "Width": "IFCQUANTITYLENGTH"
12624
+ }
12625
+ },
12626
+ "Qto_HeatExchangerBaseQuantities": {
12627
+ "entities": ["IFCHEATEXCHANGER"],
12628
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12629
+ },
12630
+ "Qto_HumidifierBaseQuantities": {
12631
+ "entities": ["IFCHUMIDIFIER"],
12632
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12633
+ },
12634
+ "Qto_InterceptorBaseQuantities": {
12635
+ "entities": ["IFCINTERCEPTOR"],
12636
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12637
+ },
12638
+ "Qto_JunctionBoxBaseQuantities": {
12639
+ "entities": ["IFCJUNCTIONBOX"],
12640
+ "quantities": {
12641
+ "GrossWeight": "IFCQUANTITYWEIGHT",
12642
+ "NumberOfGangs": "IFCQUANTITYCOUNT"
12643
+ }
12644
+ },
12645
+ "Qto_LaborResourceBaseQuantities": {
12646
+ "entities": ["IFCLABORRESOURCE"],
12647
+ "quantities": {
12648
+ "OvertimeWork": "IFCQUANTITYTIME",
12649
+ "StandardWork": "IFCQUANTITYTIME"
12650
+ }
12651
+ },
12652
+ "Qto_LampBaseQuantities": {
12653
+ "entities": ["IFCLAMP"],
12654
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12655
+ },
12656
+ "Qto_LightFixtureBaseQuantities": {
12657
+ "entities": ["IFCLIGHTFIXTURE"],
12658
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12659
+ },
12660
+ "Qto_MemberBaseQuantities": {
12661
+ "entities": ["IFCMEMBER"],
12662
+ "quantities": {
12663
+ "CrossSectionArea": "IFCQUANTITYAREA",
12664
+ "GrossSurfaceArea": "IFCQUANTITYAREA",
12665
+ "GrossVolume": "IFCQUANTITYVOLUME",
12666
+ "GrossWeight": "IFCQUANTITYWEIGHT",
12667
+ "Length": "IFCQUANTITYLENGTH",
12668
+ "NetSurfaceArea": "IFCQUANTITYAREA",
12669
+ "NetVolume": "IFCQUANTITYVOLUME",
12670
+ "NetWeight": "IFCQUANTITYWEIGHT",
12671
+ "OuterSurfaceArea": "IFCQUANTITYAREA"
12672
+ }
12673
+ },
12674
+ "Qto_MotorConnectionBaseQuantities": {
12675
+ "entities": ["IFCMOTORCONNECTION"],
12676
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12677
+ },
12678
+ "Qto_OpeningElementBaseQuantities": {
12679
+ "entities": ["IFCOPENINGELEMENT"],
12680
+ "quantities": {
12681
+ "Area": "IFCQUANTITYAREA",
12682
+ "Depth": "IFCQUANTITYLENGTH",
12683
+ "Height": "IFCQUANTITYLENGTH",
12684
+ "Volume": "IFCQUANTITYVOLUME",
12685
+ "Width": "IFCQUANTITYLENGTH"
12686
+ }
12687
+ },
12688
+ "Qto_OutletBaseQuantities": {
12689
+ "entities": ["IFCOUTLET"],
12690
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12691
+ },
12692
+ "Qto_PileBaseQuantities": {
12693
+ "entities": ["IFCPILE"],
12694
+ "quantities": {
12695
+ "CrossSectionArea": "IFCQUANTITYAREA",
12696
+ "GrossSurfaceArea": "IFCQUANTITYAREA",
12697
+ "GrossVolume": "IFCQUANTITYVOLUME",
12698
+ "GrossWeight": "IFCQUANTITYWEIGHT",
12699
+ "Length": "IFCQUANTITYLENGTH",
12700
+ "NetVolume": "IFCQUANTITYVOLUME",
12701
+ "NetWeight": "IFCQUANTITYWEIGHT",
12702
+ "OuterSurfaceArea": "IFCQUANTITYAREA"
12703
+ }
12704
+ },
12705
+ "Qto_PipeFittingBaseQuantities": {
12706
+ "entities": ["IFCPIPEFITTING"],
12707
+ "quantities": {
12708
+ "GrossCrossSectionArea": "IFCQUANTITYAREA",
12709
+ "GrossWeight": "IFCQUANTITYWEIGHT",
12710
+ "Length": "IFCQUANTITYLENGTH",
12711
+ "NetCrossSectionArea": "IFCQUANTITYAREA",
12712
+ "NetWeight": "IFCQUANTITYWEIGHT",
12713
+ "OuterSurfaceArea": "IFCQUANTITYAREA"
12714
+ }
12715
+ },
12716
+ "Qto_PipeSegmentBaseQuantities": {
12717
+ "entities": ["IFCPIPESEGMENT"],
12718
+ "quantities": {
12719
+ "GrossCrossSectionArea": "IFCQUANTITYAREA",
12720
+ "GrossWeight": "IFCQUANTITYWEIGHT",
12721
+ "Length": "IFCQUANTITYLENGTH",
12722
+ "NetCrossSectionArea": "IFCQUANTITYAREA",
12723
+ "NetWeight": "IFCQUANTITYWEIGHT",
12724
+ "OuterSurfaceArea": "IFCQUANTITYAREA"
12725
+ }
12726
+ },
12727
+ "Qto_PlateBaseQuantities": {
12728
+ "entities": ["IFCPLATE"],
12729
+ "quantities": {
12730
+ "GrossArea": "IFCQUANTITYAREA",
12731
+ "GrossVolume": "IFCQUANTITYVOLUME",
12732
+ "GrossWeight": "IFCQUANTITYWEIGHT",
12733
+ "NetArea": "IFCQUANTITYAREA",
12734
+ "NetVolume": "IFCQUANTITYVOLUME",
12735
+ "NetWeight": "IFCQUANTITYWEIGHT",
12736
+ "Perimeter": "IFCQUANTITYLENGTH",
12737
+ "Width": "IFCQUANTITYLENGTH"
12738
+ }
12739
+ },
12740
+ "Qto_ProjectionElementBaseQuantities": {
12741
+ "entities": ["IFCPROJECTIONELEMENT"],
12742
+ "quantities": {
12743
+ "Area": "IFCQUANTITYAREA",
12744
+ "Volume": "IFCQUANTITYVOLUME"
12745
+ }
12746
+ },
12747
+ "Qto_ProtectiveDeviceBaseQuantities": {
12748
+ "entities": ["IFCPROTECTIVEDEVICE"],
12749
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12750
+ },
12751
+ "Qto_ProtectiveDeviceTrippingUnitBaseQuantities": {
12752
+ "entities": ["IFCPROTECTIVEDEVICETRIPPINGUNIT"],
12753
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12754
+ },
12755
+ "Qto_PumpBaseQuantities": {
12756
+ "entities": ["IFCPUMP"],
12757
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12758
+ },
12759
+ "Qto_RailingBaseQuantities": {
12760
+ "entities": ["IFCRAILING"],
12761
+ "quantities": { "Length": "IFCQUANTITYLENGTH" }
12762
+ },
12763
+ "Qto_RampFlightBaseQuantities": {
12764
+ "entities": ["IFCRAMPFLIGHT"],
12765
+ "quantities": {
12766
+ "GrossArea": "IFCQUANTITYAREA",
12767
+ "GrossVolume": "IFCQUANTITYVOLUME",
12768
+ "Length": "IFCQUANTITYLENGTH",
12769
+ "NetArea": "IFCQUANTITYAREA",
12770
+ "NetVolume": "IFCQUANTITYVOLUME",
12771
+ "Width": "IFCQUANTITYLENGTH"
12772
+ }
12773
+ },
12774
+ "Qto_ReinforcingElementBaseQuantities": {
12775
+ "entities": ["IFCREINFORCINGELEMENT"],
12776
+ "quantities": {
12777
+ "Count": "IFCQUANTITYCOUNT",
12778
+ "Length": "IFCQUANTITYLENGTH",
12779
+ "Weight": "IFCQUANTITYWEIGHT"
12780
+ }
12781
+ },
12782
+ "Qto_RoofBaseQuantities": {
12783
+ "entities": ["IFCROOF"],
12784
+ "quantities": {
12785
+ "GrossArea": "IFCQUANTITYAREA",
12786
+ "NetArea": "IFCQUANTITYAREA",
12787
+ "ProjectedArea": "IFCQUANTITYAREA"
12788
+ }
12789
+ },
12790
+ "Qto_SanitaryTerminalBaseQuantities": {
12791
+ "entities": ["IFCSANITARYTERMINAL"],
12792
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12793
+ },
12794
+ "Qto_SensorBaseQuantities": {
12795
+ "entities": ["IFCSENSOR"],
12796
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12797
+ },
12798
+ "Qto_SiteBaseQuantities": {
12799
+ "entities": ["IFCSITE"],
12800
+ "quantities": {
12801
+ "GrossArea": "IFCQUANTITYAREA",
12802
+ "GrossPerimeter": "IFCQUANTITYLENGTH"
12803
+ }
12804
+ },
12805
+ "Qto_SlabBaseQuantities": {
12806
+ "entities": ["IFCSLAB"],
12807
+ "quantities": {
12808
+ "Depth": "IFCQUANTITYLENGTH",
12809
+ "GrossArea": "IFCQUANTITYAREA",
12810
+ "GrossVolume": "IFCQUANTITYVOLUME",
12811
+ "GrossWeight": "IFCQUANTITYWEIGHT",
12812
+ "Length": "IFCQUANTITYLENGTH",
12813
+ "NetArea": "IFCQUANTITYAREA",
12814
+ "NetVolume": "IFCQUANTITYVOLUME",
12815
+ "NetWeight": "IFCQUANTITYWEIGHT",
12816
+ "Perimeter": "IFCQUANTITYLENGTH",
12817
+ "Width": "IFCQUANTITYLENGTH"
12818
+ }
12819
+ },
12820
+ "Qto_SolarDeviceBaseQuantities": {
12821
+ "entities": ["IFCSOLARDEVICE"],
12822
+ "quantities": {
12823
+ "GrossArea": "IFCQUANTITYAREA",
12824
+ "GrossWeight": "IFCQUANTITYWEIGHT"
12825
+ }
12826
+ },
12827
+ "Qto_SpaceBaseQuantities": {
12828
+ "entities": ["IFCSPACE"],
12829
+ "quantities": {
12830
+ "FinishCeilingHeight": "IFCQUANTITYLENGTH",
12831
+ "FinishFloorHeight": "IFCQUANTITYLENGTH",
12832
+ "GrossCeilingArea": "IFCQUANTITYAREA",
12833
+ "GrossFloorArea": "IFCQUANTITYAREA",
12834
+ "GrossPerimeter": "IFCQUANTITYLENGTH",
12835
+ "GrossVolume": "IFCQUANTITYVOLUME",
12836
+ "GrossWallArea": "IFCQUANTITYAREA",
12837
+ "Height": "IFCQUANTITYLENGTH",
12838
+ "NetCeilingArea": "IFCQUANTITYAREA",
12839
+ "NetFloorArea": "IFCQUANTITYAREA",
12840
+ "NetPerimeter": "IFCQUANTITYLENGTH",
12841
+ "NetVolume": "IFCQUANTITYVOLUME",
12842
+ "NetWallArea": "IFCQUANTITYAREA"
12843
+ }
12844
+ },
12845
+ "Qto_SpaceHeaterBaseQuantities": {
12846
+ "entities": ["IFCSPACEHEATER"],
12847
+ "quantities": {
12848
+ "GrossWeight": "IFCQUANTITYWEIGHT",
12849
+ "Length": "IFCQUANTITYLENGTH",
12850
+ "NetWeight": "IFCQUANTITYWEIGHT"
12851
+ }
12852
+ },
12853
+ "Qto_StackTerminalBaseQuantities": {
12854
+ "entities": ["IFCSTACKTERMINAL"],
12855
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12856
+ },
12857
+ "Qto_StairFlightBaseQuantities": {
12858
+ "entities": ["IFCSTAIRFLIGHT"],
12859
+ "quantities": {
12860
+ "GrossVolume": "IFCQUANTITYVOLUME",
12861
+ "Length": "IFCQUANTITYLENGTH",
12862
+ "NetVolume": "IFCQUANTITYVOLUME"
12863
+ }
12864
+ },
12865
+ "Qto_SwitchingDeviceBaseQuantities": {
12866
+ "entities": ["IFCSWITCHINGDEVICE"],
12867
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12868
+ },
12869
+ "Qto_TankBaseQuantities": {
12870
+ "entities": ["IFCTANK"],
12871
+ "quantities": {
12872
+ "GrossWeight": "IFCQUANTITYWEIGHT",
12873
+ "NetWeight": "IFCQUANTITYWEIGHT",
12874
+ "TotalSurfaceArea": "IFCQUANTITYAREA"
12875
+ }
12876
+ },
12877
+ "Qto_TransformerBaseQuantities": {
12878
+ "entities": ["IFCTRANSFORMER"],
12879
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12880
+ },
12881
+ "Qto_TubeBundleBaseQuantities": {
12882
+ "entities": ["IFCTUBEBUNDLE"],
12883
+ "quantities": {
12884
+ "GrossWeight": "IFCQUANTITYWEIGHT",
12885
+ "NetWeight": "IFCQUANTITYWEIGHT"
12886
+ }
12887
+ },
12888
+ "Qto_UnitaryControlElementBaseQuantities": {
12889
+ "entities": ["IFCUNITARYCONTROLELEMENT"],
12890
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12891
+ },
12892
+ "Qto_UnitaryEquipmentBaseQuantities": {
12893
+ "entities": ["IFCUNITARYEQUIPMENT"],
12894
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12895
+ },
12896
+ "Qto_ValveBaseQuantities": {
12897
+ "entities": ["IFCVALVE"],
12898
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12899
+ },
12900
+ "Qto_VibrationIsolatorBaseQuantities": {
12901
+ "entities": ["IFCVIBRATIONISOLATOR"],
12902
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12903
+ },
12904
+ "Qto_WallBaseQuantities": {
12905
+ "entities": ["IFCWALL"],
12906
+ "quantities": {
12907
+ "GrossFootprintArea": "IFCQUANTITYAREA",
12908
+ "GrossSideArea": "IFCQUANTITYAREA",
12909
+ "GrossVolume": "IFCQUANTITYVOLUME",
12910
+ "GrossWeight": "IFCQUANTITYWEIGHT",
12911
+ "Height": "IFCQUANTITYLENGTH",
12912
+ "Length": "IFCQUANTITYLENGTH",
12913
+ "NetFootprintArea": "IFCQUANTITYAREA",
12914
+ "NetSideArea": "IFCQUANTITYAREA",
12915
+ "NetVolume": "IFCQUANTITYVOLUME",
12916
+ "NetWeight": "IFCQUANTITYWEIGHT",
12917
+ "Width": "IFCQUANTITYLENGTH"
12918
+ }
12919
+ },
12920
+ "Qto_WasteTerminalBaseQuantities": {
12921
+ "entities": ["IFCWASTETERMINAL"],
12922
+ "quantities": { "GrossWeight": "IFCQUANTITYWEIGHT" }
12923
+ },
12924
+ "Qto_WindowBaseQuantities": {
12925
+ "entities": ["IFCWINDOW"],
12926
+ "quantities": {
12927
+ "Area": "IFCQUANTITYAREA",
12928
+ "Height": "IFCQUANTITYLENGTH",
12929
+ "Perimeter": "IFCQUANTITYLENGTH",
12930
+ "Width": "IFCQUANTITYLENGTH"
12931
+ }
12932
+ }
12933
+ };
12934
+ //#endregion
12935
+ //#region src/validation/gherkinChecks.ts
12936
+ /**
12937
+ * Local implementations of the buildingSMART Validation Service's gherkin
12938
+ * normative rules that apply to the entity vocabulary this writer emits:
12939
+ *
12940
+ * - IFC102 — absence of deprecated entities and attributes (IFC4 lists from
12941
+ * the official rule definition).
12942
+ * - QTY001 — every `Qto_*` element quantity uses the standard set name,
12943
+ * quantity names, quantity entity types, applicable element, and
12944
+ * `MethodOfMeasurement='BaseQuantities'` (table generated from the official
12945
+ * qto_definitions.csv).
12946
+ * - GRF003 — a model containing facilities (IfcBuilding) declares a
12947
+ * coordinate reference system (warning severity, like the service).
12948
+ *
12949
+ * These run inside `toIfcValidated`, so the local gate covers the gherkin
12950
+ * layer before anything is uploaded.
12951
+ */
12952
+ async function checkGherkinRules(bytes) {
12953
+ const readerResult = await SpfReader.create(bytes);
12954
+ if (!readerResult.ok) return [issue("error", "PARSE_FAILED", `Could not open the model: ${readerResult.error.message}`)];
12955
+ const reader = readerResult.value;
12956
+ try {
12957
+ return [
12958
+ ...checkDeprecated(reader),
12959
+ ...checkQuantitySets(reader),
12960
+ ...checkGeoreferencing(reader)
12961
+ ];
12962
+ } finally {
12963
+ reader.close();
12964
+ }
12965
+ }
12966
+ /** IFC4 deprecated entities (IFC102, official rule table). */
12967
+ var DEPRECATED_IFC4_ENTITIES = [
12968
+ "IFC2DCOMPOSITECURVE",
12969
+ "IFCBEAMSTANDARDCASE",
12970
+ "IFCCONNECTIONPORTGEOMETRY",
12971
+ "IFCCOLUMNSTANDARDCASE",
12972
+ "IFCDOORSTANDARDCASE",
12973
+ "IFCDOORSTYLE",
12974
+ "IFCELECTRICALELEMENT",
12975
+ "IFCEQUIPMENTELEMENT",
12976
+ "IFCFACEBASEDSURFACEMODEL",
12977
+ "IFCMATERIALCLASSIFICATIONRELATIONSHIP",
12978
+ "IFCMATERIALLIST",
12979
+ "IFCMEMBERSTANDARDCASE",
12980
+ "IFCOPENINGSTANDARDCASE",
12981
+ "IFCPLATESTANDARDCASE",
12982
+ "IFCPRESENTATIONSTYLEASSIGNMENT",
12983
+ "IFCPROXY",
12984
+ "IFCRELCOVERSBLDGELEMENTS",
12985
+ "IFCRELCOVERSSPACES",
12986
+ "IFCSLABELEMENTEDCASE",
12987
+ "IFCSLABSTANDARDCASE",
12988
+ "IFCTEXTLITERAL",
12989
+ "IFCWALLELEMENTEDCASE",
12990
+ "IFCWALLSTANDARDCASE",
12991
+ "IFCWINDOWSTANDARDCASE",
12992
+ "IFCWINDOWSTYLE"
12993
+ ];
12994
+ /** IFC4 deprecated attributes (IFC102): entity -> attributes that must be empty. */
12995
+ var DEPRECATED_IFC4_ATTRIBUTES = {
12996
+ IFCDOORLININGPROPERTIES: ["ShapeAspectStyle"],
12997
+ IFCDOORPANELPROPERTIES: ["ShapeAspectStyle"],
12998
+ IFCFILLAREASTYLEHATCHING: ["PointOfReferenceHatchLine"],
12999
+ IFCREINFORCINGELEMENT: ["SteelGrade"],
13000
+ IFCRELASSIGNSTOACTOR: ["RelatedObjectsType"],
13001
+ IFCRELASSIGNSTOCONTROL: ["RelatedObjectsType"],
13002
+ IFCRELASSIGNSTOGROUP: ["RelatedObjectsType"],
13003
+ IFCRELASSIGNSTOGROUPBYFACTOR: ["RelatedObjectsType"],
13004
+ IFCRELASSIGNSTOPROCESS: ["RelatedObjectsType"],
13005
+ IFCRELASSIGNSTOPRODUCT: ["RelatedObjectsType"],
13006
+ IFCRELASSIGNSTORESOURCE: ["RelatedObjectsType"],
13007
+ IFCSTAIRFLIGHT: [
13008
+ "NumberOfRisers",
13009
+ "NumberOfTreads",
13010
+ "RiserHeight",
13011
+ "TreadLength"
13012
+ ],
13013
+ IFCWINDOWPANELPROPERTIES: ["ShapeAspectStyle"],
13014
+ IFCWINDOWLININGPROPERTIES: ["ShapeAspectStyle"]
13015
+ };
13016
+ function checkDeprecated(reader) {
13017
+ const issues = [];
13018
+ if (reader.schema !== "IFC4") return issues;
13019
+ for (const id of reader.getAllLines()) {
13020
+ const typeName = reader.typeNameOf(id);
13021
+ if (DEPRECATED_IFC4_ENTITIES.includes(typeName)) {
13022
+ issues.push(issue("error", "DEPRECATED_ENTITY", `#${id} ${typeName} is deprecated in IFC4 (IFC102)`, id));
13023
+ continue;
13024
+ }
13025
+ const attrs = DEPRECATED_IFC4_ATTRIBUTES[typeName];
13026
+ if (attrs === void 0) continue;
13027
+ const line = reader.getLine(id);
13028
+ if (line === null) continue;
13029
+ for (const attr of attrs) if (line[attr] !== null && line[attr] !== void 0) issues.push(issue("error", "DEPRECATED_ATTRIBUTE", `#${id} ${typeName}.${attr} is deprecated in IFC4 and must be empty (IFC102)`, id));
13030
+ }
13031
+ return issues;
13032
+ }
13033
+ function textOf(v) {
13034
+ const value = v?.value;
13035
+ return typeof value === "string" ? value : void 0;
13036
+ }
13037
+ function checkQuantitySets(reader) {
13038
+ const issues = [];
13039
+ const related = /* @__PURE__ */ new Map();
13040
+ for (const relId of reader.getLinesOfType(web_ifc.IFCRELDEFINESBYPROPERTIES)) {
13041
+ const rel = reader.getLine(relId);
13042
+ if (rel === null) continue;
13043
+ const target = rel["RelatingPropertyDefinition"]?.value;
13044
+ if (typeof target !== "number") continue;
13045
+ const objects = rel["RelatedObjects"];
13046
+ if (!Array.isArray(objects)) continue;
13047
+ const names = objects.map((o) => o.value).filter((v) => typeof v === "number").map((id) => reader.typeNameOf(id));
13048
+ related.set(target, [...related.get(target) ?? [], ...names]);
13049
+ }
13050
+ for (const id of reader.getLinesOfType(web_ifc.IFCELEMENTQUANTITY)) {
13051
+ const line = reader.getLine(id);
13052
+ if (line === null) continue;
13053
+ const name = textOf(line["Name"]);
13054
+ if (name === void 0 || !name.startsWith("Qto_")) continue;
13055
+ const definition = QTO_DEFINITIONS[name];
13056
+ if (definition === void 0) {
13057
+ issues.push(issue("error", "QTO_UNKNOWN_SET", `#${id} '${name}' is not a standard quantity set (QTY001)`, id));
13058
+ continue;
13059
+ }
13060
+ if (textOf(line["MethodOfMeasurement"]) !== "BaseQuantities") issues.push(issue("error", "QTO_METHOD", `#${id} '${name}' must declare MethodOfMeasurement='BaseQuantities' (QTY001)`, id));
13061
+ for (const entity of related.get(id) ?? []) if (!definition.entities.includes(entity)) issues.push(issue("error", "QTO_WRONG_ENTITY", `#${id} '${name}' is not applicable to ${entity} (QTY001)`, id));
13062
+ const quantities = line["Quantities"];
13063
+ if (!Array.isArray(quantities)) continue;
13064
+ for (const q of quantities) {
13065
+ const qid = q.value;
13066
+ if (typeof qid !== "number") continue;
13067
+ const qName = textOf(reader.getLine(qid)?.["Name"]);
13068
+ if (qName === void 0) continue;
13069
+ const expectedType = definition.quantities[qName];
13070
+ if (expectedType === void 0) issues.push(issue("error", "QTO_UNKNOWN_QUANTITY", `#${qid} '${qName}' is not defined in ${name} (QTY001)`, qid));
13071
+ else if (reader.typeNameOf(qid) !== expectedType) issues.push(issue("error", "QTO_WRONG_TYPE", `#${qid} '${qName}' must be a ${expectedType} (QTY001)`, qid));
13072
+ }
13073
+ }
13074
+ return issues;
13075
+ }
13076
+ function checkGeoreferencing(reader) {
13077
+ if (!(reader.getLinesOfType(web_ifc.IFCBUILDING).length > 0)) return [];
13078
+ if (reader.getLinesOfType(web_ifc.IFCPROJECTEDCRS).length > 0 || reader.getLinesOfType(web_ifc.IFCGEOGRAPHICCRS).length > 0) return [];
13079
+ return [issue("warning", "NO_GEOREFERENCING", "Model contains a facility but no IfcProjectedCRS/IfcGeographicCRS (GRF003) — set ProjectSpec.crs")];
13080
+ }
13081
+ //#endregion
13082
+ //#region src/serialize/toIfc.ts
13083
+ async function toIfc(model, meta) {
13084
+ const project = model.getProject();
13085
+ if (!project) return (0, brepjs.err)(ifcError("NO_PROJECT", "BimModel has no project — call model.init() first"));
13086
+ const authorName = [meta.author?.givenName, meta.author?.familyName].filter((p) => Boolean(p)).join(" ");
13087
+ const writerResult = await IfcWriter.create(meta.mvdViewDefinition, meta.ifcSchema, {
13088
+ author: authorName,
13089
+ organization: meta.organizationName
13090
+ });
13091
+ if (!writerResult.ok) return writerResult;
13092
+ const w = writerResult.value;
13093
+ w.setModelScope(project.guid);
13094
+ const elements = model.getAllElements();
13095
+ const relationships = model.getAllRelationships();
13096
+ const walls = model.getWalls();
13097
+ const slabs = model.getSlabs();
13098
+ const beams = model.getBeams();
13099
+ const columns = model.getColumns();
13100
+ const doors = model.getDoors();
13101
+ const windows = model.getWindows();
13102
+ const proxies = model.getProxies();
13103
+ const spaces = model.getSpaces();
13104
+ const roofs = model.getRoofs();
13105
+ const curtainWalls = model.getCurtainWalls();
13106
+ const footings = model.getFootings();
13107
+ const piles = model.getPiles();
13108
+ const stairs = model.getStairs();
13109
+ const ramps = model.getRamps();
13110
+ const railings = model.getRailings();
13111
+ const coverings = model.getCoverings();
13112
+ const assemblies = model.getElementAssemblies();
13113
+ const zones = model.getZones();
13114
+ const systems = model.getSystems();
13115
+ const { ownerHistoryId, geomContextId, geomSubContextId, unitAssignmentId, lengthUnitId } = writeHeader(w, meta);
13116
+ if (project.spec.crs !== void 0) writeMapConversion(w, project.spec.crs, geomContextId, lengthUnitId);
13117
+ const densityByElement = buildDensityMap(relationships);
13118
+ const idMap = /* @__PURE__ */ new Map();
13119
+ const placementMap = /* @__PURE__ */ new Map();
13120
+ const projectExpressId = writeProject$1(w, project.guid, project.spec.name, ownerHistoryId, unitAssignmentId, geomContextId);
13121
+ idMap.set(project.localId, projectExpressId);
13122
+ for (const el of elements) {
13123
+ if (el.category !== "SITE") continue;
13124
+ const { entityId, placementId } = writeSite(w, el.guid, el.spec.name, ownerHistoryId);
13125
+ idMap.set(el.localId, entityId);
13126
+ placementMap.set(el.localId, placementId);
13127
+ }
13128
+ for (const el of elements) {
13129
+ if (el.category !== "BUILDING") continue;
13130
+ const parentSiteId = findParentOf(el.localId, relationships);
13131
+ const parentPlacementId = parentSiteId !== null ? placementMap.get(parentSiteId) ?? null : null;
13132
+ const { entityId, placementId } = writeBuilding(w, el.guid, el.spec.name, ownerHistoryId, parentPlacementId);
13133
+ idMap.set(el.localId, entityId);
13134
+ placementMap.set(el.localId, placementId);
13135
+ }
13136
+ for (const el of elements) {
13137
+ if (el.category !== "STOREY") continue;
13138
+ const parentBuildingId = findParentOf(el.localId, relationships);
13139
+ const parentPlacementId = parentBuildingId !== null ? placementMap.get(parentBuildingId) ?? null : null;
13140
+ const { entityId, placementId } = writeStorey(w, el.guid, el.spec.name, el.spec.elevation, ownerHistoryId, parentPlacementId);
13141
+ idMap.set(el.localId, entityId);
13142
+ placementMap.set(el.localId, placementId);
13143
+ }
13144
+ const openingsByWall = /* @__PURE__ */ new Map();
13145
+ for (const rel of relationships) {
13146
+ if (rel.kind !== "VOIDS_WALL") continue;
13147
+ const opening = elements.find((el) => el.localId === rel.openingLocalId);
12136
13148
  if (opening === void 0 || opening.category !== "OPENING") continue;
12137
13149
  if (!isWallOpening(opening.spec)) continue;
12138
13150
  const list = openingsByWall.get(rel.wallLocalId) ?? [];
@@ -12602,11 +13614,13 @@ async function toIfcValidated(model, meta) {
12602
13614
  const geometry = collectGeometryIssues(model);
12603
13615
  const schema = await checkSchema(bytes);
12604
13616
  const roundTrip = await checkRoundTrip(bytes);
13617
+ const gherkin = await checkGherkinRules(bytes);
12605
13618
  const report = { issues: [
12606
13619
  ...integrity.issues,
12607
13620
  ...geometry.issues,
12608
13621
  ...schema.issues,
12609
- ...roundTrip.issues
13622
+ ...roundTrip.issues,
13623
+ ...gherkin
12610
13624
  ] };
12611
13625
  return (0, brepjs.ok)({
12612
13626
  bytes,
@@ -12691,149 +13705,6 @@ function findContainerOf(elementId, relationships) {
12691
13705
  return null;
12692
13706
  }
12693
13707
  //#endregion
12694
- //#region src/import/spfReader.ts
12695
- var SUPPORTED_SCHEMAS = [
12696
- "IFC2X3",
12697
- "IFC4",
12698
- "IFC4X3"
12699
- ];
12700
- /**
12701
- * Foundation layer of the IFC reader: owns the web-ifc WASM model handle and
12702
- * exposes typed line access, GUID↔expressId lookup, world-transform composition,
12703
- * geometry-engine access and STEP string decoding.
12704
- *
12705
- * Lifecycle: created via the static async {@link SpfReader.create}; the caller
12706
- * MUST call {@link SpfReader.close} (it always issues `CloseModel`, preventing
12707
- * WASM handle leaks). The handle is `using`-friendly via `Symbol.dispose`.
12708
- */
12709
- function deleteVector(vec) {
12710
- vec.delete?.();
12711
- }
12712
- var SpfReader = class SpfReader {
12713
- schema;
12714
- modelId;
12715
- #api;
12716
- #closed = false;
12717
- #linesByType = /* @__PURE__ */ new Map();
12718
- constructor(api, modelId, schema) {
12719
- this.#api = api;
12720
- this.modelId = modelId;
12721
- this.schema = schema;
12722
- }
12723
- static async create(bytes, settings = {}) {
12724
- let api;
12725
- try {
12726
- api = new web_ifc.IfcAPI();
12727
- await initIfcApi(api);
12728
- } catch (e) {
12729
- return (0, brepjs.err)(importError("OPEN_MODEL_FAILED", "Failed to initialize web-ifc", e));
12730
- }
12731
- let modelId;
12732
- try {
12733
- modelId = api.OpenModel(bytes, { COORDINATE_TO_ORIGIN: settings.coordinateToOrigin ?? false });
12734
- } catch (e) {
12735
- return (0, brepjs.err)(importError("OPEN_MODEL_FAILED", "web-ifc OpenModel threw", e));
12736
- }
12737
- if (modelId < 0 || !api.IsModelOpen(modelId)) {
12738
- if (modelId >= 0) api.CloseModel(modelId);
12739
- return (0, brepjs.err)(importError("OPEN_MODEL_FAILED", "web-ifc OpenModel returned an invalid model id"));
12740
- }
12741
- const schemaRaw = api.GetModelSchema(modelId);
12742
- if (!SUPPORTED_SCHEMAS.includes(schemaRaw)) {
12743
- api.CloseModel(modelId);
12744
- return (0, brepjs.err)(importError("SCHEMA_UNSUPPORTED", `Unsupported IFC schema "${schemaRaw}"`));
12745
- }
12746
- return (0, brepjs.ok)(new SpfReader(api, modelId, schemaRaw));
12747
- }
12748
- /** Always issues CloseModel; safe to call more than once. */
12749
- close() {
12750
- if (this.#closed) return;
12751
- this.#closed = true;
12752
- this.#api.CloseModel(this.modelId);
12753
- }
12754
- [Symbol.dispose]() {
12755
- this.close();
12756
- }
12757
- /**
12758
- * Raw line object for an express id. With `flatten=false` (default) references
12759
- * appear as `{ type, value }` wrappers and nested entities are not resolved;
12760
- * pass `flatten=true` to recursively inline referenced lines.
12761
- */
12762
- getLine(expressId, flatten = false) {
12763
- return this.#api.GetLine(this.modelId, expressId, flatten) ?? null;
12764
- }
12765
- /** Express ids of every line whose type equals `type` (no inherited types). */
12766
- getLinesOfType(type) {
12767
- const cached = this.#linesByType.get(type);
12768
- if (cached !== void 0) return cached;
12769
- const vec = this.#api.GetLineIDsWithType(this.modelId, type);
12770
- const out = [];
12771
- for (let i = 0; i < vec.size(); i++) out.push(vec.get(i));
12772
- deleteVector(vec);
12773
- this.#linesByType.set(type, out);
12774
- return out;
12775
- }
12776
- /** Express ids of every line in the model. */
12777
- getAllLines() {
12778
- const vec = this.#api.GetAllLines(this.modelId);
12779
- const out = [];
12780
- for (let i = 0; i < vec.size(); i++) out.push(vec.get(i));
12781
- deleteVector(vec);
12782
- return out;
12783
- }
12784
- /** IFC type code for an express id. */
12785
- /** Uppercased IFC entity name of the instance (e.g. `IFCWALL`). */
12786
- typeNameOf(expressId) {
12787
- return this.#api.GetNameFromTypeCode(this.getLineType(expressId)).toUpperCase();
12788
- }
12789
- getLineType(expressId) {
12790
- const t = this.#api.GetLineType(this.modelId, expressId);
12791
- return typeof t === "number" ? t : Number(t?.value ?? t);
12792
- }
12793
- /** Builds web-ifc's internal GUID→expressId index; call before guid lookups. */
12794
- buildGuidMap() {
12795
- this.#api.CreateIfcGuidToExpressIdMapping(this.modelId);
12796
- }
12797
- /** expressId for a GlobalId, or undefined. Requires {@link buildGuidMap} first. */
12798
- expressIdFromGuid(guid) {
12799
- const id = this.#api.GetExpressIdFromGuid(this.modelId, guid);
12800
- return typeof id === "number" ? id : void 0;
12801
- }
12802
- /** GlobalId for an express id, or undefined. Requires {@link buildGuidMap} first. */
12803
- guidFromExpressId(expressId) {
12804
- const guid = this.#api.GetGuidFromExpressId(this.modelId, expressId);
12805
- return typeof guid === "string" ? guid : void 0;
12806
- }
12807
- /**
12808
- * Composed world transform (column-major 16-float matrix) for a placement
12809
- * express id, resolving the full IfcLocalPlacement chain.
12810
- */
12811
- getWorldTransform(placementExpressId) {
12812
- return this.#api.GetWorldTransformMatrix(this.modelId, placementExpressId);
12813
- }
12814
- /** Streams placed meshes for the given product express ids. */
12815
- streamMeshes(expressIds, cb) {
12816
- this.#api.StreamMeshes(this.modelId, expressIds, cb);
12817
- }
12818
- /** Geometry buffers for a geometry express id; caller MUST call `.delete()`. */
12819
- getGeometry(geometryExpressId) {
12820
- return this.#api.GetGeometry(this.modelId, geometryExpressId);
12821
- }
12822
- /** Reads a Float32Array view of vertex data from a WASM pointer. */
12823
- getVertexArray(ptr, size) {
12824
- return this.#api.GetVertexArray(ptr, size);
12825
- }
12826
- /** Reads a Uint32Array view of index data from a WASM pointer. */
12827
- getIndexArray(ptr, size) {
12828
- return this.#api.GetIndexArray(ptr, size);
12829
- }
12830
- /** Decodes IFC STEP string escapes (`\X2\`, `\S\`, `\X\`) in a raw value. */
12831
- decodeText(s) {
12832
- const decoded = this.#api.DecodeText(s);
12833
- return typeof decoded === "string" ? decoded : s;
12834
- }
12835
- };
12836
- //#endregion
12837
13708
  //#region src/import/spatialTree.ts
12838
13709
  var CATEGORY_BY_TYPE = /* @__PURE__ */ new Map([
12839
13710
  [web_ifc.IFCPROJECT, "PROJECT"],
@@ -27026,6 +27897,7 @@ exports.PSET_TEMPLATES = PSET_TEMPLATES;
27026
27897
  exports.SpfReader = SpfReader;
27027
27898
  exports.bcfError = bcfError;
27028
27899
  exports.checkGeometryValidity = checkGeometryValidity;
27900
+ exports.checkGherkinRules = checkGherkinRules;
27029
27901
  exports.checkIds = checkIdsData;
27030
27902
  exports.checkIdsData = checkIdsData;
27031
27903
  exports.checkReferentialIntegrity = checkReferentialIntegrity;