brepjs-bim 0.22.0 → 0.23.1
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/dist/brepjs-bim.cjs +660 -116
- package/dist/brepjs-bim.js +660 -116
- package/dist/placementFrame.d.ts +36 -0
- package/package.json +1 -1
package/dist/brepjs-bim.cjs
CHANGED
|
@@ -206,7 +206,7 @@ function fromBrepError(inner, code, message) {
|
|
|
206
206
|
};
|
|
207
207
|
}
|
|
208
208
|
//#endregion
|
|
209
|
-
//#region \0@oxc-project+runtime@0.
|
|
209
|
+
//#region \0@oxc-project+runtime@0.146.0/helpers/esm/usingCtx.js
|
|
210
210
|
function _usingCtx() {
|
|
211
211
|
var r = "function" == typeof SuppressedError ? SuppressedError : function(r, e) {
|
|
212
212
|
var n = Error();
|
|
@@ -7205,7 +7205,7 @@ function placementToMatrix(f) {
|
|
|
7205
7205
|
f.axisX[2] - dot * z[2]
|
|
7206
7206
|
];
|
|
7207
7207
|
const x = lengthSq(projX) < 1e-12 ? normalize$1(orthogonal$2(z)) : normalize$1(projX);
|
|
7208
|
-
const y = cross$
|
|
7208
|
+
const y = cross$2(z, x);
|
|
7209
7209
|
return {
|
|
7210
7210
|
linear: [
|
|
7211
7211
|
x[0],
|
|
@@ -7225,7 +7225,27 @@ function placementToMatrix(f) {
|
|
|
7225
7225
|
]
|
|
7226
7226
|
};
|
|
7227
7227
|
}
|
|
7228
|
-
|
|
7228
|
+
/** Decomposes a column-major matrix into origin (mm) + IFC axes (Z, X). */
|
|
7229
|
+
function decomposePlacement(m) {
|
|
7230
|
+
return {
|
|
7231
|
+
axisX: normalize$1([
|
|
7232
|
+
m[0],
|
|
7233
|
+
m[1],
|
|
7234
|
+
m[2]
|
|
7235
|
+
]),
|
|
7236
|
+
axisZ: normalize$1([
|
|
7237
|
+
m[8],
|
|
7238
|
+
m[9],
|
|
7239
|
+
m[10]
|
|
7240
|
+
]),
|
|
7241
|
+
origin: [
|
|
7242
|
+
m[12],
|
|
7243
|
+
m[13],
|
|
7244
|
+
m[14]
|
|
7245
|
+
]
|
|
7246
|
+
};
|
|
7247
|
+
}
|
|
7248
|
+
function cross$2(a, b) {
|
|
7229
7249
|
return [
|
|
7230
7250
|
a[1] * b[2] - a[2] * b[1],
|
|
7231
7251
|
a[2] * b[0] - a[0] * b[2],
|
|
@@ -7249,11 +7269,11 @@ function normalize$1(v) {
|
|
|
7249
7269
|
];
|
|
7250
7270
|
}
|
|
7251
7271
|
function orthogonal$2(v) {
|
|
7252
|
-
return Math.abs(v[0]) < .9 ? cross$
|
|
7272
|
+
return Math.abs(v[0]) < .9 ? cross$2(v, [
|
|
7253
7273
|
1,
|
|
7254
7274
|
0,
|
|
7255
7275
|
0
|
|
7256
|
-
]) : cross$
|
|
7276
|
+
]) : cross$2(v, [
|
|
7257
7277
|
0,
|
|
7258
7278
|
1,
|
|
7259
7279
|
0
|
|
@@ -8759,7 +8779,7 @@ function writeWallGeometry(w, spec, geomSubContextId, parentPlacementId) {
|
|
|
8759
8779
|
value: "AREA"
|
|
8760
8780
|
},
|
|
8761
8781
|
ProfileName: null,
|
|
8762
|
-
Position: w.ref(writeAxis2Placement2D$1(w)),
|
|
8782
|
+
Position: w.ref(writeAxis2Placement2D$1(w, [thicknessM / 2, heightM / 2])),
|
|
8763
8783
|
XDim: w.mkType(web_ifc.IFCPOSITIVELENGTHMEASURE, thicknessM),
|
|
8764
8784
|
YDim: w.mkType(web_ifc.IFCPOSITIVELENGTHMEASURE, heightM)
|
|
8765
8785
|
});
|
|
@@ -8980,12 +9000,12 @@ function writeRoofGeometry(w, spec, solid, geomSubContextId, parentPlacementId)
|
|
|
8980
9000
|
usedFallback: false
|
|
8981
9001
|
};
|
|
8982
9002
|
}
|
|
8983
|
-
function writeAxis2Placement2D$1(w) {
|
|
9003
|
+
function writeAxis2Placement2D$1(w, location = [0, 0]) {
|
|
8984
9004
|
const originId = w.nextId();
|
|
8985
9005
|
w.writeLine({
|
|
8986
9006
|
expressID: originId,
|
|
8987
9007
|
type: web_ifc.IFCCARTESIANPOINT,
|
|
8988
|
-
Coordinates: [w.mkType(web_ifc.IFCLENGTHMEASURE, 0), w.mkType(web_ifc.IFCLENGTHMEASURE,
|
|
9008
|
+
Coordinates: [w.mkType(web_ifc.IFCLENGTHMEASURE, location[0]), w.mkType(web_ifc.IFCLENGTHMEASURE, location[1])]
|
|
8989
9009
|
});
|
|
8990
9010
|
const id = w.nextId();
|
|
8991
9011
|
w.writeLine({
|
|
@@ -11490,11 +11510,10 @@ function writeOpeningGeometry(w, guid, openingSpec, wallSpec, wallPlacementId, g
|
|
|
11490
11510
|
const offsetAlongWallM = toIfcLengthM(openingSpec.offsetAlongWall);
|
|
11491
11511
|
const offsetFromFloorM = toIfcLengthM(openingSpec.offsetFromFloor);
|
|
11492
11512
|
const thicknessM = toIfcLengthM(wallSpec.thickness);
|
|
11493
|
-
const wallHeightM = toIfcLengthM(wallSpec.height);
|
|
11494
11513
|
const placement3DId = writeAxis2Placement3D(w, [
|
|
11495
11514
|
offsetAlongWallM + widthM / 2,
|
|
11496
|
-
thicknessM
|
|
11497
|
-
|
|
11515
|
+
thicknessM,
|
|
11516
|
+
offsetFromFloorM + heightM / 2
|
|
11498
11517
|
], [
|
|
11499
11518
|
0,
|
|
11500
11519
|
-1,
|
|
@@ -12370,6 +12389,24 @@ var SUPPORTED_SCHEMAS = [
|
|
|
12370
12389
|
* MUST call {@link SpfReader.close} (it always issues `CloseModel`, preventing
|
|
12371
12390
|
* WASM handle leaks). The handle is `using`-friendly via `Symbol.dispose`.
|
|
12372
12391
|
*/
|
|
12392
|
+
var IFC_Z_UP_TRANSFORMATION = [
|
|
12393
|
+
1,
|
|
12394
|
+
0,
|
|
12395
|
+
0,
|
|
12396
|
+
0,
|
|
12397
|
+
0,
|
|
12398
|
+
0,
|
|
12399
|
+
1,
|
|
12400
|
+
0,
|
|
12401
|
+
0,
|
|
12402
|
+
-1,
|
|
12403
|
+
0,
|
|
12404
|
+
0,
|
|
12405
|
+
0,
|
|
12406
|
+
0,
|
|
12407
|
+
0,
|
|
12408
|
+
1
|
|
12409
|
+
];
|
|
12373
12410
|
function deleteVector(vec) {
|
|
12374
12411
|
vec.delete?.();
|
|
12375
12412
|
}
|
|
@@ -12402,6 +12439,7 @@ var SpfReader = class SpfReader {
|
|
|
12402
12439
|
if (modelId >= 0) api.CloseModel(modelId);
|
|
12403
12440
|
return (0, brepjs.err)(importError("OPEN_MODEL_FAILED", "web-ifc OpenModel returned an invalid model id"));
|
|
12404
12441
|
}
|
|
12442
|
+
api.SetGeometryTransformation(modelId, IFC_Z_UP_TRANSFORMATION);
|
|
12405
12443
|
const schemaRaw = api.GetModelSchema(modelId);
|
|
12406
12444
|
if (!SUPPORTED_SCHEMAS.includes(schemaRaw)) {
|
|
12407
12445
|
api.CloseModel(modelId);
|
|
@@ -14101,12 +14139,14 @@ function reconstructExtrusion(reader, extrusionId, scale, worldTransform, diagno
|
|
|
14101
14139
|
diagnostics.push(issue("warning", faceResult.error.code, faceResult.error.message, extrusionId));
|
|
14102
14140
|
return NONE;
|
|
14103
14141
|
}
|
|
14142
|
+
const profileFrame = readProfilePosition(reader, sweptArea.value, scale);
|
|
14104
14143
|
const depthMm = depth * scale * 1e3;
|
|
14105
|
-
const
|
|
14144
|
+
const sweptDir = readDirection(reader, ext["ExtrudedDirection"]) ?? [
|
|
14106
14145
|
0,
|
|
14107
14146
|
0,
|
|
14108
14147
|
1
|
|
14109
14148
|
];
|
|
14149
|
+
const extrudeDir = profileFrame === null ? sweptDir : rotateByTranspose(profileFrame.linear, sweptDir);
|
|
14110
14150
|
const extrudeVec = [
|
|
14111
14151
|
extrudeDir[0] * depthMm,
|
|
14112
14152
|
extrudeDir[1] * depthMm,
|
|
@@ -14128,7 +14168,11 @@ function reconstructExtrusion(reader, extrusionId, scale, worldTransform, diagno
|
|
|
14128
14168
|
return NONE;
|
|
14129
14169
|
}
|
|
14130
14170
|
const localFrame = readAxis2Placement3D(reader, ext["Position"], scale);
|
|
14131
|
-
const placed = placeSolid(solidResult.value,
|
|
14171
|
+
const placed = placeSolid(solidResult.value, [
|
|
14172
|
+
worldTransform,
|
|
14173
|
+
localFrame,
|
|
14174
|
+
profileFrame
|
|
14175
|
+
]);
|
|
14132
14176
|
if (!placed.ok) {
|
|
14133
14177
|
diagnostics.push(issue("warning", placed.error.code, placed.error.message, extrusionId));
|
|
14134
14178
|
return NONE;
|
|
@@ -14156,16 +14200,19 @@ function reconstructRevolution(reader, revolutionId, scale, worldTransform, diag
|
|
|
14156
14200
|
return NONE;
|
|
14157
14201
|
}
|
|
14158
14202
|
const axis1 = reader.getLine(axisRef.value);
|
|
14159
|
-
const
|
|
14203
|
+
const sweptCenter = (axis1 === null ? void 0 : readPoint(reader, axis1["Location"], scale)) ?? [
|
|
14160
14204
|
0,
|
|
14161
14205
|
0,
|
|
14162
14206
|
0
|
|
14163
14207
|
];
|
|
14164
|
-
const
|
|
14208
|
+
const sweptDirection = (axis1 === null ? void 0 : readDirection(reader, axis1["Axis"])) ?? [
|
|
14165
14209
|
0,
|
|
14166
14210
|
0,
|
|
14167
14211
|
1
|
|
14168
14212
|
];
|
|
14213
|
+
const profileFrame = readProfilePosition(reader, sweptArea.value, scale);
|
|
14214
|
+
const center = profileFrame === null ? sweptCenter : inverseRigidPoint(profileFrame, sweptCenter);
|
|
14215
|
+
const direction = profileFrame === null ? sweptDirection : rotateByTranspose(profileFrame.linear, sweptDirection);
|
|
14169
14216
|
const revolved = (() => {
|
|
14170
14217
|
try {
|
|
14171
14218
|
var _usingCtx3 = _usingCtx();
|
|
@@ -14192,7 +14239,11 @@ function reconstructRevolution(reader, revolutionId, scale, worldTransform, diag
|
|
|
14192
14239
|
return NONE;
|
|
14193
14240
|
}
|
|
14194
14241
|
const localFrame = readAxis2Placement3D(reader, rev["Position"], scale);
|
|
14195
|
-
const placed = placeSolid(revolved.value,
|
|
14242
|
+
const placed = placeSolid(revolved.value, [
|
|
14243
|
+
worldTransform,
|
|
14244
|
+
localFrame,
|
|
14245
|
+
profileFrame
|
|
14246
|
+
]);
|
|
14196
14247
|
if (!placed.ok) {
|
|
14197
14248
|
diagnostics.push(issue("warning", placed.error.code, placed.error.message, revolutionId));
|
|
14198
14249
|
return NONE;
|
|
@@ -14205,20 +14256,9 @@ function reconstructTessellated(reader, productExpressId, diagnostics) {
|
|
|
14205
14256
|
diagnostics.push(issue("warning", "GEOMETRY_RECONSTRUCTION_FAILED", "Tessellated geometry yielded no triangles", productExpressId));
|
|
14206
14257
|
return NONE;
|
|
14207
14258
|
}
|
|
14208
|
-
const
|
|
14209
|
-
let solid = null;
|
|
14210
|
-
try {
|
|
14211
|
-
const cast = (0, brepjs.castShape)((0, brepjs.getKernel)().importSTL(stl.buffer));
|
|
14212
|
-
if ((0, brepjs.isSolid)(cast)) {
|
|
14213
|
-
const valid = (0, brepjs.validSolid)(cast);
|
|
14214
|
-
if (valid.ok) solid = valid.value;
|
|
14215
|
-
else cast[Symbol.dispose]();
|
|
14216
|
-
} else cast[Symbol.dispose]();
|
|
14217
|
-
} catch (e) {
|
|
14218
|
-
diagnostics.push(issue("info", "TESSELLATION_NOT_MANIFOLD", `STL round-trip failed: ${errMsg$1(e)}`, productExpressId));
|
|
14219
|
-
}
|
|
14259
|
+
const solid = sewMeshToSolid(mesh, 1e3, productExpressId, diagnostics);
|
|
14220
14260
|
if (solid !== null) {
|
|
14221
|
-
diagnostics.push(issue("info", "TESSELLATED_MANIFOLD", "Tessellated mesh recovered as a closed solid
|
|
14261
|
+
diagnostics.push(issue("info", "TESSELLATED_MANIFOLD", "Tessellated mesh recovered as a closed solid by sewing its triangles", productExpressId));
|
|
14222
14262
|
return {
|
|
14223
14263
|
kind: "SOLID",
|
|
14224
14264
|
solid,
|
|
@@ -14233,6 +14273,52 @@ function reconstructTessellated(reader, productExpressId, diagnostics) {
|
|
|
14233
14273
|
diagnostic: "TESSELLATED_LOSSY"
|
|
14234
14274
|
};
|
|
14235
14275
|
}
|
|
14276
|
+
/** Loose enough to weld float32 vertices web-ifc emits per face, in mm. */
|
|
14277
|
+
var MESH_SEW_TOLERANCE_MM = .001;
|
|
14278
|
+
/**
|
|
14279
|
+
* Brands a fresh kernel result and releases its pre-downcast arena slot when
|
|
14280
|
+
* the cast moved to a new one (occt-wasm); in-place kernels share the slot and
|
|
14281
|
+
* are left alone. Mirrors brepjs's internal castResultShape.
|
|
14282
|
+
*/
|
|
14283
|
+
function castFreshResult(raw) {
|
|
14284
|
+
const cast = (0, brepjs.castShape)(raw);
|
|
14285
|
+
const rawId = raw.id;
|
|
14286
|
+
const castId = cast.wrapped.id;
|
|
14287
|
+
if (!(rawId !== void 0 && castId !== void 0 ? rawId === castId : cast.wrapped === raw)) (0, brepjs.getKernel)().dispose(raw);
|
|
14288
|
+
return cast;
|
|
14289
|
+
}
|
|
14290
|
+
/**
|
|
14291
|
+
* Sews the streamed triangles into a closed solid through the kernel's mesh
|
|
14292
|
+
* builder, the same path brepjs's mesh importers use. Null when the mesh is
|
|
14293
|
+
* open or the kernel rejects it.
|
|
14294
|
+
*/
|
|
14295
|
+
function sewMeshToSolid(mesh, scaleToMm, productExpressId, diagnostics) {
|
|
14296
|
+
const kernel = (0, brepjs.getKernel)();
|
|
14297
|
+
const { vertices, indices } = mesh;
|
|
14298
|
+
const point = (index) => [
|
|
14299
|
+
(vertices[index * 3] ?? 0) * scaleToMm,
|
|
14300
|
+
(vertices[index * 3 + 1] ?? 0) * scaleToMm,
|
|
14301
|
+
(vertices[index * 3 + 2] ?? 0) * scaleToMm
|
|
14302
|
+
];
|
|
14303
|
+
const triangles = [];
|
|
14304
|
+
for (let t = 0; t + 2 < indices.length; t += 3) {
|
|
14305
|
+
const face = kernel.buildTriFace(point(indices[t] ?? 0), point(indices[t + 1] ?? 0), point(indices[t + 2] ?? 0));
|
|
14306
|
+
if (face !== null) triangles.push(face);
|
|
14307
|
+
}
|
|
14308
|
+
if (triangles.length === 0) return null;
|
|
14309
|
+
try {
|
|
14310
|
+
const cast = castFreshResult(kernel.sewAndSolidify(triangles, MESH_SEW_TOLERANCE_MM));
|
|
14311
|
+
const valid = (0, brepjs.isSolid)(cast) ? (0, brepjs.validSolid)(cast) : null;
|
|
14312
|
+
if (valid !== null && valid.ok) return valid.value;
|
|
14313
|
+
cast[Symbol.dispose]();
|
|
14314
|
+
return null;
|
|
14315
|
+
} catch (e) {
|
|
14316
|
+
diagnostics.push(issue("info", "TESSELLATION_NOT_MANIFOLD", `Mesh sewing failed: ${errMsg$1(e)}`, productExpressId));
|
|
14317
|
+
return null;
|
|
14318
|
+
} finally {
|
|
14319
|
+
for (const face of triangles) kernel.dispose(face);
|
|
14320
|
+
}
|
|
14321
|
+
}
|
|
14236
14322
|
function collectMesh(reader, productExpressId) {
|
|
14237
14323
|
const vertices = [];
|
|
14238
14324
|
const indices = [];
|
|
@@ -14261,7 +14347,8 @@ function collectMesh(reader, productExpressId) {
|
|
|
14261
14347
|
}
|
|
14262
14348
|
}
|
|
14263
14349
|
} finally {
|
|
14264
|
-
flatMesh.
|
|
14350
|
+
releaseEmbind(flatMesh.geometries);
|
|
14351
|
+
releaseEmbind(flatMesh);
|
|
14265
14352
|
}
|
|
14266
14353
|
});
|
|
14267
14354
|
if (vertices.length === 0) return null;
|
|
@@ -14270,25 +14357,6 @@ function collectMesh(reader, productExpressId) {
|
|
|
14270
14357
|
indices: new Uint32Array(indices)
|
|
14271
14358
|
};
|
|
14272
14359
|
}
|
|
14273
|
-
function packBinaryStl(vertices, indices, scaleToMm) {
|
|
14274
|
-
const triCount = Math.floor(indices.length / 3);
|
|
14275
|
-
const buffer = /* @__PURE__ */ new ArrayBuffer(84 + triCount * 50);
|
|
14276
|
-
const view = new DataView(buffer);
|
|
14277
|
-
view.setUint32(80, triCount, true);
|
|
14278
|
-
let offset = 84;
|
|
14279
|
-
for (let t = 0; t < triCount; t++) {
|
|
14280
|
-
offset += 12;
|
|
14281
|
-
for (let c = 0; c < 3; c++) {
|
|
14282
|
-
const vi = (indices[t * 3 + c] ?? 0) * 3;
|
|
14283
|
-
view.setFloat32(offset, (vertices[vi] ?? 0) * scaleToMm, true);
|
|
14284
|
-
view.setFloat32(offset + 4, (vertices[vi + 1] ?? 0) * scaleToMm, true);
|
|
14285
|
-
view.setFloat32(offset + 8, (vertices[vi + 2] ?? 0) * scaleToMm, true);
|
|
14286
|
-
offset += 12;
|
|
14287
|
-
}
|
|
14288
|
-
offset += 2;
|
|
14289
|
-
}
|
|
14290
|
-
return new Uint8Array(buffer);
|
|
14291
|
-
}
|
|
14292
14360
|
var PARAMETRIC_PROFILE_BUILDERS = /* @__PURE__ */ new Map([
|
|
14293
14361
|
[web_ifc.IFCRECTANGLEPROFILEDEF, (f) => ({
|
|
14294
14362
|
kind: "RECTANGULAR",
|
|
@@ -14432,19 +14500,99 @@ function profileToFace(profile) {
|
|
|
14432
14500
|
if (!face.ok) return (0, brepjs.err)(importError("GEOMETRY_RECONSTRUCTION_FAILED", `Profile face build failed: ${face.error.message}`));
|
|
14433
14501
|
return (0, brepjs.ok)(face.value);
|
|
14434
14502
|
}
|
|
14435
|
-
|
|
14436
|
-
|
|
14437
|
-
|
|
14438
|
-
|
|
14439
|
-
|
|
14440
|
-
|
|
14441
|
-
|
|
14442
|
-
|
|
14443
|
-
|
|
14444
|
-
|
|
14445
|
-
|
|
14446
|
-
|
|
14447
|
-
|
|
14503
|
+
/**
|
|
14504
|
+
* IfcParameterizedProfileDef.Position places the profile inside the swept
|
|
14505
|
+
* solid's XY plane; the writers rely on it to corner-anchor centred rectangle
|
|
14506
|
+
* profiles. Absent on arbitrary (polyline) profiles.
|
|
14507
|
+
*/
|
|
14508
|
+
function readProfilePosition(reader, profileExpressId, scale) {
|
|
14509
|
+
const def = reader.getLine(profileExpressId);
|
|
14510
|
+
const positionRef = def === null ? void 0 : asRef(def["Position"]);
|
|
14511
|
+
if (positionRef === void 0) return null;
|
|
14512
|
+
const position = reader.getLine(positionRef.value);
|
|
14513
|
+
if (position === null) return null;
|
|
14514
|
+
const [x, y] = readPoint2D(reader, position["Location"], scale) ?? [0, 0];
|
|
14515
|
+
const [dx, dy] = readDirection2D(reader, position["RefDirection"]) ?? [1, 0];
|
|
14516
|
+
const len = Math.hypot(dx, dy);
|
|
14517
|
+
const c = len < 1e-12 ? 1 : dx / len;
|
|
14518
|
+
const s = len < 1e-12 ? 0 : dy / len;
|
|
14519
|
+
return {
|
|
14520
|
+
linear: [
|
|
14521
|
+
c,
|
|
14522
|
+
-s,
|
|
14523
|
+
0,
|
|
14524
|
+
s,
|
|
14525
|
+
c,
|
|
14526
|
+
0,
|
|
14527
|
+
0,
|
|
14528
|
+
0,
|
|
14529
|
+
1
|
|
14530
|
+
],
|
|
14531
|
+
translation: [
|
|
14532
|
+
x,
|
|
14533
|
+
y,
|
|
14534
|
+
0
|
|
14535
|
+
]
|
|
14536
|
+
};
|
|
14537
|
+
}
|
|
14538
|
+
/**
|
|
14539
|
+
* Applies the placement chain (outermost first) as one composed rigid motion.
|
|
14540
|
+
* A second applyMatrix on an already-transformed solid can fail BRepCheck on
|
|
14541
|
+
* occt-wasm, so the frames are never applied one after another.
|
|
14542
|
+
*/
|
|
14543
|
+
function placeSolid(solid, frames) {
|
|
14544
|
+
let composed = null;
|
|
14545
|
+
for (const frame of frames) {
|
|
14546
|
+
if (frame === null) continue;
|
|
14547
|
+
composed = composed === null ? frame : composeRigid(composed, frame);
|
|
14548
|
+
}
|
|
14549
|
+
if (composed === null || isIdentity(composed)) return (0, brepjs.ok)(solid);
|
|
14550
|
+
const applied = (0, brepjs.applyMatrix)(solid, composed);
|
|
14551
|
+
solid[Symbol.dispose]();
|
|
14552
|
+
if (!applied.ok) return (0, brepjs.err)(importError("PLACEMENT_READ_FAILED", `Placement transform failed: ${applied.error.message}`));
|
|
14553
|
+
return (0, brepjs.ok)(applied.value);
|
|
14554
|
+
}
|
|
14555
|
+
/** `outer ∘ inner`: the motion that applies `inner` first, then `outer`. */
|
|
14556
|
+
function composeRigid(outer, inner) {
|
|
14557
|
+
const [a00, a01, a02, a10, a11, a12, a20, a21, a22] = outer.linear;
|
|
14558
|
+
const [b00, b01, b02, b10, b11, b12, b20, b21, b22] = inner.linear;
|
|
14559
|
+
const [tx, ty, tz] = inner.translation;
|
|
14560
|
+
const [ux, uy, uz] = outer.translation;
|
|
14561
|
+
return {
|
|
14562
|
+
linear: [
|
|
14563
|
+
a00 * b00 + a01 * b10 + a02 * b20,
|
|
14564
|
+
a00 * b01 + a01 * b11 + a02 * b21,
|
|
14565
|
+
a00 * b02 + a01 * b12 + a02 * b22,
|
|
14566
|
+
a10 * b00 + a11 * b10 + a12 * b20,
|
|
14567
|
+
a10 * b01 + a11 * b11 + a12 * b21,
|
|
14568
|
+
a10 * b02 + a11 * b12 + a12 * b22,
|
|
14569
|
+
a20 * b00 + a21 * b10 + a22 * b20,
|
|
14570
|
+
a20 * b01 + a21 * b11 + a22 * b21,
|
|
14571
|
+
a20 * b02 + a21 * b12 + a22 * b22
|
|
14572
|
+
],
|
|
14573
|
+
translation: [
|
|
14574
|
+
a00 * tx + a01 * ty + a02 * tz + ux,
|
|
14575
|
+
a10 * tx + a11 * ty + a12 * tz + uy,
|
|
14576
|
+
a20 * tx + a21 * ty + a22 * tz + uz
|
|
14577
|
+
]
|
|
14578
|
+
};
|
|
14579
|
+
}
|
|
14580
|
+
/** Rᵀ·v for a rigid frame's rotation part. */
|
|
14581
|
+
function rotateByTranspose(linear, v) {
|
|
14582
|
+
return [
|
|
14583
|
+
linear[0] * v[0] + linear[3] * v[1] + linear[6] * v[2],
|
|
14584
|
+
linear[1] * v[0] + linear[4] * v[1] + linear[7] * v[2],
|
|
14585
|
+
linear[2] * v[0] + linear[5] * v[1] + linear[8] * v[2]
|
|
14586
|
+
];
|
|
14587
|
+
}
|
|
14588
|
+
/** Maps a point from the frame's parent space into the frame: Rᵀ·(p − t). */
|
|
14589
|
+
function inverseRigidPoint(frame, p) {
|
|
14590
|
+
const t = frame.translation;
|
|
14591
|
+
return rotateByTranspose(frame.linear, [
|
|
14592
|
+
p[0] - t[0],
|
|
14593
|
+
p[1] - t[1],
|
|
14594
|
+
p[2] - t[2]
|
|
14595
|
+
]);
|
|
14448
14596
|
}
|
|
14449
14597
|
function finalizeSolid(solid, entity, diagnostics) {
|
|
14450
14598
|
if (!(0, brepjs.isSolid)(solid)) {
|
|
@@ -14499,17 +14647,17 @@ function readAxis2Placement3D(reader, ref, scale) {
|
|
|
14499
14647
|
refDir[2] - dotXZ * zN[2]
|
|
14500
14648
|
];
|
|
14501
14649
|
const xRawLenSq = xRaw[0] * xRaw[0] + xRaw[1] * xRaw[1] + xRaw[2] * xRaw[2];
|
|
14502
|
-
const orthoZ = Math.abs(zN[0]) < .9 ? cross(zN, [
|
|
14650
|
+
const orthoZ = Math.abs(zN[0]) < .9 ? cross$1(zN, [
|
|
14503
14651
|
1,
|
|
14504
14652
|
0,
|
|
14505
14653
|
0
|
|
14506
|
-
]) : cross(zN, [
|
|
14654
|
+
]) : cross$1(zN, [
|
|
14507
14655
|
0,
|
|
14508
14656
|
1,
|
|
14509
14657
|
0
|
|
14510
14658
|
]);
|
|
14511
14659
|
const xN = xRawLenSq < 1e-12 ? normalize(orthoZ) : normalize(xRaw);
|
|
14512
|
-
const yN = cross(zN, xN);
|
|
14660
|
+
const yN = cross$1(zN, xN);
|
|
14513
14661
|
return {
|
|
14514
14662
|
linear: [
|
|
14515
14663
|
xN[0],
|
|
@@ -14593,6 +14741,24 @@ function readPolylinePoints(reader, ref, scale) {
|
|
|
14593
14741
|
}
|
|
14594
14742
|
return out.length >= 3 ? out : void 0;
|
|
14595
14743
|
}
|
|
14744
|
+
function readPoint2D(reader, ref, scale) {
|
|
14745
|
+
const pointRef = asRef(ref);
|
|
14746
|
+
if (pointRef === void 0) return void 0;
|
|
14747
|
+
const pt = reader.getLine(pointRef.value);
|
|
14748
|
+
if (pt === null) return void 0;
|
|
14749
|
+
const coords = asMeasureArray(pt["Coordinates"]);
|
|
14750
|
+
if (coords.length < 2) return void 0;
|
|
14751
|
+
return [(coords[0] ?? 0) * scale * 1e3, (coords[1] ?? 0) * scale * 1e3];
|
|
14752
|
+
}
|
|
14753
|
+
function readDirection2D(reader, ref) {
|
|
14754
|
+
const dirRef = asRef(ref);
|
|
14755
|
+
if (dirRef === void 0) return void 0;
|
|
14756
|
+
const dir = reader.getLine(dirRef.value);
|
|
14757
|
+
if (dir === null) return void 0;
|
|
14758
|
+
const ratios = asMeasureArray(dir["DirectionRatios"]);
|
|
14759
|
+
if (ratios.length < 2) return void 0;
|
|
14760
|
+
return [ratios[0] ?? 0, ratios[1] ?? 0];
|
|
14761
|
+
}
|
|
14596
14762
|
function readPoint(reader, ref, scale) {
|
|
14597
14763
|
const pointRef = asRef(ref);
|
|
14598
14764
|
if (pointRef === void 0) return void 0;
|
|
@@ -14619,6 +14785,9 @@ function readDirection(reader, ref) {
|
|
|
14619
14785
|
ratios[2] ?? 0
|
|
14620
14786
|
];
|
|
14621
14787
|
}
|
|
14788
|
+
function releaseEmbind(value) {
|
|
14789
|
+
value.delete?.();
|
|
14790
|
+
}
|
|
14622
14791
|
function asRef(value) {
|
|
14623
14792
|
if (value !== null && typeof value === "object" && "value" in value) {
|
|
14624
14793
|
const v = value.value;
|
|
@@ -14669,7 +14838,7 @@ function normalize(v) {
|
|
|
14669
14838
|
v[2] / len
|
|
14670
14839
|
];
|
|
14671
14840
|
}
|
|
14672
|
-
function cross(a, b) {
|
|
14841
|
+
function cross$1(a, b) {
|
|
14673
14842
|
return [
|
|
14674
14843
|
a[1] * b[2] - a[2] * b[1],
|
|
14675
14844
|
a[2] * b[0] - a[0] * b[2],
|
|
@@ -27931,6 +28100,217 @@ function assignNum(target, key, value) {
|
|
|
27931
28100
|
if (value !== void 0) target[key] = Number(value);
|
|
27932
28101
|
}
|
|
27933
28102
|
//#endregion
|
|
28103
|
+
//#region src/placementFrame.ts
|
|
28104
|
+
var IDENTITY_FRAME = [
|
|
28105
|
+
1,
|
|
28106
|
+
0,
|
|
28107
|
+
0,
|
|
28108
|
+
0,
|
|
28109
|
+
0,
|
|
28110
|
+
1,
|
|
28111
|
+
0,
|
|
28112
|
+
0,
|
|
28113
|
+
0,
|
|
28114
|
+
0,
|
|
28115
|
+
1,
|
|
28116
|
+
0,
|
|
28117
|
+
0,
|
|
28118
|
+
0,
|
|
28119
|
+
0,
|
|
28120
|
+
1
|
|
28121
|
+
];
|
|
28122
|
+
var DEFAULT_AXIS = [
|
|
28123
|
+
0,
|
|
28124
|
+
0,
|
|
28125
|
+
1
|
|
28126
|
+
];
|
|
28127
|
+
var ORIGIN = [
|
|
28128
|
+
0,
|
|
28129
|
+
0,
|
|
28130
|
+
0
|
|
28131
|
+
];
|
|
28132
|
+
/** Column-major 4x4 multiply: `a . b` (apply b first, then a). */
|
|
28133
|
+
function frameMul(a, b) {
|
|
28134
|
+
const out = new Array(16).fill(0);
|
|
28135
|
+
for (let col = 0; col < 4; col++) for (let row = 0; row < 4; row++) {
|
|
28136
|
+
let sum = 0;
|
|
28137
|
+
for (let k = 0; k < 4; k++) sum += (a[k * 4 + row] ?? 0) * (b[col * 4 + k] ?? 0);
|
|
28138
|
+
out[col * 4 + row] = sum;
|
|
28139
|
+
}
|
|
28140
|
+
return out;
|
|
28141
|
+
}
|
|
28142
|
+
function translationFrame(v) {
|
|
28143
|
+
return [
|
|
28144
|
+
1,
|
|
28145
|
+
0,
|
|
28146
|
+
0,
|
|
28147
|
+
0,
|
|
28148
|
+
0,
|
|
28149
|
+
1,
|
|
28150
|
+
0,
|
|
28151
|
+
0,
|
|
28152
|
+
0,
|
|
28153
|
+
0,
|
|
28154
|
+
1,
|
|
28155
|
+
0,
|
|
28156
|
+
v[0],
|
|
28157
|
+
v[1],
|
|
28158
|
+
v[2],
|
|
28159
|
+
1
|
|
28160
|
+
];
|
|
28161
|
+
}
|
|
28162
|
+
function normalizeAxis(axis) {
|
|
28163
|
+
const len = Math.sqrt(axis[0] * axis[0] + axis[1] * axis[1] + axis[2] * axis[2]);
|
|
28164
|
+
if (len < 1e-12) return DEFAULT_AXIS;
|
|
28165
|
+
return [
|
|
28166
|
+
axis[0] / len,
|
|
28167
|
+
axis[1] / len,
|
|
28168
|
+
axis[2] / len
|
|
28169
|
+
];
|
|
28170
|
+
}
|
|
28171
|
+
function cross(a, b) {
|
|
28172
|
+
return [
|
|
28173
|
+
a[1] * b[2] - a[2] * b[1],
|
|
28174
|
+
a[2] * b[0] - a[0] * b[2],
|
|
28175
|
+
a[0] * b[1] - a[1] * b[0]
|
|
28176
|
+
];
|
|
28177
|
+
}
|
|
28178
|
+
/**
|
|
28179
|
+
* Builds a frame from an authored `(origin, axisX, axisZ)` placement, using the
|
|
28180
|
+
* same IFC orthonormalization as `placementToMatrix`/`readAxis2Placement3D`:
|
|
28181
|
+
* z = normalize(axisZ); x = normalize(axisX projected perpendicular to z);
|
|
28182
|
+
* y = z x. Lets a civil node authored with explicit axis props enter the same
|
|
28183
|
+
* frame pipeline as a `tRotate` chain.
|
|
28184
|
+
*/
|
|
28185
|
+
function frameFromPlacement(f) {
|
|
28186
|
+
const z = normalizeAxis(f.axisZ);
|
|
28187
|
+
const dot = z[0] * f.axisX[0] + z[1] * f.axisX[1] + z[2] * f.axisX[2];
|
|
28188
|
+
const x = normalizeAxis([
|
|
28189
|
+
f.axisX[0] - dot * z[0],
|
|
28190
|
+
f.axisX[1] - dot * z[1],
|
|
28191
|
+
f.axisX[2] - dot * z[2]
|
|
28192
|
+
]);
|
|
28193
|
+
const y = cross(z, x);
|
|
28194
|
+
return [
|
|
28195
|
+
x[0],
|
|
28196
|
+
x[1],
|
|
28197
|
+
x[2],
|
|
28198
|
+
0,
|
|
28199
|
+
y[0],
|
|
28200
|
+
y[1],
|
|
28201
|
+
y[2],
|
|
28202
|
+
0,
|
|
28203
|
+
z[0],
|
|
28204
|
+
z[1],
|
|
28205
|
+
z[2],
|
|
28206
|
+
0,
|
|
28207
|
+
f.origin[0],
|
|
28208
|
+
f.origin[1],
|
|
28209
|
+
f.origin[2],
|
|
28210
|
+
1
|
|
28211
|
+
];
|
|
28212
|
+
}
|
|
28213
|
+
/**
|
|
28214
|
+
* Rotation by `angleDeg` about `axis` (default +Z) through pivot `at` (default
|
|
28215
|
+
* origin), matching `csg.rotate` (Rodrigues; right-handed about the axis). With
|
|
28216
|
+
* a pivot the motion is `T(at) . R . T(-at)`, i.e. `p -> at + R(p - at)`.
|
|
28217
|
+
*/
|
|
28218
|
+
function rotationFrame(angleDeg, axis = DEFAULT_AXIS, at = ORIGIN) {
|
|
28219
|
+
const [x, y, z] = normalizeAxis(axis);
|
|
28220
|
+
const t = angleDeg * Math.PI / 180;
|
|
28221
|
+
const c = Math.cos(t);
|
|
28222
|
+
const s = Math.sin(t);
|
|
28223
|
+
const C = 1 - c;
|
|
28224
|
+
const r00 = c + x * x * C;
|
|
28225
|
+
const r01 = x * y * C - z * s;
|
|
28226
|
+
const r02 = x * z * C + y * s;
|
|
28227
|
+
const r10 = y * x * C + z * s;
|
|
28228
|
+
const r11 = c + y * y * C;
|
|
28229
|
+
const r12 = y * z * C - x * s;
|
|
28230
|
+
const r20 = z * x * C - y * s;
|
|
28231
|
+
const r21 = z * y * C + x * s;
|
|
28232
|
+
const r22 = c + z * z * C;
|
|
28233
|
+
const rat0 = r00 * at[0] + r01 * at[1] + r02 * at[2];
|
|
28234
|
+
const rat1 = r10 * at[0] + r11 * at[1] + r12 * at[2];
|
|
28235
|
+
const rat2 = r20 * at[0] + r21 * at[1] + r22 * at[2];
|
|
28236
|
+
return [
|
|
28237
|
+
r00,
|
|
28238
|
+
r10,
|
|
28239
|
+
r20,
|
|
28240
|
+
0,
|
|
28241
|
+
r01,
|
|
28242
|
+
r11,
|
|
28243
|
+
r21,
|
|
28244
|
+
0,
|
|
28245
|
+
r02,
|
|
28246
|
+
r12,
|
|
28247
|
+
r22,
|
|
28248
|
+
0,
|
|
28249
|
+
at[0] - rat0,
|
|
28250
|
+
at[1] - rat1,
|
|
28251
|
+
at[2] - rat2,
|
|
28252
|
+
1
|
|
28253
|
+
];
|
|
28254
|
+
}
|
|
28255
|
+
function opFrame(op) {
|
|
28256
|
+
if (op.op === "translate") return translationFrame(op.v);
|
|
28257
|
+
return rotationFrame(op.angleDeg, op.axis ?? DEFAULT_AXIS, op.at ?? ORIGIN);
|
|
28258
|
+
}
|
|
28259
|
+
/**
|
|
28260
|
+
* Composes an authored `TransformOp` chain into one frame, matching families'
|
|
28261
|
+
* `applyOps`: `ops[0]` is innermost (applied first). For `[A, B]` the composed
|
|
28262
|
+
* motion is `B . A`, so a point transforms as `B(A(p))`.
|
|
28263
|
+
*/
|
|
28264
|
+
function frameFromOps(ops) {
|
|
28265
|
+
let m = IDENTITY_FRAME;
|
|
28266
|
+
for (const op of ops) m = frameMul(opFrame(op), m);
|
|
28267
|
+
return m;
|
|
28268
|
+
}
|
|
28269
|
+
/** Rigid inverse of `[R | t]`: `[R^T | -R^T t]`. */
|
|
28270
|
+
function frameInverse(f) {
|
|
28271
|
+
const t0 = f[12] ?? 0;
|
|
28272
|
+
const t1 = f[13] ?? 0;
|
|
28273
|
+
const t2 = f[14] ?? 0;
|
|
28274
|
+
const inv0 = -(f[0] * t0 + f[1] * t1 + f[2] * t2);
|
|
28275
|
+
const inv1 = -(f[4] * t0 + f[5] * t1 + f[6] * t2);
|
|
28276
|
+
const inv2 = -(f[8] * t0 + f[9] * t1 + f[10] * t2);
|
|
28277
|
+
return [
|
|
28278
|
+
f[0],
|
|
28279
|
+
f[4],
|
|
28280
|
+
f[8],
|
|
28281
|
+
0,
|
|
28282
|
+
f[1],
|
|
28283
|
+
f[5],
|
|
28284
|
+
f[9],
|
|
28285
|
+
0,
|
|
28286
|
+
f[2],
|
|
28287
|
+
f[6],
|
|
28288
|
+
f[10],
|
|
28289
|
+
0,
|
|
28290
|
+
inv0,
|
|
28291
|
+
inv1,
|
|
28292
|
+
inv2,
|
|
28293
|
+
1
|
|
28294
|
+
];
|
|
28295
|
+
}
|
|
28296
|
+
/** Decomposes a frame into origin (mm) + IFC axes (axisX, axisZ). */
|
|
28297
|
+
function decomposeFrame(f) {
|
|
28298
|
+
return decomposePlacement(f);
|
|
28299
|
+
}
|
|
28300
|
+
/** Translation column of a frame. */
|
|
28301
|
+
function frameOrigin(f) {
|
|
28302
|
+
return [
|
|
28303
|
+
f[12] ?? 0,
|
|
28304
|
+
f[13] ?? 0,
|
|
28305
|
+
f[14] ?? 0
|
|
28306
|
+
];
|
|
28307
|
+
}
|
|
28308
|
+
/** True when the rotation part is the identity (within tolerance): a pure
|
|
28309
|
+
* translation, so the existing translation-only placement path suffices. */
|
|
28310
|
+
function isPureTranslation(f, eps = 1e-9) {
|
|
28311
|
+
return Math.abs((f[0] ?? 1) - 1) < eps && Math.abs((f[5] ?? 1) - 1) < eps && Math.abs((f[10] ?? 1) - 1) < eps && Math.abs(f[1] ?? 0) < eps && Math.abs(f[2] ?? 0) < eps && Math.abs(f[4] ?? 0) < eps && Math.abs(f[6] ?? 0) < eps && Math.abs(f[8] ?? 0) < eps && Math.abs(f[9] ?? 0) < eps;
|
|
28312
|
+
}
|
|
28313
|
+
//#endregion
|
|
27934
28314
|
//#region src/familiesAdapter.ts
|
|
27935
28315
|
/**
|
|
27936
28316
|
* brepjs-families -> BimModel adapter. Consumes a resolved element tree and
|
|
@@ -28152,6 +28532,36 @@ function centreOrigin(base, shift) {
|
|
|
28152
28532
|
origin[2] + shift[2]
|
|
28153
28533
|
];
|
|
28154
28534
|
}
|
|
28535
|
+
function synthesizedProfile(el) {
|
|
28536
|
+
if (el.semantics?.kind !== "product" || el.props["profile"] !== void 0) return void 0;
|
|
28537
|
+
const length = semanticDimension(el, "length");
|
|
28538
|
+
const width = semanticDimension(el, "width");
|
|
28539
|
+
const height = semanticDimension(el, "height");
|
|
28540
|
+
if (el.semantics.category === "beam" && width !== void 0 && height !== void 0) return {
|
|
28541
|
+
profile: {
|
|
28542
|
+
kind: "RECTANGULAR",
|
|
28543
|
+
width,
|
|
28544
|
+
height
|
|
28545
|
+
},
|
|
28546
|
+
shift: [
|
|
28547
|
+
0,
|
|
28548
|
+
width / 2,
|
|
28549
|
+
height / 2
|
|
28550
|
+
]
|
|
28551
|
+
};
|
|
28552
|
+
if (el.semantics.category === "column" && length !== void 0 && width !== void 0) return {
|
|
28553
|
+
profile: {
|
|
28554
|
+
kind: "RECTANGULAR",
|
|
28555
|
+
width: length,
|
|
28556
|
+
height: width
|
|
28557
|
+
},
|
|
28558
|
+
shift: [
|
|
28559
|
+
length / 2,
|
|
28560
|
+
width / 2,
|
|
28561
|
+
0
|
|
28562
|
+
]
|
|
28563
|
+
};
|
|
28564
|
+
}
|
|
28155
28565
|
/**
|
|
28156
28566
|
* Adapts target-independent civil envelope dimensions onto existing typed BIM
|
|
28157
28567
|
* spec inputs. Reference infrastructure Families intentionally author domain
|
|
@@ -28166,18 +28576,7 @@ function civilProductSpecInput(el) {
|
|
|
28166
28576
|
const height = semanticDimension(el, "height");
|
|
28167
28577
|
switch (el.semantics.category) {
|
|
28168
28578
|
case "beam": {
|
|
28169
|
-
const synthesized =
|
|
28170
|
-
profile: {
|
|
28171
|
-
kind: "RECTANGULAR",
|
|
28172
|
-
width,
|
|
28173
|
-
height
|
|
28174
|
-
},
|
|
28175
|
-
shift: [
|
|
28176
|
-
0,
|
|
28177
|
-
width / 2,
|
|
28178
|
-
height / 2
|
|
28179
|
-
]
|
|
28180
|
-
} : void 0;
|
|
28579
|
+
const synthesized = synthesizedProfile(el);
|
|
28181
28580
|
return {
|
|
28182
28581
|
...base,
|
|
28183
28582
|
...synthesized !== void 0 ? {
|
|
@@ -28189,18 +28588,7 @@ function civilProductSpecInput(el) {
|
|
|
28189
28588
|
};
|
|
28190
28589
|
}
|
|
28191
28590
|
case "column": {
|
|
28192
|
-
const synthesized =
|
|
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;
|
|
28591
|
+
const synthesized = synthesizedProfile(el);
|
|
28204
28592
|
return {
|
|
28205
28593
|
...base,
|
|
28206
28594
|
...synthesized !== void 0 ? {
|
|
@@ -28267,12 +28655,12 @@ function peelTranslates(node) {
|
|
|
28267
28655
|
moved
|
|
28268
28656
|
};
|
|
28269
28657
|
}
|
|
28270
|
-
/** True when the element's rendered local transform carries a rotation
|
|
28271
|
-
*
|
|
28272
|
-
*
|
|
28273
|
-
*
|
|
28274
|
-
*
|
|
28275
|
-
*
|
|
28658
|
+
/** True when the element's rendered local transform carries a rotation, which
|
|
28659
|
+
* switches the walk to the rigid-frame placement path (the composed rotation
|
|
28660
|
+
* folds into origin + axisX + axisZ). Only authored `transform` rotations
|
|
28661
|
+
* count: a rotation a family render bakes into its own body geometry (e.g. a
|
|
28662
|
+
* circular beam oriented along axisX) stays in the body, which the spec
|
|
28663
|
+
* rebuilds parametrically from props. */
|
|
28276
28664
|
function hasRotateOp(el) {
|
|
28277
28665
|
return el.localTransforms.some((op) => op.op === "rotate");
|
|
28278
28666
|
}
|
|
@@ -28387,7 +28775,7 @@ function materializeOwnedSolid(el, evaluator, errors) {
|
|
|
28387
28775
|
/** Materialize an unrouted element's IR and add it as a proxy. The body is
|
|
28388
28776
|
* authoritative for a proxy (no parametric spec to diverge from), so baked
|
|
28389
28777
|
* transforms — rotations included — are fine here. */
|
|
28390
|
-
function addProxyElement(model, el, evaluator,
|
|
28778
|
+
function addProxyElement(model, el, evaluator, spatialFrame) {
|
|
28391
28779
|
const body = materializeOwnedSolid(el, evaluator, {
|
|
28392
28780
|
evalCode: "FAMILIES_PROXY_EVAL_FAILED",
|
|
28393
28781
|
notSolidCode: "FAMILIES_PROXY_NOT_SOLID",
|
|
@@ -28395,7 +28783,7 @@ function addProxyElement(model, el, evaluator, projectedSpatialTranslation) {
|
|
|
28395
28783
|
routeName: "proxy"
|
|
28396
28784
|
});
|
|
28397
28785
|
if (!body.ok) return body;
|
|
28398
|
-
const localized =
|
|
28786
|
+
const localized = localizeBodyToFrame(el, body.value, spatialFrame, "FAMILIES_PROXY_LOCALIZE_FAILED", "spatial parent");
|
|
28399
28787
|
if (!localized.ok) return localized;
|
|
28400
28788
|
const nameAttr = el.attributes["name"];
|
|
28401
28789
|
const materialProp = el.props["materialName"];
|
|
@@ -28558,8 +28946,9 @@ var DEFAULT_AXIS_Z = [
|
|
|
28558
28946
|
function axisEquals(value, axis) {
|
|
28559
28947
|
return Array.isArray(value) && value.length === 3 && value.every((component, i) => typeof component === "number" && component === axis[i]);
|
|
28560
28948
|
}
|
|
28561
|
-
/**
|
|
28562
|
-
*
|
|
28949
|
+
/** True when a civil node authors non-default `axisX`/`axisZ` props: like a
|
|
28950
|
+
* tRotate, this puts the node (and its subtree) on the rigid-frame placement
|
|
28951
|
+
* path so descendants relativize against the rotated parent frame. */
|
|
28563
28952
|
function hasRotatedAxes(el) {
|
|
28564
28953
|
const axisX = el.props["axisX"];
|
|
28565
28954
|
const axisZ = el.props["axisZ"];
|
|
@@ -28598,6 +28987,131 @@ function authoredTranslation(el) {
|
|
|
28598
28987
|
}
|
|
28599
28988
|
return total;
|
|
28600
28989
|
}
|
|
28990
|
+
function authoredAxisX(el) {
|
|
28991
|
+
const v = el.props["axisX"];
|
|
28992
|
+
return Array.isArray(v) && v.length === 3 ? v : DEFAULT_AXIS_X;
|
|
28993
|
+
}
|
|
28994
|
+
function authoredAxisZ(el) {
|
|
28995
|
+
const v = el.props["axisZ"];
|
|
28996
|
+
return Array.isArray(v) && v.length === 3 ? v : DEFAULT_AXIS_Z;
|
|
28997
|
+
}
|
|
28998
|
+
/** The placement shift that centres a synthesized beam/column cross-section on
|
|
28999
|
+
* its axis, in the element's own (body) frame. Zero for every other route. */
|
|
29000
|
+
function specLocalShift(el) {
|
|
29001
|
+
return synthesizedProfile(el)?.shift ?? [
|
|
29002
|
+
0,
|
|
29003
|
+
0,
|
|
29004
|
+
0
|
|
29005
|
+
];
|
|
29006
|
+
}
|
|
29007
|
+
/** Skip the resolver occurrence wrappers already owned by `cumulativeFrame`,
|
|
29008
|
+
* then recover only the Body's outer literal Datum translations. An unexpected
|
|
29009
|
+
* wrapper shape returns zero rather than risking an occurrence transform twice. */
|
|
29010
|
+
function bodyLocalTranslation(el, occurrenceTransformDepth) {
|
|
29011
|
+
let body = el.geometry;
|
|
29012
|
+
for (let i = 0; i < occurrenceTransformDepth; i++) {
|
|
29013
|
+
if (body.kind !== "Translate" && body.kind !== "Rotate") return ZERO_TRANSLATION;
|
|
29014
|
+
body = body.target;
|
|
29015
|
+
}
|
|
29016
|
+
return peelTranslates(body).total;
|
|
29017
|
+
}
|
|
29018
|
+
/** World frame of an element's own body: the walk's cumulative frame (all
|
|
29019
|
+
* authored transforms, ancestors + own) composed with the body's authored axes
|
|
29020
|
+
* (`axisX`/`axisZ` props) and its local origin (`origin` prop + literal Body
|
|
29021
|
+
* Datum + centring shift). Under no rotation this reduces to `composedOrigin`
|
|
29022
|
+
* + shift. */
|
|
29023
|
+
function elementBodyFrame(el, cumulativeFrame, occurrenceTransformDepth) {
|
|
29024
|
+
return frameMul(frameMul(cumulativeFrame, frameFromPlacement({
|
|
29025
|
+
origin: addTranslation(authoredSpecOrigin(el), bodyLocalTranslation(el, occurrenceTransformDepth)),
|
|
29026
|
+
axisX: authoredAxisX(el),
|
|
29027
|
+
axisZ: authoredAxisZ(el)
|
|
29028
|
+
})), translationFrame(specLocalShift(el)));
|
|
29029
|
+
}
|
|
29030
|
+
/** World frame of a civil spatial node: cumulative transforms composed with any
|
|
29031
|
+
* authored `origin`/`axisX`/`axisZ` props (identity when default). */
|
|
29032
|
+
function civilNodeFrame(el, cumulativeFrame) {
|
|
29033
|
+
return frameMul(cumulativeFrame, frameFromPlacement({
|
|
29034
|
+
origin: authoredSpecOrigin(el),
|
|
29035
|
+
axisX: authoredAxisX(el),
|
|
29036
|
+
axisZ: authoredAxisZ(el)
|
|
29037
|
+
}));
|
|
29038
|
+
}
|
|
29039
|
+
/** Places a routed element's spec input through the composed placement frame:
|
|
29040
|
+
* the flat spec input keeps its dimensions/profile/psets, but `origin`/`axisX`/
|
|
29041
|
+
* `axisZ` are recomputed from the element's world frame relative to its spatial
|
|
29042
|
+
* container. Stair/ramp flights, which carry their own per-flight frame, are
|
|
29043
|
+
* each re-placed the same way. */
|
|
29044
|
+
function rotatedRoutedInput(flatInput, el, cumulativeFrame, occurrenceTransformDepth, spatialFrame) {
|
|
29045
|
+
const toSpatial = frameInverse(spatialFrame);
|
|
29046
|
+
if (Array.isArray(flatInput["flights"])) {
|
|
29047
|
+
const elementFrame = elementBodyFrame(el, cumulativeFrame, occurrenceTransformDepth);
|
|
29048
|
+
const flights = flatInput["flights"];
|
|
29049
|
+
return {
|
|
29050
|
+
...flatInput,
|
|
29051
|
+
origin: [
|
|
29052
|
+
0,
|
|
29053
|
+
0,
|
|
29054
|
+
0
|
|
29055
|
+
],
|
|
29056
|
+
axisX: [
|
|
29057
|
+
1,
|
|
29058
|
+
0,
|
|
29059
|
+
0
|
|
29060
|
+
],
|
|
29061
|
+
axisZ: [
|
|
29062
|
+
0,
|
|
29063
|
+
0,
|
|
29064
|
+
1
|
|
29065
|
+
],
|
|
29066
|
+
flights: flights.map((flight) => {
|
|
29067
|
+
if (typeof flight !== "object" || flight === null) return flight;
|
|
29068
|
+
const f = flight;
|
|
29069
|
+
const world = frameMul(elementFrame, frameFromPlacement({
|
|
29070
|
+
origin: f["origin"] ?? [
|
|
29071
|
+
0,
|
|
29072
|
+
0,
|
|
29073
|
+
0
|
|
29074
|
+
],
|
|
29075
|
+
axisX: f["axisX"] ?? DEFAULT_AXIS_X,
|
|
29076
|
+
axisZ: f["axisZ"] ?? DEFAULT_AXIS_Z
|
|
29077
|
+
}));
|
|
29078
|
+
const placed = decomposeFrame(frameMul(toSpatial, world));
|
|
29079
|
+
return {
|
|
29080
|
+
...f,
|
|
29081
|
+
origin: placed.origin,
|
|
29082
|
+
axisX: placed.axisX,
|
|
29083
|
+
axisZ: placed.axisZ
|
|
29084
|
+
};
|
|
29085
|
+
})
|
|
29086
|
+
};
|
|
29087
|
+
}
|
|
29088
|
+
const placed = decomposeFrame(frameMul(toSpatial, elementBodyFrame(el, cumulativeFrame, occurrenceTransformDepth)));
|
|
29089
|
+
return {
|
|
29090
|
+
...flatInput,
|
|
29091
|
+
origin: placed.origin,
|
|
29092
|
+
axisX: placed.axisX,
|
|
29093
|
+
axisZ: placed.axisZ
|
|
29094
|
+
};
|
|
29095
|
+
}
|
|
29096
|
+
/** Moves an owned world-baked body into its spatial container's local frame.
|
|
29097
|
+
* A pure translation keeps the fast `translate` path (identical to the prior
|
|
29098
|
+
* behaviour); a rotated container applies the inverse frame via `applyMatrix`. */
|
|
29099
|
+
function localizeBodyToFrame(el, body, spatialFrame, errorCode, frameName) {
|
|
29100
|
+
if (isPureTranslation(spatialFrame)) return localizeOwnedSolid(el, body, frameOrigin(spatialFrame), errorCode, frameName);
|
|
29101
|
+
const inverse = decomposeFrame(frameInverse(spatialFrame));
|
|
29102
|
+
try {
|
|
29103
|
+
const localized = (0, brepjs.applyMatrix)(body, placementToMatrix(inverse));
|
|
29104
|
+
if (!localized.ok) {
|
|
29105
|
+
body[Symbol.dispose]();
|
|
29106
|
+
return (0, brepjs.err)(specError(errorCode, `familiesToBim: '${el.keyPath}' could not move its body into the ${frameName} frame`, localized.error));
|
|
29107
|
+
}
|
|
29108
|
+
body[Symbol.dispose]();
|
|
29109
|
+
return (0, brepjs.ok)(localized.value);
|
|
29110
|
+
} catch (cause) {
|
|
29111
|
+
body[Symbol.dispose]();
|
|
29112
|
+
return (0, brepjs.err)(specError(errorCode, `familiesToBim: '${el.keyPath}' could not move its body into the ${frameName} frame`, cause));
|
|
29113
|
+
}
|
|
29114
|
+
}
|
|
28601
29115
|
function civilSpatialInput(el, localTranslation) {
|
|
28602
29116
|
const semantics = el.semantics;
|
|
28603
29117
|
const composition = semantics !== void 0 && "composition" in semantics ? CIVIL_COMPOSITION[semantics.composition] : void 0;
|
|
@@ -28610,22 +29124,36 @@ function civilSpatialInput(el, localTranslation) {
|
|
|
28610
29124
|
...composition !== void 0 ? { compositionType: composition } : {}
|
|
28611
29125
|
};
|
|
28612
29126
|
}
|
|
29127
|
+
/** Relativizes a civil node's world frame against its parent civil frame into
|
|
29128
|
+
* the origin/axisX/axisZ the spatial specs consume. */
|
|
29129
|
+
function civilSpatialFrameInput(el, nodeFrame, parentFrame) {
|
|
29130
|
+
const semantics = el.semantics;
|
|
29131
|
+
const composition = semantics !== void 0 && "composition" in semantics ? CIVIL_COMPOSITION[semantics.composition] : void 0;
|
|
29132
|
+
const local = decomposeFrame(frameMul(frameInverse(parentFrame), nodeFrame));
|
|
29133
|
+
return {
|
|
29134
|
+
name: semanticName(el),
|
|
29135
|
+
origin: local.origin,
|
|
29136
|
+
axisX: local.axisX,
|
|
29137
|
+
axisZ: local.axisZ,
|
|
29138
|
+
...composition !== void 0 ? { compositionType: composition } : {}
|
|
29139
|
+
};
|
|
29140
|
+
}
|
|
28613
29141
|
function civilParentAccepts(kind, parent) {
|
|
28614
29142
|
if (kind === "site") return parent === "project";
|
|
28615
29143
|
if (kind === "bridge") return parent === "site";
|
|
28616
29144
|
return parent === "bridge" || parent === "bridge-part";
|
|
28617
29145
|
}
|
|
28618
|
-
function addCivilSpatialOccurrence(model, el, kind,
|
|
29146
|
+
function addCivilSpatialOccurrence(model, el, kind, input) {
|
|
28619
29147
|
if (kind === "site") {
|
|
28620
29148
|
if (el.semantics?.role !== "transport-site") return unsupportedCivilRole(el, "Site");
|
|
28621
|
-
const parsed = parseSiteSpec(
|
|
29149
|
+
const parsed = parseSiteSpec(input);
|
|
28622
29150
|
return parsed.ok ? model.addSite(parsed.value, { stableKey: el.keyPath }) : parsed;
|
|
28623
29151
|
}
|
|
28624
29152
|
if (kind === "bridge") {
|
|
28625
29153
|
const predefinedType = lookup(BRIDGE_ROLE, el.semantics?.role ?? "");
|
|
28626
29154
|
if (predefinedType === void 0) return unsupportedCivilRole(el, "Bridge");
|
|
28627
29155
|
const parsed = parseBridgeSpec({
|
|
28628
|
-
...
|
|
29156
|
+
...input,
|
|
28629
29157
|
predefinedType
|
|
28630
29158
|
});
|
|
28631
29159
|
return parsed.ok ? model.addBridge(parsed.value, { stableKey: el.keyPath }) : parsed;
|
|
@@ -28634,7 +29162,7 @@ function addCivilSpatialOccurrence(model, el, kind, localTranslation) {
|
|
|
28634
29162
|
const predefinedType = lookup(BRIDGE_PART_ROLE, el.semantics?.role ?? "");
|
|
28635
29163
|
if (predefinedType === void 0) return unsupportedCivilRole(el, "Bridge Part");
|
|
28636
29164
|
const parsed = parseBridgePartSpec({
|
|
28637
|
-
...
|
|
29165
|
+
...input,
|
|
28638
29166
|
predefinedType,
|
|
28639
29167
|
usageType: subdivision !== void 0 ? CIVIL_USAGE[subdivision] : "NOTDEFINED"
|
|
28640
29168
|
});
|
|
@@ -28661,7 +29189,7 @@ function relativeSpecInput(input, projectedSpatialTranslation) {
|
|
|
28661
29189
|
origin: relativeOrigin(input["origin"])
|
|
28662
29190
|
};
|
|
28663
29191
|
}
|
|
28664
|
-
function addEarthworksFillElement(model, el, evaluator,
|
|
29192
|
+
function addEarthworksFillElement(model, el, evaluator, spatialFrame) {
|
|
28665
29193
|
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
29194
|
const predefinedType = lookup(EARTHWORKS_FILL_ROLE, el.semantics.role);
|
|
28667
29195
|
if (predefinedType === void 0) return unsupportedCivilRole(el, "Earthworks Fill");
|
|
@@ -28672,7 +29200,7 @@ function addEarthworksFillElement(model, el, evaluator, projectedSpatialTranslat
|
|
|
28672
29200
|
routeName: "Earthworks Fill"
|
|
28673
29201
|
});
|
|
28674
29202
|
if (!body.ok) return body;
|
|
28675
|
-
const localized =
|
|
29203
|
+
const localized = localizeBodyToFrame(el, body.value, spatialFrame, "FAMILIES_EARTHWORKS_LOCALIZE_FAILED", "Bridge Part");
|
|
28676
29204
|
if (!localized.ok) return localized;
|
|
28677
29205
|
const specProps = collectSpecProps(el);
|
|
28678
29206
|
const authoredMaterial = el.props["materialName"];
|
|
@@ -28722,10 +29250,15 @@ function familiesToBim(root, options) {
|
|
|
28722
29250
|
const walk = (el, state) => {
|
|
28723
29251
|
const rotatedHere = state.rotated || hasRotateOp(el);
|
|
28724
29252
|
const cumulativeTranslationHere = addTranslation(state.cumulativeTranslation, authoredTranslation(el));
|
|
29253
|
+
const occurrenceTransformDepthHere = state.occurrenceTransformDepth + el.localTransforms.length;
|
|
29254
|
+
const cumulativeFrameHere = frameMul(state.cumulativeFrame, frameFromOps(el.localTransforms));
|
|
28725
29255
|
let proxiedHere = false;
|
|
29256
|
+
let nextRotated = rotatedHere;
|
|
28726
29257
|
let nextSpatialStructureId = state.spatialStructureId;
|
|
28727
29258
|
let nextCivilParent = state.civilParent;
|
|
28728
29259
|
let nextProjectedSpatialTranslation = state.projectedSpatialTranslation;
|
|
29260
|
+
let nextCumulativeFrame = cumulativeFrameHere;
|
|
29261
|
+
let nextSpatialFrame = state.spatialFrame;
|
|
28729
29262
|
const archetype = archetypeFor(el);
|
|
28730
29263
|
const effectiveArchetype = el.semantics?.kind === "product" ? civilProductArchetype(el) : archetype;
|
|
28731
29264
|
const route = specRoute(effectiveArchetype);
|
|
@@ -28734,15 +29267,19 @@ function familiesToBim(root, options) {
|
|
|
28734
29267
|
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
29268
|
const keyed = requireKeyed(el);
|
|
28736
29269
|
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
29270
|
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
|
|
28740
|
-
const
|
|
29271
|
+
const nodeFrame = civilNodeFrame(el, cumulativeFrameHere);
|
|
29272
|
+
const rotatedFrame = rotatedHere || hasRotatedAxes(el);
|
|
29273
|
+
const input = rotatedFrame ? civilSpatialFrameInput(el, nodeFrame, state.spatialFrame) : civilSpatialInput(el, subtractTranslation(cumulativeTranslationHere, state.projectedSpatialTranslation));
|
|
29274
|
+
const added = addCivilSpatialOccurrence(model, el, civilKind, input);
|
|
28741
29275
|
if (!added.ok) return added;
|
|
28742
29276
|
model.aggregate(state.spatialStructureId, added.value);
|
|
28743
29277
|
idByKeyPath.set(el.keyPath, added.value);
|
|
28744
29278
|
nextSpatialStructureId = added.value;
|
|
28745
29279
|
nextCivilParent = civilKind;
|
|
29280
|
+
nextRotated = rotatedFrame;
|
|
29281
|
+
nextCumulativeFrame = nodeFrame;
|
|
29282
|
+
nextSpatialFrame = nodeFrame;
|
|
28746
29283
|
nextProjectedSpatialTranslation = addTranslation(cumulativeTranslationHere, authoredSpecOrigin(el));
|
|
28747
29284
|
} 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
29285
|
else if (archetype === "storey") {
|
|
@@ -28763,21 +29300,22 @@ function familiesToBim(root, options) {
|
|
|
28763
29300
|
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
29301
|
const keyed = requireKeyed(el);
|
|
28765
29302
|
if (!keyed.ok) return keyed;
|
|
28766
|
-
const added = addEarthworksFillElement(model, el, bodyEvaluator, state.
|
|
29303
|
+
const added = addEarthworksFillElement(model, el, bodyEvaluator, state.spatialFrame);
|
|
28767
29304
|
if (!added.ok) return added;
|
|
28768
29305
|
model.placeIn(added.value, nextSpatialStructureId);
|
|
28769
29306
|
idByKeyPath.set(el.keyPath, added.value);
|
|
28770
29307
|
} else if (route !== void 0) {
|
|
28771
29308
|
const keyed = requireKeyed(el);
|
|
28772
29309
|
if (!keyed.ok) return keyed;
|
|
28773
|
-
if (rotatedHere) return (0, brepjs.err)(specError("FAMILIES_UNSUPPORTED_TRANSFORM", `familiesToBim: '${el.keyPath}' carries a rotated placement — the spec path folds only translations into IfcLocalPlacement; orient walls via axisX instead of tRotate`));
|
|
28774
29310
|
const voids = el.props["voids"];
|
|
28775
29311
|
if (Array.isArray(voids)) {
|
|
28776
29312
|
const openings = el.children.filter((c) => c.type === "Opening").length;
|
|
28777
29313
|
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`));
|
|
28778
29314
|
}
|
|
28779
29315
|
const routedInput = el.semantics?.kind === "product" ? civilProductSpecInput(el) : ("input" in route ? route.input : specInput)(el);
|
|
28780
|
-
const
|
|
29316
|
+
const rotatedBase = Array.isArray(routedInput["flights"]) ? specInput(el) : routedInput;
|
|
29317
|
+
const placedInput = rotatedHere ? rotatedRoutedInput(rotatedBase, el, cumulativeFrameHere, occurrenceTransformDepthHere, state.spatialFrame) : usesAuthoredCivilHierarchy ? relativeSpecInput(routedInput, state.projectedSpatialTranslation) : routedInput;
|
|
29318
|
+
const parsed = route.parse(placedInput);
|
|
28781
29319
|
if (!parsed.ok) return parsed;
|
|
28782
29320
|
const added = route.add(model, parsed.value, el.keyPath);
|
|
28783
29321
|
if (!added.ok) return added;
|
|
@@ -28794,7 +29332,7 @@ function familiesToBim(root, options) {
|
|
|
28794
29332
|
const keyed = requireKeyed(el);
|
|
28795
29333
|
if (!keyed.ok) return keyed;
|
|
28796
29334
|
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.
|
|
29335
|
+
const added = addProxyElement(model, el, options.proxyEvaluator, state.spatialFrame);
|
|
28798
29336
|
if (!added.ok) return added;
|
|
28799
29337
|
model.placeIn(added.value, nextSpatialStructureId);
|
|
28800
29338
|
idByKeyPath.set(el.keyPath, added.value);
|
|
@@ -28810,9 +29348,12 @@ function familiesToBim(root, options) {
|
|
|
28810
29348
|
const r = walk(child, {
|
|
28811
29349
|
spatialStructureId: nextSpatialStructureId,
|
|
28812
29350
|
civilParent: nextCivilParent,
|
|
28813
|
-
rotated:
|
|
29351
|
+
rotated: nextRotated,
|
|
28814
29352
|
cumulativeTranslation: cumulativeTranslationHere,
|
|
28815
|
-
projectedSpatialTranslation: nextProjectedSpatialTranslation
|
|
29353
|
+
projectedSpatialTranslation: nextProjectedSpatialTranslation,
|
|
29354
|
+
occurrenceTransformDepth: occurrenceTransformDepthHere,
|
|
29355
|
+
cumulativeFrame: nextCumulativeFrame,
|
|
29356
|
+
spatialFrame: nextSpatialFrame
|
|
28816
29357
|
});
|
|
28817
29358
|
if (!r.ok) return r;
|
|
28818
29359
|
}
|
|
@@ -28823,7 +29364,10 @@ function familiesToBim(root, options) {
|
|
|
28823
29364
|
civilParent: "project",
|
|
28824
29365
|
rotated: false,
|
|
28825
29366
|
cumulativeTranslation: ZERO_TRANSLATION,
|
|
28826
|
-
projectedSpatialTranslation: ZERO_TRANSLATION
|
|
29367
|
+
projectedSpatialTranslation: ZERO_TRANSLATION,
|
|
29368
|
+
occurrenceTransformDepth: 0,
|
|
29369
|
+
cumulativeFrame: IDENTITY_FRAME,
|
|
29370
|
+
spatialFrame: IDENTITY_FRAME
|
|
28827
29371
|
});
|
|
28828
29372
|
if (!walked.ok) {
|
|
28829
29373
|
model[Symbol.dispose]();
|