brepjs-bim 0.23.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 +230 -66
- package/dist/brepjs-bim.js +230 -66
- 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();
|
|
@@ -8779,7 +8779,7 @@ function writeWallGeometry(w, spec, geomSubContextId, parentPlacementId) {
|
|
|
8779
8779
|
value: "AREA"
|
|
8780
8780
|
},
|
|
8781
8781
|
ProfileName: null,
|
|
8782
|
-
Position: w.ref(writeAxis2Placement2D$1(w)),
|
|
8782
|
+
Position: w.ref(writeAxis2Placement2D$1(w, [thicknessM / 2, heightM / 2])),
|
|
8783
8783
|
XDim: w.mkType(web_ifc.IFCPOSITIVELENGTHMEASURE, thicknessM),
|
|
8784
8784
|
YDim: w.mkType(web_ifc.IFCPOSITIVELENGTHMEASURE, heightM)
|
|
8785
8785
|
});
|
|
@@ -9000,12 +9000,12 @@ function writeRoofGeometry(w, spec, solid, geomSubContextId, parentPlacementId)
|
|
|
9000
9000
|
usedFallback: false
|
|
9001
9001
|
};
|
|
9002
9002
|
}
|
|
9003
|
-
function writeAxis2Placement2D$1(w) {
|
|
9003
|
+
function writeAxis2Placement2D$1(w, location = [0, 0]) {
|
|
9004
9004
|
const originId = w.nextId();
|
|
9005
9005
|
w.writeLine({
|
|
9006
9006
|
expressID: originId,
|
|
9007
9007
|
type: web_ifc.IFCCARTESIANPOINT,
|
|
9008
|
-
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])]
|
|
9009
9009
|
});
|
|
9010
9010
|
const id = w.nextId();
|
|
9011
9011
|
w.writeLine({
|
|
@@ -11510,11 +11510,10 @@ function writeOpeningGeometry(w, guid, openingSpec, wallSpec, wallPlacementId, g
|
|
|
11510
11510
|
const offsetAlongWallM = toIfcLengthM(openingSpec.offsetAlongWall);
|
|
11511
11511
|
const offsetFromFloorM = toIfcLengthM(openingSpec.offsetFromFloor);
|
|
11512
11512
|
const thicknessM = toIfcLengthM(wallSpec.thickness);
|
|
11513
|
-
const wallHeightM = toIfcLengthM(wallSpec.height);
|
|
11514
11513
|
const placement3DId = writeAxis2Placement3D(w, [
|
|
11515
11514
|
offsetAlongWallM + widthM / 2,
|
|
11516
|
-
thicknessM
|
|
11517
|
-
|
|
11515
|
+
thicknessM,
|
|
11516
|
+
offsetFromFloorM + heightM / 2
|
|
11518
11517
|
], [
|
|
11519
11518
|
0,
|
|
11520
11519
|
-1,
|
|
@@ -12390,6 +12389,24 @@ var SUPPORTED_SCHEMAS = [
|
|
|
12390
12389
|
* MUST call {@link SpfReader.close} (it always issues `CloseModel`, preventing
|
|
12391
12390
|
* WASM handle leaks). The handle is `using`-friendly via `Symbol.dispose`.
|
|
12392
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
|
+
];
|
|
12393
12410
|
function deleteVector(vec) {
|
|
12394
12411
|
vec.delete?.();
|
|
12395
12412
|
}
|
|
@@ -12422,6 +12439,7 @@ var SpfReader = class SpfReader {
|
|
|
12422
12439
|
if (modelId >= 0) api.CloseModel(modelId);
|
|
12423
12440
|
return (0, brepjs.err)(importError("OPEN_MODEL_FAILED", "web-ifc OpenModel returned an invalid model id"));
|
|
12424
12441
|
}
|
|
12442
|
+
api.SetGeometryTransformation(modelId, IFC_Z_UP_TRANSFORMATION);
|
|
12425
12443
|
const schemaRaw = api.GetModelSchema(modelId);
|
|
12426
12444
|
if (!SUPPORTED_SCHEMAS.includes(schemaRaw)) {
|
|
12427
12445
|
api.CloseModel(modelId);
|
|
@@ -14121,12 +14139,14 @@ function reconstructExtrusion(reader, extrusionId, scale, worldTransform, diagno
|
|
|
14121
14139
|
diagnostics.push(issue("warning", faceResult.error.code, faceResult.error.message, extrusionId));
|
|
14122
14140
|
return NONE;
|
|
14123
14141
|
}
|
|
14142
|
+
const profileFrame = readProfilePosition(reader, sweptArea.value, scale);
|
|
14124
14143
|
const depthMm = depth * scale * 1e3;
|
|
14125
|
-
const
|
|
14144
|
+
const sweptDir = readDirection(reader, ext["ExtrudedDirection"]) ?? [
|
|
14126
14145
|
0,
|
|
14127
14146
|
0,
|
|
14128
14147
|
1
|
|
14129
14148
|
];
|
|
14149
|
+
const extrudeDir = profileFrame === null ? sweptDir : rotateByTranspose(profileFrame.linear, sweptDir);
|
|
14130
14150
|
const extrudeVec = [
|
|
14131
14151
|
extrudeDir[0] * depthMm,
|
|
14132
14152
|
extrudeDir[1] * depthMm,
|
|
@@ -14148,7 +14168,11 @@ function reconstructExtrusion(reader, extrusionId, scale, worldTransform, diagno
|
|
|
14148
14168
|
return NONE;
|
|
14149
14169
|
}
|
|
14150
14170
|
const localFrame = readAxis2Placement3D(reader, ext["Position"], scale);
|
|
14151
|
-
const placed = placeSolid(solidResult.value,
|
|
14171
|
+
const placed = placeSolid(solidResult.value, [
|
|
14172
|
+
worldTransform,
|
|
14173
|
+
localFrame,
|
|
14174
|
+
profileFrame
|
|
14175
|
+
]);
|
|
14152
14176
|
if (!placed.ok) {
|
|
14153
14177
|
diagnostics.push(issue("warning", placed.error.code, placed.error.message, extrusionId));
|
|
14154
14178
|
return NONE;
|
|
@@ -14176,16 +14200,19 @@ function reconstructRevolution(reader, revolutionId, scale, worldTransform, diag
|
|
|
14176
14200
|
return NONE;
|
|
14177
14201
|
}
|
|
14178
14202
|
const axis1 = reader.getLine(axisRef.value);
|
|
14179
|
-
const
|
|
14203
|
+
const sweptCenter = (axis1 === null ? void 0 : readPoint(reader, axis1["Location"], scale)) ?? [
|
|
14180
14204
|
0,
|
|
14181
14205
|
0,
|
|
14182
14206
|
0
|
|
14183
14207
|
];
|
|
14184
|
-
const
|
|
14208
|
+
const sweptDirection = (axis1 === null ? void 0 : readDirection(reader, axis1["Axis"])) ?? [
|
|
14185
14209
|
0,
|
|
14186
14210
|
0,
|
|
14187
14211
|
1
|
|
14188
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);
|
|
14189
14216
|
const revolved = (() => {
|
|
14190
14217
|
try {
|
|
14191
14218
|
var _usingCtx3 = _usingCtx();
|
|
@@ -14212,7 +14239,11 @@ function reconstructRevolution(reader, revolutionId, scale, worldTransform, diag
|
|
|
14212
14239
|
return NONE;
|
|
14213
14240
|
}
|
|
14214
14241
|
const localFrame = readAxis2Placement3D(reader, rev["Position"], scale);
|
|
14215
|
-
const placed = placeSolid(revolved.value,
|
|
14242
|
+
const placed = placeSolid(revolved.value, [
|
|
14243
|
+
worldTransform,
|
|
14244
|
+
localFrame,
|
|
14245
|
+
profileFrame
|
|
14246
|
+
]);
|
|
14216
14247
|
if (!placed.ok) {
|
|
14217
14248
|
diagnostics.push(issue("warning", placed.error.code, placed.error.message, revolutionId));
|
|
14218
14249
|
return NONE;
|
|
@@ -14225,20 +14256,9 @@ function reconstructTessellated(reader, productExpressId, diagnostics) {
|
|
|
14225
14256
|
diagnostics.push(issue("warning", "GEOMETRY_RECONSTRUCTION_FAILED", "Tessellated geometry yielded no triangles", productExpressId));
|
|
14226
14257
|
return NONE;
|
|
14227
14258
|
}
|
|
14228
|
-
const
|
|
14229
|
-
let solid = null;
|
|
14230
|
-
try {
|
|
14231
|
-
const cast = (0, brepjs.castShape)((0, brepjs.getKernel)().importSTL(stl.buffer));
|
|
14232
|
-
if ((0, brepjs.isSolid)(cast)) {
|
|
14233
|
-
const valid = (0, brepjs.validSolid)(cast);
|
|
14234
|
-
if (valid.ok) solid = valid.value;
|
|
14235
|
-
else cast[Symbol.dispose]();
|
|
14236
|
-
} else cast[Symbol.dispose]();
|
|
14237
|
-
} catch (e) {
|
|
14238
|
-
diagnostics.push(issue("info", "TESSELLATION_NOT_MANIFOLD", `STL round-trip failed: ${errMsg$1(e)}`, productExpressId));
|
|
14239
|
-
}
|
|
14259
|
+
const solid = sewMeshToSolid(mesh, 1e3, productExpressId, diagnostics);
|
|
14240
14260
|
if (solid !== null) {
|
|
14241
|
-
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));
|
|
14242
14262
|
return {
|
|
14243
14263
|
kind: "SOLID",
|
|
14244
14264
|
solid,
|
|
@@ -14253,6 +14273,52 @@ function reconstructTessellated(reader, productExpressId, diagnostics) {
|
|
|
14253
14273
|
diagnostic: "TESSELLATED_LOSSY"
|
|
14254
14274
|
};
|
|
14255
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
|
+
}
|
|
14256
14322
|
function collectMesh(reader, productExpressId) {
|
|
14257
14323
|
const vertices = [];
|
|
14258
14324
|
const indices = [];
|
|
@@ -14281,7 +14347,8 @@ function collectMesh(reader, productExpressId) {
|
|
|
14281
14347
|
}
|
|
14282
14348
|
}
|
|
14283
14349
|
} finally {
|
|
14284
|
-
flatMesh.
|
|
14350
|
+
releaseEmbind(flatMesh.geometries);
|
|
14351
|
+
releaseEmbind(flatMesh);
|
|
14285
14352
|
}
|
|
14286
14353
|
});
|
|
14287
14354
|
if (vertices.length === 0) return null;
|
|
@@ -14290,25 +14357,6 @@ function collectMesh(reader, productExpressId) {
|
|
|
14290
14357
|
indices: new Uint32Array(indices)
|
|
14291
14358
|
};
|
|
14292
14359
|
}
|
|
14293
|
-
function packBinaryStl(vertices, indices, scaleToMm) {
|
|
14294
|
-
const triCount = Math.floor(indices.length / 3);
|
|
14295
|
-
const buffer = /* @__PURE__ */ new ArrayBuffer(84 + triCount * 50);
|
|
14296
|
-
const view = new DataView(buffer);
|
|
14297
|
-
view.setUint32(80, triCount, true);
|
|
14298
|
-
let offset = 84;
|
|
14299
|
-
for (let t = 0; t < triCount; t++) {
|
|
14300
|
-
offset += 12;
|
|
14301
|
-
for (let c = 0; c < 3; c++) {
|
|
14302
|
-
const vi = (indices[t * 3 + c] ?? 0) * 3;
|
|
14303
|
-
view.setFloat32(offset, (vertices[vi] ?? 0) * scaleToMm, true);
|
|
14304
|
-
view.setFloat32(offset + 4, (vertices[vi + 1] ?? 0) * scaleToMm, true);
|
|
14305
|
-
view.setFloat32(offset + 8, (vertices[vi + 2] ?? 0) * scaleToMm, true);
|
|
14306
|
-
offset += 12;
|
|
14307
|
-
}
|
|
14308
|
-
offset += 2;
|
|
14309
|
-
}
|
|
14310
|
-
return new Uint8Array(buffer);
|
|
14311
|
-
}
|
|
14312
14360
|
var PARAMETRIC_PROFILE_BUILDERS = /* @__PURE__ */ new Map([
|
|
14313
14361
|
[web_ifc.IFCRECTANGLEPROFILEDEF, (f) => ({
|
|
14314
14362
|
kind: "RECTANGULAR",
|
|
@@ -14452,19 +14500,99 @@ function profileToFace(profile) {
|
|
|
14452
14500
|
if (!face.ok) return (0, brepjs.err)(importError("GEOMETRY_RECONSTRUCTION_FAILED", `Profile face build failed: ${face.error.message}`));
|
|
14453
14501
|
return (0, brepjs.ok)(face.value);
|
|
14454
14502
|
}
|
|
14455
|
-
|
|
14456
|
-
|
|
14457
|
-
|
|
14458
|
-
|
|
14459
|
-
|
|
14460
|
-
|
|
14461
|
-
|
|
14462
|
-
|
|
14463
|
-
|
|
14464
|
-
|
|
14465
|
-
|
|
14466
|
-
|
|
14467
|
-
|
|
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
|
+
]);
|
|
14468
14596
|
}
|
|
14469
14597
|
function finalizeSolid(solid, entity, diagnostics) {
|
|
14470
14598
|
if (!(0, brepjs.isSolid)(solid)) {
|
|
@@ -14613,6 +14741,24 @@ function readPolylinePoints(reader, ref, scale) {
|
|
|
14613
14741
|
}
|
|
14614
14742
|
return out.length >= 3 ? out : void 0;
|
|
14615
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
|
+
}
|
|
14616
14762
|
function readPoint(reader, ref, scale) {
|
|
14617
14763
|
const pointRef = asRef(ref);
|
|
14618
14764
|
if (pointRef === void 0) return void 0;
|
|
@@ -14639,6 +14785,9 @@ function readDirection(reader, ref) {
|
|
|
14639
14785
|
ratios[2] ?? 0
|
|
14640
14786
|
];
|
|
14641
14787
|
}
|
|
14788
|
+
function releaseEmbind(value) {
|
|
14789
|
+
value.delete?.();
|
|
14790
|
+
}
|
|
14642
14791
|
function asRef(value) {
|
|
14643
14792
|
if (value !== null && typeof value === "object" && "value" in value) {
|
|
14644
14793
|
const v = value.value;
|
|
@@ -28855,13 +29004,25 @@ function specLocalShift(el) {
|
|
|
28855
29004
|
0
|
|
28856
29005
|
];
|
|
28857
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
|
+
}
|
|
28858
29018
|
/** World frame of an element's own body: the walk's cumulative frame (all
|
|
28859
29019
|
* authored transforms, ancestors + own) composed with the body's authored axes
|
|
28860
|
-
* (`axisX`/`axisZ` props) and its local origin (`origin` prop +
|
|
28861
|
-
* shift). Under no rotation this reduces to `composedOrigin`
|
|
28862
|
-
|
|
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) {
|
|
28863
29024
|
return frameMul(frameMul(cumulativeFrame, frameFromPlacement({
|
|
28864
|
-
origin: authoredSpecOrigin(el),
|
|
29025
|
+
origin: addTranslation(authoredSpecOrigin(el), bodyLocalTranslation(el, occurrenceTransformDepth)),
|
|
28865
29026
|
axisX: authoredAxisX(el),
|
|
28866
29027
|
axisZ: authoredAxisZ(el)
|
|
28867
29028
|
})), translationFrame(specLocalShift(el)));
|
|
@@ -28880,10 +29041,10 @@ function civilNodeFrame(el, cumulativeFrame) {
|
|
|
28880
29041
|
* `axisZ` are recomputed from the element's world frame relative to its spatial
|
|
28881
29042
|
* container. Stair/ramp flights, which carry their own per-flight frame, are
|
|
28882
29043
|
* each re-placed the same way. */
|
|
28883
|
-
function rotatedRoutedInput(flatInput, el, cumulativeFrame, spatialFrame) {
|
|
29044
|
+
function rotatedRoutedInput(flatInput, el, cumulativeFrame, occurrenceTransformDepth, spatialFrame) {
|
|
28884
29045
|
const toSpatial = frameInverse(spatialFrame);
|
|
28885
29046
|
if (Array.isArray(flatInput["flights"])) {
|
|
28886
|
-
const elementFrame = elementBodyFrame(el, cumulativeFrame);
|
|
29047
|
+
const elementFrame = elementBodyFrame(el, cumulativeFrame, occurrenceTransformDepth);
|
|
28887
29048
|
const flights = flatInput["flights"];
|
|
28888
29049
|
return {
|
|
28889
29050
|
...flatInput,
|
|
@@ -28924,7 +29085,7 @@ function rotatedRoutedInput(flatInput, el, cumulativeFrame, spatialFrame) {
|
|
|
28924
29085
|
})
|
|
28925
29086
|
};
|
|
28926
29087
|
}
|
|
28927
|
-
const placed = decomposeFrame(frameMul(toSpatial, elementBodyFrame(el, cumulativeFrame)));
|
|
29088
|
+
const placed = decomposeFrame(frameMul(toSpatial, elementBodyFrame(el, cumulativeFrame, occurrenceTransformDepth)));
|
|
28928
29089
|
return {
|
|
28929
29090
|
...flatInput,
|
|
28930
29091
|
origin: placed.origin,
|
|
@@ -29089,6 +29250,7 @@ function familiesToBim(root, options) {
|
|
|
29089
29250
|
const walk = (el, state) => {
|
|
29090
29251
|
const rotatedHere = state.rotated || hasRotateOp(el);
|
|
29091
29252
|
const cumulativeTranslationHere = addTranslation(state.cumulativeTranslation, authoredTranslation(el));
|
|
29253
|
+
const occurrenceTransformDepthHere = state.occurrenceTransformDepth + el.localTransforms.length;
|
|
29092
29254
|
const cumulativeFrameHere = frameMul(state.cumulativeFrame, frameFromOps(el.localTransforms));
|
|
29093
29255
|
let proxiedHere = false;
|
|
29094
29256
|
let nextRotated = rotatedHere;
|
|
@@ -29152,7 +29314,7 @@ function familiesToBim(root, options) {
|
|
|
29152
29314
|
}
|
|
29153
29315
|
const routedInput = el.semantics?.kind === "product" ? civilProductSpecInput(el) : ("input" in route ? route.input : specInput)(el);
|
|
29154
29316
|
const rotatedBase = Array.isArray(routedInput["flights"]) ? specInput(el) : routedInput;
|
|
29155
|
-
const placedInput = rotatedHere ? rotatedRoutedInput(rotatedBase, el, cumulativeFrameHere, state.spatialFrame) : usesAuthoredCivilHierarchy ? relativeSpecInput(routedInput, state.projectedSpatialTranslation) : routedInput;
|
|
29317
|
+
const placedInput = rotatedHere ? rotatedRoutedInput(rotatedBase, el, cumulativeFrameHere, occurrenceTransformDepthHere, state.spatialFrame) : usesAuthoredCivilHierarchy ? relativeSpecInput(routedInput, state.projectedSpatialTranslation) : routedInput;
|
|
29156
29318
|
const parsed = route.parse(placedInput);
|
|
29157
29319
|
if (!parsed.ok) return parsed;
|
|
29158
29320
|
const added = route.add(model, parsed.value, el.keyPath);
|
|
@@ -29189,6 +29351,7 @@ function familiesToBim(root, options) {
|
|
|
29189
29351
|
rotated: nextRotated,
|
|
29190
29352
|
cumulativeTranslation: cumulativeTranslationHere,
|
|
29191
29353
|
projectedSpatialTranslation: nextProjectedSpatialTranslation,
|
|
29354
|
+
occurrenceTransformDepth: occurrenceTransformDepthHere,
|
|
29192
29355
|
cumulativeFrame: nextCumulativeFrame,
|
|
29193
29356
|
spatialFrame: nextSpatialFrame
|
|
29194
29357
|
});
|
|
@@ -29202,6 +29365,7 @@ function familiesToBim(root, options) {
|
|
|
29202
29365
|
rotated: false,
|
|
29203
29366
|
cumulativeTranslation: ZERO_TRANSLATION,
|
|
29204
29367
|
projectedSpatialTranslation: ZERO_TRANSLATION,
|
|
29368
|
+
occurrenceTransformDepth: 0,
|
|
29205
29369
|
cumulativeFrame: IDENTITY_FRAME,
|
|
29206
29370
|
spatialFrame: IDENTITY_FRAME
|
|
29207
29371
|
});
|
package/dist/brepjs-bim.js
CHANGED
|
@@ -183,7 +183,7 @@ function fromBrepError(inner, code, message) {
|
|
|
183
183
|
};
|
|
184
184
|
}
|
|
185
185
|
//#endregion
|
|
186
|
-
//#region \0@oxc-project+runtime@0.
|
|
186
|
+
//#region \0@oxc-project+runtime@0.146.0/helpers/esm/usingCtx.js
|
|
187
187
|
function _usingCtx() {
|
|
188
188
|
var r = "function" == typeof SuppressedError ? SuppressedError : function(r, e) {
|
|
189
189
|
var n = Error();
|
|
@@ -8756,7 +8756,7 @@ function writeWallGeometry(w, spec, geomSubContextId, parentPlacementId) {
|
|
|
8756
8756
|
value: "AREA"
|
|
8757
8757
|
},
|
|
8758
8758
|
ProfileName: null,
|
|
8759
|
-
Position: w.ref(writeAxis2Placement2D$1(w)),
|
|
8759
|
+
Position: w.ref(writeAxis2Placement2D$1(w, [thicknessM / 2, heightM / 2])),
|
|
8760
8760
|
XDim: w.mkType(WebIFC.IFCPOSITIVELENGTHMEASURE, thicknessM),
|
|
8761
8761
|
YDim: w.mkType(WebIFC.IFCPOSITIVELENGTHMEASURE, heightM)
|
|
8762
8762
|
});
|
|
@@ -8977,12 +8977,12 @@ function writeRoofGeometry(w, spec, solid, geomSubContextId, parentPlacementId)
|
|
|
8977
8977
|
usedFallback: false
|
|
8978
8978
|
};
|
|
8979
8979
|
}
|
|
8980
|
-
function writeAxis2Placement2D$1(w) {
|
|
8980
|
+
function writeAxis2Placement2D$1(w, location = [0, 0]) {
|
|
8981
8981
|
const originId = w.nextId();
|
|
8982
8982
|
w.writeLine({
|
|
8983
8983
|
expressID: originId,
|
|
8984
8984
|
type: WebIFC.IFCCARTESIANPOINT,
|
|
8985
|
-
Coordinates: [w.mkType(WebIFC.IFCLENGTHMEASURE, 0), w.mkType(WebIFC.IFCLENGTHMEASURE,
|
|
8985
|
+
Coordinates: [w.mkType(WebIFC.IFCLENGTHMEASURE, location[0]), w.mkType(WebIFC.IFCLENGTHMEASURE, location[1])]
|
|
8986
8986
|
});
|
|
8987
8987
|
const id = w.nextId();
|
|
8988
8988
|
w.writeLine({
|
|
@@ -11487,11 +11487,10 @@ function writeOpeningGeometry(w, guid, openingSpec, wallSpec, wallPlacementId, g
|
|
|
11487
11487
|
const offsetAlongWallM = toIfcLengthM(openingSpec.offsetAlongWall);
|
|
11488
11488
|
const offsetFromFloorM = toIfcLengthM(openingSpec.offsetFromFloor);
|
|
11489
11489
|
const thicknessM = toIfcLengthM(wallSpec.thickness);
|
|
11490
|
-
const wallHeightM = toIfcLengthM(wallSpec.height);
|
|
11491
11490
|
const placement3DId = writeAxis2Placement3D(w, [
|
|
11492
11491
|
offsetAlongWallM + widthM / 2,
|
|
11493
|
-
thicknessM
|
|
11494
|
-
|
|
11492
|
+
thicknessM,
|
|
11493
|
+
offsetFromFloorM + heightM / 2
|
|
11495
11494
|
], [
|
|
11496
11495
|
0,
|
|
11497
11496
|
-1,
|
|
@@ -12367,6 +12366,24 @@ var SUPPORTED_SCHEMAS = [
|
|
|
12367
12366
|
* MUST call {@link SpfReader.close} (it always issues `CloseModel`, preventing
|
|
12368
12367
|
* WASM handle leaks). The handle is `using`-friendly via `Symbol.dispose`.
|
|
12369
12368
|
*/
|
|
12369
|
+
var IFC_Z_UP_TRANSFORMATION = [
|
|
12370
|
+
1,
|
|
12371
|
+
0,
|
|
12372
|
+
0,
|
|
12373
|
+
0,
|
|
12374
|
+
0,
|
|
12375
|
+
0,
|
|
12376
|
+
1,
|
|
12377
|
+
0,
|
|
12378
|
+
0,
|
|
12379
|
+
-1,
|
|
12380
|
+
0,
|
|
12381
|
+
0,
|
|
12382
|
+
0,
|
|
12383
|
+
0,
|
|
12384
|
+
0,
|
|
12385
|
+
1
|
|
12386
|
+
];
|
|
12370
12387
|
function deleteVector(vec) {
|
|
12371
12388
|
vec.delete?.();
|
|
12372
12389
|
}
|
|
@@ -12399,6 +12416,7 @@ var SpfReader = class SpfReader {
|
|
|
12399
12416
|
if (modelId >= 0) api.CloseModel(modelId);
|
|
12400
12417
|
return err(importError("OPEN_MODEL_FAILED", "web-ifc OpenModel returned an invalid model id"));
|
|
12401
12418
|
}
|
|
12419
|
+
api.SetGeometryTransformation(modelId, IFC_Z_UP_TRANSFORMATION);
|
|
12402
12420
|
const schemaRaw = api.GetModelSchema(modelId);
|
|
12403
12421
|
if (!SUPPORTED_SCHEMAS.includes(schemaRaw)) {
|
|
12404
12422
|
api.CloseModel(modelId);
|
|
@@ -14098,12 +14116,14 @@ function reconstructExtrusion(reader, extrusionId, scale, worldTransform, diagno
|
|
|
14098
14116
|
diagnostics.push(issue("warning", faceResult.error.code, faceResult.error.message, extrusionId));
|
|
14099
14117
|
return NONE;
|
|
14100
14118
|
}
|
|
14119
|
+
const profileFrame = readProfilePosition(reader, sweptArea.value, scale);
|
|
14101
14120
|
const depthMm = depth * scale * 1e3;
|
|
14102
|
-
const
|
|
14121
|
+
const sweptDir = readDirection(reader, ext["ExtrudedDirection"]) ?? [
|
|
14103
14122
|
0,
|
|
14104
14123
|
0,
|
|
14105
14124
|
1
|
|
14106
14125
|
];
|
|
14126
|
+
const extrudeDir = profileFrame === null ? sweptDir : rotateByTranspose(profileFrame.linear, sweptDir);
|
|
14107
14127
|
const extrudeVec = [
|
|
14108
14128
|
extrudeDir[0] * depthMm,
|
|
14109
14129
|
extrudeDir[1] * depthMm,
|
|
@@ -14125,7 +14145,11 @@ function reconstructExtrusion(reader, extrusionId, scale, worldTransform, diagno
|
|
|
14125
14145
|
return NONE;
|
|
14126
14146
|
}
|
|
14127
14147
|
const localFrame = readAxis2Placement3D(reader, ext["Position"], scale);
|
|
14128
|
-
const placed = placeSolid(solidResult.value,
|
|
14148
|
+
const placed = placeSolid(solidResult.value, [
|
|
14149
|
+
worldTransform,
|
|
14150
|
+
localFrame,
|
|
14151
|
+
profileFrame
|
|
14152
|
+
]);
|
|
14129
14153
|
if (!placed.ok) {
|
|
14130
14154
|
diagnostics.push(issue("warning", placed.error.code, placed.error.message, extrusionId));
|
|
14131
14155
|
return NONE;
|
|
@@ -14153,16 +14177,19 @@ function reconstructRevolution(reader, revolutionId, scale, worldTransform, diag
|
|
|
14153
14177
|
return NONE;
|
|
14154
14178
|
}
|
|
14155
14179
|
const axis1 = reader.getLine(axisRef.value);
|
|
14156
|
-
const
|
|
14180
|
+
const sweptCenter = (axis1 === null ? void 0 : readPoint(reader, axis1["Location"], scale)) ?? [
|
|
14157
14181
|
0,
|
|
14158
14182
|
0,
|
|
14159
14183
|
0
|
|
14160
14184
|
];
|
|
14161
|
-
const
|
|
14185
|
+
const sweptDirection = (axis1 === null ? void 0 : readDirection(reader, axis1["Axis"])) ?? [
|
|
14162
14186
|
0,
|
|
14163
14187
|
0,
|
|
14164
14188
|
1
|
|
14165
14189
|
];
|
|
14190
|
+
const profileFrame = readProfilePosition(reader, sweptArea.value, scale);
|
|
14191
|
+
const center = profileFrame === null ? sweptCenter : inverseRigidPoint(profileFrame, sweptCenter);
|
|
14192
|
+
const direction = profileFrame === null ? sweptDirection : rotateByTranspose(profileFrame.linear, sweptDirection);
|
|
14166
14193
|
const revolved = (() => {
|
|
14167
14194
|
try {
|
|
14168
14195
|
var _usingCtx3 = _usingCtx();
|
|
@@ -14189,7 +14216,11 @@ function reconstructRevolution(reader, revolutionId, scale, worldTransform, diag
|
|
|
14189
14216
|
return NONE;
|
|
14190
14217
|
}
|
|
14191
14218
|
const localFrame = readAxis2Placement3D(reader, rev["Position"], scale);
|
|
14192
|
-
const placed = placeSolid(revolved.value,
|
|
14219
|
+
const placed = placeSolid(revolved.value, [
|
|
14220
|
+
worldTransform,
|
|
14221
|
+
localFrame,
|
|
14222
|
+
profileFrame
|
|
14223
|
+
]);
|
|
14193
14224
|
if (!placed.ok) {
|
|
14194
14225
|
diagnostics.push(issue("warning", placed.error.code, placed.error.message, revolutionId));
|
|
14195
14226
|
return NONE;
|
|
@@ -14202,20 +14233,9 @@ function reconstructTessellated(reader, productExpressId, diagnostics) {
|
|
|
14202
14233
|
diagnostics.push(issue("warning", "GEOMETRY_RECONSTRUCTION_FAILED", "Tessellated geometry yielded no triangles", productExpressId));
|
|
14203
14234
|
return NONE;
|
|
14204
14235
|
}
|
|
14205
|
-
const
|
|
14206
|
-
let solid = null;
|
|
14207
|
-
try {
|
|
14208
|
-
const cast = castShape(getKernel().importSTL(stl.buffer));
|
|
14209
|
-
if (isSolid(cast)) {
|
|
14210
|
-
const valid = validSolid(cast);
|
|
14211
|
-
if (valid.ok) solid = valid.value;
|
|
14212
|
-
else cast[Symbol.dispose]();
|
|
14213
|
-
} else cast[Symbol.dispose]();
|
|
14214
|
-
} catch (e) {
|
|
14215
|
-
diagnostics.push(issue("info", "TESSELLATION_NOT_MANIFOLD", `STL round-trip failed: ${errMsg$1(e)}`, productExpressId));
|
|
14216
|
-
}
|
|
14236
|
+
const solid = sewMeshToSolid(mesh, 1e3, productExpressId, diagnostics);
|
|
14217
14237
|
if (solid !== null) {
|
|
14218
|
-
diagnostics.push(issue("info", "TESSELLATED_MANIFOLD", "Tessellated mesh recovered as a closed solid
|
|
14238
|
+
diagnostics.push(issue("info", "TESSELLATED_MANIFOLD", "Tessellated mesh recovered as a closed solid by sewing its triangles", productExpressId));
|
|
14219
14239
|
return {
|
|
14220
14240
|
kind: "SOLID",
|
|
14221
14241
|
solid,
|
|
@@ -14230,6 +14250,52 @@ function reconstructTessellated(reader, productExpressId, diagnostics) {
|
|
|
14230
14250
|
diagnostic: "TESSELLATED_LOSSY"
|
|
14231
14251
|
};
|
|
14232
14252
|
}
|
|
14253
|
+
/** Loose enough to weld float32 vertices web-ifc emits per face, in mm. */
|
|
14254
|
+
var MESH_SEW_TOLERANCE_MM = .001;
|
|
14255
|
+
/**
|
|
14256
|
+
* Brands a fresh kernel result and releases its pre-downcast arena slot when
|
|
14257
|
+
* the cast moved to a new one (occt-wasm); in-place kernels share the slot and
|
|
14258
|
+
* are left alone. Mirrors brepjs's internal castResultShape.
|
|
14259
|
+
*/
|
|
14260
|
+
function castFreshResult(raw) {
|
|
14261
|
+
const cast = castShape(raw);
|
|
14262
|
+
const rawId = raw.id;
|
|
14263
|
+
const castId = cast.wrapped.id;
|
|
14264
|
+
if (!(rawId !== void 0 && castId !== void 0 ? rawId === castId : cast.wrapped === raw)) getKernel().dispose(raw);
|
|
14265
|
+
return cast;
|
|
14266
|
+
}
|
|
14267
|
+
/**
|
|
14268
|
+
* Sews the streamed triangles into a closed solid through the kernel's mesh
|
|
14269
|
+
* builder, the same path brepjs's mesh importers use. Null when the mesh is
|
|
14270
|
+
* open or the kernel rejects it.
|
|
14271
|
+
*/
|
|
14272
|
+
function sewMeshToSolid(mesh, scaleToMm, productExpressId, diagnostics) {
|
|
14273
|
+
const kernel = getKernel();
|
|
14274
|
+
const { vertices, indices } = mesh;
|
|
14275
|
+
const point = (index) => [
|
|
14276
|
+
(vertices[index * 3] ?? 0) * scaleToMm,
|
|
14277
|
+
(vertices[index * 3 + 1] ?? 0) * scaleToMm,
|
|
14278
|
+
(vertices[index * 3 + 2] ?? 0) * scaleToMm
|
|
14279
|
+
];
|
|
14280
|
+
const triangles = [];
|
|
14281
|
+
for (let t = 0; t + 2 < indices.length; t += 3) {
|
|
14282
|
+
const face = kernel.buildTriFace(point(indices[t] ?? 0), point(indices[t + 1] ?? 0), point(indices[t + 2] ?? 0));
|
|
14283
|
+
if (face !== null) triangles.push(face);
|
|
14284
|
+
}
|
|
14285
|
+
if (triangles.length === 0) return null;
|
|
14286
|
+
try {
|
|
14287
|
+
const cast = castFreshResult(kernel.sewAndSolidify(triangles, MESH_SEW_TOLERANCE_MM));
|
|
14288
|
+
const valid = isSolid(cast) ? validSolid(cast) : null;
|
|
14289
|
+
if (valid !== null && valid.ok) return valid.value;
|
|
14290
|
+
cast[Symbol.dispose]();
|
|
14291
|
+
return null;
|
|
14292
|
+
} catch (e) {
|
|
14293
|
+
diagnostics.push(issue("info", "TESSELLATION_NOT_MANIFOLD", `Mesh sewing failed: ${errMsg$1(e)}`, productExpressId));
|
|
14294
|
+
return null;
|
|
14295
|
+
} finally {
|
|
14296
|
+
for (const face of triangles) kernel.dispose(face);
|
|
14297
|
+
}
|
|
14298
|
+
}
|
|
14233
14299
|
function collectMesh(reader, productExpressId) {
|
|
14234
14300
|
const vertices = [];
|
|
14235
14301
|
const indices = [];
|
|
@@ -14258,7 +14324,8 @@ function collectMesh(reader, productExpressId) {
|
|
|
14258
14324
|
}
|
|
14259
14325
|
}
|
|
14260
14326
|
} finally {
|
|
14261
|
-
flatMesh.
|
|
14327
|
+
releaseEmbind(flatMesh.geometries);
|
|
14328
|
+
releaseEmbind(flatMesh);
|
|
14262
14329
|
}
|
|
14263
14330
|
});
|
|
14264
14331
|
if (vertices.length === 0) return null;
|
|
@@ -14267,25 +14334,6 @@ function collectMesh(reader, productExpressId) {
|
|
|
14267
14334
|
indices: new Uint32Array(indices)
|
|
14268
14335
|
};
|
|
14269
14336
|
}
|
|
14270
|
-
function packBinaryStl(vertices, indices, scaleToMm) {
|
|
14271
|
-
const triCount = Math.floor(indices.length / 3);
|
|
14272
|
-
const buffer = /* @__PURE__ */ new ArrayBuffer(84 + triCount * 50);
|
|
14273
|
-
const view = new DataView(buffer);
|
|
14274
|
-
view.setUint32(80, triCount, true);
|
|
14275
|
-
let offset = 84;
|
|
14276
|
-
for (let t = 0; t < triCount; t++) {
|
|
14277
|
-
offset += 12;
|
|
14278
|
-
for (let c = 0; c < 3; c++) {
|
|
14279
|
-
const vi = (indices[t * 3 + c] ?? 0) * 3;
|
|
14280
|
-
view.setFloat32(offset, (vertices[vi] ?? 0) * scaleToMm, true);
|
|
14281
|
-
view.setFloat32(offset + 4, (vertices[vi + 1] ?? 0) * scaleToMm, true);
|
|
14282
|
-
view.setFloat32(offset + 8, (vertices[vi + 2] ?? 0) * scaleToMm, true);
|
|
14283
|
-
offset += 12;
|
|
14284
|
-
}
|
|
14285
|
-
offset += 2;
|
|
14286
|
-
}
|
|
14287
|
-
return new Uint8Array(buffer);
|
|
14288
|
-
}
|
|
14289
14337
|
var PARAMETRIC_PROFILE_BUILDERS = /* @__PURE__ */ new Map([
|
|
14290
14338
|
[WebIFC.IFCRECTANGLEPROFILEDEF, (f) => ({
|
|
14291
14339
|
kind: "RECTANGULAR",
|
|
@@ -14429,19 +14477,99 @@ function profileToFace(profile) {
|
|
|
14429
14477
|
if (!face.ok) return err(importError("GEOMETRY_RECONSTRUCTION_FAILED", `Profile face build failed: ${face.error.message}`));
|
|
14430
14478
|
return ok(face.value);
|
|
14431
14479
|
}
|
|
14432
|
-
|
|
14433
|
-
|
|
14434
|
-
|
|
14435
|
-
|
|
14436
|
-
|
|
14437
|
-
|
|
14438
|
-
|
|
14439
|
-
|
|
14440
|
-
|
|
14441
|
-
|
|
14442
|
-
|
|
14443
|
-
|
|
14444
|
-
|
|
14480
|
+
/**
|
|
14481
|
+
* IfcParameterizedProfileDef.Position places the profile inside the swept
|
|
14482
|
+
* solid's XY plane; the writers rely on it to corner-anchor centred rectangle
|
|
14483
|
+
* profiles. Absent on arbitrary (polyline) profiles.
|
|
14484
|
+
*/
|
|
14485
|
+
function readProfilePosition(reader, profileExpressId, scale) {
|
|
14486
|
+
const def = reader.getLine(profileExpressId);
|
|
14487
|
+
const positionRef = def === null ? void 0 : asRef(def["Position"]);
|
|
14488
|
+
if (positionRef === void 0) return null;
|
|
14489
|
+
const position = reader.getLine(positionRef.value);
|
|
14490
|
+
if (position === null) return null;
|
|
14491
|
+
const [x, y] = readPoint2D(reader, position["Location"], scale) ?? [0, 0];
|
|
14492
|
+
const [dx, dy] = readDirection2D(reader, position["RefDirection"]) ?? [1, 0];
|
|
14493
|
+
const len = Math.hypot(dx, dy);
|
|
14494
|
+
const c = len < 1e-12 ? 1 : dx / len;
|
|
14495
|
+
const s = len < 1e-12 ? 0 : dy / len;
|
|
14496
|
+
return {
|
|
14497
|
+
linear: [
|
|
14498
|
+
c,
|
|
14499
|
+
-s,
|
|
14500
|
+
0,
|
|
14501
|
+
s,
|
|
14502
|
+
c,
|
|
14503
|
+
0,
|
|
14504
|
+
0,
|
|
14505
|
+
0,
|
|
14506
|
+
1
|
|
14507
|
+
],
|
|
14508
|
+
translation: [
|
|
14509
|
+
x,
|
|
14510
|
+
y,
|
|
14511
|
+
0
|
|
14512
|
+
]
|
|
14513
|
+
};
|
|
14514
|
+
}
|
|
14515
|
+
/**
|
|
14516
|
+
* Applies the placement chain (outermost first) as one composed rigid motion.
|
|
14517
|
+
* A second applyMatrix on an already-transformed solid can fail BRepCheck on
|
|
14518
|
+
* occt-wasm, so the frames are never applied one after another.
|
|
14519
|
+
*/
|
|
14520
|
+
function placeSolid(solid, frames) {
|
|
14521
|
+
let composed = null;
|
|
14522
|
+
for (const frame of frames) {
|
|
14523
|
+
if (frame === null) continue;
|
|
14524
|
+
composed = composed === null ? frame : composeRigid(composed, frame);
|
|
14525
|
+
}
|
|
14526
|
+
if (composed === null || isIdentity(composed)) return ok(solid);
|
|
14527
|
+
const applied = applyMatrix(solid, composed);
|
|
14528
|
+
solid[Symbol.dispose]();
|
|
14529
|
+
if (!applied.ok) return err(importError("PLACEMENT_READ_FAILED", `Placement transform failed: ${applied.error.message}`));
|
|
14530
|
+
return ok(applied.value);
|
|
14531
|
+
}
|
|
14532
|
+
/** `outer ∘ inner`: the motion that applies `inner` first, then `outer`. */
|
|
14533
|
+
function composeRigid(outer, inner) {
|
|
14534
|
+
const [a00, a01, a02, a10, a11, a12, a20, a21, a22] = outer.linear;
|
|
14535
|
+
const [b00, b01, b02, b10, b11, b12, b20, b21, b22] = inner.linear;
|
|
14536
|
+
const [tx, ty, tz] = inner.translation;
|
|
14537
|
+
const [ux, uy, uz] = outer.translation;
|
|
14538
|
+
return {
|
|
14539
|
+
linear: [
|
|
14540
|
+
a00 * b00 + a01 * b10 + a02 * b20,
|
|
14541
|
+
a00 * b01 + a01 * b11 + a02 * b21,
|
|
14542
|
+
a00 * b02 + a01 * b12 + a02 * b22,
|
|
14543
|
+
a10 * b00 + a11 * b10 + a12 * b20,
|
|
14544
|
+
a10 * b01 + a11 * b11 + a12 * b21,
|
|
14545
|
+
a10 * b02 + a11 * b12 + a12 * b22,
|
|
14546
|
+
a20 * b00 + a21 * b10 + a22 * b20,
|
|
14547
|
+
a20 * b01 + a21 * b11 + a22 * b21,
|
|
14548
|
+
a20 * b02 + a21 * b12 + a22 * b22
|
|
14549
|
+
],
|
|
14550
|
+
translation: [
|
|
14551
|
+
a00 * tx + a01 * ty + a02 * tz + ux,
|
|
14552
|
+
a10 * tx + a11 * ty + a12 * tz + uy,
|
|
14553
|
+
a20 * tx + a21 * ty + a22 * tz + uz
|
|
14554
|
+
]
|
|
14555
|
+
};
|
|
14556
|
+
}
|
|
14557
|
+
/** Rᵀ·v for a rigid frame's rotation part. */
|
|
14558
|
+
function rotateByTranspose(linear, v) {
|
|
14559
|
+
return [
|
|
14560
|
+
linear[0] * v[0] + linear[3] * v[1] + linear[6] * v[2],
|
|
14561
|
+
linear[1] * v[0] + linear[4] * v[1] + linear[7] * v[2],
|
|
14562
|
+
linear[2] * v[0] + linear[5] * v[1] + linear[8] * v[2]
|
|
14563
|
+
];
|
|
14564
|
+
}
|
|
14565
|
+
/** Maps a point from the frame's parent space into the frame: Rᵀ·(p − t). */
|
|
14566
|
+
function inverseRigidPoint(frame, p) {
|
|
14567
|
+
const t = frame.translation;
|
|
14568
|
+
return rotateByTranspose(frame.linear, [
|
|
14569
|
+
p[0] - t[0],
|
|
14570
|
+
p[1] - t[1],
|
|
14571
|
+
p[2] - t[2]
|
|
14572
|
+
]);
|
|
14445
14573
|
}
|
|
14446
14574
|
function finalizeSolid(solid, entity, diagnostics) {
|
|
14447
14575
|
if (!isSolid(solid)) {
|
|
@@ -14590,6 +14718,24 @@ function readPolylinePoints(reader, ref, scale) {
|
|
|
14590
14718
|
}
|
|
14591
14719
|
return out.length >= 3 ? out : void 0;
|
|
14592
14720
|
}
|
|
14721
|
+
function readPoint2D(reader, ref, scale) {
|
|
14722
|
+
const pointRef = asRef(ref);
|
|
14723
|
+
if (pointRef === void 0) return void 0;
|
|
14724
|
+
const pt = reader.getLine(pointRef.value);
|
|
14725
|
+
if (pt === null) return void 0;
|
|
14726
|
+
const coords = asMeasureArray(pt["Coordinates"]);
|
|
14727
|
+
if (coords.length < 2) return void 0;
|
|
14728
|
+
return [(coords[0] ?? 0) * scale * 1e3, (coords[1] ?? 0) * scale * 1e3];
|
|
14729
|
+
}
|
|
14730
|
+
function readDirection2D(reader, ref) {
|
|
14731
|
+
const dirRef = asRef(ref);
|
|
14732
|
+
if (dirRef === void 0) return void 0;
|
|
14733
|
+
const dir = reader.getLine(dirRef.value);
|
|
14734
|
+
if (dir === null) return void 0;
|
|
14735
|
+
const ratios = asMeasureArray(dir["DirectionRatios"]);
|
|
14736
|
+
if (ratios.length < 2) return void 0;
|
|
14737
|
+
return [ratios[0] ?? 0, ratios[1] ?? 0];
|
|
14738
|
+
}
|
|
14593
14739
|
function readPoint(reader, ref, scale) {
|
|
14594
14740
|
const pointRef = asRef(ref);
|
|
14595
14741
|
if (pointRef === void 0) return void 0;
|
|
@@ -14616,6 +14762,9 @@ function readDirection(reader, ref) {
|
|
|
14616
14762
|
ratios[2] ?? 0
|
|
14617
14763
|
];
|
|
14618
14764
|
}
|
|
14765
|
+
function releaseEmbind(value) {
|
|
14766
|
+
value.delete?.();
|
|
14767
|
+
}
|
|
14619
14768
|
function asRef(value) {
|
|
14620
14769
|
if (value !== null && typeof value === "object" && "value" in value) {
|
|
14621
14770
|
const v = value.value;
|
|
@@ -28832,13 +28981,25 @@ function specLocalShift(el) {
|
|
|
28832
28981
|
0
|
|
28833
28982
|
];
|
|
28834
28983
|
}
|
|
28984
|
+
/** Skip the resolver occurrence wrappers already owned by `cumulativeFrame`,
|
|
28985
|
+
* then recover only the Body's outer literal Datum translations. An unexpected
|
|
28986
|
+
* wrapper shape returns zero rather than risking an occurrence transform twice. */
|
|
28987
|
+
function bodyLocalTranslation(el, occurrenceTransformDepth) {
|
|
28988
|
+
let body = el.geometry;
|
|
28989
|
+
for (let i = 0; i < occurrenceTransformDepth; i++) {
|
|
28990
|
+
if (body.kind !== "Translate" && body.kind !== "Rotate") return ZERO_TRANSLATION;
|
|
28991
|
+
body = body.target;
|
|
28992
|
+
}
|
|
28993
|
+
return peelTranslates(body).total;
|
|
28994
|
+
}
|
|
28835
28995
|
/** World frame of an element's own body: the walk's cumulative frame (all
|
|
28836
28996
|
* authored transforms, ancestors + own) composed with the body's authored axes
|
|
28837
|
-
* (`axisX`/`axisZ` props) and its local origin (`origin` prop +
|
|
28838
|
-
* shift). Under no rotation this reduces to `composedOrigin`
|
|
28839
|
-
|
|
28997
|
+
* (`axisX`/`axisZ` props) and its local origin (`origin` prop + literal Body
|
|
28998
|
+
* Datum + centring shift). Under no rotation this reduces to `composedOrigin`
|
|
28999
|
+
* + shift. */
|
|
29000
|
+
function elementBodyFrame(el, cumulativeFrame, occurrenceTransformDepth) {
|
|
28840
29001
|
return frameMul(frameMul(cumulativeFrame, frameFromPlacement({
|
|
28841
|
-
origin: authoredSpecOrigin(el),
|
|
29002
|
+
origin: addTranslation(authoredSpecOrigin(el), bodyLocalTranslation(el, occurrenceTransformDepth)),
|
|
28842
29003
|
axisX: authoredAxisX(el),
|
|
28843
29004
|
axisZ: authoredAxisZ(el)
|
|
28844
29005
|
})), translationFrame(specLocalShift(el)));
|
|
@@ -28857,10 +29018,10 @@ function civilNodeFrame(el, cumulativeFrame) {
|
|
|
28857
29018
|
* `axisZ` are recomputed from the element's world frame relative to its spatial
|
|
28858
29019
|
* container. Stair/ramp flights, which carry their own per-flight frame, are
|
|
28859
29020
|
* each re-placed the same way. */
|
|
28860
|
-
function rotatedRoutedInput(flatInput, el, cumulativeFrame, spatialFrame) {
|
|
29021
|
+
function rotatedRoutedInput(flatInput, el, cumulativeFrame, occurrenceTransformDepth, spatialFrame) {
|
|
28861
29022
|
const toSpatial = frameInverse(spatialFrame);
|
|
28862
29023
|
if (Array.isArray(flatInput["flights"])) {
|
|
28863
|
-
const elementFrame = elementBodyFrame(el, cumulativeFrame);
|
|
29024
|
+
const elementFrame = elementBodyFrame(el, cumulativeFrame, occurrenceTransformDepth);
|
|
28864
29025
|
const flights = flatInput["flights"];
|
|
28865
29026
|
return {
|
|
28866
29027
|
...flatInput,
|
|
@@ -28901,7 +29062,7 @@ function rotatedRoutedInput(flatInput, el, cumulativeFrame, spatialFrame) {
|
|
|
28901
29062
|
})
|
|
28902
29063
|
};
|
|
28903
29064
|
}
|
|
28904
|
-
const placed = decomposeFrame(frameMul(toSpatial, elementBodyFrame(el, cumulativeFrame)));
|
|
29065
|
+
const placed = decomposeFrame(frameMul(toSpatial, elementBodyFrame(el, cumulativeFrame, occurrenceTransformDepth)));
|
|
28905
29066
|
return {
|
|
28906
29067
|
...flatInput,
|
|
28907
29068
|
origin: placed.origin,
|
|
@@ -29066,6 +29227,7 @@ function familiesToBim(root, options) {
|
|
|
29066
29227
|
const walk = (el, state) => {
|
|
29067
29228
|
const rotatedHere = state.rotated || hasRotateOp(el);
|
|
29068
29229
|
const cumulativeTranslationHere = addTranslation(state.cumulativeTranslation, authoredTranslation(el));
|
|
29230
|
+
const occurrenceTransformDepthHere = state.occurrenceTransformDepth + el.localTransforms.length;
|
|
29069
29231
|
const cumulativeFrameHere = frameMul(state.cumulativeFrame, frameFromOps(el.localTransforms));
|
|
29070
29232
|
let proxiedHere = false;
|
|
29071
29233
|
let nextRotated = rotatedHere;
|
|
@@ -29129,7 +29291,7 @@ function familiesToBim(root, options) {
|
|
|
29129
29291
|
}
|
|
29130
29292
|
const routedInput = el.semantics?.kind === "product" ? civilProductSpecInput(el) : ("input" in route ? route.input : specInput)(el);
|
|
29131
29293
|
const rotatedBase = Array.isArray(routedInput["flights"]) ? specInput(el) : routedInput;
|
|
29132
|
-
const placedInput = rotatedHere ? rotatedRoutedInput(rotatedBase, el, cumulativeFrameHere, state.spatialFrame) : usesAuthoredCivilHierarchy ? relativeSpecInput(routedInput, state.projectedSpatialTranslation) : routedInput;
|
|
29294
|
+
const placedInput = rotatedHere ? rotatedRoutedInput(rotatedBase, el, cumulativeFrameHere, occurrenceTransformDepthHere, state.spatialFrame) : usesAuthoredCivilHierarchy ? relativeSpecInput(routedInput, state.projectedSpatialTranslation) : routedInput;
|
|
29133
29295
|
const parsed = route.parse(placedInput);
|
|
29134
29296
|
if (!parsed.ok) return parsed;
|
|
29135
29297
|
const added = route.add(model, parsed.value, el.keyPath);
|
|
@@ -29166,6 +29328,7 @@ function familiesToBim(root, options) {
|
|
|
29166
29328
|
rotated: nextRotated,
|
|
29167
29329
|
cumulativeTranslation: cumulativeTranslationHere,
|
|
29168
29330
|
projectedSpatialTranslation: nextProjectedSpatialTranslation,
|
|
29331
|
+
occurrenceTransformDepth: occurrenceTransformDepthHere,
|
|
29169
29332
|
cumulativeFrame: nextCumulativeFrame,
|
|
29170
29333
|
spatialFrame: nextSpatialFrame
|
|
29171
29334
|
});
|
|
@@ -29179,6 +29342,7 @@ function familiesToBim(root, options) {
|
|
|
29179
29342
|
rotated: false,
|
|
29180
29343
|
cumulativeTranslation: ZERO_TRANSLATION,
|
|
29181
29344
|
projectedSpatialTranslation: ZERO_TRANSLATION,
|
|
29345
|
+
occurrenceTransformDepth: 0,
|
|
29182
29346
|
cumulativeFrame: IDENTITY_FRAME,
|
|
29183
29347
|
spatialFrame: IDENTITY_FRAME
|
|
29184
29348
|
});
|