brepjs-bim 0.1.0 → 0.3.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 +25 -2
- package/dist/brepjs-bim.cjs +984 -459
- package/dist/brepjs-bim.js +987 -464
- package/dist/elementFns/placedGeometry.d.ts +18 -0
- package/dist/identity/ifcGuid.d.ts +1 -0
- package/dist/ifc-writer/geometryWriter.d.ts +4 -1
- package/dist/ifc-writer/ifcWriter.d.ts +5 -1
- package/dist/ifc-writer/railingWriter.d.ts +9 -3
- package/dist/ifcRuntime.d.ts +20 -0
- package/dist/import/placement.d.ts +18 -0
- package/dist/index.d.ts +3 -0
- package/dist/model/bimModel.d.ts +8 -0
- package/dist/model/treeSummary.d.ts +21 -0
- package/dist/specs/profile.d.ts +2 -0
- package/dist/specs/railingSpec.d.ts +6 -0
- package/dist/specs/roofSpec.d.ts +7 -0
- package/package.json +27 -6
package/dist/brepjs-bim.cjs
CHANGED
|
@@ -24,8 +24,37 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
let brepjs = require("brepjs");
|
|
25
25
|
let web_ifc = require("web-ifc");
|
|
26
26
|
web_ifc = __toESM(web_ifc, 1);
|
|
27
|
+
//#region src/identity/ifcGuid.ts
|
|
28
|
+
var IFC_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_$";
|
|
29
|
+
function newIfcGuid() {
|
|
30
|
+
const bytes = crypto.getRandomValues(new Uint8Array(16));
|
|
31
|
+
bytes[6] = (bytes[6] ?? 0) & 15 | 64;
|
|
32
|
+
bytes[8] = (bytes[8] ?? 0) & 63 | 128;
|
|
33
|
+
return encodeIfcGuid(bytes);
|
|
34
|
+
}
|
|
35
|
+
function isValidIfcGuid(s) {
|
|
36
|
+
if (s.length !== 22) return false;
|
|
37
|
+
if (!"0123".includes(s[0] ?? "")) return false;
|
|
38
|
+
for (const ch of s) if (!IFC_CHARS.includes(ch)) return false;
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
function encodeIfcGuid(bytes) {
|
|
42
|
+
let result = "";
|
|
43
|
+
let acc = 0;
|
|
44
|
+
let bits = 4;
|
|
45
|
+
for (const byte of bytes) {
|
|
46
|
+
acc = acc << 8 | byte;
|
|
47
|
+
bits += 8;
|
|
48
|
+
while (bits >= 6) {
|
|
49
|
+
bits -= 6;
|
|
50
|
+
result += IFC_CHARS[acc >> bits & 63] ?? "";
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (bits > 0) result += IFC_CHARS[acc << 6 - bits & 63] ?? "";
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
//#endregion
|
|
27
57
|
//#region src/identity/guidDerivation.ts
|
|
28
|
-
var IFC_CHARS$1 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_$";
|
|
29
58
|
var NAMESPACE = "brepjs-bim:v1";
|
|
30
59
|
var FNV_OFFSET = 2166136261;
|
|
31
60
|
var FNV_PRIME = 16777619;
|
|
@@ -59,7 +88,7 @@ function digest16(stableKey) {
|
|
|
59
88
|
* keys yield distinct, format-valid (22-char) GlobalIds.
|
|
60
89
|
*/
|
|
61
90
|
function deriveIfcGuidSync(stableKey) {
|
|
62
|
-
return encodeIfcGuid
|
|
91
|
+
return encodeIfcGuid(digest16(stableKey));
|
|
63
92
|
}
|
|
64
93
|
/**
|
|
65
94
|
* Async wrapper over {@link deriveIfcGuidSync} for callers that prefer a Promise
|
|
@@ -86,21 +115,6 @@ function makeRelKey(modelScope, kind, localId) {
|
|
|
86
115
|
function makeLineKey(modelScope, expressId) {
|
|
87
116
|
return `line:${modelScope}:${expressId}`;
|
|
88
117
|
}
|
|
89
|
-
function encodeIfcGuid$1(bytes) {
|
|
90
|
-
let result = "";
|
|
91
|
-
let acc = 0;
|
|
92
|
-
let bits = 0;
|
|
93
|
-
for (const byte of bytes) {
|
|
94
|
-
acc = acc << 8 | byte;
|
|
95
|
-
bits += 8;
|
|
96
|
-
while (bits >= 6) {
|
|
97
|
-
bits -= 6;
|
|
98
|
-
result += IFC_CHARS$1[acc >> bits & 63] ?? "";
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
if (bits > 0) result += IFC_CHARS$1[acc << 6 - bits & 63] ?? "";
|
|
102
|
-
return result;
|
|
103
|
-
}
|
|
104
118
|
//#endregion
|
|
105
119
|
//#region src/identity/localId.ts
|
|
106
120
|
function makeLocalIdCounter(start = 1) {
|
|
@@ -251,7 +265,7 @@ function _usingCtx() {
|
|
|
251
265
|
//#region src/elementFns/wallFns.ts
|
|
252
266
|
function wallToSolid(spec) {
|
|
253
267
|
try {
|
|
254
|
-
var _usingCtx$
|
|
268
|
+
var _usingCtx$19 = _usingCtx();
|
|
255
269
|
if (spec.length <= 0) return (0, brepjs.err)(specError("WALL_ZERO_LENGTH", "Wall length must be positive"));
|
|
256
270
|
if (spec.height <= 0) return (0, brepjs.err)(specError("WALL_ZERO_HEIGHT", "Wall height must be positive"));
|
|
257
271
|
if (spec.thickness <= 0) return (0, brepjs.err)(specError("WALL_ZERO_THICKNESS", "Wall thickness must be positive"));
|
|
@@ -279,7 +293,7 @@ function wallToSolid(spec) {
|
|
|
279
293
|
]
|
|
280
294
|
]);
|
|
281
295
|
if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "WALL_PROFILE_FAILED", "Failed to create wall profile"));
|
|
282
|
-
const solidResult = (0, brepjs.extrude)(_usingCtx$
|
|
296
|
+
const solidResult = (0, brepjs.extrude)(_usingCtx$19.u(profileResult.value), [
|
|
283
297
|
length,
|
|
284
298
|
0,
|
|
285
299
|
0
|
|
@@ -292,16 +306,16 @@ function wallToSolid(spec) {
|
|
|
292
306
|
}
|
|
293
307
|
return (0, brepjs.ok)(solid);
|
|
294
308
|
} catch (_) {
|
|
295
|
-
_usingCtx$
|
|
309
|
+
_usingCtx$19.e = _;
|
|
296
310
|
} finally {
|
|
297
|
-
_usingCtx$
|
|
311
|
+
_usingCtx$19.d();
|
|
298
312
|
}
|
|
299
313
|
}
|
|
300
314
|
//#endregion
|
|
301
315
|
//#region src/elementFns/slabFns.ts
|
|
302
316
|
function slabToSolid(spec) {
|
|
303
317
|
try {
|
|
304
|
-
var _usingCtx$
|
|
318
|
+
var _usingCtx$18 = _usingCtx();
|
|
305
319
|
if (spec.length <= 0) return (0, brepjs.err)(specError("SLAB_ZERO_LENGTH", "Slab length must be positive"));
|
|
306
320
|
if (spec.width <= 0) return (0, brepjs.err)(specError("SLAB_ZERO_WIDTH", "Slab width must be positive"));
|
|
307
321
|
if (spec.thickness <= 0) return (0, brepjs.err)(specError("SLAB_ZERO_THICKNESS", "Slab thickness must be positive"));
|
|
@@ -329,7 +343,7 @@ function slabToSolid(spec) {
|
|
|
329
343
|
]
|
|
330
344
|
]);
|
|
331
345
|
if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "SLAB_PROFILE_FAILED", "Failed to create slab profile"));
|
|
332
|
-
const solidResult = (0, brepjs.extrude)(_usingCtx$
|
|
346
|
+
const solidResult = (0, brepjs.extrude)(_usingCtx$18.u(profileResult.value), [
|
|
333
347
|
0,
|
|
334
348
|
0,
|
|
335
349
|
thickness
|
|
@@ -342,9 +356,9 @@ function slabToSolid(spec) {
|
|
|
342
356
|
}
|
|
343
357
|
return (0, brepjs.ok)(solid);
|
|
344
358
|
} catch (_) {
|
|
345
|
-
_usingCtx$
|
|
359
|
+
_usingCtx$18.e = _;
|
|
346
360
|
} finally {
|
|
347
|
-
_usingCtx$
|
|
361
|
+
_usingCtx$18.d();
|
|
348
362
|
}
|
|
349
363
|
}
|
|
350
364
|
//#endregion
|
|
@@ -5021,14 +5035,14 @@ function to3D(points) {
|
|
|
5021
5035
|
}
|
|
5022
5036
|
function extendedProfileToFace(profile) {
|
|
5023
5037
|
try {
|
|
5024
|
-
var _usingCtx$
|
|
5038
|
+
var _usingCtx$17 = _usingCtx();
|
|
5025
5039
|
const invalid = validateProfile(profile);
|
|
5026
5040
|
if (invalid !== null) return (0, brepjs.err)(invalid);
|
|
5027
5041
|
const outerResult = (0, brepjs.polygon)(to3D(outerLoop(profile)));
|
|
5028
5042
|
if (!outerResult.ok) return (0, brepjs.err)(fromBrepError(outerResult.error, "PROFILE_FACE_FAILED", "Failed to build profile outer face"));
|
|
5029
5043
|
const holes = holeLoops(profile);
|
|
5030
5044
|
if (holes.length === 0) return (0, brepjs.ok)(outerResult.value);
|
|
5031
|
-
const outerFace = _usingCtx$
|
|
5045
|
+
const outerFace = _usingCtx$17.u(outerResult.value);
|
|
5032
5046
|
const holeFaces = [];
|
|
5033
5047
|
const holeWires = [];
|
|
5034
5048
|
const disposeHoleFaces = () => {
|
|
@@ -5053,9 +5067,9 @@ function extendedProfileToFace(profile) {
|
|
|
5053
5067
|
disposeHoleFaces();
|
|
5054
5068
|
return (0, brepjs.ok)(faceWithHoles);
|
|
5055
5069
|
} catch (_) {
|
|
5056
|
-
_usingCtx$
|
|
5070
|
+
_usingCtx$17.e = _;
|
|
5057
5071
|
} finally {
|
|
5058
|
-
_usingCtx$
|
|
5072
|
+
_usingCtx$17.d();
|
|
5059
5073
|
}
|
|
5060
5074
|
}
|
|
5061
5075
|
//#endregion
|
|
@@ -5093,7 +5107,8 @@ var CoreProfileSchema = discriminatedUnion("kind", [
|
|
|
5093
5107
|
overallWidth: number().positive(),
|
|
5094
5108
|
overallDepth: number().positive(),
|
|
5095
5109
|
flangeThickness: number().positive(),
|
|
5096
|
-
webThickness: number().positive()
|
|
5110
|
+
webThickness: number().positive(),
|
|
5111
|
+
filletRadius: number().positive().optional()
|
|
5097
5112
|
})
|
|
5098
5113
|
]);
|
|
5099
5114
|
var Pt2Schema = tuple([number(), number()]);
|
|
@@ -5225,6 +5240,11 @@ function parseProfile(input) {
|
|
|
5225
5240
|
if (profile.kind === "I_BEAM") {
|
|
5226
5241
|
if (2 * profile.flangeThickness >= profile.overallDepth) return (0, brepjs.err)(specError("INVALID_PROFILE", "I-beam flangeThickness × 2 must be less than overallDepth"));
|
|
5227
5242
|
if (profile.webThickness >= profile.overallWidth) return (0, brepjs.err)(specError("INVALID_PROFILE", "I-beam webThickness must be less than overallWidth"));
|
|
5243
|
+
if (profile.filletRadius !== void 0) {
|
|
5244
|
+
const clearHeight = profile.overallDepth / 2 - profile.flangeThickness;
|
|
5245
|
+
const clearSpan = (profile.overallWidth - profile.webThickness) / 2;
|
|
5246
|
+
if (profile.filletRadius >= clearHeight || profile.filletRadius >= clearSpan) return (0, brepjs.err)(specError("INVALID_PROFILE", "I-beam filletRadius must be smaller than the clear web height and the clear span beside the web"));
|
|
5247
|
+
}
|
|
5228
5248
|
}
|
|
5229
5249
|
return (0, brepjs.ok)(profile);
|
|
5230
5250
|
}
|
|
@@ -5235,8 +5255,42 @@ function profileCrossSectionArea(profile) {
|
|
|
5235
5255
|
switch (profile.kind) {
|
|
5236
5256
|
case "RECTANGULAR": return profile.width * profile.height;
|
|
5237
5257
|
case "CIRCULAR": return Math.PI * profile.radius * profile.radius;
|
|
5238
|
-
case "I_BEAM":
|
|
5258
|
+
case "I_BEAM": {
|
|
5259
|
+
const flangeArea = 2 * profile.overallWidth * profile.flangeThickness;
|
|
5260
|
+
const webArea = (profile.overallDepth - 2 * profile.flangeThickness) * profile.webThickness;
|
|
5261
|
+
const r = profile.filletRadius ?? 0;
|
|
5262
|
+
const filletArea = 4 * r * r * (1 - Math.PI / 4);
|
|
5263
|
+
return flangeArea + webArea + filletArea;
|
|
5264
|
+
}
|
|
5265
|
+
}
|
|
5266
|
+
}
|
|
5267
|
+
var FILLET_SEGMENTS = 8;
|
|
5268
|
+
var FILLET_MIN_ANGLE = .001;
|
|
5269
|
+
function filletArc(prev, v, next, r) {
|
|
5270
|
+
const aLen = Math.hypot(prev[0] - v[0], prev[1] - v[1]);
|
|
5271
|
+
const bLen = Math.hypot(next[0] - v[0], next[1] - v[1]);
|
|
5272
|
+
const a = [(prev[0] - v[0]) / aLen, (prev[1] - v[1]) / aLen];
|
|
5273
|
+
const b = [(next[0] - v[0]) / bLen, (next[1] - v[1]) / bLen];
|
|
5274
|
+
const alpha = Math.acos(Math.max(-1, Math.min(1, a[0] * b[0] + a[1] * b[1])));
|
|
5275
|
+
if (alpha < FILLET_MIN_ANGLE || alpha > Math.PI - FILLET_MIN_ANGLE) return [[v[0], v[1]]];
|
|
5276
|
+
const setback = r / Math.tan(alpha / 2);
|
|
5277
|
+
const center = r / Math.sin(alpha / 2);
|
|
5278
|
+
const bisLen = Math.hypot(a[0] + b[0], a[1] + b[1]);
|
|
5279
|
+
const bis = [(a[0] + b[0]) / bisLen, (a[1] + b[1]) / bisLen];
|
|
5280
|
+
const cx = v[0] + bis[0] * center;
|
|
5281
|
+
const cy = v[1] + bis[1] * center;
|
|
5282
|
+
const p1 = [v[0] + a[0] * setback, v[1] + a[1] * setback];
|
|
5283
|
+
const p2 = [v[0] + b[0] * setback, v[1] + b[1] * setback];
|
|
5284
|
+
const a1 = Math.atan2(p1[1] - cy, p1[0] - cx);
|
|
5285
|
+
let delta = Math.atan2(p2[1] - cy, p2[0] - cx) - a1;
|
|
5286
|
+
while (delta <= -Math.PI) delta += 2 * Math.PI;
|
|
5287
|
+
while (delta > Math.PI) delta -= 2 * Math.PI;
|
|
5288
|
+
const out = [];
|
|
5289
|
+
for (let i = 0; i <= FILLET_SEGMENTS; i++) {
|
|
5290
|
+
const ang = a1 + delta * i / FILLET_SEGMENTS;
|
|
5291
|
+
out.push([cx + r * Math.cos(ang), cy + r * Math.sin(ang)]);
|
|
5239
5292
|
}
|
|
5293
|
+
return out;
|
|
5240
5294
|
}
|
|
5241
5295
|
function profileToPolygon(profile, circleSegments = 32) {
|
|
5242
5296
|
if (isExtendedProfile(profile)) return (0, brepjs.err)(specError("EXTENDED_PROFILE_NO_POLYGON", `profileToPolygon: extended profile kind '${profile.kind}' has no single-polygon outline; use extendedProfileToFace()`));
|
|
@@ -5285,68 +5339,50 @@ function profileToPolygon(profile, circleSegments = 32) {
|
|
|
5285
5339
|
const halfD = profile.overallDepth / 2;
|
|
5286
5340
|
const halfWeb = profile.webThickness / 2;
|
|
5287
5341
|
const flangeInnerY = halfD - profile.flangeThickness;
|
|
5288
|
-
|
|
5289
|
-
[
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
],
|
|
5294
|
-
[
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
],
|
|
5299
|
-
[
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
],
|
|
5309
|
-
[
|
|
5310
|
-
halfWeb,
|
|
5311
|
-
flangeInnerY,
|
|
5312
|
-
0
|
|
5313
|
-
],
|
|
5314
|
-
[
|
|
5315
|
-
halfW,
|
|
5316
|
-
flangeInnerY,
|
|
5317
|
-
0
|
|
5318
|
-
],
|
|
5319
|
-
[
|
|
5320
|
-
halfW,
|
|
5321
|
-
halfD,
|
|
5322
|
-
0
|
|
5323
|
-
],
|
|
5324
|
-
[
|
|
5325
|
-
-halfW,
|
|
5326
|
-
halfD,
|
|
5327
|
-
0
|
|
5328
|
-
],
|
|
5329
|
-
[
|
|
5330
|
-
-halfW,
|
|
5331
|
-
flangeInnerY,
|
|
5332
|
-
0
|
|
5333
|
-
],
|
|
5334
|
-
[
|
|
5335
|
-
-halfWeb,
|
|
5336
|
-
flangeInnerY,
|
|
5337
|
-
0
|
|
5338
|
-
],
|
|
5339
|
-
[
|
|
5340
|
-
-halfWeb,
|
|
5341
|
-
-flangeInnerY,
|
|
5342
|
-
0
|
|
5343
|
-
],
|
|
5344
|
-
[
|
|
5345
|
-
-halfW,
|
|
5346
|
-
-flangeInnerY,
|
|
5347
|
-
0
|
|
5348
|
-
]
|
|
5342
|
+
const v = [
|
|
5343
|
+
[-halfW, -halfD],
|
|
5344
|
+
[halfW, -halfD],
|
|
5345
|
+
[halfW, -flangeInnerY],
|
|
5346
|
+
[halfWeb, -flangeInnerY],
|
|
5347
|
+
[halfWeb, flangeInnerY],
|
|
5348
|
+
[halfW, flangeInnerY],
|
|
5349
|
+
[halfW, halfD],
|
|
5350
|
+
[-halfW, halfD],
|
|
5351
|
+
[-halfW, flangeInnerY],
|
|
5352
|
+
[-halfWeb, flangeInnerY],
|
|
5353
|
+
[-halfWeb, -flangeInnerY],
|
|
5354
|
+
[-halfW, -flangeInnerY]
|
|
5355
|
+
];
|
|
5356
|
+
const r = profile.filletRadius ?? 0;
|
|
5357
|
+
const rootCorners = new Set([
|
|
5358
|
+
3,
|
|
5359
|
+
4,
|
|
5360
|
+
9,
|
|
5361
|
+
10
|
|
5349
5362
|
]);
|
|
5363
|
+
const pts = [];
|
|
5364
|
+
for (let i = 0; i < v.length; i++) {
|
|
5365
|
+
const cur = v[i];
|
|
5366
|
+
if (cur === void 0) continue;
|
|
5367
|
+
if (r > 0 && rootCorners.has(i)) {
|
|
5368
|
+
const prev = v[(i - 1 + v.length) % v.length];
|
|
5369
|
+
const next = v[(i + 1) % v.length];
|
|
5370
|
+
if (prev !== void 0 && next !== void 0) {
|
|
5371
|
+
for (const [ax, ay] of filletArc(prev, cur, next, r)) pts.push([
|
|
5372
|
+
ax,
|
|
5373
|
+
ay,
|
|
5374
|
+
0
|
|
5375
|
+
]);
|
|
5376
|
+
continue;
|
|
5377
|
+
}
|
|
5378
|
+
}
|
|
5379
|
+
pts.push([
|
|
5380
|
+
cur[0],
|
|
5381
|
+
cur[1],
|
|
5382
|
+
0
|
|
5383
|
+
]);
|
|
5384
|
+
}
|
|
5385
|
+
return (0, brepjs.ok)(pts);
|
|
5350
5386
|
}
|
|
5351
5387
|
}
|
|
5352
5388
|
}
|
|
@@ -5354,7 +5390,7 @@ function profileToPolygon(profile, circleSegments = 32) {
|
|
|
5354
5390
|
//#region src/elementFns/beamFns.ts
|
|
5355
5391
|
function beamToSolid(spec) {
|
|
5356
5392
|
try {
|
|
5357
|
-
var _usingCtx$
|
|
5393
|
+
var _usingCtx$16 = _usingCtx();
|
|
5358
5394
|
if (spec.length <= 0) return (0, brepjs.err)(specError("BEAM_ZERO_LENGTH", "Beam length must be positive"));
|
|
5359
5395
|
if (isExtendedProfile(spec.profile)) try {
|
|
5360
5396
|
var _usingCtx3 = _usingCtx();
|
|
@@ -5389,7 +5425,7 @@ function beamToSolid(spec) {
|
|
|
5389
5425
|
py
|
|
5390
5426
|
]));
|
|
5391
5427
|
if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "BEAM_PROFILE_FAILED", "Failed to create beam profile"));
|
|
5392
|
-
const solidResult = (0, brepjs.extrude)(_usingCtx$
|
|
5428
|
+
const solidResult = (0, brepjs.extrude)(_usingCtx$16.u(profileResult.value), [
|
|
5393
5429
|
spec.length,
|
|
5394
5430
|
0,
|
|
5395
5431
|
0
|
|
@@ -5402,16 +5438,16 @@ function beamToSolid(spec) {
|
|
|
5402
5438
|
}
|
|
5403
5439
|
return (0, brepjs.ok)(solid);
|
|
5404
5440
|
} catch (_) {
|
|
5405
|
-
_usingCtx$
|
|
5441
|
+
_usingCtx$16.e = _;
|
|
5406
5442
|
} finally {
|
|
5407
|
-
_usingCtx$
|
|
5443
|
+
_usingCtx$16.d();
|
|
5408
5444
|
}
|
|
5409
5445
|
}
|
|
5410
5446
|
//#endregion
|
|
5411
5447
|
//#region src/elementFns/columnFns.ts
|
|
5412
5448
|
function columnToSolid(spec) {
|
|
5413
5449
|
try {
|
|
5414
|
-
var _usingCtx$
|
|
5450
|
+
var _usingCtx$15 = _usingCtx();
|
|
5415
5451
|
if (spec.height <= 0) return (0, brepjs.err)(specError("COLUMN_ZERO_HEIGHT", "Column height must be positive"));
|
|
5416
5452
|
if (isExtendedProfile(spec.profile)) try {
|
|
5417
5453
|
var _usingCtx3 = _usingCtx();
|
|
@@ -5439,7 +5475,7 @@ function columnToSolid(spec) {
|
|
|
5439
5475
|
const profilePts = profilePtsResult.value;
|
|
5440
5476
|
const profileResult = (0, brepjs.polygon)(profilePts);
|
|
5441
5477
|
if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "COLUMN_PROFILE_FAILED", "Failed to create column profile"));
|
|
5442
|
-
const solidResult = (0, brepjs.extrude)(_usingCtx$
|
|
5478
|
+
const solidResult = (0, brepjs.extrude)(_usingCtx$15.u(profileResult.value), [
|
|
5443
5479
|
0,
|
|
5444
5480
|
0,
|
|
5445
5481
|
spec.height
|
|
@@ -5452,9 +5488,9 @@ function columnToSolid(spec) {
|
|
|
5452
5488
|
}
|
|
5453
5489
|
return (0, brepjs.ok)(solid);
|
|
5454
5490
|
} catch (_) {
|
|
5455
|
-
_usingCtx$
|
|
5491
|
+
_usingCtx$15.e = _;
|
|
5456
5492
|
} finally {
|
|
5457
|
-
_usingCtx$
|
|
5493
|
+
_usingCtx$15.d();
|
|
5458
5494
|
}
|
|
5459
5495
|
}
|
|
5460
5496
|
//#endregion
|
|
@@ -5462,7 +5498,7 @@ function columnToSolid(spec) {
|
|
|
5462
5498
|
var EPSILON_MM$1 = 1;
|
|
5463
5499
|
function openingToSolid(spec, wallThickness) {
|
|
5464
5500
|
try {
|
|
5465
|
-
var _usingCtx$
|
|
5501
|
+
var _usingCtx$14 = _usingCtx();
|
|
5466
5502
|
if (spec.width <= 0) return (0, brepjs.err)(specError("OPENING_ZERO_WIDTH", "Opening width must be positive"));
|
|
5467
5503
|
if (spec.height <= 0) return (0, brepjs.err)(specError("OPENING_ZERO_HEIGHT", "Opening height must be positive"));
|
|
5468
5504
|
if (wallThickness <= 0) return (0, brepjs.err)(specError("OPENING_ZERO_WALL_THICKNESS", "Wall thickness must be positive"));
|
|
@@ -5494,7 +5530,7 @@ function openingToSolid(spec, wallThickness) {
|
|
|
5494
5530
|
]
|
|
5495
5531
|
]);
|
|
5496
5532
|
if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "OPENING_PROFILE_FAILED", "Failed to create opening profile"));
|
|
5497
|
-
const solidResult = (0, brepjs.extrude)(_usingCtx$
|
|
5533
|
+
const solidResult = (0, brepjs.extrude)(_usingCtx$14.u(profileResult.value), [
|
|
5498
5534
|
spec.width,
|
|
5499
5535
|
0,
|
|
5500
5536
|
0
|
|
@@ -5507,9 +5543,9 @@ function openingToSolid(spec, wallThickness) {
|
|
|
5507
5543
|
}
|
|
5508
5544
|
return (0, brepjs.ok)(solid);
|
|
5509
5545
|
} catch (_) {
|
|
5510
|
-
_usingCtx$
|
|
5546
|
+
_usingCtx$14.e = _;
|
|
5511
5547
|
} finally {
|
|
5512
|
-
_usingCtx$
|
|
5548
|
+
_usingCtx$14.d();
|
|
5513
5549
|
}
|
|
5514
5550
|
}
|
|
5515
5551
|
//#endregion
|
|
@@ -5517,7 +5553,7 @@ function openingToSolid(spec, wallThickness) {
|
|
|
5517
5553
|
var EPSILON_MM = 1;
|
|
5518
5554
|
function slabOpeningToSolid(spec, slabThickness) {
|
|
5519
5555
|
try {
|
|
5520
|
-
var _usingCtx$
|
|
5556
|
+
var _usingCtx$13 = _usingCtx();
|
|
5521
5557
|
if (spec.sizeX <= 0) return (0, brepjs.err)(specError("SLAB_OPENING_ZERO_SIZE_X", "Slab opening sizeX must be positive"));
|
|
5522
5558
|
if (spec.sizeY <= 0) return (0, brepjs.err)(specError("SLAB_OPENING_ZERO_SIZE_Y", "Slab opening sizeY must be positive"));
|
|
5523
5559
|
if (slabThickness <= 0) return (0, brepjs.err)(specError("SLAB_OPENING_ZERO_SLAB_THICKNESS", "Slab thickness must be positive"));
|
|
@@ -5549,7 +5585,7 @@ function slabOpeningToSolid(spec, slabThickness) {
|
|
|
5549
5585
|
]
|
|
5550
5586
|
]);
|
|
5551
5587
|
if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "SLAB_OPENING_PROFILE_FAILED", "Failed to create slab opening profile"));
|
|
5552
|
-
const solidResult = (0, brepjs.extrude)(_usingCtx$
|
|
5588
|
+
const solidResult = (0, brepjs.extrude)(_usingCtx$13.u(profileResult.value), [
|
|
5553
5589
|
0,
|
|
5554
5590
|
0,
|
|
5555
5591
|
slabThickness + 2 * EPSILON_MM
|
|
@@ -5562,16 +5598,16 @@ function slabOpeningToSolid(spec, slabThickness) {
|
|
|
5562
5598
|
}
|
|
5563
5599
|
return (0, brepjs.ok)(solid);
|
|
5564
5600
|
} catch (_) {
|
|
5565
|
-
_usingCtx$
|
|
5601
|
+
_usingCtx$13.e = _;
|
|
5566
5602
|
} finally {
|
|
5567
|
-
_usingCtx$
|
|
5603
|
+
_usingCtx$13.d();
|
|
5568
5604
|
}
|
|
5569
5605
|
}
|
|
5570
5606
|
//#endregion
|
|
5571
5607
|
//#region src/elementFns/spaceFns.ts
|
|
5572
5608
|
function spaceToSolid(spec) {
|
|
5573
5609
|
try {
|
|
5574
|
-
var _usingCtx$
|
|
5610
|
+
var _usingCtx$12 = _usingCtx();
|
|
5575
5611
|
if (spec.length <= 0) return (0, brepjs.err)(specError("SPACE_ZERO_LENGTH", "Space length must be positive"));
|
|
5576
5612
|
if (spec.width <= 0) return (0, brepjs.err)(specError("SPACE_ZERO_WIDTH", "Space width must be positive"));
|
|
5577
5613
|
if (spec.height <= 0) return (0, brepjs.err)(specError("SPACE_ZERO_HEIGHT", "Space height must be positive"));
|
|
@@ -5599,7 +5635,7 @@ function spaceToSolid(spec) {
|
|
|
5599
5635
|
]
|
|
5600
5636
|
]);
|
|
5601
5637
|
if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "SPACE_PROFILE_FAILED", "Failed to create space footprint"));
|
|
5602
|
-
const solidResult = (0, brepjs.extrude)(_usingCtx$
|
|
5638
|
+
const solidResult = (0, brepjs.extrude)(_usingCtx$12.u(profileResult.value), [
|
|
5603
5639
|
0,
|
|
5604
5640
|
0,
|
|
5605
5641
|
height
|
|
@@ -5612,21 +5648,26 @@ function spaceToSolid(spec) {
|
|
|
5612
5648
|
}
|
|
5613
5649
|
return (0, brepjs.ok)(solid);
|
|
5614
5650
|
} catch (_) {
|
|
5615
|
-
_usingCtx$
|
|
5651
|
+
_usingCtx$12.e = _;
|
|
5616
5652
|
} finally {
|
|
5617
|
-
_usingCtx$
|
|
5653
|
+
_usingCtx$12.d();
|
|
5618
5654
|
}
|
|
5619
5655
|
}
|
|
5620
5656
|
//#endregion
|
|
5621
5657
|
//#region src/elementFns/roofFns.ts
|
|
5622
|
-
|
|
5658
|
+
var DEG2RAD = Math.PI / 180;
|
|
5659
|
+
function gate(solid, code) {
|
|
5660
|
+
if (!(0, brepjs.isValidSolid)(solid)) {
|
|
5661
|
+
solid[Symbol.dispose]();
|
|
5662
|
+
return (0, brepjs.err)(geometryError(code, "Roof solid failed validity check"));
|
|
5663
|
+
}
|
|
5664
|
+
return (0, brepjs.ok)(solid);
|
|
5665
|
+
}
|
|
5666
|
+
function flatRoof(spec) {
|
|
5623
5667
|
try {
|
|
5624
|
-
var _usingCtx$
|
|
5625
|
-
if (spec.length <= 0) return (0, brepjs.err)(specError("ROOF_ZERO_LENGTH", "Roof length must be positive"));
|
|
5626
|
-
if (spec.width <= 0) return (0, brepjs.err)(specError("ROOF_ZERO_WIDTH", "Roof width must be positive"));
|
|
5627
|
-
if (spec.thickness <= 0) return (0, brepjs.err)(specError("ROOF_ZERO_THICKNESS", "Roof thickness must be positive"));
|
|
5668
|
+
var _usingCtx$11 = _usingCtx();
|
|
5628
5669
|
const { length, width, thickness } = spec;
|
|
5629
|
-
const
|
|
5670
|
+
const face = (0, brepjs.polygon)([
|
|
5630
5671
|
[
|
|
5631
5672
|
0,
|
|
5632
5673
|
0,
|
|
@@ -5648,112 +5689,294 @@ function roofToSolid(spec) {
|
|
|
5648
5689
|
0
|
|
5649
5690
|
]
|
|
5650
5691
|
]);
|
|
5651
|
-
if (!
|
|
5652
|
-
const
|
|
5692
|
+
if (!face.ok) return (0, brepjs.err)(fromBrepError(face.error, "ROOF_PROFILE_FAILED", "Failed to create roof profile"));
|
|
5693
|
+
const solid = (0, brepjs.extrude)(_usingCtx$11.u(face.value), [
|
|
5653
5694
|
0,
|
|
5654
5695
|
0,
|
|
5655
5696
|
thickness
|
|
5656
5697
|
]);
|
|
5657
|
-
if (!
|
|
5658
|
-
|
|
5659
|
-
if (!(0, brepjs.isValidSolid)(solid)) {
|
|
5660
|
-
solid[Symbol.dispose]();
|
|
5661
|
-
return (0, brepjs.err)(geometryError("ROOF_INVALID_SOLID", "Extruded roof solid failed validity check"));
|
|
5662
|
-
}
|
|
5663
|
-
return (0, brepjs.ok)(solid);
|
|
5698
|
+
if (!solid.ok) return (0, brepjs.err)(fromBrepError(solid.error, "ROOF_EXTRUDE_FAILED", "Failed to extrude roof profile"));
|
|
5699
|
+
return gate(solid.value, "ROOF_INVALID_SOLID");
|
|
5664
5700
|
} catch (_) {
|
|
5665
|
-
_usingCtx$
|
|
5701
|
+
_usingCtx$11.e = _;
|
|
5666
5702
|
} finally {
|
|
5667
|
-
_usingCtx$
|
|
5703
|
+
_usingCtx$11.d();
|
|
5668
5704
|
}
|
|
5669
5705
|
}
|
|
5670
|
-
|
|
5671
|
-
//#region src/elementFns/curtainWallFns.ts
|
|
5672
|
-
function boxSolid(sizeX, sizeY, sizeZ) {
|
|
5706
|
+
function shedRoof(spec, pitch) {
|
|
5673
5707
|
try {
|
|
5674
|
-
var
|
|
5675
|
-
const
|
|
5708
|
+
var _usingCtx3 = _usingCtx();
|
|
5709
|
+
const { length, width, thickness } = spec;
|
|
5710
|
+
const rise = width * Math.tan(pitch * DEG2RAD);
|
|
5711
|
+
const face = (0, brepjs.polygon)([
|
|
5676
5712
|
[
|
|
5677
5713
|
0,
|
|
5678
5714
|
0,
|
|
5679
5715
|
0
|
|
5680
5716
|
],
|
|
5681
5717
|
[
|
|
5682
|
-
sizeX,
|
|
5683
5718
|
0,
|
|
5719
|
+
width,
|
|
5684
5720
|
0
|
|
5685
5721
|
],
|
|
5686
5722
|
[
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
|
|
5723
|
+
0,
|
|
5724
|
+
width,
|
|
5725
|
+
thickness + rise
|
|
5690
5726
|
],
|
|
5691
5727
|
[
|
|
5692
5728
|
0,
|
|
5693
|
-
|
|
5694
|
-
|
|
5729
|
+
0,
|
|
5730
|
+
thickness
|
|
5695
5731
|
]
|
|
5696
5732
|
]);
|
|
5697
|
-
if (!
|
|
5698
|
-
const
|
|
5699
|
-
|
|
5733
|
+
if (!face.ok) return (0, brepjs.err)(fromBrepError(face.error, "ROOF_PROFILE_FAILED", "Failed to create shed profile"));
|
|
5734
|
+
const solid = (0, brepjs.extrude)(_usingCtx3.u(face.value), [
|
|
5735
|
+
length,
|
|
5700
5736
|
0,
|
|
5701
|
-
|
|
5737
|
+
0
|
|
5702
5738
|
]);
|
|
5703
|
-
if (!
|
|
5704
|
-
|
|
5705
|
-
if (!(0, brepjs.isValidSolid)(solid)) {
|
|
5706
|
-
solid[Symbol.dispose]();
|
|
5707
|
-
return (0, brepjs.err)(geometryError("CURTAIN_WALL_INVALID_SOLID", "Extruded curtain wall component failed validity check"));
|
|
5708
|
-
}
|
|
5709
|
-
return (0, brepjs.ok)(solid);
|
|
5739
|
+
if (!solid.ok) return (0, brepjs.err)(fromBrepError(solid.error, "ROOF_EXTRUDE_FAILED", "Failed to extrude shed roof"));
|
|
5740
|
+
return gate(solid.value, "ROOF_INVALID_SOLID");
|
|
5710
5741
|
} catch (_) {
|
|
5711
|
-
|
|
5742
|
+
_usingCtx3.e = _;
|
|
5712
5743
|
} finally {
|
|
5713
|
-
|
|
5744
|
+
_usingCtx3.d();
|
|
5714
5745
|
}
|
|
5715
5746
|
}
|
|
5716
|
-
function
|
|
5717
|
-
|
|
5718
|
-
|
|
5719
|
-
|
|
5720
|
-
|
|
5721
|
-
|
|
5722
|
-
|
|
5723
|
-
* adjacent mullions. The mullion section is centred on each grid line.
|
|
5724
|
-
*
|
|
5725
|
-
* Geometry is laid out in the wall's local frame: X across the wall, Z up, Y
|
|
5726
|
-
* through the depth. Each component's local-origin solid is returned alongside
|
|
5727
|
-
* its placement origin so the IFC writer can emit one IfcLocalPlacement per
|
|
5728
|
-
* component.
|
|
5729
|
-
*/
|
|
5730
|
-
function curtainWallToGrid(spec) {
|
|
5731
|
-
const { width, height, columns, rows, panelThickness, mullionWidth, mullionDepth } = spec;
|
|
5732
|
-
const cellWidth = width / columns;
|
|
5733
|
-
const cellHeight = height / rows;
|
|
5734
|
-
const halfMullion = mullionWidth / 2;
|
|
5735
|
-
const panelWidth = cellWidth - mullionWidth;
|
|
5736
|
-
const panelHeight = cellHeight - mullionWidth;
|
|
5737
|
-
if (panelWidth <= 0 || panelHeight <= 0) return (0, brepjs.err)(geometryError("CURTAIN_WALL_DEGENERATE_PANEL", "mullionWidth leaves no room for panels in the grid"));
|
|
5738
|
-
const panels = [];
|
|
5739
|
-
const mullions = [];
|
|
5740
|
-
for (let c = 0; c < columns; c++) for (let r = 0; r < rows; r++) {
|
|
5741
|
-
const solidResult = boxSolid(panelWidth, panelThickness, panelHeight);
|
|
5742
|
-
if (!solidResult.ok) {
|
|
5743
|
-
disposeComponents(panels);
|
|
5744
|
-
disposeComponents(mullions);
|
|
5745
|
-
return (0, brepjs.err)(solidResult.error);
|
|
5746
|
-
}
|
|
5747
|
-
panels.push({
|
|
5748
|
-
origin: [
|
|
5749
|
-
c * cellWidth + halfMullion,
|
|
5747
|
+
function gableRoof(spec, pitch) {
|
|
5748
|
+
try {
|
|
5749
|
+
var _usingCtx4 = _usingCtx();
|
|
5750
|
+
const { length, width, thickness } = spec;
|
|
5751
|
+
const ridge = width / 2 * Math.tan(pitch * DEG2RAD);
|
|
5752
|
+
const face = (0, brepjs.polygon)([
|
|
5753
|
+
[
|
|
5750
5754
|
0,
|
|
5751
|
-
|
|
5755
|
+
0,
|
|
5756
|
+
0
|
|
5752
5757
|
],
|
|
5753
|
-
|
|
5754
|
-
|
|
5755
|
-
|
|
5756
|
-
|
|
5758
|
+
[
|
|
5759
|
+
0,
|
|
5760
|
+
width,
|
|
5761
|
+
0
|
|
5762
|
+
],
|
|
5763
|
+
[
|
|
5764
|
+
0,
|
|
5765
|
+
width,
|
|
5766
|
+
thickness
|
|
5767
|
+
],
|
|
5768
|
+
[
|
|
5769
|
+
0,
|
|
5770
|
+
width / 2,
|
|
5771
|
+
thickness + ridge
|
|
5772
|
+
],
|
|
5773
|
+
[
|
|
5774
|
+
0,
|
|
5775
|
+
0,
|
|
5776
|
+
thickness
|
|
5777
|
+
]
|
|
5778
|
+
]);
|
|
5779
|
+
if (!face.ok) return (0, brepjs.err)(fromBrepError(face.error, "ROOF_PROFILE_FAILED", "Failed to create gable profile"));
|
|
5780
|
+
const solid = (0, brepjs.extrude)(_usingCtx4.u(face.value), [
|
|
5781
|
+
length,
|
|
5782
|
+
0,
|
|
5783
|
+
0
|
|
5784
|
+
]);
|
|
5785
|
+
if (!solid.ok) return (0, brepjs.err)(fromBrepError(solid.error, "ROOF_EXTRUDE_FAILED", "Failed to extrude gable roof"));
|
|
5786
|
+
return gate(solid.value, "ROOF_INVALID_SOLID");
|
|
5787
|
+
} catch (_) {
|
|
5788
|
+
_usingCtx4.e = _;
|
|
5789
|
+
} finally {
|
|
5790
|
+
_usingCtx4.d();
|
|
5791
|
+
}
|
|
5792
|
+
}
|
|
5793
|
+
function hipRoof(spec, pitch) {
|
|
5794
|
+
const { length: l, width: w } = spec;
|
|
5795
|
+
const tan = Math.tan(pitch * DEG2RAD);
|
|
5796
|
+
const base = [
|
|
5797
|
+
[
|
|
5798
|
+
0,
|
|
5799
|
+
0,
|
|
5800
|
+
0
|
|
5801
|
+
],
|
|
5802
|
+
[
|
|
5803
|
+
l,
|
|
5804
|
+
0,
|
|
5805
|
+
0
|
|
5806
|
+
],
|
|
5807
|
+
[
|
|
5808
|
+
l,
|
|
5809
|
+
w,
|
|
5810
|
+
0
|
|
5811
|
+
],
|
|
5812
|
+
[
|
|
5813
|
+
0,
|
|
5814
|
+
w,
|
|
5815
|
+
0
|
|
5816
|
+
]
|
|
5817
|
+
];
|
|
5818
|
+
let ridge;
|
|
5819
|
+
if (l >= w) {
|
|
5820
|
+
const h = w / 2 * tan;
|
|
5821
|
+
ridge = [[
|
|
5822
|
+
w / 2,
|
|
5823
|
+
w / 2,
|
|
5824
|
+
h
|
|
5825
|
+
], [
|
|
5826
|
+
l - w / 2,
|
|
5827
|
+
w / 2,
|
|
5828
|
+
h
|
|
5829
|
+
]];
|
|
5830
|
+
} else {
|
|
5831
|
+
const h = l / 2 * tan;
|
|
5832
|
+
ridge = [[
|
|
5833
|
+
l / 2,
|
|
5834
|
+
l / 2,
|
|
5835
|
+
h
|
|
5836
|
+
], [
|
|
5837
|
+
l / 2,
|
|
5838
|
+
w - l / 2,
|
|
5839
|
+
h
|
|
5840
|
+
]];
|
|
5841
|
+
}
|
|
5842
|
+
const solid = (0, brepjs.convexHull)([...base, ...ridge]);
|
|
5843
|
+
if (!solid.ok) return (0, brepjs.err)(fromBrepError(solid.error, "ROOF_HIP_FAILED", "Failed to build hip roof"));
|
|
5844
|
+
return gate(solid.value, "ROOF_INVALID_SOLID");
|
|
5845
|
+
}
|
|
5846
|
+
function domeRoof(spec) {
|
|
5847
|
+
const { length, width } = spec;
|
|
5848
|
+
const r = Math.min(length, width) / 2;
|
|
5849
|
+
const cx = length / 2;
|
|
5850
|
+
const cy = width / 2;
|
|
5851
|
+
const segments = 24;
|
|
5852
|
+
const rings = [
|
|
5853
|
+
0,
|
|
5854
|
+
.4,
|
|
5855
|
+
.7,
|
|
5856
|
+
.9
|
|
5857
|
+
];
|
|
5858
|
+
const pts = [];
|
|
5859
|
+
for (const h of rings) {
|
|
5860
|
+
const z = r * h;
|
|
5861
|
+
const ringR = r * Math.sqrt(1 - h * h);
|
|
5862
|
+
for (let i = 0; i < segments; i++) {
|
|
5863
|
+
const a = 2 * Math.PI * i / segments;
|
|
5864
|
+
pts.push([
|
|
5865
|
+
cx + ringR * Math.cos(a),
|
|
5866
|
+
cy + ringR * Math.sin(a),
|
|
5867
|
+
z
|
|
5868
|
+
]);
|
|
5869
|
+
}
|
|
5870
|
+
}
|
|
5871
|
+
pts.push([
|
|
5872
|
+
cx,
|
|
5873
|
+
cy,
|
|
5874
|
+
r
|
|
5875
|
+
]);
|
|
5876
|
+
const solid = (0, brepjs.convexHull)(pts);
|
|
5877
|
+
if (!solid.ok) return (0, brepjs.err)(fromBrepError(solid.error, "ROOF_DOME_FAILED", "Failed to build dome roof"));
|
|
5878
|
+
return gate(solid.value, "ROOF_INVALID_SOLID");
|
|
5879
|
+
}
|
|
5880
|
+
function roofToSolid(spec) {
|
|
5881
|
+
if (spec.length <= 0) return (0, brepjs.err)(specError("ROOF_ZERO_LENGTH", "Roof length must be positive"));
|
|
5882
|
+
if (spec.width <= 0) return (0, brepjs.err)(specError("ROOF_ZERO_WIDTH", "Roof width must be positive"));
|
|
5883
|
+
if (spec.thickness <= 0) return (0, brepjs.err)(specError("ROOF_ZERO_THICKNESS", "Roof thickness must be positive"));
|
|
5884
|
+
if (spec.pitch === void 0) return flatRoof(spec);
|
|
5885
|
+
switch (spec.predefinedType) {
|
|
5886
|
+
case "SHED_ROOF": return shedRoof(spec, spec.pitch);
|
|
5887
|
+
case "GABLE_ROOF": return gableRoof(spec, spec.pitch);
|
|
5888
|
+
case "HIP_ROOF": return hipRoof(spec, spec.pitch);
|
|
5889
|
+
case "DOME_ROOF": return domeRoof(spec);
|
|
5890
|
+
default: return flatRoof(spec);
|
|
5891
|
+
}
|
|
5892
|
+
}
|
|
5893
|
+
//#endregion
|
|
5894
|
+
//#region src/elementFns/curtainWallFns.ts
|
|
5895
|
+
function boxSolid(sizeX, sizeY, sizeZ) {
|
|
5896
|
+
try {
|
|
5897
|
+
var _usingCtx$10 = _usingCtx();
|
|
5898
|
+
const profileResult = (0, brepjs.polygon)([
|
|
5899
|
+
[
|
|
5900
|
+
0,
|
|
5901
|
+
0,
|
|
5902
|
+
0
|
|
5903
|
+
],
|
|
5904
|
+
[
|
|
5905
|
+
sizeX,
|
|
5906
|
+
0,
|
|
5907
|
+
0
|
|
5908
|
+
],
|
|
5909
|
+
[
|
|
5910
|
+
sizeX,
|
|
5911
|
+
sizeY,
|
|
5912
|
+
0
|
|
5913
|
+
],
|
|
5914
|
+
[
|
|
5915
|
+
0,
|
|
5916
|
+
sizeY,
|
|
5917
|
+
0
|
|
5918
|
+
]
|
|
5919
|
+
]);
|
|
5920
|
+
if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "CURTAIN_WALL_PROFILE_FAILED", "Failed to create component profile"));
|
|
5921
|
+
const solidResult = (0, brepjs.extrude)(_usingCtx$10.u(profileResult.value), [
|
|
5922
|
+
0,
|
|
5923
|
+
0,
|
|
5924
|
+
sizeZ
|
|
5925
|
+
]);
|
|
5926
|
+
if (!solidResult.ok) return (0, brepjs.err)(fromBrepError(solidResult.error, "CURTAIN_WALL_EXTRUDE_FAILED", "Failed to extrude component"));
|
|
5927
|
+
const solid = solidResult.value;
|
|
5928
|
+
if (!(0, brepjs.isValidSolid)(solid)) {
|
|
5929
|
+
solid[Symbol.dispose]();
|
|
5930
|
+
return (0, brepjs.err)(geometryError("CURTAIN_WALL_INVALID_SOLID", "Extruded curtain wall component failed validity check"));
|
|
5931
|
+
}
|
|
5932
|
+
return (0, brepjs.ok)(solid);
|
|
5933
|
+
} catch (_) {
|
|
5934
|
+
_usingCtx$10.e = _;
|
|
5935
|
+
} finally {
|
|
5936
|
+
_usingCtx$10.d();
|
|
5937
|
+
}
|
|
5938
|
+
}
|
|
5939
|
+
function disposeComponents(components) {
|
|
5940
|
+
for (const c of components) c.solid[Symbol.dispose]();
|
|
5941
|
+
}
|
|
5942
|
+
/**
|
|
5943
|
+
* Decomposes a curtain wall spec into its panel (plate) and mullion (member)
|
|
5944
|
+
* geometry. Mullions run along every vertical grid line (columns + 1 of them)
|
|
5945
|
+
* and every horizontal grid line (rows + 1); panels fill the cells between
|
|
5946
|
+
* adjacent mullions. The mullion section is centred on each grid line.
|
|
5947
|
+
*
|
|
5948
|
+
* Geometry is laid out in the wall's local frame: X across the wall, Z up, Y
|
|
5949
|
+
* through the depth. Each component's local-origin solid is returned alongside
|
|
5950
|
+
* its placement origin so the IFC writer can emit one IfcLocalPlacement per
|
|
5951
|
+
* component.
|
|
5952
|
+
*/
|
|
5953
|
+
function curtainWallToGrid(spec) {
|
|
5954
|
+
const { width, height, columns, rows, panelThickness, mullionWidth, mullionDepth } = spec;
|
|
5955
|
+
const cellWidth = width / columns;
|
|
5956
|
+
const cellHeight = height / rows;
|
|
5957
|
+
const halfMullion = mullionWidth / 2;
|
|
5958
|
+
const panelWidth = cellWidth - mullionWidth;
|
|
5959
|
+
const panelHeight = cellHeight - mullionWidth;
|
|
5960
|
+
if (panelWidth <= 0 || panelHeight <= 0) return (0, brepjs.err)(geometryError("CURTAIN_WALL_DEGENERATE_PANEL", "mullionWidth leaves no room for panels in the grid"));
|
|
5961
|
+
const panels = [];
|
|
5962
|
+
const mullions = [];
|
|
5963
|
+
for (let c = 0; c < columns; c++) for (let r = 0; r < rows; r++) {
|
|
5964
|
+
const solidResult = boxSolid(panelWidth, panelThickness, panelHeight);
|
|
5965
|
+
if (!solidResult.ok) {
|
|
5966
|
+
disposeComponents(panels);
|
|
5967
|
+
disposeComponents(mullions);
|
|
5968
|
+
return (0, brepjs.err)(solidResult.error);
|
|
5969
|
+
}
|
|
5970
|
+
panels.push({
|
|
5971
|
+
origin: [
|
|
5972
|
+
c * cellWidth + halfMullion,
|
|
5973
|
+
0,
|
|
5974
|
+
r * cellHeight + halfMullion
|
|
5975
|
+
],
|
|
5976
|
+
size: [
|
|
5977
|
+
panelWidth,
|
|
5978
|
+
panelThickness,
|
|
5979
|
+
panelHeight
|
|
5757
5980
|
],
|
|
5758
5981
|
solid: solidResult.value
|
|
5759
5982
|
});
|
|
@@ -5809,7 +6032,7 @@ function curtainWallToGrid(spec) {
|
|
|
5809
6032
|
//#region src/elementFns/foundationFns.ts
|
|
5810
6033
|
function footingToSolid(spec) {
|
|
5811
6034
|
try {
|
|
5812
|
-
var _usingCtx$
|
|
6035
|
+
var _usingCtx$9 = _usingCtx();
|
|
5813
6036
|
if (spec.length <= 0) return (0, brepjs.err)(specError("FOOTING_ZERO_LENGTH", "Footing length must be positive"));
|
|
5814
6037
|
if (spec.width <= 0) return (0, brepjs.err)(specError("FOOTING_ZERO_WIDTH", "Footing width must be positive"));
|
|
5815
6038
|
if (spec.thickness <= 0) return (0, brepjs.err)(specError("FOOTING_ZERO_THICKNESS", "Footing thickness must be positive"));
|
|
@@ -5837,7 +6060,7 @@ function footingToSolid(spec) {
|
|
|
5837
6060
|
]
|
|
5838
6061
|
]);
|
|
5839
6062
|
if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "FOOTING_PROFILE_FAILED", "Failed to create footing profile"));
|
|
5840
|
-
const solidResult = (0, brepjs.extrude)(_usingCtx$
|
|
6063
|
+
const solidResult = (0, brepjs.extrude)(_usingCtx$9.u(profileResult.value), [
|
|
5841
6064
|
0,
|
|
5842
6065
|
0,
|
|
5843
6066
|
thickness
|
|
@@ -5850,9 +6073,9 @@ function footingToSolid(spec) {
|
|
|
5850
6073
|
}
|
|
5851
6074
|
return (0, brepjs.ok)(solid);
|
|
5852
6075
|
} catch (_) {
|
|
5853
|
-
_usingCtx$
|
|
6076
|
+
_usingCtx$9.e = _;
|
|
5854
6077
|
} finally {
|
|
5855
|
-
_usingCtx$
|
|
6078
|
+
_usingCtx$9.d();
|
|
5856
6079
|
}
|
|
5857
6080
|
}
|
|
5858
6081
|
function pileToSolid(spec) {
|
|
@@ -5905,12 +6128,9 @@ function pileToSolid(spec) {
|
|
|
5905
6128
|
}
|
|
5906
6129
|
//#endregion
|
|
5907
6130
|
//#region src/elementFns/railingFns.ts
|
|
5908
|
-
function
|
|
6131
|
+
function panelRailing(spec) {
|
|
5909
6132
|
try {
|
|
5910
|
-
var _usingCtx$
|
|
5911
|
-
if (spec.length <= 0) return (0, brepjs.err)(specError("RAILING_ZERO_LENGTH", "Railing length must be positive"));
|
|
5912
|
-
if (spec.height <= 0) return (0, brepjs.err)(specError("RAILING_ZERO_HEIGHT", "Railing height must be positive"));
|
|
5913
|
-
if (spec.thickness <= 0) return (0, brepjs.err)(specError("RAILING_ZERO_THICKNESS", "Railing thickness must be positive"));
|
|
6133
|
+
var _usingCtx$8 = _usingCtx();
|
|
5914
6134
|
const { length, height, thickness } = spec;
|
|
5915
6135
|
const profileResult = (0, brepjs.polygon)([
|
|
5916
6136
|
[
|
|
@@ -5935,7 +6155,7 @@ function railingToSolid(spec) {
|
|
|
5935
6155
|
]
|
|
5936
6156
|
]);
|
|
5937
6157
|
if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "RAILING_PROFILE_FAILED", "Failed to create railing profile"));
|
|
5938
|
-
const solidResult = (0, brepjs.extrude)(_usingCtx$
|
|
6158
|
+
const solidResult = (0, brepjs.extrude)(_usingCtx$8.u(profileResult.value), [
|
|
5939
6159
|
length,
|
|
5940
6160
|
0,
|
|
5941
6161
|
0
|
|
@@ -5948,16 +6168,72 @@ function railingToSolid(spec) {
|
|
|
5948
6168
|
}
|
|
5949
6169
|
return (0, brepjs.ok)(solid);
|
|
5950
6170
|
} catch (_) {
|
|
5951
|
-
_usingCtx$
|
|
6171
|
+
_usingCtx$8.e = _;
|
|
5952
6172
|
} finally {
|
|
5953
|
-
_usingCtx$
|
|
6173
|
+
_usingCtx$8.d();
|
|
5954
6174
|
}
|
|
5955
6175
|
}
|
|
6176
|
+
function postedRailing(spec) {
|
|
6177
|
+
const { length, height, thickness: t } = spec;
|
|
6178
|
+
const boxes = [];
|
|
6179
|
+
const postCount = Math.max(2, Math.round(length / 1e3) + 1);
|
|
6180
|
+
for (let i = 0; i < postCount; i++) {
|
|
6181
|
+
const x = t / 2 + (length - t) * (i / (postCount - 1));
|
|
6182
|
+
boxes.push((0, brepjs.box)(t, t, height, {
|
|
6183
|
+
at: [
|
|
6184
|
+
x,
|
|
6185
|
+
t / 2,
|
|
6186
|
+
height / 2
|
|
6187
|
+
],
|
|
6188
|
+
centered: true
|
|
6189
|
+
}));
|
|
6190
|
+
}
|
|
6191
|
+
for (const z of [height - t / 2, t / 2]) boxes.push((0, brepjs.box)(length, t, t, {
|
|
6192
|
+
at: [
|
|
6193
|
+
length / 2,
|
|
6194
|
+
t / 2,
|
|
6195
|
+
z
|
|
6196
|
+
],
|
|
6197
|
+
centered: true
|
|
6198
|
+
}));
|
|
6199
|
+
const scratch = [...boxes];
|
|
6200
|
+
let acc;
|
|
6201
|
+
let failure;
|
|
6202
|
+
for (const b of boxes) {
|
|
6203
|
+
if (acc === void 0) {
|
|
6204
|
+
acc = b;
|
|
6205
|
+
continue;
|
|
6206
|
+
}
|
|
6207
|
+
const fused = (0, brepjs.fuse)(acc, b);
|
|
6208
|
+
if (!fused.ok) {
|
|
6209
|
+
failure = fromBrepError(fused.error, "RAILING_FUSE_FAILED", "Failed to fuse railing parts");
|
|
6210
|
+
break;
|
|
6211
|
+
}
|
|
6212
|
+
acc = fused.value;
|
|
6213
|
+
scratch.push(acc);
|
|
6214
|
+
}
|
|
6215
|
+
const survivor = failure ? void 0 : acc;
|
|
6216
|
+
for (const s of scratch) if (s !== survivor) s[Symbol.dispose]();
|
|
6217
|
+
if (failure) return (0, brepjs.err)(failure);
|
|
6218
|
+
if (survivor === void 0) return (0, brepjs.err)(geometryError("RAILING_INVALID_SOLID", "Posted railing produced no solid"));
|
|
6219
|
+
const result = survivor;
|
|
6220
|
+
if (!(0, brepjs.isValidSolid)(result)) {
|
|
6221
|
+
result[Symbol.dispose]();
|
|
6222
|
+
return (0, brepjs.err)(geometryError("RAILING_INVALID_SOLID", "Posted railing solid failed validity check"));
|
|
6223
|
+
}
|
|
6224
|
+
return (0, brepjs.ok)(result);
|
|
6225
|
+
}
|
|
6226
|
+
function railingToSolid(spec) {
|
|
6227
|
+
if (spec.length <= 0) return (0, brepjs.err)(specError("RAILING_ZERO_LENGTH", "Railing length must be positive"));
|
|
6228
|
+
if (spec.height <= 0) return (0, brepjs.err)(specError("RAILING_ZERO_HEIGHT", "Railing height must be positive"));
|
|
6229
|
+
if (spec.thickness <= 0) return (0, brepjs.err)(specError("RAILING_ZERO_THICKNESS", "Railing thickness must be positive"));
|
|
6230
|
+
return spec.infill === "POSTED" ? postedRailing(spec) : panelRailing(spec);
|
|
6231
|
+
}
|
|
5956
6232
|
//#endregion
|
|
5957
6233
|
//#region src/elementFns/coveringFns.ts
|
|
5958
6234
|
function coveringToSolid(spec) {
|
|
5959
6235
|
try {
|
|
5960
|
-
var _usingCtx$
|
|
6236
|
+
var _usingCtx$7 = _usingCtx();
|
|
5961
6237
|
if (spec.length <= 0) return (0, brepjs.err)(specError("COVERING_ZERO_LENGTH", "Covering length must be positive"));
|
|
5962
6238
|
if (spec.width <= 0) return (0, brepjs.err)(specError("COVERING_ZERO_WIDTH", "Covering width must be positive"));
|
|
5963
6239
|
if (spec.thickness <= 0) return (0, brepjs.err)(specError("COVERING_ZERO_THICKNESS", "Covering thickness must be positive"));
|
|
@@ -5985,7 +6261,7 @@ function coveringToSolid(spec) {
|
|
|
5985
6261
|
]
|
|
5986
6262
|
]);
|
|
5987
6263
|
if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "COVERING_PROFILE_FAILED", "Failed to create covering profile"));
|
|
5988
|
-
const solidResult = (0, brepjs.extrude)(_usingCtx$
|
|
6264
|
+
const solidResult = (0, brepjs.extrude)(_usingCtx$7.u(profileResult.value), [
|
|
5989
6265
|
0,
|
|
5990
6266
|
0,
|
|
5991
6267
|
thickness
|
|
@@ -5998,9 +6274,9 @@ function coveringToSolid(spec) {
|
|
|
5998
6274
|
}
|
|
5999
6275
|
return (0, brepjs.ok)(solid);
|
|
6000
6276
|
} catch (_) {
|
|
6001
|
-
_usingCtx$
|
|
6277
|
+
_usingCtx$7.e = _;
|
|
6002
6278
|
} finally {
|
|
6003
|
-
_usingCtx$
|
|
6279
|
+
_usingCtx$7.d();
|
|
6004
6280
|
}
|
|
6005
6281
|
}
|
|
6006
6282
|
//#endregion
|
|
@@ -6430,17 +6706,17 @@ var BimModel = class {
|
|
|
6430
6706
|
}
|
|
6431
6707
|
#cutWallGeometry(wall, openingSpec) {
|
|
6432
6708
|
try {
|
|
6433
|
-
var _usingCtx$
|
|
6709
|
+
var _usingCtx$6 = _usingCtx();
|
|
6434
6710
|
const toolResult = openingToSolid(openingSpec, wall.spec.thickness);
|
|
6435
6711
|
if (!toolResult.ok) return (0, brepjs.err)(toolResult.error);
|
|
6436
|
-
const tool = _usingCtx$
|
|
6712
|
+
const tool = _usingCtx$6.u(toolResult.value);
|
|
6437
6713
|
const cutResult = (0, brepjs.cut)(wall.geometry, tool);
|
|
6438
6714
|
if (!cutResult.ok) return (0, brepjs.err)(fromBrepError(cutResult.error, "WALL_CUT_FAILED", "Boolean cut of wall with opening failed"));
|
|
6439
6715
|
return (0, brepjs.ok)(cutResult.value);
|
|
6440
6716
|
} catch (_) {
|
|
6441
|
-
_usingCtx$
|
|
6717
|
+
_usingCtx$6.e = _;
|
|
6442
6718
|
} finally {
|
|
6443
|
-
_usingCtx$
|
|
6719
|
+
_usingCtx$6.d();
|
|
6444
6720
|
}
|
|
6445
6721
|
}
|
|
6446
6722
|
#replaceWallGeometry(wall, newGeometry) {
|
|
@@ -6530,6 +6806,50 @@ var BimModel = class {
|
|
|
6530
6806
|
getElement(id) {
|
|
6531
6807
|
return this.#elements.get(id) ?? null;
|
|
6532
6808
|
}
|
|
6809
|
+
/**
|
|
6810
|
+
* A serializable summary of the model's structure, rooted at the project and
|
|
6811
|
+
* walking the IFC spatial hierarchy (AGGREGATES: project → site → building →
|
|
6812
|
+
* storey) plus the elements contained in each storey (placeIn). Useful for a
|
|
6813
|
+
* read-only tree view of the model across a worker boundary.
|
|
6814
|
+
*/
|
|
6815
|
+
toTreeSummary() {
|
|
6816
|
+
const aggregated = /* @__PURE__ */ new Map();
|
|
6817
|
+
const contained = /* @__PURE__ */ new Map();
|
|
6818
|
+
for (const rel of this.#relationships.values()) if (rel.kind === "AGGREGATES") {
|
|
6819
|
+
const list = aggregated.get(rel.relatingObject) ?? [];
|
|
6820
|
+
list.push(...rel.relatedObjects);
|
|
6821
|
+
aggregated.set(rel.relatingObject, list);
|
|
6822
|
+
} else if (rel.kind === "CONTAINED_IN") {
|
|
6823
|
+
const list = contained.get(rel.relatingStructure) ?? [];
|
|
6824
|
+
list.push(...rel.relatedElements);
|
|
6825
|
+
contained.set(rel.relatingStructure, list);
|
|
6826
|
+
}
|
|
6827
|
+
const labelFor = (el) => {
|
|
6828
|
+
const spec = el.spec;
|
|
6829
|
+
const base = typeof spec.name === "string" && spec.name.length > 0 ? spec.name : el.category;
|
|
6830
|
+
return el.category === "STOREY" && typeof spec.elevation === "number" ? `${base} (+${spec.elevation} mm)` : base;
|
|
6831
|
+
};
|
|
6832
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6833
|
+
const build = (id) => {
|
|
6834
|
+
if (seen.has(id)) return null;
|
|
6835
|
+
seen.add(id);
|
|
6836
|
+
const el = this.#elements.get(id);
|
|
6837
|
+
if (el === void 0) return null;
|
|
6838
|
+
const children = [...aggregated.get(id) ?? [], ...contained.get(id) ?? []].map(build).filter((n) => n !== null);
|
|
6839
|
+
return {
|
|
6840
|
+
id,
|
|
6841
|
+
label: labelFor(el),
|
|
6842
|
+
category: el.category,
|
|
6843
|
+
children
|
|
6844
|
+
};
|
|
6845
|
+
};
|
|
6846
|
+
const root = this.#projectId !== null ? build(this.#projectId) : null;
|
|
6847
|
+
const countNodes = (node) => 1 + node.children.reduce((sum, c) => sum + countNodes(c), 0);
|
|
6848
|
+
return {
|
|
6849
|
+
root,
|
|
6850
|
+
elementCount: root ? countNodes(root) : 0
|
|
6851
|
+
};
|
|
6852
|
+
}
|
|
6533
6853
|
getWalls() {
|
|
6534
6854
|
const walls = [];
|
|
6535
6855
|
for (const el of this.#elements.values()) if (el.category === "WALL") walls.push(el);
|
|
@@ -6633,18 +6953,365 @@ var BimModel = class {
|
|
|
6633
6953
|
this.#elements.set(localId, el);
|
|
6634
6954
|
return localId;
|
|
6635
6955
|
}
|
|
6636
|
-
#makeRel(fields) {
|
|
6637
|
-
const localId = this.#counter.next();
|
|
6638
|
-
const guid = deriveIfcGuidSync(makeRelKey(this.#modelScope, fields.kind, localId));
|
|
6639
|
-
const rel = {
|
|
6640
|
-
...fields,
|
|
6641
|
-
guid,
|
|
6642
|
-
localId
|
|
6643
|
-
};
|
|
6644
|
-
this.#relationships.set(localId, rel);
|
|
6645
|
-
return localId;
|
|
6956
|
+
#makeRel(fields) {
|
|
6957
|
+
const localId = this.#counter.next();
|
|
6958
|
+
const guid = deriveIfcGuidSync(makeRelKey(this.#modelScope, fields.kind, localId));
|
|
6959
|
+
const rel = {
|
|
6960
|
+
...fields,
|
|
6961
|
+
guid,
|
|
6962
|
+
localId
|
|
6963
|
+
};
|
|
6964
|
+
this.#relationships.set(localId, rel);
|
|
6965
|
+
return localId;
|
|
6966
|
+
}
|
|
6967
|
+
};
|
|
6968
|
+
//#endregion
|
|
6969
|
+
//#region src/import/placement.ts
|
|
6970
|
+
/**
|
|
6971
|
+
* Metres-per-file-unit length scale read from the IfcUnitAssignment's
|
|
6972
|
+
* LENGTHUNIT. Multiply a file-unit length by this to get metres, then by 1000
|
|
6973
|
+
* for brepjs millimetres. Returns 1.0 (assume metres) when no length unit is
|
|
6974
|
+
* declared.
|
|
6975
|
+
*/
|
|
6976
|
+
function readLengthScale(reader) {
|
|
6977
|
+
const assignments = reader.getLinesOfType(web_ifc.IFCUNITASSIGNMENT);
|
|
6978
|
+
for (const assignmentId of assignments) {
|
|
6979
|
+
const units = asRefArray$1(reader.getLine(assignmentId)?.["Units"]);
|
|
6980
|
+
for (const unitId of units) {
|
|
6981
|
+
const scale = lengthScaleFromUnit(reader, unitId);
|
|
6982
|
+
if (scale !== null) return scale;
|
|
6983
|
+
}
|
|
6984
|
+
}
|
|
6985
|
+
return 1;
|
|
6986
|
+
}
|
|
6987
|
+
/**
|
|
6988
|
+
* Radians-per-file-unit plane-angle scale read from the IfcUnitAssignment's
|
|
6989
|
+
* PLANEANGLEUNIT (1 for RADIAN, ~0.0174533 for DEGREE). Defaults to 1 (the IFC
|
|
6990
|
+
* default plane-angle unit is the radian).
|
|
6991
|
+
*/
|
|
6992
|
+
function readPlaneAngleScale(reader) {
|
|
6993
|
+
for (const assignmentId of reader.getLinesOfType(web_ifc.IFCUNITASSIGNMENT)) {
|
|
6994
|
+
const units = asRefArray$1(reader.getLine(assignmentId)?.["Units"]);
|
|
6995
|
+
for (const unitId of units) {
|
|
6996
|
+
const s = planeAngleScaleFromUnit(reader, unitId);
|
|
6997
|
+
if (s !== null) return s;
|
|
6998
|
+
}
|
|
6999
|
+
}
|
|
7000
|
+
return 1;
|
|
7001
|
+
}
|
|
7002
|
+
function planeAngleScaleFromUnit(reader, unitId) {
|
|
7003
|
+
const unit = reader.getLine(unitId);
|
|
7004
|
+
if (unit === null) return null;
|
|
7005
|
+
const type = reader.getLineType(unitId);
|
|
7006
|
+
if (type === web_ifc.IFCSIUNIT) {
|
|
7007
|
+
if (enumValue(unit["UnitType"]) !== "PLANEANGLEUNIT") return null;
|
|
7008
|
+
if (enumValue(unit["Name"]) !== "RADIAN") return null;
|
|
7009
|
+
return 1;
|
|
7010
|
+
}
|
|
7011
|
+
if (type === web_ifc.IFCCONVERSIONBASEDUNIT) {
|
|
7012
|
+
if (enumValue(unit["UnitType"]) !== "PLANEANGLEUNIT") return null;
|
|
7013
|
+
const measureId = refValue$2(unit["ConversionFactor"]);
|
|
7014
|
+
if (measureId === null) return null;
|
|
7015
|
+
const factor = numericValue(reader.getLine(measureId)?.["ValueComponent"]);
|
|
7016
|
+
if (factor === null) return null;
|
|
7017
|
+
return factor;
|
|
7018
|
+
}
|
|
7019
|
+
return null;
|
|
7020
|
+
}
|
|
7021
|
+
function lengthScaleFromUnit(reader, unitId) {
|
|
7022
|
+
const unit = reader.getLine(unitId);
|
|
7023
|
+
if (unit === null) return null;
|
|
7024
|
+
const type = reader.getLineType(unitId);
|
|
7025
|
+
if (type === web_ifc.IFCSIUNIT) {
|
|
7026
|
+
if (enumValue(unit["UnitType"]) !== "LENGTHUNIT") return null;
|
|
7027
|
+
if (enumValue(unit["Name"]) !== "METRE") return null;
|
|
7028
|
+
return siPrefixFactor(enumValue(unit["Prefix"]));
|
|
7029
|
+
}
|
|
7030
|
+
if (type === web_ifc.IFCCONVERSIONBASEDUNIT) {
|
|
7031
|
+
if (enumValue(unit["UnitType"]) !== "LENGTHUNIT") return null;
|
|
7032
|
+
const measureId = refValue$2(unit["ConversionFactor"]);
|
|
7033
|
+
if (measureId === null) return null;
|
|
7034
|
+
const measure = reader.getLine(measureId);
|
|
7035
|
+
const factor = numericValue(measure?.["ValueComponent"]);
|
|
7036
|
+
if (factor === null) return null;
|
|
7037
|
+
const baseId = refValue$2(measure?.["UnitComponent"]);
|
|
7038
|
+
return factor * (baseId !== null ? lengthScaleFromUnit(reader, baseId) ?? 1 : 1);
|
|
7039
|
+
}
|
|
7040
|
+
return null;
|
|
7041
|
+
}
|
|
7042
|
+
function siPrefixFactor(prefix) {
|
|
7043
|
+
switch (prefix) {
|
|
7044
|
+
case null: return 1;
|
|
7045
|
+
case "KILO": return 1e3;
|
|
7046
|
+
case "HECTO": return 100;
|
|
7047
|
+
case "DECA": return 10;
|
|
7048
|
+
case "DECI": return .1;
|
|
7049
|
+
case "CENTI": return .01;
|
|
7050
|
+
case "MILLI": return .001;
|
|
7051
|
+
case "MICRO": return 1e-6;
|
|
7052
|
+
default: return 1;
|
|
7053
|
+
}
|
|
7054
|
+
}
|
|
7055
|
+
/**
|
|
7056
|
+
* Builds a row-major MatrixTransform (for brepjs `applyMatrix`) from an
|
|
7057
|
+
* (origin, axisX, axisZ) frame, using the SAME IFC orthonormalization as
|
|
7058
|
+
* {@link readAxis2Placement3D}: z = normalize(axisZ); x = normalize(axisX
|
|
7059
|
+
* projected onto the plane ⊥ z); y = z × x. The basis vectors are the matrix
|
|
7060
|
+
* columns, so the row-major linear array is [Xx,Yx,Zx, Xy,Yy,Zy, Xz,Yz,Zz];
|
|
7061
|
+
* translation = origin (mm). This is the display-side counterpart to the IFC
|
|
7062
|
+
* writer's Axis2Placement3D (Axis=Z, RefDirection=X) so on-screen placement and
|
|
7063
|
+
* the IFC export agree.
|
|
7064
|
+
*/
|
|
7065
|
+
function placementToMatrix(f) {
|
|
7066
|
+
const z = normalize$1(f.axisZ);
|
|
7067
|
+
const dot = z[0] * f.axisX[0] + z[1] * f.axisX[1] + z[2] * f.axisX[2];
|
|
7068
|
+
const projX = [
|
|
7069
|
+
f.axisX[0] - dot * z[0],
|
|
7070
|
+
f.axisX[1] - dot * z[1],
|
|
7071
|
+
f.axisX[2] - dot * z[2]
|
|
7072
|
+
];
|
|
7073
|
+
const x = lengthSq(projX) < 1e-12 ? normalize$1(orthogonal$2(z)) : normalize$1(projX);
|
|
7074
|
+
const y = cross$1(z, x);
|
|
7075
|
+
return {
|
|
7076
|
+
linear: [
|
|
7077
|
+
x[0],
|
|
7078
|
+
y[0],
|
|
7079
|
+
z[0],
|
|
7080
|
+
x[1],
|
|
7081
|
+
y[1],
|
|
7082
|
+
z[1],
|
|
7083
|
+
x[2],
|
|
7084
|
+
y[2],
|
|
7085
|
+
z[2]
|
|
7086
|
+
],
|
|
7087
|
+
translation: [
|
|
7088
|
+
f.origin[0],
|
|
7089
|
+
f.origin[1],
|
|
7090
|
+
f.origin[2]
|
|
7091
|
+
]
|
|
7092
|
+
};
|
|
7093
|
+
}
|
|
7094
|
+
function cross$1(a, b) {
|
|
7095
|
+
return [
|
|
7096
|
+
a[1] * b[2] - a[2] * b[1],
|
|
7097
|
+
a[2] * b[0] - a[0] * b[2],
|
|
7098
|
+
a[0] * b[1] - a[1] * b[0]
|
|
7099
|
+
];
|
|
7100
|
+
}
|
|
7101
|
+
function lengthSq(v) {
|
|
7102
|
+
return v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
|
|
7103
|
+
}
|
|
7104
|
+
function normalize$1(v) {
|
|
7105
|
+
const len = Math.sqrt(lengthSq(v));
|
|
7106
|
+
if (len < 1e-12) return [
|
|
7107
|
+
0,
|
|
7108
|
+
0,
|
|
7109
|
+
1
|
|
7110
|
+
];
|
|
7111
|
+
return [
|
|
7112
|
+
v[0] / len,
|
|
7113
|
+
v[1] / len,
|
|
7114
|
+
v[2] / len
|
|
7115
|
+
];
|
|
7116
|
+
}
|
|
7117
|
+
function orthogonal$2(v) {
|
|
7118
|
+
return Math.abs(v[0]) < .9 ? cross$1(v, [
|
|
7119
|
+
1,
|
|
7120
|
+
0,
|
|
7121
|
+
0
|
|
7122
|
+
]) : cross$1(v, [
|
|
7123
|
+
0,
|
|
7124
|
+
1,
|
|
7125
|
+
0
|
|
7126
|
+
]);
|
|
7127
|
+
}
|
|
7128
|
+
function refValue$2(v) {
|
|
7129
|
+
if (v === null || v === void 0) return null;
|
|
7130
|
+
if (typeof v === "number") return v;
|
|
7131
|
+
const value = v.value;
|
|
7132
|
+
return typeof value === "number" ? value : null;
|
|
7133
|
+
}
|
|
7134
|
+
function asRefArray$1(v) {
|
|
7135
|
+
if (!Array.isArray(v)) return [];
|
|
7136
|
+
const out = [];
|
|
7137
|
+
for (const item of v) {
|
|
7138
|
+
const id = refValue$2(item);
|
|
7139
|
+
if (id !== null) out.push(id);
|
|
7140
|
+
}
|
|
7141
|
+
return out;
|
|
7142
|
+
}
|
|
7143
|
+
function numericValue(v) {
|
|
7144
|
+
if (typeof v === "number") return v;
|
|
7145
|
+
if (v === null || v === void 0) return null;
|
|
7146
|
+
const value = v.value;
|
|
7147
|
+
return typeof value === "number" ? value : null;
|
|
7148
|
+
}
|
|
7149
|
+
function enumValue(v) {
|
|
7150
|
+
if (typeof v === "string") return v;
|
|
7151
|
+
if (v === null || v === void 0) return null;
|
|
7152
|
+
const value = v.value;
|
|
7153
|
+
return typeof value === "string" ? value : null;
|
|
7154
|
+
}
|
|
7155
|
+
//#endregion
|
|
7156
|
+
//#region src/elementFns/stairFns.ts
|
|
7157
|
+
function buildSilhouette$1(numberOfRisers, riserHeight, treadLength) {
|
|
7158
|
+
const pts = [];
|
|
7159
|
+
pts.push([
|
|
7160
|
+
0,
|
|
7161
|
+
0,
|
|
7162
|
+
0
|
|
7163
|
+
]);
|
|
7164
|
+
let x = 0;
|
|
7165
|
+
let z = 0;
|
|
7166
|
+
for (let i = 0; i < numberOfRisers; i++) {
|
|
7167
|
+
z += riserHeight;
|
|
7168
|
+
pts.push([
|
|
7169
|
+
x,
|
|
7170
|
+
0,
|
|
7171
|
+
z
|
|
7172
|
+
]);
|
|
7173
|
+
x += treadLength;
|
|
7174
|
+
pts.push([
|
|
7175
|
+
x,
|
|
7176
|
+
0,
|
|
7177
|
+
z
|
|
7178
|
+
]);
|
|
7179
|
+
}
|
|
7180
|
+
pts.push([
|
|
7181
|
+
x,
|
|
7182
|
+
0,
|
|
7183
|
+
0
|
|
7184
|
+
]);
|
|
7185
|
+
return pts;
|
|
7186
|
+
}
|
|
7187
|
+
function stairFlightToSolid(spec) {
|
|
7188
|
+
try {
|
|
7189
|
+
var _usingCtx$5 = _usingCtx();
|
|
7190
|
+
if (spec.width <= 0) return (0, brepjs.err)(specError("STAIR_FLIGHT_ZERO_WIDTH", "Stair flight width must be positive"));
|
|
7191
|
+
if (spec.riserHeight <= 0) return (0, brepjs.err)(specError("STAIR_FLIGHT_ZERO_RISER", "Stair flight riserHeight must be positive"));
|
|
7192
|
+
if (spec.treadLength <= 0) return (0, brepjs.err)(specError("STAIR_FLIGHT_ZERO_TREAD", "Stair flight treadLength must be positive"));
|
|
7193
|
+
if (!Number.isInteger(spec.numberOfRisers) || spec.numberOfRisers < 1) return (0, brepjs.err)(specError("STAIR_FLIGHT_BAD_RISERS", "Stair flight numberOfRisers must be a positive integer"));
|
|
7194
|
+
const profileResult = (0, brepjs.polygon)(buildSilhouette$1(spec.numberOfRisers, spec.riserHeight, spec.treadLength));
|
|
7195
|
+
if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "STAIR_FLIGHT_PROFILE_FAILED", "Failed to create stair flight silhouette profile"));
|
|
7196
|
+
const solidResult = (0, brepjs.extrude)(_usingCtx$5.u(profileResult.value), [
|
|
7197
|
+
0,
|
|
7198
|
+
spec.width,
|
|
7199
|
+
0
|
|
7200
|
+
]);
|
|
7201
|
+
if (!solidResult.ok) return (0, brepjs.err)(fromBrepError(solidResult.error, "STAIR_FLIGHT_EXTRUDE_FAILED", "Failed to extrude stair flight silhouette"));
|
|
7202
|
+
const solid = solidResult.value;
|
|
7203
|
+
if (!(0, brepjs.isValidSolid)(solid)) {
|
|
7204
|
+
solid[Symbol.dispose]();
|
|
7205
|
+
return (0, brepjs.err)(geometryError("STAIR_FLIGHT_INVALID_SOLID", "Stair flight solid failed validity check"));
|
|
7206
|
+
}
|
|
7207
|
+
return (0, brepjs.ok)({
|
|
7208
|
+
solid,
|
|
7209
|
+
geometrySimplified: false
|
|
7210
|
+
});
|
|
7211
|
+
} catch (_) {
|
|
7212
|
+
_usingCtx$5.e = _;
|
|
7213
|
+
} finally {
|
|
7214
|
+
_usingCtx$5.d();
|
|
7215
|
+
}
|
|
7216
|
+
}
|
|
7217
|
+
//#endregion
|
|
7218
|
+
//#region src/elementFns/placedGeometry.ts
|
|
7219
|
+
function place(solid, frame) {
|
|
7220
|
+
const result = (0, brepjs.applyMatrix)(solid, placementToMatrix(frame));
|
|
7221
|
+
if (!result.ok) return (0, brepjs.err)(fromBrepError(result.error, "PLACED_GEOMETRY_FAILED", "Failed to place element geometry"));
|
|
7222
|
+
return (0, brepjs.ok)(result.value);
|
|
7223
|
+
}
|
|
7224
|
+
function disposeAll(solids) {
|
|
7225
|
+
for (const s of solids) s[Symbol.dispose]();
|
|
7226
|
+
}
|
|
7227
|
+
/**
|
|
7228
|
+
* Returns each element's geometry transformed to its world placement, as fresh
|
|
7229
|
+
* caller-owned solids, wrapped in a `Result` (Layer-2 code prefers `Result` over
|
|
7230
|
+
* throwing). **Dispose the returned solids** (e.g. via `using` / `[Symbol.dispose]`)
|
|
7231
|
+
* when you own their lifetime — they are independent of the model
|
|
7232
|
+
* (`BimModel[Symbol.dispose]` frees only the stored, unplaced `.geometry`). On any
|
|
7233
|
+
* failure the solids already built for this call are disposed before the error is
|
|
7234
|
+
* returned, so no partial array is leaked.
|
|
7235
|
+
*
|
|
7236
|
+
* Stairs carry no element solid (`.geometry` is null), so flight solids are built
|
|
7237
|
+
* from `spec.flights` and placed per flight. Curtain walls return placed panels +
|
|
7238
|
+
* mullions. Elements with no solid geometry (doors/windows/ramps/groups/spatial)
|
|
7239
|
+
* return an empty array.
|
|
7240
|
+
*/
|
|
7241
|
+
function placedSolids(el) {
|
|
7242
|
+
switch (el.category) {
|
|
7243
|
+
case "WALL":
|
|
7244
|
+
case "SLAB":
|
|
7245
|
+
case "BEAM":
|
|
7246
|
+
case "COLUMN":
|
|
7247
|
+
case "SPACE":
|
|
7248
|
+
case "ROOF":
|
|
7249
|
+
case "FOOTING":
|
|
7250
|
+
case "PILE":
|
|
7251
|
+
case "RAILING": {
|
|
7252
|
+
const placed = place(el.geometry, el.spec);
|
|
7253
|
+
if (!placed.ok) return placed;
|
|
7254
|
+
return (0, brepjs.ok)([placed.value]);
|
|
7255
|
+
}
|
|
7256
|
+
case "STAIR": {
|
|
7257
|
+
const out = [];
|
|
7258
|
+
for (const flight of el.spec.flights) try {
|
|
7259
|
+
var _usingCtx$4 = _usingCtx();
|
|
7260
|
+
const built = stairFlightToSolid(flight);
|
|
7261
|
+
if (!built.ok) {
|
|
7262
|
+
disposeAll(out);
|
|
7263
|
+
return (0, brepjs.err)(built.error);
|
|
7264
|
+
}
|
|
7265
|
+
const placed = place(_usingCtx$4.u(built.value.solid), flight);
|
|
7266
|
+
if (!placed.ok) {
|
|
7267
|
+
disposeAll(out);
|
|
7268
|
+
return placed;
|
|
7269
|
+
}
|
|
7270
|
+
out.push(placed.value);
|
|
7271
|
+
} catch (_) {
|
|
7272
|
+
_usingCtx$4.e = _;
|
|
7273
|
+
} finally {
|
|
7274
|
+
_usingCtx$4.d();
|
|
7275
|
+
}
|
|
7276
|
+
return (0, brepjs.ok)(out);
|
|
7277
|
+
}
|
|
7278
|
+
case "CURTAIN_WALL": {
|
|
7279
|
+
const out = [];
|
|
7280
|
+
for (const c of [...el.geometry.panels, ...el.geometry.mullions]) try {
|
|
7281
|
+
var _usingCtx3 = _usingCtx();
|
|
7282
|
+
const componentLocal = place(c.solid, {
|
|
7283
|
+
origin: c.origin,
|
|
7284
|
+
axisX: [
|
|
7285
|
+
1,
|
|
7286
|
+
0,
|
|
7287
|
+
0
|
|
7288
|
+
],
|
|
7289
|
+
axisZ: [
|
|
7290
|
+
0,
|
|
7291
|
+
0,
|
|
7292
|
+
1
|
|
7293
|
+
]
|
|
7294
|
+
});
|
|
7295
|
+
if (!componentLocal.ok) {
|
|
7296
|
+
disposeAll(out);
|
|
7297
|
+
return componentLocal;
|
|
7298
|
+
}
|
|
7299
|
+
const placed = place(_usingCtx3.u(componentLocal.value), el.spec);
|
|
7300
|
+
if (!placed.ok) {
|
|
7301
|
+
disposeAll(out);
|
|
7302
|
+
return placed;
|
|
7303
|
+
}
|
|
7304
|
+
out.push(placed.value);
|
|
7305
|
+
} catch (_) {
|
|
7306
|
+
_usingCtx3.e = _;
|
|
7307
|
+
} finally {
|
|
7308
|
+
_usingCtx3.d();
|
|
7309
|
+
}
|
|
7310
|
+
return (0, brepjs.ok)(out);
|
|
7311
|
+
}
|
|
7312
|
+
default: return (0, brepjs.ok)([]);
|
|
6646
7313
|
}
|
|
6647
|
-
}
|
|
7314
|
+
}
|
|
6648
7315
|
//#endregion
|
|
6649
7316
|
//#region src/ifc-writer/schemaVersion.ts
|
|
6650
7317
|
/**
|
|
@@ -6707,27 +7374,62 @@ function schemaSupports(schema, entityName) {
|
|
|
6707
7374
|
return true;
|
|
6708
7375
|
}
|
|
6709
7376
|
//#endregion
|
|
7377
|
+
//#region src/ifcRuntime.ts
|
|
7378
|
+
var wasmLocateFile;
|
|
7379
|
+
/**
|
|
7380
|
+
* Override how web-ifc finds its `.wasm` file. Applied by every web-ifc entry
|
|
7381
|
+
* point in this package — IFC export ({@link toIfc}), import ({@link fromIfc})
|
|
7382
|
+
* and validation. Required when brepjs-bim is bundled into a worker that serves
|
|
7383
|
+
* the wasm itself; not needed in Node.
|
|
7384
|
+
*/
|
|
7385
|
+
function setIfcWasmLocateFile(locate) {
|
|
7386
|
+
wasmLocateFile = locate;
|
|
7387
|
+
}
|
|
7388
|
+
/**
|
|
7389
|
+
* Initialize a web-ifc API instance the way this package always wants it: with
|
|
7390
|
+
* the host-provided wasm locator and forced single-threaded.
|
|
7391
|
+
*
|
|
7392
|
+
* Single-threaded matters in a cross-origin-isolated context (e.g. a page that
|
|
7393
|
+
* sets COOP/COEP for another WASM kernel): web-ifc would otherwise load its
|
|
7394
|
+
* pthread build and spawn a sub-Worker, which fails when brepjs-bim is itself
|
|
7395
|
+
* bundled inside a Web Worker. In Node the flag is a no-op (web-ifc is already
|
|
7396
|
+
* single-threaded there), and multithreading only speeds up parsing/geometry,
|
|
7397
|
+
* not the one-shot serialize/read this package does.
|
|
7398
|
+
*/
|
|
7399
|
+
async function initIfcApi(api) {
|
|
7400
|
+
await api.Init(wasmLocateFile, true);
|
|
7401
|
+
}
|
|
7402
|
+
//#endregion
|
|
6710
7403
|
//#region src/ifc-writer/ifcWriter.ts
|
|
6711
7404
|
/** Default MVD ViewDefinition declared in the STEP FILE_DESCRIPTION header. */
|
|
6712
7405
|
var DEFAULT_MVD_VIEW_DEFINITION = "ReferenceView_v1.2";
|
|
6713
7406
|
var VIEW_DEFINITION_RE = /ViewDefinition \[[^\]]*\]/;
|
|
7407
|
+
var FILE_NAME_RE = /(FILE_NAME\('[^']*','[^']*',)(?:\$|\(\$\)),(?:\$|\(\$\)),('[^']*','[^']*'),\$\)/;
|
|
7408
|
+
/** STEP single-quoted string literal with embedded quotes doubled per ISO 10303-21. */
|
|
7409
|
+
function stepString(value) {
|
|
7410
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
7411
|
+
}
|
|
6714
7412
|
var IfcWriter = class IfcWriter {
|
|
6715
7413
|
#api;
|
|
6716
7414
|
#modelId;
|
|
6717
7415
|
#mvdViewDefinition;
|
|
7416
|
+
#author;
|
|
7417
|
+
#organization;
|
|
6718
7418
|
#nextExpressId = 1;
|
|
6719
7419
|
#closed = false;
|
|
6720
7420
|
#modelScope = "";
|
|
6721
|
-
constructor(api, modelId, mvdViewDefinition) {
|
|
7421
|
+
constructor(api, modelId, mvdViewDefinition, header) {
|
|
6722
7422
|
this.#api = api;
|
|
6723
7423
|
this.#modelId = modelId;
|
|
6724
7424
|
this.#mvdViewDefinition = mvdViewDefinition;
|
|
7425
|
+
this.#author = header.author ?? "";
|
|
7426
|
+
this.#organization = header.organization ?? "";
|
|
6725
7427
|
}
|
|
6726
|
-
static async create(mvdViewDefinition = DEFAULT_MVD_VIEW_DEFINITION, ifcSchema = DEFAULT_IFC_SCHEMA) {
|
|
7428
|
+
static async create(mvdViewDefinition = DEFAULT_MVD_VIEW_DEFINITION, ifcSchema = DEFAULT_IFC_SCHEMA, header = {}) {
|
|
6727
7429
|
try {
|
|
6728
7430
|
const api = new web_ifc.IfcAPI();
|
|
6729
|
-
await api
|
|
6730
|
-
return (0, brepjs.ok)(new IfcWriter(api, api.CreateModel({ schema: fileSchemaString(ifcSchema) }), mvdViewDefinition));
|
|
7431
|
+
await initIfcApi(api);
|
|
7432
|
+
return (0, brepjs.ok)(new IfcWriter(api, api.CreateModel({ schema: fileSchemaString(ifcSchema) }), mvdViewDefinition, header));
|
|
6731
7433
|
} catch (e) {
|
|
6732
7434
|
return (0, brepjs.err)(ifcError("IFC_INIT_FAILED", "Failed to initialize web-ifc", e));
|
|
6733
7435
|
}
|
|
@@ -6761,7 +7463,7 @@ var IfcWriter = class IfcWriter {
|
|
|
6761
7463
|
if (this.#closed) return (0, brepjs.err)(ifcError("IFC_ALREADY_SAVED", "Model has already been saved and closed"));
|
|
6762
7464
|
try {
|
|
6763
7465
|
const bytes = this.#api.SaveModel(this.#modelId);
|
|
6764
|
-
return (0, brepjs.ok)(this.#
|
|
7466
|
+
return (0, brepjs.ok)(this.#patchHeader(bytes));
|
|
6765
7467
|
} catch (e) {
|
|
6766
7468
|
return (0, brepjs.err)(ifcError("IFC_SAVE_FAILED", "Failed to serialize IFC model", e));
|
|
6767
7469
|
} finally {
|
|
@@ -6770,21 +7472,20 @@ var IfcWriter = class IfcWriter {
|
|
|
6770
7472
|
}
|
|
6771
7473
|
}
|
|
6772
7474
|
/**
|
|
6773
|
-
*
|
|
6774
|
-
*
|
|
6775
|
-
*
|
|
6776
|
-
* (e.g. a future web-ifc
|
|
7475
|
+
* Rewrites the STEP header in the ASCII region web-ifc emits: declares the MVD
|
|
7476
|
+
* in FILE_DESCRIPTION and makes FILE_NAME's author/organization/authorization
|
|
7477
|
+
* spec-conformant (web-ifc leaves them as bare `$`). web-ifc exposes neither
|
|
7478
|
+
* for configuration. If an expected pattern is absent (e.g. a future web-ifc
|
|
7479
|
+
* default change) that part is skipped and the bytes returned unchanged.
|
|
6777
7480
|
*/
|
|
6778
|
-
#
|
|
6779
|
-
if (this.#mvdViewDefinition.length === 0) return bytes;
|
|
7481
|
+
#patchHeader(bytes) {
|
|
6780
7482
|
const HEADER_SCAN = Math.min(bytes.byteLength, 2048);
|
|
6781
|
-
|
|
6782
|
-
if (
|
|
6783
|
-
|
|
6784
|
-
|
|
6785
|
-
}
|
|
6786
|
-
const
|
|
6787
|
-
const patchedHeadBytes = new TextEncoder().encode(patchedHead);
|
|
7483
|
+
let head = new TextDecoder().decode(bytes.subarray(0, HEADER_SCAN));
|
|
7484
|
+
if (FILE_NAME_RE.test(head)) head = head.replace(FILE_NAME_RE, (_m, prefix, systems) => `${prefix}(${stepString(this.#author)}),(${stepString(this.#organization)}),${systems},${stepString("")})`);
|
|
7485
|
+
else console.warn("IfcWriter: FILE_NAME null-field pattern not found; author/organization/authorization left unpatched");
|
|
7486
|
+
if (this.#mvdViewDefinition.length > 0) if (VIEW_DEFINITION_RE.test(head)) head = head.replace(VIEW_DEFINITION_RE, `ViewDefinition [${this.#mvdViewDefinition}]`);
|
|
7487
|
+
else console.warn(`IfcWriter: FILE_DESCRIPTION ViewDefinition not found; MVD "${this.#mvdViewDefinition}" not declared`);
|
|
7488
|
+
const patchedHeadBytes = new TextEncoder().encode(head);
|
|
6788
7489
|
const tail = bytes.subarray(HEADER_SCAN);
|
|
6789
7490
|
const out = new Uint8Array(patchedHeadBytes.byteLength + tail.byteLength);
|
|
6790
7491
|
out.set(patchedHeadBytes, 0);
|
|
@@ -7881,7 +8582,7 @@ function writeSlabGeometry(w, spec, geomSubContextId, parentPlacementId) {
|
|
|
7881
8582
|
productDefinitionShapeId
|
|
7882
8583
|
};
|
|
7883
8584
|
}
|
|
7884
|
-
function writeRoofGeometry(w, spec, geomSubContextId, parentPlacementId) {
|
|
8585
|
+
function writeRoofGeometry(w, spec, solid, geomSubContextId, parentPlacementId) {
|
|
7885
8586
|
const placement3DId = writeAxis2Placement3D(w, spec.origin.map(toIfcLengthM), spec.axisZ, spec.axisX);
|
|
7886
8587
|
const localPlacementId = w.nextId();
|
|
7887
8588
|
w.writeLine({
|
|
@@ -7890,6 +8591,14 @@ function writeRoofGeometry(w, spec, geomSubContextId, parentPlacementId) {
|
|
|
7890
8591
|
PlacementRelTo: parentPlacementId !== null ? w.ref(parentPlacementId) : null,
|
|
7891
8592
|
RelativePlacement: w.ref(placement3DId)
|
|
7892
8593
|
});
|
|
8594
|
+
if (spec.pitch !== void 0) {
|
|
8595
|
+
const tess = writeTessellation(w, solid, geomSubContextId, localPlacementId);
|
|
8596
|
+
return {
|
|
8597
|
+
localPlacementId,
|
|
8598
|
+
productDefinitionShapeId: tess.productDefinitionShapeId,
|
|
8599
|
+
usedFallback: tess.usedFallback
|
|
8600
|
+
};
|
|
8601
|
+
}
|
|
7893
8602
|
const lengthM = toIfcLengthM(spec.length);
|
|
7894
8603
|
const widthM = toIfcLengthM(spec.width);
|
|
7895
8604
|
const thicknessM = toIfcLengthM(spec.thickness);
|
|
@@ -7957,7 +8666,8 @@ function writeRoofGeometry(w, spec, geomSubContextId, parentPlacementId) {
|
|
|
7957
8666
|
});
|
|
7958
8667
|
return {
|
|
7959
8668
|
localPlacementId,
|
|
7960
|
-
productDefinitionShapeId
|
|
8669
|
+
productDefinitionShapeId,
|
|
8670
|
+
usedFallback: false
|
|
7961
8671
|
};
|
|
7962
8672
|
}
|
|
7963
8673
|
function writeAxis2Placement2D$1(w) {
|
|
@@ -8022,7 +8732,7 @@ function writeProfile(w, profile) {
|
|
|
8022
8732
|
OverallDepth: w.mkType(web_ifc.IFCPOSITIVELENGTHMEASURE, toIfcLengthM(profile.overallDepth)),
|
|
8023
8733
|
WebThickness: w.mkType(web_ifc.IFCPOSITIVELENGTHMEASURE, toIfcLengthM(profile.webThickness)),
|
|
8024
8734
|
FlangeThickness: w.mkType(web_ifc.IFCPOSITIVELENGTHMEASURE, toIfcLengthM(profile.flangeThickness)),
|
|
8025
|
-
FilletRadius: null,
|
|
8735
|
+
FilletRadius: profile.filletRadius === void 0 ? null : w.mkType(web_ifc.IFCPOSITIVELENGTHMEASURE, toIfcLengthM(profile.filletRadius)),
|
|
8026
8736
|
FlangeEdgeRadius: null,
|
|
8027
8737
|
FlangeSlope: null
|
|
8028
8738
|
});
|
|
@@ -8660,68 +9370,6 @@ function writeRelContainedInSpatialStructure(w, guid, ownerHistoryId, relatingSt
|
|
|
8660
9370
|
});
|
|
8661
9371
|
}
|
|
8662
9372
|
//#endregion
|
|
8663
|
-
//#region src/elementFns/stairFns.ts
|
|
8664
|
-
function buildSilhouette$1(numberOfRisers, riserHeight, treadLength) {
|
|
8665
|
-
const pts = [];
|
|
8666
|
-
pts.push([
|
|
8667
|
-
0,
|
|
8668
|
-
0,
|
|
8669
|
-
0
|
|
8670
|
-
]);
|
|
8671
|
-
let x = 0;
|
|
8672
|
-
let z = 0;
|
|
8673
|
-
for (let i = 0; i < numberOfRisers; i++) {
|
|
8674
|
-
z += riserHeight;
|
|
8675
|
-
pts.push([
|
|
8676
|
-
x,
|
|
8677
|
-
0,
|
|
8678
|
-
z
|
|
8679
|
-
]);
|
|
8680
|
-
x += treadLength;
|
|
8681
|
-
pts.push([
|
|
8682
|
-
x,
|
|
8683
|
-
0,
|
|
8684
|
-
z
|
|
8685
|
-
]);
|
|
8686
|
-
}
|
|
8687
|
-
pts.push([
|
|
8688
|
-
x,
|
|
8689
|
-
0,
|
|
8690
|
-
0
|
|
8691
|
-
]);
|
|
8692
|
-
return pts;
|
|
8693
|
-
}
|
|
8694
|
-
function stairFlightToSolid(spec) {
|
|
8695
|
-
try {
|
|
8696
|
-
var _usingCtx$4 = _usingCtx();
|
|
8697
|
-
if (spec.width <= 0) return (0, brepjs.err)(specError("STAIR_FLIGHT_ZERO_WIDTH", "Stair flight width must be positive"));
|
|
8698
|
-
if (spec.riserHeight <= 0) return (0, brepjs.err)(specError("STAIR_FLIGHT_ZERO_RISER", "Stair flight riserHeight must be positive"));
|
|
8699
|
-
if (spec.treadLength <= 0) return (0, brepjs.err)(specError("STAIR_FLIGHT_ZERO_TREAD", "Stair flight treadLength must be positive"));
|
|
8700
|
-
if (!Number.isInteger(spec.numberOfRisers) || spec.numberOfRisers < 1) return (0, brepjs.err)(specError("STAIR_FLIGHT_BAD_RISERS", "Stair flight numberOfRisers must be a positive integer"));
|
|
8701
|
-
const profileResult = (0, brepjs.polygon)(buildSilhouette$1(spec.numberOfRisers, spec.riserHeight, spec.treadLength));
|
|
8702
|
-
if (!profileResult.ok) return (0, brepjs.err)(fromBrepError(profileResult.error, "STAIR_FLIGHT_PROFILE_FAILED", "Failed to create stair flight silhouette profile"));
|
|
8703
|
-
const solidResult = (0, brepjs.extrude)(_usingCtx$4.u(profileResult.value), [
|
|
8704
|
-
0,
|
|
8705
|
-
spec.width,
|
|
8706
|
-
0
|
|
8707
|
-
]);
|
|
8708
|
-
if (!solidResult.ok) return (0, brepjs.err)(fromBrepError(solidResult.error, "STAIR_FLIGHT_EXTRUDE_FAILED", "Failed to extrude stair flight silhouette"));
|
|
8709
|
-
const solid = solidResult.value;
|
|
8710
|
-
if (!(0, brepjs.isValidSolid)(solid)) {
|
|
8711
|
-
solid[Symbol.dispose]();
|
|
8712
|
-
return (0, brepjs.err)(geometryError("STAIR_FLIGHT_INVALID_SOLID", "Stair flight solid failed validity check"));
|
|
8713
|
-
}
|
|
8714
|
-
return (0, brepjs.ok)({
|
|
8715
|
-
solid,
|
|
8716
|
-
geometrySimplified: false
|
|
8717
|
-
});
|
|
8718
|
-
} catch (_) {
|
|
8719
|
-
_usingCtx$4.e = _;
|
|
8720
|
-
} finally {
|
|
8721
|
-
_usingCtx$4.d();
|
|
8722
|
-
}
|
|
8723
|
-
}
|
|
8724
|
-
//#endregion
|
|
8725
9373
|
//#region src/elementFns/rampFns.ts
|
|
8726
9374
|
function buildSilhouette(length, rise, thickness) {
|
|
8727
9375
|
return [
|
|
@@ -9032,7 +9680,7 @@ function writeRampAssembly(w, spec, rampKey, ownerHistoryId, geomSubContextId, p
|
|
|
9032
9680
|
}
|
|
9033
9681
|
//#endregion
|
|
9034
9682
|
//#region src/ifc-writer/railingWriter.ts
|
|
9035
|
-
function writeRailingGeometry(w, spec, geomSubContextId, parentPlacementId) {
|
|
9683
|
+
function writeRailingGeometry(w, spec, solid, geomSubContextId, parentPlacementId) {
|
|
9036
9684
|
const placement3DId = writeAxis2Placement3D(w, spec.origin.map(toIfcLengthM), spec.axisZ, spec.axisX);
|
|
9037
9685
|
const localPlacementId = w.nextId();
|
|
9038
9686
|
w.writeLine({
|
|
@@ -9041,6 +9689,15 @@ function writeRailingGeometry(w, spec, geomSubContextId, parentPlacementId) {
|
|
|
9041
9689
|
PlacementRelTo: parentPlacementId !== null ? w.ref(parentPlacementId) : null,
|
|
9042
9690
|
RelativePlacement: w.ref(placement3DId)
|
|
9043
9691
|
});
|
|
9692
|
+
if (spec.infill === "POSTED") {
|
|
9693
|
+
const tess = writeTessellation(w, solid, geomSubContextId, localPlacementId);
|
|
9694
|
+
return {
|
|
9695
|
+
localPlacementId,
|
|
9696
|
+
productDefinitionShapeId: tess.productDefinitionShapeId,
|
|
9697
|
+
bodyItemId: null,
|
|
9698
|
+
usedFallback: tess.usedFallback
|
|
9699
|
+
};
|
|
9700
|
+
}
|
|
9044
9701
|
const thicknessM = toIfcLengthM(spec.thickness);
|
|
9045
9702
|
const heightM = toIfcLengthM(spec.height);
|
|
9046
9703
|
const lengthM = toIfcLengthM(spec.length);
|
|
@@ -9117,7 +9774,8 @@ function writeRailingGeometry(w, spec, geomSubContextId, parentPlacementId) {
|
|
|
9117
9774
|
return {
|
|
9118
9775
|
localPlacementId,
|
|
9119
9776
|
productDefinitionShapeId,
|
|
9120
|
-
bodyItemId: extrusionId
|
|
9777
|
+
bodyItemId: extrusionId,
|
|
9778
|
+
usedFallback: false
|
|
9121
9779
|
};
|
|
9122
9780
|
}
|
|
9123
9781
|
function writeRailingEntity(w, guid, name, predefinedType, ownerHistoryId, localPlacementId, productDefinitionShapeId) {
|
|
@@ -11152,35 +11810,6 @@ function checkOpeningExists(issues, elementsById, openingId, code) {
|
|
|
11152
11810
|
if (opening.category !== "OPENING") issues.push(issue("error", code === "VOID_OPENING_MISSING" ? "VOID_OPENING_WRONG_CATEGORY" : "FILL_OPENING_WRONG_CATEGORY", `References opening localId ${openingId}, expected OPENING but found ${opening.category}`, openingId, { actual: opening.category }));
|
|
11153
11811
|
}
|
|
11154
11812
|
//#endregion
|
|
11155
|
-
//#region src/identity/ifcGuid.ts
|
|
11156
|
-
var IFC_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_$";
|
|
11157
|
-
function newIfcGuid() {
|
|
11158
|
-
const bytes = crypto.getRandomValues(new Uint8Array(16));
|
|
11159
|
-
bytes[6] = (bytes[6] ?? 0) & 15 | 64;
|
|
11160
|
-
bytes[8] = (bytes[8] ?? 0) & 63 | 128;
|
|
11161
|
-
return encodeIfcGuid(bytes);
|
|
11162
|
-
}
|
|
11163
|
-
function isValidIfcGuid(s) {
|
|
11164
|
-
if (s.length !== 22) return false;
|
|
11165
|
-
for (const ch of s) if (!IFC_CHARS.includes(ch)) return false;
|
|
11166
|
-
return true;
|
|
11167
|
-
}
|
|
11168
|
-
function encodeIfcGuid(bytes) {
|
|
11169
|
-
let result = "";
|
|
11170
|
-
let acc = 0;
|
|
11171
|
-
let bits = 0;
|
|
11172
|
-
for (const byte of bytes) {
|
|
11173
|
-
acc = acc << 8 | byte;
|
|
11174
|
-
bits += 8;
|
|
11175
|
-
while (bits >= 6) {
|
|
11176
|
-
bits -= 6;
|
|
11177
|
-
result += IFC_CHARS[acc >> bits & 63] ?? "";
|
|
11178
|
-
}
|
|
11179
|
-
}
|
|
11180
|
-
if (bits > 0) result += IFC_CHARS[acc << 6 - bits & 63] ?? "";
|
|
11181
|
-
return result;
|
|
11182
|
-
}
|
|
11183
|
-
//#endregion
|
|
11184
11813
|
//#region src/validation/schemaCheck.ts
|
|
11185
11814
|
/**
|
|
11186
11815
|
* EXPRESS/STEP self-validation gate.
|
|
@@ -11197,7 +11826,7 @@ function encodeIfcGuid(bytes) {
|
|
|
11197
11826
|
async function checkSchema(bytes) {
|
|
11198
11827
|
if (bytes.byteLength === 0) return appendIssue(emptyReport(), issue("error", "EMPTY_MODEL", "IFC byte buffer is empty; nothing to validate"));
|
|
11199
11828
|
const api = new web_ifc.IfcAPI();
|
|
11200
|
-
await api
|
|
11829
|
+
await initIfcApi(api);
|
|
11201
11830
|
let modelId;
|
|
11202
11831
|
try {
|
|
11203
11832
|
modelId = api.OpenModel(bytes);
|
|
@@ -11291,7 +11920,7 @@ var KEY_ENTITY_TYPES = [
|
|
|
11291
11920
|
*/
|
|
11292
11921
|
async function firstPassCounts(bytes) {
|
|
11293
11922
|
const api = new web_ifc.IfcAPI();
|
|
11294
|
-
await api
|
|
11923
|
+
await initIfcApi(api);
|
|
11295
11924
|
const modelId = api.OpenModel(bytes);
|
|
11296
11925
|
try {
|
|
11297
11926
|
return collectCounts(api, modelId);
|
|
@@ -11305,7 +11934,7 @@ async function firstPassCounts(bytes) {
|
|
|
11305
11934
|
*/
|
|
11306
11935
|
async function secondPassCounts(bytes) {
|
|
11307
11936
|
const api = new web_ifc.IfcAPI();
|
|
11308
|
-
await api
|
|
11937
|
+
await initIfcApi(api);
|
|
11309
11938
|
const sourceModelId = api.OpenModel(bytes);
|
|
11310
11939
|
let resaved;
|
|
11311
11940
|
try {
|
|
@@ -11369,7 +11998,11 @@ async function checkRoundTrip(bytes) {
|
|
|
11369
11998
|
async function toIfc(model, meta) {
|
|
11370
11999
|
const project = model.getProject();
|
|
11371
12000
|
if (!project) return (0, brepjs.err)(ifcError("NO_PROJECT", "BimModel has no project — call model.init() first"));
|
|
11372
|
-
const
|
|
12001
|
+
const authorName = [meta.author?.givenName, meta.author?.familyName].filter((p) => Boolean(p)).join(" ");
|
|
12002
|
+
const writerResult = await IfcWriter.create(meta.mvdViewDefinition, meta.ifcSchema, {
|
|
12003
|
+
author: authorName,
|
|
12004
|
+
organization: meta.organizationName
|
|
12005
|
+
});
|
|
11373
12006
|
if (!writerResult.ok) return writerResult;
|
|
11374
12007
|
const w = writerResult.value;
|
|
11375
12008
|
w.setModelScope(project.guid);
|
|
@@ -11513,7 +12146,8 @@ async function toIfc(model, meta) {
|
|
|
11513
12146
|
for (const [i, roof] of roofs.entries()) {
|
|
11514
12147
|
const containingId = findContainerOf(roof.localId, relationships);
|
|
11515
12148
|
const storeyPlacementId = containingId !== null ? placementMap.get(containingId) ?? null : null;
|
|
11516
|
-
const { localPlacementId, productDefinitionShapeId } = writeRoofGeometry(w, roof.spec, geomSubContextId, storeyPlacementId);
|
|
12149
|
+
const { localPlacementId, productDefinitionShapeId, usedFallback } = writeRoofGeometry(w, roof.spec, roof.geometry, geomSubContextId, storeyPlacementId);
|
|
12150
|
+
if (usedFallback) console.warn(`Roof ${i + 1} tessellation failed; IFC body is a degenerate fallback.`);
|
|
11517
12151
|
const roofExpressId = writeRoofEntity(w, roof.guid, `Roof ${i + 1}`, roof.spec.predefinedType, ownerHistoryId, localPlacementId, productDefinitionShapeId);
|
|
11518
12152
|
idMap.set(roof.localId, roofExpressId);
|
|
11519
12153
|
placementMap.set(roof.localId, localPlacementId);
|
|
@@ -11585,14 +12219,15 @@ async function toIfc(model, meta) {
|
|
|
11585
12219
|
for (const [i, railing] of railings.entries()) {
|
|
11586
12220
|
const containingId = findContainerOf(railing.localId, relationships);
|
|
11587
12221
|
const storeyPlacementId = containingId !== null ? placementMap.get(containingId) ?? null : null;
|
|
11588
|
-
const { localPlacementId, productDefinitionShapeId, bodyItemId } = writeRailingGeometry(w, railing.spec, geomSubContextId, storeyPlacementId);
|
|
12222
|
+
const { localPlacementId, productDefinitionShapeId, bodyItemId, usedFallback } = writeRailingGeometry(w, railing.spec, railing.geometry, geomSubContextId, storeyPlacementId);
|
|
12223
|
+
if (usedFallback) console.warn(`Railing ${i + 1} tessellation failed; IFC body is a degenerate fallback.`);
|
|
11589
12224
|
const railingExpressId = writeRailingEntity(w, railing.guid, `Railing ${i + 1}`, railing.spec.predefinedType ?? "NOTDEFINED", ownerHistoryId, localPlacementId, productDefinitionShapeId);
|
|
11590
12225
|
idMap.set(railing.localId, railingExpressId);
|
|
11591
12226
|
placementMap.set(railing.localId, localPlacementId);
|
|
11592
12227
|
writeRailingCommonPset(w, ownerHistoryId, railingExpressId, railing.spec);
|
|
11593
12228
|
writeManufacturerPset(w, ownerHistoryId, railingExpressId, railing.spec);
|
|
11594
12229
|
if (railing.spec.customProperties !== void 0) writeCustomPsets(w, ownerHistoryId, railingExpressId, railing.spec.customProperties);
|
|
11595
|
-
applySurfaceStyle(w, model, railing.localId, bodyItemId);
|
|
12230
|
+
if (bodyItemId !== null) applySurfaceStyle(w, model, railing.localId, bodyItemId);
|
|
11596
12231
|
}
|
|
11597
12232
|
for (const [i, covering] of coverings.entries()) {
|
|
11598
12233
|
const containingId = findContainerOf(covering.localId, relationships);
|
|
@@ -12014,7 +12649,7 @@ var SpfReader = class SpfReader {
|
|
|
12014
12649
|
let api;
|
|
12015
12650
|
try {
|
|
12016
12651
|
api = new web_ifc.IfcAPI();
|
|
12017
|
-
await api
|
|
12652
|
+
await initIfcApi(api);
|
|
12018
12653
|
} catch (e) {
|
|
12019
12654
|
return (0, brepjs.err)(importError("OPEN_MODEL_FAILED", "Failed to initialize web-ifc", e));
|
|
12020
12655
|
}
|
|
@@ -12120,120 +12755,6 @@ var SpfReader = class SpfReader {
|
|
|
12120
12755
|
}
|
|
12121
12756
|
};
|
|
12122
12757
|
//#endregion
|
|
12123
|
-
//#region src/import/placement.ts
|
|
12124
|
-
/**
|
|
12125
|
-
* Metres-per-file-unit length scale read from the IfcUnitAssignment's
|
|
12126
|
-
* LENGTHUNIT. Multiply a file-unit length by this to get metres, then by 1000
|
|
12127
|
-
* for brepjs millimetres. Returns 1.0 (assume metres) when no length unit is
|
|
12128
|
-
* declared.
|
|
12129
|
-
*/
|
|
12130
|
-
function readLengthScale(reader) {
|
|
12131
|
-
const assignments = reader.getLinesOfType(web_ifc.IFCUNITASSIGNMENT);
|
|
12132
|
-
for (const assignmentId of assignments) {
|
|
12133
|
-
const units = asRefArray$1(reader.getLine(assignmentId)?.["Units"]);
|
|
12134
|
-
for (const unitId of units) {
|
|
12135
|
-
const scale = lengthScaleFromUnit(reader, unitId);
|
|
12136
|
-
if (scale !== null) return scale;
|
|
12137
|
-
}
|
|
12138
|
-
}
|
|
12139
|
-
return 1;
|
|
12140
|
-
}
|
|
12141
|
-
/**
|
|
12142
|
-
* Radians-per-file-unit plane-angle scale read from the IfcUnitAssignment's
|
|
12143
|
-
* PLANEANGLEUNIT (1 for RADIAN, ~0.0174533 for DEGREE). Defaults to 1 (the IFC
|
|
12144
|
-
* default plane-angle unit is the radian).
|
|
12145
|
-
*/
|
|
12146
|
-
function readPlaneAngleScale(reader) {
|
|
12147
|
-
for (const assignmentId of reader.getLinesOfType(web_ifc.IFCUNITASSIGNMENT)) {
|
|
12148
|
-
const units = asRefArray$1(reader.getLine(assignmentId)?.["Units"]);
|
|
12149
|
-
for (const unitId of units) {
|
|
12150
|
-
const s = planeAngleScaleFromUnit(reader, unitId);
|
|
12151
|
-
if (s !== null) return s;
|
|
12152
|
-
}
|
|
12153
|
-
}
|
|
12154
|
-
return 1;
|
|
12155
|
-
}
|
|
12156
|
-
function planeAngleScaleFromUnit(reader, unitId) {
|
|
12157
|
-
const unit = reader.getLine(unitId);
|
|
12158
|
-
if (unit === null) return null;
|
|
12159
|
-
const type = reader.getLineType(unitId);
|
|
12160
|
-
if (type === web_ifc.IFCSIUNIT) {
|
|
12161
|
-
if (enumValue(unit["UnitType"]) !== "PLANEANGLEUNIT") return null;
|
|
12162
|
-
if (enumValue(unit["Name"]) !== "RADIAN") return null;
|
|
12163
|
-
return 1;
|
|
12164
|
-
}
|
|
12165
|
-
if (type === web_ifc.IFCCONVERSIONBASEDUNIT) {
|
|
12166
|
-
if (enumValue(unit["UnitType"]) !== "PLANEANGLEUNIT") return null;
|
|
12167
|
-
const measureId = refValue$2(unit["ConversionFactor"]);
|
|
12168
|
-
if (measureId === null) return null;
|
|
12169
|
-
const factor = numericValue(reader.getLine(measureId)?.["ValueComponent"]);
|
|
12170
|
-
if (factor === null) return null;
|
|
12171
|
-
return factor;
|
|
12172
|
-
}
|
|
12173
|
-
return null;
|
|
12174
|
-
}
|
|
12175
|
-
function lengthScaleFromUnit(reader, unitId) {
|
|
12176
|
-
const unit = reader.getLine(unitId);
|
|
12177
|
-
if (unit === null) return null;
|
|
12178
|
-
const type = reader.getLineType(unitId);
|
|
12179
|
-
if (type === web_ifc.IFCSIUNIT) {
|
|
12180
|
-
if (enumValue(unit["UnitType"]) !== "LENGTHUNIT") return null;
|
|
12181
|
-
if (enumValue(unit["Name"]) !== "METRE") return null;
|
|
12182
|
-
return siPrefixFactor(enumValue(unit["Prefix"]));
|
|
12183
|
-
}
|
|
12184
|
-
if (type === web_ifc.IFCCONVERSIONBASEDUNIT) {
|
|
12185
|
-
if (enumValue(unit["UnitType"]) !== "LENGTHUNIT") return null;
|
|
12186
|
-
const measureId = refValue$2(unit["ConversionFactor"]);
|
|
12187
|
-
if (measureId === null) return null;
|
|
12188
|
-
const measure = reader.getLine(measureId);
|
|
12189
|
-
const factor = numericValue(measure?.["ValueComponent"]);
|
|
12190
|
-
if (factor === null) return null;
|
|
12191
|
-
const baseId = refValue$2(measure?.["UnitComponent"]);
|
|
12192
|
-
return factor * (baseId !== null ? lengthScaleFromUnit(reader, baseId) ?? 1 : 1);
|
|
12193
|
-
}
|
|
12194
|
-
return null;
|
|
12195
|
-
}
|
|
12196
|
-
function siPrefixFactor(prefix) {
|
|
12197
|
-
switch (prefix) {
|
|
12198
|
-
case null: return 1;
|
|
12199
|
-
case "KILO": return 1e3;
|
|
12200
|
-
case "HECTO": return 100;
|
|
12201
|
-
case "DECA": return 10;
|
|
12202
|
-
case "DECI": return .1;
|
|
12203
|
-
case "CENTI": return .01;
|
|
12204
|
-
case "MILLI": return .001;
|
|
12205
|
-
case "MICRO": return 1e-6;
|
|
12206
|
-
default: return 1;
|
|
12207
|
-
}
|
|
12208
|
-
}
|
|
12209
|
-
function refValue$2(v) {
|
|
12210
|
-
if (v === null || v === void 0) return null;
|
|
12211
|
-
if (typeof v === "number") return v;
|
|
12212
|
-
const value = v.value;
|
|
12213
|
-
return typeof value === "number" ? value : null;
|
|
12214
|
-
}
|
|
12215
|
-
function asRefArray$1(v) {
|
|
12216
|
-
if (!Array.isArray(v)) return [];
|
|
12217
|
-
const out = [];
|
|
12218
|
-
for (const item of v) {
|
|
12219
|
-
const id = refValue$2(item);
|
|
12220
|
-
if (id !== null) out.push(id);
|
|
12221
|
-
}
|
|
12222
|
-
return out;
|
|
12223
|
-
}
|
|
12224
|
-
function numericValue(v) {
|
|
12225
|
-
if (typeof v === "number") return v;
|
|
12226
|
-
if (v === null || v === void 0) return null;
|
|
12227
|
-
const value = v.value;
|
|
12228
|
-
return typeof value === "number" ? value : null;
|
|
12229
|
-
}
|
|
12230
|
-
function enumValue(v) {
|
|
12231
|
-
if (typeof v === "string") return v;
|
|
12232
|
-
if (v === null || v === void 0) return null;
|
|
12233
|
-
const value = v.value;
|
|
12234
|
-
return typeof value === "string" ? value : null;
|
|
12235
|
-
}
|
|
12236
|
-
//#endregion
|
|
12237
12758
|
//#region src/import/spatialTree.ts
|
|
12238
12759
|
var CATEGORY_BY_TYPE = new Map([
|
|
12239
12760
|
[web_ifc.IFCPROJECT, "PROJECT"],
|
|
@@ -13859,6 +14380,7 @@ var RoofSpecSchema = object({
|
|
|
13859
14380
|
fireRating: string().optional(),
|
|
13860
14381
|
thermalTransmittance: number().positive().optional(),
|
|
13861
14382
|
status: string().optional(),
|
|
14383
|
+
pitch: number().positive().max(89).optional(),
|
|
13862
14384
|
materialLayers: array(MaterialLayerSchema).optional(),
|
|
13863
14385
|
layerSetName: string().optional(),
|
|
13864
14386
|
classification: ClassificationRefSchema.optional(),
|
|
@@ -14190,6 +14712,7 @@ var RailingSpecSchema = object({
|
|
|
14190
14712
|
"HANDRAIL",
|
|
14191
14713
|
"NOTDEFINED"
|
|
14192
14714
|
]).optional(),
|
|
14715
|
+
infill: _enum(["PANEL", "POSTED"]).optional(),
|
|
14193
14716
|
materialName: string().min(1),
|
|
14194
14717
|
isExternal: boolean().optional(),
|
|
14195
14718
|
fireRating: string().optional(),
|
|
@@ -15907,10 +16430,12 @@ exports.parseSystemSpec = parseSystemSpec;
|
|
|
15907
16430
|
exports.parseWallSpec = parseWallSpec;
|
|
15908
16431
|
exports.parseWindowSpec = parseWindowSpec;
|
|
15909
16432
|
exports.parseZoneSpec = parseZoneSpec;
|
|
16433
|
+
exports.placedSolids = placedSolids;
|
|
15910
16434
|
exports.schemaSupports = schemaSupports;
|
|
15911
16435
|
exports.serializeBcfFiles = serializeBcfFiles;
|
|
15912
16436
|
exports.serializeCobieToCsv = serializeCobieToCsv;
|
|
15913
16437
|
exports.serializeCobieToJson = serializeCobieToJson;
|
|
16438
|
+
exports.setIfcWasmLocateFile = setIfcWasmLocateFile;
|
|
15914
16439
|
exports.specError = specError;
|
|
15915
16440
|
exports.templateFor = templateFor;
|
|
15916
16441
|
exports.toIfc = toIfc;
|