brepjs-bim 0.20.0 → 0.22.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.
- package/README.md +53 -24
- package/dist/brepjs-bim.cjs +950 -137
- package/dist/brepjs-bim.js +950 -139
- package/dist/elementFns/placedGeometry.d.ts +20 -9
- package/dist/familiesAdapter.d.ts +9 -2
- package/dist/ifc-writer/assemblyWriter.d.ts +6 -1
- package/dist/ifc-writer/connectivityWriter.d.ts +6 -1
- package/dist/ifc-writer/entityWriter.d.ts +3 -1
- package/dist/ifc-writer/infrastructureWriter.d.ts +12 -0
- package/dist/ifc-writer/proxyWriter.d.ts +5 -9
- package/dist/ifc-writer/spaceWriter.d.ts +5 -0
- package/dist/ifc-writer/tessellatedProductWriter.d.ts +10 -0
- package/dist/import/importedModel.d.ts +5 -2
- package/dist/import/spatialTree.d.ts +3 -3
- package/dist/index.d.ts +5 -1
- package/dist/model/bimModel.d.ts +10 -1
- package/dist/specs/assemblySpec.d.ts +5 -0
- package/dist/specs/curtainWallSpec.d.ts +5 -0
- package/dist/specs/infrastructureSpec.d.ts +40 -0
- package/dist/specs/proxySpec.d.ts +5 -3
- package/dist/specs/spatialSpec.d.ts +23 -1
- package/dist/types/bimTypes.d.ts +5 -4
- package/dist/validation/roundTrip.d.ts +1 -1
- package/package.json +1 -1
package/dist/brepjs-bim.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { addHoles, applyMatrix, autoHeal, box, castShape, clone, convexHull, cut, err, extrude, fuse, getKernel, getSolids, isClosedWire, isOk, isPlanarWire, isSolid, isValid, isValidSolid, measureVolume, mesh, ok, outerWire, polygon, revolve, rotate, validSolid } from "brepjs";
|
|
1
|
+
import { addHoles, applyMatrix, autoHeal, box, castShape, clone, convexHull, cut, err, extrude, fuse, getKernel, getSolids, isClosedWire, isOk, isPlanarWire, isSolid, isValid, isValidSolid, measureVolume, mesh, ok, outerWire, polygon, revolve, rotate, translate, validSolid } from "brepjs";
|
|
2
2
|
import * as WebIFC from "web-ifc";
|
|
3
3
|
import { Handle, IfcAPI } from "web-ifc";
|
|
4
4
|
//#region src/identity/ifcGuid.ts
|
|
@@ -6297,15 +6297,17 @@ var BimModel = class {
|
|
|
6297
6297
|
#projectId = null;
|
|
6298
6298
|
#modelScope = "";
|
|
6299
6299
|
#usedStableKeys = /* @__PURE__ */ new Set();
|
|
6300
|
-
init(spec) {
|
|
6300
|
+
init(spec, options) {
|
|
6301
6301
|
if (this.#projectId !== null) return err(specError("DUPLICATE_PROJECT", "BimModel.init() called twice — only one project per model"));
|
|
6302
6302
|
this.#modelScope = spec.projectId ?? `${spec.name}::${spec.description ?? ""}`;
|
|
6303
|
-
const
|
|
6303
|
+
const keyCheck = this.#checkStableKey(options);
|
|
6304
|
+
if (!keyCheck.ok) return keyCheck;
|
|
6305
|
+
const id = this.#makeElement("PROJECT", spec, null, options?.stableKey);
|
|
6304
6306
|
this.#projectId = id;
|
|
6305
6307
|
return ok(id);
|
|
6306
6308
|
}
|
|
6307
6309
|
[Symbol.dispose]() {
|
|
6308
|
-
for (const el of this.#elements.values()) if (el.category === "WALL" || el.category === "SLAB" || el.category === "BEAM" || el.category === "COLUMN" || el.category === "PROXY" || el.category === "SPACE" || el.category === "ROOF" || el.category === "FOOTING" || el.category === "PILE" || el.category === "RAILING" || el.category === "COVERING") el.geometry[Symbol.dispose]();
|
|
6310
|
+
for (const el of this.#elements.values()) if (el.category === "WALL" || el.category === "SLAB" || el.category === "BEAM" || el.category === "COLUMN" || el.category === "PROXY" || el.category === "EARTHWORKS_FILL" || el.category === "SPACE" || el.category === "ROOF" || el.category === "FOOTING" || el.category === "PILE" || el.category === "RAILING" || el.category === "COVERING") el.geometry[Symbol.dispose]();
|
|
6309
6311
|
else if (el.category === "CURTAIN_WALL") {
|
|
6310
6312
|
for (const panel of el.geometry.panels) panel.solid[Symbol.dispose]();
|
|
6311
6313
|
for (const mullion of el.geometry.mullions) mullion.solid[Symbol.dispose]();
|
|
@@ -6316,6 +6318,26 @@ var BimModel = class {
|
|
|
6316
6318
|
if (!keyCheck.ok) return keyCheck;
|
|
6317
6319
|
return ok(this.#makeElement("SITE", spec, null, options?.stableKey));
|
|
6318
6320
|
}
|
|
6321
|
+
addBridge(spec, options) {
|
|
6322
|
+
const keyCheck = this.#checkStableKey(options);
|
|
6323
|
+
if (!keyCheck.ok) return keyCheck;
|
|
6324
|
+
return ok(this.#makeElement("BRIDGE", spec, null, options?.stableKey));
|
|
6325
|
+
}
|
|
6326
|
+
addBridgePart(spec, options) {
|
|
6327
|
+
const keyCheck = this.#checkStableKey(options);
|
|
6328
|
+
if (!keyCheck.ok) return keyCheck;
|
|
6329
|
+
return ok(this.#makeElement("BRIDGE_PART", spec, null, options?.stableKey));
|
|
6330
|
+
}
|
|
6331
|
+
/** Adds a typed IfcEarthworksFill body. Ownership of `spec.solid` transfers
|
|
6332
|
+
* to the model only when this call succeeds. */
|
|
6333
|
+
addEarthworksFill(spec, options) {
|
|
6334
|
+
const keyCheck = this.#checkStableKey(options);
|
|
6335
|
+
if (!keyCheck.ok) return keyCheck;
|
|
6336
|
+
if (spec.solid === null || spec.solid === void 0) return err(specError("EARTHWORKS_FILL_NO_GEOMETRY", "EarthworksFillSpec.solid is required"));
|
|
6337
|
+
const id = this.#makeElement("EARTHWORKS_FILL", spec, spec.solid, options?.stableKey);
|
|
6338
|
+
this.#associateMaterial(id, spec);
|
|
6339
|
+
return ok(id);
|
|
6340
|
+
}
|
|
6319
6341
|
addBuilding(spec, options) {
|
|
6320
6342
|
const keyCheck = this.#checkStableKey(options);
|
|
6321
6343
|
if (!keyCheck.ok) return keyCheck;
|
|
@@ -6935,6 +6957,21 @@ var BimModel = class {
|
|
|
6935
6957
|
for (const el of this.#elements.values()) if (el.category === "BEAM") beams.push(el);
|
|
6936
6958
|
return beams;
|
|
6937
6959
|
}
|
|
6960
|
+
getBridges() {
|
|
6961
|
+
const bridges = [];
|
|
6962
|
+
for (const el of this.#elements.values()) if (el.category === "BRIDGE") bridges.push(el);
|
|
6963
|
+
return bridges;
|
|
6964
|
+
}
|
|
6965
|
+
getBridgeParts() {
|
|
6966
|
+
const parts = [];
|
|
6967
|
+
for (const el of this.#elements.values()) if (el.category === "BRIDGE_PART") parts.push(el);
|
|
6968
|
+
return parts;
|
|
6969
|
+
}
|
|
6970
|
+
getEarthworksFills() {
|
|
6971
|
+
const fills = [];
|
|
6972
|
+
for (const el of this.#elements.values()) if (el.category === "EARTHWORKS_FILL") fills.push(el);
|
|
6973
|
+
return fills;
|
|
6974
|
+
}
|
|
6938
6975
|
getColumns() {
|
|
6939
6976
|
const columns = [];
|
|
6940
6977
|
for (const el of this.#elements.values()) if (el.category === "COLUMN") columns.push(el);
|
|
@@ -7359,23 +7396,38 @@ function place(solid, frame) {
|
|
|
7359
7396
|
function disposeAll(solids) {
|
|
7360
7397
|
for (const s of solids) s[Symbol.dispose]();
|
|
7361
7398
|
}
|
|
7399
|
+
function placeWithinParent(solid, localFrame, parentFrame) {
|
|
7400
|
+
try {
|
|
7401
|
+
var _usingCtx$3 = _usingCtx();
|
|
7402
|
+
const local = place(solid, localFrame);
|
|
7403
|
+
if (!local.ok || parentFrame === void 0) return local;
|
|
7404
|
+
return place(_usingCtx$3.u(local.value), parentFrame);
|
|
7405
|
+
} catch (_) {
|
|
7406
|
+
_usingCtx$3.e = _;
|
|
7407
|
+
} finally {
|
|
7408
|
+
_usingCtx$3.d();
|
|
7409
|
+
}
|
|
7410
|
+
}
|
|
7362
7411
|
/**
|
|
7363
|
-
* Returns each element's geometry transformed to its
|
|
7364
|
-
*
|
|
7365
|
-
*
|
|
7366
|
-
*
|
|
7412
|
+
* Returns each element's geometry transformed to its element-local placement,
|
|
7413
|
+
* and optionally through the cumulative frame of its containing spatial
|
|
7414
|
+
* structure, as fresh caller-owned solids. Pass `parentFrame` to obtain world
|
|
7415
|
+
* coordinates for elements beneath a placed Site, Bridge, Bridge Part, or
|
|
7416
|
+
* other spatial structure. **Dispose the returned solids** (e.g. via `using` /
|
|
7417
|
+
* `[Symbol.dispose]`) when you own their lifetime — they are independent of the model
|
|
7367
7418
|
* (`BimModel[Symbol.dispose]` frees only the stored, unplaced `.geometry`). On any
|
|
7368
7419
|
* failure the solids already built for this call are disposed before the error is
|
|
7369
7420
|
* returned, so no partial array is leaked.
|
|
7370
7421
|
*
|
|
7371
7422
|
* Stairs and ramps carry no element solid (`.geometry` is null), so flight
|
|
7372
7423
|
* solids are built from `spec.flights` and placed per flight. Curtain walls
|
|
7373
|
-
* return placed panels + mullions.
|
|
7374
|
-
*
|
|
7375
|
-
*
|
|
7376
|
-
* an empty array.
|
|
7424
|
+
* return placed panels + mullions. Proxy and Earthworks Fill bodies are stored
|
|
7425
|
+
* relative to their containing spatial structure, so their body is copied and
|
|
7426
|
+
* then transformed by `parentFrame` when supplied. Elements with no solid geometry
|
|
7427
|
+
* (doors/windows/groups/spatial) return an empty array.
|
|
7377
7428
|
*/
|
|
7378
|
-
function placedSolids(el) {
|
|
7429
|
+
function placedSolids(el, options = {}) {
|
|
7430
|
+
const parentFrame = options.parentFrame;
|
|
7379
7431
|
switch (el.category) {
|
|
7380
7432
|
case "WALL":
|
|
7381
7433
|
case "SLAB":
|
|
@@ -7387,56 +7439,57 @@ function placedSolids(el) {
|
|
|
7387
7439
|
case "PILE":
|
|
7388
7440
|
case "COVERING":
|
|
7389
7441
|
case "RAILING": {
|
|
7390
|
-
const placed =
|
|
7442
|
+
const placed = placeWithinParent(el.geometry, el.spec, parentFrame);
|
|
7391
7443
|
if (!placed.ok) return placed;
|
|
7392
7444
|
return ok([placed.value]);
|
|
7393
7445
|
}
|
|
7394
7446
|
case "STAIR": {
|
|
7395
7447
|
const out = [];
|
|
7396
7448
|
for (const flight of el.spec.flights) try {
|
|
7397
|
-
var
|
|
7449
|
+
var _usingCtx3 = _usingCtx();
|
|
7398
7450
|
const built = stairFlightToSolid(flight);
|
|
7399
7451
|
if (!built.ok) {
|
|
7400
7452
|
disposeAll(out);
|
|
7401
7453
|
return err(built.error);
|
|
7402
7454
|
}
|
|
7403
|
-
const placed =
|
|
7455
|
+
const placed = placeWithinParent(_usingCtx3.u(built.value.solid), flight, parentFrame);
|
|
7404
7456
|
if (!placed.ok) {
|
|
7405
7457
|
disposeAll(out);
|
|
7406
7458
|
return placed;
|
|
7407
7459
|
}
|
|
7408
7460
|
out.push(placed.value);
|
|
7409
7461
|
} catch (_) {
|
|
7410
|
-
|
|
7462
|
+
_usingCtx3.e = _;
|
|
7411
7463
|
} finally {
|
|
7412
|
-
|
|
7464
|
+
_usingCtx3.d();
|
|
7413
7465
|
}
|
|
7414
7466
|
return ok(out);
|
|
7415
7467
|
}
|
|
7416
7468
|
case "RAMP": {
|
|
7417
7469
|
const out = [];
|
|
7418
7470
|
for (const flight of el.spec.flights) try {
|
|
7419
|
-
var
|
|
7471
|
+
var _usingCtx4 = _usingCtx();
|
|
7420
7472
|
const built = rampFlightToSolid(flight);
|
|
7421
7473
|
if (!built.ok) {
|
|
7422
7474
|
disposeAll(out);
|
|
7423
7475
|
return err(built.error);
|
|
7424
7476
|
}
|
|
7425
|
-
const placed =
|
|
7477
|
+
const placed = placeWithinParent(_usingCtx4.u(built.value.solid), flight, parentFrame);
|
|
7426
7478
|
if (!placed.ok) {
|
|
7427
7479
|
disposeAll(out);
|
|
7428
7480
|
return placed;
|
|
7429
7481
|
}
|
|
7430
7482
|
out.push(placed.value);
|
|
7431
7483
|
} catch (_) {
|
|
7432
|
-
|
|
7484
|
+
_usingCtx4.e = _;
|
|
7433
7485
|
} finally {
|
|
7434
|
-
|
|
7486
|
+
_usingCtx4.d();
|
|
7435
7487
|
}
|
|
7436
7488
|
return ok(out);
|
|
7437
7489
|
}
|
|
7438
|
-
case "PROXY":
|
|
7439
|
-
|
|
7490
|
+
case "PROXY":
|
|
7491
|
+
case "EARTHWORKS_FILL": {
|
|
7492
|
+
const placed = placeWithinParent(el.geometry, {
|
|
7440
7493
|
origin: [
|
|
7441
7494
|
0,
|
|
7442
7495
|
0,
|
|
@@ -7452,14 +7505,14 @@ function placedSolids(el) {
|
|
|
7452
7505
|
0,
|
|
7453
7506
|
1
|
|
7454
7507
|
]
|
|
7455
|
-
});
|
|
7508
|
+
}, parentFrame);
|
|
7456
7509
|
if (!placed.ok) return placed;
|
|
7457
7510
|
return ok([placed.value]);
|
|
7458
7511
|
}
|
|
7459
7512
|
case "CURTAIN_WALL": {
|
|
7460
7513
|
const out = [];
|
|
7461
7514
|
for (const c of [...el.geometry.panels, ...el.geometry.mullions]) try {
|
|
7462
|
-
var
|
|
7515
|
+
var _usingCtx5 = _usingCtx();
|
|
7463
7516
|
const componentLocal = place(c.solid, {
|
|
7464
7517
|
origin: c.origin,
|
|
7465
7518
|
axisX: [
|
|
@@ -7477,16 +7530,16 @@ function placedSolids(el) {
|
|
|
7477
7530
|
disposeAll(out);
|
|
7478
7531
|
return componentLocal;
|
|
7479
7532
|
}
|
|
7480
|
-
const placed =
|
|
7533
|
+
const placed = placeWithinParent(_usingCtx5.u(componentLocal.value), el.spec, parentFrame);
|
|
7481
7534
|
if (!placed.ok) {
|
|
7482
7535
|
disposeAll(out);
|
|
7483
7536
|
return placed;
|
|
7484
7537
|
}
|
|
7485
7538
|
out.push(placed.value);
|
|
7486
7539
|
} catch (_) {
|
|
7487
|
-
|
|
7540
|
+
_usingCtx5.e = _;
|
|
7488
7541
|
} finally {
|
|
7489
|
-
|
|
7542
|
+
_usingCtx5.d();
|
|
7490
7543
|
}
|
|
7491
7544
|
return ok(out);
|
|
7492
7545
|
}
|
|
@@ -7541,6 +7594,8 @@ var IFC4X3_ONLY_ENTITIES = /* @__PURE__ */ new Set([
|
|
|
7541
7594
|
"IfcRoad",
|
|
7542
7595
|
"IfcRailway",
|
|
7543
7596
|
"IfcBridge",
|
|
7597
|
+
"IfcBridgePart",
|
|
7598
|
+
"IfcEarthworksFill",
|
|
7544
7599
|
"IfcMarineFacility"
|
|
7545
7600
|
]);
|
|
7546
7601
|
/**
|
|
@@ -7999,34 +8054,23 @@ function writeProject$1(w, guid, name, ownerHistoryId, unitAssignmentId, geomCon
|
|
|
7999
8054
|
});
|
|
8000
8055
|
return id;
|
|
8001
8056
|
}
|
|
8002
|
-
function writeSite(w, guid,
|
|
8003
|
-
const
|
|
8004
|
-
0,
|
|
8005
|
-
0,
|
|
8006
|
-
0
|
|
8007
|
-
]);
|
|
8008
|
-
const localPlacementId = w.nextId();
|
|
8009
|
-
w.writeLine({
|
|
8010
|
-
expressID: localPlacementId,
|
|
8011
|
-
type: WebIFC.IFCLOCALPLACEMENT,
|
|
8012
|
-
PlacementRelTo: null,
|
|
8013
|
-
RelativePlacement: w.ref(placement3DId)
|
|
8014
|
-
});
|
|
8057
|
+
function writeSite(w, guid, spec, ownerHistoryId, parentPlacementId = null) {
|
|
8058
|
+
const localPlacementId = writeSpatialLocalPlacement(w, spec, parentPlacementId);
|
|
8015
8059
|
const entityId = w.nextId();
|
|
8016
8060
|
w.writeLine({
|
|
8017
8061
|
expressID: entityId,
|
|
8018
8062
|
type: WebIFC.IFCSITE,
|
|
8019
8063
|
GlobalId: w.mkType(WebIFC.IFCGLOBALLYUNIQUEID, guid),
|
|
8020
8064
|
OwnerHistory: w.ref(ownerHistoryId),
|
|
8021
|
-
Name: w.mkType(WebIFC.IFCLABEL, name),
|
|
8022
|
-
Description: null,
|
|
8065
|
+
Name: w.mkType(WebIFC.IFCLABEL, spec.name),
|
|
8066
|
+
Description: spec.description ? w.mkType(WebIFC.IFCTEXT, spec.description) : null,
|
|
8023
8067
|
ObjectType: null,
|
|
8024
8068
|
ObjectPlacement: w.ref(localPlacementId),
|
|
8025
8069
|
Representation: null,
|
|
8026
8070
|
LongName: null,
|
|
8027
8071
|
CompositionType: {
|
|
8028
8072
|
type: 3,
|
|
8029
|
-
value: "ELEMENT"
|
|
8073
|
+
value: spec.compositionType ?? "ELEMENT"
|
|
8030
8074
|
},
|
|
8031
8075
|
RefLatitude: null,
|
|
8032
8076
|
RefLongitude: null,
|
|
@@ -8039,6 +8083,34 @@ function writeSite(w, guid, name, ownerHistoryId) {
|
|
|
8039
8083
|
placementId: localPlacementId
|
|
8040
8084
|
};
|
|
8041
8085
|
}
|
|
8086
|
+
function writeSpatialLocalPlacement(w, spec, parentPlacementId) {
|
|
8087
|
+
const toMetres = (value) => [
|
|
8088
|
+
toIfcLengthM(value[0]),
|
|
8089
|
+
toIfcLengthM(value[1]),
|
|
8090
|
+
toIfcLengthM(value[2])
|
|
8091
|
+
];
|
|
8092
|
+
const placement3DId = writeAxis2Placement3D(w, toMetres(spec.origin ?? [
|
|
8093
|
+
0,
|
|
8094
|
+
0,
|
|
8095
|
+
0
|
|
8096
|
+
]), spec.axisZ ?? [
|
|
8097
|
+
0,
|
|
8098
|
+
0,
|
|
8099
|
+
1
|
|
8100
|
+
], spec.axisX ?? [
|
|
8101
|
+
1,
|
|
8102
|
+
0,
|
|
8103
|
+
0
|
|
8104
|
+
]);
|
|
8105
|
+
const localPlacementId = w.nextId();
|
|
8106
|
+
w.writeLine({
|
|
8107
|
+
expressID: localPlacementId,
|
|
8108
|
+
type: WebIFC.IFCLOCALPLACEMENT,
|
|
8109
|
+
PlacementRelTo: parentPlacementId !== null ? w.ref(parentPlacementId) : null,
|
|
8110
|
+
RelativePlacement: w.ref(placement3DId)
|
|
8111
|
+
});
|
|
8112
|
+
return localPlacementId;
|
|
8113
|
+
}
|
|
8042
8114
|
function writeBuilding(w, guid, name, ownerHistoryId, parentPlacementId) {
|
|
8043
8115
|
const placement3DId = writeAxis2Placement3D(w, [
|
|
8044
8116
|
0,
|
|
@@ -10158,6 +10230,111 @@ function writeRelNests(w, guid, ownerHistoryId, relatingObjectId, relatedObjectI
|
|
|
10158
10230
|
});
|
|
10159
10231
|
}
|
|
10160
10232
|
//#endregion
|
|
10233
|
+
//#region src/ifc-writer/infrastructureWriter.ts
|
|
10234
|
+
function writeBridge(w, guid, spec, ownerHistoryId, parentPlacementId) {
|
|
10235
|
+
const placementId = writeSpatialLocalPlacement(w, spec, parentPlacementId);
|
|
10236
|
+
const entityId = w.nextId();
|
|
10237
|
+
w.writeLine({
|
|
10238
|
+
expressID: entityId,
|
|
10239
|
+
type: WebIFC.IFCBRIDGE,
|
|
10240
|
+
GlobalId: w.mkType(WebIFC.IFCGLOBALLYUNIQUEID, guid),
|
|
10241
|
+
OwnerHistory: w.ref(ownerHistoryId),
|
|
10242
|
+
Name: w.mkType(WebIFC.IFCLABEL, spec.name),
|
|
10243
|
+
Description: spec.description ? w.mkType(WebIFC.IFCTEXT, spec.description) : null,
|
|
10244
|
+
ObjectType: null,
|
|
10245
|
+
ObjectPlacement: w.ref(placementId),
|
|
10246
|
+
Representation: null,
|
|
10247
|
+
LongName: null,
|
|
10248
|
+
CompositionType: {
|
|
10249
|
+
type: 3,
|
|
10250
|
+
value: spec.compositionType ?? "ELEMENT"
|
|
10251
|
+
},
|
|
10252
|
+
PredefinedType: {
|
|
10253
|
+
type: 3,
|
|
10254
|
+
value: spec.predefinedType ?? "NOTDEFINED"
|
|
10255
|
+
}
|
|
10256
|
+
});
|
|
10257
|
+
return {
|
|
10258
|
+
entityId,
|
|
10259
|
+
placementId
|
|
10260
|
+
};
|
|
10261
|
+
}
|
|
10262
|
+
function writeBridgePart(w, guid, spec, ownerHistoryId, parentPlacementId) {
|
|
10263
|
+
const placementId = writeSpatialLocalPlacement(w, spec, parentPlacementId);
|
|
10264
|
+
const entityId = w.nextId();
|
|
10265
|
+
w.writeLine({
|
|
10266
|
+
expressID: entityId,
|
|
10267
|
+
type: WebIFC.IFCBRIDGEPART,
|
|
10268
|
+
GlobalId: w.mkType(WebIFC.IFCGLOBALLYUNIQUEID, guid),
|
|
10269
|
+
OwnerHistory: w.ref(ownerHistoryId),
|
|
10270
|
+
Name: w.mkType(WebIFC.IFCLABEL, spec.name),
|
|
10271
|
+
Description: spec.description ? w.mkType(WebIFC.IFCTEXT, spec.description) : null,
|
|
10272
|
+
ObjectType: null,
|
|
10273
|
+
ObjectPlacement: w.ref(placementId),
|
|
10274
|
+
Representation: null,
|
|
10275
|
+
LongName: null,
|
|
10276
|
+
CompositionType: {
|
|
10277
|
+
type: 3,
|
|
10278
|
+
value: spec.compositionType ?? "ELEMENT"
|
|
10279
|
+
},
|
|
10280
|
+
UsageType: {
|
|
10281
|
+
type: 3,
|
|
10282
|
+
value: spec.usageType
|
|
10283
|
+
},
|
|
10284
|
+
PredefinedType: {
|
|
10285
|
+
type: 3,
|
|
10286
|
+
value: spec.predefinedType ?? "NOTDEFINED"
|
|
10287
|
+
}
|
|
10288
|
+
});
|
|
10289
|
+
return {
|
|
10290
|
+
entityId,
|
|
10291
|
+
placementId
|
|
10292
|
+
};
|
|
10293
|
+
}
|
|
10294
|
+
function writeEarthworksFillEntity(w, guid, spec, ownerHistoryId, localPlacementId, productDefinitionShapeId) {
|
|
10295
|
+
const entityId = w.nextId();
|
|
10296
|
+
w.writeLine({
|
|
10297
|
+
expressID: entityId,
|
|
10298
|
+
type: WebIFC.IFCEARTHWORKSFILL,
|
|
10299
|
+
GlobalId: w.mkType(WebIFC.IFCGLOBALLYUNIQUEID, guid),
|
|
10300
|
+
OwnerHistory: w.ref(ownerHistoryId),
|
|
10301
|
+
Name: w.mkType(WebIFC.IFCLABEL, spec.name),
|
|
10302
|
+
Description: null,
|
|
10303
|
+
ObjectType: null,
|
|
10304
|
+
ObjectPlacement: w.ref(localPlacementId),
|
|
10305
|
+
Representation: w.ref(productDefinitionShapeId),
|
|
10306
|
+
Tag: null,
|
|
10307
|
+
PredefinedType: {
|
|
10308
|
+
type: 3,
|
|
10309
|
+
value: spec.predefinedType ?? "NOTDEFINED"
|
|
10310
|
+
}
|
|
10311
|
+
});
|
|
10312
|
+
return entityId;
|
|
10313
|
+
}
|
|
10314
|
+
//#endregion
|
|
10315
|
+
//#region src/ifc-writer/tessellatedProductWriter.ts
|
|
10316
|
+
/** Write a tessellated body in the coordinate frame supplied by the caller. */
|
|
10317
|
+
function writeTessellatedProductGeometry(w, solid, geomSubContextId, parentPlacementId) {
|
|
10318
|
+
const placement3DId = writeAxis2Placement3D(w, [
|
|
10319
|
+
0,
|
|
10320
|
+
0,
|
|
10321
|
+
0
|
|
10322
|
+
]);
|
|
10323
|
+
const localPlacementId = w.nextId();
|
|
10324
|
+
w.writeLine({
|
|
10325
|
+
expressID: localPlacementId,
|
|
10326
|
+
type: WebIFC.IFCLOCALPLACEMENT,
|
|
10327
|
+
PlacementRelTo: parentPlacementId !== null ? w.ref(parentPlacementId) : null,
|
|
10328
|
+
RelativePlacement: w.ref(placement3DId)
|
|
10329
|
+
});
|
|
10330
|
+
const tessellation = writeTessellation(w, solid, geomSubContextId, localPlacementId);
|
|
10331
|
+
return {
|
|
10332
|
+
localPlacementId,
|
|
10333
|
+
productDefinitionShapeId: tessellation.productDefinitionShapeId,
|
|
10334
|
+
tessellation
|
|
10335
|
+
};
|
|
10336
|
+
}
|
|
10337
|
+
//#endregion
|
|
10161
10338
|
//#region src/ifc-writer/groupWriter.ts
|
|
10162
10339
|
/**
|
|
10163
10340
|
* Emits an IfcZone grouping object. `longName` (a descriptive label such as
|
|
@@ -11636,29 +11813,12 @@ function writeWindowCommonPset(w, ownerHistoryId, windowEntityId, spec) {
|
|
|
11636
11813
|
//#region src/ifc-writer/proxyWriter.ts
|
|
11637
11814
|
/**
|
|
11638
11815
|
* Writes the IfcLocalPlacement (at origin, relative to parentPlacementId) and a
|
|
11639
|
-
* tessellated body (IfcTriangulatedFaceSet) for a proxy's solid.
|
|
11640
|
-
*
|
|
11641
|
-
*
|
|
11816
|
+
* tessellated body (IfcTriangulatedFaceSet) for a proxy's solid. Solid
|
|
11817
|
+
* coordinates are emitted as supplied; the caller is responsible for matching
|
|
11818
|
+
* them to the containing spatial structure's frame.
|
|
11642
11819
|
*/
|
|
11643
11820
|
function writeProxyGeometry(w, spec, geomSubContextId, parentPlacementId) {
|
|
11644
|
-
|
|
11645
|
-
0,
|
|
11646
|
-
0,
|
|
11647
|
-
0
|
|
11648
|
-
]);
|
|
11649
|
-
const localPlacementId = w.nextId();
|
|
11650
|
-
w.writeLine({
|
|
11651
|
-
expressID: localPlacementId,
|
|
11652
|
-
type: WebIFC.IFCLOCALPLACEMENT,
|
|
11653
|
-
PlacementRelTo: parentPlacementId !== null ? w.ref(parentPlacementId) : null,
|
|
11654
|
-
RelativePlacement: w.ref(placement3DId)
|
|
11655
|
-
});
|
|
11656
|
-
const tessellation = writeTessellation(w, spec.solid, geomSubContextId, localPlacementId);
|
|
11657
|
-
return {
|
|
11658
|
-
localPlacementId,
|
|
11659
|
-
productDefinitionShapeId: tessellation.productDefinitionShapeId,
|
|
11660
|
-
tessellation
|
|
11661
|
-
};
|
|
11821
|
+
return writeTessellatedProductGeometry(w, spec.solid, geomSubContextId, parentPlacementId);
|
|
11662
11822
|
}
|
|
11663
11823
|
function writeProxyEntity(w, guid, name, predefinedType, ownerHistoryId, localPlacementId, productDefinitionShapeId) {
|
|
11664
11824
|
const id = w.nextId();
|
|
@@ -11909,6 +12069,7 @@ var CONTAINABLE_CATEGORIES = /* @__PURE__ */ new Set([
|
|
|
11909
12069
|
"SLAB",
|
|
11910
12070
|
"BEAM",
|
|
11911
12071
|
"COLUMN",
|
|
12072
|
+
"EARTHWORKS_FILL",
|
|
11912
12073
|
"DOOR",
|
|
11913
12074
|
"WINDOW"
|
|
11914
12075
|
]);
|
|
@@ -12028,7 +12189,9 @@ function hasSpatialStructure(api, modelId) {
|
|
|
12028
12189
|
return [
|
|
12029
12190
|
WebIFC.IFCSITE,
|
|
12030
12191
|
WebIFC.IFCBUILDING,
|
|
12031
|
-
WebIFC.IFCBUILDINGSTOREY
|
|
12192
|
+
WebIFC.IFCBUILDINGSTOREY,
|
|
12193
|
+
WebIFC.IFCBRIDGE,
|
|
12194
|
+
WebIFC.IFCBRIDGEPART
|
|
12032
12195
|
].some((t) => api.GetLineIDsWithType(modelId, t).size() > 0);
|
|
12033
12196
|
}
|
|
12034
12197
|
function checkGlobalIds(api, modelId, lineIds, lineCount) {
|
|
@@ -12072,6 +12235,7 @@ var KEY_ENTITY_NAMES = [
|
|
|
12072
12235
|
"IfcSlab",
|
|
12073
12236
|
"IfcBeam",
|
|
12074
12237
|
"IfcColumn",
|
|
12238
|
+
"IfcEarthworksFill",
|
|
12075
12239
|
"IfcRelContainedInSpatialStructure",
|
|
12076
12240
|
"IfcRelAggregates",
|
|
12077
12241
|
"IfcPropertySet"
|
|
@@ -12082,6 +12246,7 @@ var KEY_ENTITY_TYPES = [
|
|
|
12082
12246
|
["IfcSlab", WebIFC.IFCSLAB],
|
|
12083
12247
|
["IfcBeam", WebIFC.IFCBEAM],
|
|
12084
12248
|
["IfcColumn", WebIFC.IFCCOLUMN],
|
|
12249
|
+
["IfcEarthworksFill", WebIFC.IFCEARTHWORKSFILL],
|
|
12085
12250
|
["IfcRelContainedInSpatialStructure", WebIFC.IFCRELCONTAINEDINSPATIALSTRUCTURE],
|
|
12086
12251
|
["IfcRelAggregates", WebIFC.IFCRELAGGREGATES],
|
|
12087
12252
|
["IfcPropertySet", WebIFC.IFCPROPERTYSET]
|
|
@@ -13074,7 +13239,7 @@ function checkQuantitySets(reader) {
|
|
|
13074
13239
|
return issues;
|
|
13075
13240
|
}
|
|
13076
13241
|
function checkGeoreferencing(reader) {
|
|
13077
|
-
if (!(reader.getLinesOfType(WebIFC.IFCBUILDING).length > 0)) return [];
|
|
13242
|
+
if (!(reader.getLinesOfType(WebIFC.IFCBUILDING).length > 0 || reader.getLinesOfType(WebIFC.IFCBRIDGE).length > 0)) return [];
|
|
13078
13243
|
if (reader.getLinesOfType(WebIFC.IFCPROJECTEDCRS).length > 0 || reader.getLinesOfType(WebIFC.IFCGEOGRAPHICCRS).length > 0) return [];
|
|
13079
13244
|
return [issue("warning", "NO_GEOREFERENCING", "Model contains a facility but no IfcProjectedCRS/IfcGeographicCRS (GRF003) — set ProjectSpec.crs")];
|
|
13080
13245
|
}
|
|
@@ -13083,6 +13248,7 @@ function checkGeoreferencing(reader) {
|
|
|
13083
13248
|
async function toIfc(model, meta) {
|
|
13084
13249
|
const project = model.getProject();
|
|
13085
13250
|
if (!project) return err(ifcError("NO_PROJECT", "BimModel has no project — call model.init() first"));
|
|
13251
|
+
if ((model.getBridges().length > 0 || model.getBridgeParts().length > 0 || model.getEarthworksFills().length > 0) && (meta.ifcSchema ?? "IFC4") !== "IFC4X3") return err(ifcError("IFC4X3_REQUIRED", "Bridge, Bridge Part, and Earthworks Fill entities require IFC4X3 serialization"));
|
|
13086
13252
|
const authorName = [meta.author?.givenName, meta.author?.familyName].filter((p) => Boolean(p)).join(" ");
|
|
13087
13253
|
const writerResult = await IfcWriter.create(meta.mvdViewDefinition, meta.ifcSchema, {
|
|
13088
13254
|
author: authorName,
|
|
@@ -13096,6 +13262,9 @@ async function toIfc(model, meta) {
|
|
|
13096
13262
|
const walls = model.getWalls();
|
|
13097
13263
|
const slabs = model.getSlabs();
|
|
13098
13264
|
const beams = model.getBeams();
|
|
13265
|
+
const bridges = model.getBridges();
|
|
13266
|
+
const bridgeParts = model.getBridgeParts();
|
|
13267
|
+
const earthworksFills = model.getEarthworksFills();
|
|
13099
13268
|
const columns = model.getColumns();
|
|
13100
13269
|
const doors = model.getDoors();
|
|
13101
13270
|
const windows = model.getWindows();
|
|
@@ -13121,10 +13290,33 @@ async function toIfc(model, meta) {
|
|
|
13121
13290
|
idMap.set(project.localId, projectExpressId);
|
|
13122
13291
|
for (const el of elements) {
|
|
13123
13292
|
if (el.category !== "SITE") continue;
|
|
13124
|
-
const
|
|
13293
|
+
const parentId = findParentOf(el.localId, relationships);
|
|
13294
|
+
const parentPlacementId = parentId !== null ? placementMap.get(parentId) ?? null : null;
|
|
13295
|
+
const { entityId, placementId } = writeSite(w, el.guid, el.spec, ownerHistoryId, parentPlacementId);
|
|
13125
13296
|
idMap.set(el.localId, entityId);
|
|
13126
13297
|
placementMap.set(el.localId, placementId);
|
|
13127
13298
|
}
|
|
13299
|
+
for (const bridge of bridges) {
|
|
13300
|
+
const parentId = findParentOf(bridge.localId, relationships);
|
|
13301
|
+
const parentPlacementId = parentId !== null ? placementMap.get(parentId) ?? null : null;
|
|
13302
|
+
const { entityId, placementId } = writeBridge(w, bridge.guid, bridge.spec, ownerHistoryId, parentPlacementId);
|
|
13303
|
+
idMap.set(bridge.localId, entityId);
|
|
13304
|
+
placementMap.set(bridge.localId, placementId);
|
|
13305
|
+
}
|
|
13306
|
+
const pendingParts = [...bridgeParts];
|
|
13307
|
+
while (pendingParts.length > 0) {
|
|
13308
|
+
const readyIndex = pendingParts.findIndex((candidate) => {
|
|
13309
|
+
const candidateParentId = findParentOf(candidate.localId, relationships);
|
|
13310
|
+
return candidateParentId === null || placementMap.has(candidateParentId);
|
|
13311
|
+
});
|
|
13312
|
+
const [part] = pendingParts.splice(readyIndex === -1 ? 0 : readyIndex, 1);
|
|
13313
|
+
if (part === void 0) break;
|
|
13314
|
+
const parentId = findParentOf(part.localId, relationships);
|
|
13315
|
+
const parentPlacementId = parentId !== null ? placementMap.get(parentId) ?? null : null;
|
|
13316
|
+
const { entityId, placementId } = writeBridgePart(w, part.guid, part.spec, ownerHistoryId, parentPlacementId);
|
|
13317
|
+
idMap.set(part.localId, entityId);
|
|
13318
|
+
placementMap.set(part.localId, placementId);
|
|
13319
|
+
}
|
|
13128
13320
|
for (const el of elements) {
|
|
13129
13321
|
if (el.category !== "BUILDING") continue;
|
|
13130
13322
|
const parentSiteId = findParentOf(el.localId, relationships);
|
|
@@ -13218,6 +13410,15 @@ async function toIfc(model, meta) {
|
|
|
13218
13410
|
placementMap.set(proxy.localId, localPlacementId);
|
|
13219
13411
|
if (proxy.spec.customProperties !== void 0) writeCustomPsets(w, ownerHistoryId, proxyExpressId, proxy.spec.customProperties);
|
|
13220
13412
|
}
|
|
13413
|
+
for (const fill of earthworksFills) {
|
|
13414
|
+
const containingId = findContainerOf(fill.localId, relationships);
|
|
13415
|
+
const parentPlacementId = containingId !== null ? placementMap.get(containingId) ?? null : null;
|
|
13416
|
+
const { localPlacementId, productDefinitionShapeId } = writeTessellatedProductGeometry(w, fill.geometry, geomSubContextId, parentPlacementId);
|
|
13417
|
+
const expressId = writeEarthworksFillEntity(w, fill.guid, fill.spec, ownerHistoryId, localPlacementId, productDefinitionShapeId);
|
|
13418
|
+
idMap.set(fill.localId, expressId);
|
|
13419
|
+
placementMap.set(fill.localId, localPlacementId);
|
|
13420
|
+
if (fill.spec.customProperties !== void 0) writeCustomPsets(w, ownerHistoryId, expressId, fill.spec.customProperties);
|
|
13421
|
+
}
|
|
13221
13422
|
for (const [i, space] of spaces.entries()) {
|
|
13222
13423
|
const containingId = findContainerOf(space.localId, relationships);
|
|
13223
13424
|
const storeyPlacementId = containingId !== null ? placementMap.get(containingId) ?? null : null;
|
|
@@ -13640,6 +13841,7 @@ function collectGeometryIssues(model) {
|
|
|
13640
13841
|
["Beam", model.getBeams()],
|
|
13641
13842
|
["Column", model.getColumns()],
|
|
13642
13843
|
["Proxy", model.getProxies()],
|
|
13844
|
+
["Earthworks Fill", model.getEarthworksFills()],
|
|
13643
13845
|
["Space", model.getSpaces()],
|
|
13644
13846
|
["Roof", model.getRoofs()],
|
|
13645
13847
|
["Footing", model.getFootings()],
|
|
@@ -13710,7 +13912,9 @@ var CATEGORY_BY_TYPE = /* @__PURE__ */ new Map([
|
|
|
13710
13912
|
[WebIFC.IFCPROJECT, "PROJECT"],
|
|
13711
13913
|
[WebIFC.IFCSITE, "SITE"],
|
|
13712
13914
|
[WebIFC.IFCBUILDING, "BUILDING"],
|
|
13713
|
-
[WebIFC.IFCBUILDINGSTOREY, "STOREY"]
|
|
13915
|
+
[WebIFC.IFCBUILDINGSTOREY, "STOREY"],
|
|
13916
|
+
[WebIFC.IFCBRIDGE, "BRIDGE"],
|
|
13917
|
+
[WebIFC.IFCBRIDGEPART, "BRIDGE_PART"]
|
|
13714
13918
|
]);
|
|
13715
13919
|
function refId$1(ref) {
|
|
13716
13920
|
const v = ref?.value;
|
|
@@ -14793,6 +14997,7 @@ var ELEMENT_TYPES = [
|
|
|
14793
14997
|
[WebIFC.IFCRAILING, "RAILING"],
|
|
14794
14998
|
[WebIFC.IFCCOVERING, "COVERING"],
|
|
14795
14999
|
[WebIFC.IFCELEMENTASSEMBLY, "ELEMENT_ASSEMBLY"],
|
|
15000
|
+
[WebIFC.IFCEARTHWORKSFILL, "EARTHWORKS_FILL"],
|
|
14796
15001
|
[WebIFC.IFCBUILDINGELEMENTPROXY, "PROXY"]
|
|
14797
15002
|
];
|
|
14798
15003
|
/**
|
|
@@ -14870,14 +15075,17 @@ function readElement(reader, expressId, category, scale, containment, typeEnums,
|
|
|
14870
15075
|
const psets = readPsets(reader, expressId).map(toImportedPset);
|
|
14871
15076
|
const material = readMaterial(reader, expressId, scale);
|
|
14872
15077
|
const classification = readClassification(reader, expressId);
|
|
14873
|
-
const
|
|
15078
|
+
const spatialStructureExpressId = containment.get(expressId);
|
|
14874
15079
|
return {
|
|
14875
15080
|
expressId,
|
|
14876
15081
|
guid,
|
|
14877
15082
|
name,
|
|
14878
15083
|
category,
|
|
14879
15084
|
...predefinedType !== void 0 ? { predefinedType } : {},
|
|
14880
|
-
...
|
|
15085
|
+
...spatialStructureExpressId !== void 0 ? {
|
|
15086
|
+
spatialStructureExpressId,
|
|
15087
|
+
storeyExpressId: spatialStructureExpressId
|
|
15088
|
+
} : {},
|
|
14881
15089
|
geometry,
|
|
14882
15090
|
psets,
|
|
14883
15091
|
material,
|
|
@@ -15877,6 +16085,115 @@ function parseSlabOpeningInput(input) {
|
|
|
15877
16085
|
return ok(result.data);
|
|
15878
16086
|
}
|
|
15879
16087
|
//#endregion
|
|
16088
|
+
//#region src/specs/spatialSpec.ts
|
|
16089
|
+
var unitVector = tuple([
|
|
16090
|
+
number(),
|
|
16091
|
+
number(),
|
|
16092
|
+
number()
|
|
16093
|
+
]).refine((value) => Math.abs(value[0] ** 2 + value[1] ** 2 + value[2] ** 2 - 1) < 1e-6, { error: "must be a unit vector" });
|
|
16094
|
+
var spatialPlacementFields = {
|
|
16095
|
+
origin: tuple([
|
|
16096
|
+
number(),
|
|
16097
|
+
number(),
|
|
16098
|
+
number()
|
|
16099
|
+
]).optional(),
|
|
16100
|
+
axisX: unitVector.optional(),
|
|
16101
|
+
axisZ: unitVector.optional()
|
|
16102
|
+
};
|
|
16103
|
+
function validateSpatialAxes(data, ctx) {
|
|
16104
|
+
const axisX = data.axisX ?? [
|
|
16105
|
+
1,
|
|
16106
|
+
0,
|
|
16107
|
+
0
|
|
16108
|
+
];
|
|
16109
|
+
const axisZ = data.axisZ ?? [
|
|
16110
|
+
0,
|
|
16111
|
+
0,
|
|
16112
|
+
1
|
|
16113
|
+
];
|
|
16114
|
+
const dot = axisX[0] * axisZ[0] + axisX[1] * axisZ[1] + axisX[2] * axisZ[2];
|
|
16115
|
+
if (Math.abs(dot) > 1e-6) ctx.addIssue({
|
|
16116
|
+
code: "custom",
|
|
16117
|
+
message: "axisX and axisZ must be orthogonal",
|
|
16118
|
+
path: ["axisZ"]
|
|
16119
|
+
});
|
|
16120
|
+
}
|
|
16121
|
+
var SiteSpecSchema = object({
|
|
16122
|
+
name: string().min(1),
|
|
16123
|
+
description: string().optional(),
|
|
16124
|
+
...spatialPlacementFields,
|
|
16125
|
+
compositionType: _enum([
|
|
16126
|
+
"COMPLEX",
|
|
16127
|
+
"ELEMENT",
|
|
16128
|
+
"PARTIAL"
|
|
16129
|
+
]).optional()
|
|
16130
|
+
}).superRefine(validateSpatialAxes);
|
|
16131
|
+
function parseSiteSpec(input) {
|
|
16132
|
+
const result = SiteSpecSchema.safeParse(input);
|
|
16133
|
+
return result.success ? ok(result.data) : err(specError("INVALID_SITE_SPEC", result.error.message, result.error));
|
|
16134
|
+
}
|
|
16135
|
+
//#endregion
|
|
16136
|
+
//#region src/specs/infrastructureSpec.ts
|
|
16137
|
+
var composition = _enum([
|
|
16138
|
+
"COMPLEX",
|
|
16139
|
+
"ELEMENT",
|
|
16140
|
+
"PARTIAL"
|
|
16141
|
+
]).optional();
|
|
16142
|
+
var BridgeSpecSchema = object({
|
|
16143
|
+
name: string().min(1),
|
|
16144
|
+
description: string().optional(),
|
|
16145
|
+
...spatialPlacementFields,
|
|
16146
|
+
compositionType: composition,
|
|
16147
|
+
predefinedType: _enum([
|
|
16148
|
+
"ARCHED",
|
|
16149
|
+
"CABLE_STAYED",
|
|
16150
|
+
"CANTILEVER",
|
|
16151
|
+
"CULVERT",
|
|
16152
|
+
"FRAMEWORK",
|
|
16153
|
+
"GIRDER",
|
|
16154
|
+
"SUSPENSION",
|
|
16155
|
+
"TRUSS",
|
|
16156
|
+
"USERDEFINED",
|
|
16157
|
+
"NOTDEFINED"
|
|
16158
|
+
]).optional()
|
|
16159
|
+
}).superRefine(validateSpatialAxes);
|
|
16160
|
+
var BridgePartSpecSchema = object({
|
|
16161
|
+
name: string().min(1),
|
|
16162
|
+
description: string().optional(),
|
|
16163
|
+
...spatialPlacementFields,
|
|
16164
|
+
compositionType: composition,
|
|
16165
|
+
usageType: _enum([
|
|
16166
|
+
"LATERAL",
|
|
16167
|
+
"LONGITUDINAL",
|
|
16168
|
+
"REGION",
|
|
16169
|
+
"VERTICAL",
|
|
16170
|
+
"USERDEFINED",
|
|
16171
|
+
"NOTDEFINED"
|
|
16172
|
+
]),
|
|
16173
|
+
predefinedType: _enum([
|
|
16174
|
+
"ABUTMENT",
|
|
16175
|
+
"DECK",
|
|
16176
|
+
"DECK_SEGMENT",
|
|
16177
|
+
"FOUNDATION",
|
|
16178
|
+
"PIER",
|
|
16179
|
+
"PIER_SEGMENT",
|
|
16180
|
+
"PYLON",
|
|
16181
|
+
"SUBSTRUCTURE",
|
|
16182
|
+
"SUPERSTRUCTURE",
|
|
16183
|
+
"SURFACESTRUCTURE",
|
|
16184
|
+
"USERDEFINED",
|
|
16185
|
+
"NOTDEFINED"
|
|
16186
|
+
]).optional()
|
|
16187
|
+
}).superRefine(validateSpatialAxes);
|
|
16188
|
+
function parseBridgeSpec(input) {
|
|
16189
|
+
const result = BridgeSpecSchema.safeParse(input);
|
|
16190
|
+
return result.success ? ok(result.data) : err(specError("INVALID_BRIDGE_SPEC", result.error.message, result.error));
|
|
16191
|
+
}
|
|
16192
|
+
function parseBridgePartSpec(input) {
|
|
16193
|
+
const result = BridgePartSpecSchema.safeParse(input);
|
|
16194
|
+
return result.success ? ok(result.data) : err(specError("INVALID_BRIDGE_PART_SPEC", result.error.message, result.error));
|
|
16195
|
+
}
|
|
16196
|
+
//#endregion
|
|
15880
16197
|
//#region src/cobie/cobieExport.ts
|
|
15881
16198
|
/**
|
|
15882
16199
|
* Physical-element categories that become COBie Component rows. Spatial
|
|
@@ -15898,7 +16215,8 @@ var COMPONENT_CATEGORIES = /* @__PURE__ */ new Set([
|
|
|
15898
16215
|
"STAIR",
|
|
15899
16216
|
"RAMP",
|
|
15900
16217
|
"RAILING",
|
|
15901
|
-
"COVERING"
|
|
16218
|
+
"COVERING",
|
|
16219
|
+
"EARTHWORKS_FILL"
|
|
15902
16220
|
]);
|
|
15903
16221
|
/**
|
|
15904
16222
|
* Title-cased label used as the COBie Type/Component name prefix for a category,
|
|
@@ -15920,7 +16238,8 @@ var CATEGORY_LABEL = {
|
|
|
15920
16238
|
STAIR: "Stair",
|
|
15921
16239
|
RAMP: "Ramp",
|
|
15922
16240
|
RAILING: "Railing",
|
|
15923
|
-
COVERING: "Covering"
|
|
16241
|
+
COVERING: "Covering",
|
|
16242
|
+
EARTHWORKS_FILL: "EarthworksFill"
|
|
15924
16243
|
};
|
|
15925
16244
|
function categoryLabel(category) {
|
|
15926
16245
|
return CATEGORY_LABEL[category] ?? category;
|
|
@@ -27597,8 +27916,11 @@ function assignNum(target, key, value) {
|
|
|
27597
27916
|
* while the IR path serves the viewport and dedup. GlobalIds derive from
|
|
27598
27917
|
* families key paths (stable under reordering), not insertion order.
|
|
27599
27918
|
*
|
|
27600
|
-
* Scope: Storey containers
|
|
27601
|
-
*
|
|
27919
|
+
* Scope: building Storey containers; civil Site/Bridge/recursive Bridge Part
|
|
27920
|
+
* structure and Earthworks Fill bodies; Wall/Slab/Column/Beam/Roof/Stair,
|
|
27921
|
+
* Footing/Pile, Railing/Ramp, Covering/CurtainWall, and Space elements; and
|
|
27922
|
+
* wall openings — a fill-role void (Door/Window family) maps onto
|
|
27923
|
+
* addDoor/addWindow, which cut
|
|
27602
27924
|
* the wall and wire IfcRelVoidsElement + IfcRelFillsElement; the opening and
|
|
27603
27925
|
* filler GlobalIds derive from the synthesized key paths. Anonymous (non-fill)
|
|
27604
27926
|
* voids are rejected: they cut only the IR/viewport geometry, and exporting
|
|
@@ -27652,12 +27974,71 @@ var SPEC_ROUTES = {
|
|
|
27652
27974
|
stair: {
|
|
27653
27975
|
parse: parseStairSpec,
|
|
27654
27976
|
add: (m, spec, key) => m.addStair(spec, { stableKey: key }),
|
|
27655
|
-
input:
|
|
27977
|
+
input: flightsSpecInput
|
|
27978
|
+
},
|
|
27979
|
+
footing: {
|
|
27980
|
+
parse: parseFootingSpec,
|
|
27981
|
+
add: (m, spec, key) => m.addFooting(spec, { stableKey: key })
|
|
27982
|
+
},
|
|
27983
|
+
pile: {
|
|
27984
|
+
parse: parsePileSpec,
|
|
27985
|
+
add: (m, spec, key) => m.addPile(spec, { stableKey: key })
|
|
27986
|
+
},
|
|
27987
|
+
railing: {
|
|
27988
|
+
parse: parseRailingSpec,
|
|
27989
|
+
add: (m, spec, key) => m.addRailing(spec, { stableKey: key })
|
|
27990
|
+
},
|
|
27991
|
+
ramp: {
|
|
27992
|
+
parse: parseRampSpec,
|
|
27993
|
+
add: (m, spec, key) => m.addRamp(spec, { stableKey: key }),
|
|
27994
|
+
input: flightsSpecInput
|
|
27995
|
+
},
|
|
27996
|
+
covering: {
|
|
27997
|
+
parse: parseCoveringSpec,
|
|
27998
|
+
add: (m, spec, key) => m.addCovering(spec, void 0, { stableKey: key })
|
|
27999
|
+
},
|
|
28000
|
+
curtainWall: {
|
|
28001
|
+
parse: parseCurtainWallSpec,
|
|
28002
|
+
add: (m, spec, key) => m.addCurtainWall(spec, { stableKey: key })
|
|
28003
|
+
},
|
|
28004
|
+
space: {
|
|
28005
|
+
parse: parseSpaceSpec,
|
|
28006
|
+
add: (m, spec, key) => m.addSpace(spec, { stableKey: key })
|
|
28007
|
+
}
|
|
28008
|
+
};
|
|
28009
|
+
var CIVIL_PRODUCT_ROUTES = {
|
|
28010
|
+
beam: {
|
|
28011
|
+
archetype: "beam",
|
|
28012
|
+
roles: [
|
|
28013
|
+
"beam",
|
|
28014
|
+
"cross-girder",
|
|
28015
|
+
"girder"
|
|
28016
|
+
]
|
|
28017
|
+
},
|
|
28018
|
+
column: {
|
|
28019
|
+
archetype: "column",
|
|
28020
|
+
roles: ["pier-stem"]
|
|
28021
|
+
},
|
|
28022
|
+
footing: {
|
|
28023
|
+
archetype: "footing",
|
|
28024
|
+
roles: ["pad"]
|
|
28025
|
+
},
|
|
28026
|
+
railing: {
|
|
28027
|
+
archetype: "railing",
|
|
28028
|
+
roles: ["guardrail"]
|
|
28029
|
+
},
|
|
28030
|
+
slab: {
|
|
28031
|
+
archetype: "slab",
|
|
28032
|
+
roles: ["deck"]
|
|
28033
|
+
},
|
|
28034
|
+
wall: {
|
|
28035
|
+
archetype: "wall",
|
|
28036
|
+
roles: ["wall"]
|
|
27656
28037
|
}
|
|
27657
28038
|
};
|
|
27658
|
-
/**
|
|
27659
|
-
* element's folded translate lands on every flight's origin instead. */
|
|
27660
|
-
function
|
|
28039
|
+
/** Stair and ramp specs have no top-level origin — placement lives per flight —
|
|
28040
|
+
* so the element's folded translate lands on every flight's origin instead. */
|
|
28041
|
+
function flightsSpecInput(el) {
|
|
27661
28042
|
const base = specInput(el);
|
|
27662
28043
|
const fold = base["origin"] ?? [
|
|
27663
28044
|
0,
|
|
@@ -27699,14 +28080,145 @@ var NAME_ARCHETYPES = {
|
|
|
27699
28080
|
Roof: "roof",
|
|
27700
28081
|
Stair: "stair",
|
|
27701
28082
|
Door: "door",
|
|
27702
|
-
Window: "window"
|
|
28083
|
+
Window: "window",
|
|
28084
|
+
Footing: "footing",
|
|
28085
|
+
Pile: "pile",
|
|
28086
|
+
Railing: "railing",
|
|
28087
|
+
Ramp: "ramp",
|
|
28088
|
+
Covering: "covering",
|
|
28089
|
+
CurtainWall: "curtainWall",
|
|
28090
|
+
Space: "space"
|
|
27703
28091
|
};
|
|
27704
28092
|
function archetypeFor(el) {
|
|
27705
|
-
return el.archetype ?? NAME_ARCHETYPES
|
|
28093
|
+
return el.archetype ?? lookup(NAME_ARCHETYPES, el.type);
|
|
27706
28094
|
}
|
|
27707
28095
|
function specRoute(archetype) {
|
|
27708
28096
|
return archetype !== void 0 && Object.hasOwn(SPEC_ROUTES, archetype) ? SPEC_ROUTES[archetype] : void 0;
|
|
27709
28097
|
}
|
|
28098
|
+
/** Own-property lookup: semantics categories and roles are free-form author
|
|
28099
|
+
* strings, so Object.prototype keys must never resolve to a route. */
|
|
28100
|
+
function lookup(map, key) {
|
|
28101
|
+
return Object.hasOwn(map, key) ? map[key] : void 0;
|
|
28102
|
+
}
|
|
28103
|
+
function civilProductArchetype(el) {
|
|
28104
|
+
if (el.semantics?.kind !== "product") return void 0;
|
|
28105
|
+
const definition = lookup(CIVIL_PRODUCT_ROUTES, el.semantics.category);
|
|
28106
|
+
if (definition === void 0 || !definition.roles.includes(el.semantics.role)) return void 0;
|
|
28107
|
+
return definition.archetype;
|
|
28108
|
+
}
|
|
28109
|
+
function semanticDimension(el, ...names) {
|
|
28110
|
+
if (el.semantics?.kind !== "product") return void 0;
|
|
28111
|
+
for (const name of names) {
|
|
28112
|
+
const value = el.semantics.dimensionsMm[name];
|
|
28113
|
+
if (value !== void 0) return value;
|
|
28114
|
+
}
|
|
28115
|
+
}
|
|
28116
|
+
/** Beam/column solids centre their cross-section on the placement axis, while
|
|
28117
|
+
* the semantics envelope sits corner-anchored at the folded IR origin — shift
|
|
28118
|
+
* the origin to the envelope centre so the exported body occupies the same
|
|
28119
|
+
* space as the viewport body. */
|
|
28120
|
+
function centreOrigin(base, shift) {
|
|
28121
|
+
const origin = base["origin"] ?? [
|
|
28122
|
+
0,
|
|
28123
|
+
0,
|
|
28124
|
+
0
|
|
28125
|
+
];
|
|
28126
|
+
return [
|
|
28127
|
+
origin[0] + shift[0],
|
|
28128
|
+
origin[1] + shift[1],
|
|
28129
|
+
origin[2] + shift[2]
|
|
28130
|
+
];
|
|
28131
|
+
}
|
|
28132
|
+
/**
|
|
28133
|
+
* Adapts target-independent civil envelope dimensions onto existing typed BIM
|
|
28134
|
+
* spec inputs. Reference infrastructure Families intentionally author domain
|
|
28135
|
+
* props such as `depth`, `capOffset`, and compound railing profiles rather than
|
|
28136
|
+
* BIM `profile`/`thickness` fields; semantics is the stable projection seam.
|
|
28137
|
+
*/
|
|
28138
|
+
function civilProductSpecInput(el) {
|
|
28139
|
+
const base = specInput(el);
|
|
28140
|
+
if (el.semantics?.kind !== "product") return base;
|
|
28141
|
+
const length = semanticDimension(el, "length");
|
|
28142
|
+
const width = semanticDimension(el, "width");
|
|
28143
|
+
const height = semanticDimension(el, "height");
|
|
28144
|
+
switch (el.semantics.category) {
|
|
28145
|
+
case "beam": {
|
|
28146
|
+
const synthesized = base["profile"] === void 0 && width !== void 0 && height !== void 0 ? {
|
|
28147
|
+
profile: {
|
|
28148
|
+
kind: "RECTANGULAR",
|
|
28149
|
+
width,
|
|
28150
|
+
height
|
|
28151
|
+
},
|
|
28152
|
+
shift: [
|
|
28153
|
+
0,
|
|
28154
|
+
width / 2,
|
|
28155
|
+
height / 2
|
|
28156
|
+
]
|
|
28157
|
+
} : void 0;
|
|
28158
|
+
return {
|
|
28159
|
+
...base,
|
|
28160
|
+
...synthesized !== void 0 ? {
|
|
28161
|
+
origin: centreOrigin(base, synthesized.shift),
|
|
28162
|
+
profile: synthesized.profile
|
|
28163
|
+
} : {},
|
|
28164
|
+
length: length ?? base["length"],
|
|
28165
|
+
predefinedType: base["predefinedType"] ?? "BEAM"
|
|
28166
|
+
};
|
|
28167
|
+
}
|
|
28168
|
+
case "column": {
|
|
28169
|
+
const synthesized = base["profile"] === void 0 && length !== void 0 && width !== void 0 ? {
|
|
28170
|
+
profile: {
|
|
28171
|
+
kind: "RECTANGULAR",
|
|
28172
|
+
width: length,
|
|
28173
|
+
height: width
|
|
28174
|
+
},
|
|
28175
|
+
shift: [
|
|
28176
|
+
length / 2,
|
|
28177
|
+
width / 2,
|
|
28178
|
+
0
|
|
28179
|
+
]
|
|
28180
|
+
} : void 0;
|
|
28181
|
+
return {
|
|
28182
|
+
...base,
|
|
28183
|
+
...synthesized !== void 0 ? {
|
|
28184
|
+
origin: centreOrigin(base, synthesized.shift),
|
|
28185
|
+
profile: synthesized.profile
|
|
28186
|
+
} : {},
|
|
28187
|
+
height: height ?? base["height"],
|
|
28188
|
+
predefinedType: base["predefinedType"] ?? "COLUMN"
|
|
28189
|
+
};
|
|
28190
|
+
}
|
|
28191
|
+
case "footing": return {
|
|
28192
|
+
...base,
|
|
28193
|
+
length: length ?? base["length"],
|
|
28194
|
+
width: width ?? base["width"],
|
|
28195
|
+
thickness: height ?? base["thickness"],
|
|
28196
|
+
predefinedType: base["predefinedType"] ?? "PAD_FOOTING"
|
|
28197
|
+
};
|
|
28198
|
+
case "railing": return {
|
|
28199
|
+
...base,
|
|
28200
|
+
length: length ?? base["length"],
|
|
28201
|
+
thickness: width ?? base["thickness"],
|
|
28202
|
+
height: height ?? base["height"],
|
|
28203
|
+
predefinedType: base["predefinedType"] ?? "GUARDRAIL"
|
|
28204
|
+
};
|
|
28205
|
+
case "slab": return {
|
|
28206
|
+
...base,
|
|
28207
|
+
length: length ?? base["length"],
|
|
28208
|
+
width: width ?? base["width"],
|
|
28209
|
+
thickness: height ?? base["thickness"],
|
|
28210
|
+
predefinedType: base["predefinedType"] ?? "FLOOR"
|
|
28211
|
+
};
|
|
28212
|
+
case "wall": return {
|
|
28213
|
+
...base,
|
|
28214
|
+
length: length ?? base["length"],
|
|
28215
|
+
thickness: width ?? base["thickness"],
|
|
28216
|
+
height: height ?? base["height"],
|
|
28217
|
+
predefinedType: base["predefinedType"] ?? "NOTDEFINED"
|
|
28218
|
+
};
|
|
28219
|
+
default: return base;
|
|
28220
|
+
}
|
|
28221
|
+
}
|
|
27710
28222
|
/** Total of the resolved geometry's OUTER literal translate chain. The
|
|
27711
28223
|
* transform vocabulary is translate-only, so frame differences are exact
|
|
27712
28224
|
* subtractions. Parameter-driven translations stop the peel. */
|
|
@@ -27732,15 +28244,14 @@ function peelTranslates(node) {
|
|
|
27732
28244
|
moved
|
|
27733
28245
|
};
|
|
27734
28246
|
}
|
|
27735
|
-
/** True when the element's transform
|
|
28247
|
+
/** True when the element's rendered local transform carries a rotation. The spec path
|
|
27736
28248
|
* folds only translations into IfcLocalPlacement (walls orient via `axisX`),
|
|
27737
28249
|
* so a tRotate placement would export un-rotated while the viewport shows it
|
|
27738
28250
|
* rotated — reject instead of diverging. Rotations a family render bakes
|
|
27739
28251
|
* into its own body geometry (e.g. a circular beam oriented along axisX) are
|
|
27740
28252
|
* fine: the spec rebuilds that body parametrically from props. */
|
|
27741
28253
|
function hasRotateOp(el) {
|
|
27742
|
-
|
|
27743
|
-
return Array.isArray(ops) && ops.some((op) => typeof op === "object" && op !== null && op.op === "rotate");
|
|
28254
|
+
return el.localTransforms.some((op) => op.op === "rotate");
|
|
27744
28255
|
}
|
|
27745
28256
|
/** Fold the resolved geometry's outer translate chain into the spec placement
|
|
27746
28257
|
* origin, so IfcLocalPlacement matches the IR world frame no matter where the
|
|
@@ -27786,10 +28297,14 @@ var COMMON_PSET_FIELDS = {
|
|
|
27786
28297
|
function collectSpecProps(el) {
|
|
27787
28298
|
const out = {};
|
|
27788
28299
|
const material = el.attributes["material"];
|
|
27789
|
-
|
|
28300
|
+
const semanticsMaterial = el.semantics?.kind === "product" ? el.semantics.material : void 0;
|
|
28301
|
+
if (el.props["materialName"] === void 0) {
|
|
28302
|
+
if (typeof material === "string") out["materialName"] = material;
|
|
28303
|
+
else if (semanticsMaterial !== void 0) out["materialName"] = semanticsMaterial;
|
|
28304
|
+
}
|
|
27790
28305
|
const psets = el.attributes["psets"];
|
|
28306
|
+
const custom = {};
|
|
27791
28307
|
if (psets && typeof psets === "object") {
|
|
27792
|
-
const custom = {};
|
|
27793
28308
|
const ownCommonPset = `Pset_${el.type}Common`;
|
|
27794
28309
|
for (const [psetName, fields] of Object.entries(psets)) {
|
|
27795
28310
|
if (!fields || typeof fields !== "object") continue;
|
|
@@ -27802,14 +28317,15 @@ function collectSpecProps(el) {
|
|
|
27802
28317
|
if (Object.keys(values).length > 0) custom[psetName] = values;
|
|
27803
28318
|
}
|
|
27804
28319
|
}
|
|
27805
|
-
if (Object.keys(custom).length > 0) {
|
|
27806
|
-
const declared = el.props["customProperties"];
|
|
27807
|
-
out["customProperties"] = declared && typeof declared === "object" ? {
|
|
27808
|
-
...custom,
|
|
27809
|
-
...declared
|
|
27810
|
-
} : custom;
|
|
27811
|
-
}
|
|
27812
28320
|
}
|
|
28321
|
+
const declared = el.props["customProperties"];
|
|
28322
|
+
if (declared && typeof declared === "object" && !Array.isArray(declared)) for (const [psetName, fields] of Object.entries(declared)) {
|
|
28323
|
+
if (!fields || typeof fields !== "object" || Array.isArray(fields)) continue;
|
|
28324
|
+
const values = {};
|
|
28325
|
+
for (const [field, value] of Object.entries(fields)) if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") values[field] = value;
|
|
28326
|
+
if (Object.keys(values).length > 0) custom[psetName] = values;
|
|
28327
|
+
}
|
|
28328
|
+
if (Object.keys(custom).length > 0) out["customProperties"] = custom;
|
|
27813
28329
|
return out;
|
|
27814
28330
|
}
|
|
27815
28331
|
function addFill(model, fill, input, identity) {
|
|
@@ -27826,35 +28342,49 @@ function addFill(model, fill, input, identity) {
|
|
|
27826
28342
|
}
|
|
27827
28343
|
return err(specError("FAMILIES_UNSUPPORTED_FILL", `familiesToBim: no fill mapping for element type '${fill.type}' at '${fill.keyPath}' (archetype: ${archetype ?? "none"}) — a filler needs archetype: 'door' or 'window'`));
|
|
27828
28344
|
}
|
|
27829
|
-
|
|
27830
|
-
* authoritative for a proxy (no parametric spec to diverge from), so baked
|
|
27831
|
-
* transforms — rotations included — are fine here. */
|
|
27832
|
-
function addProxyElement(model, el, evaluator) {
|
|
28345
|
+
function materializeOwnedSolid(el, evaluator, errors) {
|
|
27833
28346
|
const evaluated = evaluator.evaluate(el.geometry);
|
|
27834
|
-
if (!evaluated.ok) return err(specError(
|
|
28347
|
+
if (!evaluated.ok) return err(specError(errors.evalCode, `familiesToBim: '${el.keyPath}' failed to materialize for the ${errors.routeName} route: ${evaluated.error.message}`, evaluated.error));
|
|
27835
28348
|
let source = evaluated.value;
|
|
27836
28349
|
if (!isSolid(source)) {
|
|
27837
28350
|
const solids = getSolids(source);
|
|
27838
28351
|
const only = solids.length === 1 ? solids[0] : void 0;
|
|
27839
|
-
if (only === void 0) return err(specError(
|
|
28352
|
+
if (only === void 0) return err(specError(errors.notSolidCode, `familiesToBim: '${el.keyPath}' materialized to ${solids.length} solids — the ${errors.routeName} body must be exactly one`));
|
|
27840
28353
|
source = only;
|
|
27841
28354
|
}
|
|
27842
28355
|
const copy = clone(source);
|
|
27843
|
-
if (!copy.ok) return err(specError(
|
|
28356
|
+
if (!copy.ok) return err(specError(errors.evalCode, `familiesToBim: '${el.keyPath}' could not copy the materialized body`, copy.error));
|
|
27844
28357
|
const valid = validSolid(copy.value);
|
|
27845
28358
|
if (!valid.ok) {
|
|
27846
28359
|
copy.value[Symbol.dispose]();
|
|
27847
|
-
return err(specError(
|
|
28360
|
+
return err(specError(errors.invalidCode, `familiesToBim: '${el.keyPath}' materialized to an invalid solid: ${valid.error}`));
|
|
27848
28361
|
}
|
|
28362
|
+
return ok(valid.value);
|
|
28363
|
+
}
|
|
28364
|
+
/** Materialize an unrouted element's IR and add it as a proxy. The body is
|
|
28365
|
+
* authoritative for a proxy (no parametric spec to diverge from), so baked
|
|
28366
|
+
* transforms — rotations included — are fine here. */
|
|
28367
|
+
function addProxyElement(model, el, evaluator, projectedSpatialTranslation) {
|
|
28368
|
+
const body = materializeOwnedSolid(el, evaluator, {
|
|
28369
|
+
evalCode: "FAMILIES_PROXY_EVAL_FAILED",
|
|
28370
|
+
notSolidCode: "FAMILIES_PROXY_NOT_SOLID",
|
|
28371
|
+
invalidCode: "FAMILIES_PROXY_INVALID",
|
|
28372
|
+
routeName: "proxy"
|
|
28373
|
+
});
|
|
28374
|
+
if (!body.ok) return body;
|
|
28375
|
+
const localized = localizeOwnedSolid(el, body.value, projectedSpatialTranslation, "FAMILIES_PROXY_LOCALIZE_FAILED", "spatial parent");
|
|
28376
|
+
if (!localized.ok) return localized;
|
|
27849
28377
|
const nameAttr = el.attributes["name"];
|
|
27850
28378
|
const materialProp = el.props["materialName"];
|
|
27851
28379
|
const specProps = collectSpecProps(el);
|
|
27852
|
-
|
|
28380
|
+
const added = model.addProxy({
|
|
27853
28381
|
name: typeof nameAttr === "string" ? nameAttr : el.type,
|
|
27854
|
-
solid:
|
|
28382
|
+
solid: localized.value,
|
|
27855
28383
|
materialName: typeof materialProp === "string" ? materialProp : specProps["materialName"],
|
|
27856
28384
|
customProperties: specProps["customProperties"]
|
|
27857
28385
|
}, { stableKey: el.keyPath });
|
|
28386
|
+
if (!added.ok) localized.value[Symbol.dispose]();
|
|
28387
|
+
return added;
|
|
27858
28388
|
}
|
|
27859
28389
|
/** Map a wall's synthesized Opening children onto addDoor/addWindow. The
|
|
27860
28390
|
* wall-relative offsets come from the void geometry's frame minus the host's:
|
|
@@ -27904,38 +28434,296 @@ function requireKeyed(el) {
|
|
|
27904
28434
|
if (el.keyed) return ok(void 0);
|
|
27905
28435
|
return err(specError("FAMILIES_UNKEYED_ELEMENT", `familiesToBim: '${el.keyPath}' has no explicit key — IFC identity needs order-independent key paths (add a key to the element)`));
|
|
27906
28436
|
}
|
|
28437
|
+
var BRIDGE_ROLE = {
|
|
28438
|
+
arched: "ARCHED",
|
|
28439
|
+
"cable-stayed": "CABLE_STAYED",
|
|
28440
|
+
cantilever: "CANTILEVER",
|
|
28441
|
+
culvert: "CULVERT",
|
|
28442
|
+
framework: "FRAMEWORK",
|
|
28443
|
+
girder: "GIRDER",
|
|
28444
|
+
suspension: "SUSPENSION",
|
|
28445
|
+
truss: "TRUSS"
|
|
28446
|
+
};
|
|
28447
|
+
var BRIDGE_PART_ROLE = {
|
|
28448
|
+
abutment: "ABUTMENT",
|
|
28449
|
+
deck: "DECK",
|
|
28450
|
+
"deck-segment": "DECK_SEGMENT",
|
|
28451
|
+
foundation: "FOUNDATION",
|
|
28452
|
+
pier: "PIER",
|
|
28453
|
+
"pier-segment": "PIER_SEGMENT",
|
|
28454
|
+
pylon: "PYLON",
|
|
28455
|
+
substructure: "SUBSTRUCTURE",
|
|
28456
|
+
superstructure: "SUPERSTRUCTURE",
|
|
28457
|
+
"surface-structure": "SURFACESTRUCTURE"
|
|
28458
|
+
};
|
|
28459
|
+
var CIVIL_COMPOSITION = {
|
|
28460
|
+
collection: "COMPLEX",
|
|
28461
|
+
element: "ELEMENT",
|
|
28462
|
+
partial: "PARTIAL"
|
|
28463
|
+
};
|
|
28464
|
+
var CIVIL_USAGE = {
|
|
28465
|
+
lateral: "LATERAL",
|
|
28466
|
+
longitudinal: "LONGITUDINAL",
|
|
28467
|
+
regional: "REGION",
|
|
28468
|
+
vertical: "VERTICAL"
|
|
28469
|
+
};
|
|
28470
|
+
var EARTHWORKS_FILL_ROLE = {
|
|
28471
|
+
backfill: "BACKFILL",
|
|
28472
|
+
counterweight: "COUNTERWEIGHT",
|
|
28473
|
+
embankment: "EMBANKMENT",
|
|
28474
|
+
"slope-fill": "SLOPEFILL",
|
|
28475
|
+
subgrade: "SUBGRADE",
|
|
28476
|
+
"subgrade-bed": "SUBGRADEBED",
|
|
28477
|
+
"transition-section": "TRANSITIONSECTION"
|
|
28478
|
+
};
|
|
28479
|
+
function unsupportedCivilRole(el, entity) {
|
|
28480
|
+
return err(specError("FAMILIES_UNSUPPORTED_CIVIL_SEMANTICS", `familiesToBim: unsupported ${entity} role '${el.semantics?.role ?? ""}' at '${el.keyPath}'`));
|
|
28481
|
+
}
|
|
28482
|
+
function civilSpatialKind(el) {
|
|
28483
|
+
const semantics = el.semantics;
|
|
28484
|
+
if (semantics?.kind === "site" && semantics.category === "site") return "site";
|
|
28485
|
+
if (semantics?.kind === "facility" && semantics.category === "bridge") return "bridge";
|
|
28486
|
+
if (semantics?.kind === "spatial-part" && semantics.category === "bridge-part") return "bridge-part";
|
|
28487
|
+
}
|
|
28488
|
+
function isCivilSpatialIntent(el) {
|
|
28489
|
+
return el.semantics?.kind === "site" || el.semantics?.kind === "facility" || el.semantics?.kind === "spatial-part";
|
|
28490
|
+
}
|
|
28491
|
+
function hasCivilSpatialIntent(el) {
|
|
28492
|
+
return isCivilSpatialIntent(el) || el.children.some(hasCivilSpatialIntent);
|
|
28493
|
+
}
|
|
28494
|
+
function isEarthworksFillOccurrence(el) {
|
|
28495
|
+
return el.semantics?.kind === "product" && el.semantics.category === "earthworks-fill";
|
|
28496
|
+
}
|
|
28497
|
+
function semanticName(el) {
|
|
28498
|
+
const name = el.attributes["name"];
|
|
28499
|
+
if (typeof name === "string" && name.trim().length > 0) return name;
|
|
28500
|
+
const semanticNameValue = el.semantics?.properties?.["name"];
|
|
28501
|
+
return typeof semanticNameValue === "string" && semanticNameValue.trim().length > 0 ? semanticNameValue : el.keyPath;
|
|
28502
|
+
}
|
|
28503
|
+
var ZERO_TRANSLATION = [
|
|
28504
|
+
0,
|
|
28505
|
+
0,
|
|
28506
|
+
0
|
|
28507
|
+
];
|
|
28508
|
+
function addTranslation(a, b) {
|
|
28509
|
+
return [
|
|
28510
|
+
a[0] + b[0],
|
|
28511
|
+
a[1] + b[1],
|
|
28512
|
+
a[2] + b[2]
|
|
28513
|
+
];
|
|
28514
|
+
}
|
|
28515
|
+
function subtractTranslation(a, b) {
|
|
28516
|
+
return [
|
|
28517
|
+
a[0] - b[0],
|
|
28518
|
+
a[1] - b[1],
|
|
28519
|
+
a[2] - b[2]
|
|
28520
|
+
];
|
|
28521
|
+
}
|
|
28522
|
+
function hasTranslation(value) {
|
|
28523
|
+
return value[0] !== 0 || value[1] !== 0 || value[2] !== 0;
|
|
28524
|
+
}
|
|
28525
|
+
var DEFAULT_AXIS_X = [
|
|
28526
|
+
1,
|
|
28527
|
+
0,
|
|
28528
|
+
0
|
|
28529
|
+
];
|
|
28530
|
+
var DEFAULT_AXIS_Z = [
|
|
28531
|
+
0,
|
|
28532
|
+
0,
|
|
28533
|
+
1
|
|
28534
|
+
];
|
|
28535
|
+
function axisEquals(value, axis) {
|
|
28536
|
+
return Array.isArray(value) && value.length === 3 && value.every((component, i) => typeof component === "number" && component === axis[i]);
|
|
28537
|
+
}
|
|
28538
|
+
/** Civil descendant relativization is translation-only, so a civil frame with
|
|
28539
|
+
* non-default axes would rotate the exported hierarchy away from the IR. */
|
|
28540
|
+
function hasRotatedAxes(el) {
|
|
28541
|
+
const axisX = el.props["axisX"];
|
|
28542
|
+
const axisZ = el.props["axisZ"];
|
|
28543
|
+
return axisX !== void 0 && !axisEquals(axisX, DEFAULT_AXIS_X) || axisZ !== void 0 && !axisEquals(axisZ, DEFAULT_AXIS_Z);
|
|
28544
|
+
}
|
|
28545
|
+
function authoredSpecOrigin(el) {
|
|
28546
|
+
const origin = el.props["origin"];
|
|
28547
|
+
return Array.isArray(origin) && origin.length === 3 && origin.every((c) => typeof c === "number") ? origin : ZERO_TRANSLATION;
|
|
28548
|
+
}
|
|
28549
|
+
function localizeOwnedSolid(el, body, projectedSpatialTranslation, errorCode, frameName) {
|
|
28550
|
+
if (!hasTranslation(projectedSpatialTranslation)) return ok(body);
|
|
28551
|
+
try {
|
|
28552
|
+
const localized = translate(body, [
|
|
28553
|
+
-projectedSpatialTranslation[0],
|
|
28554
|
+
-projectedSpatialTranslation[1],
|
|
28555
|
+
-projectedSpatialTranslation[2]
|
|
28556
|
+
]);
|
|
28557
|
+
body[Symbol.dispose]();
|
|
28558
|
+
return ok(localized);
|
|
28559
|
+
} catch (cause) {
|
|
28560
|
+
body[Symbol.dispose]();
|
|
28561
|
+
return err(specError(errorCode, `familiesToBim: '${el.keyPath}' could not move its body into the ${frameName} frame`, cause));
|
|
28562
|
+
}
|
|
28563
|
+
}
|
|
28564
|
+
function authoredTranslation(el) {
|
|
28565
|
+
const total = [
|
|
28566
|
+
0,
|
|
28567
|
+
0,
|
|
28568
|
+
0
|
|
28569
|
+
];
|
|
28570
|
+
for (const op of el.localTransforms) {
|
|
28571
|
+
if (op.op !== "translate") continue;
|
|
28572
|
+
total[0] += op.v[0];
|
|
28573
|
+
total[1] += op.v[1];
|
|
28574
|
+
total[2] += op.v[2];
|
|
28575
|
+
}
|
|
28576
|
+
return total;
|
|
28577
|
+
}
|
|
28578
|
+
function civilSpatialInput(el, localTranslation) {
|
|
28579
|
+
const semantics = el.semantics;
|
|
28580
|
+
const composition = semantics !== void 0 && "composition" in semantics ? CIVIL_COMPOSITION[semantics.composition] : void 0;
|
|
28581
|
+
const origin = addTranslation(el.props["origin"] ?? ZERO_TRANSLATION, localTranslation);
|
|
28582
|
+
return {
|
|
28583
|
+
name: semanticName(el),
|
|
28584
|
+
...el.props["origin"] !== void 0 || hasTranslation(localTranslation) ? { origin } : {},
|
|
28585
|
+
...el.props["axisX"] !== void 0 ? { axisX: el.props["axisX"] } : {},
|
|
28586
|
+
...el.props["axisZ"] !== void 0 ? { axisZ: el.props["axisZ"] } : {},
|
|
28587
|
+
...composition !== void 0 ? { compositionType: composition } : {}
|
|
28588
|
+
};
|
|
28589
|
+
}
|
|
28590
|
+
function civilParentAccepts(kind, parent) {
|
|
28591
|
+
if (kind === "site") return parent === "project";
|
|
28592
|
+
if (kind === "bridge") return parent === "site";
|
|
28593
|
+
return parent === "bridge" || parent === "bridge-part";
|
|
28594
|
+
}
|
|
28595
|
+
function addCivilSpatialOccurrence(model, el, kind, localTranslation) {
|
|
28596
|
+
if (kind === "site") {
|
|
28597
|
+
if (el.semantics?.role !== "transport-site") return unsupportedCivilRole(el, "Site");
|
|
28598
|
+
const parsed = parseSiteSpec(civilSpatialInput(el, localTranslation));
|
|
28599
|
+
return parsed.ok ? model.addSite(parsed.value, { stableKey: el.keyPath }) : parsed;
|
|
28600
|
+
}
|
|
28601
|
+
if (kind === "bridge") {
|
|
28602
|
+
const predefinedType = lookup(BRIDGE_ROLE, el.semantics?.role ?? "");
|
|
28603
|
+
if (predefinedType === void 0) return unsupportedCivilRole(el, "Bridge");
|
|
28604
|
+
const parsed = parseBridgeSpec({
|
|
28605
|
+
...civilSpatialInput(el, localTranslation),
|
|
28606
|
+
predefinedType
|
|
28607
|
+
});
|
|
28608
|
+
return parsed.ok ? model.addBridge(parsed.value, { stableKey: el.keyPath }) : parsed;
|
|
28609
|
+
}
|
|
28610
|
+
const subdivision = el.semantics?.kind === "spatial-part" ? el.semantics.subdivision : void 0;
|
|
28611
|
+
const predefinedType = lookup(BRIDGE_PART_ROLE, el.semantics?.role ?? "");
|
|
28612
|
+
if (predefinedType === void 0) return unsupportedCivilRole(el, "Bridge Part");
|
|
28613
|
+
const parsed = parseBridgePartSpec({
|
|
28614
|
+
...civilSpatialInput(el, localTranslation),
|
|
28615
|
+
predefinedType,
|
|
28616
|
+
usageType: subdivision !== void 0 ? CIVIL_USAGE[subdivision] : "NOTDEFINED"
|
|
28617
|
+
});
|
|
28618
|
+
return parsed.ok ? model.addBridgePart(parsed.value, { stableKey: el.keyPath }) : parsed;
|
|
28619
|
+
}
|
|
28620
|
+
function relativeSpecInput(input, projectedSpatialTranslation) {
|
|
28621
|
+
const relativeOrigin = (origin) => Array.isArray(origin) && origin.length === 3 && origin.every((value) => typeof value === "number") ? subtractTranslation(origin, projectedSpatialTranslation) : origin;
|
|
28622
|
+
if (Array.isArray(input["flights"])) {
|
|
28623
|
+
const flights = input["flights"];
|
|
28624
|
+
return {
|
|
28625
|
+
...input,
|
|
28626
|
+
flights: flights.map((flight) => {
|
|
28627
|
+
if (typeof flight !== "object" || flight === null) return flight;
|
|
28628
|
+
const flightInput = flight;
|
|
28629
|
+
return {
|
|
28630
|
+
...flightInput,
|
|
28631
|
+
origin: relativeOrigin(flightInput["origin"])
|
|
28632
|
+
};
|
|
28633
|
+
})
|
|
28634
|
+
};
|
|
28635
|
+
}
|
|
28636
|
+
return {
|
|
28637
|
+
...input,
|
|
28638
|
+
origin: relativeOrigin(input["origin"])
|
|
28639
|
+
};
|
|
28640
|
+
}
|
|
28641
|
+
function addEarthworksFillElement(model, el, evaluator, projectedSpatialTranslation) {
|
|
28642
|
+
if (el.semantics?.kind !== "product") return err(specError("FAMILIES_UNSUPPORTED_CIVIL_SEMANTICS", `familiesToBim: '${el.keyPath}' is not authored as a civil Product`));
|
|
28643
|
+
const predefinedType = lookup(EARTHWORKS_FILL_ROLE, el.semantics.role);
|
|
28644
|
+
if (predefinedType === void 0) return unsupportedCivilRole(el, "Earthworks Fill");
|
|
28645
|
+
const body = materializeOwnedSolid(el, evaluator, {
|
|
28646
|
+
evalCode: "FAMILIES_EARTHWORKS_EVAL_FAILED",
|
|
28647
|
+
notSolidCode: "FAMILIES_EARTHWORKS_NOT_SOLID",
|
|
28648
|
+
invalidCode: "FAMILIES_EARTHWORKS_INVALID",
|
|
28649
|
+
routeName: "Earthworks Fill"
|
|
28650
|
+
});
|
|
28651
|
+
if (!body.ok) return body;
|
|
28652
|
+
const localized = localizeOwnedSolid(el, body.value, projectedSpatialTranslation, "FAMILIES_EARTHWORKS_LOCALIZE_FAILED", "Bridge Part");
|
|
28653
|
+
if (!localized.ok) return localized;
|
|
28654
|
+
const specProps = collectSpecProps(el);
|
|
28655
|
+
const authoredMaterial = el.props["materialName"];
|
|
28656
|
+
const added = model.addEarthworksFill({
|
|
28657
|
+
name: semanticName(el),
|
|
28658
|
+
solid: localized.value,
|
|
28659
|
+
materialName: typeof authoredMaterial === "string" ? authoredMaterial : specProps["materialName"] ?? el.semantics.material,
|
|
28660
|
+
predefinedType,
|
|
28661
|
+
customProperties: specProps["customProperties"]
|
|
28662
|
+
}, { stableKey: el.keyPath });
|
|
28663
|
+
if (!added.ok) localized.value[Symbol.dispose]();
|
|
28664
|
+
return added;
|
|
28665
|
+
}
|
|
27907
28666
|
/**
|
|
27908
28667
|
* Project a resolved families tree into an eager BimModel. The caller owns
|
|
27909
28668
|
* the returned model (`using`); families stays domain-neutral — this adapter
|
|
27910
28669
|
* is where families types meet the IFC vocabulary.
|
|
27911
28670
|
*/
|
|
27912
28671
|
function familiesToBim(root, options) {
|
|
28672
|
+
const usesAuthoredCivilHierarchy = hasCivilSpatialIntent(root);
|
|
28673
|
+
if (usesAuthoredCivilHierarchy) {
|
|
28674
|
+
const keyed = requireKeyed(root);
|
|
28675
|
+
if (!keyed.ok) return keyed;
|
|
28676
|
+
}
|
|
27913
28677
|
const model = new BimModel();
|
|
27914
|
-
const initResult = model.init(options.project);
|
|
28678
|
+
const initResult = model.init(options.project, usesAuthoredCivilHierarchy ? { stableKey: civilSpatialKind(root) !== void 0 ? `${root.keyPath}#project` : root.keyPath } : void 0);
|
|
27915
28679
|
if (!initResult.ok) return initResult;
|
|
27916
|
-
const
|
|
27917
|
-
|
|
27918
|
-
|
|
27919
|
-
|
|
27920
|
-
|
|
27921
|
-
|
|
27922
|
-
|
|
27923
|
-
|
|
27924
|
-
|
|
27925
|
-
|
|
27926
|
-
|
|
27927
|
-
|
|
27928
|
-
|
|
27929
|
-
|
|
27930
|
-
|
|
28680
|
+
const projectId = initResult.value;
|
|
28681
|
+
let buildingId = null;
|
|
28682
|
+
if (!usesAuthoredCivilHierarchy) {
|
|
28683
|
+
const siteResult = model.addSite({ name: options.siteName ?? "Site" });
|
|
28684
|
+
if (!siteResult.ok) {
|
|
28685
|
+
model[Symbol.dispose]();
|
|
28686
|
+
return siteResult;
|
|
28687
|
+
}
|
|
28688
|
+
const buildingResult = model.addBuilding({ name: options.buildingName ?? "Building" });
|
|
28689
|
+
if (!buildingResult.ok) {
|
|
28690
|
+
model[Symbol.dispose]();
|
|
28691
|
+
return buildingResult;
|
|
28692
|
+
}
|
|
28693
|
+
buildingId = buildingResult.value;
|
|
28694
|
+
model.aggregate(projectId, siteResult.value);
|
|
28695
|
+
model.aggregate(siteResult.value, buildingId);
|
|
28696
|
+
}
|
|
28697
|
+
const idByKeyPath = new Map(usesAuthoredCivilHierarchy ? [[root.keyPath, projectId]] : []);
|
|
27931
28698
|
const proxied = [];
|
|
27932
|
-
const walk = (el,
|
|
27933
|
-
const rotatedHere = rotated || hasRotateOp(el);
|
|
28699
|
+
const walk = (el, state) => {
|
|
28700
|
+
const rotatedHere = state.rotated || hasRotateOp(el);
|
|
28701
|
+
const cumulativeTranslationHere = addTranslation(state.cumulativeTranslation, authoredTranslation(el));
|
|
27934
28702
|
let proxiedHere = false;
|
|
27935
|
-
let
|
|
28703
|
+
let nextSpatialStructureId = state.spatialStructureId;
|
|
28704
|
+
let nextCivilParent = state.civilParent;
|
|
28705
|
+
let nextProjectedSpatialTranslation = state.projectedSpatialTranslation;
|
|
27936
28706
|
const archetype = archetypeFor(el);
|
|
27937
|
-
const
|
|
27938
|
-
|
|
28707
|
+
const effectiveArchetype = el.semantics?.kind === "product" ? civilProductArchetype(el) : archetype;
|
|
28708
|
+
const route = specRoute(effectiveArchetype);
|
|
28709
|
+
const civilKind = civilSpatialKind(el);
|
|
28710
|
+
if (civilKind !== void 0) {
|
|
28711
|
+
if (!usesAuthoredCivilHierarchy || !civilParentAccepts(civilKind, state.civilParent) || state.spatialStructureId === null) return err(specError("FAMILIES_INVALID_CIVIL_HIERARCHY", `familiesToBim: civil '${civilKind}' at '${el.keyPath}' cannot occur under '${state.civilParent}'`));
|
|
28712
|
+
const keyed = requireKeyed(el);
|
|
28713
|
+
if (!keyed.ok) return keyed;
|
|
28714
|
+
if (rotatedHere || hasRotatedAxes(el)) return err(specError("FAMILIES_UNSUPPORTED_TRANSFORM", `familiesToBim: civil spatial element '${el.keyPath}' carries a rotated frame — descendants are relativized by translation only, so bake the orientation into child geometry`));
|
|
28715
|
+
if (el.geometry.kind !== "Empty") return err(specError("FAMILIES_UNSUPPORTED_CIVIL_SEMANTICS", `familiesToBim: civil spatial element '${el.keyPath}' carries its own geometry — Site/Bridge/Bridge Part export no body, so author it as a child Product (e.g. Earthworks Fill)`));
|
|
28716
|
+
const localTranslation = subtractTranslation(cumulativeTranslationHere, state.projectedSpatialTranslation);
|
|
28717
|
+
const added = addCivilSpatialOccurrence(model, el, civilKind, localTranslation);
|
|
28718
|
+
if (!added.ok) return added;
|
|
28719
|
+
model.aggregate(state.spatialStructureId, added.value);
|
|
28720
|
+
idByKeyPath.set(el.keyPath, added.value);
|
|
28721
|
+
nextSpatialStructureId = added.value;
|
|
28722
|
+
nextCivilParent = civilKind;
|
|
28723
|
+
nextProjectedSpatialTranslation = addTranslation(cumulativeTranslationHere, authoredSpecOrigin(el));
|
|
28724
|
+
} else if (isCivilSpatialIntent(el)) return err(specError("FAMILIES_UNSUPPORTED_CIVIL_SEMANTICS", `familiesToBim: unsupported civil '${el.semantics?.kind ?? "unknown"}' category '${el.semantics?.category ?? ""}' at '${el.keyPath}'`));
|
|
28725
|
+
else if (archetype === "storey") {
|
|
28726
|
+
if (buildingId === null) return err(specError("FAMILIES_INVALID_CIVIL_HIERARCHY", `familiesToBim: Storey '${el.keyPath}' is not part of the civil Bridge hierarchy`));
|
|
27939
28727
|
const keyed = requireKeyed(el);
|
|
27940
28728
|
if (!keyed.ok) return keyed;
|
|
27941
28729
|
const storeyResult = model.addStorey({
|
|
@@ -27945,7 +28733,17 @@ function familiesToBim(root, options) {
|
|
|
27945
28733
|
if (!storeyResult.ok) return storeyResult;
|
|
27946
28734
|
model.aggregate(buildingId, storeyResult.value);
|
|
27947
28735
|
idByKeyPath.set(el.keyPath, storeyResult.value);
|
|
27948
|
-
|
|
28736
|
+
nextSpatialStructureId = storeyResult.value;
|
|
28737
|
+
} else if (isEarthworksFillOccurrence(el)) {
|
|
28738
|
+
if (!usesAuthoredCivilHierarchy || nextSpatialStructureId === null || state.civilParent !== "bridge-part") return err(specError("FAMILIES_INVALID_CIVIL_HIERARCHY", `familiesToBim: Earthworks Fill '${el.keyPath}' needs a Bridge Part ancestor`));
|
|
28739
|
+
const bodyEvaluator = options.bodyEvaluator ?? options.proxyEvaluator;
|
|
28740
|
+
if (bodyEvaluator === void 0) return err(specError("FAMILIES_EARTHWORKS_EVALUATOR_REQUIRED", `familiesToBim: Earthworks Fill '${el.keyPath}' needs bodyEvaluator to materialize its exact Product Body`));
|
|
28741
|
+
const keyed = requireKeyed(el);
|
|
28742
|
+
if (!keyed.ok) return keyed;
|
|
28743
|
+
const added = addEarthworksFillElement(model, el, bodyEvaluator, state.projectedSpatialTranslation);
|
|
28744
|
+
if (!added.ok) return added;
|
|
28745
|
+
model.placeIn(added.value, nextSpatialStructureId);
|
|
28746
|
+
idByKeyPath.set(el.keyPath, added.value);
|
|
27949
28747
|
} else if (route !== void 0) {
|
|
27950
28748
|
const keyed = requireKeyed(el);
|
|
27951
28749
|
if (!keyed.ok) return keyed;
|
|
@@ -27955,26 +28753,27 @@ function familiesToBim(root, options) {
|
|
|
27955
28753
|
const openings = el.children.filter((c) => c.type === "Opening").length;
|
|
27956
28754
|
if (voids.length > openings) return err(specError("FAMILIES_ANONYMOUS_VOID", `familiesToBim: '${el.keyPath}' has ${voids.length - openings} anonymous void(s) the IFC body cannot carry — use a fill-role family (Door/Window) for each void`));
|
|
27957
28755
|
}
|
|
27958
|
-
const
|
|
28756
|
+
const routedInput = el.semantics?.kind === "product" ? civilProductSpecInput(el) : ("input" in route ? route.input : specInput)(el);
|
|
28757
|
+
const parsed = route.parse(usesAuthoredCivilHierarchy ? relativeSpecInput(routedInput, state.projectedSpatialTranslation) : routedInput);
|
|
27959
28758
|
if (!parsed.ok) return parsed;
|
|
27960
28759
|
const added = route.add(model, parsed.value, el.keyPath);
|
|
27961
28760
|
if (!added.ok) return added;
|
|
27962
28761
|
idByKeyPath.set(el.keyPath, added.value);
|
|
27963
|
-
if (
|
|
27964
|
-
model.placeIn(added.value,
|
|
27965
|
-
if (
|
|
27966
|
-
const opened = addOpenings(model, el, added.value,
|
|
28762
|
+
if (nextSpatialStructureId === null || usesAuthoredCivilHierarchy && state.civilParent !== "bridge-part") return err(specError(usesAuthoredCivilHierarchy ? "FAMILIES_INVALID_CIVIL_HIERARCHY" : "FAMILIES_NO_STOREY", usesAuthoredCivilHierarchy ? `familiesToBim: physical product '${el.keyPath}' needs a Bridge Part ancestor` : `familiesToBim: '${el.keyPath}' has no Storey ancestor — IFC elements need spatial containment; a container family needs archetype: 'storey' to be recognised under any name`));
|
|
28763
|
+
model.placeIn(added.value, nextSpatialStructureId);
|
|
28764
|
+
if (effectiveArchetype === "wall") {
|
|
28765
|
+
const opened = addOpenings(model, el, added.value, nextSpatialStructureId, idByKeyPath);
|
|
27967
28766
|
if (!opened.ok) return opened;
|
|
27968
28767
|
}
|
|
27969
28768
|
} 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`));
|
|
27970
28769
|
else if (el.type !== "Group" && el.geometry.kind !== "Empty") {
|
|
27971
|
-
if (options.proxyEvaluator === void 0) return err(specError("FAMILIES_UNSUPPORTED_TYPE", `familiesToBim: no spec mapping for element type '${el.type}' at '${el.keyPath}' (archetype: ${el.archetype ?? "none"}) —
|
|
28770
|
+
if (options.proxyEvaluator === void 0) return err(specError("FAMILIES_UNSUPPORTED_TYPE", `familiesToBim: no supported spec mapping for element type '${el.type}' at '${el.keyPath}' (archetype: ${el.archetype ?? "none"}) — declare a recognized archetype or civil Product category/role, add a spec route, or pass proxyEvaluator to export it as an IfcBuildingElementProxy`));
|
|
27972
28771
|
const keyed = requireKeyed(el);
|
|
27973
28772
|
if (!keyed.ok) return keyed;
|
|
27974
|
-
if (
|
|
27975
|
-
const added = addProxyElement(model, el, options.proxyEvaluator);
|
|
28773
|
+
if (nextSpatialStructureId === null || usesAuthoredCivilHierarchy && state.civilParent !== "bridge-part") return err(specError(usesAuthoredCivilHierarchy ? "FAMILIES_INVALID_CIVIL_HIERARCHY" : "FAMILIES_NO_STOREY", usesAuthoredCivilHierarchy ? `familiesToBim: physical product '${el.keyPath}' needs a Bridge Part ancestor` : `familiesToBim: '${el.keyPath}' has no Storey ancestor — IFC elements need spatial containment; a container family needs archetype: 'storey' to be recognised under any name`));
|
|
28774
|
+
const added = addProxyElement(model, el, options.proxyEvaluator, state.projectedSpatialTranslation);
|
|
27976
28775
|
if (!added.ok) return added;
|
|
27977
|
-
model.placeIn(added.value,
|
|
28776
|
+
model.placeIn(added.value, nextSpatialStructureId);
|
|
27978
28777
|
idByKeyPath.set(el.keyPath, added.value);
|
|
27979
28778
|
proxied.push({
|
|
27980
28779
|
keyPath: el.keyPath,
|
|
@@ -27984,13 +28783,25 @@ function familiesToBim(root, options) {
|
|
|
27984
28783
|
proxiedHere = true;
|
|
27985
28784
|
}
|
|
27986
28785
|
for (const child of el.children) {
|
|
27987
|
-
if ((
|
|
27988
|
-
const r = walk(child,
|
|
28786
|
+
if ((effectiveArchetype === "wall" || proxiedHere) && child.type === "Opening") continue;
|
|
28787
|
+
const r = walk(child, {
|
|
28788
|
+
spatialStructureId: nextSpatialStructureId,
|
|
28789
|
+
civilParent: nextCivilParent,
|
|
28790
|
+
rotated: rotatedHere,
|
|
28791
|
+
cumulativeTranslation: cumulativeTranslationHere,
|
|
28792
|
+
projectedSpatialTranslation: nextProjectedSpatialTranslation
|
|
28793
|
+
});
|
|
27989
28794
|
if (!r.ok) return r;
|
|
27990
28795
|
}
|
|
27991
28796
|
return ok(void 0);
|
|
27992
28797
|
};
|
|
27993
|
-
const walked = walk(root,
|
|
28798
|
+
const walked = walk(root, {
|
|
28799
|
+
spatialStructureId: usesAuthoredCivilHierarchy ? projectId : null,
|
|
28800
|
+
civilParent: "project",
|
|
28801
|
+
rotated: false,
|
|
28802
|
+
cumulativeTranslation: ZERO_TRANSLATION,
|
|
28803
|
+
projectedSpatialTranslation: ZERO_TRANSLATION
|
|
28804
|
+
});
|
|
27994
28805
|
if (!walked.ok) {
|
|
27995
28806
|
model[Symbol.dispose]();
|
|
27996
28807
|
return walked;
|
|
@@ -28002,4 +28813,4 @@ function familiesToBim(root, options) {
|
|
|
28002
28813
|
});
|
|
28003
28814
|
}
|
|
28004
28815
|
//#endregion
|
|
28005
|
-
export { BimModel, DEFAULT_IFC_SCHEMA, DEFAULT_UNITS, IFC_SCHEMAS, PSET_PROPERTY_TYPE_TABLE, PSET_TEMPLATES, SpfReader, bcfError, checkGeometryValidity, checkGherkinRules, checkIdsData as checkIds, checkIdsData, 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 };
|
|
28816
|
+
export { BimModel, DEFAULT_IFC_SCHEMA, DEFAULT_UNITS, IFC_SCHEMAS, PSET_PROPERTY_TYPE_TABLE, PSET_TEMPLATES, SpfReader, bcfError, checkGeometryValidity, checkGherkinRules, checkIdsData as checkIds, checkIdsData, 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, parseBridgePartSpec, parseBridgeSpec, 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 };
|