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.cjs
CHANGED
|
@@ -6320,15 +6320,17 @@ var BimModel = class {
|
|
|
6320
6320
|
#projectId = null;
|
|
6321
6321
|
#modelScope = "";
|
|
6322
6322
|
#usedStableKeys = /* @__PURE__ */ new Set();
|
|
6323
|
-
init(spec) {
|
|
6323
|
+
init(spec, options) {
|
|
6324
6324
|
if (this.#projectId !== null) return (0, brepjs.err)(specError("DUPLICATE_PROJECT", "BimModel.init() called twice — only one project per model"));
|
|
6325
6325
|
this.#modelScope = spec.projectId ?? `${spec.name}::${spec.description ?? ""}`;
|
|
6326
|
-
const
|
|
6326
|
+
const keyCheck = this.#checkStableKey(options);
|
|
6327
|
+
if (!keyCheck.ok) return keyCheck;
|
|
6328
|
+
const id = this.#makeElement("PROJECT", spec, null, options?.stableKey);
|
|
6327
6329
|
this.#projectId = id;
|
|
6328
6330
|
return (0, brepjs.ok)(id);
|
|
6329
6331
|
}
|
|
6330
6332
|
[Symbol.dispose]() {
|
|
6331
|
-
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]();
|
|
6333
|
+
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]();
|
|
6332
6334
|
else if (el.category === "CURTAIN_WALL") {
|
|
6333
6335
|
for (const panel of el.geometry.panels) panel.solid[Symbol.dispose]();
|
|
6334
6336
|
for (const mullion of el.geometry.mullions) mullion.solid[Symbol.dispose]();
|
|
@@ -6339,6 +6341,26 @@ var BimModel = class {
|
|
|
6339
6341
|
if (!keyCheck.ok) return keyCheck;
|
|
6340
6342
|
return (0, brepjs.ok)(this.#makeElement("SITE", spec, null, options?.stableKey));
|
|
6341
6343
|
}
|
|
6344
|
+
addBridge(spec, options) {
|
|
6345
|
+
const keyCheck = this.#checkStableKey(options);
|
|
6346
|
+
if (!keyCheck.ok) return keyCheck;
|
|
6347
|
+
return (0, brepjs.ok)(this.#makeElement("BRIDGE", spec, null, options?.stableKey));
|
|
6348
|
+
}
|
|
6349
|
+
addBridgePart(spec, options) {
|
|
6350
|
+
const keyCheck = this.#checkStableKey(options);
|
|
6351
|
+
if (!keyCheck.ok) return keyCheck;
|
|
6352
|
+
return (0, brepjs.ok)(this.#makeElement("BRIDGE_PART", spec, null, options?.stableKey));
|
|
6353
|
+
}
|
|
6354
|
+
/** Adds a typed IfcEarthworksFill body. Ownership of `spec.solid` transfers
|
|
6355
|
+
* to the model only when this call succeeds. */
|
|
6356
|
+
addEarthworksFill(spec, options) {
|
|
6357
|
+
const keyCheck = this.#checkStableKey(options);
|
|
6358
|
+
if (!keyCheck.ok) return keyCheck;
|
|
6359
|
+
if (spec.solid === null || spec.solid === void 0) return (0, brepjs.err)(specError("EARTHWORKS_FILL_NO_GEOMETRY", "EarthworksFillSpec.solid is required"));
|
|
6360
|
+
const id = this.#makeElement("EARTHWORKS_FILL", spec, spec.solid, options?.stableKey);
|
|
6361
|
+
this.#associateMaterial(id, spec);
|
|
6362
|
+
return (0, brepjs.ok)(id);
|
|
6363
|
+
}
|
|
6342
6364
|
addBuilding(spec, options) {
|
|
6343
6365
|
const keyCheck = this.#checkStableKey(options);
|
|
6344
6366
|
if (!keyCheck.ok) return keyCheck;
|
|
@@ -6958,6 +6980,21 @@ var BimModel = class {
|
|
|
6958
6980
|
for (const el of this.#elements.values()) if (el.category === "BEAM") beams.push(el);
|
|
6959
6981
|
return beams;
|
|
6960
6982
|
}
|
|
6983
|
+
getBridges() {
|
|
6984
|
+
const bridges = [];
|
|
6985
|
+
for (const el of this.#elements.values()) if (el.category === "BRIDGE") bridges.push(el);
|
|
6986
|
+
return bridges;
|
|
6987
|
+
}
|
|
6988
|
+
getBridgeParts() {
|
|
6989
|
+
const parts = [];
|
|
6990
|
+
for (const el of this.#elements.values()) if (el.category === "BRIDGE_PART") parts.push(el);
|
|
6991
|
+
return parts;
|
|
6992
|
+
}
|
|
6993
|
+
getEarthworksFills() {
|
|
6994
|
+
const fills = [];
|
|
6995
|
+
for (const el of this.#elements.values()) if (el.category === "EARTHWORKS_FILL") fills.push(el);
|
|
6996
|
+
return fills;
|
|
6997
|
+
}
|
|
6961
6998
|
getColumns() {
|
|
6962
6999
|
const columns = [];
|
|
6963
7000
|
for (const el of this.#elements.values()) if (el.category === "COLUMN") columns.push(el);
|
|
@@ -7382,23 +7419,38 @@ function place(solid, frame) {
|
|
|
7382
7419
|
function disposeAll(solids) {
|
|
7383
7420
|
for (const s of solids) s[Symbol.dispose]();
|
|
7384
7421
|
}
|
|
7422
|
+
function placeWithinParent(solid, localFrame, parentFrame) {
|
|
7423
|
+
try {
|
|
7424
|
+
var _usingCtx$3 = _usingCtx();
|
|
7425
|
+
const local = place(solid, localFrame);
|
|
7426
|
+
if (!local.ok || parentFrame === void 0) return local;
|
|
7427
|
+
return place(_usingCtx$3.u(local.value), parentFrame);
|
|
7428
|
+
} catch (_) {
|
|
7429
|
+
_usingCtx$3.e = _;
|
|
7430
|
+
} finally {
|
|
7431
|
+
_usingCtx$3.d();
|
|
7432
|
+
}
|
|
7433
|
+
}
|
|
7385
7434
|
/**
|
|
7386
|
-
* Returns each element's geometry transformed to its
|
|
7387
|
-
*
|
|
7388
|
-
*
|
|
7389
|
-
*
|
|
7435
|
+
* Returns each element's geometry transformed to its element-local placement,
|
|
7436
|
+
* and optionally through the cumulative frame of its containing spatial
|
|
7437
|
+
* structure, as fresh caller-owned solids. Pass `parentFrame` to obtain world
|
|
7438
|
+
* coordinates for elements beneath a placed Site, Bridge, Bridge Part, or
|
|
7439
|
+
* other spatial structure. **Dispose the returned solids** (e.g. via `using` /
|
|
7440
|
+
* `[Symbol.dispose]`) when you own their lifetime — they are independent of the model
|
|
7390
7441
|
* (`BimModel[Symbol.dispose]` frees only the stored, unplaced `.geometry`). On any
|
|
7391
7442
|
* failure the solids already built for this call are disposed before the error is
|
|
7392
7443
|
* returned, so no partial array is leaked.
|
|
7393
7444
|
*
|
|
7394
7445
|
* Stairs and ramps carry no element solid (`.geometry` is null), so flight
|
|
7395
7446
|
* solids are built from `spec.flights` and placed per flight. Curtain walls
|
|
7396
|
-
* return placed panels + mullions.
|
|
7397
|
-
*
|
|
7398
|
-
*
|
|
7399
|
-
* an empty array.
|
|
7447
|
+
* return placed panels + mullions. Proxy and Earthworks Fill bodies are stored
|
|
7448
|
+
* relative to their containing spatial structure, so their body is copied and
|
|
7449
|
+
* then transformed by `parentFrame` when supplied. Elements with no solid geometry
|
|
7450
|
+
* (doors/windows/groups/spatial) return an empty array.
|
|
7400
7451
|
*/
|
|
7401
|
-
function placedSolids(el) {
|
|
7452
|
+
function placedSolids(el, options = {}) {
|
|
7453
|
+
const parentFrame = options.parentFrame;
|
|
7402
7454
|
switch (el.category) {
|
|
7403
7455
|
case "WALL":
|
|
7404
7456
|
case "SLAB":
|
|
@@ -7410,56 +7462,57 @@ function placedSolids(el) {
|
|
|
7410
7462
|
case "PILE":
|
|
7411
7463
|
case "COVERING":
|
|
7412
7464
|
case "RAILING": {
|
|
7413
|
-
const placed =
|
|
7465
|
+
const placed = placeWithinParent(el.geometry, el.spec, parentFrame);
|
|
7414
7466
|
if (!placed.ok) return placed;
|
|
7415
7467
|
return (0, brepjs.ok)([placed.value]);
|
|
7416
7468
|
}
|
|
7417
7469
|
case "STAIR": {
|
|
7418
7470
|
const out = [];
|
|
7419
7471
|
for (const flight of el.spec.flights) try {
|
|
7420
|
-
var
|
|
7472
|
+
var _usingCtx3 = _usingCtx();
|
|
7421
7473
|
const built = stairFlightToSolid(flight);
|
|
7422
7474
|
if (!built.ok) {
|
|
7423
7475
|
disposeAll(out);
|
|
7424
7476
|
return (0, brepjs.err)(built.error);
|
|
7425
7477
|
}
|
|
7426
|
-
const placed =
|
|
7478
|
+
const placed = placeWithinParent(_usingCtx3.u(built.value.solid), flight, parentFrame);
|
|
7427
7479
|
if (!placed.ok) {
|
|
7428
7480
|
disposeAll(out);
|
|
7429
7481
|
return placed;
|
|
7430
7482
|
}
|
|
7431
7483
|
out.push(placed.value);
|
|
7432
7484
|
} catch (_) {
|
|
7433
|
-
|
|
7485
|
+
_usingCtx3.e = _;
|
|
7434
7486
|
} finally {
|
|
7435
|
-
|
|
7487
|
+
_usingCtx3.d();
|
|
7436
7488
|
}
|
|
7437
7489
|
return (0, brepjs.ok)(out);
|
|
7438
7490
|
}
|
|
7439
7491
|
case "RAMP": {
|
|
7440
7492
|
const out = [];
|
|
7441
7493
|
for (const flight of el.spec.flights) try {
|
|
7442
|
-
var
|
|
7494
|
+
var _usingCtx4 = _usingCtx();
|
|
7443
7495
|
const built = rampFlightToSolid(flight);
|
|
7444
7496
|
if (!built.ok) {
|
|
7445
7497
|
disposeAll(out);
|
|
7446
7498
|
return (0, brepjs.err)(built.error);
|
|
7447
7499
|
}
|
|
7448
|
-
const placed =
|
|
7500
|
+
const placed = placeWithinParent(_usingCtx4.u(built.value.solid), flight, parentFrame);
|
|
7449
7501
|
if (!placed.ok) {
|
|
7450
7502
|
disposeAll(out);
|
|
7451
7503
|
return placed;
|
|
7452
7504
|
}
|
|
7453
7505
|
out.push(placed.value);
|
|
7454
7506
|
} catch (_) {
|
|
7455
|
-
|
|
7507
|
+
_usingCtx4.e = _;
|
|
7456
7508
|
} finally {
|
|
7457
|
-
|
|
7509
|
+
_usingCtx4.d();
|
|
7458
7510
|
}
|
|
7459
7511
|
return (0, brepjs.ok)(out);
|
|
7460
7512
|
}
|
|
7461
|
-
case "PROXY":
|
|
7462
|
-
|
|
7513
|
+
case "PROXY":
|
|
7514
|
+
case "EARTHWORKS_FILL": {
|
|
7515
|
+
const placed = placeWithinParent(el.geometry, {
|
|
7463
7516
|
origin: [
|
|
7464
7517
|
0,
|
|
7465
7518
|
0,
|
|
@@ -7475,14 +7528,14 @@ function placedSolids(el) {
|
|
|
7475
7528
|
0,
|
|
7476
7529
|
1
|
|
7477
7530
|
]
|
|
7478
|
-
});
|
|
7531
|
+
}, parentFrame);
|
|
7479
7532
|
if (!placed.ok) return placed;
|
|
7480
7533
|
return (0, brepjs.ok)([placed.value]);
|
|
7481
7534
|
}
|
|
7482
7535
|
case "CURTAIN_WALL": {
|
|
7483
7536
|
const out = [];
|
|
7484
7537
|
for (const c of [...el.geometry.panels, ...el.geometry.mullions]) try {
|
|
7485
|
-
var
|
|
7538
|
+
var _usingCtx5 = _usingCtx();
|
|
7486
7539
|
const componentLocal = place(c.solid, {
|
|
7487
7540
|
origin: c.origin,
|
|
7488
7541
|
axisX: [
|
|
@@ -7500,16 +7553,16 @@ function placedSolids(el) {
|
|
|
7500
7553
|
disposeAll(out);
|
|
7501
7554
|
return componentLocal;
|
|
7502
7555
|
}
|
|
7503
|
-
const placed =
|
|
7556
|
+
const placed = placeWithinParent(_usingCtx5.u(componentLocal.value), el.spec, parentFrame);
|
|
7504
7557
|
if (!placed.ok) {
|
|
7505
7558
|
disposeAll(out);
|
|
7506
7559
|
return placed;
|
|
7507
7560
|
}
|
|
7508
7561
|
out.push(placed.value);
|
|
7509
7562
|
} catch (_) {
|
|
7510
|
-
|
|
7563
|
+
_usingCtx5.e = _;
|
|
7511
7564
|
} finally {
|
|
7512
|
-
|
|
7565
|
+
_usingCtx5.d();
|
|
7513
7566
|
}
|
|
7514
7567
|
return (0, brepjs.ok)(out);
|
|
7515
7568
|
}
|
|
@@ -7564,6 +7617,8 @@ var IFC4X3_ONLY_ENTITIES = /* @__PURE__ */ new Set([
|
|
|
7564
7617
|
"IfcRoad",
|
|
7565
7618
|
"IfcRailway",
|
|
7566
7619
|
"IfcBridge",
|
|
7620
|
+
"IfcBridgePart",
|
|
7621
|
+
"IfcEarthworksFill",
|
|
7567
7622
|
"IfcMarineFacility"
|
|
7568
7623
|
]);
|
|
7569
7624
|
/**
|
|
@@ -8022,34 +8077,23 @@ function writeProject$1(w, guid, name, ownerHistoryId, unitAssignmentId, geomCon
|
|
|
8022
8077
|
});
|
|
8023
8078
|
return id;
|
|
8024
8079
|
}
|
|
8025
|
-
function writeSite(w, guid,
|
|
8026
|
-
const
|
|
8027
|
-
0,
|
|
8028
|
-
0,
|
|
8029
|
-
0
|
|
8030
|
-
]);
|
|
8031
|
-
const localPlacementId = w.nextId();
|
|
8032
|
-
w.writeLine({
|
|
8033
|
-
expressID: localPlacementId,
|
|
8034
|
-
type: web_ifc.IFCLOCALPLACEMENT,
|
|
8035
|
-
PlacementRelTo: null,
|
|
8036
|
-
RelativePlacement: w.ref(placement3DId)
|
|
8037
|
-
});
|
|
8080
|
+
function writeSite(w, guid, spec, ownerHistoryId, parentPlacementId = null) {
|
|
8081
|
+
const localPlacementId = writeSpatialLocalPlacement(w, spec, parentPlacementId);
|
|
8038
8082
|
const entityId = w.nextId();
|
|
8039
8083
|
w.writeLine({
|
|
8040
8084
|
expressID: entityId,
|
|
8041
8085
|
type: web_ifc.IFCSITE,
|
|
8042
8086
|
GlobalId: w.mkType(web_ifc.IFCGLOBALLYUNIQUEID, guid),
|
|
8043
8087
|
OwnerHistory: w.ref(ownerHistoryId),
|
|
8044
|
-
Name: w.mkType(web_ifc.IFCLABEL, name),
|
|
8045
|
-
Description: null,
|
|
8088
|
+
Name: w.mkType(web_ifc.IFCLABEL, spec.name),
|
|
8089
|
+
Description: spec.description ? w.mkType(web_ifc.IFCTEXT, spec.description) : null,
|
|
8046
8090
|
ObjectType: null,
|
|
8047
8091
|
ObjectPlacement: w.ref(localPlacementId),
|
|
8048
8092
|
Representation: null,
|
|
8049
8093
|
LongName: null,
|
|
8050
8094
|
CompositionType: {
|
|
8051
8095
|
type: 3,
|
|
8052
|
-
value: "ELEMENT"
|
|
8096
|
+
value: spec.compositionType ?? "ELEMENT"
|
|
8053
8097
|
},
|
|
8054
8098
|
RefLatitude: null,
|
|
8055
8099
|
RefLongitude: null,
|
|
@@ -8062,6 +8106,34 @@ function writeSite(w, guid, name, ownerHistoryId) {
|
|
|
8062
8106
|
placementId: localPlacementId
|
|
8063
8107
|
};
|
|
8064
8108
|
}
|
|
8109
|
+
function writeSpatialLocalPlacement(w, spec, parentPlacementId) {
|
|
8110
|
+
const toMetres = (value) => [
|
|
8111
|
+
toIfcLengthM(value[0]),
|
|
8112
|
+
toIfcLengthM(value[1]),
|
|
8113
|
+
toIfcLengthM(value[2])
|
|
8114
|
+
];
|
|
8115
|
+
const placement3DId = writeAxis2Placement3D(w, toMetres(spec.origin ?? [
|
|
8116
|
+
0,
|
|
8117
|
+
0,
|
|
8118
|
+
0
|
|
8119
|
+
]), spec.axisZ ?? [
|
|
8120
|
+
0,
|
|
8121
|
+
0,
|
|
8122
|
+
1
|
|
8123
|
+
], spec.axisX ?? [
|
|
8124
|
+
1,
|
|
8125
|
+
0,
|
|
8126
|
+
0
|
|
8127
|
+
]);
|
|
8128
|
+
const localPlacementId = w.nextId();
|
|
8129
|
+
w.writeLine({
|
|
8130
|
+
expressID: localPlacementId,
|
|
8131
|
+
type: web_ifc.IFCLOCALPLACEMENT,
|
|
8132
|
+
PlacementRelTo: parentPlacementId !== null ? w.ref(parentPlacementId) : null,
|
|
8133
|
+
RelativePlacement: w.ref(placement3DId)
|
|
8134
|
+
});
|
|
8135
|
+
return localPlacementId;
|
|
8136
|
+
}
|
|
8065
8137
|
function writeBuilding(w, guid, name, ownerHistoryId, parentPlacementId) {
|
|
8066
8138
|
const placement3DId = writeAxis2Placement3D(w, [
|
|
8067
8139
|
0,
|
|
@@ -10181,6 +10253,111 @@ function writeRelNests(w, guid, ownerHistoryId, relatingObjectId, relatedObjectI
|
|
|
10181
10253
|
});
|
|
10182
10254
|
}
|
|
10183
10255
|
//#endregion
|
|
10256
|
+
//#region src/ifc-writer/infrastructureWriter.ts
|
|
10257
|
+
function writeBridge(w, guid, spec, ownerHistoryId, parentPlacementId) {
|
|
10258
|
+
const placementId = writeSpatialLocalPlacement(w, spec, parentPlacementId);
|
|
10259
|
+
const entityId = w.nextId();
|
|
10260
|
+
w.writeLine({
|
|
10261
|
+
expressID: entityId,
|
|
10262
|
+
type: web_ifc.IFCBRIDGE,
|
|
10263
|
+
GlobalId: w.mkType(web_ifc.IFCGLOBALLYUNIQUEID, guid),
|
|
10264
|
+
OwnerHistory: w.ref(ownerHistoryId),
|
|
10265
|
+
Name: w.mkType(web_ifc.IFCLABEL, spec.name),
|
|
10266
|
+
Description: spec.description ? w.mkType(web_ifc.IFCTEXT, spec.description) : null,
|
|
10267
|
+
ObjectType: null,
|
|
10268
|
+
ObjectPlacement: w.ref(placementId),
|
|
10269
|
+
Representation: null,
|
|
10270
|
+
LongName: null,
|
|
10271
|
+
CompositionType: {
|
|
10272
|
+
type: 3,
|
|
10273
|
+
value: spec.compositionType ?? "ELEMENT"
|
|
10274
|
+
},
|
|
10275
|
+
PredefinedType: {
|
|
10276
|
+
type: 3,
|
|
10277
|
+
value: spec.predefinedType ?? "NOTDEFINED"
|
|
10278
|
+
}
|
|
10279
|
+
});
|
|
10280
|
+
return {
|
|
10281
|
+
entityId,
|
|
10282
|
+
placementId
|
|
10283
|
+
};
|
|
10284
|
+
}
|
|
10285
|
+
function writeBridgePart(w, guid, spec, ownerHistoryId, parentPlacementId) {
|
|
10286
|
+
const placementId = writeSpatialLocalPlacement(w, spec, parentPlacementId);
|
|
10287
|
+
const entityId = w.nextId();
|
|
10288
|
+
w.writeLine({
|
|
10289
|
+
expressID: entityId,
|
|
10290
|
+
type: web_ifc.IFCBRIDGEPART,
|
|
10291
|
+
GlobalId: w.mkType(web_ifc.IFCGLOBALLYUNIQUEID, guid),
|
|
10292
|
+
OwnerHistory: w.ref(ownerHistoryId),
|
|
10293
|
+
Name: w.mkType(web_ifc.IFCLABEL, spec.name),
|
|
10294
|
+
Description: spec.description ? w.mkType(web_ifc.IFCTEXT, spec.description) : null,
|
|
10295
|
+
ObjectType: null,
|
|
10296
|
+
ObjectPlacement: w.ref(placementId),
|
|
10297
|
+
Representation: null,
|
|
10298
|
+
LongName: null,
|
|
10299
|
+
CompositionType: {
|
|
10300
|
+
type: 3,
|
|
10301
|
+
value: spec.compositionType ?? "ELEMENT"
|
|
10302
|
+
},
|
|
10303
|
+
UsageType: {
|
|
10304
|
+
type: 3,
|
|
10305
|
+
value: spec.usageType
|
|
10306
|
+
},
|
|
10307
|
+
PredefinedType: {
|
|
10308
|
+
type: 3,
|
|
10309
|
+
value: spec.predefinedType ?? "NOTDEFINED"
|
|
10310
|
+
}
|
|
10311
|
+
});
|
|
10312
|
+
return {
|
|
10313
|
+
entityId,
|
|
10314
|
+
placementId
|
|
10315
|
+
};
|
|
10316
|
+
}
|
|
10317
|
+
function writeEarthworksFillEntity(w, guid, spec, ownerHistoryId, localPlacementId, productDefinitionShapeId) {
|
|
10318
|
+
const entityId = w.nextId();
|
|
10319
|
+
w.writeLine({
|
|
10320
|
+
expressID: entityId,
|
|
10321
|
+
type: web_ifc.IFCEARTHWORKSFILL,
|
|
10322
|
+
GlobalId: w.mkType(web_ifc.IFCGLOBALLYUNIQUEID, guid),
|
|
10323
|
+
OwnerHistory: w.ref(ownerHistoryId),
|
|
10324
|
+
Name: w.mkType(web_ifc.IFCLABEL, spec.name),
|
|
10325
|
+
Description: null,
|
|
10326
|
+
ObjectType: null,
|
|
10327
|
+
ObjectPlacement: w.ref(localPlacementId),
|
|
10328
|
+
Representation: w.ref(productDefinitionShapeId),
|
|
10329
|
+
Tag: null,
|
|
10330
|
+
PredefinedType: {
|
|
10331
|
+
type: 3,
|
|
10332
|
+
value: spec.predefinedType ?? "NOTDEFINED"
|
|
10333
|
+
}
|
|
10334
|
+
});
|
|
10335
|
+
return entityId;
|
|
10336
|
+
}
|
|
10337
|
+
//#endregion
|
|
10338
|
+
//#region src/ifc-writer/tessellatedProductWriter.ts
|
|
10339
|
+
/** Write a tessellated body in the coordinate frame supplied by the caller. */
|
|
10340
|
+
function writeTessellatedProductGeometry(w, solid, geomSubContextId, parentPlacementId) {
|
|
10341
|
+
const placement3DId = writeAxis2Placement3D(w, [
|
|
10342
|
+
0,
|
|
10343
|
+
0,
|
|
10344
|
+
0
|
|
10345
|
+
]);
|
|
10346
|
+
const localPlacementId = w.nextId();
|
|
10347
|
+
w.writeLine({
|
|
10348
|
+
expressID: localPlacementId,
|
|
10349
|
+
type: web_ifc.IFCLOCALPLACEMENT,
|
|
10350
|
+
PlacementRelTo: parentPlacementId !== null ? w.ref(parentPlacementId) : null,
|
|
10351
|
+
RelativePlacement: w.ref(placement3DId)
|
|
10352
|
+
});
|
|
10353
|
+
const tessellation = writeTessellation(w, solid, geomSubContextId, localPlacementId);
|
|
10354
|
+
return {
|
|
10355
|
+
localPlacementId,
|
|
10356
|
+
productDefinitionShapeId: tessellation.productDefinitionShapeId,
|
|
10357
|
+
tessellation
|
|
10358
|
+
};
|
|
10359
|
+
}
|
|
10360
|
+
//#endregion
|
|
10184
10361
|
//#region src/ifc-writer/groupWriter.ts
|
|
10185
10362
|
/**
|
|
10186
10363
|
* Emits an IfcZone grouping object. `longName` (a descriptive label such as
|
|
@@ -11659,29 +11836,12 @@ function writeWindowCommonPset(w, ownerHistoryId, windowEntityId, spec) {
|
|
|
11659
11836
|
//#region src/ifc-writer/proxyWriter.ts
|
|
11660
11837
|
/**
|
|
11661
11838
|
* Writes the IfcLocalPlacement (at origin, relative to parentPlacementId) and a
|
|
11662
|
-
* tessellated body (IfcTriangulatedFaceSet) for a proxy's solid.
|
|
11663
|
-
*
|
|
11664
|
-
*
|
|
11839
|
+
* tessellated body (IfcTriangulatedFaceSet) for a proxy's solid. Solid
|
|
11840
|
+
* coordinates are emitted as supplied; the caller is responsible for matching
|
|
11841
|
+
* them to the containing spatial structure's frame.
|
|
11665
11842
|
*/
|
|
11666
11843
|
function writeProxyGeometry(w, spec, geomSubContextId, parentPlacementId) {
|
|
11667
|
-
|
|
11668
|
-
0,
|
|
11669
|
-
0,
|
|
11670
|
-
0
|
|
11671
|
-
]);
|
|
11672
|
-
const localPlacementId = w.nextId();
|
|
11673
|
-
w.writeLine({
|
|
11674
|
-
expressID: localPlacementId,
|
|
11675
|
-
type: web_ifc.IFCLOCALPLACEMENT,
|
|
11676
|
-
PlacementRelTo: parentPlacementId !== null ? w.ref(parentPlacementId) : null,
|
|
11677
|
-
RelativePlacement: w.ref(placement3DId)
|
|
11678
|
-
});
|
|
11679
|
-
const tessellation = writeTessellation(w, spec.solid, geomSubContextId, localPlacementId);
|
|
11680
|
-
return {
|
|
11681
|
-
localPlacementId,
|
|
11682
|
-
productDefinitionShapeId: tessellation.productDefinitionShapeId,
|
|
11683
|
-
tessellation
|
|
11684
|
-
};
|
|
11844
|
+
return writeTessellatedProductGeometry(w, spec.solid, geomSubContextId, parentPlacementId);
|
|
11685
11845
|
}
|
|
11686
11846
|
function writeProxyEntity(w, guid, name, predefinedType, ownerHistoryId, localPlacementId, productDefinitionShapeId) {
|
|
11687
11847
|
const id = w.nextId();
|
|
@@ -11932,6 +12092,7 @@ var CONTAINABLE_CATEGORIES = /* @__PURE__ */ new Set([
|
|
|
11932
12092
|
"SLAB",
|
|
11933
12093
|
"BEAM",
|
|
11934
12094
|
"COLUMN",
|
|
12095
|
+
"EARTHWORKS_FILL",
|
|
11935
12096
|
"DOOR",
|
|
11936
12097
|
"WINDOW"
|
|
11937
12098
|
]);
|
|
@@ -12051,7 +12212,9 @@ function hasSpatialStructure(api, modelId) {
|
|
|
12051
12212
|
return [
|
|
12052
12213
|
web_ifc.IFCSITE,
|
|
12053
12214
|
web_ifc.IFCBUILDING,
|
|
12054
|
-
web_ifc.IFCBUILDINGSTOREY
|
|
12215
|
+
web_ifc.IFCBUILDINGSTOREY,
|
|
12216
|
+
web_ifc.IFCBRIDGE,
|
|
12217
|
+
web_ifc.IFCBRIDGEPART
|
|
12055
12218
|
].some((t) => api.GetLineIDsWithType(modelId, t).size() > 0);
|
|
12056
12219
|
}
|
|
12057
12220
|
function checkGlobalIds(api, modelId, lineIds, lineCount) {
|
|
@@ -12095,6 +12258,7 @@ var KEY_ENTITY_NAMES = [
|
|
|
12095
12258
|
"IfcSlab",
|
|
12096
12259
|
"IfcBeam",
|
|
12097
12260
|
"IfcColumn",
|
|
12261
|
+
"IfcEarthworksFill",
|
|
12098
12262
|
"IfcRelContainedInSpatialStructure",
|
|
12099
12263
|
"IfcRelAggregates",
|
|
12100
12264
|
"IfcPropertySet"
|
|
@@ -12105,6 +12269,7 @@ var KEY_ENTITY_TYPES = [
|
|
|
12105
12269
|
["IfcSlab", web_ifc.IFCSLAB],
|
|
12106
12270
|
["IfcBeam", web_ifc.IFCBEAM],
|
|
12107
12271
|
["IfcColumn", web_ifc.IFCCOLUMN],
|
|
12272
|
+
["IfcEarthworksFill", web_ifc.IFCEARTHWORKSFILL],
|
|
12108
12273
|
["IfcRelContainedInSpatialStructure", web_ifc.IFCRELCONTAINEDINSPATIALSTRUCTURE],
|
|
12109
12274
|
["IfcRelAggregates", web_ifc.IFCRELAGGREGATES],
|
|
12110
12275
|
["IfcPropertySet", web_ifc.IFCPROPERTYSET]
|
|
@@ -13097,7 +13262,7 @@ function checkQuantitySets(reader) {
|
|
|
13097
13262
|
return issues;
|
|
13098
13263
|
}
|
|
13099
13264
|
function checkGeoreferencing(reader) {
|
|
13100
|
-
if (!(reader.getLinesOfType(web_ifc.IFCBUILDING).length > 0)) return [];
|
|
13265
|
+
if (!(reader.getLinesOfType(web_ifc.IFCBUILDING).length > 0 || reader.getLinesOfType(web_ifc.IFCBRIDGE).length > 0)) return [];
|
|
13101
13266
|
if (reader.getLinesOfType(web_ifc.IFCPROJECTEDCRS).length > 0 || reader.getLinesOfType(web_ifc.IFCGEOGRAPHICCRS).length > 0) return [];
|
|
13102
13267
|
return [issue("warning", "NO_GEOREFERENCING", "Model contains a facility but no IfcProjectedCRS/IfcGeographicCRS (GRF003) — set ProjectSpec.crs")];
|
|
13103
13268
|
}
|
|
@@ -13106,6 +13271,7 @@ function checkGeoreferencing(reader) {
|
|
|
13106
13271
|
async function toIfc(model, meta) {
|
|
13107
13272
|
const project = model.getProject();
|
|
13108
13273
|
if (!project) return (0, brepjs.err)(ifcError("NO_PROJECT", "BimModel has no project — call model.init() first"));
|
|
13274
|
+
if ((model.getBridges().length > 0 || model.getBridgeParts().length > 0 || model.getEarthworksFills().length > 0) && (meta.ifcSchema ?? "IFC4") !== "IFC4X3") return (0, brepjs.err)(ifcError("IFC4X3_REQUIRED", "Bridge, Bridge Part, and Earthworks Fill entities require IFC4X3 serialization"));
|
|
13109
13275
|
const authorName = [meta.author?.givenName, meta.author?.familyName].filter((p) => Boolean(p)).join(" ");
|
|
13110
13276
|
const writerResult = await IfcWriter.create(meta.mvdViewDefinition, meta.ifcSchema, {
|
|
13111
13277
|
author: authorName,
|
|
@@ -13119,6 +13285,9 @@ async function toIfc(model, meta) {
|
|
|
13119
13285
|
const walls = model.getWalls();
|
|
13120
13286
|
const slabs = model.getSlabs();
|
|
13121
13287
|
const beams = model.getBeams();
|
|
13288
|
+
const bridges = model.getBridges();
|
|
13289
|
+
const bridgeParts = model.getBridgeParts();
|
|
13290
|
+
const earthworksFills = model.getEarthworksFills();
|
|
13122
13291
|
const columns = model.getColumns();
|
|
13123
13292
|
const doors = model.getDoors();
|
|
13124
13293
|
const windows = model.getWindows();
|
|
@@ -13144,10 +13313,33 @@ async function toIfc(model, meta) {
|
|
|
13144
13313
|
idMap.set(project.localId, projectExpressId);
|
|
13145
13314
|
for (const el of elements) {
|
|
13146
13315
|
if (el.category !== "SITE") continue;
|
|
13147
|
-
const
|
|
13316
|
+
const parentId = findParentOf(el.localId, relationships);
|
|
13317
|
+
const parentPlacementId = parentId !== null ? placementMap.get(parentId) ?? null : null;
|
|
13318
|
+
const { entityId, placementId } = writeSite(w, el.guid, el.spec, ownerHistoryId, parentPlacementId);
|
|
13148
13319
|
idMap.set(el.localId, entityId);
|
|
13149
13320
|
placementMap.set(el.localId, placementId);
|
|
13150
13321
|
}
|
|
13322
|
+
for (const bridge of bridges) {
|
|
13323
|
+
const parentId = findParentOf(bridge.localId, relationships);
|
|
13324
|
+
const parentPlacementId = parentId !== null ? placementMap.get(parentId) ?? null : null;
|
|
13325
|
+
const { entityId, placementId } = writeBridge(w, bridge.guid, bridge.spec, ownerHistoryId, parentPlacementId);
|
|
13326
|
+
idMap.set(bridge.localId, entityId);
|
|
13327
|
+
placementMap.set(bridge.localId, placementId);
|
|
13328
|
+
}
|
|
13329
|
+
const pendingParts = [...bridgeParts];
|
|
13330
|
+
while (pendingParts.length > 0) {
|
|
13331
|
+
const readyIndex = pendingParts.findIndex((candidate) => {
|
|
13332
|
+
const candidateParentId = findParentOf(candidate.localId, relationships);
|
|
13333
|
+
return candidateParentId === null || placementMap.has(candidateParentId);
|
|
13334
|
+
});
|
|
13335
|
+
const [part] = pendingParts.splice(readyIndex === -1 ? 0 : readyIndex, 1);
|
|
13336
|
+
if (part === void 0) break;
|
|
13337
|
+
const parentId = findParentOf(part.localId, relationships);
|
|
13338
|
+
const parentPlacementId = parentId !== null ? placementMap.get(parentId) ?? null : null;
|
|
13339
|
+
const { entityId, placementId } = writeBridgePart(w, part.guid, part.spec, ownerHistoryId, parentPlacementId);
|
|
13340
|
+
idMap.set(part.localId, entityId);
|
|
13341
|
+
placementMap.set(part.localId, placementId);
|
|
13342
|
+
}
|
|
13151
13343
|
for (const el of elements) {
|
|
13152
13344
|
if (el.category !== "BUILDING") continue;
|
|
13153
13345
|
const parentSiteId = findParentOf(el.localId, relationships);
|
|
@@ -13241,6 +13433,15 @@ async function toIfc(model, meta) {
|
|
|
13241
13433
|
placementMap.set(proxy.localId, localPlacementId);
|
|
13242
13434
|
if (proxy.spec.customProperties !== void 0) writeCustomPsets(w, ownerHistoryId, proxyExpressId, proxy.spec.customProperties);
|
|
13243
13435
|
}
|
|
13436
|
+
for (const fill of earthworksFills) {
|
|
13437
|
+
const containingId = findContainerOf(fill.localId, relationships);
|
|
13438
|
+
const parentPlacementId = containingId !== null ? placementMap.get(containingId) ?? null : null;
|
|
13439
|
+
const { localPlacementId, productDefinitionShapeId } = writeTessellatedProductGeometry(w, fill.geometry, geomSubContextId, parentPlacementId);
|
|
13440
|
+
const expressId = writeEarthworksFillEntity(w, fill.guid, fill.spec, ownerHistoryId, localPlacementId, productDefinitionShapeId);
|
|
13441
|
+
idMap.set(fill.localId, expressId);
|
|
13442
|
+
placementMap.set(fill.localId, localPlacementId);
|
|
13443
|
+
if (fill.spec.customProperties !== void 0) writeCustomPsets(w, ownerHistoryId, expressId, fill.spec.customProperties);
|
|
13444
|
+
}
|
|
13244
13445
|
for (const [i, space] of spaces.entries()) {
|
|
13245
13446
|
const containingId = findContainerOf(space.localId, relationships);
|
|
13246
13447
|
const storeyPlacementId = containingId !== null ? placementMap.get(containingId) ?? null : null;
|
|
@@ -13663,6 +13864,7 @@ function collectGeometryIssues(model) {
|
|
|
13663
13864
|
["Beam", model.getBeams()],
|
|
13664
13865
|
["Column", model.getColumns()],
|
|
13665
13866
|
["Proxy", model.getProxies()],
|
|
13867
|
+
["Earthworks Fill", model.getEarthworksFills()],
|
|
13666
13868
|
["Space", model.getSpaces()],
|
|
13667
13869
|
["Roof", model.getRoofs()],
|
|
13668
13870
|
["Footing", model.getFootings()],
|
|
@@ -13733,7 +13935,9 @@ var CATEGORY_BY_TYPE = /* @__PURE__ */ new Map([
|
|
|
13733
13935
|
[web_ifc.IFCPROJECT, "PROJECT"],
|
|
13734
13936
|
[web_ifc.IFCSITE, "SITE"],
|
|
13735
13937
|
[web_ifc.IFCBUILDING, "BUILDING"],
|
|
13736
|
-
[web_ifc.IFCBUILDINGSTOREY, "STOREY"]
|
|
13938
|
+
[web_ifc.IFCBUILDINGSTOREY, "STOREY"],
|
|
13939
|
+
[web_ifc.IFCBRIDGE, "BRIDGE"],
|
|
13940
|
+
[web_ifc.IFCBRIDGEPART, "BRIDGE_PART"]
|
|
13737
13941
|
]);
|
|
13738
13942
|
function refId$1(ref) {
|
|
13739
13943
|
const v = ref?.value;
|
|
@@ -14816,6 +15020,7 @@ var ELEMENT_TYPES = [
|
|
|
14816
15020
|
[web_ifc.IFCRAILING, "RAILING"],
|
|
14817
15021
|
[web_ifc.IFCCOVERING, "COVERING"],
|
|
14818
15022
|
[web_ifc.IFCELEMENTASSEMBLY, "ELEMENT_ASSEMBLY"],
|
|
15023
|
+
[web_ifc.IFCEARTHWORKSFILL, "EARTHWORKS_FILL"],
|
|
14819
15024
|
[web_ifc.IFCBUILDINGELEMENTPROXY, "PROXY"]
|
|
14820
15025
|
];
|
|
14821
15026
|
/**
|
|
@@ -14893,14 +15098,17 @@ function readElement(reader, expressId, category, scale, containment, typeEnums,
|
|
|
14893
15098
|
const psets = readPsets(reader, expressId).map(toImportedPset);
|
|
14894
15099
|
const material = readMaterial(reader, expressId, scale);
|
|
14895
15100
|
const classification = readClassification(reader, expressId);
|
|
14896
|
-
const
|
|
15101
|
+
const spatialStructureExpressId = containment.get(expressId);
|
|
14897
15102
|
return {
|
|
14898
15103
|
expressId,
|
|
14899
15104
|
guid,
|
|
14900
15105
|
name,
|
|
14901
15106
|
category,
|
|
14902
15107
|
...predefinedType !== void 0 ? { predefinedType } : {},
|
|
14903
|
-
...
|
|
15108
|
+
...spatialStructureExpressId !== void 0 ? {
|
|
15109
|
+
spatialStructureExpressId,
|
|
15110
|
+
storeyExpressId: spatialStructureExpressId
|
|
15111
|
+
} : {},
|
|
14904
15112
|
geometry,
|
|
14905
15113
|
psets,
|
|
14906
15114
|
material,
|
|
@@ -15900,6 +16108,115 @@ function parseSlabOpeningInput(input) {
|
|
|
15900
16108
|
return (0, brepjs.ok)(result.data);
|
|
15901
16109
|
}
|
|
15902
16110
|
//#endregion
|
|
16111
|
+
//#region src/specs/spatialSpec.ts
|
|
16112
|
+
var unitVector = tuple([
|
|
16113
|
+
number(),
|
|
16114
|
+
number(),
|
|
16115
|
+
number()
|
|
16116
|
+
]).refine((value) => Math.abs(value[0] ** 2 + value[1] ** 2 + value[2] ** 2 - 1) < 1e-6, { error: "must be a unit vector" });
|
|
16117
|
+
var spatialPlacementFields = {
|
|
16118
|
+
origin: tuple([
|
|
16119
|
+
number(),
|
|
16120
|
+
number(),
|
|
16121
|
+
number()
|
|
16122
|
+
]).optional(),
|
|
16123
|
+
axisX: unitVector.optional(),
|
|
16124
|
+
axisZ: unitVector.optional()
|
|
16125
|
+
};
|
|
16126
|
+
function validateSpatialAxes(data, ctx) {
|
|
16127
|
+
const axisX = data.axisX ?? [
|
|
16128
|
+
1,
|
|
16129
|
+
0,
|
|
16130
|
+
0
|
|
16131
|
+
];
|
|
16132
|
+
const axisZ = data.axisZ ?? [
|
|
16133
|
+
0,
|
|
16134
|
+
0,
|
|
16135
|
+
1
|
|
16136
|
+
];
|
|
16137
|
+
const dot = axisX[0] * axisZ[0] + axisX[1] * axisZ[1] + axisX[2] * axisZ[2];
|
|
16138
|
+
if (Math.abs(dot) > 1e-6) ctx.addIssue({
|
|
16139
|
+
code: "custom",
|
|
16140
|
+
message: "axisX and axisZ must be orthogonal",
|
|
16141
|
+
path: ["axisZ"]
|
|
16142
|
+
});
|
|
16143
|
+
}
|
|
16144
|
+
var SiteSpecSchema = object({
|
|
16145
|
+
name: string().min(1),
|
|
16146
|
+
description: string().optional(),
|
|
16147
|
+
...spatialPlacementFields,
|
|
16148
|
+
compositionType: _enum([
|
|
16149
|
+
"COMPLEX",
|
|
16150
|
+
"ELEMENT",
|
|
16151
|
+
"PARTIAL"
|
|
16152
|
+
]).optional()
|
|
16153
|
+
}).superRefine(validateSpatialAxes);
|
|
16154
|
+
function parseSiteSpec(input) {
|
|
16155
|
+
const result = SiteSpecSchema.safeParse(input);
|
|
16156
|
+
return result.success ? (0, brepjs.ok)(result.data) : (0, brepjs.err)(specError("INVALID_SITE_SPEC", result.error.message, result.error));
|
|
16157
|
+
}
|
|
16158
|
+
//#endregion
|
|
16159
|
+
//#region src/specs/infrastructureSpec.ts
|
|
16160
|
+
var composition = _enum([
|
|
16161
|
+
"COMPLEX",
|
|
16162
|
+
"ELEMENT",
|
|
16163
|
+
"PARTIAL"
|
|
16164
|
+
]).optional();
|
|
16165
|
+
var BridgeSpecSchema = object({
|
|
16166
|
+
name: string().min(1),
|
|
16167
|
+
description: string().optional(),
|
|
16168
|
+
...spatialPlacementFields,
|
|
16169
|
+
compositionType: composition,
|
|
16170
|
+
predefinedType: _enum([
|
|
16171
|
+
"ARCHED",
|
|
16172
|
+
"CABLE_STAYED",
|
|
16173
|
+
"CANTILEVER",
|
|
16174
|
+
"CULVERT",
|
|
16175
|
+
"FRAMEWORK",
|
|
16176
|
+
"GIRDER",
|
|
16177
|
+
"SUSPENSION",
|
|
16178
|
+
"TRUSS",
|
|
16179
|
+
"USERDEFINED",
|
|
16180
|
+
"NOTDEFINED"
|
|
16181
|
+
]).optional()
|
|
16182
|
+
}).superRefine(validateSpatialAxes);
|
|
16183
|
+
var BridgePartSpecSchema = object({
|
|
16184
|
+
name: string().min(1),
|
|
16185
|
+
description: string().optional(),
|
|
16186
|
+
...spatialPlacementFields,
|
|
16187
|
+
compositionType: composition,
|
|
16188
|
+
usageType: _enum([
|
|
16189
|
+
"LATERAL",
|
|
16190
|
+
"LONGITUDINAL",
|
|
16191
|
+
"REGION",
|
|
16192
|
+
"VERTICAL",
|
|
16193
|
+
"USERDEFINED",
|
|
16194
|
+
"NOTDEFINED"
|
|
16195
|
+
]),
|
|
16196
|
+
predefinedType: _enum([
|
|
16197
|
+
"ABUTMENT",
|
|
16198
|
+
"DECK",
|
|
16199
|
+
"DECK_SEGMENT",
|
|
16200
|
+
"FOUNDATION",
|
|
16201
|
+
"PIER",
|
|
16202
|
+
"PIER_SEGMENT",
|
|
16203
|
+
"PYLON",
|
|
16204
|
+
"SUBSTRUCTURE",
|
|
16205
|
+
"SUPERSTRUCTURE",
|
|
16206
|
+
"SURFACESTRUCTURE",
|
|
16207
|
+
"USERDEFINED",
|
|
16208
|
+
"NOTDEFINED"
|
|
16209
|
+
]).optional()
|
|
16210
|
+
}).superRefine(validateSpatialAxes);
|
|
16211
|
+
function parseBridgeSpec(input) {
|
|
16212
|
+
const result = BridgeSpecSchema.safeParse(input);
|
|
16213
|
+
return result.success ? (0, brepjs.ok)(result.data) : (0, brepjs.err)(specError("INVALID_BRIDGE_SPEC", result.error.message, result.error));
|
|
16214
|
+
}
|
|
16215
|
+
function parseBridgePartSpec(input) {
|
|
16216
|
+
const result = BridgePartSpecSchema.safeParse(input);
|
|
16217
|
+
return result.success ? (0, brepjs.ok)(result.data) : (0, brepjs.err)(specError("INVALID_BRIDGE_PART_SPEC", result.error.message, result.error));
|
|
16218
|
+
}
|
|
16219
|
+
//#endregion
|
|
15903
16220
|
//#region src/cobie/cobieExport.ts
|
|
15904
16221
|
/**
|
|
15905
16222
|
* Physical-element categories that become COBie Component rows. Spatial
|
|
@@ -15921,7 +16238,8 @@ var COMPONENT_CATEGORIES = /* @__PURE__ */ new Set([
|
|
|
15921
16238
|
"STAIR",
|
|
15922
16239
|
"RAMP",
|
|
15923
16240
|
"RAILING",
|
|
15924
|
-
"COVERING"
|
|
16241
|
+
"COVERING",
|
|
16242
|
+
"EARTHWORKS_FILL"
|
|
15925
16243
|
]);
|
|
15926
16244
|
/**
|
|
15927
16245
|
* Title-cased label used as the COBie Type/Component name prefix for a category,
|
|
@@ -15943,7 +16261,8 @@ var CATEGORY_LABEL = {
|
|
|
15943
16261
|
STAIR: "Stair",
|
|
15944
16262
|
RAMP: "Ramp",
|
|
15945
16263
|
RAILING: "Railing",
|
|
15946
|
-
COVERING: "Covering"
|
|
16264
|
+
COVERING: "Covering",
|
|
16265
|
+
EARTHWORKS_FILL: "EarthworksFill"
|
|
15947
16266
|
};
|
|
15948
16267
|
function categoryLabel(category) {
|
|
15949
16268
|
return CATEGORY_LABEL[category] ?? category;
|
|
@@ -27620,8 +27939,11 @@ function assignNum(target, key, value) {
|
|
|
27620
27939
|
* while the IR path serves the viewport and dedup. GlobalIds derive from
|
|
27621
27940
|
* families key paths (stable under reordering), not insertion order.
|
|
27622
27941
|
*
|
|
27623
|
-
* Scope: Storey containers
|
|
27624
|
-
*
|
|
27942
|
+
* Scope: building Storey containers; civil Site/Bridge/recursive Bridge Part
|
|
27943
|
+
* structure and Earthworks Fill bodies; Wall/Slab/Column/Beam/Roof/Stair,
|
|
27944
|
+
* Footing/Pile, Railing/Ramp, Covering/CurtainWall, and Space elements; and
|
|
27945
|
+
* wall openings — a fill-role void (Door/Window family) maps onto
|
|
27946
|
+
* addDoor/addWindow, which cut
|
|
27625
27947
|
* the wall and wire IfcRelVoidsElement + IfcRelFillsElement; the opening and
|
|
27626
27948
|
* filler GlobalIds derive from the synthesized key paths. Anonymous (non-fill)
|
|
27627
27949
|
* voids are rejected: they cut only the IR/viewport geometry, and exporting
|
|
@@ -27675,12 +27997,71 @@ var SPEC_ROUTES = {
|
|
|
27675
27997
|
stair: {
|
|
27676
27998
|
parse: parseStairSpec,
|
|
27677
27999
|
add: (m, spec, key) => m.addStair(spec, { stableKey: key }),
|
|
27678
|
-
input:
|
|
28000
|
+
input: flightsSpecInput
|
|
28001
|
+
},
|
|
28002
|
+
footing: {
|
|
28003
|
+
parse: parseFootingSpec,
|
|
28004
|
+
add: (m, spec, key) => m.addFooting(spec, { stableKey: key })
|
|
28005
|
+
},
|
|
28006
|
+
pile: {
|
|
28007
|
+
parse: parsePileSpec,
|
|
28008
|
+
add: (m, spec, key) => m.addPile(spec, { stableKey: key })
|
|
28009
|
+
},
|
|
28010
|
+
railing: {
|
|
28011
|
+
parse: parseRailingSpec,
|
|
28012
|
+
add: (m, spec, key) => m.addRailing(spec, { stableKey: key })
|
|
28013
|
+
},
|
|
28014
|
+
ramp: {
|
|
28015
|
+
parse: parseRampSpec,
|
|
28016
|
+
add: (m, spec, key) => m.addRamp(spec, { stableKey: key }),
|
|
28017
|
+
input: flightsSpecInput
|
|
28018
|
+
},
|
|
28019
|
+
covering: {
|
|
28020
|
+
parse: parseCoveringSpec,
|
|
28021
|
+
add: (m, spec, key) => m.addCovering(spec, void 0, { stableKey: key })
|
|
28022
|
+
},
|
|
28023
|
+
curtainWall: {
|
|
28024
|
+
parse: parseCurtainWallSpec,
|
|
28025
|
+
add: (m, spec, key) => m.addCurtainWall(spec, { stableKey: key })
|
|
28026
|
+
},
|
|
28027
|
+
space: {
|
|
28028
|
+
parse: parseSpaceSpec,
|
|
28029
|
+
add: (m, spec, key) => m.addSpace(spec, { stableKey: key })
|
|
28030
|
+
}
|
|
28031
|
+
};
|
|
28032
|
+
var CIVIL_PRODUCT_ROUTES = {
|
|
28033
|
+
beam: {
|
|
28034
|
+
archetype: "beam",
|
|
28035
|
+
roles: [
|
|
28036
|
+
"beam",
|
|
28037
|
+
"cross-girder",
|
|
28038
|
+
"girder"
|
|
28039
|
+
]
|
|
28040
|
+
},
|
|
28041
|
+
column: {
|
|
28042
|
+
archetype: "column",
|
|
28043
|
+
roles: ["pier-stem"]
|
|
28044
|
+
},
|
|
28045
|
+
footing: {
|
|
28046
|
+
archetype: "footing",
|
|
28047
|
+
roles: ["pad"]
|
|
28048
|
+
},
|
|
28049
|
+
railing: {
|
|
28050
|
+
archetype: "railing",
|
|
28051
|
+
roles: ["guardrail"]
|
|
28052
|
+
},
|
|
28053
|
+
slab: {
|
|
28054
|
+
archetype: "slab",
|
|
28055
|
+
roles: ["deck"]
|
|
28056
|
+
},
|
|
28057
|
+
wall: {
|
|
28058
|
+
archetype: "wall",
|
|
28059
|
+
roles: ["wall"]
|
|
27679
28060
|
}
|
|
27680
28061
|
};
|
|
27681
|
-
/**
|
|
27682
|
-
* element's folded translate lands on every flight's origin instead. */
|
|
27683
|
-
function
|
|
28062
|
+
/** Stair and ramp specs have no top-level origin — placement lives per flight —
|
|
28063
|
+
* so the element's folded translate lands on every flight's origin instead. */
|
|
28064
|
+
function flightsSpecInput(el) {
|
|
27684
28065
|
const base = specInput(el);
|
|
27685
28066
|
const fold = base["origin"] ?? [
|
|
27686
28067
|
0,
|
|
@@ -27722,14 +28103,145 @@ var NAME_ARCHETYPES = {
|
|
|
27722
28103
|
Roof: "roof",
|
|
27723
28104
|
Stair: "stair",
|
|
27724
28105
|
Door: "door",
|
|
27725
|
-
Window: "window"
|
|
28106
|
+
Window: "window",
|
|
28107
|
+
Footing: "footing",
|
|
28108
|
+
Pile: "pile",
|
|
28109
|
+
Railing: "railing",
|
|
28110
|
+
Ramp: "ramp",
|
|
28111
|
+
Covering: "covering",
|
|
28112
|
+
CurtainWall: "curtainWall",
|
|
28113
|
+
Space: "space"
|
|
27726
28114
|
};
|
|
27727
28115
|
function archetypeFor(el) {
|
|
27728
|
-
return el.archetype ?? NAME_ARCHETYPES
|
|
28116
|
+
return el.archetype ?? lookup(NAME_ARCHETYPES, el.type);
|
|
27729
28117
|
}
|
|
27730
28118
|
function specRoute(archetype) {
|
|
27731
28119
|
return archetype !== void 0 && Object.hasOwn(SPEC_ROUTES, archetype) ? SPEC_ROUTES[archetype] : void 0;
|
|
27732
28120
|
}
|
|
28121
|
+
/** Own-property lookup: semantics categories and roles are free-form author
|
|
28122
|
+
* strings, so Object.prototype keys must never resolve to a route. */
|
|
28123
|
+
function lookup(map, key) {
|
|
28124
|
+
return Object.hasOwn(map, key) ? map[key] : void 0;
|
|
28125
|
+
}
|
|
28126
|
+
function civilProductArchetype(el) {
|
|
28127
|
+
if (el.semantics?.kind !== "product") return void 0;
|
|
28128
|
+
const definition = lookup(CIVIL_PRODUCT_ROUTES, el.semantics.category);
|
|
28129
|
+
if (definition === void 0 || !definition.roles.includes(el.semantics.role)) return void 0;
|
|
28130
|
+
return definition.archetype;
|
|
28131
|
+
}
|
|
28132
|
+
function semanticDimension(el, ...names) {
|
|
28133
|
+
if (el.semantics?.kind !== "product") return void 0;
|
|
28134
|
+
for (const name of names) {
|
|
28135
|
+
const value = el.semantics.dimensionsMm[name];
|
|
28136
|
+
if (value !== void 0) return value;
|
|
28137
|
+
}
|
|
28138
|
+
}
|
|
28139
|
+
/** Beam/column solids centre their cross-section on the placement axis, while
|
|
28140
|
+
* the semantics envelope sits corner-anchored at the folded IR origin — shift
|
|
28141
|
+
* the origin to the envelope centre so the exported body occupies the same
|
|
28142
|
+
* space as the viewport body. */
|
|
28143
|
+
function centreOrigin(base, shift) {
|
|
28144
|
+
const origin = base["origin"] ?? [
|
|
28145
|
+
0,
|
|
28146
|
+
0,
|
|
28147
|
+
0
|
|
28148
|
+
];
|
|
28149
|
+
return [
|
|
28150
|
+
origin[0] + shift[0],
|
|
28151
|
+
origin[1] + shift[1],
|
|
28152
|
+
origin[2] + shift[2]
|
|
28153
|
+
];
|
|
28154
|
+
}
|
|
28155
|
+
/**
|
|
28156
|
+
* Adapts target-independent civil envelope dimensions onto existing typed BIM
|
|
28157
|
+
* spec inputs. Reference infrastructure Families intentionally author domain
|
|
28158
|
+
* props such as `depth`, `capOffset`, and compound railing profiles rather than
|
|
28159
|
+
* BIM `profile`/`thickness` fields; semantics is the stable projection seam.
|
|
28160
|
+
*/
|
|
28161
|
+
function civilProductSpecInput(el) {
|
|
28162
|
+
const base = specInput(el);
|
|
28163
|
+
if (el.semantics?.kind !== "product") return base;
|
|
28164
|
+
const length = semanticDimension(el, "length");
|
|
28165
|
+
const width = semanticDimension(el, "width");
|
|
28166
|
+
const height = semanticDimension(el, "height");
|
|
28167
|
+
switch (el.semantics.category) {
|
|
28168
|
+
case "beam": {
|
|
28169
|
+
const synthesized = base["profile"] === void 0 && width !== void 0 && height !== void 0 ? {
|
|
28170
|
+
profile: {
|
|
28171
|
+
kind: "RECTANGULAR",
|
|
28172
|
+
width,
|
|
28173
|
+
height
|
|
28174
|
+
},
|
|
28175
|
+
shift: [
|
|
28176
|
+
0,
|
|
28177
|
+
width / 2,
|
|
28178
|
+
height / 2
|
|
28179
|
+
]
|
|
28180
|
+
} : void 0;
|
|
28181
|
+
return {
|
|
28182
|
+
...base,
|
|
28183
|
+
...synthesized !== void 0 ? {
|
|
28184
|
+
origin: centreOrigin(base, synthesized.shift),
|
|
28185
|
+
profile: synthesized.profile
|
|
28186
|
+
} : {},
|
|
28187
|
+
length: length ?? base["length"],
|
|
28188
|
+
predefinedType: base["predefinedType"] ?? "BEAM"
|
|
28189
|
+
};
|
|
28190
|
+
}
|
|
28191
|
+
case "column": {
|
|
28192
|
+
const synthesized = base["profile"] === void 0 && length !== void 0 && width !== void 0 ? {
|
|
28193
|
+
profile: {
|
|
28194
|
+
kind: "RECTANGULAR",
|
|
28195
|
+
width: length,
|
|
28196
|
+
height: width
|
|
28197
|
+
},
|
|
28198
|
+
shift: [
|
|
28199
|
+
length / 2,
|
|
28200
|
+
width / 2,
|
|
28201
|
+
0
|
|
28202
|
+
]
|
|
28203
|
+
} : void 0;
|
|
28204
|
+
return {
|
|
28205
|
+
...base,
|
|
28206
|
+
...synthesized !== void 0 ? {
|
|
28207
|
+
origin: centreOrigin(base, synthesized.shift),
|
|
28208
|
+
profile: synthesized.profile
|
|
28209
|
+
} : {},
|
|
28210
|
+
height: height ?? base["height"],
|
|
28211
|
+
predefinedType: base["predefinedType"] ?? "COLUMN"
|
|
28212
|
+
};
|
|
28213
|
+
}
|
|
28214
|
+
case "footing": return {
|
|
28215
|
+
...base,
|
|
28216
|
+
length: length ?? base["length"],
|
|
28217
|
+
width: width ?? base["width"],
|
|
28218
|
+
thickness: height ?? base["thickness"],
|
|
28219
|
+
predefinedType: base["predefinedType"] ?? "PAD_FOOTING"
|
|
28220
|
+
};
|
|
28221
|
+
case "railing": return {
|
|
28222
|
+
...base,
|
|
28223
|
+
length: length ?? base["length"],
|
|
28224
|
+
thickness: width ?? base["thickness"],
|
|
28225
|
+
height: height ?? base["height"],
|
|
28226
|
+
predefinedType: base["predefinedType"] ?? "GUARDRAIL"
|
|
28227
|
+
};
|
|
28228
|
+
case "slab": return {
|
|
28229
|
+
...base,
|
|
28230
|
+
length: length ?? base["length"],
|
|
28231
|
+
width: width ?? base["width"],
|
|
28232
|
+
thickness: height ?? base["thickness"],
|
|
28233
|
+
predefinedType: base["predefinedType"] ?? "FLOOR"
|
|
28234
|
+
};
|
|
28235
|
+
case "wall": return {
|
|
28236
|
+
...base,
|
|
28237
|
+
length: length ?? base["length"],
|
|
28238
|
+
thickness: width ?? base["thickness"],
|
|
28239
|
+
height: height ?? base["height"],
|
|
28240
|
+
predefinedType: base["predefinedType"] ?? "NOTDEFINED"
|
|
28241
|
+
};
|
|
28242
|
+
default: return base;
|
|
28243
|
+
}
|
|
28244
|
+
}
|
|
27733
28245
|
/** Total of the resolved geometry's OUTER literal translate chain. The
|
|
27734
28246
|
* transform vocabulary is translate-only, so frame differences are exact
|
|
27735
28247
|
* subtractions. Parameter-driven translations stop the peel. */
|
|
@@ -27755,15 +28267,14 @@ function peelTranslates(node) {
|
|
|
27755
28267
|
moved
|
|
27756
28268
|
};
|
|
27757
28269
|
}
|
|
27758
|
-
/** True when the element's transform
|
|
28270
|
+
/** True when the element's rendered local transform carries a rotation. The spec path
|
|
27759
28271
|
* folds only translations into IfcLocalPlacement (walls orient via `axisX`),
|
|
27760
28272
|
* so a tRotate placement would export un-rotated while the viewport shows it
|
|
27761
28273
|
* rotated — reject instead of diverging. Rotations a family render bakes
|
|
27762
28274
|
* into its own body geometry (e.g. a circular beam oriented along axisX) are
|
|
27763
28275
|
* fine: the spec rebuilds that body parametrically from props. */
|
|
27764
28276
|
function hasRotateOp(el) {
|
|
27765
|
-
|
|
27766
|
-
return Array.isArray(ops) && ops.some((op) => typeof op === "object" && op !== null && op.op === "rotate");
|
|
28277
|
+
return el.localTransforms.some((op) => op.op === "rotate");
|
|
27767
28278
|
}
|
|
27768
28279
|
/** Fold the resolved geometry's outer translate chain into the spec placement
|
|
27769
28280
|
* origin, so IfcLocalPlacement matches the IR world frame no matter where the
|
|
@@ -27809,10 +28320,14 @@ var COMMON_PSET_FIELDS = {
|
|
|
27809
28320
|
function collectSpecProps(el) {
|
|
27810
28321
|
const out = {};
|
|
27811
28322
|
const material = el.attributes["material"];
|
|
27812
|
-
|
|
28323
|
+
const semanticsMaterial = el.semantics?.kind === "product" ? el.semantics.material : void 0;
|
|
28324
|
+
if (el.props["materialName"] === void 0) {
|
|
28325
|
+
if (typeof material === "string") out["materialName"] = material;
|
|
28326
|
+
else if (semanticsMaterial !== void 0) out["materialName"] = semanticsMaterial;
|
|
28327
|
+
}
|
|
27813
28328
|
const psets = el.attributes["psets"];
|
|
28329
|
+
const custom = {};
|
|
27814
28330
|
if (psets && typeof psets === "object") {
|
|
27815
|
-
const custom = {};
|
|
27816
28331
|
const ownCommonPset = `Pset_${el.type}Common`;
|
|
27817
28332
|
for (const [psetName, fields] of Object.entries(psets)) {
|
|
27818
28333
|
if (!fields || typeof fields !== "object") continue;
|
|
@@ -27825,14 +28340,15 @@ function collectSpecProps(el) {
|
|
|
27825
28340
|
if (Object.keys(values).length > 0) custom[psetName] = values;
|
|
27826
28341
|
}
|
|
27827
28342
|
}
|
|
27828
|
-
if (Object.keys(custom).length > 0) {
|
|
27829
|
-
const declared = el.props["customProperties"];
|
|
27830
|
-
out["customProperties"] = declared && typeof declared === "object" ? {
|
|
27831
|
-
...custom,
|
|
27832
|
-
...declared
|
|
27833
|
-
} : custom;
|
|
27834
|
-
}
|
|
27835
28343
|
}
|
|
28344
|
+
const declared = el.props["customProperties"];
|
|
28345
|
+
if (declared && typeof declared === "object" && !Array.isArray(declared)) for (const [psetName, fields] of Object.entries(declared)) {
|
|
28346
|
+
if (!fields || typeof fields !== "object" || Array.isArray(fields)) continue;
|
|
28347
|
+
const values = {};
|
|
28348
|
+
for (const [field, value] of Object.entries(fields)) if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") values[field] = value;
|
|
28349
|
+
if (Object.keys(values).length > 0) custom[psetName] = values;
|
|
28350
|
+
}
|
|
28351
|
+
if (Object.keys(custom).length > 0) out["customProperties"] = custom;
|
|
27836
28352
|
return out;
|
|
27837
28353
|
}
|
|
27838
28354
|
function addFill(model, fill, input, identity) {
|
|
@@ -27849,35 +28365,49 @@ function addFill(model, fill, input, identity) {
|
|
|
27849
28365
|
}
|
|
27850
28366
|
return (0, brepjs.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'`));
|
|
27851
28367
|
}
|
|
27852
|
-
|
|
27853
|
-
* authoritative for a proxy (no parametric spec to diverge from), so baked
|
|
27854
|
-
* transforms — rotations included — are fine here. */
|
|
27855
|
-
function addProxyElement(model, el, evaluator) {
|
|
28368
|
+
function materializeOwnedSolid(el, evaluator, errors) {
|
|
27856
28369
|
const evaluated = evaluator.evaluate(el.geometry);
|
|
27857
|
-
if (!evaluated.ok) return (0, brepjs.err)(specError(
|
|
28370
|
+
if (!evaluated.ok) return (0, brepjs.err)(specError(errors.evalCode, `familiesToBim: '${el.keyPath}' failed to materialize for the ${errors.routeName} route: ${evaluated.error.message}`, evaluated.error));
|
|
27858
28371
|
let source = evaluated.value;
|
|
27859
28372
|
if (!(0, brepjs.isSolid)(source)) {
|
|
27860
28373
|
const solids = (0, brepjs.getSolids)(source);
|
|
27861
28374
|
const only = solids.length === 1 ? solids[0] : void 0;
|
|
27862
|
-
if (only === void 0) return (0, brepjs.err)(specError(
|
|
28375
|
+
if (only === void 0) return (0, brepjs.err)(specError(errors.notSolidCode, `familiesToBim: '${el.keyPath}' materialized to ${solids.length} solids — the ${errors.routeName} body must be exactly one`));
|
|
27863
28376
|
source = only;
|
|
27864
28377
|
}
|
|
27865
28378
|
const copy = (0, brepjs.clone)(source);
|
|
27866
|
-
if (!copy.ok) return (0, brepjs.err)(specError(
|
|
28379
|
+
if (!copy.ok) return (0, brepjs.err)(specError(errors.evalCode, `familiesToBim: '${el.keyPath}' could not copy the materialized body`, copy.error));
|
|
27867
28380
|
const valid = (0, brepjs.validSolid)(copy.value);
|
|
27868
28381
|
if (!valid.ok) {
|
|
27869
28382
|
copy.value[Symbol.dispose]();
|
|
27870
|
-
return (0, brepjs.err)(specError(
|
|
28383
|
+
return (0, brepjs.err)(specError(errors.invalidCode, `familiesToBim: '${el.keyPath}' materialized to an invalid solid: ${valid.error}`));
|
|
27871
28384
|
}
|
|
28385
|
+
return (0, brepjs.ok)(valid.value);
|
|
28386
|
+
}
|
|
28387
|
+
/** Materialize an unrouted element's IR and add it as a proxy. The body is
|
|
28388
|
+
* authoritative for a proxy (no parametric spec to diverge from), so baked
|
|
28389
|
+
* transforms — rotations included — are fine here. */
|
|
28390
|
+
function addProxyElement(model, el, evaluator, projectedSpatialTranslation) {
|
|
28391
|
+
const body = materializeOwnedSolid(el, evaluator, {
|
|
28392
|
+
evalCode: "FAMILIES_PROXY_EVAL_FAILED",
|
|
28393
|
+
notSolidCode: "FAMILIES_PROXY_NOT_SOLID",
|
|
28394
|
+
invalidCode: "FAMILIES_PROXY_INVALID",
|
|
28395
|
+
routeName: "proxy"
|
|
28396
|
+
});
|
|
28397
|
+
if (!body.ok) return body;
|
|
28398
|
+
const localized = localizeOwnedSolid(el, body.value, projectedSpatialTranslation, "FAMILIES_PROXY_LOCALIZE_FAILED", "spatial parent");
|
|
28399
|
+
if (!localized.ok) return localized;
|
|
27872
28400
|
const nameAttr = el.attributes["name"];
|
|
27873
28401
|
const materialProp = el.props["materialName"];
|
|
27874
28402
|
const specProps = collectSpecProps(el);
|
|
27875
|
-
|
|
28403
|
+
const added = model.addProxy({
|
|
27876
28404
|
name: typeof nameAttr === "string" ? nameAttr : el.type,
|
|
27877
|
-
solid:
|
|
28405
|
+
solid: localized.value,
|
|
27878
28406
|
materialName: typeof materialProp === "string" ? materialProp : specProps["materialName"],
|
|
27879
28407
|
customProperties: specProps["customProperties"]
|
|
27880
28408
|
}, { stableKey: el.keyPath });
|
|
28409
|
+
if (!added.ok) localized.value[Symbol.dispose]();
|
|
28410
|
+
return added;
|
|
27881
28411
|
}
|
|
27882
28412
|
/** Map a wall's synthesized Opening children onto addDoor/addWindow. The
|
|
27883
28413
|
* wall-relative offsets come from the void geometry's frame minus the host's:
|
|
@@ -27927,38 +28457,296 @@ function requireKeyed(el) {
|
|
|
27927
28457
|
if (el.keyed) return (0, brepjs.ok)(void 0);
|
|
27928
28458
|
return (0, brepjs.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)`));
|
|
27929
28459
|
}
|
|
28460
|
+
var BRIDGE_ROLE = {
|
|
28461
|
+
arched: "ARCHED",
|
|
28462
|
+
"cable-stayed": "CABLE_STAYED",
|
|
28463
|
+
cantilever: "CANTILEVER",
|
|
28464
|
+
culvert: "CULVERT",
|
|
28465
|
+
framework: "FRAMEWORK",
|
|
28466
|
+
girder: "GIRDER",
|
|
28467
|
+
suspension: "SUSPENSION",
|
|
28468
|
+
truss: "TRUSS"
|
|
28469
|
+
};
|
|
28470
|
+
var BRIDGE_PART_ROLE = {
|
|
28471
|
+
abutment: "ABUTMENT",
|
|
28472
|
+
deck: "DECK",
|
|
28473
|
+
"deck-segment": "DECK_SEGMENT",
|
|
28474
|
+
foundation: "FOUNDATION",
|
|
28475
|
+
pier: "PIER",
|
|
28476
|
+
"pier-segment": "PIER_SEGMENT",
|
|
28477
|
+
pylon: "PYLON",
|
|
28478
|
+
substructure: "SUBSTRUCTURE",
|
|
28479
|
+
superstructure: "SUPERSTRUCTURE",
|
|
28480
|
+
"surface-structure": "SURFACESTRUCTURE"
|
|
28481
|
+
};
|
|
28482
|
+
var CIVIL_COMPOSITION = {
|
|
28483
|
+
collection: "COMPLEX",
|
|
28484
|
+
element: "ELEMENT",
|
|
28485
|
+
partial: "PARTIAL"
|
|
28486
|
+
};
|
|
28487
|
+
var CIVIL_USAGE = {
|
|
28488
|
+
lateral: "LATERAL",
|
|
28489
|
+
longitudinal: "LONGITUDINAL",
|
|
28490
|
+
regional: "REGION",
|
|
28491
|
+
vertical: "VERTICAL"
|
|
28492
|
+
};
|
|
28493
|
+
var EARTHWORKS_FILL_ROLE = {
|
|
28494
|
+
backfill: "BACKFILL",
|
|
28495
|
+
counterweight: "COUNTERWEIGHT",
|
|
28496
|
+
embankment: "EMBANKMENT",
|
|
28497
|
+
"slope-fill": "SLOPEFILL",
|
|
28498
|
+
subgrade: "SUBGRADE",
|
|
28499
|
+
"subgrade-bed": "SUBGRADEBED",
|
|
28500
|
+
"transition-section": "TRANSITIONSECTION"
|
|
28501
|
+
};
|
|
28502
|
+
function unsupportedCivilRole(el, entity) {
|
|
28503
|
+
return (0, brepjs.err)(specError("FAMILIES_UNSUPPORTED_CIVIL_SEMANTICS", `familiesToBim: unsupported ${entity} role '${el.semantics?.role ?? ""}' at '${el.keyPath}'`));
|
|
28504
|
+
}
|
|
28505
|
+
function civilSpatialKind(el) {
|
|
28506
|
+
const semantics = el.semantics;
|
|
28507
|
+
if (semantics?.kind === "site" && semantics.category === "site") return "site";
|
|
28508
|
+
if (semantics?.kind === "facility" && semantics.category === "bridge") return "bridge";
|
|
28509
|
+
if (semantics?.kind === "spatial-part" && semantics.category === "bridge-part") return "bridge-part";
|
|
28510
|
+
}
|
|
28511
|
+
function isCivilSpatialIntent(el) {
|
|
28512
|
+
return el.semantics?.kind === "site" || el.semantics?.kind === "facility" || el.semantics?.kind === "spatial-part";
|
|
28513
|
+
}
|
|
28514
|
+
function hasCivilSpatialIntent(el) {
|
|
28515
|
+
return isCivilSpatialIntent(el) || el.children.some(hasCivilSpatialIntent);
|
|
28516
|
+
}
|
|
28517
|
+
function isEarthworksFillOccurrence(el) {
|
|
28518
|
+
return el.semantics?.kind === "product" && el.semantics.category === "earthworks-fill";
|
|
28519
|
+
}
|
|
28520
|
+
function semanticName(el) {
|
|
28521
|
+
const name = el.attributes["name"];
|
|
28522
|
+
if (typeof name === "string" && name.trim().length > 0) return name;
|
|
28523
|
+
const semanticNameValue = el.semantics?.properties?.["name"];
|
|
28524
|
+
return typeof semanticNameValue === "string" && semanticNameValue.trim().length > 0 ? semanticNameValue : el.keyPath;
|
|
28525
|
+
}
|
|
28526
|
+
var ZERO_TRANSLATION = [
|
|
28527
|
+
0,
|
|
28528
|
+
0,
|
|
28529
|
+
0
|
|
28530
|
+
];
|
|
28531
|
+
function addTranslation(a, b) {
|
|
28532
|
+
return [
|
|
28533
|
+
a[0] + b[0],
|
|
28534
|
+
a[1] + b[1],
|
|
28535
|
+
a[2] + b[2]
|
|
28536
|
+
];
|
|
28537
|
+
}
|
|
28538
|
+
function subtractTranslation(a, b) {
|
|
28539
|
+
return [
|
|
28540
|
+
a[0] - b[0],
|
|
28541
|
+
a[1] - b[1],
|
|
28542
|
+
a[2] - b[2]
|
|
28543
|
+
];
|
|
28544
|
+
}
|
|
28545
|
+
function hasTranslation(value) {
|
|
28546
|
+
return value[0] !== 0 || value[1] !== 0 || value[2] !== 0;
|
|
28547
|
+
}
|
|
28548
|
+
var DEFAULT_AXIS_X = [
|
|
28549
|
+
1,
|
|
28550
|
+
0,
|
|
28551
|
+
0
|
|
28552
|
+
];
|
|
28553
|
+
var DEFAULT_AXIS_Z = [
|
|
28554
|
+
0,
|
|
28555
|
+
0,
|
|
28556
|
+
1
|
|
28557
|
+
];
|
|
28558
|
+
function axisEquals(value, axis) {
|
|
28559
|
+
return Array.isArray(value) && value.length === 3 && value.every((component, i) => typeof component === "number" && component === axis[i]);
|
|
28560
|
+
}
|
|
28561
|
+
/** Civil descendant relativization is translation-only, so a civil frame with
|
|
28562
|
+
* non-default axes would rotate the exported hierarchy away from the IR. */
|
|
28563
|
+
function hasRotatedAxes(el) {
|
|
28564
|
+
const axisX = el.props["axisX"];
|
|
28565
|
+
const axisZ = el.props["axisZ"];
|
|
28566
|
+
return axisX !== void 0 && !axisEquals(axisX, DEFAULT_AXIS_X) || axisZ !== void 0 && !axisEquals(axisZ, DEFAULT_AXIS_Z);
|
|
28567
|
+
}
|
|
28568
|
+
function authoredSpecOrigin(el) {
|
|
28569
|
+
const origin = el.props["origin"];
|
|
28570
|
+
return Array.isArray(origin) && origin.length === 3 && origin.every((c) => typeof c === "number") ? origin : ZERO_TRANSLATION;
|
|
28571
|
+
}
|
|
28572
|
+
function localizeOwnedSolid(el, body, projectedSpatialTranslation, errorCode, frameName) {
|
|
28573
|
+
if (!hasTranslation(projectedSpatialTranslation)) return (0, brepjs.ok)(body);
|
|
28574
|
+
try {
|
|
28575
|
+
const localized = (0, brepjs.translate)(body, [
|
|
28576
|
+
-projectedSpatialTranslation[0],
|
|
28577
|
+
-projectedSpatialTranslation[1],
|
|
28578
|
+
-projectedSpatialTranslation[2]
|
|
28579
|
+
]);
|
|
28580
|
+
body[Symbol.dispose]();
|
|
28581
|
+
return (0, brepjs.ok)(localized);
|
|
28582
|
+
} catch (cause) {
|
|
28583
|
+
body[Symbol.dispose]();
|
|
28584
|
+
return (0, brepjs.err)(specError(errorCode, `familiesToBim: '${el.keyPath}' could not move its body into the ${frameName} frame`, cause));
|
|
28585
|
+
}
|
|
28586
|
+
}
|
|
28587
|
+
function authoredTranslation(el) {
|
|
28588
|
+
const total = [
|
|
28589
|
+
0,
|
|
28590
|
+
0,
|
|
28591
|
+
0
|
|
28592
|
+
];
|
|
28593
|
+
for (const op of el.localTransforms) {
|
|
28594
|
+
if (op.op !== "translate") continue;
|
|
28595
|
+
total[0] += op.v[0];
|
|
28596
|
+
total[1] += op.v[1];
|
|
28597
|
+
total[2] += op.v[2];
|
|
28598
|
+
}
|
|
28599
|
+
return total;
|
|
28600
|
+
}
|
|
28601
|
+
function civilSpatialInput(el, localTranslation) {
|
|
28602
|
+
const semantics = el.semantics;
|
|
28603
|
+
const composition = semantics !== void 0 && "composition" in semantics ? CIVIL_COMPOSITION[semantics.composition] : void 0;
|
|
28604
|
+
const origin = addTranslation(el.props["origin"] ?? ZERO_TRANSLATION, localTranslation);
|
|
28605
|
+
return {
|
|
28606
|
+
name: semanticName(el),
|
|
28607
|
+
...el.props["origin"] !== void 0 || hasTranslation(localTranslation) ? { origin } : {},
|
|
28608
|
+
...el.props["axisX"] !== void 0 ? { axisX: el.props["axisX"] } : {},
|
|
28609
|
+
...el.props["axisZ"] !== void 0 ? { axisZ: el.props["axisZ"] } : {},
|
|
28610
|
+
...composition !== void 0 ? { compositionType: composition } : {}
|
|
28611
|
+
};
|
|
28612
|
+
}
|
|
28613
|
+
function civilParentAccepts(kind, parent) {
|
|
28614
|
+
if (kind === "site") return parent === "project";
|
|
28615
|
+
if (kind === "bridge") return parent === "site";
|
|
28616
|
+
return parent === "bridge" || parent === "bridge-part";
|
|
28617
|
+
}
|
|
28618
|
+
function addCivilSpatialOccurrence(model, el, kind, localTranslation) {
|
|
28619
|
+
if (kind === "site") {
|
|
28620
|
+
if (el.semantics?.role !== "transport-site") return unsupportedCivilRole(el, "Site");
|
|
28621
|
+
const parsed = parseSiteSpec(civilSpatialInput(el, localTranslation));
|
|
28622
|
+
return parsed.ok ? model.addSite(parsed.value, { stableKey: el.keyPath }) : parsed;
|
|
28623
|
+
}
|
|
28624
|
+
if (kind === "bridge") {
|
|
28625
|
+
const predefinedType = lookup(BRIDGE_ROLE, el.semantics?.role ?? "");
|
|
28626
|
+
if (predefinedType === void 0) return unsupportedCivilRole(el, "Bridge");
|
|
28627
|
+
const parsed = parseBridgeSpec({
|
|
28628
|
+
...civilSpatialInput(el, localTranslation),
|
|
28629
|
+
predefinedType
|
|
28630
|
+
});
|
|
28631
|
+
return parsed.ok ? model.addBridge(parsed.value, { stableKey: el.keyPath }) : parsed;
|
|
28632
|
+
}
|
|
28633
|
+
const subdivision = el.semantics?.kind === "spatial-part" ? el.semantics.subdivision : void 0;
|
|
28634
|
+
const predefinedType = lookup(BRIDGE_PART_ROLE, el.semantics?.role ?? "");
|
|
28635
|
+
if (predefinedType === void 0) return unsupportedCivilRole(el, "Bridge Part");
|
|
28636
|
+
const parsed = parseBridgePartSpec({
|
|
28637
|
+
...civilSpatialInput(el, localTranslation),
|
|
28638
|
+
predefinedType,
|
|
28639
|
+
usageType: subdivision !== void 0 ? CIVIL_USAGE[subdivision] : "NOTDEFINED"
|
|
28640
|
+
});
|
|
28641
|
+
return parsed.ok ? model.addBridgePart(parsed.value, { stableKey: el.keyPath }) : parsed;
|
|
28642
|
+
}
|
|
28643
|
+
function relativeSpecInput(input, projectedSpatialTranslation) {
|
|
28644
|
+
const relativeOrigin = (origin) => Array.isArray(origin) && origin.length === 3 && origin.every((value) => typeof value === "number") ? subtractTranslation(origin, projectedSpatialTranslation) : origin;
|
|
28645
|
+
if (Array.isArray(input["flights"])) {
|
|
28646
|
+
const flights = input["flights"];
|
|
28647
|
+
return {
|
|
28648
|
+
...input,
|
|
28649
|
+
flights: flights.map((flight) => {
|
|
28650
|
+
if (typeof flight !== "object" || flight === null) return flight;
|
|
28651
|
+
const flightInput = flight;
|
|
28652
|
+
return {
|
|
28653
|
+
...flightInput,
|
|
28654
|
+
origin: relativeOrigin(flightInput["origin"])
|
|
28655
|
+
};
|
|
28656
|
+
})
|
|
28657
|
+
};
|
|
28658
|
+
}
|
|
28659
|
+
return {
|
|
28660
|
+
...input,
|
|
28661
|
+
origin: relativeOrigin(input["origin"])
|
|
28662
|
+
};
|
|
28663
|
+
}
|
|
28664
|
+
function addEarthworksFillElement(model, el, evaluator, projectedSpatialTranslation) {
|
|
28665
|
+
if (el.semantics?.kind !== "product") return (0, brepjs.err)(specError("FAMILIES_UNSUPPORTED_CIVIL_SEMANTICS", `familiesToBim: '${el.keyPath}' is not authored as a civil Product`));
|
|
28666
|
+
const predefinedType = lookup(EARTHWORKS_FILL_ROLE, el.semantics.role);
|
|
28667
|
+
if (predefinedType === void 0) return unsupportedCivilRole(el, "Earthworks Fill");
|
|
28668
|
+
const body = materializeOwnedSolid(el, evaluator, {
|
|
28669
|
+
evalCode: "FAMILIES_EARTHWORKS_EVAL_FAILED",
|
|
28670
|
+
notSolidCode: "FAMILIES_EARTHWORKS_NOT_SOLID",
|
|
28671
|
+
invalidCode: "FAMILIES_EARTHWORKS_INVALID",
|
|
28672
|
+
routeName: "Earthworks Fill"
|
|
28673
|
+
});
|
|
28674
|
+
if (!body.ok) return body;
|
|
28675
|
+
const localized = localizeOwnedSolid(el, body.value, projectedSpatialTranslation, "FAMILIES_EARTHWORKS_LOCALIZE_FAILED", "Bridge Part");
|
|
28676
|
+
if (!localized.ok) return localized;
|
|
28677
|
+
const specProps = collectSpecProps(el);
|
|
28678
|
+
const authoredMaterial = el.props["materialName"];
|
|
28679
|
+
const added = model.addEarthworksFill({
|
|
28680
|
+
name: semanticName(el),
|
|
28681
|
+
solid: localized.value,
|
|
28682
|
+
materialName: typeof authoredMaterial === "string" ? authoredMaterial : specProps["materialName"] ?? el.semantics.material,
|
|
28683
|
+
predefinedType,
|
|
28684
|
+
customProperties: specProps["customProperties"]
|
|
28685
|
+
}, { stableKey: el.keyPath });
|
|
28686
|
+
if (!added.ok) localized.value[Symbol.dispose]();
|
|
28687
|
+
return added;
|
|
28688
|
+
}
|
|
27930
28689
|
/**
|
|
27931
28690
|
* Project a resolved families tree into an eager BimModel. The caller owns
|
|
27932
28691
|
* the returned model (`using`); families stays domain-neutral — this adapter
|
|
27933
28692
|
* is where families types meet the IFC vocabulary.
|
|
27934
28693
|
*/
|
|
27935
28694
|
function familiesToBim(root, options) {
|
|
28695
|
+
const usesAuthoredCivilHierarchy = hasCivilSpatialIntent(root);
|
|
28696
|
+
if (usesAuthoredCivilHierarchy) {
|
|
28697
|
+
const keyed = requireKeyed(root);
|
|
28698
|
+
if (!keyed.ok) return keyed;
|
|
28699
|
+
}
|
|
27936
28700
|
const model = new BimModel();
|
|
27937
|
-
const initResult = model.init(options.project);
|
|
28701
|
+
const initResult = model.init(options.project, usesAuthoredCivilHierarchy ? { stableKey: civilSpatialKind(root) !== void 0 ? `${root.keyPath}#project` : root.keyPath } : void 0);
|
|
27938
28702
|
if (!initResult.ok) return initResult;
|
|
27939
|
-
const
|
|
27940
|
-
|
|
27941
|
-
|
|
27942
|
-
|
|
27943
|
-
|
|
27944
|
-
|
|
27945
|
-
|
|
27946
|
-
|
|
27947
|
-
|
|
27948
|
-
|
|
27949
|
-
|
|
27950
|
-
|
|
27951
|
-
|
|
27952
|
-
|
|
27953
|
-
|
|
28703
|
+
const projectId = initResult.value;
|
|
28704
|
+
let buildingId = null;
|
|
28705
|
+
if (!usesAuthoredCivilHierarchy) {
|
|
28706
|
+
const siteResult = model.addSite({ name: options.siteName ?? "Site" });
|
|
28707
|
+
if (!siteResult.ok) {
|
|
28708
|
+
model[Symbol.dispose]();
|
|
28709
|
+
return siteResult;
|
|
28710
|
+
}
|
|
28711
|
+
const buildingResult = model.addBuilding({ name: options.buildingName ?? "Building" });
|
|
28712
|
+
if (!buildingResult.ok) {
|
|
28713
|
+
model[Symbol.dispose]();
|
|
28714
|
+
return buildingResult;
|
|
28715
|
+
}
|
|
28716
|
+
buildingId = buildingResult.value;
|
|
28717
|
+
model.aggregate(projectId, siteResult.value);
|
|
28718
|
+
model.aggregate(siteResult.value, buildingId);
|
|
28719
|
+
}
|
|
28720
|
+
const idByKeyPath = new Map(usesAuthoredCivilHierarchy ? [[root.keyPath, projectId]] : []);
|
|
27954
28721
|
const proxied = [];
|
|
27955
|
-
const walk = (el,
|
|
27956
|
-
const rotatedHere = rotated || hasRotateOp(el);
|
|
28722
|
+
const walk = (el, state) => {
|
|
28723
|
+
const rotatedHere = state.rotated || hasRotateOp(el);
|
|
28724
|
+
const cumulativeTranslationHere = addTranslation(state.cumulativeTranslation, authoredTranslation(el));
|
|
27957
28725
|
let proxiedHere = false;
|
|
27958
|
-
let
|
|
28726
|
+
let nextSpatialStructureId = state.spatialStructureId;
|
|
28727
|
+
let nextCivilParent = state.civilParent;
|
|
28728
|
+
let nextProjectedSpatialTranslation = state.projectedSpatialTranslation;
|
|
27959
28729
|
const archetype = archetypeFor(el);
|
|
27960
|
-
const
|
|
27961
|
-
|
|
28730
|
+
const effectiveArchetype = el.semantics?.kind === "product" ? civilProductArchetype(el) : archetype;
|
|
28731
|
+
const route = specRoute(effectiveArchetype);
|
|
28732
|
+
const civilKind = civilSpatialKind(el);
|
|
28733
|
+
if (civilKind !== void 0) {
|
|
28734
|
+
if (!usesAuthoredCivilHierarchy || !civilParentAccepts(civilKind, state.civilParent) || state.spatialStructureId === null) return (0, brepjs.err)(specError("FAMILIES_INVALID_CIVIL_HIERARCHY", `familiesToBim: civil '${civilKind}' at '${el.keyPath}' cannot occur under '${state.civilParent}'`));
|
|
28735
|
+
const keyed = requireKeyed(el);
|
|
28736
|
+
if (!keyed.ok) return keyed;
|
|
28737
|
+
if (rotatedHere || hasRotatedAxes(el)) return (0, brepjs.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`));
|
|
28738
|
+
if (el.geometry.kind !== "Empty") return (0, brepjs.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)`));
|
|
28739
|
+
const localTranslation = subtractTranslation(cumulativeTranslationHere, state.projectedSpatialTranslation);
|
|
28740
|
+
const added = addCivilSpatialOccurrence(model, el, civilKind, localTranslation);
|
|
28741
|
+
if (!added.ok) return added;
|
|
28742
|
+
model.aggregate(state.spatialStructureId, added.value);
|
|
28743
|
+
idByKeyPath.set(el.keyPath, added.value);
|
|
28744
|
+
nextSpatialStructureId = added.value;
|
|
28745
|
+
nextCivilParent = civilKind;
|
|
28746
|
+
nextProjectedSpatialTranslation = addTranslation(cumulativeTranslationHere, authoredSpecOrigin(el));
|
|
28747
|
+
} else if (isCivilSpatialIntent(el)) return (0, brepjs.err)(specError("FAMILIES_UNSUPPORTED_CIVIL_SEMANTICS", `familiesToBim: unsupported civil '${el.semantics?.kind ?? "unknown"}' category '${el.semantics?.category ?? ""}' at '${el.keyPath}'`));
|
|
28748
|
+
else if (archetype === "storey") {
|
|
28749
|
+
if (buildingId === null) return (0, brepjs.err)(specError("FAMILIES_INVALID_CIVIL_HIERARCHY", `familiesToBim: Storey '${el.keyPath}' is not part of the civil Bridge hierarchy`));
|
|
27962
28750
|
const keyed = requireKeyed(el);
|
|
27963
28751
|
if (!keyed.ok) return keyed;
|
|
27964
28752
|
const storeyResult = model.addStorey({
|
|
@@ -27968,7 +28756,17 @@ function familiesToBim(root, options) {
|
|
|
27968
28756
|
if (!storeyResult.ok) return storeyResult;
|
|
27969
28757
|
model.aggregate(buildingId, storeyResult.value);
|
|
27970
28758
|
idByKeyPath.set(el.keyPath, storeyResult.value);
|
|
27971
|
-
|
|
28759
|
+
nextSpatialStructureId = storeyResult.value;
|
|
28760
|
+
} else if (isEarthworksFillOccurrence(el)) {
|
|
28761
|
+
if (!usesAuthoredCivilHierarchy || nextSpatialStructureId === null || state.civilParent !== "bridge-part") return (0, brepjs.err)(specError("FAMILIES_INVALID_CIVIL_HIERARCHY", `familiesToBim: Earthworks Fill '${el.keyPath}' needs a Bridge Part ancestor`));
|
|
28762
|
+
const bodyEvaluator = options.bodyEvaluator ?? options.proxyEvaluator;
|
|
28763
|
+
if (bodyEvaluator === void 0) return (0, brepjs.err)(specError("FAMILIES_EARTHWORKS_EVALUATOR_REQUIRED", `familiesToBim: Earthworks Fill '${el.keyPath}' needs bodyEvaluator to materialize its exact Product Body`));
|
|
28764
|
+
const keyed = requireKeyed(el);
|
|
28765
|
+
if (!keyed.ok) return keyed;
|
|
28766
|
+
const added = addEarthworksFillElement(model, el, bodyEvaluator, state.projectedSpatialTranslation);
|
|
28767
|
+
if (!added.ok) return added;
|
|
28768
|
+
model.placeIn(added.value, nextSpatialStructureId);
|
|
28769
|
+
idByKeyPath.set(el.keyPath, added.value);
|
|
27972
28770
|
} else if (route !== void 0) {
|
|
27973
28771
|
const keyed = requireKeyed(el);
|
|
27974
28772
|
if (!keyed.ok) return keyed;
|
|
@@ -27978,26 +28776,27 @@ function familiesToBim(root, options) {
|
|
|
27978
28776
|
const openings = el.children.filter((c) => c.type === "Opening").length;
|
|
27979
28777
|
if (voids.length > openings) return (0, brepjs.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`));
|
|
27980
28778
|
}
|
|
27981
|
-
const
|
|
28779
|
+
const routedInput = el.semantics?.kind === "product" ? civilProductSpecInput(el) : ("input" in route ? route.input : specInput)(el);
|
|
28780
|
+
const parsed = route.parse(usesAuthoredCivilHierarchy ? relativeSpecInput(routedInput, state.projectedSpatialTranslation) : routedInput);
|
|
27982
28781
|
if (!parsed.ok) return parsed;
|
|
27983
28782
|
const added = route.add(model, parsed.value, el.keyPath);
|
|
27984
28783
|
if (!added.ok) return added;
|
|
27985
28784
|
idByKeyPath.set(el.keyPath, added.value);
|
|
27986
|
-
if (
|
|
27987
|
-
model.placeIn(added.value,
|
|
27988
|
-
if (
|
|
27989
|
-
const opened = addOpenings(model, el, added.value,
|
|
28785
|
+
if (nextSpatialStructureId === null || usesAuthoredCivilHierarchy && state.civilParent !== "bridge-part") return (0, brepjs.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`));
|
|
28786
|
+
model.placeIn(added.value, nextSpatialStructureId);
|
|
28787
|
+
if (effectiveArchetype === "wall") {
|
|
28788
|
+
const opened = addOpenings(model, el, added.value, nextSpatialStructureId, idByKeyPath);
|
|
27990
28789
|
if (!opened.ok) return opened;
|
|
27991
28790
|
}
|
|
27992
28791
|
} else if (el.type === "Opening") return (0, brepjs.err)(specError("FAMILIES_OPENING_OUTSIDE_WALL", `familiesToBim: opening '${el.keyPath}' is not hosted by a Wall — only wall openings are mapped`));
|
|
27993
28792
|
else if (el.type !== "Group" && el.geometry.kind !== "Empty") {
|
|
27994
|
-
if (options.proxyEvaluator === void 0) return (0, brepjs.err)(specError("FAMILIES_UNSUPPORTED_TYPE", `familiesToBim: no spec mapping for element type '${el.type}' at '${el.keyPath}' (archetype: ${el.archetype ?? "none"}) —
|
|
28793
|
+
if (options.proxyEvaluator === void 0) return (0, brepjs.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`));
|
|
27995
28794
|
const keyed = requireKeyed(el);
|
|
27996
28795
|
if (!keyed.ok) return keyed;
|
|
27997
|
-
if (
|
|
27998
|
-
const added = addProxyElement(model, el, options.proxyEvaluator);
|
|
28796
|
+
if (nextSpatialStructureId === null || usesAuthoredCivilHierarchy && state.civilParent !== "bridge-part") return (0, brepjs.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`));
|
|
28797
|
+
const added = addProxyElement(model, el, options.proxyEvaluator, state.projectedSpatialTranslation);
|
|
27999
28798
|
if (!added.ok) return added;
|
|
28000
|
-
model.placeIn(added.value,
|
|
28799
|
+
model.placeIn(added.value, nextSpatialStructureId);
|
|
28001
28800
|
idByKeyPath.set(el.keyPath, added.value);
|
|
28002
28801
|
proxied.push({
|
|
28003
28802
|
keyPath: el.keyPath,
|
|
@@ -28007,13 +28806,25 @@ function familiesToBim(root, options) {
|
|
|
28007
28806
|
proxiedHere = true;
|
|
28008
28807
|
}
|
|
28009
28808
|
for (const child of el.children) {
|
|
28010
|
-
if ((
|
|
28011
|
-
const r = walk(child,
|
|
28809
|
+
if ((effectiveArchetype === "wall" || proxiedHere) && child.type === "Opening") continue;
|
|
28810
|
+
const r = walk(child, {
|
|
28811
|
+
spatialStructureId: nextSpatialStructureId,
|
|
28812
|
+
civilParent: nextCivilParent,
|
|
28813
|
+
rotated: rotatedHere,
|
|
28814
|
+
cumulativeTranslation: cumulativeTranslationHere,
|
|
28815
|
+
projectedSpatialTranslation: nextProjectedSpatialTranslation
|
|
28816
|
+
});
|
|
28012
28817
|
if (!r.ok) return r;
|
|
28013
28818
|
}
|
|
28014
28819
|
return (0, brepjs.ok)(void 0);
|
|
28015
28820
|
};
|
|
28016
|
-
const walked = walk(root,
|
|
28821
|
+
const walked = walk(root, {
|
|
28822
|
+
spatialStructureId: usesAuthoredCivilHierarchy ? projectId : null,
|
|
28823
|
+
civilParent: "project",
|
|
28824
|
+
rotated: false,
|
|
28825
|
+
cumulativeTranslation: ZERO_TRANSLATION,
|
|
28826
|
+
projectedSpatialTranslation: ZERO_TRANSLATION
|
|
28827
|
+
});
|
|
28017
28828
|
if (!walked.ok) {
|
|
28018
28829
|
model[Symbol.dispose]();
|
|
28019
28830
|
return walked;
|
|
@@ -28069,6 +28880,8 @@ exports.measureTypeFor = measureTypeFor;
|
|
|
28069
28880
|
exports.newIfcGuid = newIfcGuid;
|
|
28070
28881
|
exports.parseBcfFiles = parseBcfFiles;
|
|
28071
28882
|
exports.parseBeamSpec = parseBeamSpec;
|
|
28883
|
+
exports.parseBridgePartSpec = parseBridgePartSpec;
|
|
28884
|
+
exports.parseBridgeSpec = parseBridgeSpec;
|
|
28072
28885
|
exports.parseColumnSpec = parseColumnSpec;
|
|
28073
28886
|
exports.parseCoveringSpec = parseCoveringSpec;
|
|
28074
28887
|
exports.parseCurtainWallSpec = parseCurtainWallSpec;
|