brepjs-bim 0.24.5 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -0
- package/dist/brepjs-bim.cjs +38 -265
- package/dist/brepjs-bim.js +38 -265
- package/dist/ifc-writer/geometryWriter.d.ts +0 -6
- package/dist/ifc-writer/headerWriter.d.ts +2 -0
- package/dist/ifc-writer/openingWriter.d.ts +1 -1
- package/dist/ifc-writer/tessellationWriter.d.ts +2 -2
- package/dist/serialize/productBodyPreflight.d.ts +15 -0
- package/package.json +2 -2
- package/dist/serialize/exactBodyPreflight.d.ts +0 -15
package/README.md
CHANGED
|
@@ -210,3 +210,18 @@ If measurement or temporary cleanup fails, serialization omits optional Wall qua
|
|
|
210
210
|
Opening reconstruction retains every surviving solid in source-item order, including split results. Complete removal produces no imported solids and null aggregate bounds and volume. The singleton `.solid` field remains a borrowed alias only for a complete one-solid result.
|
|
211
211
|
|
|
212
212
|
Complete imported aggregates use shared occupied-material measurement and all-item bounds. Overlaps count once. Failed measurement leaves both aggregates unavailable with an import diagnostic. Later opening failures clean the current host list, including every earlier survivor, and never retry an uncertain release.
|
|
213
|
+
|
|
214
|
+
### Retained Body IFC geometry
|
|
215
|
+
|
|
216
|
+
Wall and Railing exports preflight and tessellate every retained item in order, preserving placement, styles, and metadata.
|
|
217
|
+
|
|
218
|
+
Wall openings export as IFC `Reference` geometry because the retained Wall Body already
|
|
219
|
+
contains its cuts. Void/fill relationships and opening placements remain intact. IFC4 defines
|
|
220
|
+
Reference openings as non-subtractive; gross recipe exports such as Slab openings still use
|
|
221
|
+
subtractive `Body` geometry. The importer keeps Reference opening records and relationships
|
|
222
|
+
without treating their reference shape as a display Body or cutting tool.
|
|
223
|
+
|
|
224
|
+
IfcOpenShell 0.8.5 still subtracts Reference openings in its default geometry engine, even with
|
|
225
|
+
a separate Reference context. If a replacement Body adds material inside a retained opening's
|
|
226
|
+
region, that engine can remove the added material. Schema validation and shape generation alone
|
|
227
|
+
do not detect this difference; check the representation semantics and retained item geometry.
|
package/dist/brepjs-bim.cjs
CHANGED
|
@@ -1719,7 +1719,7 @@ var Doc = class {
|
|
|
1719
1719
|
var version = {
|
|
1720
1720
|
major: 4,
|
|
1721
1721
|
minor: 6,
|
|
1722
|
-
patch:
|
|
1722
|
+
patch: 5
|
|
1723
1723
|
};
|
|
1724
1724
|
//#endregion
|
|
1725
1725
|
//#region ../../node_modules/zod/v4/core/schemas.js
|
|
@@ -9765,11 +9765,21 @@ function writeHeader(w, meta) {
|
|
|
9765
9765
|
WorldCoordinateSystem: w.ref(writeAxis2Placement3D(w)),
|
|
9766
9766
|
TrueNorth: null
|
|
9767
9767
|
});
|
|
9768
|
+
return {
|
|
9769
|
+
ownerHistoryId,
|
|
9770
|
+
geomContextId,
|
|
9771
|
+
geomSubContextId: writeGeometricSubContext(w, geomContextId, "Body"),
|
|
9772
|
+
unitAssignmentId,
|
|
9773
|
+
lengthUnitId
|
|
9774
|
+
};
|
|
9775
|
+
}
|
|
9776
|
+
/** Separates display Body geometry from non-subtractive Reference geometry. */
|
|
9777
|
+
function writeGeometricSubContext(w, geomContextId, identifier) {
|
|
9768
9778
|
const geomSubContextId = w.nextId();
|
|
9769
9779
|
w.writeLine({
|
|
9770
9780
|
expressID: geomSubContextId,
|
|
9771
9781
|
type: web_ifc.IFCGEOMETRICREPRESENTATIONSUBCONTEXT,
|
|
9772
|
-
ContextIdentifier: w.mkType(web_ifc.IFCLABEL,
|
|
9782
|
+
ContextIdentifier: w.mkType(web_ifc.IFCLABEL, identifier),
|
|
9773
9783
|
ContextType: w.mkType(web_ifc.IFCLABEL, "Model"),
|
|
9774
9784
|
CoordinateSpaceDimension: null,
|
|
9775
9785
|
Precision: null,
|
|
@@ -9783,13 +9793,7 @@ function writeHeader(w, meta) {
|
|
|
9783
9793
|
},
|
|
9784
9794
|
UserDefinedTargetView: null
|
|
9785
9795
|
});
|
|
9786
|
-
return
|
|
9787
|
-
ownerHistoryId,
|
|
9788
|
-
geomContextId,
|
|
9789
|
-
geomSubContextId,
|
|
9790
|
-
unitAssignmentId,
|
|
9791
|
-
lengthUnitId
|
|
9792
|
-
};
|
|
9796
|
+
return geomSubContextId;
|
|
9793
9797
|
}
|
|
9794
9798
|
/**
|
|
9795
9799
|
* Writes IfcProjectedCRS + IfcMapConversion georeferencing the model context
|
|
@@ -10197,7 +10201,7 @@ function writePreparedTessellationBody(w, items, geomSubContextId) {
|
|
|
10197
10201
|
bodyItemIds
|
|
10198
10202
|
};
|
|
10199
10203
|
}
|
|
10200
|
-
function
|
|
10204
|
+
function writeProductBodyGeometry(w, placement, items, geomSubContextId, parentPlacementId) {
|
|
10201
10205
|
const placement3DId = writeAxis2Placement3D(w, [
|
|
10202
10206
|
toIfcLengthM(placement.origin[0]),
|
|
10203
10207
|
toIfcLengthM(placement.origin[1]),
|
|
@@ -10243,47 +10247,6 @@ function writePreparedTessellationItem(w, prepared) {
|
|
|
10243
10247
|
});
|
|
10244
10248
|
return faceSetId;
|
|
10245
10249
|
}
|
|
10246
|
-
/**
|
|
10247
|
-
* Builds a wall 'Axis' IfcShapeRepresentation: an IfcPolyline from (0,0) to
|
|
10248
|
-
* (lengthM, 0) in the wall's local XY plane, wrapped in an IfcShapeRepresentation
|
|
10249
|
-
* with RepresentationIdentifier='Axis', RepresentationType='Curve2D'. Returns
|
|
10250
|
-
* the IfcShapeRepresentation express ID so callers can add it alongside the Body
|
|
10251
|
-
* representation in an IfcProductDefinitionShape.
|
|
10252
|
-
*
|
|
10253
|
-
* wallLengthMm is the wall length in mm (brepjs native units); it is converted
|
|
10254
|
-
* to metres for IFC.
|
|
10255
|
-
*/
|
|
10256
|
-
function writeWallAxisRepresentation(w, wallLengthMm, geomSubContextId) {
|
|
10257
|
-
const lengthM = toIfcLengthM(wallLengthMm);
|
|
10258
|
-
const startId = w.nextId();
|
|
10259
|
-
w.writeLine({
|
|
10260
|
-
expressID: startId,
|
|
10261
|
-
type: web_ifc.IFCCARTESIANPOINT,
|
|
10262
|
-
Coordinates: [w.mkType(web_ifc.IFCLENGTHMEASURE, 0), w.mkType(web_ifc.IFCLENGTHMEASURE, 0)]
|
|
10263
|
-
});
|
|
10264
|
-
const endId = w.nextId();
|
|
10265
|
-
w.writeLine({
|
|
10266
|
-
expressID: endId,
|
|
10267
|
-
type: web_ifc.IFCCARTESIANPOINT,
|
|
10268
|
-
Coordinates: [w.mkType(web_ifc.IFCLENGTHMEASURE, lengthM), w.mkType(web_ifc.IFCLENGTHMEASURE, 0)]
|
|
10269
|
-
});
|
|
10270
|
-
const polylineId = w.nextId();
|
|
10271
|
-
w.writeLine({
|
|
10272
|
-
expressID: polylineId,
|
|
10273
|
-
type: web_ifc.IFCPOLYLINE,
|
|
10274
|
-
Points: [w.ref(startId), w.ref(endId)]
|
|
10275
|
-
});
|
|
10276
|
-
const shapeRepId = w.nextId();
|
|
10277
|
-
w.writeLine({
|
|
10278
|
-
expressID: shapeRepId,
|
|
10279
|
-
type: web_ifc.IFCSHAPEREPRESENTATION,
|
|
10280
|
-
ContextOfItems: w.ref(geomSubContextId),
|
|
10281
|
-
RepresentationIdentifier: w.mkType(web_ifc.IFCLABEL, "Axis"),
|
|
10282
|
-
RepresentationType: w.mkType(web_ifc.IFCLABEL, "Curve2D"),
|
|
10283
|
-
Items: [w.ref(polylineId)]
|
|
10284
|
-
});
|
|
10285
|
-
return shapeRepId;
|
|
10286
|
-
}
|
|
10287
10250
|
function writeFacetedBrepFallback(w, geomSubContextId, reason) {
|
|
10288
10251
|
const originId = w.nextId();
|
|
10289
10252
|
w.writeLine({
|
|
@@ -10614,81 +10577,6 @@ function writePolyline(w, points) {
|
|
|
10614
10577
|
}
|
|
10615
10578
|
//#endregion
|
|
10616
10579
|
//#region src/ifc-writer/geometryWriter.ts
|
|
10617
|
-
function writeWallGeometry(w, spec, geomSubContextId, parentPlacementId) {
|
|
10618
|
-
const placement3DId = writeAxis2Placement3D(w, spec.origin.map(toIfcLengthM), spec.axisZ, spec.axisX);
|
|
10619
|
-
const localPlacementId = w.nextId();
|
|
10620
|
-
w.writeLine({
|
|
10621
|
-
expressID: localPlacementId,
|
|
10622
|
-
type: web_ifc.IFCLOCALPLACEMENT,
|
|
10623
|
-
PlacementRelTo: parentPlacementId !== null ? w.ref(parentPlacementId) : null,
|
|
10624
|
-
RelativePlacement: w.ref(placement3DId)
|
|
10625
|
-
});
|
|
10626
|
-
const thicknessM = toIfcLengthM(spec.thickness);
|
|
10627
|
-
const heightM = toIfcLengthM(spec.height);
|
|
10628
|
-
const lengthM = toIfcLengthM(spec.length);
|
|
10629
|
-
const profileId = w.nextId();
|
|
10630
|
-
w.writeLine({
|
|
10631
|
-
expressID: profileId,
|
|
10632
|
-
type: web_ifc.IFCRECTANGLEPROFILEDEF,
|
|
10633
|
-
ProfileType: {
|
|
10634
|
-
type: 3,
|
|
10635
|
-
value: "AREA"
|
|
10636
|
-
},
|
|
10637
|
-
ProfileName: null,
|
|
10638
|
-
Position: w.ref(writeAxis2Placement2D$1(w, [thicknessM / 2, heightM / 2])),
|
|
10639
|
-
XDim: w.mkType(web_ifc.IFCPOSITIVELENGTHMEASURE, thicknessM),
|
|
10640
|
-
YDim: w.mkType(web_ifc.IFCPOSITIVELENGTHMEASURE, heightM)
|
|
10641
|
-
});
|
|
10642
|
-
const extrusionPosId = writeAxis2Placement3D(w, [
|
|
10643
|
-
0,
|
|
10644
|
-
0,
|
|
10645
|
-
0
|
|
10646
|
-
], [
|
|
10647
|
-
1,
|
|
10648
|
-
0,
|
|
10649
|
-
0
|
|
10650
|
-
], [
|
|
10651
|
-
0,
|
|
10652
|
-
1,
|
|
10653
|
-
0
|
|
10654
|
-
]);
|
|
10655
|
-
const extrusionDirId = writeDirection(w, [
|
|
10656
|
-
0,
|
|
10657
|
-
0,
|
|
10658
|
-
1
|
|
10659
|
-
]);
|
|
10660
|
-
const extrusionId = w.nextId();
|
|
10661
|
-
w.writeLine({
|
|
10662
|
-
expressID: extrusionId,
|
|
10663
|
-
type: web_ifc.IFCEXTRUDEDAREASOLID,
|
|
10664
|
-
SweptArea: w.ref(profileId),
|
|
10665
|
-
Position: w.ref(extrusionPosId),
|
|
10666
|
-
ExtrudedDirection: w.ref(extrusionDirId),
|
|
10667
|
-
Depth: w.mkType(web_ifc.IFCPOSITIVELENGTHMEASURE, lengthM)
|
|
10668
|
-
});
|
|
10669
|
-
const shapeRepId = w.nextId();
|
|
10670
|
-
w.writeLine({
|
|
10671
|
-
expressID: shapeRepId,
|
|
10672
|
-
type: web_ifc.IFCSHAPEREPRESENTATION,
|
|
10673
|
-
ContextOfItems: w.ref(geomSubContextId),
|
|
10674
|
-
RepresentationIdentifier: w.mkType(web_ifc.IFCLABEL, "Body"),
|
|
10675
|
-
RepresentationType: w.mkType(web_ifc.IFCLABEL, "SweptSolid"),
|
|
10676
|
-
Items: [w.ref(extrusionId)]
|
|
10677
|
-
});
|
|
10678
|
-
const axisRepId = writeWallAxisRepresentation(w, spec.length, geomSubContextId);
|
|
10679
|
-
const productDefinitionShapeId = w.nextId();
|
|
10680
|
-
w.writeLine({
|
|
10681
|
-
expressID: productDefinitionShapeId,
|
|
10682
|
-
type: web_ifc.IFCPRODUCTDEFINITIONSHAPE,
|
|
10683
|
-
Name: null,
|
|
10684
|
-
Description: null,
|
|
10685
|
-
Representations: [w.ref(axisRepId), w.ref(shapeRepId)]
|
|
10686
|
-
});
|
|
10687
|
-
return {
|
|
10688
|
-
localPlacementId,
|
|
10689
|
-
productDefinitionShapeId
|
|
10690
|
-
};
|
|
10691
|
-
}
|
|
10692
10580
|
function writeSlabGeometry(w, spec, geomSubContextId, parentPlacementId) {
|
|
10693
10581
|
const placement3DId = writeAxis2Placement3D(w, spec.origin.map(toIfcLengthM), spec.axisZ, spec.axisX);
|
|
10694
10582
|
const localPlacementId = w.nextId();
|
|
@@ -11831,104 +11719,6 @@ function writeRampAssembly(w, spec, rampKey, assemblyGuid, ownerHistoryId, geomS
|
|
|
11831
11719
|
}
|
|
11832
11720
|
//#endregion
|
|
11833
11721
|
//#region src/ifc-writer/railingWriter.ts
|
|
11834
|
-
function writeRailingGeometry(w, spec, solid, geomSubContextId, parentPlacementId) {
|
|
11835
|
-
const placement3DId = writeAxis2Placement3D(w, spec.origin.map(toIfcLengthM), spec.axisZ, spec.axisX);
|
|
11836
|
-
const localPlacementId = w.nextId();
|
|
11837
|
-
w.writeLine({
|
|
11838
|
-
expressID: localPlacementId,
|
|
11839
|
-
type: web_ifc.IFCLOCALPLACEMENT,
|
|
11840
|
-
PlacementRelTo: parentPlacementId !== null ? w.ref(parentPlacementId) : null,
|
|
11841
|
-
RelativePlacement: w.ref(placement3DId)
|
|
11842
|
-
});
|
|
11843
|
-
if (spec.infill === "POSTED") {
|
|
11844
|
-
const tess = writeTessellation(w, solid, geomSubContextId, localPlacementId);
|
|
11845
|
-
return {
|
|
11846
|
-
localPlacementId,
|
|
11847
|
-
productDefinitionShapeId: tess.productDefinitionShapeId,
|
|
11848
|
-
bodyItemId: null,
|
|
11849
|
-
usedFallback: tess.usedFallback
|
|
11850
|
-
};
|
|
11851
|
-
}
|
|
11852
|
-
const thicknessM = toIfcLengthM(spec.thickness);
|
|
11853
|
-
const heightM = toIfcLengthM(spec.height);
|
|
11854
|
-
const lengthM = toIfcLengthM(spec.length);
|
|
11855
|
-
const profileOriginId = w.nextId();
|
|
11856
|
-
w.writeLine({
|
|
11857
|
-
expressID: profileOriginId,
|
|
11858
|
-
type: web_ifc.IFCCARTESIANPOINT,
|
|
11859
|
-
Coordinates: [w.mkType(web_ifc.IFCLENGTHMEASURE, thicknessM / 2), w.mkType(web_ifc.IFCLENGTHMEASURE, heightM / 2)]
|
|
11860
|
-
});
|
|
11861
|
-
const profilePosId = w.nextId();
|
|
11862
|
-
w.writeLine({
|
|
11863
|
-
expressID: profilePosId,
|
|
11864
|
-
type: web_ifc.IFCAXIS2PLACEMENT2D,
|
|
11865
|
-
Location: w.ref(profileOriginId),
|
|
11866
|
-
RefDirection: null
|
|
11867
|
-
});
|
|
11868
|
-
const profileId = w.nextId();
|
|
11869
|
-
w.writeLine({
|
|
11870
|
-
expressID: profileId,
|
|
11871
|
-
type: web_ifc.IFCRECTANGLEPROFILEDEF,
|
|
11872
|
-
ProfileType: {
|
|
11873
|
-
type: 3,
|
|
11874
|
-
value: "AREA"
|
|
11875
|
-
},
|
|
11876
|
-
ProfileName: null,
|
|
11877
|
-
Position: w.ref(profilePosId),
|
|
11878
|
-
XDim: w.mkType(web_ifc.IFCPOSITIVELENGTHMEASURE, thicknessM),
|
|
11879
|
-
YDim: w.mkType(web_ifc.IFCPOSITIVELENGTHMEASURE, heightM)
|
|
11880
|
-
});
|
|
11881
|
-
const extrusionPosId = writeAxis2Placement3D(w, [
|
|
11882
|
-
0,
|
|
11883
|
-
0,
|
|
11884
|
-
0
|
|
11885
|
-
], [
|
|
11886
|
-
1,
|
|
11887
|
-
0,
|
|
11888
|
-
0
|
|
11889
|
-
], [
|
|
11890
|
-
0,
|
|
11891
|
-
1,
|
|
11892
|
-
0
|
|
11893
|
-
]);
|
|
11894
|
-
const extrusionDirId = writeDirection(w, [
|
|
11895
|
-
0,
|
|
11896
|
-
0,
|
|
11897
|
-
1
|
|
11898
|
-
]);
|
|
11899
|
-
const extrusionId = w.nextId();
|
|
11900
|
-
w.writeLine({
|
|
11901
|
-
expressID: extrusionId,
|
|
11902
|
-
type: web_ifc.IFCEXTRUDEDAREASOLID,
|
|
11903
|
-
SweptArea: w.ref(profileId),
|
|
11904
|
-
Position: w.ref(extrusionPosId),
|
|
11905
|
-
ExtrudedDirection: w.ref(extrusionDirId),
|
|
11906
|
-
Depth: w.mkType(web_ifc.IFCPOSITIVELENGTHMEASURE, lengthM)
|
|
11907
|
-
});
|
|
11908
|
-
const shapeRepId = w.nextId();
|
|
11909
|
-
w.writeLine({
|
|
11910
|
-
expressID: shapeRepId,
|
|
11911
|
-
type: web_ifc.IFCSHAPEREPRESENTATION,
|
|
11912
|
-
ContextOfItems: w.ref(geomSubContextId),
|
|
11913
|
-
RepresentationIdentifier: w.mkType(web_ifc.IFCLABEL, "Body"),
|
|
11914
|
-
RepresentationType: w.mkType(web_ifc.IFCLABEL, "SweptSolid"),
|
|
11915
|
-
Items: [w.ref(extrusionId)]
|
|
11916
|
-
});
|
|
11917
|
-
const productDefinitionShapeId = w.nextId();
|
|
11918
|
-
w.writeLine({
|
|
11919
|
-
expressID: productDefinitionShapeId,
|
|
11920
|
-
type: web_ifc.IFCPRODUCTDEFINITIONSHAPE,
|
|
11921
|
-
Name: null,
|
|
11922
|
-
Description: null,
|
|
11923
|
-
Representations: [w.ref(shapeRepId)]
|
|
11924
|
-
});
|
|
11925
|
-
return {
|
|
11926
|
-
localPlacementId,
|
|
11927
|
-
productDefinitionShapeId,
|
|
11928
|
-
bodyItemId: extrusionId,
|
|
11929
|
-
usedFallback: false
|
|
11930
|
-
};
|
|
11931
|
-
}
|
|
11932
11722
|
function writeRailingEntity(w, guid, name, _predefinedType, ownerHistoryId, localPlacementId, productDefinitionShapeId) {
|
|
11933
11723
|
const id = w.nextId();
|
|
11934
11724
|
w.writeLine({
|
|
@@ -13367,7 +13157,7 @@ function writeAxis2Placement2D(w) {
|
|
|
13367
13157
|
});
|
|
13368
13158
|
return id;
|
|
13369
13159
|
}
|
|
13370
|
-
function writeOpeningGeometry(w, guid, openingSpec, wallSpec, wallPlacementId, geomSubContextId, ownerHistoryId) {
|
|
13160
|
+
function writeOpeningGeometry(w, guid, openingSpec, wallSpec, wallPlacementId, geomSubContextId, ownerHistoryId, representationIdentifier) {
|
|
13371
13161
|
const widthM = toIfcLengthM(openingSpec.width);
|
|
13372
13162
|
const heightM = toIfcLengthM(openingSpec.height);
|
|
13373
13163
|
const offsetAlongWallM = toIfcLengthM(openingSpec.offsetAlongWall);
|
|
@@ -13430,7 +13220,7 @@ function writeOpeningGeometry(w, guid, openingSpec, wallSpec, wallPlacementId, g
|
|
|
13430
13220
|
expressID: shapeRepId,
|
|
13431
13221
|
type: web_ifc.IFCSHAPEREPRESENTATION,
|
|
13432
13222
|
ContextOfItems: w.ref(geomSubContextId),
|
|
13433
|
-
RepresentationIdentifier: w.mkType(web_ifc.IFCLABEL,
|
|
13223
|
+
RepresentationIdentifier: w.mkType(web_ifc.IFCLABEL, representationIdentifier),
|
|
13434
13224
|
RepresentationType: w.mkType(web_ifc.IFCLABEL, "SweptSolid"),
|
|
13435
13225
|
Items: [w.ref(extrusionId)]
|
|
13436
13226
|
});
|
|
@@ -15187,18 +14977,22 @@ function checkGeoreferencing(reader) {
|
|
|
15187
14977
|
return [issue("warning", "NO_GEOREFERENCING", "Model contains a facility but no IfcProjectedCRS/IfcGeographicCRS (GRF003) — set ProjectSpec.crs")];
|
|
15188
14978
|
}
|
|
15189
14979
|
//#endregion
|
|
15190
|
-
//#region src/serialize/
|
|
14980
|
+
//#region src/serialize/productBodyPreflight.ts
|
|
15191
14981
|
var testItemPreparer = null;
|
|
15192
|
-
/** Prepares every
|
|
15193
|
-
function
|
|
14982
|
+
/** Prepares every retained Body item without writing IFC lines. Source solids remain borrowed. */
|
|
14983
|
+
function preflightProductBody(input) {
|
|
15194
14984
|
const prepareItem = input.prepareItem ?? testItemPreparer ?? prepareTessellation;
|
|
14985
|
+
const failed = (itemIndex, reason, cause) => (0, brepjs.err)(ifcError("BODY_TESSELLATION_FAILED", `Product Body item ${itemIndex} for ${input.localId} could not be tessellated: ${reason}`, cause, {
|
|
14986
|
+
localId: input.localId,
|
|
14987
|
+
itemIndex
|
|
14988
|
+
}));
|
|
15195
14989
|
const prepareAt = (solid, itemIndex) => {
|
|
15196
|
-
|
|
15197
|
-
|
|
15198
|
-
|
|
15199
|
-
|
|
15200
|
-
|
|
15201
|
-
|
|
14990
|
+
try {
|
|
14991
|
+
const item = prepareItem(solid);
|
|
14992
|
+
return item.ok ? (0, brepjs.ok)(item.value) : failed(itemIndex, item.reason, item.cause);
|
|
14993
|
+
} catch (cause) {
|
|
14994
|
+
return failed(itemIndex, cause instanceof Error ? cause.message : String(cause), cause);
|
|
14995
|
+
}
|
|
15202
14996
|
};
|
|
15203
14997
|
const [firstSolid, ...remainingSolids] = input.solids;
|
|
15204
14998
|
const first = prepareAt(firstSolid, 0);
|
|
@@ -15266,24 +15060,13 @@ function wallVolumeError(cause) {
|
|
|
15266
15060
|
function preflightProductBodies(elements) {
|
|
15267
15061
|
const preflighted = [];
|
|
15268
15062
|
for (const element of elements) {
|
|
15269
|
-
const
|
|
15270
|
-
if (body.kind === "PARAMETRIC") {
|
|
15271
|
-
preflighted.push({
|
|
15272
|
-
kind: "PARAMETRIC",
|
|
15273
|
-
element,
|
|
15274
|
-
solid: body.solid
|
|
15275
|
-
});
|
|
15276
|
-
continue;
|
|
15277
|
-
}
|
|
15278
|
-
const prepared = preflightExactBody({
|
|
15063
|
+
const prepared = preflightProductBody({
|
|
15279
15064
|
localId: element.localId,
|
|
15280
|
-
solids:
|
|
15065
|
+
solids: bodySolids(element.geometry)
|
|
15281
15066
|
});
|
|
15282
15067
|
if (!prepared.ok) return (0, brepjs.err)(prepared.error);
|
|
15283
15068
|
preflighted.push({
|
|
15284
|
-
kind: "EXACT",
|
|
15285
15069
|
element,
|
|
15286
|
-
solids: body.solids,
|
|
15287
15070
|
items: prepared.value
|
|
15288
15071
|
});
|
|
15289
15072
|
}
|
|
@@ -15418,13 +15201,14 @@ async function serializeIfc(model, meta) {
|
|
|
15418
15201
|
spec: wall.spec,
|
|
15419
15202
|
solids: bodySolids(wall.geometry)
|
|
15420
15203
|
});
|
|
15421
|
-
const { localPlacementId, productDefinitionShapeId } =
|
|
15204
|
+
const { localPlacementId, productDefinitionShapeId, bodyItemIds } = writeProductBodyGeometry(w, wall.spec, preflighted.items, geomSubContextId, storeyPlacementId);
|
|
15422
15205
|
const wallExpressId = writeWallEntity(w, wall.guid, `Wall ${i + 1}`, ownerHistoryId, localPlacementId, productDefinitionShapeId);
|
|
15423
15206
|
idMap.set(wall.localId, wallExpressId);
|
|
15424
15207
|
placementMap.set(wall.localId, localPlacementId);
|
|
15425
15208
|
writeWallCommonPset(w, ownerHistoryId, wallExpressId, wall.spec);
|
|
15426
15209
|
writeManufacturerPset(w, ownerHistoryId, wallExpressId, wall.spec);
|
|
15427
15210
|
if (wall.spec.customProperties !== void 0) writeCustomPsets(w, ownerHistoryId, wallExpressId, wall.spec.customProperties);
|
|
15211
|
+
for (const itemId of bodyItemIds) applySurfaceStyle(w, model, wall.localId, itemId);
|
|
15428
15212
|
if (!bodyQuantities.ok) issues.push(issue("warning", "WALL_QUANTITY_OMITTED", "Wall quantities omitted because occupied material volume could not be measured", wall.localId, {
|
|
15429
15213
|
guid: wall.guid,
|
|
15430
15214
|
cause: bodyQuantities.error
|
|
@@ -15574,27 +15358,14 @@ async function serializeIfc(model, meta) {
|
|
|
15574
15358
|
const railing = preflighted.element;
|
|
15575
15359
|
const containingId = findContainerOf(railing.localId, relationships);
|
|
15576
15360
|
const storeyPlacementId = containingId !== null ? placementMap.get(containingId) ?? null : null;
|
|
15577
|
-
|
|
15578
|
-
let exactBodyItemIds = [];
|
|
15579
|
-
if (preflighted.kind === "EXACT") {
|
|
15580
|
-
const exact = writeExactBodyGeometry(w, railing.spec, preflighted.items, geomSubContextId, storeyPlacementId);
|
|
15581
|
-
exactBodyItemIds = exact.bodyItemIds;
|
|
15582
|
-
representation = {
|
|
15583
|
-
...exact,
|
|
15584
|
-
bodyItemId: null,
|
|
15585
|
-
usedFallback: false
|
|
15586
|
-
};
|
|
15587
|
-
} else representation = writeRailingGeometry(w, railing.spec, preflighted.solid, geomSubContextId, storeyPlacementId);
|
|
15588
|
-
const { localPlacementId, productDefinitionShapeId, bodyItemId, usedFallback } = representation;
|
|
15589
|
-
if (usedFallback) console.warn(`Railing ${i + 1} tessellation failed; IFC body is a degenerate fallback.`);
|
|
15361
|
+
const { localPlacementId, productDefinitionShapeId, bodyItemIds } = writeProductBodyGeometry(w, railing.spec, preflighted.items, geomSubContextId, storeyPlacementId);
|
|
15590
15362
|
const railingExpressId = writeRailingEntity(w, railing.guid, `Railing ${i + 1}`, railing.spec.predefinedType ?? "NOTDEFINED", ownerHistoryId, localPlacementId, productDefinitionShapeId);
|
|
15591
15363
|
idMap.set(railing.localId, railingExpressId);
|
|
15592
15364
|
placementMap.set(railing.localId, localPlacementId);
|
|
15593
15365
|
writeRailingCommonPset(w, ownerHistoryId, railingExpressId, railing.spec);
|
|
15594
15366
|
writeManufacturerPset(w, ownerHistoryId, railingExpressId, railing.spec);
|
|
15595
15367
|
if (railing.spec.customProperties !== void 0) writeCustomPsets(w, ownerHistoryId, railingExpressId, railing.spec.customProperties);
|
|
15596
|
-
|
|
15597
|
-
else if (bodyItemId !== null) applySurfaceStyle(w, model, railing.localId, bodyItemId);
|
|
15368
|
+
for (const itemId of bodyItemIds) applySurfaceStyle(w, model, railing.localId, itemId);
|
|
15598
15369
|
}
|
|
15599
15370
|
for (const [i, covering] of coverings.entries()) {
|
|
15600
15371
|
const containingId = findContainerOf(covering.localId, relationships);
|
|
@@ -15610,6 +15381,7 @@ async function serializeIfc(model, meta) {
|
|
|
15610
15381
|
}
|
|
15611
15382
|
const openingPlacementMap = /* @__PURE__ */ new Map();
|
|
15612
15383
|
const openingEntityMap = /* @__PURE__ */ new Map();
|
|
15384
|
+
let referenceContextId;
|
|
15613
15385
|
for (const rel of relationships) {
|
|
15614
15386
|
if (rel.kind !== "VOIDS_WALL") continue;
|
|
15615
15387
|
const wallElement = elements.find((el) => el.category === "WALL" && el.localId === rel.wallLocalId);
|
|
@@ -15620,7 +15392,8 @@ async function serializeIfc(model, meta) {
|
|
|
15620
15392
|
const openingElement = elements.find((el) => el.localId === rel.openingLocalId);
|
|
15621
15393
|
if (openingElement === void 0 || openingElement.category !== "OPENING") continue;
|
|
15622
15394
|
if (!isWallOpening(openingElement.spec)) continue;
|
|
15623
|
-
|
|
15395
|
+
referenceContextId ??= writeGeometricSubContext(w, geomContextId, "Reference");
|
|
15396
|
+
const { openingEntityId, openingPlacementId } = writeOpeningGeometry(w, openingElement.guid, openingElement.spec, wallElement.spec, wallPlacementId, referenceContextId, ownerHistoryId, "Reference");
|
|
15624
15397
|
idMap.set(rel.openingLocalId, openingEntityId);
|
|
15625
15398
|
openingPlacementMap.set(rel.openingLocalId, openingPlacementId);
|
|
15626
15399
|
openingEntityMap.set(rel.openingLocalId, openingEntityId);
|
package/dist/brepjs-bim.js
CHANGED
|
@@ -1696,7 +1696,7 @@ var Doc = class {
|
|
|
1696
1696
|
var version = {
|
|
1697
1697
|
major: 4,
|
|
1698
1698
|
minor: 6,
|
|
1699
|
-
patch:
|
|
1699
|
+
patch: 5
|
|
1700
1700
|
};
|
|
1701
1701
|
//#endregion
|
|
1702
1702
|
//#region ../../node_modules/zod/v4/core/schemas.js
|
|
@@ -9742,11 +9742,21 @@ function writeHeader(w, meta) {
|
|
|
9742
9742
|
WorldCoordinateSystem: w.ref(writeAxis2Placement3D(w)),
|
|
9743
9743
|
TrueNorth: null
|
|
9744
9744
|
});
|
|
9745
|
+
return {
|
|
9746
|
+
ownerHistoryId,
|
|
9747
|
+
geomContextId,
|
|
9748
|
+
geomSubContextId: writeGeometricSubContext(w, geomContextId, "Body"),
|
|
9749
|
+
unitAssignmentId,
|
|
9750
|
+
lengthUnitId
|
|
9751
|
+
};
|
|
9752
|
+
}
|
|
9753
|
+
/** Separates display Body geometry from non-subtractive Reference geometry. */
|
|
9754
|
+
function writeGeometricSubContext(w, geomContextId, identifier) {
|
|
9745
9755
|
const geomSubContextId = w.nextId();
|
|
9746
9756
|
w.writeLine({
|
|
9747
9757
|
expressID: geomSubContextId,
|
|
9748
9758
|
type: WebIFC.IFCGEOMETRICREPRESENTATIONSUBCONTEXT,
|
|
9749
|
-
ContextIdentifier: w.mkType(WebIFC.IFCLABEL,
|
|
9759
|
+
ContextIdentifier: w.mkType(WebIFC.IFCLABEL, identifier),
|
|
9750
9760
|
ContextType: w.mkType(WebIFC.IFCLABEL, "Model"),
|
|
9751
9761
|
CoordinateSpaceDimension: null,
|
|
9752
9762
|
Precision: null,
|
|
@@ -9760,13 +9770,7 @@ function writeHeader(w, meta) {
|
|
|
9760
9770
|
},
|
|
9761
9771
|
UserDefinedTargetView: null
|
|
9762
9772
|
});
|
|
9763
|
-
return
|
|
9764
|
-
ownerHistoryId,
|
|
9765
|
-
geomContextId,
|
|
9766
|
-
geomSubContextId,
|
|
9767
|
-
unitAssignmentId,
|
|
9768
|
-
lengthUnitId
|
|
9769
|
-
};
|
|
9773
|
+
return geomSubContextId;
|
|
9770
9774
|
}
|
|
9771
9775
|
/**
|
|
9772
9776
|
* Writes IfcProjectedCRS + IfcMapConversion georeferencing the model context
|
|
@@ -10174,7 +10178,7 @@ function writePreparedTessellationBody(w, items, geomSubContextId) {
|
|
|
10174
10178
|
bodyItemIds
|
|
10175
10179
|
};
|
|
10176
10180
|
}
|
|
10177
|
-
function
|
|
10181
|
+
function writeProductBodyGeometry(w, placement, items, geomSubContextId, parentPlacementId) {
|
|
10178
10182
|
const placement3DId = writeAxis2Placement3D(w, [
|
|
10179
10183
|
toIfcLengthM(placement.origin[0]),
|
|
10180
10184
|
toIfcLengthM(placement.origin[1]),
|
|
@@ -10220,47 +10224,6 @@ function writePreparedTessellationItem(w, prepared) {
|
|
|
10220
10224
|
});
|
|
10221
10225
|
return faceSetId;
|
|
10222
10226
|
}
|
|
10223
|
-
/**
|
|
10224
|
-
* Builds a wall 'Axis' IfcShapeRepresentation: an IfcPolyline from (0,0) to
|
|
10225
|
-
* (lengthM, 0) in the wall's local XY plane, wrapped in an IfcShapeRepresentation
|
|
10226
|
-
* with RepresentationIdentifier='Axis', RepresentationType='Curve2D'. Returns
|
|
10227
|
-
* the IfcShapeRepresentation express ID so callers can add it alongside the Body
|
|
10228
|
-
* representation in an IfcProductDefinitionShape.
|
|
10229
|
-
*
|
|
10230
|
-
* wallLengthMm is the wall length in mm (brepjs native units); it is converted
|
|
10231
|
-
* to metres for IFC.
|
|
10232
|
-
*/
|
|
10233
|
-
function writeWallAxisRepresentation(w, wallLengthMm, geomSubContextId) {
|
|
10234
|
-
const lengthM = toIfcLengthM(wallLengthMm);
|
|
10235
|
-
const startId = w.nextId();
|
|
10236
|
-
w.writeLine({
|
|
10237
|
-
expressID: startId,
|
|
10238
|
-
type: WebIFC.IFCCARTESIANPOINT,
|
|
10239
|
-
Coordinates: [w.mkType(WebIFC.IFCLENGTHMEASURE, 0), w.mkType(WebIFC.IFCLENGTHMEASURE, 0)]
|
|
10240
|
-
});
|
|
10241
|
-
const endId = w.nextId();
|
|
10242
|
-
w.writeLine({
|
|
10243
|
-
expressID: endId,
|
|
10244
|
-
type: WebIFC.IFCCARTESIANPOINT,
|
|
10245
|
-
Coordinates: [w.mkType(WebIFC.IFCLENGTHMEASURE, lengthM), w.mkType(WebIFC.IFCLENGTHMEASURE, 0)]
|
|
10246
|
-
});
|
|
10247
|
-
const polylineId = w.nextId();
|
|
10248
|
-
w.writeLine({
|
|
10249
|
-
expressID: polylineId,
|
|
10250
|
-
type: WebIFC.IFCPOLYLINE,
|
|
10251
|
-
Points: [w.ref(startId), w.ref(endId)]
|
|
10252
|
-
});
|
|
10253
|
-
const shapeRepId = w.nextId();
|
|
10254
|
-
w.writeLine({
|
|
10255
|
-
expressID: shapeRepId,
|
|
10256
|
-
type: WebIFC.IFCSHAPEREPRESENTATION,
|
|
10257
|
-
ContextOfItems: w.ref(geomSubContextId),
|
|
10258
|
-
RepresentationIdentifier: w.mkType(WebIFC.IFCLABEL, "Axis"),
|
|
10259
|
-
RepresentationType: w.mkType(WebIFC.IFCLABEL, "Curve2D"),
|
|
10260
|
-
Items: [w.ref(polylineId)]
|
|
10261
|
-
});
|
|
10262
|
-
return shapeRepId;
|
|
10263
|
-
}
|
|
10264
10227
|
function writeFacetedBrepFallback(w, geomSubContextId, reason) {
|
|
10265
10228
|
const originId = w.nextId();
|
|
10266
10229
|
w.writeLine({
|
|
@@ -10591,81 +10554,6 @@ function writePolyline(w, points) {
|
|
|
10591
10554
|
}
|
|
10592
10555
|
//#endregion
|
|
10593
10556
|
//#region src/ifc-writer/geometryWriter.ts
|
|
10594
|
-
function writeWallGeometry(w, spec, geomSubContextId, parentPlacementId) {
|
|
10595
|
-
const placement3DId = writeAxis2Placement3D(w, spec.origin.map(toIfcLengthM), spec.axisZ, spec.axisX);
|
|
10596
|
-
const localPlacementId = w.nextId();
|
|
10597
|
-
w.writeLine({
|
|
10598
|
-
expressID: localPlacementId,
|
|
10599
|
-
type: WebIFC.IFCLOCALPLACEMENT,
|
|
10600
|
-
PlacementRelTo: parentPlacementId !== null ? w.ref(parentPlacementId) : null,
|
|
10601
|
-
RelativePlacement: w.ref(placement3DId)
|
|
10602
|
-
});
|
|
10603
|
-
const thicknessM = toIfcLengthM(spec.thickness);
|
|
10604
|
-
const heightM = toIfcLengthM(spec.height);
|
|
10605
|
-
const lengthM = toIfcLengthM(spec.length);
|
|
10606
|
-
const profileId = w.nextId();
|
|
10607
|
-
w.writeLine({
|
|
10608
|
-
expressID: profileId,
|
|
10609
|
-
type: WebIFC.IFCRECTANGLEPROFILEDEF,
|
|
10610
|
-
ProfileType: {
|
|
10611
|
-
type: 3,
|
|
10612
|
-
value: "AREA"
|
|
10613
|
-
},
|
|
10614
|
-
ProfileName: null,
|
|
10615
|
-
Position: w.ref(writeAxis2Placement2D$1(w, [thicknessM / 2, heightM / 2])),
|
|
10616
|
-
XDim: w.mkType(WebIFC.IFCPOSITIVELENGTHMEASURE, thicknessM),
|
|
10617
|
-
YDim: w.mkType(WebIFC.IFCPOSITIVELENGTHMEASURE, heightM)
|
|
10618
|
-
});
|
|
10619
|
-
const extrusionPosId = writeAxis2Placement3D(w, [
|
|
10620
|
-
0,
|
|
10621
|
-
0,
|
|
10622
|
-
0
|
|
10623
|
-
], [
|
|
10624
|
-
1,
|
|
10625
|
-
0,
|
|
10626
|
-
0
|
|
10627
|
-
], [
|
|
10628
|
-
0,
|
|
10629
|
-
1,
|
|
10630
|
-
0
|
|
10631
|
-
]);
|
|
10632
|
-
const extrusionDirId = writeDirection(w, [
|
|
10633
|
-
0,
|
|
10634
|
-
0,
|
|
10635
|
-
1
|
|
10636
|
-
]);
|
|
10637
|
-
const extrusionId = w.nextId();
|
|
10638
|
-
w.writeLine({
|
|
10639
|
-
expressID: extrusionId,
|
|
10640
|
-
type: WebIFC.IFCEXTRUDEDAREASOLID,
|
|
10641
|
-
SweptArea: w.ref(profileId),
|
|
10642
|
-
Position: w.ref(extrusionPosId),
|
|
10643
|
-
ExtrudedDirection: w.ref(extrusionDirId),
|
|
10644
|
-
Depth: w.mkType(WebIFC.IFCPOSITIVELENGTHMEASURE, lengthM)
|
|
10645
|
-
});
|
|
10646
|
-
const shapeRepId = w.nextId();
|
|
10647
|
-
w.writeLine({
|
|
10648
|
-
expressID: shapeRepId,
|
|
10649
|
-
type: WebIFC.IFCSHAPEREPRESENTATION,
|
|
10650
|
-
ContextOfItems: w.ref(geomSubContextId),
|
|
10651
|
-
RepresentationIdentifier: w.mkType(WebIFC.IFCLABEL, "Body"),
|
|
10652
|
-
RepresentationType: w.mkType(WebIFC.IFCLABEL, "SweptSolid"),
|
|
10653
|
-
Items: [w.ref(extrusionId)]
|
|
10654
|
-
});
|
|
10655
|
-
const axisRepId = writeWallAxisRepresentation(w, spec.length, geomSubContextId);
|
|
10656
|
-
const productDefinitionShapeId = w.nextId();
|
|
10657
|
-
w.writeLine({
|
|
10658
|
-
expressID: productDefinitionShapeId,
|
|
10659
|
-
type: WebIFC.IFCPRODUCTDEFINITIONSHAPE,
|
|
10660
|
-
Name: null,
|
|
10661
|
-
Description: null,
|
|
10662
|
-
Representations: [w.ref(axisRepId), w.ref(shapeRepId)]
|
|
10663
|
-
});
|
|
10664
|
-
return {
|
|
10665
|
-
localPlacementId,
|
|
10666
|
-
productDefinitionShapeId
|
|
10667
|
-
};
|
|
10668
|
-
}
|
|
10669
10557
|
function writeSlabGeometry(w, spec, geomSubContextId, parentPlacementId) {
|
|
10670
10558
|
const placement3DId = writeAxis2Placement3D(w, spec.origin.map(toIfcLengthM), spec.axisZ, spec.axisX);
|
|
10671
10559
|
const localPlacementId = w.nextId();
|
|
@@ -11808,104 +11696,6 @@ function writeRampAssembly(w, spec, rampKey, assemblyGuid, ownerHistoryId, geomS
|
|
|
11808
11696
|
}
|
|
11809
11697
|
//#endregion
|
|
11810
11698
|
//#region src/ifc-writer/railingWriter.ts
|
|
11811
|
-
function writeRailingGeometry(w, spec, solid, geomSubContextId, parentPlacementId) {
|
|
11812
|
-
const placement3DId = writeAxis2Placement3D(w, spec.origin.map(toIfcLengthM), spec.axisZ, spec.axisX);
|
|
11813
|
-
const localPlacementId = w.nextId();
|
|
11814
|
-
w.writeLine({
|
|
11815
|
-
expressID: localPlacementId,
|
|
11816
|
-
type: WebIFC.IFCLOCALPLACEMENT,
|
|
11817
|
-
PlacementRelTo: parentPlacementId !== null ? w.ref(parentPlacementId) : null,
|
|
11818
|
-
RelativePlacement: w.ref(placement3DId)
|
|
11819
|
-
});
|
|
11820
|
-
if (spec.infill === "POSTED") {
|
|
11821
|
-
const tess = writeTessellation(w, solid, geomSubContextId, localPlacementId);
|
|
11822
|
-
return {
|
|
11823
|
-
localPlacementId,
|
|
11824
|
-
productDefinitionShapeId: tess.productDefinitionShapeId,
|
|
11825
|
-
bodyItemId: null,
|
|
11826
|
-
usedFallback: tess.usedFallback
|
|
11827
|
-
};
|
|
11828
|
-
}
|
|
11829
|
-
const thicknessM = toIfcLengthM(spec.thickness);
|
|
11830
|
-
const heightM = toIfcLengthM(spec.height);
|
|
11831
|
-
const lengthM = toIfcLengthM(spec.length);
|
|
11832
|
-
const profileOriginId = w.nextId();
|
|
11833
|
-
w.writeLine({
|
|
11834
|
-
expressID: profileOriginId,
|
|
11835
|
-
type: WebIFC.IFCCARTESIANPOINT,
|
|
11836
|
-
Coordinates: [w.mkType(WebIFC.IFCLENGTHMEASURE, thicknessM / 2), w.mkType(WebIFC.IFCLENGTHMEASURE, heightM / 2)]
|
|
11837
|
-
});
|
|
11838
|
-
const profilePosId = w.nextId();
|
|
11839
|
-
w.writeLine({
|
|
11840
|
-
expressID: profilePosId,
|
|
11841
|
-
type: WebIFC.IFCAXIS2PLACEMENT2D,
|
|
11842
|
-
Location: w.ref(profileOriginId),
|
|
11843
|
-
RefDirection: null
|
|
11844
|
-
});
|
|
11845
|
-
const profileId = w.nextId();
|
|
11846
|
-
w.writeLine({
|
|
11847
|
-
expressID: profileId,
|
|
11848
|
-
type: WebIFC.IFCRECTANGLEPROFILEDEF,
|
|
11849
|
-
ProfileType: {
|
|
11850
|
-
type: 3,
|
|
11851
|
-
value: "AREA"
|
|
11852
|
-
},
|
|
11853
|
-
ProfileName: null,
|
|
11854
|
-
Position: w.ref(profilePosId),
|
|
11855
|
-
XDim: w.mkType(WebIFC.IFCPOSITIVELENGTHMEASURE, thicknessM),
|
|
11856
|
-
YDim: w.mkType(WebIFC.IFCPOSITIVELENGTHMEASURE, heightM)
|
|
11857
|
-
});
|
|
11858
|
-
const extrusionPosId = writeAxis2Placement3D(w, [
|
|
11859
|
-
0,
|
|
11860
|
-
0,
|
|
11861
|
-
0
|
|
11862
|
-
], [
|
|
11863
|
-
1,
|
|
11864
|
-
0,
|
|
11865
|
-
0
|
|
11866
|
-
], [
|
|
11867
|
-
0,
|
|
11868
|
-
1,
|
|
11869
|
-
0
|
|
11870
|
-
]);
|
|
11871
|
-
const extrusionDirId = writeDirection(w, [
|
|
11872
|
-
0,
|
|
11873
|
-
0,
|
|
11874
|
-
1
|
|
11875
|
-
]);
|
|
11876
|
-
const extrusionId = w.nextId();
|
|
11877
|
-
w.writeLine({
|
|
11878
|
-
expressID: extrusionId,
|
|
11879
|
-
type: WebIFC.IFCEXTRUDEDAREASOLID,
|
|
11880
|
-
SweptArea: w.ref(profileId),
|
|
11881
|
-
Position: w.ref(extrusionPosId),
|
|
11882
|
-
ExtrudedDirection: w.ref(extrusionDirId),
|
|
11883
|
-
Depth: w.mkType(WebIFC.IFCPOSITIVELENGTHMEASURE, lengthM)
|
|
11884
|
-
});
|
|
11885
|
-
const shapeRepId = w.nextId();
|
|
11886
|
-
w.writeLine({
|
|
11887
|
-
expressID: shapeRepId,
|
|
11888
|
-
type: WebIFC.IFCSHAPEREPRESENTATION,
|
|
11889
|
-
ContextOfItems: w.ref(geomSubContextId),
|
|
11890
|
-
RepresentationIdentifier: w.mkType(WebIFC.IFCLABEL, "Body"),
|
|
11891
|
-
RepresentationType: w.mkType(WebIFC.IFCLABEL, "SweptSolid"),
|
|
11892
|
-
Items: [w.ref(extrusionId)]
|
|
11893
|
-
});
|
|
11894
|
-
const productDefinitionShapeId = w.nextId();
|
|
11895
|
-
w.writeLine({
|
|
11896
|
-
expressID: productDefinitionShapeId,
|
|
11897
|
-
type: WebIFC.IFCPRODUCTDEFINITIONSHAPE,
|
|
11898
|
-
Name: null,
|
|
11899
|
-
Description: null,
|
|
11900
|
-
Representations: [w.ref(shapeRepId)]
|
|
11901
|
-
});
|
|
11902
|
-
return {
|
|
11903
|
-
localPlacementId,
|
|
11904
|
-
productDefinitionShapeId,
|
|
11905
|
-
bodyItemId: extrusionId,
|
|
11906
|
-
usedFallback: false
|
|
11907
|
-
};
|
|
11908
|
-
}
|
|
11909
11699
|
function writeRailingEntity(w, guid, name, _predefinedType, ownerHistoryId, localPlacementId, productDefinitionShapeId) {
|
|
11910
11700
|
const id = w.nextId();
|
|
11911
11701
|
w.writeLine({
|
|
@@ -13344,7 +13134,7 @@ function writeAxis2Placement2D(w) {
|
|
|
13344
13134
|
});
|
|
13345
13135
|
return id;
|
|
13346
13136
|
}
|
|
13347
|
-
function writeOpeningGeometry(w, guid, openingSpec, wallSpec, wallPlacementId, geomSubContextId, ownerHistoryId) {
|
|
13137
|
+
function writeOpeningGeometry(w, guid, openingSpec, wallSpec, wallPlacementId, geomSubContextId, ownerHistoryId, representationIdentifier) {
|
|
13348
13138
|
const widthM = toIfcLengthM(openingSpec.width);
|
|
13349
13139
|
const heightM = toIfcLengthM(openingSpec.height);
|
|
13350
13140
|
const offsetAlongWallM = toIfcLengthM(openingSpec.offsetAlongWall);
|
|
@@ -13407,7 +13197,7 @@ function writeOpeningGeometry(w, guid, openingSpec, wallSpec, wallPlacementId, g
|
|
|
13407
13197
|
expressID: shapeRepId,
|
|
13408
13198
|
type: WebIFC.IFCSHAPEREPRESENTATION,
|
|
13409
13199
|
ContextOfItems: w.ref(geomSubContextId),
|
|
13410
|
-
RepresentationIdentifier: w.mkType(WebIFC.IFCLABEL,
|
|
13200
|
+
RepresentationIdentifier: w.mkType(WebIFC.IFCLABEL, representationIdentifier),
|
|
13411
13201
|
RepresentationType: w.mkType(WebIFC.IFCLABEL, "SweptSolid"),
|
|
13412
13202
|
Items: [w.ref(extrusionId)]
|
|
13413
13203
|
});
|
|
@@ -15164,18 +14954,22 @@ function checkGeoreferencing(reader) {
|
|
|
15164
14954
|
return [issue("warning", "NO_GEOREFERENCING", "Model contains a facility but no IfcProjectedCRS/IfcGeographicCRS (GRF003) — set ProjectSpec.crs")];
|
|
15165
14955
|
}
|
|
15166
14956
|
//#endregion
|
|
15167
|
-
//#region src/serialize/
|
|
14957
|
+
//#region src/serialize/productBodyPreflight.ts
|
|
15168
14958
|
var testItemPreparer = null;
|
|
15169
|
-
/** Prepares every
|
|
15170
|
-
function
|
|
14959
|
+
/** Prepares every retained Body item without writing IFC lines. Source solids remain borrowed. */
|
|
14960
|
+
function preflightProductBody(input) {
|
|
15171
14961
|
const prepareItem = input.prepareItem ?? testItemPreparer ?? prepareTessellation;
|
|
14962
|
+
const failed = (itemIndex, reason, cause) => err(ifcError("BODY_TESSELLATION_FAILED", `Product Body item ${itemIndex} for ${input.localId} could not be tessellated: ${reason}`, cause, {
|
|
14963
|
+
localId: input.localId,
|
|
14964
|
+
itemIndex
|
|
14965
|
+
}));
|
|
15172
14966
|
const prepareAt = (solid, itemIndex) => {
|
|
15173
|
-
|
|
15174
|
-
|
|
15175
|
-
|
|
15176
|
-
|
|
15177
|
-
|
|
15178
|
-
|
|
14967
|
+
try {
|
|
14968
|
+
const item = prepareItem(solid);
|
|
14969
|
+
return item.ok ? ok(item.value) : failed(itemIndex, item.reason, item.cause);
|
|
14970
|
+
} catch (cause) {
|
|
14971
|
+
return failed(itemIndex, cause instanceof Error ? cause.message : String(cause), cause);
|
|
14972
|
+
}
|
|
15179
14973
|
};
|
|
15180
14974
|
const [firstSolid, ...remainingSolids] = input.solids;
|
|
15181
14975
|
const first = prepareAt(firstSolid, 0);
|
|
@@ -15243,24 +15037,13 @@ function wallVolumeError(cause) {
|
|
|
15243
15037
|
function preflightProductBodies(elements) {
|
|
15244
15038
|
const preflighted = [];
|
|
15245
15039
|
for (const element of elements) {
|
|
15246
|
-
const
|
|
15247
|
-
if (body.kind === "PARAMETRIC") {
|
|
15248
|
-
preflighted.push({
|
|
15249
|
-
kind: "PARAMETRIC",
|
|
15250
|
-
element,
|
|
15251
|
-
solid: body.solid
|
|
15252
|
-
});
|
|
15253
|
-
continue;
|
|
15254
|
-
}
|
|
15255
|
-
const prepared = preflightExactBody({
|
|
15040
|
+
const prepared = preflightProductBody({
|
|
15256
15041
|
localId: element.localId,
|
|
15257
|
-
solids:
|
|
15042
|
+
solids: bodySolids(element.geometry)
|
|
15258
15043
|
});
|
|
15259
15044
|
if (!prepared.ok) return err(prepared.error);
|
|
15260
15045
|
preflighted.push({
|
|
15261
|
-
kind: "EXACT",
|
|
15262
15046
|
element,
|
|
15263
|
-
solids: body.solids,
|
|
15264
15047
|
items: prepared.value
|
|
15265
15048
|
});
|
|
15266
15049
|
}
|
|
@@ -15395,13 +15178,14 @@ async function serializeIfc(model, meta) {
|
|
|
15395
15178
|
spec: wall.spec,
|
|
15396
15179
|
solids: bodySolids(wall.geometry)
|
|
15397
15180
|
});
|
|
15398
|
-
const { localPlacementId, productDefinitionShapeId } =
|
|
15181
|
+
const { localPlacementId, productDefinitionShapeId, bodyItemIds } = writeProductBodyGeometry(w, wall.spec, preflighted.items, geomSubContextId, storeyPlacementId);
|
|
15399
15182
|
const wallExpressId = writeWallEntity(w, wall.guid, `Wall ${i + 1}`, ownerHistoryId, localPlacementId, productDefinitionShapeId);
|
|
15400
15183
|
idMap.set(wall.localId, wallExpressId);
|
|
15401
15184
|
placementMap.set(wall.localId, localPlacementId);
|
|
15402
15185
|
writeWallCommonPset(w, ownerHistoryId, wallExpressId, wall.spec);
|
|
15403
15186
|
writeManufacturerPset(w, ownerHistoryId, wallExpressId, wall.spec);
|
|
15404
15187
|
if (wall.spec.customProperties !== void 0) writeCustomPsets(w, ownerHistoryId, wallExpressId, wall.spec.customProperties);
|
|
15188
|
+
for (const itemId of bodyItemIds) applySurfaceStyle(w, model, wall.localId, itemId);
|
|
15405
15189
|
if (!bodyQuantities.ok) issues.push(issue("warning", "WALL_QUANTITY_OMITTED", "Wall quantities omitted because occupied material volume could not be measured", wall.localId, {
|
|
15406
15190
|
guid: wall.guid,
|
|
15407
15191
|
cause: bodyQuantities.error
|
|
@@ -15551,27 +15335,14 @@ async function serializeIfc(model, meta) {
|
|
|
15551
15335
|
const railing = preflighted.element;
|
|
15552
15336
|
const containingId = findContainerOf(railing.localId, relationships);
|
|
15553
15337
|
const storeyPlacementId = containingId !== null ? placementMap.get(containingId) ?? null : null;
|
|
15554
|
-
|
|
15555
|
-
let exactBodyItemIds = [];
|
|
15556
|
-
if (preflighted.kind === "EXACT") {
|
|
15557
|
-
const exact = writeExactBodyGeometry(w, railing.spec, preflighted.items, geomSubContextId, storeyPlacementId);
|
|
15558
|
-
exactBodyItemIds = exact.bodyItemIds;
|
|
15559
|
-
representation = {
|
|
15560
|
-
...exact,
|
|
15561
|
-
bodyItemId: null,
|
|
15562
|
-
usedFallback: false
|
|
15563
|
-
};
|
|
15564
|
-
} else representation = writeRailingGeometry(w, railing.spec, preflighted.solid, geomSubContextId, storeyPlacementId);
|
|
15565
|
-
const { localPlacementId, productDefinitionShapeId, bodyItemId, usedFallback } = representation;
|
|
15566
|
-
if (usedFallback) console.warn(`Railing ${i + 1} tessellation failed; IFC body is a degenerate fallback.`);
|
|
15338
|
+
const { localPlacementId, productDefinitionShapeId, bodyItemIds } = writeProductBodyGeometry(w, railing.spec, preflighted.items, geomSubContextId, storeyPlacementId);
|
|
15567
15339
|
const railingExpressId = writeRailingEntity(w, railing.guid, `Railing ${i + 1}`, railing.spec.predefinedType ?? "NOTDEFINED", ownerHistoryId, localPlacementId, productDefinitionShapeId);
|
|
15568
15340
|
idMap.set(railing.localId, railingExpressId);
|
|
15569
15341
|
placementMap.set(railing.localId, localPlacementId);
|
|
15570
15342
|
writeRailingCommonPset(w, ownerHistoryId, railingExpressId, railing.spec);
|
|
15571
15343
|
writeManufacturerPset(w, ownerHistoryId, railingExpressId, railing.spec);
|
|
15572
15344
|
if (railing.spec.customProperties !== void 0) writeCustomPsets(w, ownerHistoryId, railingExpressId, railing.spec.customProperties);
|
|
15573
|
-
|
|
15574
|
-
else if (bodyItemId !== null) applySurfaceStyle(w, model, railing.localId, bodyItemId);
|
|
15345
|
+
for (const itemId of bodyItemIds) applySurfaceStyle(w, model, railing.localId, itemId);
|
|
15575
15346
|
}
|
|
15576
15347
|
for (const [i, covering] of coverings.entries()) {
|
|
15577
15348
|
const containingId = findContainerOf(covering.localId, relationships);
|
|
@@ -15587,6 +15358,7 @@ async function serializeIfc(model, meta) {
|
|
|
15587
15358
|
}
|
|
15588
15359
|
const openingPlacementMap = /* @__PURE__ */ new Map();
|
|
15589
15360
|
const openingEntityMap = /* @__PURE__ */ new Map();
|
|
15361
|
+
let referenceContextId;
|
|
15590
15362
|
for (const rel of relationships) {
|
|
15591
15363
|
if (rel.kind !== "VOIDS_WALL") continue;
|
|
15592
15364
|
const wallElement = elements.find((el) => el.category === "WALL" && el.localId === rel.wallLocalId);
|
|
@@ -15597,7 +15369,8 @@ async function serializeIfc(model, meta) {
|
|
|
15597
15369
|
const openingElement = elements.find((el) => el.localId === rel.openingLocalId);
|
|
15598
15370
|
if (openingElement === void 0 || openingElement.category !== "OPENING") continue;
|
|
15599
15371
|
if (!isWallOpening(openingElement.spec)) continue;
|
|
15600
|
-
|
|
15372
|
+
referenceContextId ??= writeGeometricSubContext(w, geomContextId, "Reference");
|
|
15373
|
+
const { openingEntityId, openingPlacementId } = writeOpeningGeometry(w, openingElement.guid, openingElement.spec, wallElement.spec, wallPlacementId, referenceContextId, ownerHistoryId, "Reference");
|
|
15601
15374
|
idMap.set(rel.openingLocalId, openingEntityId);
|
|
15602
15375
|
openingPlacementMap.set(rel.openingLocalId, openingPlacementId);
|
|
15603
15376
|
openingEntityMap.set(rel.openingLocalId, openingEntityId);
|
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
import { ValidSolid } from 'brepjs';
|
|
2
2
|
import { IfcWriter } from './ifcWriter.js';
|
|
3
|
-
import { WallSpec } from '../specs/wallSpec.js';
|
|
4
3
|
import { SlabSpec } from '../specs/slabSpec.js';
|
|
5
4
|
import { BeamSpec } from '../specs/beamSpec.js';
|
|
6
5
|
import { ColumnSpec } from '../specs/columnSpec.js';
|
|
7
6
|
import { RoofSpec } from '../specs/roofSpec.js';
|
|
8
7
|
import { Profile } from '../specs/profile.js';
|
|
9
|
-
export interface WallRepresentationIds {
|
|
10
|
-
localPlacementId: number;
|
|
11
|
-
productDefinitionShapeId: number;
|
|
12
|
-
}
|
|
13
8
|
export interface SlabRepresentationIds {
|
|
14
9
|
localPlacementId: number;
|
|
15
10
|
productDefinitionShapeId: number;
|
|
@@ -18,7 +13,6 @@ export interface LinearElementRepresentationIds {
|
|
|
18
13
|
localPlacementId: number;
|
|
19
14
|
productDefinitionShapeId: number;
|
|
20
15
|
}
|
|
21
|
-
export declare function writeWallGeometry(w: IfcWriter, spec: WallSpec, geomSubContextId: number, parentPlacementId: number | null): WallRepresentationIds;
|
|
22
16
|
export declare function writeSlabGeometry(w: IfcWriter, spec: SlabSpec, geomSubContextId: number, parentPlacementId: number | null): SlabRepresentationIds;
|
|
23
17
|
export declare function writeRoofGeometry(w: IfcWriter, spec: RoofSpec, solid: ValidSolid, geomSubContextId: number, parentPlacementId: number | null): SlabRepresentationIds & {
|
|
24
18
|
usedFallback: boolean;
|
|
@@ -28,6 +28,8 @@ export interface BimModelMeta {
|
|
|
28
28
|
ifcSchema?: IfcSchema | undefined;
|
|
29
29
|
}
|
|
30
30
|
export declare function writeHeader(w: IfcWriter, meta: BimModelMeta): HeaderIds;
|
|
31
|
+
/** Separates display Body geometry from non-subtractive Reference geometry. */
|
|
32
|
+
export declare function writeGeometricSubContext(w: IfcWriter, geomContextId: number, identifier: 'Body' | 'Reference'): number;
|
|
31
33
|
/**
|
|
32
34
|
* Writes IfcProjectedCRS + IfcMapConversion georeferencing the model context
|
|
33
35
|
* (GRF003). Coordinates are metres; rotation defaults to identity.
|
|
@@ -13,7 +13,7 @@ type OpeningPsetSpec = {
|
|
|
13
13
|
readonly acousticRating?: string | undefined;
|
|
14
14
|
readonly thermalTransmittance?: number | undefined;
|
|
15
15
|
};
|
|
16
|
-
export declare function writeOpeningGeometry(w: IfcWriter, guid: IfcGuid, openingSpec: WallOpeningSpec, wallSpec: WallSpec, wallPlacementId: number, geomSubContextId: number, ownerHistoryId: number): OpeningIds;
|
|
16
|
+
export declare function writeOpeningGeometry(w: IfcWriter, guid: IfcGuid, openingSpec: WallOpeningSpec, wallSpec: WallSpec, wallPlacementId: number, geomSubContextId: number, ownerHistoryId: number, representationIdentifier: 'Body' | 'Reference'): OpeningIds;
|
|
17
17
|
export declare function writeSlabOpeningGeometry(w: IfcWriter, guid: IfcGuid, openingSpec: SlabOpeningSpec, slabSpec: SlabSpec, slabPlacementId: number, geomSubContextId: number, ownerHistoryId: number): OpeningIds;
|
|
18
18
|
export declare function writeDoorEntity(w: IfcWriter, guid: IfcGuid, name: string, ownerHistoryId: number, openingPlacementId: number, geomSubContextId: number, overallWidthM: number, overallHeightM: number, nominalDepthMm?: number): number;
|
|
19
19
|
export declare function writeWindowEntity(w: IfcWriter, guid: IfcGuid, name: string, ownerHistoryId: number, openingPlacementId: number, geomSubContextId: number, overallWidthM: number, overallHeightM: number, nominalDepthMm?: number): number;
|
|
@@ -25,7 +25,7 @@ export type TessellationPreparation = {
|
|
|
25
25
|
readonly reason: string;
|
|
26
26
|
readonly cause?: unknown;
|
|
27
27
|
};
|
|
28
|
-
export interface
|
|
28
|
+
export interface ProductBodyRepresentationIds {
|
|
29
29
|
readonly localPlacementId: number;
|
|
30
30
|
readonly productDefinitionShapeId: number;
|
|
31
31
|
readonly bodyItemIds: readonly number[];
|
|
@@ -53,7 +53,7 @@ export declare function writePreparedTessellationBody(w: IfcWriter, items: NonEm
|
|
|
53
53
|
readonly productDefinitionShapeId: number;
|
|
54
54
|
readonly bodyItemIds: readonly number[];
|
|
55
55
|
};
|
|
56
|
-
export declare function
|
|
56
|
+
export declare function writeProductBodyGeometry(w: IfcWriter, placement: FrameInput, items: NonEmpty<PreparedTessellation>, geomSubContextId: number, parentPlacementId: number | null): ProductBodyRepresentationIds;
|
|
57
57
|
export declare function writePreparedTessellationItem(w: IfcWriter, prepared: PreparedTessellation): number;
|
|
58
58
|
/**
|
|
59
59
|
* Builds a wall 'Axis' IfcShapeRepresentation: an IfcPolyline from (0,0) to
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Result, ValidSolid } from 'brepjs';
|
|
2
|
+
import { BimError } from '../errors/bimError.js';
|
|
3
|
+
import { LocalId } from '../identity/localId.js';
|
|
4
|
+
import { NonEmpty } from '../types/productBody.js';
|
|
5
|
+
import { PreparedTessellation, TessellationPreparation } from '../ifc-writer/tessellationWriter.js';
|
|
6
|
+
export type ProductBodyItemPreparer = (solid: ValidSolid) => TessellationPreparation;
|
|
7
|
+
/** Package-internal deterministic failure seam for serialization cleanup tests. */
|
|
8
|
+
export declare function setProductBodyItemPreparerForTesting(prepareItem: ProductBodyItemPreparer | null): void;
|
|
9
|
+
export interface ProductBodyPreflightInput {
|
|
10
|
+
readonly localId: LocalId;
|
|
11
|
+
readonly solids: NonEmpty<ValidSolid>;
|
|
12
|
+
readonly prepareItem?: ProductBodyItemPreparer | undefined;
|
|
13
|
+
}
|
|
14
|
+
/** Prepares every retained Body item without writing IFC lines. Source solids remain borrowed. */
|
|
15
|
+
export declare function preflightProductBody(input: ProductBodyPreflightInput): Result<NonEmpty<PreparedTessellation>, BimError>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "brepjs-bim",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.0",
|
|
4
4
|
"description": "BIM layer for brepjs — IFC4-aligned parametric building elements",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bim",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"vite-plugin-dts": "^5.0.0",
|
|
60
60
|
"vitest": "^5.0.0",
|
|
61
61
|
"web-ifc": "^0.0.77",
|
|
62
|
-
"zod": "4.6.
|
|
62
|
+
"zod": "4.6.5"
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
65
|
"brepjs-families": ">=0.1.0 <1.0.0"
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { Result, ValidSolid } from 'brepjs';
|
|
2
|
-
import { BimError } from '../errors/bimError.js';
|
|
3
|
-
import { LocalId } from '../identity/localId.js';
|
|
4
|
-
import { NonEmpty } from '../types/productBody.js';
|
|
5
|
-
import { PreparedTessellation, TessellationPreparation } from '../ifc-writer/tessellationWriter.js';
|
|
6
|
-
export type ExactBodyItemPreparer = (solid: ValidSolid) => TessellationPreparation;
|
|
7
|
-
/** Package-internal deterministic failure seam for serialization cleanup tests. */
|
|
8
|
-
export declare function setExactBodyItemPreparerForTesting(prepareItem: ExactBodyItemPreparer | null): void;
|
|
9
|
-
export interface ExactBodyPreflightInput {
|
|
10
|
-
readonly localId: LocalId;
|
|
11
|
-
readonly solids: NonEmpty<ValidSolid>;
|
|
12
|
-
readonly prepareItem?: ExactBodyItemPreparer | undefined;
|
|
13
|
-
}
|
|
14
|
-
/** Prepares every exact Body item without writing IFC lines. Source solids remain borrowed. */
|
|
15
|
-
export declare function preflightExactBody(input: ExactBodyPreflightInput): Result<NonEmpty<PreparedTessellation>, BimError>;
|