brepjs-bim 0.23.0 → 0.23.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/dist/brepjs-bim.cjs +525 -128
- package/dist/brepjs-bim.js +517 -120
- package/dist/import/fromIfc.d.ts +7 -1
- package/dist/import/geometryRead.d.ts +13 -1
- package/dist/import/importedModel.d.ts +17 -8
- package/dist/index.d.ts +1 -1
- 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();
|
|
@@ -535,7 +535,7 @@ var propertyKeyTypes = /* @__PURE__*/ new Set([
|
|
|
535
535
|
function escapeRegex(str) {
|
|
536
536
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
537
537
|
}
|
|
538
|
-
function clone$
|
|
538
|
+
function clone$2(inst, def, params) {
|
|
539
539
|
const cl = new inst._zod.constr(def ?? inst._zod.def);
|
|
540
540
|
if (!def || params?.parent) cl._zod.parent = inst;
|
|
541
541
|
return cl;
|
|
@@ -571,7 +571,7 @@ function pick(schema, mask) {
|
|
|
571
571
|
const currDef = schema._zod.def;
|
|
572
572
|
const checks = currDef.checks;
|
|
573
573
|
if (checks && checks.length > 0) throw new Error(".pick() cannot be used on object schemas containing refinements");
|
|
574
|
-
return clone$
|
|
574
|
+
return clone$2(schema, mergeDefs(schema._zod.def, {
|
|
575
575
|
get shape() {
|
|
576
576
|
const newShape = {};
|
|
577
577
|
for (const key in mask) {
|
|
@@ -589,7 +589,7 @@ function omit(schema, mask) {
|
|
|
589
589
|
const currDef = schema._zod.def;
|
|
590
590
|
const checks = currDef.checks;
|
|
591
591
|
if (checks && checks.length > 0) throw new Error(".omit() cannot be used on object schemas containing refinements");
|
|
592
|
-
return clone$
|
|
592
|
+
return clone$2(schema, mergeDefs(schema._zod.def, {
|
|
593
593
|
get shape() {
|
|
594
594
|
const newShape = { ...schema._zod.def.shape };
|
|
595
595
|
for (const key in mask) {
|
|
@@ -610,7 +610,7 @@ function extend(schema, shape) {
|
|
|
610
610
|
const existingShape = schema._zod.def.shape;
|
|
611
611
|
for (const key in shape) if (Object.getOwnPropertyDescriptor(existingShape, key) !== void 0) throw new Error("Cannot overwrite keys on object schemas containing refinements. Use `.safeExtend()` instead.");
|
|
612
612
|
}
|
|
613
|
-
return clone$
|
|
613
|
+
return clone$2(schema, mergeDefs(schema._zod.def, { get shape() {
|
|
614
614
|
const _shape = {
|
|
615
615
|
...schema._zod.def.shape,
|
|
616
616
|
...shape
|
|
@@ -621,7 +621,7 @@ function extend(schema, shape) {
|
|
|
621
621
|
}
|
|
622
622
|
function safeExtend(schema, shape) {
|
|
623
623
|
if (!isPlainObject(shape)) throw new Error("Invalid input to safeExtend: expected a plain object");
|
|
624
|
-
return clone$
|
|
624
|
+
return clone$2(schema, mergeDefs(schema._zod.def, { get shape() {
|
|
625
625
|
const _shape = {
|
|
626
626
|
...schema._zod.def.shape,
|
|
627
627
|
...shape
|
|
@@ -632,7 +632,7 @@ function safeExtend(schema, shape) {
|
|
|
632
632
|
}
|
|
633
633
|
function merge(a, b) {
|
|
634
634
|
if (a._zod.def.checks?.length) throw new Error(".merge() cannot be used on object schemas containing refinements. Use .safeExtend() instead.");
|
|
635
|
-
return clone$
|
|
635
|
+
return clone$2(a, mergeDefs(a._zod.def, {
|
|
636
636
|
get shape() {
|
|
637
637
|
const _shape = {
|
|
638
638
|
...a._zod.def.shape,
|
|
@@ -650,7 +650,7 @@ function merge(a, b) {
|
|
|
650
650
|
function partial(Class, schema, mask) {
|
|
651
651
|
const checks = schema._zod.def.checks;
|
|
652
652
|
if (checks && checks.length > 0) throw new Error(".partial() cannot be used on object schemas containing refinements");
|
|
653
|
-
return clone$
|
|
653
|
+
return clone$2(schema, mergeDefs(schema._zod.def, {
|
|
654
654
|
get shape() {
|
|
655
655
|
const oldShape = schema._zod.def.shape;
|
|
656
656
|
const shape = { ...oldShape };
|
|
@@ -673,7 +673,7 @@ function partial(Class, schema, mask) {
|
|
|
673
673
|
}));
|
|
674
674
|
}
|
|
675
675
|
function required(Class, schema, mask) {
|
|
676
|
-
return clone$
|
|
676
|
+
return clone$2(schema, mergeDefs(schema._zod.def, { get shape() {
|
|
677
677
|
const oldShape = schema._zod.def.shape;
|
|
678
678
|
const shape = { ...oldShape };
|
|
679
679
|
if (mask) for (const key in mask) {
|
|
@@ -4042,7 +4042,7 @@ var ZodType = /*@__PURE__*/ $constructor("ZodType", (inst, def) => {
|
|
|
4042
4042
|
return this.check(...chks);
|
|
4043
4043
|
},
|
|
4044
4044
|
clone(def, params) {
|
|
4045
|
-
return clone$
|
|
4045
|
+
return clone$2(this, def, params);
|
|
4046
4046
|
},
|
|
4047
4047
|
brand() {
|
|
4048
4048
|
return this;
|
|
@@ -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);
|
|
@@ -14070,6 +14088,7 @@ function buildElementContainmentMap(reader) {
|
|
|
14070
14088
|
//#endregion
|
|
14071
14089
|
//#region src/import/geometryRead.ts
|
|
14072
14090
|
var NONE = { kind: "NONE" };
|
|
14091
|
+
var testHooks$1 = null;
|
|
14073
14092
|
/**
|
|
14074
14093
|
* Reconstructs the `Body` (SweptSolid) representation of a product into a brepjs
|
|
14075
14094
|
* solid, falling back to tessellated mesh import when no parametric path exists.
|
|
@@ -14080,27 +14099,66 @@ var NONE = { kind: "NONE" };
|
|
|
14080
14099
|
* single bad element never aborts a model import.
|
|
14081
14100
|
*/
|
|
14082
14101
|
function readBodyGeometry(reader, productExpressId, scale, diagnostics) {
|
|
14102
|
+
const body = readBodyItems(reader, productExpressId, scale, diagnostics);
|
|
14103
|
+
const selected = body.items.find((item) => item.kind !== "NONE") ?? NONE;
|
|
14104
|
+
for (const item of body.items) if (item !== selected && item.kind === "SOLID") item.solid[Symbol.dispose]();
|
|
14105
|
+
return selected;
|
|
14106
|
+
}
|
|
14107
|
+
/** Reconstructs each supported IFC Body item independently in representation order. */
|
|
14108
|
+
function readBodyItems(reader, productExpressId, scale, diagnostics) {
|
|
14083
14109
|
const product = reader.getLine(productExpressId);
|
|
14084
|
-
if (product === null) return
|
|
14110
|
+
if (product === null) return {
|
|
14111
|
+
hasBody: false,
|
|
14112
|
+
itemCount: 0,
|
|
14113
|
+
items: []
|
|
14114
|
+
};
|
|
14085
14115
|
const representationRef = asRef(product["Representation"]);
|
|
14086
|
-
if (representationRef === void 0) return
|
|
14116
|
+
if (representationRef === void 0) return {
|
|
14117
|
+
hasBody: false,
|
|
14118
|
+
itemCount: 0,
|
|
14119
|
+
items: []
|
|
14120
|
+
};
|
|
14087
14121
|
const productShape = reader.getLine(representationRef.value);
|
|
14088
14122
|
const representations = productShape === null ? void 0 : asRefArray(productShape["Representations"]);
|
|
14089
|
-
if (representations === void 0) return
|
|
14090
|
-
|
|
14091
|
-
|
|
14123
|
+
if (representations === void 0) return {
|
|
14124
|
+
hasBody: false,
|
|
14125
|
+
itemCount: 0,
|
|
14126
|
+
items: []
|
|
14127
|
+
};
|
|
14128
|
+
const bodyItemIds = findBodyItems(reader, representations);
|
|
14129
|
+
if (bodyItemIds === null) return {
|
|
14130
|
+
hasBody: false,
|
|
14131
|
+
itemCount: 0,
|
|
14132
|
+
items: []
|
|
14133
|
+
};
|
|
14092
14134
|
const worldTransform = readWorldTransform(reader, product, scale);
|
|
14093
|
-
const
|
|
14094
|
-
|
|
14095
|
-
|
|
14096
|
-
|
|
14097
|
-
|
|
14098
|
-
} catch (
|
|
14099
|
-
diagnostics.push(issue("warning", "GEOMETRY_RECONSTRUCTION_FAILED", `Body
|
|
14100
|
-
return NONE;
|
|
14135
|
+
const itemTypes = new Map(bodyItemIds.map((id) => [id, reader.getLineType(id)]));
|
|
14136
|
+
const streamedItemIds = new Set(bodyItemIds.filter((id) => isStreamedTessellatedType(itemTypes.get(id))));
|
|
14137
|
+
let streamedMeshes = /* @__PURE__ */ new Map();
|
|
14138
|
+
if (streamedItemIds.size > 0) try {
|
|
14139
|
+
streamedMeshes = collectItemMeshes(reader, productExpressId, streamedItemIds);
|
|
14140
|
+
} catch (cause) {
|
|
14141
|
+
diagnostics.push(issue("warning", "GEOMETRY_RECONSTRUCTION_FAILED", `Body tessellation stream failed: ${errMsg$1(cause)}`, productExpressId));
|
|
14101
14142
|
}
|
|
14102
|
-
|
|
14103
|
-
|
|
14143
|
+
const items = bodyItemIds.map((bodyItemId) => {
|
|
14144
|
+
const itemType = itemTypes.get(bodyItemId);
|
|
14145
|
+
try {
|
|
14146
|
+
if (itemType === web_ifc.IFCEXTRUDEDAREASOLID) return reconstructExtrusion(reader, bodyItemId, scale, worldTransform, diagnostics);
|
|
14147
|
+
if (itemType === web_ifc.IFCREVOLVEDAREASOLID) return reconstructRevolution(reader, bodyItemId, scale, worldTransform, diagnostics);
|
|
14148
|
+
if (itemType === web_ifc.IFCTRIANGULATEDFACESET) return reconstructTriangulatedItem(reader, bodyItemId, scale, worldTransform, diagnostics);
|
|
14149
|
+
if (isStreamedTessellatedType(itemType)) return reconstructStreamedTessellatedItem(streamedMeshes.get(bodyItemId), bodyItemId, diagnostics);
|
|
14150
|
+
} catch (cause) {
|
|
14151
|
+
diagnostics.push(issue("warning", "GEOMETRY_RECONSTRUCTION_FAILED", `Body item ${bodyItemId} reconstruction threw: ${errMsg$1(cause)}`, bodyItemId, { productExpressId }));
|
|
14152
|
+
return NONE;
|
|
14153
|
+
}
|
|
14154
|
+
diagnostics.push(issue("warning", "UNSUPPORTED_REPRESENTATION_ITEM", `Unsupported Body item ${bodyItemId} type ${itemType}; geometry skipped`, bodyItemId, { productExpressId }));
|
|
14155
|
+
return NONE;
|
|
14156
|
+
});
|
|
14157
|
+
return {
|
|
14158
|
+
hasBody: true,
|
|
14159
|
+
itemCount: bodyItemIds.length,
|
|
14160
|
+
items
|
|
14161
|
+
};
|
|
14104
14162
|
}
|
|
14105
14163
|
function reconstructExtrusion(reader, extrusionId, scale, worldTransform, diagnostics) {
|
|
14106
14164
|
const ext = reader.getLine(extrusionId);
|
|
@@ -14121,12 +14179,14 @@ function reconstructExtrusion(reader, extrusionId, scale, worldTransform, diagno
|
|
|
14121
14179
|
diagnostics.push(issue("warning", faceResult.error.code, faceResult.error.message, extrusionId));
|
|
14122
14180
|
return NONE;
|
|
14123
14181
|
}
|
|
14182
|
+
const profileFrame = readProfilePosition(reader, sweptArea.value, scale);
|
|
14124
14183
|
const depthMm = depth * scale * 1e3;
|
|
14125
|
-
const
|
|
14184
|
+
const sweptDir = readDirection(reader, ext["ExtrudedDirection"]) ?? [
|
|
14126
14185
|
0,
|
|
14127
14186
|
0,
|
|
14128
14187
|
1
|
|
14129
14188
|
];
|
|
14189
|
+
const extrudeDir = profileFrame === null ? sweptDir : rotateByTranspose(profileFrame.linear, sweptDir);
|
|
14130
14190
|
const extrudeVec = [
|
|
14131
14191
|
extrudeDir[0] * depthMm,
|
|
14132
14192
|
extrudeDir[1] * depthMm,
|
|
@@ -14148,7 +14208,11 @@ function reconstructExtrusion(reader, extrusionId, scale, worldTransform, diagno
|
|
|
14148
14208
|
return NONE;
|
|
14149
14209
|
}
|
|
14150
14210
|
const localFrame = readAxis2Placement3D(reader, ext["Position"], scale);
|
|
14151
|
-
const placed = placeSolid(solidResult.value,
|
|
14211
|
+
const placed = placeSolid(solidResult.value, [
|
|
14212
|
+
worldTransform,
|
|
14213
|
+
localFrame,
|
|
14214
|
+
profileFrame
|
|
14215
|
+
]);
|
|
14152
14216
|
if (!placed.ok) {
|
|
14153
14217
|
diagnostics.push(issue("warning", placed.error.code, placed.error.message, extrusionId));
|
|
14154
14218
|
return NONE;
|
|
@@ -14176,16 +14240,19 @@ function reconstructRevolution(reader, revolutionId, scale, worldTransform, diag
|
|
|
14176
14240
|
return NONE;
|
|
14177
14241
|
}
|
|
14178
14242
|
const axis1 = reader.getLine(axisRef.value);
|
|
14179
|
-
const
|
|
14243
|
+
const sweptCenter = (axis1 === null ? void 0 : readPoint(reader, axis1["Location"], scale)) ?? [
|
|
14180
14244
|
0,
|
|
14181
14245
|
0,
|
|
14182
14246
|
0
|
|
14183
14247
|
];
|
|
14184
|
-
const
|
|
14248
|
+
const sweptDirection = (axis1 === null ? void 0 : readDirection(reader, axis1["Axis"])) ?? [
|
|
14185
14249
|
0,
|
|
14186
14250
|
0,
|
|
14187
14251
|
1
|
|
14188
14252
|
];
|
|
14253
|
+
const profileFrame = readProfilePosition(reader, sweptArea.value, scale);
|
|
14254
|
+
const center = profileFrame === null ? sweptCenter : inverseRigidPoint(profileFrame, sweptCenter);
|
|
14255
|
+
const direction = profileFrame === null ? sweptDirection : rotateByTranspose(profileFrame.linear, sweptDirection);
|
|
14189
14256
|
const revolved = (() => {
|
|
14190
14257
|
try {
|
|
14191
14258
|
var _usingCtx3 = _usingCtx();
|
|
@@ -14212,40 +14279,72 @@ function reconstructRevolution(reader, revolutionId, scale, worldTransform, diag
|
|
|
14212
14279
|
return NONE;
|
|
14213
14280
|
}
|
|
14214
14281
|
const localFrame = readAxis2Placement3D(reader, rev["Position"], scale);
|
|
14215
|
-
const placed = placeSolid(revolved.value,
|
|
14282
|
+
const placed = placeSolid(revolved.value, [
|
|
14283
|
+
worldTransform,
|
|
14284
|
+
localFrame,
|
|
14285
|
+
profileFrame
|
|
14286
|
+
]);
|
|
14216
14287
|
if (!placed.ok) {
|
|
14217
14288
|
diagnostics.push(issue("warning", placed.error.code, placed.error.message, revolutionId));
|
|
14218
14289
|
return NONE;
|
|
14219
14290
|
}
|
|
14220
14291
|
return finalizeSolid(placed.value, revolutionId, diagnostics);
|
|
14221
14292
|
}
|
|
14222
|
-
function
|
|
14223
|
-
const
|
|
14293
|
+
function reconstructTriangulatedItem(reader, itemExpressId, scale, worldTransform, diagnostics) {
|
|
14294
|
+
const faceSet = reader.getLine(itemExpressId);
|
|
14295
|
+
const coordinatesRef = faceSet === null ? void 0 : asRef(faceSet["Coordinates"]);
|
|
14296
|
+
const rawPoints = (coordinatesRef === void 0 ? null : reader.getLine(coordinatesRef.value))?.["CoordList"];
|
|
14297
|
+
const rawTriangles = faceSet?.["CoordIndex"];
|
|
14298
|
+
return reconstructTessellatedMesh(readTriangulatedMesh(rawPoints, rawTriangles, scale, worldTransform), 1, itemExpressId, diagnostics);
|
|
14299
|
+
}
|
|
14300
|
+
function reconstructStreamedTessellatedItem(mesh, itemExpressId, diagnostics) {
|
|
14301
|
+
return reconstructTessellatedMesh(mesh ?? null, 1e3, itemExpressId, diagnostics);
|
|
14302
|
+
}
|
|
14303
|
+
function reconstructTessellatedMesh(mesh, scaleToMm, itemExpressId, diagnostics) {
|
|
14224
14304
|
if (mesh === null || mesh.indices.length === 0) {
|
|
14225
|
-
diagnostics.push(issue("warning", "GEOMETRY_RECONSTRUCTION_FAILED", "Tessellated geometry yielded no triangles",
|
|
14305
|
+
diagnostics.push(issue("warning", "GEOMETRY_RECONSTRUCTION_FAILED", "Tessellated geometry yielded no triangles", itemExpressId));
|
|
14226
14306
|
return NONE;
|
|
14227
14307
|
}
|
|
14228
|
-
const stl = packBinaryStl(mesh.vertices, mesh.indices,
|
|
14229
|
-
let solid =
|
|
14308
|
+
const stl = packBinaryStl(mesh.vertices, mesh.indices, scaleToMm);
|
|
14309
|
+
let solid = sewMeshToSolid(mesh, scaleToMm, itemExpressId, diagnostics);
|
|
14230
14310
|
try {
|
|
14231
|
-
|
|
14232
|
-
|
|
14233
|
-
|
|
14234
|
-
|
|
14235
|
-
|
|
14236
|
-
|
|
14311
|
+
if (solid === null) {
|
|
14312
|
+
const cast = castFreshResult((0, brepjs.getKernel)().importSTL(new Uint8Array(stl).buffer));
|
|
14313
|
+
if ((0, brepjs.isSolid)(cast)) {
|
|
14314
|
+
const valid = (0, brepjs.validSolid)(cast);
|
|
14315
|
+
if (valid.ok) solid = valid.value;
|
|
14316
|
+
else cast[Symbol.dispose]();
|
|
14317
|
+
} else {
|
|
14318
|
+
const solids = (0, brepjs.getSolids)(cast);
|
|
14319
|
+
if (solids.length === 1 && solids[0] !== void 0) {
|
|
14320
|
+
const copied = (0, brepjs.clone)(solids[0]);
|
|
14321
|
+
if (copied.ok) {
|
|
14322
|
+
const valid = (0, brepjs.validSolid)(copied.value);
|
|
14323
|
+
if (valid.ok) solid = valid.value;
|
|
14324
|
+
else copied.value[Symbol.dispose]();
|
|
14325
|
+
}
|
|
14326
|
+
}
|
|
14327
|
+
cast[Symbol.dispose]();
|
|
14328
|
+
}
|
|
14329
|
+
}
|
|
14237
14330
|
} catch (e) {
|
|
14238
|
-
diagnostics.push(issue("info", "TESSELLATION_NOT_MANIFOLD", `STL round-trip failed: ${errMsg$1(e)}`,
|
|
14331
|
+
diagnostics.push(issue("info", "TESSELLATION_NOT_MANIFOLD", `STL round-trip failed: ${errMsg$1(e)}`, itemExpressId));
|
|
14239
14332
|
}
|
|
14240
14333
|
if (solid !== null) {
|
|
14241
|
-
|
|
14334
|
+
try {
|
|
14335
|
+
testHooks$1?.afterItemSolid?.(itemExpressId, solid);
|
|
14336
|
+
} catch (cause) {
|
|
14337
|
+
solid[Symbol.dispose]();
|
|
14338
|
+
throw cause;
|
|
14339
|
+
}
|
|
14340
|
+
diagnostics.push(issue("info", "TESSELLATED_MANIFOLD", "Tessellated mesh recovered as a closed solid", itemExpressId));
|
|
14242
14341
|
return {
|
|
14243
14342
|
kind: "SOLID",
|
|
14244
14343
|
solid,
|
|
14245
14344
|
lossy: true
|
|
14246
14345
|
};
|
|
14247
14346
|
}
|
|
14248
|
-
diagnostics.push(issue("info", "TESSELLATION_NOT_MANIFOLD", "Tessellated mesh is not closed/manifold; returning raw triangle data (lossy)",
|
|
14347
|
+
diagnostics.push(issue("info", "TESSELLATION_NOT_MANIFOLD", "Tessellated mesh is not closed/manifold; returning raw triangle data (lossy)", itemExpressId));
|
|
14249
14348
|
return {
|
|
14250
14349
|
kind: "MESH",
|
|
14251
14350
|
vertices: mesh.vertices,
|
|
@@ -14253,43 +14352,122 @@ function reconstructTessellated(reader, productExpressId, diagnostics) {
|
|
|
14253
14352
|
diagnostic: "TESSELLATED_LOSSY"
|
|
14254
14353
|
};
|
|
14255
14354
|
}
|
|
14256
|
-
|
|
14257
|
-
|
|
14258
|
-
|
|
14259
|
-
|
|
14355
|
+
/** Loose enough to weld float32 vertices emitted per face, expressed in millimetres. */
|
|
14356
|
+
var MESH_SEW_TOLERANCE_MM = .001;
|
|
14357
|
+
/**
|
|
14358
|
+
* Brands a fresh kernel result and releases its pre-downcast arena slot when
|
|
14359
|
+
* the cast moved to a new one. In-place kernels share the slot.
|
|
14360
|
+
*/
|
|
14361
|
+
function castFreshResult(raw) {
|
|
14362
|
+
const cast = (0, brepjs.castShape)(raw);
|
|
14363
|
+
const rawId = raw.id;
|
|
14364
|
+
const castId = cast.wrapped.id;
|
|
14365
|
+
if (!(rawId !== void 0 && castId !== void 0 ? rawId === castId : cast.wrapped === raw)) (0, brepjs.getKernel)().dispose(raw);
|
|
14366
|
+
return cast;
|
|
14367
|
+
}
|
|
14368
|
+
function sewMeshToSolid(mesh, scaleToMm, itemExpressId, diagnostics) {
|
|
14369
|
+
const kernel = (0, brepjs.getKernel)();
|
|
14370
|
+
const { vertices, indices } = mesh;
|
|
14371
|
+
const point = (index) => [
|
|
14372
|
+
(vertices[index * 3] ?? 0) * scaleToMm,
|
|
14373
|
+
(vertices[index * 3 + 1] ?? 0) * scaleToMm,
|
|
14374
|
+
(vertices[index * 3 + 2] ?? 0) * scaleToMm
|
|
14375
|
+
];
|
|
14376
|
+
const triangles = [];
|
|
14377
|
+
for (let index = 0; index + 2 < indices.length; index += 3) {
|
|
14378
|
+
const face = kernel.buildTriFace(point(indices[index] ?? 0), point(indices[index + 1] ?? 0), point(indices[index + 2] ?? 0));
|
|
14379
|
+
if (face !== null) triangles.push(face);
|
|
14380
|
+
}
|
|
14381
|
+
if (triangles.length === 0) return null;
|
|
14382
|
+
try {
|
|
14383
|
+
const cast = castFreshResult(kernel.sewAndSolidify(triangles, MESH_SEW_TOLERANCE_MM));
|
|
14384
|
+
const valid = (0, brepjs.isSolid)(cast) ? (0, brepjs.validSolid)(cast) : null;
|
|
14385
|
+
if (valid !== null && valid.ok) return valid.value;
|
|
14386
|
+
cast[Symbol.dispose]();
|
|
14387
|
+
return null;
|
|
14388
|
+
} catch (cause) {
|
|
14389
|
+
diagnostics.push(issue("info", "TESSELLATION_NOT_MANIFOLD", `Mesh sewing failed: ${errMsg$1(cause)}`, itemExpressId));
|
|
14390
|
+
return null;
|
|
14391
|
+
} finally {
|
|
14392
|
+
for (const face of triangles) kernel.dispose(face);
|
|
14393
|
+
}
|
|
14394
|
+
}
|
|
14395
|
+
/** Streams a product once, then separates web-ifc geometry by Body item express ID. */
|
|
14396
|
+
function collectItemMeshes(reader, productExpressId, itemExpressIds) {
|
|
14397
|
+
const byItem = /* @__PURE__ */ new Map();
|
|
14398
|
+
const soleItemExpressId = itemExpressIds.size === 1 ? [...itemExpressIds][0] : void 0;
|
|
14260
14399
|
reader.streamMeshes([productExpressId], (flatMesh) => {
|
|
14261
14400
|
try {
|
|
14262
14401
|
const geometries = flatMesh.geometries;
|
|
14263
|
-
for (let
|
|
14264
|
-
const placed = geometries.get(
|
|
14265
|
-
const
|
|
14402
|
+
for (let geometryIndex = 0; geometryIndex < geometries.size(); geometryIndex++) {
|
|
14403
|
+
const placed = geometries.get(geometryIndex);
|
|
14404
|
+
const itemExpressId = itemExpressIds.has(placed.geometryExpressID) ? placed.geometryExpressID : soleItemExpressId;
|
|
14405
|
+
if (itemExpressId === void 0) continue;
|
|
14406
|
+
const accumulator = byItem.get(itemExpressId) ?? {
|
|
14407
|
+
vertices: [],
|
|
14408
|
+
indices: []
|
|
14409
|
+
};
|
|
14410
|
+
byItem.set(itemExpressId, accumulator);
|
|
14411
|
+
const geometry = reader.getGeometry(placed.geometryExpressID);
|
|
14266
14412
|
try {
|
|
14267
|
-
const
|
|
14268
|
-
const
|
|
14269
|
-
const
|
|
14270
|
-
const
|
|
14271
|
-
|
|
14272
|
-
|
|
14273
|
-
const
|
|
14274
|
-
const
|
|
14275
|
-
|
|
14413
|
+
const vertices = reader.getVertexArray(geometry.GetVertexData(), geometry.GetVertexDataSize());
|
|
14414
|
+
const indices = reader.getIndexArray(geometry.GetIndexData(), geometry.GetIndexDataSize());
|
|
14415
|
+
const transform = placed.flatTransformation;
|
|
14416
|
+
const base = accumulator.vertices.length / 3;
|
|
14417
|
+
const vertexCount = vertices.length / 6;
|
|
14418
|
+
for (let vertexIndex = 0; vertexIndex < vertexCount; vertexIndex++) {
|
|
14419
|
+
const x = vertices[vertexIndex * 6] ?? 0;
|
|
14420
|
+
const y = vertices[vertexIndex * 6 + 1] ?? 0;
|
|
14421
|
+
const z = vertices[vertexIndex * 6 + 2] ?? 0;
|
|
14422
|
+
accumulator.vertices.push((transform[0] ?? 1) * x + (transform[4] ?? 0) * y + (transform[8] ?? 0) * z + (transform[12] ?? 0), (transform[1] ?? 0) * x + (transform[5] ?? 1) * y + (transform[9] ?? 0) * z + (transform[13] ?? 0), (transform[2] ?? 0) * x + (transform[6] ?? 0) * y + (transform[10] ?? 1) * z + (transform[14] ?? 0));
|
|
14276
14423
|
}
|
|
14277
|
-
for (let
|
|
14278
|
-
base += vertexCount;
|
|
14424
|
+
for (let index = 0; index < indices.length; index++) accumulator.indices.push(base + (indices[index] ?? 0));
|
|
14279
14425
|
} finally {
|
|
14280
|
-
|
|
14426
|
+
geometry.delete();
|
|
14281
14427
|
}
|
|
14282
14428
|
}
|
|
14283
14429
|
} finally {
|
|
14284
|
-
flatMesh.
|
|
14430
|
+
releaseEmbind(flatMesh.geometries);
|
|
14431
|
+
releaseEmbind(flatMesh);
|
|
14285
14432
|
}
|
|
14286
14433
|
});
|
|
14287
|
-
|
|
14434
|
+
return new Map([...byItem].map(([itemExpressId, mesh]) => [itemExpressId, {
|
|
14435
|
+
vertices: new Float32Array(mesh.vertices),
|
|
14436
|
+
indices: new Uint32Array(mesh.indices)
|
|
14437
|
+
}]));
|
|
14438
|
+
}
|
|
14439
|
+
function readTriangulatedMesh(rawPoints, rawTriangles, scale, worldTransform) {
|
|
14440
|
+
if (!Array.isArray(rawPoints) || !Array.isArray(rawTriangles)) return null;
|
|
14441
|
+
const vertices = [];
|
|
14442
|
+
const indices = [];
|
|
14443
|
+
const lengthFactor = scale * 1e3;
|
|
14444
|
+
for (const rawPoint of rawPoints) {
|
|
14445
|
+
if (!Array.isArray(rawPoint) || rawPoint.length < 3) return null;
|
|
14446
|
+
const placed = transformPoint([
|
|
14447
|
+
(readMeasure(rawPoint[0]) ?? 0) * lengthFactor,
|
|
14448
|
+
(readMeasure(rawPoint[1]) ?? 0) * lengthFactor,
|
|
14449
|
+
(readMeasure(rawPoint[2]) ?? 0) * lengthFactor
|
|
14450
|
+
], worldTransform);
|
|
14451
|
+
vertices.push(placed[0], placed[1], placed[2]);
|
|
14452
|
+
}
|
|
14453
|
+
for (const rawTriangle of rawTriangles) {
|
|
14454
|
+
if (!Array.isArray(rawTriangle) || rawTriangle.length < 3) return null;
|
|
14455
|
+
indices.push((readMeasure(rawTriangle[0]) ?? 1) - 1, (readMeasure(rawTriangle[1]) ?? 1) - 1, (readMeasure(rawTriangle[2]) ?? 1) - 1);
|
|
14456
|
+
}
|
|
14288
14457
|
return {
|
|
14289
14458
|
vertices: new Float32Array(vertices),
|
|
14290
14459
|
indices: new Uint32Array(indices)
|
|
14291
14460
|
};
|
|
14292
14461
|
}
|
|
14462
|
+
function transformPoint(point, transform) {
|
|
14463
|
+
if (transform === null) return point;
|
|
14464
|
+
const m = transform.linear;
|
|
14465
|
+
return [
|
|
14466
|
+
(m[0] ?? 1) * point[0] + (m[1] ?? 0) * point[1] + (m[2] ?? 0) * point[2] + transform.translation[0],
|
|
14467
|
+
(m[3] ?? 0) * point[0] + (m[4] ?? 1) * point[1] + (m[5] ?? 0) * point[2] + transform.translation[1],
|
|
14468
|
+
(m[6] ?? 0) * point[0] + (m[7] ?? 0) * point[1] + (m[8] ?? 1) * point[2] + transform.translation[2]
|
|
14469
|
+
];
|
|
14470
|
+
}
|
|
14293
14471
|
function packBinaryStl(vertices, indices, scaleToMm) {
|
|
14294
14472
|
const triCount = Math.floor(indices.length / 3);
|
|
14295
14473
|
const buffer = /* @__PURE__ */ new ArrayBuffer(84 + triCount * 50);
|
|
@@ -14452,19 +14630,99 @@ function profileToFace(profile) {
|
|
|
14452
14630
|
if (!face.ok) return (0, brepjs.err)(importError("GEOMETRY_RECONSTRUCTION_FAILED", `Profile face build failed: ${face.error.message}`));
|
|
14453
14631
|
return (0, brepjs.ok)(face.value);
|
|
14454
14632
|
}
|
|
14455
|
-
|
|
14456
|
-
|
|
14457
|
-
|
|
14458
|
-
|
|
14459
|
-
|
|
14460
|
-
|
|
14461
|
-
|
|
14462
|
-
|
|
14463
|
-
|
|
14464
|
-
|
|
14465
|
-
|
|
14466
|
-
|
|
14467
|
-
|
|
14633
|
+
/**
|
|
14634
|
+
* IfcParameterizedProfileDef.Position places the profile inside the swept
|
|
14635
|
+
* solid's XY plane; the writers rely on it to corner-anchor centred rectangle
|
|
14636
|
+
* profiles. Absent on arbitrary (polyline) profiles.
|
|
14637
|
+
*/
|
|
14638
|
+
function readProfilePosition(reader, profileExpressId, scale) {
|
|
14639
|
+
const def = reader.getLine(profileExpressId);
|
|
14640
|
+
const positionRef = def === null ? void 0 : asRef(def["Position"]);
|
|
14641
|
+
if (positionRef === void 0) return null;
|
|
14642
|
+
const position = reader.getLine(positionRef.value);
|
|
14643
|
+
if (position === null) return null;
|
|
14644
|
+
const [x, y] = readPoint2D(reader, position["Location"], scale) ?? [0, 0];
|
|
14645
|
+
const [dx, dy] = readDirection2D(reader, position["RefDirection"]) ?? [1, 0];
|
|
14646
|
+
const len = Math.hypot(dx, dy);
|
|
14647
|
+
const c = len < 1e-12 ? 1 : dx / len;
|
|
14648
|
+
const s = len < 1e-12 ? 0 : dy / len;
|
|
14649
|
+
return {
|
|
14650
|
+
linear: [
|
|
14651
|
+
c,
|
|
14652
|
+
-s,
|
|
14653
|
+
0,
|
|
14654
|
+
s,
|
|
14655
|
+
c,
|
|
14656
|
+
0,
|
|
14657
|
+
0,
|
|
14658
|
+
0,
|
|
14659
|
+
1
|
|
14660
|
+
],
|
|
14661
|
+
translation: [
|
|
14662
|
+
x,
|
|
14663
|
+
y,
|
|
14664
|
+
0
|
|
14665
|
+
]
|
|
14666
|
+
};
|
|
14667
|
+
}
|
|
14668
|
+
/**
|
|
14669
|
+
* Applies the placement chain (outermost first) as one composed rigid motion.
|
|
14670
|
+
* A second applyMatrix on an already-transformed solid can fail BRepCheck on
|
|
14671
|
+
* occt-wasm, so the frames are never applied one after another.
|
|
14672
|
+
*/
|
|
14673
|
+
function placeSolid(solid, frames) {
|
|
14674
|
+
let composed = null;
|
|
14675
|
+
for (const frame of frames) {
|
|
14676
|
+
if (frame === null) continue;
|
|
14677
|
+
composed = composed === null ? frame : composeRigid(composed, frame);
|
|
14678
|
+
}
|
|
14679
|
+
if (composed === null || isIdentity(composed)) return (0, brepjs.ok)(solid);
|
|
14680
|
+
const applied = (0, brepjs.applyMatrix)(solid, composed);
|
|
14681
|
+
solid[Symbol.dispose]();
|
|
14682
|
+
if (!applied.ok) return (0, brepjs.err)(importError("PLACEMENT_READ_FAILED", `Placement transform failed: ${applied.error.message}`));
|
|
14683
|
+
return (0, brepjs.ok)(applied.value);
|
|
14684
|
+
}
|
|
14685
|
+
/** `outer ∘ inner`: the motion that applies `inner` first, then `outer`. */
|
|
14686
|
+
function composeRigid(outer, inner) {
|
|
14687
|
+
const [a00, a01, a02, a10, a11, a12, a20, a21, a22] = outer.linear;
|
|
14688
|
+
const [b00, b01, b02, b10, b11, b12, b20, b21, b22] = inner.linear;
|
|
14689
|
+
const [tx, ty, tz] = inner.translation;
|
|
14690
|
+
const [ux, uy, uz] = outer.translation;
|
|
14691
|
+
return {
|
|
14692
|
+
linear: [
|
|
14693
|
+
a00 * b00 + a01 * b10 + a02 * b20,
|
|
14694
|
+
a00 * b01 + a01 * b11 + a02 * b21,
|
|
14695
|
+
a00 * b02 + a01 * b12 + a02 * b22,
|
|
14696
|
+
a10 * b00 + a11 * b10 + a12 * b20,
|
|
14697
|
+
a10 * b01 + a11 * b11 + a12 * b21,
|
|
14698
|
+
a10 * b02 + a11 * b12 + a12 * b22,
|
|
14699
|
+
a20 * b00 + a21 * b10 + a22 * b20,
|
|
14700
|
+
a20 * b01 + a21 * b11 + a22 * b21,
|
|
14701
|
+
a20 * b02 + a21 * b12 + a22 * b22
|
|
14702
|
+
],
|
|
14703
|
+
translation: [
|
|
14704
|
+
a00 * tx + a01 * ty + a02 * tz + ux,
|
|
14705
|
+
a10 * tx + a11 * ty + a12 * tz + uy,
|
|
14706
|
+
a20 * tx + a21 * ty + a22 * tz + uz
|
|
14707
|
+
]
|
|
14708
|
+
};
|
|
14709
|
+
}
|
|
14710
|
+
/** Rᵀ·v for a rigid frame's rotation part. */
|
|
14711
|
+
function rotateByTranspose(linear, v) {
|
|
14712
|
+
return [
|
|
14713
|
+
linear[0] * v[0] + linear[3] * v[1] + linear[6] * v[2],
|
|
14714
|
+
linear[1] * v[0] + linear[4] * v[1] + linear[7] * v[2],
|
|
14715
|
+
linear[2] * v[0] + linear[5] * v[1] + linear[8] * v[2]
|
|
14716
|
+
];
|
|
14717
|
+
}
|
|
14718
|
+
/** Maps a point from the frame's parent space into the frame: Rᵀ·(p − t). */
|
|
14719
|
+
function inverseRigidPoint(frame, p) {
|
|
14720
|
+
const t = frame.translation;
|
|
14721
|
+
return rotateByTranspose(frame.linear, [
|
|
14722
|
+
p[0] - t[0],
|
|
14723
|
+
p[1] - t[1],
|
|
14724
|
+
p[2] - t[2]
|
|
14725
|
+
]);
|
|
14468
14726
|
}
|
|
14469
14727
|
function finalizeSolid(solid, entity, diagnostics) {
|
|
14470
14728
|
if (!(0, brepjs.isSolid)(solid)) {
|
|
@@ -14581,15 +14839,16 @@ function isIdentity(t) {
|
|
|
14581
14839
|
for (let i = 0; i < 9; i++) if (Math.abs((t.linear[i] ?? 0) - (expected[i] ?? 0)) > 1e-9) return false;
|
|
14582
14840
|
return Math.abs(t.translation[0]) < 1e-9 && Math.abs(t.translation[1]) < 1e-9 && Math.abs(t.translation[2]) < 1e-9;
|
|
14583
14841
|
}
|
|
14584
|
-
function
|
|
14842
|
+
function findBodyItems(reader, representations) {
|
|
14585
14843
|
for (const repRef of representations) {
|
|
14586
14844
|
const rep = reader.getLine(repRef.value);
|
|
14587
14845
|
if (rep === null) continue;
|
|
14588
14846
|
if (readLabel(rep["RepresentationIdentifier"]) !== "Body") continue;
|
|
14589
14847
|
const items = asRefArray(rep["Items"]);
|
|
14590
|
-
if (items === void 0
|
|
14591
|
-
return items
|
|
14848
|
+
if (items === void 0) return [];
|
|
14849
|
+
return items.map((item) => item.value);
|
|
14592
14850
|
}
|
|
14851
|
+
return null;
|
|
14593
14852
|
}
|
|
14594
14853
|
function readPolylinePoints(reader, ref, scale) {
|
|
14595
14854
|
const curveRef = asRef(ref);
|
|
@@ -14613,6 +14872,24 @@ function readPolylinePoints(reader, ref, scale) {
|
|
|
14613
14872
|
}
|
|
14614
14873
|
return out.length >= 3 ? out : void 0;
|
|
14615
14874
|
}
|
|
14875
|
+
function readPoint2D(reader, ref, scale) {
|
|
14876
|
+
const pointRef = asRef(ref);
|
|
14877
|
+
if (pointRef === void 0) return void 0;
|
|
14878
|
+
const pt = reader.getLine(pointRef.value);
|
|
14879
|
+
if (pt === null) return void 0;
|
|
14880
|
+
const coords = asMeasureArray(pt["Coordinates"]);
|
|
14881
|
+
if (coords.length < 2) return void 0;
|
|
14882
|
+
return [(coords[0] ?? 0) * scale * 1e3, (coords[1] ?? 0) * scale * 1e3];
|
|
14883
|
+
}
|
|
14884
|
+
function readDirection2D(reader, ref) {
|
|
14885
|
+
const dirRef = asRef(ref);
|
|
14886
|
+
if (dirRef === void 0) return void 0;
|
|
14887
|
+
const dir = reader.getLine(dirRef.value);
|
|
14888
|
+
if (dir === null) return void 0;
|
|
14889
|
+
const ratios = asMeasureArray(dir["DirectionRatios"]);
|
|
14890
|
+
if (ratios.length < 2) return void 0;
|
|
14891
|
+
return [ratios[0] ?? 0, ratios[1] ?? 0];
|
|
14892
|
+
}
|
|
14616
14893
|
function readPoint(reader, ref, scale) {
|
|
14617
14894
|
const pointRef = asRef(ref);
|
|
14618
14895
|
if (pointRef === void 0) return void 0;
|
|
@@ -14639,6 +14916,9 @@ function readDirection(reader, ref) {
|
|
|
14639
14916
|
ratios[2] ?? 0
|
|
14640
14917
|
];
|
|
14641
14918
|
}
|
|
14919
|
+
function releaseEmbind(value) {
|
|
14920
|
+
value.delete?.();
|
|
14921
|
+
}
|
|
14642
14922
|
function asRef(value) {
|
|
14643
14923
|
if (value !== null && typeof value === "object" && "value" in value) {
|
|
14644
14924
|
const v = value.value;
|
|
@@ -14673,8 +14953,8 @@ function readLabel(value) {
|
|
|
14673
14953
|
if (typeof v === "string") return v;
|
|
14674
14954
|
}
|
|
14675
14955
|
}
|
|
14676
|
-
function
|
|
14677
|
-
return type === web_ifc.
|
|
14956
|
+
function isStreamedTessellatedType(type) {
|
|
14957
|
+
return type === web_ifc.IFCPOLYGONALFACESET || type === web_ifc.IFCFACEBASEDSURFACEMODEL;
|
|
14678
14958
|
}
|
|
14679
14959
|
function normalize(v) {
|
|
14680
14960
|
const len = Math.hypot(v[0], v[1], v[2]);
|
|
@@ -15013,6 +15293,7 @@ function readOwnerHistory(reader, ownerHistoryExpressId) {
|
|
|
15013
15293
|
}
|
|
15014
15294
|
//#endregion
|
|
15015
15295
|
//#region src/import/fromIfc.ts
|
|
15296
|
+
var testHooks = null;
|
|
15016
15297
|
/**
|
|
15017
15298
|
* Each enumerated physical-element IFC type and the {@link ImportedElementCategory}
|
|
15018
15299
|
* it maps to. Curtain-wall sub-components (IfcPlate/IfcMember) and stair/ramp
|
|
@@ -15061,6 +15342,7 @@ async function fromIfc(bytes, options = {}) {
|
|
|
15061
15342
|
const readerResult = await SpfReader.create(bytes, settings);
|
|
15062
15343
|
if (!readerResult.ok) return (0, brepjs.err)(readerResult.error);
|
|
15063
15344
|
const reader = readerResult.value;
|
|
15345
|
+
const elements = [];
|
|
15064
15346
|
try {
|
|
15065
15347
|
reader.buildGuidMap();
|
|
15066
15348
|
const diagnostics = [];
|
|
@@ -15069,13 +15351,13 @@ async function fromIfc(bytes, options = {}) {
|
|
|
15069
15351
|
const spatialRoot = buildSpatialTree(reader, scale);
|
|
15070
15352
|
const containment = buildElementContainmentMap(reader);
|
|
15071
15353
|
const typeEnums = buildTypePredefinedMap(reader);
|
|
15072
|
-
const elements = [];
|
|
15073
15354
|
const byExpressId = /* @__PURE__ */ new Map();
|
|
15074
15355
|
for (const [type, category] of ELEMENT_TYPES) for (const expressId of reader.getLinesOfType(type)) {
|
|
15075
15356
|
const element = readElement(reader, expressId, category, scale, containment, typeEnums, options.skipGeometry ?? false, diagnostics);
|
|
15076
15357
|
if (element === null) continue;
|
|
15077
15358
|
elements.push(element);
|
|
15078
15359
|
byExpressId.set(element.expressId, element);
|
|
15360
|
+
testHooks?.afterElement?.(element, elements.length);
|
|
15079
15361
|
}
|
|
15080
15362
|
const report = appendIssues(emptyReport(), diagnostics);
|
|
15081
15363
|
const applicationName = readApplicationName(reader);
|
|
@@ -15089,6 +15371,7 @@ async function fromIfc(bytes, options = {}) {
|
|
|
15089
15371
|
};
|
|
15090
15372
|
return (0, brepjs.ok)(model);
|
|
15091
15373
|
} catch (e) {
|
|
15374
|
+
disposeElements(elements);
|
|
15092
15375
|
return (0, brepjs.err)(importError("IMPORT_FAILED", "Unexpected failure during IFC import", e));
|
|
15093
15376
|
} finally {
|
|
15094
15377
|
reader.close();
|
|
@@ -15100,6 +15383,7 @@ async function fromIfc(bytes, options = {}) {
|
|
|
15100
15383
|
* sub-read failure is isolated by its own reader and never aborts the element.
|
|
15101
15384
|
*/
|
|
15102
15385
|
function readElement(reader, expressId, category, scale, containment, typeEnums, skipGeometry, diagnostics) {
|
|
15386
|
+
let geometry = null;
|
|
15103
15387
|
try {
|
|
15104
15388
|
const line = reader.getLine(expressId);
|
|
15105
15389
|
if (line === null) {
|
|
@@ -15111,10 +15395,15 @@ function readElement(reader, expressId, category, scale, containment, typeEnums,
|
|
|
15111
15395
|
const predefinedType = readPredefinedType(line) ?? typeEnums.get(expressId);
|
|
15112
15396
|
const voidedBy = readVoids(reader, expressId).map((v) => v.openingExpressId);
|
|
15113
15397
|
const fills = findFills(reader, expressId);
|
|
15114
|
-
|
|
15398
|
+
geometry = skipGeometry ? {
|
|
15115
15399
|
fidelity: "NONE",
|
|
15116
|
-
|
|
15400
|
+
completeness: "NONE",
|
|
15401
|
+
solids: [],
|
|
15402
|
+
solid: null,
|
|
15403
|
+
bounds: null,
|
|
15404
|
+
volumeMm3: null
|
|
15117
15405
|
} : reconstructGeometry(reader, expressId, scale, voidedBy, diagnostics);
|
|
15406
|
+
testHooks?.afterGeometry?.(expressId, geometry);
|
|
15118
15407
|
const psets = readPsets(reader, expressId).map(toImportedPset);
|
|
15119
15408
|
const material = readMaterial(reader, expressId, scale);
|
|
15120
15409
|
const classification = readClassification(reader, expressId);
|
|
@@ -15137,6 +15426,7 @@ function readElement(reader, expressId, category, scale, containment, typeEnums,
|
|
|
15137
15426
|
...fills !== void 0 ? { fills } : {}
|
|
15138
15427
|
};
|
|
15139
15428
|
} catch (e) {
|
|
15429
|
+
if (geometry !== null) disposeGeometry(geometry);
|
|
15140
15430
|
diagnostics.push(issue("error", "ELEMENT_READ_FAILED", `Element ${expressId} reconstruction threw: ${errMsg(e)}`, expressId));
|
|
15141
15431
|
return null;
|
|
15142
15432
|
}
|
|
@@ -15158,42 +15448,134 @@ function findFills(reader, elementExpressId) {
|
|
|
15158
15448
|
* model. Falls back to the uncut solid (with a diagnostic) if a cut fails.
|
|
15159
15449
|
*/
|
|
15160
15450
|
function reconstructGeometry(reader, expressId, scale, voidedBy, diagnostics) {
|
|
15161
|
-
const base = toImportedGeometry(
|
|
15162
|
-
if (base.fidelity !== "PARAMETRIC" || base.
|
|
15163
|
-
|
|
15451
|
+
const base = toImportedGeometry(reader, expressId, scale, diagnostics);
|
|
15452
|
+
if (base.fidelity !== "PARAMETRIC" || base.completeness !== "COMPLETE" || voidedBy.length === 0) return base;
|
|
15453
|
+
const firstHost = base.solids[0];
|
|
15454
|
+
if (firstHost === void 0) return base;
|
|
15455
|
+
const hosts = [firstHost, ...base.solids.slice(1)];
|
|
15164
15456
|
for (const openingId of voidedBy) {
|
|
15165
15457
|
const opening = readBodyGeometry(reader, openingId, scale, diagnostics);
|
|
15166
15458
|
if (opening.kind !== "SOLID") continue;
|
|
15167
|
-
|
|
15168
|
-
|
|
15169
|
-
|
|
15170
|
-
|
|
15171
|
-
|
|
15172
|
-
|
|
15173
|
-
|
|
15174
|
-
|
|
15175
|
-
|
|
15459
|
+
try {
|
|
15460
|
+
for (let hostIndex = 0; hostIndex < hosts.length; hostIndex++) {
|
|
15461
|
+
const host = hosts[hostIndex];
|
|
15462
|
+
if (host === void 0) continue;
|
|
15463
|
+
const cutResult = (0, brepjs.cut)(host, opening.solid);
|
|
15464
|
+
if (!cutResult.ok) {
|
|
15465
|
+
diagnostics.push(issue("warning", "VOID_SUBTRACTION_FAILED", `Opening ${openingId} could not be subtracted from element ${expressId}: ${cutResult.error.message}`, expressId));
|
|
15466
|
+
continue;
|
|
15467
|
+
}
|
|
15468
|
+
host[Symbol.dispose]();
|
|
15469
|
+
hosts[hostIndex] = cutResult.value;
|
|
15470
|
+
}
|
|
15471
|
+
} finally {
|
|
15472
|
+
opening.solid[Symbol.dispose]();
|
|
15473
|
+
}
|
|
15474
|
+
}
|
|
15475
|
+
return completeImportedGeometry("PARAMETRIC", hosts, expressId, diagnostics);
|
|
15476
|
+
}
|
|
15477
|
+
function toImportedGeometry(reader, expressId, scale, diagnostics) {
|
|
15478
|
+
const body = readBodyItems(reader, expressId, scale, diagnostics);
|
|
15479
|
+
const solids = [];
|
|
15480
|
+
let hasTessellatedSolid = false;
|
|
15481
|
+
let lossyMesh = null;
|
|
15482
|
+
for (const item of body.items) if (item.kind === "SOLID") {
|
|
15483
|
+
solids.push(item.solid);
|
|
15484
|
+
hasTessellatedSolid ||= item.lossy;
|
|
15485
|
+
} else if (item.kind === "MESH") {
|
|
15486
|
+
const meshAccumulator = lossyMesh ?? {
|
|
15487
|
+
vertices: [],
|
|
15488
|
+
indices: []
|
|
15489
|
+
};
|
|
15490
|
+
lossyMesh = meshAccumulator;
|
|
15491
|
+
const vertexOffset = meshAccumulator.vertices.length / 3;
|
|
15492
|
+
for (const vertex of item.vertices) meshAccumulator.vertices.push(vertex);
|
|
15493
|
+
for (const index of item.indices) meshAccumulator.indices.push(vertexOffset + index);
|
|
15494
|
+
}
|
|
15495
|
+
const completeness = body.hasBody && body.itemCount > 0 && solids.length === body.itemCount ? "COMPLETE" : solids.length > 0 ? "PARTIAL" : "NONE";
|
|
15496
|
+
if (completeness === "PARTIAL") diagnostics.push(issue("warning", "PARTIAL_BODY_RECONSTRUCTION", `Reconstructed ${solids.length} of ${body.itemCount} Body items`, expressId, {
|
|
15497
|
+
reconstructedItems: solids.length,
|
|
15498
|
+
bodyItems: body.itemCount
|
|
15499
|
+
}));
|
|
15500
|
+
else if (completeness === "NONE" && body.hasBody) diagnostics.push(issue("warning", "BODY_RECONSTRUCTION_NONE", `The Product has ${body.itemCount} Body item(s), but none reconstructed as solids`, expressId, { bodyItems: body.itemCount }));
|
|
15501
|
+
const fidelity = lossyMesh !== null ? "TESSELLATED_LOSSY" : hasTessellatedSolid ? "TESSELLATED_MANIFOLD" : solids.length > 0 ? "PARAMETRIC" : "NONE";
|
|
15502
|
+
const aggregate = completeness === "COMPLETE" ? measureCompleteBody(solids, expressId, diagnostics) : {
|
|
15503
|
+
bounds: null,
|
|
15504
|
+
volumeMm3: null
|
|
15505
|
+
};
|
|
15176
15506
|
return {
|
|
15177
|
-
fidelity
|
|
15178
|
-
|
|
15507
|
+
fidelity,
|
|
15508
|
+
completeness,
|
|
15509
|
+
solids,
|
|
15510
|
+
solid: completeness === "COMPLETE" && solids.length === 1 ? solids[0] ?? null : null,
|
|
15511
|
+
...aggregate,
|
|
15512
|
+
...lossyMesh !== null ? {
|
|
15513
|
+
meshVertices: new Float32Array(lossyMesh.vertices),
|
|
15514
|
+
meshIndices: new Uint32Array(lossyMesh.indices)
|
|
15515
|
+
} : {}
|
|
15179
15516
|
};
|
|
15180
15517
|
}
|
|
15181
|
-
function
|
|
15182
|
-
if (result.kind === "SOLID") return {
|
|
15183
|
-
fidelity: result.lossy ? "TESSELLATED_MANIFOLD" : "PARAMETRIC",
|
|
15184
|
-
solid: result.solid
|
|
15185
|
-
};
|
|
15186
|
-
if (result.kind === "MESH") return {
|
|
15187
|
-
fidelity: "TESSELLATED_LOSSY",
|
|
15188
|
-
solid: null,
|
|
15189
|
-
meshVertices: result.vertices,
|
|
15190
|
-
meshIndices: result.indices
|
|
15191
|
-
};
|
|
15518
|
+
function completeImportedGeometry(fidelity, solids, expressId, diagnostics) {
|
|
15192
15519
|
return {
|
|
15193
|
-
fidelity
|
|
15194
|
-
|
|
15520
|
+
fidelity,
|
|
15521
|
+
completeness: "COMPLETE",
|
|
15522
|
+
solids,
|
|
15523
|
+
solid: solids.length === 1 ? solids[0] : null,
|
|
15524
|
+
...measureCompleteBody(solids, expressId, diagnostics)
|
|
15195
15525
|
};
|
|
15196
15526
|
}
|
|
15527
|
+
function measureCompleteBody(solids, expressId, diagnostics) {
|
|
15528
|
+
try {
|
|
15529
|
+
const first = solids[0];
|
|
15530
|
+
if (first === void 0) return {
|
|
15531
|
+
bounds: null,
|
|
15532
|
+
volumeMm3: null
|
|
15533
|
+
};
|
|
15534
|
+
const initial = (0, brepjs.getBounds)(first);
|
|
15535
|
+
let xMin = initial.xMin;
|
|
15536
|
+
let xMax = initial.xMax;
|
|
15537
|
+
let yMin = initial.yMin;
|
|
15538
|
+
let yMax = initial.yMax;
|
|
15539
|
+
let zMin = initial.zMin;
|
|
15540
|
+
let zMax = initial.zMax;
|
|
15541
|
+
let volumeMm3 = 0;
|
|
15542
|
+
for (const solid of solids) {
|
|
15543
|
+
const measured = (0, brepjs.measureVolume)(solid);
|
|
15544
|
+
if (!measured.ok) throw new Error(measured.error.message);
|
|
15545
|
+
volumeMm3 += measured.value;
|
|
15546
|
+
const itemBounds = (0, brepjs.getBounds)(solid);
|
|
15547
|
+
xMin = Math.min(xMin, itemBounds.xMin);
|
|
15548
|
+
xMax = Math.max(xMax, itemBounds.xMax);
|
|
15549
|
+
yMin = Math.min(yMin, itemBounds.yMin);
|
|
15550
|
+
yMax = Math.max(yMax, itemBounds.yMax);
|
|
15551
|
+
zMin = Math.min(zMin, itemBounds.zMin);
|
|
15552
|
+
zMax = Math.max(zMax, itemBounds.zMax);
|
|
15553
|
+
}
|
|
15554
|
+
return {
|
|
15555
|
+
bounds: {
|
|
15556
|
+
xMin,
|
|
15557
|
+
xMax,
|
|
15558
|
+
yMin,
|
|
15559
|
+
yMax,
|
|
15560
|
+
zMin,
|
|
15561
|
+
zMax
|
|
15562
|
+
},
|
|
15563
|
+
volumeMm3
|
|
15564
|
+
};
|
|
15565
|
+
} catch (cause) {
|
|
15566
|
+
diagnostics.push(issue("warning", "BODY_AGGREGATE_MEASUREMENT_FAILED", `Complete Body aggregate measurement failed: ${errMsg(cause)}`, expressId));
|
|
15567
|
+
return {
|
|
15568
|
+
bounds: null,
|
|
15569
|
+
volumeMm3: null
|
|
15570
|
+
};
|
|
15571
|
+
}
|
|
15572
|
+
}
|
|
15573
|
+
function disposeGeometry(geometry) {
|
|
15574
|
+
for (const solid of geometry.solids) solid[Symbol.dispose]();
|
|
15575
|
+
}
|
|
15576
|
+
function disposeElements(elements) {
|
|
15577
|
+
for (const element of elements) disposeGeometry(element.geometry);
|
|
15578
|
+
}
|
|
15197
15579
|
function toImportedPset(pset) {
|
|
15198
15580
|
return {
|
|
15199
15581
|
name: pset.name,
|
|
@@ -15271,7 +15653,7 @@ function errMsg(e) {
|
|
|
15271
15653
|
* with the model (it is not reclaimed automatically when the model is GC'd).
|
|
15272
15654
|
*/
|
|
15273
15655
|
function disposeImportedModel(model) {
|
|
15274
|
-
for (const el of model.elements) el.geometry.solid
|
|
15656
|
+
for (const el of model.elements) for (const solid of el.geometry.solids) solid[Symbol.dispose]();
|
|
15275
15657
|
}
|
|
15276
15658
|
//#endregion
|
|
15277
15659
|
//#region src/specs/materialSpec.ts
|
|
@@ -28855,13 +29237,25 @@ function specLocalShift(el) {
|
|
|
28855
29237
|
0
|
|
28856
29238
|
];
|
|
28857
29239
|
}
|
|
29240
|
+
/** Skip the resolver occurrence wrappers already owned by `cumulativeFrame`,
|
|
29241
|
+
* then recover only the Body's outer literal Datum translations. An unexpected
|
|
29242
|
+
* wrapper shape returns zero rather than risking an occurrence transform twice. */
|
|
29243
|
+
function bodyLocalTranslation(el, occurrenceTransformDepth) {
|
|
29244
|
+
let body = el.geometry;
|
|
29245
|
+
for (let i = 0; i < occurrenceTransformDepth; i++) {
|
|
29246
|
+
if (body.kind !== "Translate" && body.kind !== "Rotate") return ZERO_TRANSLATION;
|
|
29247
|
+
body = body.target;
|
|
29248
|
+
}
|
|
29249
|
+
return peelTranslates(body).total;
|
|
29250
|
+
}
|
|
28858
29251
|
/** World frame of an element's own body: the walk's cumulative frame (all
|
|
28859
29252
|
* 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
|
-
|
|
29253
|
+
* (`axisX`/`axisZ` props) and its local origin (`origin` prop + literal Body
|
|
29254
|
+
* Datum + centring shift). Under no rotation this reduces to `composedOrigin`
|
|
29255
|
+
* + shift. */
|
|
29256
|
+
function elementBodyFrame(el, cumulativeFrame, occurrenceTransformDepth) {
|
|
28863
29257
|
return frameMul(frameMul(cumulativeFrame, frameFromPlacement({
|
|
28864
|
-
origin: authoredSpecOrigin(el),
|
|
29258
|
+
origin: addTranslation(authoredSpecOrigin(el), bodyLocalTranslation(el, occurrenceTransformDepth)),
|
|
28865
29259
|
axisX: authoredAxisX(el),
|
|
28866
29260
|
axisZ: authoredAxisZ(el)
|
|
28867
29261
|
})), translationFrame(specLocalShift(el)));
|
|
@@ -28880,10 +29274,10 @@ function civilNodeFrame(el, cumulativeFrame) {
|
|
|
28880
29274
|
* `axisZ` are recomputed from the element's world frame relative to its spatial
|
|
28881
29275
|
* container. Stair/ramp flights, which carry their own per-flight frame, are
|
|
28882
29276
|
* each re-placed the same way. */
|
|
28883
|
-
function rotatedRoutedInput(flatInput, el, cumulativeFrame, spatialFrame) {
|
|
29277
|
+
function rotatedRoutedInput(flatInput, el, cumulativeFrame, occurrenceTransformDepth, spatialFrame) {
|
|
28884
29278
|
const toSpatial = frameInverse(spatialFrame);
|
|
28885
29279
|
if (Array.isArray(flatInput["flights"])) {
|
|
28886
|
-
const elementFrame = elementBodyFrame(el, cumulativeFrame);
|
|
29280
|
+
const elementFrame = elementBodyFrame(el, cumulativeFrame, occurrenceTransformDepth);
|
|
28887
29281
|
const flights = flatInput["flights"];
|
|
28888
29282
|
return {
|
|
28889
29283
|
...flatInput,
|
|
@@ -28924,7 +29318,7 @@ function rotatedRoutedInput(flatInput, el, cumulativeFrame, spatialFrame) {
|
|
|
28924
29318
|
})
|
|
28925
29319
|
};
|
|
28926
29320
|
}
|
|
28927
|
-
const placed = decomposeFrame(frameMul(toSpatial, elementBodyFrame(el, cumulativeFrame)));
|
|
29321
|
+
const placed = decomposeFrame(frameMul(toSpatial, elementBodyFrame(el, cumulativeFrame, occurrenceTransformDepth)));
|
|
28928
29322
|
return {
|
|
28929
29323
|
...flatInput,
|
|
28930
29324
|
origin: placed.origin,
|
|
@@ -29089,6 +29483,7 @@ function familiesToBim(root, options) {
|
|
|
29089
29483
|
const walk = (el, state) => {
|
|
29090
29484
|
const rotatedHere = state.rotated || hasRotateOp(el);
|
|
29091
29485
|
const cumulativeTranslationHere = addTranslation(state.cumulativeTranslation, authoredTranslation(el));
|
|
29486
|
+
const occurrenceTransformDepthHere = state.occurrenceTransformDepth + el.localTransforms.length;
|
|
29092
29487
|
const cumulativeFrameHere = frameMul(state.cumulativeFrame, frameFromOps(el.localTransforms));
|
|
29093
29488
|
let proxiedHere = false;
|
|
29094
29489
|
let nextRotated = rotatedHere;
|
|
@@ -29152,7 +29547,7 @@ function familiesToBim(root, options) {
|
|
|
29152
29547
|
}
|
|
29153
29548
|
const routedInput = el.semantics?.kind === "product" ? civilProductSpecInput(el) : ("input" in route ? route.input : specInput)(el);
|
|
29154
29549
|
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;
|
|
29550
|
+
const placedInput = rotatedHere ? rotatedRoutedInput(rotatedBase, el, cumulativeFrameHere, occurrenceTransformDepthHere, state.spatialFrame) : usesAuthoredCivilHierarchy ? relativeSpecInput(routedInput, state.projectedSpatialTranslation) : routedInput;
|
|
29156
29551
|
const parsed = route.parse(placedInput);
|
|
29157
29552
|
if (!parsed.ok) return parsed;
|
|
29158
29553
|
const added = route.add(model, parsed.value, el.keyPath);
|
|
@@ -29189,6 +29584,7 @@ function familiesToBim(root, options) {
|
|
|
29189
29584
|
rotated: nextRotated,
|
|
29190
29585
|
cumulativeTranslation: cumulativeTranslationHere,
|
|
29191
29586
|
projectedSpatialTranslation: nextProjectedSpatialTranslation,
|
|
29587
|
+
occurrenceTransformDepth: occurrenceTransformDepthHere,
|
|
29192
29588
|
cumulativeFrame: nextCumulativeFrame,
|
|
29193
29589
|
spatialFrame: nextSpatialFrame
|
|
29194
29590
|
});
|
|
@@ -29202,6 +29598,7 @@ function familiesToBim(root, options) {
|
|
|
29202
29598
|
rotated: false,
|
|
29203
29599
|
cumulativeTranslation: ZERO_TRANSLATION,
|
|
29204
29600
|
projectedSpatialTranslation: ZERO_TRANSLATION,
|
|
29601
|
+
occurrenceTransformDepth: 0,
|
|
29205
29602
|
cumulativeFrame: IDENTITY_FRAME,
|
|
29206
29603
|
spatialFrame: IDENTITY_FRAME
|
|
29207
29604
|
});
|