brepjs-bim 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -1
- package/dist/bcf/bcfXml.d.ts +9 -3
- package/dist/brepjs-bim.cjs +792 -309
- package/dist/brepjs-bim.js +793 -311
- package/dist/elementFns/placedGeometry.d.ts +18 -0
- package/dist/ifc-writer/geometryWriter.d.ts +4 -1
- package/dist/ifc-writer/railingWriter.d.ts +9 -3
- package/dist/import/placement.d.ts +18 -0
- package/dist/index.d.ts +1 -0
- package/dist/specs/railingSpec.d.ts +6 -0
- package/dist/specs/roofSpec.d.ts +7 -0
- package/package.json +6 -2
package/dist/brepjs-bim.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { addHoles, applyMatrix, autoHeal, castShape, cut, err, extrude, getKernel, isClosedWire, isOk, isPlanarWire, isSolid, isValid, isValidSolid, measureVolume, mesh, ok, outerWire, polygon, revolve, rotate, validSolid } from "brepjs";
|
|
1
|
+
import { addHoles, applyMatrix, autoHeal, box, castShape, convexHull, cut, err, extrude, fuse, getKernel, isClosedWire, isOk, isPlanarWire, isSolid, isValid, isValidSolid, measureVolume, mesh, ok, outerWire, polygon, revolve, rotate, validSolid } from "brepjs";
|
|
2
2
|
import * as WebIFC from "web-ifc";
|
|
3
3
|
import { Handle, IfcAPI } from "web-ifc";
|
|
4
4
|
//#region src/identity/ifcGuid.ts
|
|
@@ -242,7 +242,7 @@ function _usingCtx() {
|
|
|
242
242
|
//#region src/elementFns/wallFns.ts
|
|
243
243
|
function wallToSolid(spec) {
|
|
244
244
|
try {
|
|
245
|
-
var _usingCtx$
|
|
245
|
+
var _usingCtx$19 = _usingCtx();
|
|
246
246
|
if (spec.length <= 0) return err(specError("WALL_ZERO_LENGTH", "Wall length must be positive"));
|
|
247
247
|
if (spec.height <= 0) return err(specError("WALL_ZERO_HEIGHT", "Wall height must be positive"));
|
|
248
248
|
if (spec.thickness <= 0) return err(specError("WALL_ZERO_THICKNESS", "Wall thickness must be positive"));
|
|
@@ -270,7 +270,7 @@ function wallToSolid(spec) {
|
|
|
270
270
|
]
|
|
271
271
|
]);
|
|
272
272
|
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "WALL_PROFILE_FAILED", "Failed to create wall profile"));
|
|
273
|
-
const solidResult = extrude(_usingCtx$
|
|
273
|
+
const solidResult = extrude(_usingCtx$19.u(profileResult.value), [
|
|
274
274
|
length,
|
|
275
275
|
0,
|
|
276
276
|
0
|
|
@@ -283,16 +283,16 @@ function wallToSolid(spec) {
|
|
|
283
283
|
}
|
|
284
284
|
return ok(solid);
|
|
285
285
|
} catch (_) {
|
|
286
|
-
_usingCtx$
|
|
286
|
+
_usingCtx$19.e = _;
|
|
287
287
|
} finally {
|
|
288
|
-
_usingCtx$
|
|
288
|
+
_usingCtx$19.d();
|
|
289
289
|
}
|
|
290
290
|
}
|
|
291
291
|
//#endregion
|
|
292
292
|
//#region src/elementFns/slabFns.ts
|
|
293
293
|
function slabToSolid(spec) {
|
|
294
294
|
try {
|
|
295
|
-
var _usingCtx$
|
|
295
|
+
var _usingCtx$18 = _usingCtx();
|
|
296
296
|
if (spec.length <= 0) return err(specError("SLAB_ZERO_LENGTH", "Slab length must be positive"));
|
|
297
297
|
if (spec.width <= 0) return err(specError("SLAB_ZERO_WIDTH", "Slab width must be positive"));
|
|
298
298
|
if (spec.thickness <= 0) return err(specError("SLAB_ZERO_THICKNESS", "Slab thickness must be positive"));
|
|
@@ -320,7 +320,7 @@ function slabToSolid(spec) {
|
|
|
320
320
|
]
|
|
321
321
|
]);
|
|
322
322
|
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "SLAB_PROFILE_FAILED", "Failed to create slab profile"));
|
|
323
|
-
const solidResult = extrude(_usingCtx$
|
|
323
|
+
const solidResult = extrude(_usingCtx$18.u(profileResult.value), [
|
|
324
324
|
0,
|
|
325
325
|
0,
|
|
326
326
|
thickness
|
|
@@ -333,9 +333,9 @@ function slabToSolid(spec) {
|
|
|
333
333
|
}
|
|
334
334
|
return ok(solid);
|
|
335
335
|
} catch (_) {
|
|
336
|
-
_usingCtx$
|
|
336
|
+
_usingCtx$18.e = _;
|
|
337
337
|
} finally {
|
|
338
|
-
_usingCtx$
|
|
338
|
+
_usingCtx$18.d();
|
|
339
339
|
}
|
|
340
340
|
}
|
|
341
341
|
//#endregion
|
|
@@ -5012,14 +5012,14 @@ function to3D(points) {
|
|
|
5012
5012
|
}
|
|
5013
5013
|
function extendedProfileToFace(profile) {
|
|
5014
5014
|
try {
|
|
5015
|
-
var _usingCtx$
|
|
5015
|
+
var _usingCtx$17 = _usingCtx();
|
|
5016
5016
|
const invalid = validateProfile(profile);
|
|
5017
5017
|
if (invalid !== null) return err(invalid);
|
|
5018
5018
|
const outerResult = polygon(to3D(outerLoop(profile)));
|
|
5019
5019
|
if (!outerResult.ok) return err(fromBrepError(outerResult.error, "PROFILE_FACE_FAILED", "Failed to build profile outer face"));
|
|
5020
5020
|
const holes = holeLoops(profile);
|
|
5021
5021
|
if (holes.length === 0) return ok(outerResult.value);
|
|
5022
|
-
const outerFace = _usingCtx$
|
|
5022
|
+
const outerFace = _usingCtx$17.u(outerResult.value);
|
|
5023
5023
|
const holeFaces = [];
|
|
5024
5024
|
const holeWires = [];
|
|
5025
5025
|
const disposeHoleFaces = () => {
|
|
@@ -5044,9 +5044,9 @@ function extendedProfileToFace(profile) {
|
|
|
5044
5044
|
disposeHoleFaces();
|
|
5045
5045
|
return ok(faceWithHoles);
|
|
5046
5046
|
} catch (_) {
|
|
5047
|
-
_usingCtx$
|
|
5047
|
+
_usingCtx$17.e = _;
|
|
5048
5048
|
} finally {
|
|
5049
|
-
_usingCtx$
|
|
5049
|
+
_usingCtx$17.d();
|
|
5050
5050
|
}
|
|
5051
5051
|
}
|
|
5052
5052
|
//#endregion
|
|
@@ -5367,7 +5367,7 @@ function profileToPolygon(profile, circleSegments = 32) {
|
|
|
5367
5367
|
//#region src/elementFns/beamFns.ts
|
|
5368
5368
|
function beamToSolid(spec) {
|
|
5369
5369
|
try {
|
|
5370
|
-
var _usingCtx$
|
|
5370
|
+
var _usingCtx$16 = _usingCtx();
|
|
5371
5371
|
if (spec.length <= 0) return err(specError("BEAM_ZERO_LENGTH", "Beam length must be positive"));
|
|
5372
5372
|
if (isExtendedProfile(spec.profile)) try {
|
|
5373
5373
|
var _usingCtx3 = _usingCtx();
|
|
@@ -5402,7 +5402,7 @@ function beamToSolid(spec) {
|
|
|
5402
5402
|
py
|
|
5403
5403
|
]));
|
|
5404
5404
|
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "BEAM_PROFILE_FAILED", "Failed to create beam profile"));
|
|
5405
|
-
const solidResult = extrude(_usingCtx$
|
|
5405
|
+
const solidResult = extrude(_usingCtx$16.u(profileResult.value), [
|
|
5406
5406
|
spec.length,
|
|
5407
5407
|
0,
|
|
5408
5408
|
0
|
|
@@ -5415,16 +5415,16 @@ function beamToSolid(spec) {
|
|
|
5415
5415
|
}
|
|
5416
5416
|
return ok(solid);
|
|
5417
5417
|
} catch (_) {
|
|
5418
|
-
_usingCtx$
|
|
5418
|
+
_usingCtx$16.e = _;
|
|
5419
5419
|
} finally {
|
|
5420
|
-
_usingCtx$
|
|
5420
|
+
_usingCtx$16.d();
|
|
5421
5421
|
}
|
|
5422
5422
|
}
|
|
5423
5423
|
//#endregion
|
|
5424
5424
|
//#region src/elementFns/columnFns.ts
|
|
5425
5425
|
function columnToSolid(spec) {
|
|
5426
5426
|
try {
|
|
5427
|
-
var _usingCtx$
|
|
5427
|
+
var _usingCtx$15 = _usingCtx();
|
|
5428
5428
|
if (spec.height <= 0) return err(specError("COLUMN_ZERO_HEIGHT", "Column height must be positive"));
|
|
5429
5429
|
if (isExtendedProfile(spec.profile)) try {
|
|
5430
5430
|
var _usingCtx3 = _usingCtx();
|
|
@@ -5452,7 +5452,7 @@ function columnToSolid(spec) {
|
|
|
5452
5452
|
const profilePts = profilePtsResult.value;
|
|
5453
5453
|
const profileResult = polygon(profilePts);
|
|
5454
5454
|
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "COLUMN_PROFILE_FAILED", "Failed to create column profile"));
|
|
5455
|
-
const solidResult = extrude(_usingCtx$
|
|
5455
|
+
const solidResult = extrude(_usingCtx$15.u(profileResult.value), [
|
|
5456
5456
|
0,
|
|
5457
5457
|
0,
|
|
5458
5458
|
spec.height
|
|
@@ -5465,9 +5465,9 @@ function columnToSolid(spec) {
|
|
|
5465
5465
|
}
|
|
5466
5466
|
return ok(solid);
|
|
5467
5467
|
} catch (_) {
|
|
5468
|
-
_usingCtx$
|
|
5468
|
+
_usingCtx$15.e = _;
|
|
5469
5469
|
} finally {
|
|
5470
|
-
_usingCtx$
|
|
5470
|
+
_usingCtx$15.d();
|
|
5471
5471
|
}
|
|
5472
5472
|
}
|
|
5473
5473
|
//#endregion
|
|
@@ -5475,7 +5475,7 @@ function columnToSolid(spec) {
|
|
|
5475
5475
|
var EPSILON_MM$1 = 1;
|
|
5476
5476
|
function openingToSolid(spec, wallThickness) {
|
|
5477
5477
|
try {
|
|
5478
|
-
var _usingCtx$
|
|
5478
|
+
var _usingCtx$14 = _usingCtx();
|
|
5479
5479
|
if (spec.width <= 0) return err(specError("OPENING_ZERO_WIDTH", "Opening width must be positive"));
|
|
5480
5480
|
if (spec.height <= 0) return err(specError("OPENING_ZERO_HEIGHT", "Opening height must be positive"));
|
|
5481
5481
|
if (wallThickness <= 0) return err(specError("OPENING_ZERO_WALL_THICKNESS", "Wall thickness must be positive"));
|
|
@@ -5507,7 +5507,7 @@ function openingToSolid(spec, wallThickness) {
|
|
|
5507
5507
|
]
|
|
5508
5508
|
]);
|
|
5509
5509
|
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "OPENING_PROFILE_FAILED", "Failed to create opening profile"));
|
|
5510
|
-
const solidResult = extrude(_usingCtx$
|
|
5510
|
+
const solidResult = extrude(_usingCtx$14.u(profileResult.value), [
|
|
5511
5511
|
spec.width,
|
|
5512
5512
|
0,
|
|
5513
5513
|
0
|
|
@@ -5520,9 +5520,9 @@ function openingToSolid(spec, wallThickness) {
|
|
|
5520
5520
|
}
|
|
5521
5521
|
return ok(solid);
|
|
5522
5522
|
} catch (_) {
|
|
5523
|
-
_usingCtx$
|
|
5523
|
+
_usingCtx$14.e = _;
|
|
5524
5524
|
} finally {
|
|
5525
|
-
_usingCtx$
|
|
5525
|
+
_usingCtx$14.d();
|
|
5526
5526
|
}
|
|
5527
5527
|
}
|
|
5528
5528
|
//#endregion
|
|
@@ -5530,7 +5530,7 @@ function openingToSolid(spec, wallThickness) {
|
|
|
5530
5530
|
var EPSILON_MM = 1;
|
|
5531
5531
|
function slabOpeningToSolid(spec, slabThickness) {
|
|
5532
5532
|
try {
|
|
5533
|
-
var _usingCtx$
|
|
5533
|
+
var _usingCtx$13 = _usingCtx();
|
|
5534
5534
|
if (spec.sizeX <= 0) return err(specError("SLAB_OPENING_ZERO_SIZE_X", "Slab opening sizeX must be positive"));
|
|
5535
5535
|
if (spec.sizeY <= 0) return err(specError("SLAB_OPENING_ZERO_SIZE_Y", "Slab opening sizeY must be positive"));
|
|
5536
5536
|
if (slabThickness <= 0) return err(specError("SLAB_OPENING_ZERO_SLAB_THICKNESS", "Slab thickness must be positive"));
|
|
@@ -5562,7 +5562,7 @@ function slabOpeningToSolid(spec, slabThickness) {
|
|
|
5562
5562
|
]
|
|
5563
5563
|
]);
|
|
5564
5564
|
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "SLAB_OPENING_PROFILE_FAILED", "Failed to create slab opening profile"));
|
|
5565
|
-
const solidResult = extrude(_usingCtx$
|
|
5565
|
+
const solidResult = extrude(_usingCtx$13.u(profileResult.value), [
|
|
5566
5566
|
0,
|
|
5567
5567
|
0,
|
|
5568
5568
|
slabThickness + 2 * EPSILON_MM
|
|
@@ -5575,16 +5575,16 @@ function slabOpeningToSolid(spec, slabThickness) {
|
|
|
5575
5575
|
}
|
|
5576
5576
|
return ok(solid);
|
|
5577
5577
|
} catch (_) {
|
|
5578
|
-
_usingCtx$
|
|
5578
|
+
_usingCtx$13.e = _;
|
|
5579
5579
|
} finally {
|
|
5580
|
-
_usingCtx$
|
|
5580
|
+
_usingCtx$13.d();
|
|
5581
5581
|
}
|
|
5582
5582
|
}
|
|
5583
5583
|
//#endregion
|
|
5584
5584
|
//#region src/elementFns/spaceFns.ts
|
|
5585
5585
|
function spaceToSolid(spec) {
|
|
5586
5586
|
try {
|
|
5587
|
-
var _usingCtx$
|
|
5587
|
+
var _usingCtx$12 = _usingCtx();
|
|
5588
5588
|
if (spec.length <= 0) return err(specError("SPACE_ZERO_LENGTH", "Space length must be positive"));
|
|
5589
5589
|
if (spec.width <= 0) return err(specError("SPACE_ZERO_WIDTH", "Space width must be positive"));
|
|
5590
5590
|
if (spec.height <= 0) return err(specError("SPACE_ZERO_HEIGHT", "Space height must be positive"));
|
|
@@ -5612,7 +5612,7 @@ function spaceToSolid(spec) {
|
|
|
5612
5612
|
]
|
|
5613
5613
|
]);
|
|
5614
5614
|
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "SPACE_PROFILE_FAILED", "Failed to create space footprint"));
|
|
5615
|
-
const solidResult = extrude(_usingCtx$
|
|
5615
|
+
const solidResult = extrude(_usingCtx$12.u(profileResult.value), [
|
|
5616
5616
|
0,
|
|
5617
5617
|
0,
|
|
5618
5618
|
height
|
|
@@ -5625,21 +5625,26 @@ function spaceToSolid(spec) {
|
|
|
5625
5625
|
}
|
|
5626
5626
|
return ok(solid);
|
|
5627
5627
|
} catch (_) {
|
|
5628
|
-
_usingCtx$
|
|
5628
|
+
_usingCtx$12.e = _;
|
|
5629
5629
|
} finally {
|
|
5630
|
-
_usingCtx$
|
|
5630
|
+
_usingCtx$12.d();
|
|
5631
5631
|
}
|
|
5632
5632
|
}
|
|
5633
5633
|
//#endregion
|
|
5634
5634
|
//#region src/elementFns/roofFns.ts
|
|
5635
|
-
|
|
5635
|
+
var DEG2RAD = Math.PI / 180;
|
|
5636
|
+
function gate(solid, code) {
|
|
5637
|
+
if (!isValidSolid(solid)) {
|
|
5638
|
+
solid[Symbol.dispose]();
|
|
5639
|
+
return err(geometryError(code, "Roof solid failed validity check"));
|
|
5640
|
+
}
|
|
5641
|
+
return ok(solid);
|
|
5642
|
+
}
|
|
5643
|
+
function flatRoof(spec) {
|
|
5636
5644
|
try {
|
|
5637
|
-
var _usingCtx$
|
|
5638
|
-
if (spec.length <= 0) return err(specError("ROOF_ZERO_LENGTH", "Roof length must be positive"));
|
|
5639
|
-
if (spec.width <= 0) return err(specError("ROOF_ZERO_WIDTH", "Roof width must be positive"));
|
|
5640
|
-
if (spec.thickness <= 0) return err(specError("ROOF_ZERO_THICKNESS", "Roof thickness must be positive"));
|
|
5645
|
+
var _usingCtx$11 = _usingCtx();
|
|
5641
5646
|
const { length, width, thickness } = spec;
|
|
5642
|
-
const
|
|
5647
|
+
const face = polygon([
|
|
5643
5648
|
[
|
|
5644
5649
|
0,
|
|
5645
5650
|
0,
|
|
@@ -5661,30 +5666,212 @@ function roofToSolid(spec) {
|
|
|
5661
5666
|
0
|
|
5662
5667
|
]
|
|
5663
5668
|
]);
|
|
5664
|
-
if (!
|
|
5665
|
-
const
|
|
5669
|
+
if (!face.ok) return err(fromBrepError(face.error, "ROOF_PROFILE_FAILED", "Failed to create roof profile"));
|
|
5670
|
+
const solid = extrude(_usingCtx$11.u(face.value), [
|
|
5666
5671
|
0,
|
|
5667
5672
|
0,
|
|
5668
5673
|
thickness
|
|
5669
5674
|
]);
|
|
5670
|
-
if (!
|
|
5671
|
-
|
|
5672
|
-
if (!isValidSolid(solid)) {
|
|
5673
|
-
solid[Symbol.dispose]();
|
|
5674
|
-
return err(geometryError("ROOF_INVALID_SOLID", "Extruded roof solid failed validity check"));
|
|
5675
|
-
}
|
|
5676
|
-
return ok(solid);
|
|
5675
|
+
if (!solid.ok) return err(fromBrepError(solid.error, "ROOF_EXTRUDE_FAILED", "Failed to extrude roof profile"));
|
|
5676
|
+
return gate(solid.value, "ROOF_INVALID_SOLID");
|
|
5677
5677
|
} catch (_) {
|
|
5678
|
-
_usingCtx$
|
|
5678
|
+
_usingCtx$11.e = _;
|
|
5679
5679
|
} finally {
|
|
5680
|
-
_usingCtx$
|
|
5680
|
+
_usingCtx$11.d();
|
|
5681
|
+
}
|
|
5682
|
+
}
|
|
5683
|
+
function shedRoof(spec, pitch) {
|
|
5684
|
+
try {
|
|
5685
|
+
var _usingCtx3 = _usingCtx();
|
|
5686
|
+
const { length, width, thickness } = spec;
|
|
5687
|
+
const rise = width * Math.tan(pitch * DEG2RAD);
|
|
5688
|
+
const face = polygon([
|
|
5689
|
+
[
|
|
5690
|
+
0,
|
|
5691
|
+
0,
|
|
5692
|
+
0
|
|
5693
|
+
],
|
|
5694
|
+
[
|
|
5695
|
+
0,
|
|
5696
|
+
width,
|
|
5697
|
+
0
|
|
5698
|
+
],
|
|
5699
|
+
[
|
|
5700
|
+
0,
|
|
5701
|
+
width,
|
|
5702
|
+
thickness + rise
|
|
5703
|
+
],
|
|
5704
|
+
[
|
|
5705
|
+
0,
|
|
5706
|
+
0,
|
|
5707
|
+
thickness
|
|
5708
|
+
]
|
|
5709
|
+
]);
|
|
5710
|
+
if (!face.ok) return err(fromBrepError(face.error, "ROOF_PROFILE_FAILED", "Failed to create shed profile"));
|
|
5711
|
+
const solid = extrude(_usingCtx3.u(face.value), [
|
|
5712
|
+
length,
|
|
5713
|
+
0,
|
|
5714
|
+
0
|
|
5715
|
+
]);
|
|
5716
|
+
if (!solid.ok) return err(fromBrepError(solid.error, "ROOF_EXTRUDE_FAILED", "Failed to extrude shed roof"));
|
|
5717
|
+
return gate(solid.value, "ROOF_INVALID_SOLID");
|
|
5718
|
+
} catch (_) {
|
|
5719
|
+
_usingCtx3.e = _;
|
|
5720
|
+
} finally {
|
|
5721
|
+
_usingCtx3.d();
|
|
5722
|
+
}
|
|
5723
|
+
}
|
|
5724
|
+
function gableRoof(spec, pitch) {
|
|
5725
|
+
try {
|
|
5726
|
+
var _usingCtx4 = _usingCtx();
|
|
5727
|
+
const { length, width, thickness } = spec;
|
|
5728
|
+
const ridge = width / 2 * Math.tan(pitch * DEG2RAD);
|
|
5729
|
+
const face = polygon([
|
|
5730
|
+
[
|
|
5731
|
+
0,
|
|
5732
|
+
0,
|
|
5733
|
+
0
|
|
5734
|
+
],
|
|
5735
|
+
[
|
|
5736
|
+
0,
|
|
5737
|
+
width,
|
|
5738
|
+
0
|
|
5739
|
+
],
|
|
5740
|
+
[
|
|
5741
|
+
0,
|
|
5742
|
+
width,
|
|
5743
|
+
thickness
|
|
5744
|
+
],
|
|
5745
|
+
[
|
|
5746
|
+
0,
|
|
5747
|
+
width / 2,
|
|
5748
|
+
thickness + ridge
|
|
5749
|
+
],
|
|
5750
|
+
[
|
|
5751
|
+
0,
|
|
5752
|
+
0,
|
|
5753
|
+
thickness
|
|
5754
|
+
]
|
|
5755
|
+
]);
|
|
5756
|
+
if (!face.ok) return err(fromBrepError(face.error, "ROOF_PROFILE_FAILED", "Failed to create gable profile"));
|
|
5757
|
+
const solid = extrude(_usingCtx4.u(face.value), [
|
|
5758
|
+
length,
|
|
5759
|
+
0,
|
|
5760
|
+
0
|
|
5761
|
+
]);
|
|
5762
|
+
if (!solid.ok) return err(fromBrepError(solid.error, "ROOF_EXTRUDE_FAILED", "Failed to extrude gable roof"));
|
|
5763
|
+
return gate(solid.value, "ROOF_INVALID_SOLID");
|
|
5764
|
+
} catch (_) {
|
|
5765
|
+
_usingCtx4.e = _;
|
|
5766
|
+
} finally {
|
|
5767
|
+
_usingCtx4.d();
|
|
5768
|
+
}
|
|
5769
|
+
}
|
|
5770
|
+
function hipRoof(spec, pitch) {
|
|
5771
|
+
const { length: l, width: w } = spec;
|
|
5772
|
+
const tan = Math.tan(pitch * DEG2RAD);
|
|
5773
|
+
const base = [
|
|
5774
|
+
[
|
|
5775
|
+
0,
|
|
5776
|
+
0,
|
|
5777
|
+
0
|
|
5778
|
+
],
|
|
5779
|
+
[
|
|
5780
|
+
l,
|
|
5781
|
+
0,
|
|
5782
|
+
0
|
|
5783
|
+
],
|
|
5784
|
+
[
|
|
5785
|
+
l,
|
|
5786
|
+
w,
|
|
5787
|
+
0
|
|
5788
|
+
],
|
|
5789
|
+
[
|
|
5790
|
+
0,
|
|
5791
|
+
w,
|
|
5792
|
+
0
|
|
5793
|
+
]
|
|
5794
|
+
];
|
|
5795
|
+
let ridge;
|
|
5796
|
+
if (l >= w) {
|
|
5797
|
+
const h = w / 2 * tan;
|
|
5798
|
+
ridge = [[
|
|
5799
|
+
w / 2,
|
|
5800
|
+
w / 2,
|
|
5801
|
+
h
|
|
5802
|
+
], [
|
|
5803
|
+
l - w / 2,
|
|
5804
|
+
w / 2,
|
|
5805
|
+
h
|
|
5806
|
+
]];
|
|
5807
|
+
} else {
|
|
5808
|
+
const h = l / 2 * tan;
|
|
5809
|
+
ridge = [[
|
|
5810
|
+
l / 2,
|
|
5811
|
+
l / 2,
|
|
5812
|
+
h
|
|
5813
|
+
], [
|
|
5814
|
+
l / 2,
|
|
5815
|
+
w - l / 2,
|
|
5816
|
+
h
|
|
5817
|
+
]];
|
|
5818
|
+
}
|
|
5819
|
+
const solid = convexHull([...base, ...ridge]);
|
|
5820
|
+
if (!solid.ok) return err(fromBrepError(solid.error, "ROOF_HIP_FAILED", "Failed to build hip roof"));
|
|
5821
|
+
return gate(solid.value, "ROOF_INVALID_SOLID");
|
|
5822
|
+
}
|
|
5823
|
+
function domeRoof(spec) {
|
|
5824
|
+
const { length, width } = spec;
|
|
5825
|
+
const r = Math.min(length, width) / 2;
|
|
5826
|
+
const cx = length / 2;
|
|
5827
|
+
const cy = width / 2;
|
|
5828
|
+
const segments = 24;
|
|
5829
|
+
const rings = [
|
|
5830
|
+
0,
|
|
5831
|
+
.4,
|
|
5832
|
+
.7,
|
|
5833
|
+
.9
|
|
5834
|
+
];
|
|
5835
|
+
const pts = [];
|
|
5836
|
+
for (const h of rings) {
|
|
5837
|
+
const z = r * h;
|
|
5838
|
+
const ringR = r * Math.sqrt(1 - h * h);
|
|
5839
|
+
for (let i = 0; i < segments; i++) {
|
|
5840
|
+
const a = 2 * Math.PI * i / segments;
|
|
5841
|
+
pts.push([
|
|
5842
|
+
cx + ringR * Math.cos(a),
|
|
5843
|
+
cy + ringR * Math.sin(a),
|
|
5844
|
+
z
|
|
5845
|
+
]);
|
|
5846
|
+
}
|
|
5847
|
+
}
|
|
5848
|
+
pts.push([
|
|
5849
|
+
cx,
|
|
5850
|
+
cy,
|
|
5851
|
+
r
|
|
5852
|
+
]);
|
|
5853
|
+
const solid = convexHull(pts);
|
|
5854
|
+
if (!solid.ok) return err(fromBrepError(solid.error, "ROOF_DOME_FAILED", "Failed to build dome roof"));
|
|
5855
|
+
return gate(solid.value, "ROOF_INVALID_SOLID");
|
|
5856
|
+
}
|
|
5857
|
+
function roofToSolid(spec) {
|
|
5858
|
+
if (spec.length <= 0) return err(specError("ROOF_ZERO_LENGTH", "Roof length must be positive"));
|
|
5859
|
+
if (spec.width <= 0) return err(specError("ROOF_ZERO_WIDTH", "Roof width must be positive"));
|
|
5860
|
+
if (spec.thickness <= 0) return err(specError("ROOF_ZERO_THICKNESS", "Roof thickness must be positive"));
|
|
5861
|
+
if (spec.pitch === void 0) return flatRoof(spec);
|
|
5862
|
+
switch (spec.predefinedType) {
|
|
5863
|
+
case "SHED_ROOF": return shedRoof(spec, spec.pitch);
|
|
5864
|
+
case "GABLE_ROOF": return gableRoof(spec, spec.pitch);
|
|
5865
|
+
case "HIP_ROOF": return hipRoof(spec, spec.pitch);
|
|
5866
|
+
case "DOME_ROOF": return domeRoof(spec);
|
|
5867
|
+
default: return flatRoof(spec);
|
|
5681
5868
|
}
|
|
5682
5869
|
}
|
|
5683
5870
|
//#endregion
|
|
5684
5871
|
//#region src/elementFns/curtainWallFns.ts
|
|
5685
5872
|
function boxSolid(sizeX, sizeY, sizeZ) {
|
|
5686
5873
|
try {
|
|
5687
|
-
var _usingCtx$
|
|
5874
|
+
var _usingCtx$10 = _usingCtx();
|
|
5688
5875
|
const profileResult = polygon([
|
|
5689
5876
|
[
|
|
5690
5877
|
0,
|
|
@@ -5708,7 +5895,7 @@ function boxSolid(sizeX, sizeY, sizeZ) {
|
|
|
5708
5895
|
]
|
|
5709
5896
|
]);
|
|
5710
5897
|
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "CURTAIN_WALL_PROFILE_FAILED", "Failed to create component profile"));
|
|
5711
|
-
const solidResult = extrude(_usingCtx$
|
|
5898
|
+
const solidResult = extrude(_usingCtx$10.u(profileResult.value), [
|
|
5712
5899
|
0,
|
|
5713
5900
|
0,
|
|
5714
5901
|
sizeZ
|
|
@@ -5721,9 +5908,9 @@ function boxSolid(sizeX, sizeY, sizeZ) {
|
|
|
5721
5908
|
}
|
|
5722
5909
|
return ok(solid);
|
|
5723
5910
|
} catch (_) {
|
|
5724
|
-
_usingCtx$
|
|
5911
|
+
_usingCtx$10.e = _;
|
|
5725
5912
|
} finally {
|
|
5726
|
-
_usingCtx$
|
|
5913
|
+
_usingCtx$10.d();
|
|
5727
5914
|
}
|
|
5728
5915
|
}
|
|
5729
5916
|
function disposeComponents(components) {
|
|
@@ -5822,7 +6009,7 @@ function curtainWallToGrid(spec) {
|
|
|
5822
6009
|
//#region src/elementFns/foundationFns.ts
|
|
5823
6010
|
function footingToSolid(spec) {
|
|
5824
6011
|
try {
|
|
5825
|
-
var _usingCtx$
|
|
6012
|
+
var _usingCtx$9 = _usingCtx();
|
|
5826
6013
|
if (spec.length <= 0) return err(specError("FOOTING_ZERO_LENGTH", "Footing length must be positive"));
|
|
5827
6014
|
if (spec.width <= 0) return err(specError("FOOTING_ZERO_WIDTH", "Footing width must be positive"));
|
|
5828
6015
|
if (spec.thickness <= 0) return err(specError("FOOTING_ZERO_THICKNESS", "Footing thickness must be positive"));
|
|
@@ -5850,7 +6037,7 @@ function footingToSolid(spec) {
|
|
|
5850
6037
|
]
|
|
5851
6038
|
]);
|
|
5852
6039
|
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "FOOTING_PROFILE_FAILED", "Failed to create footing profile"));
|
|
5853
|
-
const solidResult = extrude(_usingCtx$
|
|
6040
|
+
const solidResult = extrude(_usingCtx$9.u(profileResult.value), [
|
|
5854
6041
|
0,
|
|
5855
6042
|
0,
|
|
5856
6043
|
thickness
|
|
@@ -5863,9 +6050,9 @@ function footingToSolid(spec) {
|
|
|
5863
6050
|
}
|
|
5864
6051
|
return ok(solid);
|
|
5865
6052
|
} catch (_) {
|
|
5866
|
-
_usingCtx$
|
|
6053
|
+
_usingCtx$9.e = _;
|
|
5867
6054
|
} finally {
|
|
5868
|
-
_usingCtx$
|
|
6055
|
+
_usingCtx$9.d();
|
|
5869
6056
|
}
|
|
5870
6057
|
}
|
|
5871
6058
|
function pileToSolid(spec) {
|
|
@@ -5918,12 +6105,9 @@ function pileToSolid(spec) {
|
|
|
5918
6105
|
}
|
|
5919
6106
|
//#endregion
|
|
5920
6107
|
//#region src/elementFns/railingFns.ts
|
|
5921
|
-
function
|
|
6108
|
+
function panelRailing(spec) {
|
|
5922
6109
|
try {
|
|
5923
|
-
var _usingCtx$
|
|
5924
|
-
if (spec.length <= 0) return err(specError("RAILING_ZERO_LENGTH", "Railing length must be positive"));
|
|
5925
|
-
if (spec.height <= 0) return err(specError("RAILING_ZERO_HEIGHT", "Railing height must be positive"));
|
|
5926
|
-
if (spec.thickness <= 0) return err(specError("RAILING_ZERO_THICKNESS", "Railing thickness must be positive"));
|
|
6110
|
+
var _usingCtx$8 = _usingCtx();
|
|
5927
6111
|
const { length, height, thickness } = spec;
|
|
5928
6112
|
const profileResult = polygon([
|
|
5929
6113
|
[
|
|
@@ -5948,7 +6132,7 @@ function railingToSolid(spec) {
|
|
|
5948
6132
|
]
|
|
5949
6133
|
]);
|
|
5950
6134
|
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "RAILING_PROFILE_FAILED", "Failed to create railing profile"));
|
|
5951
|
-
const solidResult = extrude(_usingCtx$
|
|
6135
|
+
const solidResult = extrude(_usingCtx$8.u(profileResult.value), [
|
|
5952
6136
|
length,
|
|
5953
6137
|
0,
|
|
5954
6138
|
0
|
|
@@ -5961,16 +6145,72 @@ function railingToSolid(spec) {
|
|
|
5961
6145
|
}
|
|
5962
6146
|
return ok(solid);
|
|
5963
6147
|
} catch (_) {
|
|
5964
|
-
_usingCtx$
|
|
6148
|
+
_usingCtx$8.e = _;
|
|
5965
6149
|
} finally {
|
|
5966
|
-
_usingCtx$
|
|
6150
|
+
_usingCtx$8.d();
|
|
6151
|
+
}
|
|
6152
|
+
}
|
|
6153
|
+
function postedRailing(spec) {
|
|
6154
|
+
const { length, height, thickness: t } = spec;
|
|
6155
|
+
const boxes = [];
|
|
6156
|
+
const postCount = Math.max(2, Math.round(length / 1e3) + 1);
|
|
6157
|
+
for (let i = 0; i < postCount; i++) {
|
|
6158
|
+
const x = t / 2 + (length - t) * (i / (postCount - 1));
|
|
6159
|
+
boxes.push(box(t, t, height, {
|
|
6160
|
+
at: [
|
|
6161
|
+
x,
|
|
6162
|
+
t / 2,
|
|
6163
|
+
height / 2
|
|
6164
|
+
],
|
|
6165
|
+
centered: true
|
|
6166
|
+
}));
|
|
6167
|
+
}
|
|
6168
|
+
for (const z of [height - t / 2, t / 2]) boxes.push(box(length, t, t, {
|
|
6169
|
+
at: [
|
|
6170
|
+
length / 2,
|
|
6171
|
+
t / 2,
|
|
6172
|
+
z
|
|
6173
|
+
],
|
|
6174
|
+
centered: true
|
|
6175
|
+
}));
|
|
6176
|
+
const scratch = [...boxes];
|
|
6177
|
+
let acc;
|
|
6178
|
+
let failure;
|
|
6179
|
+
for (const b of boxes) {
|
|
6180
|
+
if (acc === void 0) {
|
|
6181
|
+
acc = b;
|
|
6182
|
+
continue;
|
|
6183
|
+
}
|
|
6184
|
+
const fused = fuse(acc, b);
|
|
6185
|
+
if (!fused.ok) {
|
|
6186
|
+
failure = fromBrepError(fused.error, "RAILING_FUSE_FAILED", "Failed to fuse railing parts");
|
|
6187
|
+
break;
|
|
6188
|
+
}
|
|
6189
|
+
acc = fused.value;
|
|
6190
|
+
scratch.push(acc);
|
|
5967
6191
|
}
|
|
6192
|
+
const survivor = failure ? void 0 : acc;
|
|
6193
|
+
for (const s of scratch) if (s !== survivor) s[Symbol.dispose]();
|
|
6194
|
+
if (failure) return err(failure);
|
|
6195
|
+
if (survivor === void 0) return err(geometryError("RAILING_INVALID_SOLID", "Posted railing produced no solid"));
|
|
6196
|
+
const result = survivor;
|
|
6197
|
+
if (!isValidSolid(result)) {
|
|
6198
|
+
result[Symbol.dispose]();
|
|
6199
|
+
return err(geometryError("RAILING_INVALID_SOLID", "Posted railing solid failed validity check"));
|
|
6200
|
+
}
|
|
6201
|
+
return ok(result);
|
|
6202
|
+
}
|
|
6203
|
+
function railingToSolid(spec) {
|
|
6204
|
+
if (spec.length <= 0) return err(specError("RAILING_ZERO_LENGTH", "Railing length must be positive"));
|
|
6205
|
+
if (spec.height <= 0) return err(specError("RAILING_ZERO_HEIGHT", "Railing height must be positive"));
|
|
6206
|
+
if (spec.thickness <= 0) return err(specError("RAILING_ZERO_THICKNESS", "Railing thickness must be positive"));
|
|
6207
|
+
return spec.infill === "POSTED" ? postedRailing(spec) : panelRailing(spec);
|
|
5968
6208
|
}
|
|
5969
6209
|
//#endregion
|
|
5970
6210
|
//#region src/elementFns/coveringFns.ts
|
|
5971
6211
|
function coveringToSolid(spec) {
|
|
5972
6212
|
try {
|
|
5973
|
-
var _usingCtx$
|
|
6213
|
+
var _usingCtx$7 = _usingCtx();
|
|
5974
6214
|
if (spec.length <= 0) return err(specError("COVERING_ZERO_LENGTH", "Covering length must be positive"));
|
|
5975
6215
|
if (spec.width <= 0) return err(specError("COVERING_ZERO_WIDTH", "Covering width must be positive"));
|
|
5976
6216
|
if (spec.thickness <= 0) return err(specError("COVERING_ZERO_THICKNESS", "Covering thickness must be positive"));
|
|
@@ -5998,7 +6238,7 @@ function coveringToSolid(spec) {
|
|
|
5998
6238
|
]
|
|
5999
6239
|
]);
|
|
6000
6240
|
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "COVERING_PROFILE_FAILED", "Failed to create covering profile"));
|
|
6001
|
-
const solidResult = extrude(_usingCtx$
|
|
6241
|
+
const solidResult = extrude(_usingCtx$7.u(profileResult.value), [
|
|
6002
6242
|
0,
|
|
6003
6243
|
0,
|
|
6004
6244
|
thickness
|
|
@@ -6011,9 +6251,9 @@ function coveringToSolid(spec) {
|
|
|
6011
6251
|
}
|
|
6012
6252
|
return ok(solid);
|
|
6013
6253
|
} catch (_) {
|
|
6014
|
-
_usingCtx$
|
|
6254
|
+
_usingCtx$7.e = _;
|
|
6015
6255
|
} finally {
|
|
6016
|
-
_usingCtx$
|
|
6256
|
+
_usingCtx$7.d();
|
|
6017
6257
|
}
|
|
6018
6258
|
}
|
|
6019
6259
|
//#endregion
|
|
@@ -6443,17 +6683,17 @@ var BimModel = class {
|
|
|
6443
6683
|
}
|
|
6444
6684
|
#cutWallGeometry(wall, openingSpec) {
|
|
6445
6685
|
try {
|
|
6446
|
-
var _usingCtx$
|
|
6686
|
+
var _usingCtx$6 = _usingCtx();
|
|
6447
6687
|
const toolResult = openingToSolid(openingSpec, wall.spec.thickness);
|
|
6448
6688
|
if (!toolResult.ok) return err(toolResult.error);
|
|
6449
|
-
const tool = _usingCtx$
|
|
6689
|
+
const tool = _usingCtx$6.u(toolResult.value);
|
|
6450
6690
|
const cutResult = cut(wall.geometry, tool);
|
|
6451
6691
|
if (!cutResult.ok) return err(fromBrepError(cutResult.error, "WALL_CUT_FAILED", "Boolean cut of wall with opening failed"));
|
|
6452
6692
|
return ok(cutResult.value);
|
|
6453
6693
|
} catch (_) {
|
|
6454
|
-
_usingCtx$
|
|
6694
|
+
_usingCtx$6.e = _;
|
|
6455
6695
|
} finally {
|
|
6456
|
-
_usingCtx$
|
|
6696
|
+
_usingCtx$6.d();
|
|
6457
6697
|
}
|
|
6458
6698
|
}
|
|
6459
6699
|
#replaceWallGeometry(wall, newGeometry) {
|
|
@@ -6690,18 +6930,365 @@ var BimModel = class {
|
|
|
6690
6930
|
this.#elements.set(localId, el);
|
|
6691
6931
|
return localId;
|
|
6692
6932
|
}
|
|
6693
|
-
#makeRel(fields) {
|
|
6694
|
-
const localId = this.#counter.next();
|
|
6695
|
-
const guid = deriveIfcGuidSync(makeRelKey(this.#modelScope, fields.kind, localId));
|
|
6696
|
-
const rel = {
|
|
6697
|
-
...fields,
|
|
6698
|
-
guid,
|
|
6699
|
-
localId
|
|
6700
|
-
};
|
|
6701
|
-
this.#relationships.set(localId, rel);
|
|
6702
|
-
return localId;
|
|
6933
|
+
#makeRel(fields) {
|
|
6934
|
+
const localId = this.#counter.next();
|
|
6935
|
+
const guid = deriveIfcGuidSync(makeRelKey(this.#modelScope, fields.kind, localId));
|
|
6936
|
+
const rel = {
|
|
6937
|
+
...fields,
|
|
6938
|
+
guid,
|
|
6939
|
+
localId
|
|
6940
|
+
};
|
|
6941
|
+
this.#relationships.set(localId, rel);
|
|
6942
|
+
return localId;
|
|
6943
|
+
}
|
|
6944
|
+
};
|
|
6945
|
+
//#endregion
|
|
6946
|
+
//#region src/import/placement.ts
|
|
6947
|
+
/**
|
|
6948
|
+
* Metres-per-file-unit length scale read from the IfcUnitAssignment's
|
|
6949
|
+
* LENGTHUNIT. Multiply a file-unit length by this to get metres, then by 1000
|
|
6950
|
+
* for brepjs millimetres. Returns 1.0 (assume metres) when no length unit is
|
|
6951
|
+
* declared.
|
|
6952
|
+
*/
|
|
6953
|
+
function readLengthScale(reader) {
|
|
6954
|
+
const assignments = reader.getLinesOfType(WebIFC.IFCUNITASSIGNMENT);
|
|
6955
|
+
for (const assignmentId of assignments) {
|
|
6956
|
+
const units = asRefArray$1(reader.getLine(assignmentId)?.["Units"]);
|
|
6957
|
+
for (const unitId of units) {
|
|
6958
|
+
const scale = lengthScaleFromUnit(reader, unitId);
|
|
6959
|
+
if (scale !== null) return scale;
|
|
6960
|
+
}
|
|
6961
|
+
}
|
|
6962
|
+
return 1;
|
|
6963
|
+
}
|
|
6964
|
+
/**
|
|
6965
|
+
* Radians-per-file-unit plane-angle scale read from the IfcUnitAssignment's
|
|
6966
|
+
* PLANEANGLEUNIT (1 for RADIAN, ~0.0174533 for DEGREE). Defaults to 1 (the IFC
|
|
6967
|
+
* default plane-angle unit is the radian).
|
|
6968
|
+
*/
|
|
6969
|
+
function readPlaneAngleScale(reader) {
|
|
6970
|
+
for (const assignmentId of reader.getLinesOfType(WebIFC.IFCUNITASSIGNMENT)) {
|
|
6971
|
+
const units = asRefArray$1(reader.getLine(assignmentId)?.["Units"]);
|
|
6972
|
+
for (const unitId of units) {
|
|
6973
|
+
const s = planeAngleScaleFromUnit(reader, unitId);
|
|
6974
|
+
if (s !== null) return s;
|
|
6975
|
+
}
|
|
6976
|
+
}
|
|
6977
|
+
return 1;
|
|
6978
|
+
}
|
|
6979
|
+
function planeAngleScaleFromUnit(reader, unitId) {
|
|
6980
|
+
const unit = reader.getLine(unitId);
|
|
6981
|
+
if (unit === null) return null;
|
|
6982
|
+
const type = reader.getLineType(unitId);
|
|
6983
|
+
if (type === WebIFC.IFCSIUNIT) {
|
|
6984
|
+
if (enumValue(unit["UnitType"]) !== "PLANEANGLEUNIT") return null;
|
|
6985
|
+
if (enumValue(unit["Name"]) !== "RADIAN") return null;
|
|
6986
|
+
return 1;
|
|
6987
|
+
}
|
|
6988
|
+
if (type === WebIFC.IFCCONVERSIONBASEDUNIT) {
|
|
6989
|
+
if (enumValue(unit["UnitType"]) !== "PLANEANGLEUNIT") return null;
|
|
6990
|
+
const measureId = refValue$2(unit["ConversionFactor"]);
|
|
6991
|
+
if (measureId === null) return null;
|
|
6992
|
+
const factor = numericValue(reader.getLine(measureId)?.["ValueComponent"]);
|
|
6993
|
+
if (factor === null) return null;
|
|
6994
|
+
return factor;
|
|
6995
|
+
}
|
|
6996
|
+
return null;
|
|
6997
|
+
}
|
|
6998
|
+
function lengthScaleFromUnit(reader, unitId) {
|
|
6999
|
+
const unit = reader.getLine(unitId);
|
|
7000
|
+
if (unit === null) return null;
|
|
7001
|
+
const type = reader.getLineType(unitId);
|
|
7002
|
+
if (type === WebIFC.IFCSIUNIT) {
|
|
7003
|
+
if (enumValue(unit["UnitType"]) !== "LENGTHUNIT") return null;
|
|
7004
|
+
if (enumValue(unit["Name"]) !== "METRE") return null;
|
|
7005
|
+
return siPrefixFactor(enumValue(unit["Prefix"]));
|
|
7006
|
+
}
|
|
7007
|
+
if (type === WebIFC.IFCCONVERSIONBASEDUNIT) {
|
|
7008
|
+
if (enumValue(unit["UnitType"]) !== "LENGTHUNIT") return null;
|
|
7009
|
+
const measureId = refValue$2(unit["ConversionFactor"]);
|
|
7010
|
+
if (measureId === null) return null;
|
|
7011
|
+
const measure = reader.getLine(measureId);
|
|
7012
|
+
const factor = numericValue(measure?.["ValueComponent"]);
|
|
7013
|
+
if (factor === null) return null;
|
|
7014
|
+
const baseId = refValue$2(measure?.["UnitComponent"]);
|
|
7015
|
+
return factor * (baseId !== null ? lengthScaleFromUnit(reader, baseId) ?? 1 : 1);
|
|
7016
|
+
}
|
|
7017
|
+
return null;
|
|
7018
|
+
}
|
|
7019
|
+
function siPrefixFactor(prefix) {
|
|
7020
|
+
switch (prefix) {
|
|
7021
|
+
case null: return 1;
|
|
7022
|
+
case "KILO": return 1e3;
|
|
7023
|
+
case "HECTO": return 100;
|
|
7024
|
+
case "DECA": return 10;
|
|
7025
|
+
case "DECI": return .1;
|
|
7026
|
+
case "CENTI": return .01;
|
|
7027
|
+
case "MILLI": return .001;
|
|
7028
|
+
case "MICRO": return 1e-6;
|
|
7029
|
+
default: return 1;
|
|
7030
|
+
}
|
|
7031
|
+
}
|
|
7032
|
+
/**
|
|
7033
|
+
* Builds a row-major MatrixTransform (for brepjs `applyMatrix`) from an
|
|
7034
|
+
* (origin, axisX, axisZ) frame, using the SAME IFC orthonormalization as
|
|
7035
|
+
* {@link readAxis2Placement3D}: z = normalize(axisZ); x = normalize(axisX
|
|
7036
|
+
* projected onto the plane ⊥ z); y = z × x. The basis vectors are the matrix
|
|
7037
|
+
* columns, so the row-major linear array is [Xx,Yx,Zx, Xy,Yy,Zy, Xz,Yz,Zz];
|
|
7038
|
+
* translation = origin (mm). This is the display-side counterpart to the IFC
|
|
7039
|
+
* writer's Axis2Placement3D (Axis=Z, RefDirection=X) so on-screen placement and
|
|
7040
|
+
* the IFC export agree.
|
|
7041
|
+
*/
|
|
7042
|
+
function placementToMatrix(f) {
|
|
7043
|
+
const z = normalize$1(f.axisZ);
|
|
7044
|
+
const dot = z[0] * f.axisX[0] + z[1] * f.axisX[1] + z[2] * f.axisX[2];
|
|
7045
|
+
const projX = [
|
|
7046
|
+
f.axisX[0] - dot * z[0],
|
|
7047
|
+
f.axisX[1] - dot * z[1],
|
|
7048
|
+
f.axisX[2] - dot * z[2]
|
|
7049
|
+
];
|
|
7050
|
+
const x = lengthSq(projX) < 1e-12 ? normalize$1(orthogonal$2(z)) : normalize$1(projX);
|
|
7051
|
+
const y = cross$1(z, x);
|
|
7052
|
+
return {
|
|
7053
|
+
linear: [
|
|
7054
|
+
x[0],
|
|
7055
|
+
y[0],
|
|
7056
|
+
z[0],
|
|
7057
|
+
x[1],
|
|
7058
|
+
y[1],
|
|
7059
|
+
z[1],
|
|
7060
|
+
x[2],
|
|
7061
|
+
y[2],
|
|
7062
|
+
z[2]
|
|
7063
|
+
],
|
|
7064
|
+
translation: [
|
|
7065
|
+
f.origin[0],
|
|
7066
|
+
f.origin[1],
|
|
7067
|
+
f.origin[2]
|
|
7068
|
+
]
|
|
7069
|
+
};
|
|
7070
|
+
}
|
|
7071
|
+
function cross$1(a, b) {
|
|
7072
|
+
return [
|
|
7073
|
+
a[1] * b[2] - a[2] * b[1],
|
|
7074
|
+
a[2] * b[0] - a[0] * b[2],
|
|
7075
|
+
a[0] * b[1] - a[1] * b[0]
|
|
7076
|
+
];
|
|
7077
|
+
}
|
|
7078
|
+
function lengthSq(v) {
|
|
7079
|
+
return v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
|
|
7080
|
+
}
|
|
7081
|
+
function normalize$1(v) {
|
|
7082
|
+
const len = Math.sqrt(lengthSq(v));
|
|
7083
|
+
if (len < 1e-12) return [
|
|
7084
|
+
0,
|
|
7085
|
+
0,
|
|
7086
|
+
1
|
|
7087
|
+
];
|
|
7088
|
+
return [
|
|
7089
|
+
v[0] / len,
|
|
7090
|
+
v[1] / len,
|
|
7091
|
+
v[2] / len
|
|
7092
|
+
];
|
|
7093
|
+
}
|
|
7094
|
+
function orthogonal$2(v) {
|
|
7095
|
+
return Math.abs(v[0]) < .9 ? cross$1(v, [
|
|
7096
|
+
1,
|
|
7097
|
+
0,
|
|
7098
|
+
0
|
|
7099
|
+
]) : cross$1(v, [
|
|
7100
|
+
0,
|
|
7101
|
+
1,
|
|
7102
|
+
0
|
|
7103
|
+
]);
|
|
7104
|
+
}
|
|
7105
|
+
function refValue$2(v) {
|
|
7106
|
+
if (v === null || v === void 0) return null;
|
|
7107
|
+
if (typeof v === "number") return v;
|
|
7108
|
+
const value = v.value;
|
|
7109
|
+
return typeof value === "number" ? value : null;
|
|
7110
|
+
}
|
|
7111
|
+
function asRefArray$1(v) {
|
|
7112
|
+
if (!Array.isArray(v)) return [];
|
|
7113
|
+
const out = [];
|
|
7114
|
+
for (const item of v) {
|
|
7115
|
+
const id = refValue$2(item);
|
|
7116
|
+
if (id !== null) out.push(id);
|
|
7117
|
+
}
|
|
7118
|
+
return out;
|
|
7119
|
+
}
|
|
7120
|
+
function numericValue(v) {
|
|
7121
|
+
if (typeof v === "number") return v;
|
|
7122
|
+
if (v === null || v === void 0) return null;
|
|
7123
|
+
const value = v.value;
|
|
7124
|
+
return typeof value === "number" ? value : null;
|
|
7125
|
+
}
|
|
7126
|
+
function enumValue(v) {
|
|
7127
|
+
if (typeof v === "string") return v;
|
|
7128
|
+
if (v === null || v === void 0) return null;
|
|
7129
|
+
const value = v.value;
|
|
7130
|
+
return typeof value === "string" ? value : null;
|
|
7131
|
+
}
|
|
7132
|
+
//#endregion
|
|
7133
|
+
//#region src/elementFns/stairFns.ts
|
|
7134
|
+
function buildSilhouette$1(numberOfRisers, riserHeight, treadLength) {
|
|
7135
|
+
const pts = [];
|
|
7136
|
+
pts.push([
|
|
7137
|
+
0,
|
|
7138
|
+
0,
|
|
7139
|
+
0
|
|
7140
|
+
]);
|
|
7141
|
+
let x = 0;
|
|
7142
|
+
let z = 0;
|
|
7143
|
+
for (let i = 0; i < numberOfRisers; i++) {
|
|
7144
|
+
z += riserHeight;
|
|
7145
|
+
pts.push([
|
|
7146
|
+
x,
|
|
7147
|
+
0,
|
|
7148
|
+
z
|
|
7149
|
+
]);
|
|
7150
|
+
x += treadLength;
|
|
7151
|
+
pts.push([
|
|
7152
|
+
x,
|
|
7153
|
+
0,
|
|
7154
|
+
z
|
|
7155
|
+
]);
|
|
7156
|
+
}
|
|
7157
|
+
pts.push([
|
|
7158
|
+
x,
|
|
7159
|
+
0,
|
|
7160
|
+
0
|
|
7161
|
+
]);
|
|
7162
|
+
return pts;
|
|
7163
|
+
}
|
|
7164
|
+
function stairFlightToSolid(spec) {
|
|
7165
|
+
try {
|
|
7166
|
+
var _usingCtx$5 = _usingCtx();
|
|
7167
|
+
if (spec.width <= 0) return err(specError("STAIR_FLIGHT_ZERO_WIDTH", "Stair flight width must be positive"));
|
|
7168
|
+
if (spec.riserHeight <= 0) return err(specError("STAIR_FLIGHT_ZERO_RISER", "Stair flight riserHeight must be positive"));
|
|
7169
|
+
if (spec.treadLength <= 0) return err(specError("STAIR_FLIGHT_ZERO_TREAD", "Stair flight treadLength must be positive"));
|
|
7170
|
+
if (!Number.isInteger(spec.numberOfRisers) || spec.numberOfRisers < 1) return err(specError("STAIR_FLIGHT_BAD_RISERS", "Stair flight numberOfRisers must be a positive integer"));
|
|
7171
|
+
const profileResult = polygon(buildSilhouette$1(spec.numberOfRisers, spec.riserHeight, spec.treadLength));
|
|
7172
|
+
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "STAIR_FLIGHT_PROFILE_FAILED", "Failed to create stair flight silhouette profile"));
|
|
7173
|
+
const solidResult = extrude(_usingCtx$5.u(profileResult.value), [
|
|
7174
|
+
0,
|
|
7175
|
+
spec.width,
|
|
7176
|
+
0
|
|
7177
|
+
]);
|
|
7178
|
+
if (!solidResult.ok) return err(fromBrepError(solidResult.error, "STAIR_FLIGHT_EXTRUDE_FAILED", "Failed to extrude stair flight silhouette"));
|
|
7179
|
+
const solid = solidResult.value;
|
|
7180
|
+
if (!isValidSolid(solid)) {
|
|
7181
|
+
solid[Symbol.dispose]();
|
|
7182
|
+
return err(geometryError("STAIR_FLIGHT_INVALID_SOLID", "Stair flight solid failed validity check"));
|
|
7183
|
+
}
|
|
7184
|
+
return ok({
|
|
7185
|
+
solid,
|
|
7186
|
+
geometrySimplified: false
|
|
7187
|
+
});
|
|
7188
|
+
} catch (_) {
|
|
7189
|
+
_usingCtx$5.e = _;
|
|
7190
|
+
} finally {
|
|
7191
|
+
_usingCtx$5.d();
|
|
7192
|
+
}
|
|
7193
|
+
}
|
|
7194
|
+
//#endregion
|
|
7195
|
+
//#region src/elementFns/placedGeometry.ts
|
|
7196
|
+
function place(solid, frame) {
|
|
7197
|
+
const result = applyMatrix(solid, placementToMatrix(frame));
|
|
7198
|
+
if (!result.ok) return err(fromBrepError(result.error, "PLACED_GEOMETRY_FAILED", "Failed to place element geometry"));
|
|
7199
|
+
return ok(result.value);
|
|
7200
|
+
}
|
|
7201
|
+
function disposeAll(solids) {
|
|
7202
|
+
for (const s of solids) s[Symbol.dispose]();
|
|
7203
|
+
}
|
|
7204
|
+
/**
|
|
7205
|
+
* Returns each element's geometry transformed to its world placement, as fresh
|
|
7206
|
+
* caller-owned solids, wrapped in a `Result` (Layer-2 code prefers `Result` over
|
|
7207
|
+
* throwing). **Dispose the returned solids** (e.g. via `using` / `[Symbol.dispose]`)
|
|
7208
|
+
* when you own their lifetime — they are independent of the model
|
|
7209
|
+
* (`BimModel[Symbol.dispose]` frees only the stored, unplaced `.geometry`). On any
|
|
7210
|
+
* failure the solids already built for this call are disposed before the error is
|
|
7211
|
+
* returned, so no partial array is leaked.
|
|
7212
|
+
*
|
|
7213
|
+
* Stairs carry no element solid (`.geometry` is null), so flight solids are built
|
|
7214
|
+
* from `spec.flights` and placed per flight. Curtain walls return placed panels +
|
|
7215
|
+
* mullions. Elements with no solid geometry (doors/windows/ramps/groups/spatial)
|
|
7216
|
+
* return an empty array.
|
|
7217
|
+
*/
|
|
7218
|
+
function placedSolids(el) {
|
|
7219
|
+
switch (el.category) {
|
|
7220
|
+
case "WALL":
|
|
7221
|
+
case "SLAB":
|
|
7222
|
+
case "BEAM":
|
|
7223
|
+
case "COLUMN":
|
|
7224
|
+
case "SPACE":
|
|
7225
|
+
case "ROOF":
|
|
7226
|
+
case "FOOTING":
|
|
7227
|
+
case "PILE":
|
|
7228
|
+
case "RAILING": {
|
|
7229
|
+
const placed = place(el.geometry, el.spec);
|
|
7230
|
+
if (!placed.ok) return placed;
|
|
7231
|
+
return ok([placed.value]);
|
|
7232
|
+
}
|
|
7233
|
+
case "STAIR": {
|
|
7234
|
+
const out = [];
|
|
7235
|
+
for (const flight of el.spec.flights) try {
|
|
7236
|
+
var _usingCtx$4 = _usingCtx();
|
|
7237
|
+
const built = stairFlightToSolid(flight);
|
|
7238
|
+
if (!built.ok) {
|
|
7239
|
+
disposeAll(out);
|
|
7240
|
+
return err(built.error);
|
|
7241
|
+
}
|
|
7242
|
+
const placed = place(_usingCtx$4.u(built.value.solid), flight);
|
|
7243
|
+
if (!placed.ok) {
|
|
7244
|
+
disposeAll(out);
|
|
7245
|
+
return placed;
|
|
7246
|
+
}
|
|
7247
|
+
out.push(placed.value);
|
|
7248
|
+
} catch (_) {
|
|
7249
|
+
_usingCtx$4.e = _;
|
|
7250
|
+
} finally {
|
|
7251
|
+
_usingCtx$4.d();
|
|
7252
|
+
}
|
|
7253
|
+
return ok(out);
|
|
7254
|
+
}
|
|
7255
|
+
case "CURTAIN_WALL": {
|
|
7256
|
+
const out = [];
|
|
7257
|
+
for (const c of [...el.geometry.panels, ...el.geometry.mullions]) try {
|
|
7258
|
+
var _usingCtx3 = _usingCtx();
|
|
7259
|
+
const componentLocal = place(c.solid, {
|
|
7260
|
+
origin: c.origin,
|
|
7261
|
+
axisX: [
|
|
7262
|
+
1,
|
|
7263
|
+
0,
|
|
7264
|
+
0
|
|
7265
|
+
],
|
|
7266
|
+
axisZ: [
|
|
7267
|
+
0,
|
|
7268
|
+
0,
|
|
7269
|
+
1
|
|
7270
|
+
]
|
|
7271
|
+
});
|
|
7272
|
+
if (!componentLocal.ok) {
|
|
7273
|
+
disposeAll(out);
|
|
7274
|
+
return componentLocal;
|
|
7275
|
+
}
|
|
7276
|
+
const placed = place(_usingCtx3.u(componentLocal.value), el.spec);
|
|
7277
|
+
if (!placed.ok) {
|
|
7278
|
+
disposeAll(out);
|
|
7279
|
+
return placed;
|
|
7280
|
+
}
|
|
7281
|
+
out.push(placed.value);
|
|
7282
|
+
} catch (_) {
|
|
7283
|
+
_usingCtx3.e = _;
|
|
7284
|
+
} finally {
|
|
7285
|
+
_usingCtx3.d();
|
|
7286
|
+
}
|
|
7287
|
+
return ok(out);
|
|
7288
|
+
}
|
|
7289
|
+
default: return ok([]);
|
|
6703
7290
|
}
|
|
6704
|
-
}
|
|
7291
|
+
}
|
|
6705
7292
|
//#endregion
|
|
6706
7293
|
//#region src/ifc-writer/schemaVersion.ts
|
|
6707
7294
|
/**
|
|
@@ -7972,7 +8559,7 @@ function writeSlabGeometry(w, spec, geomSubContextId, parentPlacementId) {
|
|
|
7972
8559
|
productDefinitionShapeId
|
|
7973
8560
|
};
|
|
7974
8561
|
}
|
|
7975
|
-
function writeRoofGeometry(w, spec, geomSubContextId, parentPlacementId) {
|
|
8562
|
+
function writeRoofGeometry(w, spec, solid, geomSubContextId, parentPlacementId) {
|
|
7976
8563
|
const placement3DId = writeAxis2Placement3D(w, spec.origin.map(toIfcLengthM), spec.axisZ, spec.axisX);
|
|
7977
8564
|
const localPlacementId = w.nextId();
|
|
7978
8565
|
w.writeLine({
|
|
@@ -7981,6 +8568,14 @@ function writeRoofGeometry(w, spec, geomSubContextId, parentPlacementId) {
|
|
|
7981
8568
|
PlacementRelTo: parentPlacementId !== null ? w.ref(parentPlacementId) : null,
|
|
7982
8569
|
RelativePlacement: w.ref(placement3DId)
|
|
7983
8570
|
});
|
|
8571
|
+
if (spec.pitch !== void 0) {
|
|
8572
|
+
const tess = writeTessellation(w, solid, geomSubContextId, localPlacementId);
|
|
8573
|
+
return {
|
|
8574
|
+
localPlacementId,
|
|
8575
|
+
productDefinitionShapeId: tess.productDefinitionShapeId,
|
|
8576
|
+
usedFallback: tess.usedFallback
|
|
8577
|
+
};
|
|
8578
|
+
}
|
|
7984
8579
|
const lengthM = toIfcLengthM(spec.length);
|
|
7985
8580
|
const widthM = toIfcLengthM(spec.width);
|
|
7986
8581
|
const thicknessM = toIfcLengthM(spec.thickness);
|
|
@@ -8048,7 +8643,8 @@ function writeRoofGeometry(w, spec, geomSubContextId, parentPlacementId) {
|
|
|
8048
8643
|
});
|
|
8049
8644
|
return {
|
|
8050
8645
|
localPlacementId,
|
|
8051
|
-
productDefinitionShapeId
|
|
8646
|
+
productDefinitionShapeId,
|
|
8647
|
+
usedFallback: false
|
|
8052
8648
|
};
|
|
8053
8649
|
}
|
|
8054
8650
|
function writeAxis2Placement2D$1(w) {
|
|
@@ -8751,68 +9347,6 @@ function writeRelContainedInSpatialStructure(w, guid, ownerHistoryId, relatingSt
|
|
|
8751
9347
|
});
|
|
8752
9348
|
}
|
|
8753
9349
|
//#endregion
|
|
8754
|
-
//#region src/elementFns/stairFns.ts
|
|
8755
|
-
function buildSilhouette$1(numberOfRisers, riserHeight, treadLength) {
|
|
8756
|
-
const pts = [];
|
|
8757
|
-
pts.push([
|
|
8758
|
-
0,
|
|
8759
|
-
0,
|
|
8760
|
-
0
|
|
8761
|
-
]);
|
|
8762
|
-
let x = 0;
|
|
8763
|
-
let z = 0;
|
|
8764
|
-
for (let i = 0; i < numberOfRisers; i++) {
|
|
8765
|
-
z += riserHeight;
|
|
8766
|
-
pts.push([
|
|
8767
|
-
x,
|
|
8768
|
-
0,
|
|
8769
|
-
z
|
|
8770
|
-
]);
|
|
8771
|
-
x += treadLength;
|
|
8772
|
-
pts.push([
|
|
8773
|
-
x,
|
|
8774
|
-
0,
|
|
8775
|
-
z
|
|
8776
|
-
]);
|
|
8777
|
-
}
|
|
8778
|
-
pts.push([
|
|
8779
|
-
x,
|
|
8780
|
-
0,
|
|
8781
|
-
0
|
|
8782
|
-
]);
|
|
8783
|
-
return pts;
|
|
8784
|
-
}
|
|
8785
|
-
function stairFlightToSolid(spec) {
|
|
8786
|
-
try {
|
|
8787
|
-
var _usingCtx$4 = _usingCtx();
|
|
8788
|
-
if (spec.width <= 0) return err(specError("STAIR_FLIGHT_ZERO_WIDTH", "Stair flight width must be positive"));
|
|
8789
|
-
if (spec.riserHeight <= 0) return err(specError("STAIR_FLIGHT_ZERO_RISER", "Stair flight riserHeight must be positive"));
|
|
8790
|
-
if (spec.treadLength <= 0) return err(specError("STAIR_FLIGHT_ZERO_TREAD", "Stair flight treadLength must be positive"));
|
|
8791
|
-
if (!Number.isInteger(spec.numberOfRisers) || spec.numberOfRisers < 1) return err(specError("STAIR_FLIGHT_BAD_RISERS", "Stair flight numberOfRisers must be a positive integer"));
|
|
8792
|
-
const profileResult = polygon(buildSilhouette$1(spec.numberOfRisers, spec.riserHeight, spec.treadLength));
|
|
8793
|
-
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "STAIR_FLIGHT_PROFILE_FAILED", "Failed to create stair flight silhouette profile"));
|
|
8794
|
-
const solidResult = extrude(_usingCtx$4.u(profileResult.value), [
|
|
8795
|
-
0,
|
|
8796
|
-
spec.width,
|
|
8797
|
-
0
|
|
8798
|
-
]);
|
|
8799
|
-
if (!solidResult.ok) return err(fromBrepError(solidResult.error, "STAIR_FLIGHT_EXTRUDE_FAILED", "Failed to extrude stair flight silhouette"));
|
|
8800
|
-
const solid = solidResult.value;
|
|
8801
|
-
if (!isValidSolid(solid)) {
|
|
8802
|
-
solid[Symbol.dispose]();
|
|
8803
|
-
return err(geometryError("STAIR_FLIGHT_INVALID_SOLID", "Stair flight solid failed validity check"));
|
|
8804
|
-
}
|
|
8805
|
-
return ok({
|
|
8806
|
-
solid,
|
|
8807
|
-
geometrySimplified: false
|
|
8808
|
-
});
|
|
8809
|
-
} catch (_) {
|
|
8810
|
-
_usingCtx$4.e = _;
|
|
8811
|
-
} finally {
|
|
8812
|
-
_usingCtx$4.d();
|
|
8813
|
-
}
|
|
8814
|
-
}
|
|
8815
|
-
//#endregion
|
|
8816
9350
|
//#region src/elementFns/rampFns.ts
|
|
8817
9351
|
function buildSilhouette(length, rise, thickness) {
|
|
8818
9352
|
return [
|
|
@@ -9123,7 +9657,7 @@ function writeRampAssembly(w, spec, rampKey, ownerHistoryId, geomSubContextId, p
|
|
|
9123
9657
|
}
|
|
9124
9658
|
//#endregion
|
|
9125
9659
|
//#region src/ifc-writer/railingWriter.ts
|
|
9126
|
-
function writeRailingGeometry(w, spec, geomSubContextId, parentPlacementId) {
|
|
9660
|
+
function writeRailingGeometry(w, spec, solid, geomSubContextId, parentPlacementId) {
|
|
9127
9661
|
const placement3DId = writeAxis2Placement3D(w, spec.origin.map(toIfcLengthM), spec.axisZ, spec.axisX);
|
|
9128
9662
|
const localPlacementId = w.nextId();
|
|
9129
9663
|
w.writeLine({
|
|
@@ -9132,6 +9666,15 @@ function writeRailingGeometry(w, spec, geomSubContextId, parentPlacementId) {
|
|
|
9132
9666
|
PlacementRelTo: parentPlacementId !== null ? w.ref(parentPlacementId) : null,
|
|
9133
9667
|
RelativePlacement: w.ref(placement3DId)
|
|
9134
9668
|
});
|
|
9669
|
+
if (spec.infill === "POSTED") {
|
|
9670
|
+
const tess = writeTessellation(w, solid, geomSubContextId, localPlacementId);
|
|
9671
|
+
return {
|
|
9672
|
+
localPlacementId,
|
|
9673
|
+
productDefinitionShapeId: tess.productDefinitionShapeId,
|
|
9674
|
+
bodyItemId: null,
|
|
9675
|
+
usedFallback: tess.usedFallback
|
|
9676
|
+
};
|
|
9677
|
+
}
|
|
9135
9678
|
const thicknessM = toIfcLengthM(spec.thickness);
|
|
9136
9679
|
const heightM = toIfcLengthM(spec.height);
|
|
9137
9680
|
const lengthM = toIfcLengthM(spec.length);
|
|
@@ -9208,7 +9751,8 @@ function writeRailingGeometry(w, spec, geomSubContextId, parentPlacementId) {
|
|
|
9208
9751
|
return {
|
|
9209
9752
|
localPlacementId,
|
|
9210
9753
|
productDefinitionShapeId,
|
|
9211
|
-
bodyItemId: extrusionId
|
|
9754
|
+
bodyItemId: extrusionId,
|
|
9755
|
+
usedFallback: false
|
|
9212
9756
|
};
|
|
9213
9757
|
}
|
|
9214
9758
|
function writeRailingEntity(w, guid, name, predefinedType, ownerHistoryId, localPlacementId, productDefinitionShapeId) {
|
|
@@ -11579,7 +12123,8 @@ async function toIfc(model, meta) {
|
|
|
11579
12123
|
for (const [i, roof] of roofs.entries()) {
|
|
11580
12124
|
const containingId = findContainerOf(roof.localId, relationships);
|
|
11581
12125
|
const storeyPlacementId = containingId !== null ? placementMap.get(containingId) ?? null : null;
|
|
11582
|
-
const { localPlacementId, productDefinitionShapeId } = writeRoofGeometry(w, roof.spec, geomSubContextId, storeyPlacementId);
|
|
12126
|
+
const { localPlacementId, productDefinitionShapeId, usedFallback } = writeRoofGeometry(w, roof.spec, roof.geometry, geomSubContextId, storeyPlacementId);
|
|
12127
|
+
if (usedFallback) console.warn(`Roof ${i + 1} tessellation failed; IFC body is a degenerate fallback.`);
|
|
11583
12128
|
const roofExpressId = writeRoofEntity(w, roof.guid, `Roof ${i + 1}`, roof.spec.predefinedType, ownerHistoryId, localPlacementId, productDefinitionShapeId);
|
|
11584
12129
|
idMap.set(roof.localId, roofExpressId);
|
|
11585
12130
|
placementMap.set(roof.localId, localPlacementId);
|
|
@@ -11651,14 +12196,15 @@ async function toIfc(model, meta) {
|
|
|
11651
12196
|
for (const [i, railing] of railings.entries()) {
|
|
11652
12197
|
const containingId = findContainerOf(railing.localId, relationships);
|
|
11653
12198
|
const storeyPlacementId = containingId !== null ? placementMap.get(containingId) ?? null : null;
|
|
11654
|
-
const { localPlacementId, productDefinitionShapeId, bodyItemId } = writeRailingGeometry(w, railing.spec, geomSubContextId, storeyPlacementId);
|
|
12199
|
+
const { localPlacementId, productDefinitionShapeId, bodyItemId, usedFallback } = writeRailingGeometry(w, railing.spec, railing.geometry, geomSubContextId, storeyPlacementId);
|
|
12200
|
+
if (usedFallback) console.warn(`Railing ${i + 1} tessellation failed; IFC body is a degenerate fallback.`);
|
|
11655
12201
|
const railingExpressId = writeRailingEntity(w, railing.guid, `Railing ${i + 1}`, railing.spec.predefinedType ?? "NOTDEFINED", ownerHistoryId, localPlacementId, productDefinitionShapeId);
|
|
11656
12202
|
idMap.set(railing.localId, railingExpressId);
|
|
11657
12203
|
placementMap.set(railing.localId, localPlacementId);
|
|
11658
12204
|
writeRailingCommonPset(w, ownerHistoryId, railingExpressId, railing.spec);
|
|
11659
12205
|
writeManufacturerPset(w, ownerHistoryId, railingExpressId, railing.spec);
|
|
11660
12206
|
if (railing.spec.customProperties !== void 0) writeCustomPsets(w, ownerHistoryId, railingExpressId, railing.spec.customProperties);
|
|
11661
|
-
applySurfaceStyle(w, model, railing.localId, bodyItemId);
|
|
12207
|
+
if (bodyItemId !== null) applySurfaceStyle(w, model, railing.localId, bodyItemId);
|
|
11662
12208
|
}
|
|
11663
12209
|
for (const [i, covering] of coverings.entries()) {
|
|
11664
12210
|
const containingId = findContainerOf(covering.localId, relationships);
|
|
@@ -12186,120 +12732,6 @@ var SpfReader = class SpfReader {
|
|
|
12186
12732
|
}
|
|
12187
12733
|
};
|
|
12188
12734
|
//#endregion
|
|
12189
|
-
//#region src/import/placement.ts
|
|
12190
|
-
/**
|
|
12191
|
-
* Metres-per-file-unit length scale read from the IfcUnitAssignment's
|
|
12192
|
-
* LENGTHUNIT. Multiply a file-unit length by this to get metres, then by 1000
|
|
12193
|
-
* for brepjs millimetres. Returns 1.0 (assume metres) when no length unit is
|
|
12194
|
-
* declared.
|
|
12195
|
-
*/
|
|
12196
|
-
function readLengthScale(reader) {
|
|
12197
|
-
const assignments = reader.getLinesOfType(WebIFC.IFCUNITASSIGNMENT);
|
|
12198
|
-
for (const assignmentId of assignments) {
|
|
12199
|
-
const units = asRefArray$1(reader.getLine(assignmentId)?.["Units"]);
|
|
12200
|
-
for (const unitId of units) {
|
|
12201
|
-
const scale = lengthScaleFromUnit(reader, unitId);
|
|
12202
|
-
if (scale !== null) return scale;
|
|
12203
|
-
}
|
|
12204
|
-
}
|
|
12205
|
-
return 1;
|
|
12206
|
-
}
|
|
12207
|
-
/**
|
|
12208
|
-
* Radians-per-file-unit plane-angle scale read from the IfcUnitAssignment's
|
|
12209
|
-
* PLANEANGLEUNIT (1 for RADIAN, ~0.0174533 for DEGREE). Defaults to 1 (the IFC
|
|
12210
|
-
* default plane-angle unit is the radian).
|
|
12211
|
-
*/
|
|
12212
|
-
function readPlaneAngleScale(reader) {
|
|
12213
|
-
for (const assignmentId of reader.getLinesOfType(WebIFC.IFCUNITASSIGNMENT)) {
|
|
12214
|
-
const units = asRefArray$1(reader.getLine(assignmentId)?.["Units"]);
|
|
12215
|
-
for (const unitId of units) {
|
|
12216
|
-
const s = planeAngleScaleFromUnit(reader, unitId);
|
|
12217
|
-
if (s !== null) return s;
|
|
12218
|
-
}
|
|
12219
|
-
}
|
|
12220
|
-
return 1;
|
|
12221
|
-
}
|
|
12222
|
-
function planeAngleScaleFromUnit(reader, unitId) {
|
|
12223
|
-
const unit = reader.getLine(unitId);
|
|
12224
|
-
if (unit === null) return null;
|
|
12225
|
-
const type = reader.getLineType(unitId);
|
|
12226
|
-
if (type === WebIFC.IFCSIUNIT) {
|
|
12227
|
-
if (enumValue(unit["UnitType"]) !== "PLANEANGLEUNIT") return null;
|
|
12228
|
-
if (enumValue(unit["Name"]) !== "RADIAN") return null;
|
|
12229
|
-
return 1;
|
|
12230
|
-
}
|
|
12231
|
-
if (type === WebIFC.IFCCONVERSIONBASEDUNIT) {
|
|
12232
|
-
if (enumValue(unit["UnitType"]) !== "PLANEANGLEUNIT") return null;
|
|
12233
|
-
const measureId = refValue$2(unit["ConversionFactor"]);
|
|
12234
|
-
if (measureId === null) return null;
|
|
12235
|
-
const factor = numericValue(reader.getLine(measureId)?.["ValueComponent"]);
|
|
12236
|
-
if (factor === null) return null;
|
|
12237
|
-
return factor;
|
|
12238
|
-
}
|
|
12239
|
-
return null;
|
|
12240
|
-
}
|
|
12241
|
-
function lengthScaleFromUnit(reader, unitId) {
|
|
12242
|
-
const unit = reader.getLine(unitId);
|
|
12243
|
-
if (unit === null) return null;
|
|
12244
|
-
const type = reader.getLineType(unitId);
|
|
12245
|
-
if (type === WebIFC.IFCSIUNIT) {
|
|
12246
|
-
if (enumValue(unit["UnitType"]) !== "LENGTHUNIT") return null;
|
|
12247
|
-
if (enumValue(unit["Name"]) !== "METRE") return null;
|
|
12248
|
-
return siPrefixFactor(enumValue(unit["Prefix"]));
|
|
12249
|
-
}
|
|
12250
|
-
if (type === WebIFC.IFCCONVERSIONBASEDUNIT) {
|
|
12251
|
-
if (enumValue(unit["UnitType"]) !== "LENGTHUNIT") return null;
|
|
12252
|
-
const measureId = refValue$2(unit["ConversionFactor"]);
|
|
12253
|
-
if (measureId === null) return null;
|
|
12254
|
-
const measure = reader.getLine(measureId);
|
|
12255
|
-
const factor = numericValue(measure?.["ValueComponent"]);
|
|
12256
|
-
if (factor === null) return null;
|
|
12257
|
-
const baseId = refValue$2(measure?.["UnitComponent"]);
|
|
12258
|
-
return factor * (baseId !== null ? lengthScaleFromUnit(reader, baseId) ?? 1 : 1);
|
|
12259
|
-
}
|
|
12260
|
-
return null;
|
|
12261
|
-
}
|
|
12262
|
-
function siPrefixFactor(prefix) {
|
|
12263
|
-
switch (prefix) {
|
|
12264
|
-
case null: return 1;
|
|
12265
|
-
case "KILO": return 1e3;
|
|
12266
|
-
case "HECTO": return 100;
|
|
12267
|
-
case "DECA": return 10;
|
|
12268
|
-
case "DECI": return .1;
|
|
12269
|
-
case "CENTI": return .01;
|
|
12270
|
-
case "MILLI": return .001;
|
|
12271
|
-
case "MICRO": return 1e-6;
|
|
12272
|
-
default: return 1;
|
|
12273
|
-
}
|
|
12274
|
-
}
|
|
12275
|
-
function refValue$2(v) {
|
|
12276
|
-
if (v === null || v === void 0) return null;
|
|
12277
|
-
if (typeof v === "number") return v;
|
|
12278
|
-
const value = v.value;
|
|
12279
|
-
return typeof value === "number" ? value : null;
|
|
12280
|
-
}
|
|
12281
|
-
function asRefArray$1(v) {
|
|
12282
|
-
if (!Array.isArray(v)) return [];
|
|
12283
|
-
const out = [];
|
|
12284
|
-
for (const item of v) {
|
|
12285
|
-
const id = refValue$2(item);
|
|
12286
|
-
if (id !== null) out.push(id);
|
|
12287
|
-
}
|
|
12288
|
-
return out;
|
|
12289
|
-
}
|
|
12290
|
-
function numericValue(v) {
|
|
12291
|
-
if (typeof v === "number") return v;
|
|
12292
|
-
if (v === null || v === void 0) return null;
|
|
12293
|
-
const value = v.value;
|
|
12294
|
-
return typeof value === "number" ? value : null;
|
|
12295
|
-
}
|
|
12296
|
-
function enumValue(v) {
|
|
12297
|
-
if (typeof v === "string") return v;
|
|
12298
|
-
if (v === null || v === void 0) return null;
|
|
12299
|
-
const value = v.value;
|
|
12300
|
-
return typeof value === "string" ? value : null;
|
|
12301
|
-
}
|
|
12302
|
-
//#endregion
|
|
12303
12735
|
//#region src/import/spatialTree.ts
|
|
12304
12736
|
var CATEGORY_BY_TYPE = new Map([
|
|
12305
12737
|
[WebIFC.IFCPROJECT, "PROJECT"],
|
|
@@ -13925,6 +14357,7 @@ var RoofSpecSchema = object({
|
|
|
13925
14357
|
fireRating: string().optional(),
|
|
13926
14358
|
thermalTransmittance: number().positive().optional(),
|
|
13927
14359
|
status: string().optional(),
|
|
14360
|
+
pitch: number().positive().max(89).optional(),
|
|
13928
14361
|
materialLayers: array(MaterialLayerSchema).optional(),
|
|
13929
14362
|
layerSetName: string().optional(),
|
|
13930
14363
|
classification: ClassificationRefSchema.optional(),
|
|
@@ -14256,6 +14689,7 @@ var RailingSpecSchema = object({
|
|
|
14256
14689
|
"HANDRAIL",
|
|
14257
14690
|
"NOTDEFINED"
|
|
14258
14691
|
]).optional(),
|
|
14692
|
+
infill: _enum(["PANEL", "POSTED"]).optional(),
|
|
14259
14693
|
materialName: string().min(1),
|
|
14260
14694
|
isExternal: boolean().optional(),
|
|
14261
14695
|
fireRating: string().optional(),
|
|
@@ -15522,24 +15956,24 @@ var XML_DECLARATION = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
|
|
|
15522
15956
|
function xmlDocument(rootXml) {
|
|
15523
15957
|
return `${XML_DECLARATION}\n${rootXml}\n`;
|
|
15524
15958
|
}
|
|
15525
|
-
function
|
|
15526
|
-
|
|
15527
|
-
|
|
15528
|
-
|
|
15529
|
-
|
|
15530
|
-
const key = m[1];
|
|
15531
|
-
const val = m[2];
|
|
15532
|
-
if (key !== void 0 && val !== void 0) attrs[key] = unescapeXml(val);
|
|
15533
|
-
}
|
|
15534
|
-
return attrs;
|
|
15959
|
+
function isNameChar(c) {
|
|
15960
|
+
return /[\w:.-]/.test(c);
|
|
15961
|
+
}
|
|
15962
|
+
function isWhitespace(c) {
|
|
15963
|
+
return /\s/.test(c);
|
|
15535
15964
|
}
|
|
15536
15965
|
/**
|
|
15537
|
-
* Parse an XML string into a tree. Tolerant of the XML declaration,
|
|
15538
|
-
* whitespace, self-closing tags, and CDATA-free text.
|
|
15539
|
-
*
|
|
15966
|
+
* Parse an XML string into a tree. Tolerant of the XML declaration, processing
|
|
15967
|
+
* instructions, comments, whitespace, self-closing tags, and CDATA-free text.
|
|
15968
|
+
* Throws on malformed or unbalanced structure; callers wrap this in a `Result`.
|
|
15969
|
+
*
|
|
15970
|
+
* This is a hand-written cursor scan rather than a single tokenizing regex: the
|
|
15971
|
+
* input is an untrusted `.bcfzip` payload, and a backtracking regex over
|
|
15972
|
+
* uncontrolled data is a polynomial-ReDoS vector. Every construct here is
|
|
15973
|
+
* consumed by an `indexOf` or a single-character advance, so the parse is linear
|
|
15974
|
+
* in the input length. The sibling `ids/idsXml.ts` parser scans the same way.
|
|
15540
15975
|
*/
|
|
15541
15976
|
function parseXml(xml) {
|
|
15542
|
-
const tokenRe = /<!--[\s\S]*?-->|<\?[\s\S]*?\?>|<\/([\w:.-]+)\s*>|<([\w:.-]+)((?:\s+[\w:.-]+\s*=\s*"[^"]*")*)\s*(\/?)>|([^<]+)/g;
|
|
15543
15977
|
const root = {
|
|
15544
15978
|
tag: "#root",
|
|
15545
15979
|
attrs: {},
|
|
@@ -15547,36 +15981,84 @@ function parseXml(xml) {
|
|
|
15547
15981
|
text: ""
|
|
15548
15982
|
};
|
|
15549
15983
|
const stack = [root];
|
|
15550
|
-
|
|
15551
|
-
|
|
15552
|
-
|
|
15553
|
-
|
|
15554
|
-
|
|
15555
|
-
|
|
15556
|
-
|
|
15557
|
-
|
|
15558
|
-
|
|
15559
|
-
|
|
15560
|
-
|
|
15561
|
-
|
|
15562
|
-
|
|
15563
|
-
|
|
15564
|
-
|
|
15565
|
-
|
|
15566
|
-
|
|
15567
|
-
const
|
|
15568
|
-
if (
|
|
15569
|
-
|
|
15570
|
-
if (
|
|
15571
|
-
|
|
15984
|
+
const len = xml.length;
|
|
15985
|
+
let i = 0;
|
|
15986
|
+
const fail = (msg) => {
|
|
15987
|
+
throw new Error(`Malformed XML: ${msg} at offset ${String(i)}`);
|
|
15988
|
+
};
|
|
15989
|
+
const skipWhitespace = () => {
|
|
15990
|
+
while (i < len && isWhitespace(xml.charAt(i))) i += 1;
|
|
15991
|
+
};
|
|
15992
|
+
const readName = () => {
|
|
15993
|
+
const start = i;
|
|
15994
|
+
while (i < len && isNameChar(xml.charAt(i))) i += 1;
|
|
15995
|
+
return xml.slice(start, i);
|
|
15996
|
+
};
|
|
15997
|
+
const readAttrs = () => {
|
|
15998
|
+
const attrs = {};
|
|
15999
|
+
for (;;) {
|
|
16000
|
+
skipWhitespace();
|
|
16001
|
+
const c = xml.charAt(i);
|
|
16002
|
+
if (i >= len || c === ">" || c === "/") return attrs;
|
|
16003
|
+
const name = readName();
|
|
16004
|
+
if (name.length === 0) fail("expected attribute name");
|
|
16005
|
+
skipWhitespace();
|
|
16006
|
+
if (xml.charAt(i) !== "=") fail(`expected '=' after attribute "${name}"`);
|
|
16007
|
+
i += 1;
|
|
16008
|
+
skipWhitespace();
|
|
16009
|
+
if (xml.charAt(i) !== "\"") fail(`expected '"' opening attribute "${name}"`);
|
|
16010
|
+
i += 1;
|
|
16011
|
+
const end = xml.indexOf("\"", i);
|
|
16012
|
+
if (end === -1) fail(`unterminated value for attribute "${name}"`);
|
|
16013
|
+
attrs[name] = unescapeXml(xml.slice(i, end));
|
|
16014
|
+
i = end + 1;
|
|
15572
16015
|
}
|
|
15573
|
-
|
|
15574
|
-
|
|
15575
|
-
|
|
15576
|
-
|
|
15577
|
-
|
|
15578
|
-
|
|
16016
|
+
};
|
|
16017
|
+
while (i < len) if (xml.startsWith("<!--", i)) {
|
|
16018
|
+
const end = xml.indexOf("-->", i + 4);
|
|
16019
|
+
if (end === -1) fail("unterminated comment");
|
|
16020
|
+
i = end + 3;
|
|
16021
|
+
} else if (xml.startsWith("<?", i)) {
|
|
16022
|
+
const end = xml.indexOf("?>", i + 2);
|
|
16023
|
+
if (end === -1) fail("unterminated processing instruction");
|
|
16024
|
+
i = end + 2;
|
|
16025
|
+
} else if (xml.startsWith("</", i)) {
|
|
16026
|
+
i += 2;
|
|
16027
|
+
const name = readName();
|
|
16028
|
+
skipWhitespace();
|
|
16029
|
+
if (xml.charAt(i) !== ">") fail(`expected '>' closing </${name}>`);
|
|
16030
|
+
i += 1;
|
|
16031
|
+
const top = stack[stack.length - 1];
|
|
16032
|
+
if (top === void 0 || top.tag !== name) throw new Error(`Unbalanced XML: unexpected </${name}>`);
|
|
16033
|
+
stack.pop();
|
|
16034
|
+
} else if (xml.charAt(i) === "<") {
|
|
16035
|
+
i += 1;
|
|
16036
|
+
const tag = readName();
|
|
16037
|
+
if (tag.length === 0) fail("expected element name");
|
|
16038
|
+
const node = {
|
|
16039
|
+
tag,
|
|
16040
|
+
attrs: readAttrs(),
|
|
16041
|
+
children: [],
|
|
16042
|
+
text: ""
|
|
16043
|
+
};
|
|
16044
|
+
const parent = stack[stack.length - 1];
|
|
16045
|
+
if (parent === void 0) throw new Error("Unbalanced XML: empty stack");
|
|
16046
|
+
parent.children.push(node);
|
|
16047
|
+
skipWhitespace();
|
|
16048
|
+
if (xml.startsWith("/>", i)) i += 2;
|
|
16049
|
+
else if (xml.charAt(i) === ">") {
|
|
16050
|
+
i += 1;
|
|
16051
|
+
stack.push(node);
|
|
16052
|
+
} else fail(`expected '>' in <${tag}>`);
|
|
16053
|
+
} else {
|
|
16054
|
+
const next = xml.indexOf("<", i);
|
|
16055
|
+
const end = next === -1 ? len : next;
|
|
16056
|
+
const decoded = unescapeXml(xml.slice(i, end));
|
|
16057
|
+
if (decoded.trim().length > 0) {
|
|
16058
|
+
const top = stack[stack.length - 1];
|
|
16059
|
+
if (top !== void 0) top.text += decoded;
|
|
15579
16060
|
}
|
|
16061
|
+
i = end;
|
|
15580
16062
|
}
|
|
15581
16063
|
if (stack.length !== 1) throw new Error("Unbalanced XML: unclosed elements remain");
|
|
15582
16064
|
const top = root.children[0];
|
|
@@ -15908,4 +16390,4 @@ function assignNum(target, key, value) {
|
|
|
15908
16390
|
if (value !== void 0) target[key] = Number(value);
|
|
15909
16391
|
}
|
|
15910
16392
|
//#endregion
|
|
15911
|
-
export { BimModel, DEFAULT_IFC_SCHEMA, DEFAULT_UNITS, IFC_SCHEMAS, PSET_PROPERTY_TYPE_TABLE, PSET_TEMPLATES, SpfReader, bcfError, checkGeometryValidity, checkModelAgainstIds as checkIds, checkModelAgainstIds, checkReferentialIntegrity, checkRoundTrip, checkSchema, countBySeverity, deriveCobieModel, deriveCobieModel as exportCobie, deriveIfcGuid, deriveIfcGuidSync, disposeImportedModel, emptyReport, extendedProfileArea, extendedProfileToFace, fileSchemaString, fromBrepError, fromIfc, geometryError, hasErrors, idsError, ifcError, importError, isExtendedProfile, isIfcSchema, isSlabOpening, isValidIfcGuid, isWallOpening, issue, makeLocalIdCounter, measureTypeFor, newIfcGuid, parseBcfFiles, parseBeamSpec, parseColumnSpec, parseCoveringSpec, parseCurtainWallSpec, parseDoorSpec, parseElementAssemblySpec, parseFootingSpec, parseIdsXml, parsePileSpec, parseProfile, parseRailingSpec, parseRampFlightSpec, parseRampSpec, parseRoofSpec, parseSlabOpeningInput, parseSlabSpec, parseSpaceSpec, parseStairFlightSpec, parseStairSpec, parseSurfaceStyleSpec, parseSystemSpec, parseWallSpec, parseWindowSpec, parseZoneSpec, schemaSupports, serializeBcfFiles, serializeCobieToCsv, serializeCobieToJson, setIfcWasmLocateFile, specError, templateFor, toIfc, toIfcLengthM, toIfcValidated, toLengthMm, writeClassificationRefs, writeElementAssemblyEntity, writeIfcType, writeMaterialLayerSet, writeMaterialProfileSet, writeMaterialSimple, writePresentationLayer, writeRelAggregatesElements, writeRelAssignsToGroup, writeRelConnectsElements, writeRelConnectsPathElements, writeRelNests, writeStyledItem, writeSurfaceStyle, writeSystemEntity, writeZoneEntity };
|
|
16393
|
+
export { BimModel, DEFAULT_IFC_SCHEMA, DEFAULT_UNITS, IFC_SCHEMAS, PSET_PROPERTY_TYPE_TABLE, PSET_TEMPLATES, SpfReader, bcfError, checkGeometryValidity, checkModelAgainstIds as checkIds, checkModelAgainstIds, checkReferentialIntegrity, checkRoundTrip, checkSchema, countBySeverity, deriveCobieModel, deriveCobieModel as exportCobie, deriveIfcGuid, deriveIfcGuidSync, disposeImportedModel, emptyReport, extendedProfileArea, extendedProfileToFace, fileSchemaString, fromBrepError, fromIfc, geometryError, hasErrors, idsError, ifcError, importError, isExtendedProfile, isIfcSchema, isSlabOpening, isValidIfcGuid, isWallOpening, issue, makeLocalIdCounter, measureTypeFor, newIfcGuid, parseBcfFiles, parseBeamSpec, parseColumnSpec, parseCoveringSpec, parseCurtainWallSpec, parseDoorSpec, parseElementAssemblySpec, parseFootingSpec, parseIdsXml, parsePileSpec, parseProfile, parseRailingSpec, parseRampFlightSpec, parseRampSpec, parseRoofSpec, parseSlabOpeningInput, parseSlabSpec, parseSpaceSpec, parseStairFlightSpec, parseStairSpec, parseSurfaceStyleSpec, parseSystemSpec, parseWallSpec, parseWindowSpec, parseZoneSpec, placedSolids, schemaSupports, serializeBcfFiles, serializeCobieToCsv, serializeCobieToJson, setIfcWasmLocateFile, specError, templateFor, toIfc, toIfcLengthM, toIfcValidated, toLengthMm, writeClassificationRefs, writeElementAssemblyEntity, writeIfcType, writeMaterialLayerSet, writeMaterialProfileSet, writeMaterialSimple, writePresentationLayer, writeRelAggregatesElements, writeRelAssignsToGroup, writeRelConnectsElements, writeRelConnectsPathElements, writeRelNests, writeStyledItem, writeSurfaceStyle, writeSystemEntity, writeZoneEntity };
|