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.js
CHANGED
|
@@ -1,8 +1,37 @@
|
|
|
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
|
+
//#region src/identity/ifcGuid.ts
|
|
5
|
+
var IFC_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_$";
|
|
6
|
+
function newIfcGuid() {
|
|
7
|
+
const bytes = crypto.getRandomValues(new Uint8Array(16));
|
|
8
|
+
bytes[6] = (bytes[6] ?? 0) & 15 | 64;
|
|
9
|
+
bytes[8] = (bytes[8] ?? 0) & 63 | 128;
|
|
10
|
+
return encodeIfcGuid(bytes);
|
|
11
|
+
}
|
|
12
|
+
function isValidIfcGuid(s) {
|
|
13
|
+
if (s.length !== 22) return false;
|
|
14
|
+
if (!"0123".includes(s[0] ?? "")) return false;
|
|
15
|
+
for (const ch of s) if (!IFC_CHARS.includes(ch)) return false;
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
function encodeIfcGuid(bytes) {
|
|
19
|
+
let result = "";
|
|
20
|
+
let acc = 0;
|
|
21
|
+
let bits = 4;
|
|
22
|
+
for (const byte of bytes) {
|
|
23
|
+
acc = acc << 8 | byte;
|
|
24
|
+
bits += 8;
|
|
25
|
+
while (bits >= 6) {
|
|
26
|
+
bits -= 6;
|
|
27
|
+
result += IFC_CHARS[acc >> bits & 63] ?? "";
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (bits > 0) result += IFC_CHARS[acc << 6 - bits & 63] ?? "";
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
//#endregion
|
|
4
34
|
//#region src/identity/guidDerivation.ts
|
|
5
|
-
var IFC_CHARS$1 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_$";
|
|
6
35
|
var NAMESPACE = "brepjs-bim:v1";
|
|
7
36
|
var FNV_OFFSET = 2166136261;
|
|
8
37
|
var FNV_PRIME = 16777619;
|
|
@@ -36,7 +65,7 @@ function digest16(stableKey) {
|
|
|
36
65
|
* keys yield distinct, format-valid (22-char) GlobalIds.
|
|
37
66
|
*/
|
|
38
67
|
function deriveIfcGuidSync(stableKey) {
|
|
39
|
-
return encodeIfcGuid
|
|
68
|
+
return encodeIfcGuid(digest16(stableKey));
|
|
40
69
|
}
|
|
41
70
|
/**
|
|
42
71
|
* Async wrapper over {@link deriveIfcGuidSync} for callers that prefer a Promise
|
|
@@ -63,21 +92,6 @@ function makeRelKey(modelScope, kind, localId) {
|
|
|
63
92
|
function makeLineKey(modelScope, expressId) {
|
|
64
93
|
return `line:${modelScope}:${expressId}`;
|
|
65
94
|
}
|
|
66
|
-
function encodeIfcGuid$1(bytes) {
|
|
67
|
-
let result = "";
|
|
68
|
-
let acc = 0;
|
|
69
|
-
let bits = 0;
|
|
70
|
-
for (const byte of bytes) {
|
|
71
|
-
acc = acc << 8 | byte;
|
|
72
|
-
bits += 8;
|
|
73
|
-
while (bits >= 6) {
|
|
74
|
-
bits -= 6;
|
|
75
|
-
result += IFC_CHARS$1[acc >> bits & 63] ?? "";
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
if (bits > 0) result += IFC_CHARS$1[acc << 6 - bits & 63] ?? "";
|
|
79
|
-
return result;
|
|
80
|
-
}
|
|
81
95
|
//#endregion
|
|
82
96
|
//#region src/identity/localId.ts
|
|
83
97
|
function makeLocalIdCounter(start = 1) {
|
|
@@ -228,7 +242,7 @@ function _usingCtx() {
|
|
|
228
242
|
//#region src/elementFns/wallFns.ts
|
|
229
243
|
function wallToSolid(spec) {
|
|
230
244
|
try {
|
|
231
|
-
var _usingCtx$
|
|
245
|
+
var _usingCtx$19 = _usingCtx();
|
|
232
246
|
if (spec.length <= 0) return err(specError("WALL_ZERO_LENGTH", "Wall length must be positive"));
|
|
233
247
|
if (spec.height <= 0) return err(specError("WALL_ZERO_HEIGHT", "Wall height must be positive"));
|
|
234
248
|
if (spec.thickness <= 0) return err(specError("WALL_ZERO_THICKNESS", "Wall thickness must be positive"));
|
|
@@ -256,7 +270,7 @@ function wallToSolid(spec) {
|
|
|
256
270
|
]
|
|
257
271
|
]);
|
|
258
272
|
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "WALL_PROFILE_FAILED", "Failed to create wall profile"));
|
|
259
|
-
const solidResult = extrude(_usingCtx$
|
|
273
|
+
const solidResult = extrude(_usingCtx$19.u(profileResult.value), [
|
|
260
274
|
length,
|
|
261
275
|
0,
|
|
262
276
|
0
|
|
@@ -269,16 +283,16 @@ function wallToSolid(spec) {
|
|
|
269
283
|
}
|
|
270
284
|
return ok(solid);
|
|
271
285
|
} catch (_) {
|
|
272
|
-
_usingCtx$
|
|
286
|
+
_usingCtx$19.e = _;
|
|
273
287
|
} finally {
|
|
274
|
-
_usingCtx$
|
|
288
|
+
_usingCtx$19.d();
|
|
275
289
|
}
|
|
276
290
|
}
|
|
277
291
|
//#endregion
|
|
278
292
|
//#region src/elementFns/slabFns.ts
|
|
279
293
|
function slabToSolid(spec) {
|
|
280
294
|
try {
|
|
281
|
-
var _usingCtx$
|
|
295
|
+
var _usingCtx$18 = _usingCtx();
|
|
282
296
|
if (spec.length <= 0) return err(specError("SLAB_ZERO_LENGTH", "Slab length must be positive"));
|
|
283
297
|
if (spec.width <= 0) return err(specError("SLAB_ZERO_WIDTH", "Slab width must be positive"));
|
|
284
298
|
if (spec.thickness <= 0) return err(specError("SLAB_ZERO_THICKNESS", "Slab thickness must be positive"));
|
|
@@ -306,7 +320,7 @@ function slabToSolid(spec) {
|
|
|
306
320
|
]
|
|
307
321
|
]);
|
|
308
322
|
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "SLAB_PROFILE_FAILED", "Failed to create slab profile"));
|
|
309
|
-
const solidResult = extrude(_usingCtx$
|
|
323
|
+
const solidResult = extrude(_usingCtx$18.u(profileResult.value), [
|
|
310
324
|
0,
|
|
311
325
|
0,
|
|
312
326
|
thickness
|
|
@@ -319,9 +333,9 @@ function slabToSolid(spec) {
|
|
|
319
333
|
}
|
|
320
334
|
return ok(solid);
|
|
321
335
|
} catch (_) {
|
|
322
|
-
_usingCtx$
|
|
336
|
+
_usingCtx$18.e = _;
|
|
323
337
|
} finally {
|
|
324
|
-
_usingCtx$
|
|
338
|
+
_usingCtx$18.d();
|
|
325
339
|
}
|
|
326
340
|
}
|
|
327
341
|
//#endregion
|
|
@@ -4998,14 +5012,14 @@ function to3D(points) {
|
|
|
4998
5012
|
}
|
|
4999
5013
|
function extendedProfileToFace(profile) {
|
|
5000
5014
|
try {
|
|
5001
|
-
var _usingCtx$
|
|
5015
|
+
var _usingCtx$17 = _usingCtx();
|
|
5002
5016
|
const invalid = validateProfile(profile);
|
|
5003
5017
|
if (invalid !== null) return err(invalid);
|
|
5004
5018
|
const outerResult = polygon(to3D(outerLoop(profile)));
|
|
5005
5019
|
if (!outerResult.ok) return err(fromBrepError(outerResult.error, "PROFILE_FACE_FAILED", "Failed to build profile outer face"));
|
|
5006
5020
|
const holes = holeLoops(profile);
|
|
5007
5021
|
if (holes.length === 0) return ok(outerResult.value);
|
|
5008
|
-
const outerFace = _usingCtx$
|
|
5022
|
+
const outerFace = _usingCtx$17.u(outerResult.value);
|
|
5009
5023
|
const holeFaces = [];
|
|
5010
5024
|
const holeWires = [];
|
|
5011
5025
|
const disposeHoleFaces = () => {
|
|
@@ -5030,9 +5044,9 @@ function extendedProfileToFace(profile) {
|
|
|
5030
5044
|
disposeHoleFaces();
|
|
5031
5045
|
return ok(faceWithHoles);
|
|
5032
5046
|
} catch (_) {
|
|
5033
|
-
_usingCtx$
|
|
5047
|
+
_usingCtx$17.e = _;
|
|
5034
5048
|
} finally {
|
|
5035
|
-
_usingCtx$
|
|
5049
|
+
_usingCtx$17.d();
|
|
5036
5050
|
}
|
|
5037
5051
|
}
|
|
5038
5052
|
//#endregion
|
|
@@ -5070,7 +5084,8 @@ var CoreProfileSchema = discriminatedUnion("kind", [
|
|
|
5070
5084
|
overallWidth: number().positive(),
|
|
5071
5085
|
overallDepth: number().positive(),
|
|
5072
5086
|
flangeThickness: number().positive(),
|
|
5073
|
-
webThickness: number().positive()
|
|
5087
|
+
webThickness: number().positive(),
|
|
5088
|
+
filletRadius: number().positive().optional()
|
|
5074
5089
|
})
|
|
5075
5090
|
]);
|
|
5076
5091
|
var Pt2Schema = tuple([number(), number()]);
|
|
@@ -5202,6 +5217,11 @@ function parseProfile(input) {
|
|
|
5202
5217
|
if (profile.kind === "I_BEAM") {
|
|
5203
5218
|
if (2 * profile.flangeThickness >= profile.overallDepth) return err(specError("INVALID_PROFILE", "I-beam flangeThickness × 2 must be less than overallDepth"));
|
|
5204
5219
|
if (profile.webThickness >= profile.overallWidth) return err(specError("INVALID_PROFILE", "I-beam webThickness must be less than overallWidth"));
|
|
5220
|
+
if (profile.filletRadius !== void 0) {
|
|
5221
|
+
const clearHeight = profile.overallDepth / 2 - profile.flangeThickness;
|
|
5222
|
+
const clearSpan = (profile.overallWidth - profile.webThickness) / 2;
|
|
5223
|
+
if (profile.filletRadius >= clearHeight || profile.filletRadius >= clearSpan) return err(specError("INVALID_PROFILE", "I-beam filletRadius must be smaller than the clear web height and the clear span beside the web"));
|
|
5224
|
+
}
|
|
5205
5225
|
}
|
|
5206
5226
|
return ok(profile);
|
|
5207
5227
|
}
|
|
@@ -5212,8 +5232,42 @@ function profileCrossSectionArea(profile) {
|
|
|
5212
5232
|
switch (profile.kind) {
|
|
5213
5233
|
case "RECTANGULAR": return profile.width * profile.height;
|
|
5214
5234
|
case "CIRCULAR": return Math.PI * profile.radius * profile.radius;
|
|
5215
|
-
case "I_BEAM":
|
|
5235
|
+
case "I_BEAM": {
|
|
5236
|
+
const flangeArea = 2 * profile.overallWidth * profile.flangeThickness;
|
|
5237
|
+
const webArea = (profile.overallDepth - 2 * profile.flangeThickness) * profile.webThickness;
|
|
5238
|
+
const r = profile.filletRadius ?? 0;
|
|
5239
|
+
const filletArea = 4 * r * r * (1 - Math.PI / 4);
|
|
5240
|
+
return flangeArea + webArea + filletArea;
|
|
5241
|
+
}
|
|
5242
|
+
}
|
|
5243
|
+
}
|
|
5244
|
+
var FILLET_SEGMENTS = 8;
|
|
5245
|
+
var FILLET_MIN_ANGLE = .001;
|
|
5246
|
+
function filletArc(prev, v, next, r) {
|
|
5247
|
+
const aLen = Math.hypot(prev[0] - v[0], prev[1] - v[1]);
|
|
5248
|
+
const bLen = Math.hypot(next[0] - v[0], next[1] - v[1]);
|
|
5249
|
+
const a = [(prev[0] - v[0]) / aLen, (prev[1] - v[1]) / aLen];
|
|
5250
|
+
const b = [(next[0] - v[0]) / bLen, (next[1] - v[1]) / bLen];
|
|
5251
|
+
const alpha = Math.acos(Math.max(-1, Math.min(1, a[0] * b[0] + a[1] * b[1])));
|
|
5252
|
+
if (alpha < FILLET_MIN_ANGLE || alpha > Math.PI - FILLET_MIN_ANGLE) return [[v[0], v[1]]];
|
|
5253
|
+
const setback = r / Math.tan(alpha / 2);
|
|
5254
|
+
const center = r / Math.sin(alpha / 2);
|
|
5255
|
+
const bisLen = Math.hypot(a[0] + b[0], a[1] + b[1]);
|
|
5256
|
+
const bis = [(a[0] + b[0]) / bisLen, (a[1] + b[1]) / bisLen];
|
|
5257
|
+
const cx = v[0] + bis[0] * center;
|
|
5258
|
+
const cy = v[1] + bis[1] * center;
|
|
5259
|
+
const p1 = [v[0] + a[0] * setback, v[1] + a[1] * setback];
|
|
5260
|
+
const p2 = [v[0] + b[0] * setback, v[1] + b[1] * setback];
|
|
5261
|
+
const a1 = Math.atan2(p1[1] - cy, p1[0] - cx);
|
|
5262
|
+
let delta = Math.atan2(p2[1] - cy, p2[0] - cx) - a1;
|
|
5263
|
+
while (delta <= -Math.PI) delta += 2 * Math.PI;
|
|
5264
|
+
while (delta > Math.PI) delta -= 2 * Math.PI;
|
|
5265
|
+
const out = [];
|
|
5266
|
+
for (let i = 0; i <= FILLET_SEGMENTS; i++) {
|
|
5267
|
+
const ang = a1 + delta * i / FILLET_SEGMENTS;
|
|
5268
|
+
out.push([cx + r * Math.cos(ang), cy + r * Math.sin(ang)]);
|
|
5216
5269
|
}
|
|
5270
|
+
return out;
|
|
5217
5271
|
}
|
|
5218
5272
|
function profileToPolygon(profile, circleSegments = 32) {
|
|
5219
5273
|
if (isExtendedProfile(profile)) return err(specError("EXTENDED_PROFILE_NO_POLYGON", `profileToPolygon: extended profile kind '${profile.kind}' has no single-polygon outline; use extendedProfileToFace()`));
|
|
@@ -5262,68 +5316,50 @@ function profileToPolygon(profile, circleSegments = 32) {
|
|
|
5262
5316
|
const halfD = profile.overallDepth / 2;
|
|
5263
5317
|
const halfWeb = profile.webThickness / 2;
|
|
5264
5318
|
const flangeInnerY = halfD - profile.flangeThickness;
|
|
5265
|
-
|
|
5266
|
-
[
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
],
|
|
5271
|
-
[
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
],
|
|
5276
|
-
[
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
],
|
|
5286
|
-
[
|
|
5287
|
-
halfWeb,
|
|
5288
|
-
flangeInnerY,
|
|
5289
|
-
0
|
|
5290
|
-
],
|
|
5291
|
-
[
|
|
5292
|
-
halfW,
|
|
5293
|
-
flangeInnerY,
|
|
5294
|
-
0
|
|
5295
|
-
],
|
|
5296
|
-
[
|
|
5297
|
-
halfW,
|
|
5298
|
-
halfD,
|
|
5299
|
-
0
|
|
5300
|
-
],
|
|
5301
|
-
[
|
|
5302
|
-
-halfW,
|
|
5303
|
-
halfD,
|
|
5304
|
-
0
|
|
5305
|
-
],
|
|
5306
|
-
[
|
|
5307
|
-
-halfW,
|
|
5308
|
-
flangeInnerY,
|
|
5309
|
-
0
|
|
5310
|
-
],
|
|
5311
|
-
[
|
|
5312
|
-
-halfWeb,
|
|
5313
|
-
flangeInnerY,
|
|
5314
|
-
0
|
|
5315
|
-
],
|
|
5316
|
-
[
|
|
5317
|
-
-halfWeb,
|
|
5318
|
-
-flangeInnerY,
|
|
5319
|
-
0
|
|
5320
|
-
],
|
|
5321
|
-
[
|
|
5322
|
-
-halfW,
|
|
5323
|
-
-flangeInnerY,
|
|
5324
|
-
0
|
|
5325
|
-
]
|
|
5319
|
+
const v = [
|
|
5320
|
+
[-halfW, -halfD],
|
|
5321
|
+
[halfW, -halfD],
|
|
5322
|
+
[halfW, -flangeInnerY],
|
|
5323
|
+
[halfWeb, -flangeInnerY],
|
|
5324
|
+
[halfWeb, flangeInnerY],
|
|
5325
|
+
[halfW, flangeInnerY],
|
|
5326
|
+
[halfW, halfD],
|
|
5327
|
+
[-halfW, halfD],
|
|
5328
|
+
[-halfW, flangeInnerY],
|
|
5329
|
+
[-halfWeb, flangeInnerY],
|
|
5330
|
+
[-halfWeb, -flangeInnerY],
|
|
5331
|
+
[-halfW, -flangeInnerY]
|
|
5332
|
+
];
|
|
5333
|
+
const r = profile.filletRadius ?? 0;
|
|
5334
|
+
const rootCorners = new Set([
|
|
5335
|
+
3,
|
|
5336
|
+
4,
|
|
5337
|
+
9,
|
|
5338
|
+
10
|
|
5326
5339
|
]);
|
|
5340
|
+
const pts = [];
|
|
5341
|
+
for (let i = 0; i < v.length; i++) {
|
|
5342
|
+
const cur = v[i];
|
|
5343
|
+
if (cur === void 0) continue;
|
|
5344
|
+
if (r > 0 && rootCorners.has(i)) {
|
|
5345
|
+
const prev = v[(i - 1 + v.length) % v.length];
|
|
5346
|
+
const next = v[(i + 1) % v.length];
|
|
5347
|
+
if (prev !== void 0 && next !== void 0) {
|
|
5348
|
+
for (const [ax, ay] of filletArc(prev, cur, next, r)) pts.push([
|
|
5349
|
+
ax,
|
|
5350
|
+
ay,
|
|
5351
|
+
0
|
|
5352
|
+
]);
|
|
5353
|
+
continue;
|
|
5354
|
+
}
|
|
5355
|
+
}
|
|
5356
|
+
pts.push([
|
|
5357
|
+
cur[0],
|
|
5358
|
+
cur[1],
|
|
5359
|
+
0
|
|
5360
|
+
]);
|
|
5361
|
+
}
|
|
5362
|
+
return ok(pts);
|
|
5327
5363
|
}
|
|
5328
5364
|
}
|
|
5329
5365
|
}
|
|
@@ -5331,7 +5367,7 @@ function profileToPolygon(profile, circleSegments = 32) {
|
|
|
5331
5367
|
//#region src/elementFns/beamFns.ts
|
|
5332
5368
|
function beamToSolid(spec) {
|
|
5333
5369
|
try {
|
|
5334
|
-
var _usingCtx$
|
|
5370
|
+
var _usingCtx$16 = _usingCtx();
|
|
5335
5371
|
if (spec.length <= 0) return err(specError("BEAM_ZERO_LENGTH", "Beam length must be positive"));
|
|
5336
5372
|
if (isExtendedProfile(spec.profile)) try {
|
|
5337
5373
|
var _usingCtx3 = _usingCtx();
|
|
@@ -5366,7 +5402,7 @@ function beamToSolid(spec) {
|
|
|
5366
5402
|
py
|
|
5367
5403
|
]));
|
|
5368
5404
|
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "BEAM_PROFILE_FAILED", "Failed to create beam profile"));
|
|
5369
|
-
const solidResult = extrude(_usingCtx$
|
|
5405
|
+
const solidResult = extrude(_usingCtx$16.u(profileResult.value), [
|
|
5370
5406
|
spec.length,
|
|
5371
5407
|
0,
|
|
5372
5408
|
0
|
|
@@ -5379,16 +5415,16 @@ function beamToSolid(spec) {
|
|
|
5379
5415
|
}
|
|
5380
5416
|
return ok(solid);
|
|
5381
5417
|
} catch (_) {
|
|
5382
|
-
_usingCtx$
|
|
5418
|
+
_usingCtx$16.e = _;
|
|
5383
5419
|
} finally {
|
|
5384
|
-
_usingCtx$
|
|
5420
|
+
_usingCtx$16.d();
|
|
5385
5421
|
}
|
|
5386
5422
|
}
|
|
5387
5423
|
//#endregion
|
|
5388
5424
|
//#region src/elementFns/columnFns.ts
|
|
5389
5425
|
function columnToSolid(spec) {
|
|
5390
5426
|
try {
|
|
5391
|
-
var _usingCtx$
|
|
5427
|
+
var _usingCtx$15 = _usingCtx();
|
|
5392
5428
|
if (spec.height <= 0) return err(specError("COLUMN_ZERO_HEIGHT", "Column height must be positive"));
|
|
5393
5429
|
if (isExtendedProfile(spec.profile)) try {
|
|
5394
5430
|
var _usingCtx3 = _usingCtx();
|
|
@@ -5416,7 +5452,7 @@ function columnToSolid(spec) {
|
|
|
5416
5452
|
const profilePts = profilePtsResult.value;
|
|
5417
5453
|
const profileResult = polygon(profilePts);
|
|
5418
5454
|
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "COLUMN_PROFILE_FAILED", "Failed to create column profile"));
|
|
5419
|
-
const solidResult = extrude(_usingCtx$
|
|
5455
|
+
const solidResult = extrude(_usingCtx$15.u(profileResult.value), [
|
|
5420
5456
|
0,
|
|
5421
5457
|
0,
|
|
5422
5458
|
spec.height
|
|
@@ -5429,9 +5465,9 @@ function columnToSolid(spec) {
|
|
|
5429
5465
|
}
|
|
5430
5466
|
return ok(solid);
|
|
5431
5467
|
} catch (_) {
|
|
5432
|
-
_usingCtx$
|
|
5468
|
+
_usingCtx$15.e = _;
|
|
5433
5469
|
} finally {
|
|
5434
|
-
_usingCtx$
|
|
5470
|
+
_usingCtx$15.d();
|
|
5435
5471
|
}
|
|
5436
5472
|
}
|
|
5437
5473
|
//#endregion
|
|
@@ -5439,7 +5475,7 @@ function columnToSolid(spec) {
|
|
|
5439
5475
|
var EPSILON_MM$1 = 1;
|
|
5440
5476
|
function openingToSolid(spec, wallThickness) {
|
|
5441
5477
|
try {
|
|
5442
|
-
var _usingCtx$
|
|
5478
|
+
var _usingCtx$14 = _usingCtx();
|
|
5443
5479
|
if (spec.width <= 0) return err(specError("OPENING_ZERO_WIDTH", "Opening width must be positive"));
|
|
5444
5480
|
if (spec.height <= 0) return err(specError("OPENING_ZERO_HEIGHT", "Opening height must be positive"));
|
|
5445
5481
|
if (wallThickness <= 0) return err(specError("OPENING_ZERO_WALL_THICKNESS", "Wall thickness must be positive"));
|
|
@@ -5471,7 +5507,7 @@ function openingToSolid(spec, wallThickness) {
|
|
|
5471
5507
|
]
|
|
5472
5508
|
]);
|
|
5473
5509
|
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "OPENING_PROFILE_FAILED", "Failed to create opening profile"));
|
|
5474
|
-
const solidResult = extrude(_usingCtx$
|
|
5510
|
+
const solidResult = extrude(_usingCtx$14.u(profileResult.value), [
|
|
5475
5511
|
spec.width,
|
|
5476
5512
|
0,
|
|
5477
5513
|
0
|
|
@@ -5484,9 +5520,9 @@ function openingToSolid(spec, wallThickness) {
|
|
|
5484
5520
|
}
|
|
5485
5521
|
return ok(solid);
|
|
5486
5522
|
} catch (_) {
|
|
5487
|
-
_usingCtx$
|
|
5523
|
+
_usingCtx$14.e = _;
|
|
5488
5524
|
} finally {
|
|
5489
|
-
_usingCtx$
|
|
5525
|
+
_usingCtx$14.d();
|
|
5490
5526
|
}
|
|
5491
5527
|
}
|
|
5492
5528
|
//#endregion
|
|
@@ -5494,7 +5530,7 @@ function openingToSolid(spec, wallThickness) {
|
|
|
5494
5530
|
var EPSILON_MM = 1;
|
|
5495
5531
|
function slabOpeningToSolid(spec, slabThickness) {
|
|
5496
5532
|
try {
|
|
5497
|
-
var _usingCtx$
|
|
5533
|
+
var _usingCtx$13 = _usingCtx();
|
|
5498
5534
|
if (spec.sizeX <= 0) return err(specError("SLAB_OPENING_ZERO_SIZE_X", "Slab opening sizeX must be positive"));
|
|
5499
5535
|
if (spec.sizeY <= 0) return err(specError("SLAB_OPENING_ZERO_SIZE_Y", "Slab opening sizeY must be positive"));
|
|
5500
5536
|
if (slabThickness <= 0) return err(specError("SLAB_OPENING_ZERO_SLAB_THICKNESS", "Slab thickness must be positive"));
|
|
@@ -5526,7 +5562,7 @@ function slabOpeningToSolid(spec, slabThickness) {
|
|
|
5526
5562
|
]
|
|
5527
5563
|
]);
|
|
5528
5564
|
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "SLAB_OPENING_PROFILE_FAILED", "Failed to create slab opening profile"));
|
|
5529
|
-
const solidResult = extrude(_usingCtx$
|
|
5565
|
+
const solidResult = extrude(_usingCtx$13.u(profileResult.value), [
|
|
5530
5566
|
0,
|
|
5531
5567
|
0,
|
|
5532
5568
|
slabThickness + 2 * EPSILON_MM
|
|
@@ -5539,16 +5575,16 @@ function slabOpeningToSolid(spec, slabThickness) {
|
|
|
5539
5575
|
}
|
|
5540
5576
|
return ok(solid);
|
|
5541
5577
|
} catch (_) {
|
|
5542
|
-
_usingCtx$
|
|
5578
|
+
_usingCtx$13.e = _;
|
|
5543
5579
|
} finally {
|
|
5544
|
-
_usingCtx$
|
|
5580
|
+
_usingCtx$13.d();
|
|
5545
5581
|
}
|
|
5546
5582
|
}
|
|
5547
5583
|
//#endregion
|
|
5548
5584
|
//#region src/elementFns/spaceFns.ts
|
|
5549
5585
|
function spaceToSolid(spec) {
|
|
5550
5586
|
try {
|
|
5551
|
-
var _usingCtx$
|
|
5587
|
+
var _usingCtx$12 = _usingCtx();
|
|
5552
5588
|
if (spec.length <= 0) return err(specError("SPACE_ZERO_LENGTH", "Space length must be positive"));
|
|
5553
5589
|
if (spec.width <= 0) return err(specError("SPACE_ZERO_WIDTH", "Space width must be positive"));
|
|
5554
5590
|
if (spec.height <= 0) return err(specError("SPACE_ZERO_HEIGHT", "Space height must be positive"));
|
|
@@ -5576,7 +5612,7 @@ function spaceToSolid(spec) {
|
|
|
5576
5612
|
]
|
|
5577
5613
|
]);
|
|
5578
5614
|
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "SPACE_PROFILE_FAILED", "Failed to create space footprint"));
|
|
5579
|
-
const solidResult = extrude(_usingCtx$
|
|
5615
|
+
const solidResult = extrude(_usingCtx$12.u(profileResult.value), [
|
|
5580
5616
|
0,
|
|
5581
5617
|
0,
|
|
5582
5618
|
height
|
|
@@ -5589,21 +5625,26 @@ function spaceToSolid(spec) {
|
|
|
5589
5625
|
}
|
|
5590
5626
|
return ok(solid);
|
|
5591
5627
|
} catch (_) {
|
|
5592
|
-
_usingCtx$
|
|
5628
|
+
_usingCtx$12.e = _;
|
|
5593
5629
|
} finally {
|
|
5594
|
-
_usingCtx$
|
|
5630
|
+
_usingCtx$12.d();
|
|
5595
5631
|
}
|
|
5596
5632
|
}
|
|
5597
5633
|
//#endregion
|
|
5598
5634
|
//#region src/elementFns/roofFns.ts
|
|
5599
|
-
|
|
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) {
|
|
5600
5644
|
try {
|
|
5601
|
-
var _usingCtx$
|
|
5602
|
-
if (spec.length <= 0) return err(specError("ROOF_ZERO_LENGTH", "Roof length must be positive"));
|
|
5603
|
-
if (spec.width <= 0) return err(specError("ROOF_ZERO_WIDTH", "Roof width must be positive"));
|
|
5604
|
-
if (spec.thickness <= 0) return err(specError("ROOF_ZERO_THICKNESS", "Roof thickness must be positive"));
|
|
5645
|
+
var _usingCtx$11 = _usingCtx();
|
|
5605
5646
|
const { length, width, thickness } = spec;
|
|
5606
|
-
const
|
|
5647
|
+
const face = polygon([
|
|
5607
5648
|
[
|
|
5608
5649
|
0,
|
|
5609
5650
|
0,
|
|
@@ -5625,115 +5666,297 @@ function roofToSolid(spec) {
|
|
|
5625
5666
|
0
|
|
5626
5667
|
]
|
|
5627
5668
|
]);
|
|
5628
|
-
if (!
|
|
5629
|
-
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), [
|
|
5630
5671
|
0,
|
|
5631
5672
|
0,
|
|
5632
5673
|
thickness
|
|
5633
5674
|
]);
|
|
5634
|
-
if (!
|
|
5635
|
-
|
|
5636
|
-
if (!isValidSolid(solid)) {
|
|
5637
|
-
solid[Symbol.dispose]();
|
|
5638
|
-
return err(geometryError("ROOF_INVALID_SOLID", "Extruded roof solid failed validity check"));
|
|
5639
|
-
}
|
|
5640
|
-
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");
|
|
5641
5677
|
} catch (_) {
|
|
5642
|
-
_usingCtx$
|
|
5678
|
+
_usingCtx$11.e = _;
|
|
5643
5679
|
} finally {
|
|
5644
|
-
_usingCtx$
|
|
5680
|
+
_usingCtx$11.d();
|
|
5645
5681
|
}
|
|
5646
5682
|
}
|
|
5647
|
-
|
|
5648
|
-
//#region src/elementFns/curtainWallFns.ts
|
|
5649
|
-
function boxSolid(sizeX, sizeY, sizeZ) {
|
|
5683
|
+
function shedRoof(spec, pitch) {
|
|
5650
5684
|
try {
|
|
5651
|
-
var
|
|
5652
|
-
const
|
|
5685
|
+
var _usingCtx3 = _usingCtx();
|
|
5686
|
+
const { length, width, thickness } = spec;
|
|
5687
|
+
const rise = width * Math.tan(pitch * DEG2RAD);
|
|
5688
|
+
const face = polygon([
|
|
5653
5689
|
[
|
|
5654
5690
|
0,
|
|
5655
5691
|
0,
|
|
5656
5692
|
0
|
|
5657
5693
|
],
|
|
5658
5694
|
[
|
|
5659
|
-
sizeX,
|
|
5660
5695
|
0,
|
|
5696
|
+
width,
|
|
5661
5697
|
0
|
|
5662
5698
|
],
|
|
5663
5699
|
[
|
|
5664
|
-
|
|
5665
|
-
|
|
5666
|
-
|
|
5700
|
+
0,
|
|
5701
|
+
width,
|
|
5702
|
+
thickness + rise
|
|
5667
5703
|
],
|
|
5668
5704
|
[
|
|
5669
5705
|
0,
|
|
5670
|
-
|
|
5671
|
-
|
|
5706
|
+
0,
|
|
5707
|
+
thickness
|
|
5672
5708
|
]
|
|
5673
5709
|
]);
|
|
5674
|
-
if (!
|
|
5675
|
-
const
|
|
5676
|
-
|
|
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,
|
|
5677
5713
|
0,
|
|
5678
|
-
|
|
5714
|
+
0
|
|
5679
5715
|
]);
|
|
5680
|
-
if (!
|
|
5681
|
-
|
|
5682
|
-
if (!isValidSolid(solid)) {
|
|
5683
|
-
solid[Symbol.dispose]();
|
|
5684
|
-
return err(geometryError("CURTAIN_WALL_INVALID_SOLID", "Extruded curtain wall component failed validity check"));
|
|
5685
|
-
}
|
|
5686
|
-
return ok(solid);
|
|
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");
|
|
5687
5718
|
} catch (_) {
|
|
5688
|
-
|
|
5719
|
+
_usingCtx3.e = _;
|
|
5689
5720
|
} finally {
|
|
5690
|
-
|
|
5721
|
+
_usingCtx3.d();
|
|
5691
5722
|
}
|
|
5692
5723
|
}
|
|
5693
|
-
function
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
* adjacent mullions. The mullion section is centred on each grid line.
|
|
5701
|
-
*
|
|
5702
|
-
* Geometry is laid out in the wall's local frame: X across the wall, Z up, Y
|
|
5703
|
-
* through the depth. Each component's local-origin solid is returned alongside
|
|
5704
|
-
* its placement origin so the IFC writer can emit one IfcLocalPlacement per
|
|
5705
|
-
* component.
|
|
5706
|
-
*/
|
|
5707
|
-
function curtainWallToGrid(spec) {
|
|
5708
|
-
const { width, height, columns, rows, panelThickness, mullionWidth, mullionDepth } = spec;
|
|
5709
|
-
const cellWidth = width / columns;
|
|
5710
|
-
const cellHeight = height / rows;
|
|
5711
|
-
const halfMullion = mullionWidth / 2;
|
|
5712
|
-
const panelWidth = cellWidth - mullionWidth;
|
|
5713
|
-
const panelHeight = cellHeight - mullionWidth;
|
|
5714
|
-
if (panelWidth <= 0 || panelHeight <= 0) return err(geometryError("CURTAIN_WALL_DEGENERATE_PANEL", "mullionWidth leaves no room for panels in the grid"));
|
|
5715
|
-
const panels = [];
|
|
5716
|
-
const mullions = [];
|
|
5717
|
-
for (let c = 0; c < columns; c++) for (let r = 0; r < rows; r++) {
|
|
5718
|
-
const solidResult = boxSolid(panelWidth, panelThickness, panelHeight);
|
|
5719
|
-
if (!solidResult.ok) {
|
|
5720
|
-
disposeComponents(panels);
|
|
5721
|
-
disposeComponents(mullions);
|
|
5722
|
-
return err(solidResult.error);
|
|
5723
|
-
}
|
|
5724
|
-
panels.push({
|
|
5725
|
-
origin: [
|
|
5726
|
-
c * cellWidth + halfMullion,
|
|
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
|
+
[
|
|
5727
5731
|
0,
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
size: [
|
|
5731
|
-
panelWidth,
|
|
5732
|
-
panelThickness,
|
|
5733
|
-
panelHeight
|
|
5732
|
+
0,
|
|
5733
|
+
0
|
|
5734
5734
|
],
|
|
5735
|
-
|
|
5736
|
-
|
|
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);
|
|
5868
|
+
}
|
|
5869
|
+
}
|
|
5870
|
+
//#endregion
|
|
5871
|
+
//#region src/elementFns/curtainWallFns.ts
|
|
5872
|
+
function boxSolid(sizeX, sizeY, sizeZ) {
|
|
5873
|
+
try {
|
|
5874
|
+
var _usingCtx$10 = _usingCtx();
|
|
5875
|
+
const profileResult = polygon([
|
|
5876
|
+
[
|
|
5877
|
+
0,
|
|
5878
|
+
0,
|
|
5879
|
+
0
|
|
5880
|
+
],
|
|
5881
|
+
[
|
|
5882
|
+
sizeX,
|
|
5883
|
+
0,
|
|
5884
|
+
0
|
|
5885
|
+
],
|
|
5886
|
+
[
|
|
5887
|
+
sizeX,
|
|
5888
|
+
sizeY,
|
|
5889
|
+
0
|
|
5890
|
+
],
|
|
5891
|
+
[
|
|
5892
|
+
0,
|
|
5893
|
+
sizeY,
|
|
5894
|
+
0
|
|
5895
|
+
]
|
|
5896
|
+
]);
|
|
5897
|
+
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "CURTAIN_WALL_PROFILE_FAILED", "Failed to create component profile"));
|
|
5898
|
+
const solidResult = extrude(_usingCtx$10.u(profileResult.value), [
|
|
5899
|
+
0,
|
|
5900
|
+
0,
|
|
5901
|
+
sizeZ
|
|
5902
|
+
]);
|
|
5903
|
+
if (!solidResult.ok) return err(fromBrepError(solidResult.error, "CURTAIN_WALL_EXTRUDE_FAILED", "Failed to extrude component"));
|
|
5904
|
+
const solid = solidResult.value;
|
|
5905
|
+
if (!isValidSolid(solid)) {
|
|
5906
|
+
solid[Symbol.dispose]();
|
|
5907
|
+
return err(geometryError("CURTAIN_WALL_INVALID_SOLID", "Extruded curtain wall component failed validity check"));
|
|
5908
|
+
}
|
|
5909
|
+
return ok(solid);
|
|
5910
|
+
} catch (_) {
|
|
5911
|
+
_usingCtx$10.e = _;
|
|
5912
|
+
} finally {
|
|
5913
|
+
_usingCtx$10.d();
|
|
5914
|
+
}
|
|
5915
|
+
}
|
|
5916
|
+
function disposeComponents(components) {
|
|
5917
|
+
for (const c of components) c.solid[Symbol.dispose]();
|
|
5918
|
+
}
|
|
5919
|
+
/**
|
|
5920
|
+
* Decomposes a curtain wall spec into its panel (plate) and mullion (member)
|
|
5921
|
+
* geometry. Mullions run along every vertical grid line (columns + 1 of them)
|
|
5922
|
+
* and every horizontal grid line (rows + 1); panels fill the cells between
|
|
5923
|
+
* adjacent mullions. The mullion section is centred on each grid line.
|
|
5924
|
+
*
|
|
5925
|
+
* Geometry is laid out in the wall's local frame: X across the wall, Z up, Y
|
|
5926
|
+
* through the depth. Each component's local-origin solid is returned alongside
|
|
5927
|
+
* its placement origin so the IFC writer can emit one IfcLocalPlacement per
|
|
5928
|
+
* component.
|
|
5929
|
+
*/
|
|
5930
|
+
function curtainWallToGrid(spec) {
|
|
5931
|
+
const { width, height, columns, rows, panelThickness, mullionWidth, mullionDepth } = spec;
|
|
5932
|
+
const cellWidth = width / columns;
|
|
5933
|
+
const cellHeight = height / rows;
|
|
5934
|
+
const halfMullion = mullionWidth / 2;
|
|
5935
|
+
const panelWidth = cellWidth - mullionWidth;
|
|
5936
|
+
const panelHeight = cellHeight - mullionWidth;
|
|
5937
|
+
if (panelWidth <= 0 || panelHeight <= 0) return err(geometryError("CURTAIN_WALL_DEGENERATE_PANEL", "mullionWidth leaves no room for panels in the grid"));
|
|
5938
|
+
const panels = [];
|
|
5939
|
+
const mullions = [];
|
|
5940
|
+
for (let c = 0; c < columns; c++) for (let r = 0; r < rows; r++) {
|
|
5941
|
+
const solidResult = boxSolid(panelWidth, panelThickness, panelHeight);
|
|
5942
|
+
if (!solidResult.ok) {
|
|
5943
|
+
disposeComponents(panels);
|
|
5944
|
+
disposeComponents(mullions);
|
|
5945
|
+
return err(solidResult.error);
|
|
5946
|
+
}
|
|
5947
|
+
panels.push({
|
|
5948
|
+
origin: [
|
|
5949
|
+
c * cellWidth + halfMullion,
|
|
5950
|
+
0,
|
|
5951
|
+
r * cellHeight + halfMullion
|
|
5952
|
+
],
|
|
5953
|
+
size: [
|
|
5954
|
+
panelWidth,
|
|
5955
|
+
panelThickness,
|
|
5956
|
+
panelHeight
|
|
5957
|
+
],
|
|
5958
|
+
solid: solidResult.value
|
|
5959
|
+
});
|
|
5737
5960
|
}
|
|
5738
5961
|
for (let c = 0; c <= columns; c++) {
|
|
5739
5962
|
const solidResult = boxSolid(mullionWidth, mullionDepth, height);
|
|
@@ -5786,7 +6009,7 @@ function curtainWallToGrid(spec) {
|
|
|
5786
6009
|
//#region src/elementFns/foundationFns.ts
|
|
5787
6010
|
function footingToSolid(spec) {
|
|
5788
6011
|
try {
|
|
5789
|
-
var _usingCtx$
|
|
6012
|
+
var _usingCtx$9 = _usingCtx();
|
|
5790
6013
|
if (spec.length <= 0) return err(specError("FOOTING_ZERO_LENGTH", "Footing length must be positive"));
|
|
5791
6014
|
if (spec.width <= 0) return err(specError("FOOTING_ZERO_WIDTH", "Footing width must be positive"));
|
|
5792
6015
|
if (spec.thickness <= 0) return err(specError("FOOTING_ZERO_THICKNESS", "Footing thickness must be positive"));
|
|
@@ -5814,7 +6037,7 @@ function footingToSolid(spec) {
|
|
|
5814
6037
|
]
|
|
5815
6038
|
]);
|
|
5816
6039
|
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "FOOTING_PROFILE_FAILED", "Failed to create footing profile"));
|
|
5817
|
-
const solidResult = extrude(_usingCtx$
|
|
6040
|
+
const solidResult = extrude(_usingCtx$9.u(profileResult.value), [
|
|
5818
6041
|
0,
|
|
5819
6042
|
0,
|
|
5820
6043
|
thickness
|
|
@@ -5827,9 +6050,9 @@ function footingToSolid(spec) {
|
|
|
5827
6050
|
}
|
|
5828
6051
|
return ok(solid);
|
|
5829
6052
|
} catch (_) {
|
|
5830
|
-
_usingCtx$
|
|
6053
|
+
_usingCtx$9.e = _;
|
|
5831
6054
|
} finally {
|
|
5832
|
-
_usingCtx$
|
|
6055
|
+
_usingCtx$9.d();
|
|
5833
6056
|
}
|
|
5834
6057
|
}
|
|
5835
6058
|
function pileToSolid(spec) {
|
|
@@ -5882,12 +6105,9 @@ function pileToSolid(spec) {
|
|
|
5882
6105
|
}
|
|
5883
6106
|
//#endregion
|
|
5884
6107
|
//#region src/elementFns/railingFns.ts
|
|
5885
|
-
function
|
|
6108
|
+
function panelRailing(spec) {
|
|
5886
6109
|
try {
|
|
5887
|
-
var _usingCtx$
|
|
5888
|
-
if (spec.length <= 0) return err(specError("RAILING_ZERO_LENGTH", "Railing length must be positive"));
|
|
5889
|
-
if (spec.height <= 0) return err(specError("RAILING_ZERO_HEIGHT", "Railing height must be positive"));
|
|
5890
|
-
if (spec.thickness <= 0) return err(specError("RAILING_ZERO_THICKNESS", "Railing thickness must be positive"));
|
|
6110
|
+
var _usingCtx$8 = _usingCtx();
|
|
5891
6111
|
const { length, height, thickness } = spec;
|
|
5892
6112
|
const profileResult = polygon([
|
|
5893
6113
|
[
|
|
@@ -5912,7 +6132,7 @@ function railingToSolid(spec) {
|
|
|
5912
6132
|
]
|
|
5913
6133
|
]);
|
|
5914
6134
|
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "RAILING_PROFILE_FAILED", "Failed to create railing profile"));
|
|
5915
|
-
const solidResult = extrude(_usingCtx$
|
|
6135
|
+
const solidResult = extrude(_usingCtx$8.u(profileResult.value), [
|
|
5916
6136
|
length,
|
|
5917
6137
|
0,
|
|
5918
6138
|
0
|
|
@@ -5925,16 +6145,72 @@ function railingToSolid(spec) {
|
|
|
5925
6145
|
}
|
|
5926
6146
|
return ok(solid);
|
|
5927
6147
|
} catch (_) {
|
|
5928
|
-
_usingCtx$
|
|
6148
|
+
_usingCtx$8.e = _;
|
|
5929
6149
|
} finally {
|
|
5930
|
-
_usingCtx$
|
|
6150
|
+
_usingCtx$8.d();
|
|
5931
6151
|
}
|
|
5932
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);
|
|
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);
|
|
6208
|
+
}
|
|
5933
6209
|
//#endregion
|
|
5934
6210
|
//#region src/elementFns/coveringFns.ts
|
|
5935
6211
|
function coveringToSolid(spec) {
|
|
5936
6212
|
try {
|
|
5937
|
-
var _usingCtx$
|
|
6213
|
+
var _usingCtx$7 = _usingCtx();
|
|
5938
6214
|
if (spec.length <= 0) return err(specError("COVERING_ZERO_LENGTH", "Covering length must be positive"));
|
|
5939
6215
|
if (spec.width <= 0) return err(specError("COVERING_ZERO_WIDTH", "Covering width must be positive"));
|
|
5940
6216
|
if (spec.thickness <= 0) return err(specError("COVERING_ZERO_THICKNESS", "Covering thickness must be positive"));
|
|
@@ -5962,7 +6238,7 @@ function coveringToSolid(spec) {
|
|
|
5962
6238
|
]
|
|
5963
6239
|
]);
|
|
5964
6240
|
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "COVERING_PROFILE_FAILED", "Failed to create covering profile"));
|
|
5965
|
-
const solidResult = extrude(_usingCtx$
|
|
6241
|
+
const solidResult = extrude(_usingCtx$7.u(profileResult.value), [
|
|
5966
6242
|
0,
|
|
5967
6243
|
0,
|
|
5968
6244
|
thickness
|
|
@@ -5975,9 +6251,9 @@ function coveringToSolid(spec) {
|
|
|
5975
6251
|
}
|
|
5976
6252
|
return ok(solid);
|
|
5977
6253
|
} catch (_) {
|
|
5978
|
-
_usingCtx$
|
|
6254
|
+
_usingCtx$7.e = _;
|
|
5979
6255
|
} finally {
|
|
5980
|
-
_usingCtx$
|
|
6256
|
+
_usingCtx$7.d();
|
|
5981
6257
|
}
|
|
5982
6258
|
}
|
|
5983
6259
|
//#endregion
|
|
@@ -6407,17 +6683,17 @@ var BimModel = class {
|
|
|
6407
6683
|
}
|
|
6408
6684
|
#cutWallGeometry(wall, openingSpec) {
|
|
6409
6685
|
try {
|
|
6410
|
-
var _usingCtx$
|
|
6686
|
+
var _usingCtx$6 = _usingCtx();
|
|
6411
6687
|
const toolResult = openingToSolid(openingSpec, wall.spec.thickness);
|
|
6412
6688
|
if (!toolResult.ok) return err(toolResult.error);
|
|
6413
|
-
const tool = _usingCtx$
|
|
6689
|
+
const tool = _usingCtx$6.u(toolResult.value);
|
|
6414
6690
|
const cutResult = cut(wall.geometry, tool);
|
|
6415
6691
|
if (!cutResult.ok) return err(fromBrepError(cutResult.error, "WALL_CUT_FAILED", "Boolean cut of wall with opening failed"));
|
|
6416
6692
|
return ok(cutResult.value);
|
|
6417
6693
|
} catch (_) {
|
|
6418
|
-
_usingCtx$
|
|
6694
|
+
_usingCtx$6.e = _;
|
|
6419
6695
|
} finally {
|
|
6420
|
-
_usingCtx$
|
|
6696
|
+
_usingCtx$6.d();
|
|
6421
6697
|
}
|
|
6422
6698
|
}
|
|
6423
6699
|
#replaceWallGeometry(wall, newGeometry) {
|
|
@@ -6507,6 +6783,50 @@ var BimModel = class {
|
|
|
6507
6783
|
getElement(id) {
|
|
6508
6784
|
return this.#elements.get(id) ?? null;
|
|
6509
6785
|
}
|
|
6786
|
+
/**
|
|
6787
|
+
* A serializable summary of the model's structure, rooted at the project and
|
|
6788
|
+
* walking the IFC spatial hierarchy (AGGREGATES: project → site → building →
|
|
6789
|
+
* storey) plus the elements contained in each storey (placeIn). Useful for a
|
|
6790
|
+
* read-only tree view of the model across a worker boundary.
|
|
6791
|
+
*/
|
|
6792
|
+
toTreeSummary() {
|
|
6793
|
+
const aggregated = /* @__PURE__ */ new Map();
|
|
6794
|
+
const contained = /* @__PURE__ */ new Map();
|
|
6795
|
+
for (const rel of this.#relationships.values()) if (rel.kind === "AGGREGATES") {
|
|
6796
|
+
const list = aggregated.get(rel.relatingObject) ?? [];
|
|
6797
|
+
list.push(...rel.relatedObjects);
|
|
6798
|
+
aggregated.set(rel.relatingObject, list);
|
|
6799
|
+
} else if (rel.kind === "CONTAINED_IN") {
|
|
6800
|
+
const list = contained.get(rel.relatingStructure) ?? [];
|
|
6801
|
+
list.push(...rel.relatedElements);
|
|
6802
|
+
contained.set(rel.relatingStructure, list);
|
|
6803
|
+
}
|
|
6804
|
+
const labelFor = (el) => {
|
|
6805
|
+
const spec = el.spec;
|
|
6806
|
+
const base = typeof spec.name === "string" && spec.name.length > 0 ? spec.name : el.category;
|
|
6807
|
+
return el.category === "STOREY" && typeof spec.elevation === "number" ? `${base} (+${spec.elevation} mm)` : base;
|
|
6808
|
+
};
|
|
6809
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6810
|
+
const build = (id) => {
|
|
6811
|
+
if (seen.has(id)) return null;
|
|
6812
|
+
seen.add(id);
|
|
6813
|
+
const el = this.#elements.get(id);
|
|
6814
|
+
if (el === void 0) return null;
|
|
6815
|
+
const children = [...aggregated.get(id) ?? [], ...contained.get(id) ?? []].map(build).filter((n) => n !== null);
|
|
6816
|
+
return {
|
|
6817
|
+
id,
|
|
6818
|
+
label: labelFor(el),
|
|
6819
|
+
category: el.category,
|
|
6820
|
+
children
|
|
6821
|
+
};
|
|
6822
|
+
};
|
|
6823
|
+
const root = this.#projectId !== null ? build(this.#projectId) : null;
|
|
6824
|
+
const countNodes = (node) => 1 + node.children.reduce((sum, c) => sum + countNodes(c), 0);
|
|
6825
|
+
return {
|
|
6826
|
+
root,
|
|
6827
|
+
elementCount: root ? countNodes(root) : 0
|
|
6828
|
+
};
|
|
6829
|
+
}
|
|
6510
6830
|
getWalls() {
|
|
6511
6831
|
const walls = [];
|
|
6512
6832
|
for (const el of this.#elements.values()) if (el.category === "WALL") walls.push(el);
|
|
@@ -6610,18 +6930,365 @@ var BimModel = class {
|
|
|
6610
6930
|
this.#elements.set(localId, el);
|
|
6611
6931
|
return localId;
|
|
6612
6932
|
}
|
|
6613
|
-
#makeRel(fields) {
|
|
6614
|
-
const localId = this.#counter.next();
|
|
6615
|
-
const guid = deriveIfcGuidSync(makeRelKey(this.#modelScope, fields.kind, localId));
|
|
6616
|
-
const rel = {
|
|
6617
|
-
...fields,
|
|
6618
|
-
guid,
|
|
6619
|
-
localId
|
|
6620
|
-
};
|
|
6621
|
-
this.#relationships.set(localId, rel);
|
|
6622
|
-
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([]);
|
|
6623
7290
|
}
|
|
6624
|
-
}
|
|
7291
|
+
}
|
|
6625
7292
|
//#endregion
|
|
6626
7293
|
//#region src/ifc-writer/schemaVersion.ts
|
|
6627
7294
|
/**
|
|
@@ -6684,27 +7351,62 @@ function schemaSupports(schema, entityName) {
|
|
|
6684
7351
|
return true;
|
|
6685
7352
|
}
|
|
6686
7353
|
//#endregion
|
|
7354
|
+
//#region src/ifcRuntime.ts
|
|
7355
|
+
var wasmLocateFile;
|
|
7356
|
+
/**
|
|
7357
|
+
* Override how web-ifc finds its `.wasm` file. Applied by every web-ifc entry
|
|
7358
|
+
* point in this package — IFC export ({@link toIfc}), import ({@link fromIfc})
|
|
7359
|
+
* and validation. Required when brepjs-bim is bundled into a worker that serves
|
|
7360
|
+
* the wasm itself; not needed in Node.
|
|
7361
|
+
*/
|
|
7362
|
+
function setIfcWasmLocateFile(locate) {
|
|
7363
|
+
wasmLocateFile = locate;
|
|
7364
|
+
}
|
|
7365
|
+
/**
|
|
7366
|
+
* Initialize a web-ifc API instance the way this package always wants it: with
|
|
7367
|
+
* the host-provided wasm locator and forced single-threaded.
|
|
7368
|
+
*
|
|
7369
|
+
* Single-threaded matters in a cross-origin-isolated context (e.g. a page that
|
|
7370
|
+
* sets COOP/COEP for another WASM kernel): web-ifc would otherwise load its
|
|
7371
|
+
* pthread build and spawn a sub-Worker, which fails when brepjs-bim is itself
|
|
7372
|
+
* bundled inside a Web Worker. In Node the flag is a no-op (web-ifc is already
|
|
7373
|
+
* single-threaded there), and multithreading only speeds up parsing/geometry,
|
|
7374
|
+
* not the one-shot serialize/read this package does.
|
|
7375
|
+
*/
|
|
7376
|
+
async function initIfcApi(api) {
|
|
7377
|
+
await api.Init(wasmLocateFile, true);
|
|
7378
|
+
}
|
|
7379
|
+
//#endregion
|
|
6687
7380
|
//#region src/ifc-writer/ifcWriter.ts
|
|
6688
7381
|
/** Default MVD ViewDefinition declared in the STEP FILE_DESCRIPTION header. */
|
|
6689
7382
|
var DEFAULT_MVD_VIEW_DEFINITION = "ReferenceView_v1.2";
|
|
6690
7383
|
var VIEW_DEFINITION_RE = /ViewDefinition \[[^\]]*\]/;
|
|
7384
|
+
var FILE_NAME_RE = /(FILE_NAME\('[^']*','[^']*',)(?:\$|\(\$\)),(?:\$|\(\$\)),('[^']*','[^']*'),\$\)/;
|
|
7385
|
+
/** STEP single-quoted string literal with embedded quotes doubled per ISO 10303-21. */
|
|
7386
|
+
function stepString(value) {
|
|
7387
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
7388
|
+
}
|
|
6691
7389
|
var IfcWriter = class IfcWriter {
|
|
6692
7390
|
#api;
|
|
6693
7391
|
#modelId;
|
|
6694
7392
|
#mvdViewDefinition;
|
|
7393
|
+
#author;
|
|
7394
|
+
#organization;
|
|
6695
7395
|
#nextExpressId = 1;
|
|
6696
7396
|
#closed = false;
|
|
6697
7397
|
#modelScope = "";
|
|
6698
|
-
constructor(api, modelId, mvdViewDefinition) {
|
|
7398
|
+
constructor(api, modelId, mvdViewDefinition, header) {
|
|
6699
7399
|
this.#api = api;
|
|
6700
7400
|
this.#modelId = modelId;
|
|
6701
7401
|
this.#mvdViewDefinition = mvdViewDefinition;
|
|
7402
|
+
this.#author = header.author ?? "";
|
|
7403
|
+
this.#organization = header.organization ?? "";
|
|
6702
7404
|
}
|
|
6703
|
-
static async create(mvdViewDefinition = DEFAULT_MVD_VIEW_DEFINITION, ifcSchema = DEFAULT_IFC_SCHEMA) {
|
|
7405
|
+
static async create(mvdViewDefinition = DEFAULT_MVD_VIEW_DEFINITION, ifcSchema = DEFAULT_IFC_SCHEMA, header = {}) {
|
|
6704
7406
|
try {
|
|
6705
7407
|
const api = new IfcAPI();
|
|
6706
|
-
await api
|
|
6707
|
-
return ok(new IfcWriter(api, api.CreateModel({ schema: fileSchemaString(ifcSchema) }), mvdViewDefinition));
|
|
7408
|
+
await initIfcApi(api);
|
|
7409
|
+
return ok(new IfcWriter(api, api.CreateModel({ schema: fileSchemaString(ifcSchema) }), mvdViewDefinition, header));
|
|
6708
7410
|
} catch (e) {
|
|
6709
7411
|
return err(ifcError("IFC_INIT_FAILED", "Failed to initialize web-ifc", e));
|
|
6710
7412
|
}
|
|
@@ -6738,7 +7440,7 @@ var IfcWriter = class IfcWriter {
|
|
|
6738
7440
|
if (this.#closed) return err(ifcError("IFC_ALREADY_SAVED", "Model has already been saved and closed"));
|
|
6739
7441
|
try {
|
|
6740
7442
|
const bytes = this.#api.SaveModel(this.#modelId);
|
|
6741
|
-
return ok(this.#
|
|
7443
|
+
return ok(this.#patchHeader(bytes));
|
|
6742
7444
|
} catch (e) {
|
|
6743
7445
|
return err(ifcError("IFC_SAVE_FAILED", "Failed to serialize IFC model", e));
|
|
6744
7446
|
} finally {
|
|
@@ -6747,21 +7449,20 @@ var IfcWriter = class IfcWriter {
|
|
|
6747
7449
|
}
|
|
6748
7450
|
}
|
|
6749
7451
|
/**
|
|
6750
|
-
*
|
|
6751
|
-
*
|
|
6752
|
-
*
|
|
6753
|
-
* (e.g. a future web-ifc
|
|
7452
|
+
* Rewrites the STEP header in the ASCII region web-ifc emits: declares the MVD
|
|
7453
|
+
* in FILE_DESCRIPTION and makes FILE_NAME's author/organization/authorization
|
|
7454
|
+
* spec-conformant (web-ifc leaves them as bare `$`). web-ifc exposes neither
|
|
7455
|
+
* for configuration. If an expected pattern is absent (e.g. a future web-ifc
|
|
7456
|
+
* default change) that part is skipped and the bytes returned unchanged.
|
|
6754
7457
|
*/
|
|
6755
|
-
#
|
|
6756
|
-
if (this.#mvdViewDefinition.length === 0) return bytes;
|
|
7458
|
+
#patchHeader(bytes) {
|
|
6757
7459
|
const HEADER_SCAN = Math.min(bytes.byteLength, 2048);
|
|
6758
|
-
|
|
6759
|
-
if (
|
|
6760
|
-
|
|
6761
|
-
|
|
6762
|
-
}
|
|
6763
|
-
const
|
|
6764
|
-
const patchedHeadBytes = new TextEncoder().encode(patchedHead);
|
|
7460
|
+
let head = new TextDecoder().decode(bytes.subarray(0, HEADER_SCAN));
|
|
7461
|
+
if (FILE_NAME_RE.test(head)) head = head.replace(FILE_NAME_RE, (_m, prefix, systems) => `${prefix}(${stepString(this.#author)}),(${stepString(this.#organization)}),${systems},${stepString("")})`);
|
|
7462
|
+
else console.warn("IfcWriter: FILE_NAME null-field pattern not found; author/organization/authorization left unpatched");
|
|
7463
|
+
if (this.#mvdViewDefinition.length > 0) if (VIEW_DEFINITION_RE.test(head)) head = head.replace(VIEW_DEFINITION_RE, `ViewDefinition [${this.#mvdViewDefinition}]`);
|
|
7464
|
+
else console.warn(`IfcWriter: FILE_DESCRIPTION ViewDefinition not found; MVD "${this.#mvdViewDefinition}" not declared`);
|
|
7465
|
+
const patchedHeadBytes = new TextEncoder().encode(head);
|
|
6765
7466
|
const tail = bytes.subarray(HEADER_SCAN);
|
|
6766
7467
|
const out = new Uint8Array(patchedHeadBytes.byteLength + tail.byteLength);
|
|
6767
7468
|
out.set(patchedHeadBytes, 0);
|
|
@@ -7858,7 +8559,7 @@ function writeSlabGeometry(w, spec, geomSubContextId, parentPlacementId) {
|
|
|
7858
8559
|
productDefinitionShapeId
|
|
7859
8560
|
};
|
|
7860
8561
|
}
|
|
7861
|
-
function writeRoofGeometry(w, spec, geomSubContextId, parentPlacementId) {
|
|
8562
|
+
function writeRoofGeometry(w, spec, solid, geomSubContextId, parentPlacementId) {
|
|
7862
8563
|
const placement3DId = writeAxis2Placement3D(w, spec.origin.map(toIfcLengthM), spec.axisZ, spec.axisX);
|
|
7863
8564
|
const localPlacementId = w.nextId();
|
|
7864
8565
|
w.writeLine({
|
|
@@ -7867,6 +8568,14 @@ function writeRoofGeometry(w, spec, geomSubContextId, parentPlacementId) {
|
|
|
7867
8568
|
PlacementRelTo: parentPlacementId !== null ? w.ref(parentPlacementId) : null,
|
|
7868
8569
|
RelativePlacement: w.ref(placement3DId)
|
|
7869
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
|
+
}
|
|
7870
8579
|
const lengthM = toIfcLengthM(spec.length);
|
|
7871
8580
|
const widthM = toIfcLengthM(spec.width);
|
|
7872
8581
|
const thicknessM = toIfcLengthM(spec.thickness);
|
|
@@ -7934,7 +8643,8 @@ function writeRoofGeometry(w, spec, geomSubContextId, parentPlacementId) {
|
|
|
7934
8643
|
});
|
|
7935
8644
|
return {
|
|
7936
8645
|
localPlacementId,
|
|
7937
|
-
productDefinitionShapeId
|
|
8646
|
+
productDefinitionShapeId,
|
|
8647
|
+
usedFallback: false
|
|
7938
8648
|
};
|
|
7939
8649
|
}
|
|
7940
8650
|
function writeAxis2Placement2D$1(w) {
|
|
@@ -7999,7 +8709,7 @@ function writeProfile(w, profile) {
|
|
|
7999
8709
|
OverallDepth: w.mkType(WebIFC.IFCPOSITIVELENGTHMEASURE, toIfcLengthM(profile.overallDepth)),
|
|
8000
8710
|
WebThickness: w.mkType(WebIFC.IFCPOSITIVELENGTHMEASURE, toIfcLengthM(profile.webThickness)),
|
|
8001
8711
|
FlangeThickness: w.mkType(WebIFC.IFCPOSITIVELENGTHMEASURE, toIfcLengthM(profile.flangeThickness)),
|
|
8002
|
-
FilletRadius: null,
|
|
8712
|
+
FilletRadius: profile.filletRadius === void 0 ? null : w.mkType(WebIFC.IFCPOSITIVELENGTHMEASURE, toIfcLengthM(profile.filletRadius)),
|
|
8003
8713
|
FlangeEdgeRadius: null,
|
|
8004
8714
|
FlangeSlope: null
|
|
8005
8715
|
});
|
|
@@ -8637,68 +9347,6 @@ function writeRelContainedInSpatialStructure(w, guid, ownerHistoryId, relatingSt
|
|
|
8637
9347
|
});
|
|
8638
9348
|
}
|
|
8639
9349
|
//#endregion
|
|
8640
|
-
//#region src/elementFns/stairFns.ts
|
|
8641
|
-
function buildSilhouette$1(numberOfRisers, riserHeight, treadLength) {
|
|
8642
|
-
const pts = [];
|
|
8643
|
-
pts.push([
|
|
8644
|
-
0,
|
|
8645
|
-
0,
|
|
8646
|
-
0
|
|
8647
|
-
]);
|
|
8648
|
-
let x = 0;
|
|
8649
|
-
let z = 0;
|
|
8650
|
-
for (let i = 0; i < numberOfRisers; i++) {
|
|
8651
|
-
z += riserHeight;
|
|
8652
|
-
pts.push([
|
|
8653
|
-
x,
|
|
8654
|
-
0,
|
|
8655
|
-
z
|
|
8656
|
-
]);
|
|
8657
|
-
x += treadLength;
|
|
8658
|
-
pts.push([
|
|
8659
|
-
x,
|
|
8660
|
-
0,
|
|
8661
|
-
z
|
|
8662
|
-
]);
|
|
8663
|
-
}
|
|
8664
|
-
pts.push([
|
|
8665
|
-
x,
|
|
8666
|
-
0,
|
|
8667
|
-
0
|
|
8668
|
-
]);
|
|
8669
|
-
return pts;
|
|
8670
|
-
}
|
|
8671
|
-
function stairFlightToSolid(spec) {
|
|
8672
|
-
try {
|
|
8673
|
-
var _usingCtx$4 = _usingCtx();
|
|
8674
|
-
if (spec.width <= 0) return err(specError("STAIR_FLIGHT_ZERO_WIDTH", "Stair flight width must be positive"));
|
|
8675
|
-
if (spec.riserHeight <= 0) return err(specError("STAIR_FLIGHT_ZERO_RISER", "Stair flight riserHeight must be positive"));
|
|
8676
|
-
if (spec.treadLength <= 0) return err(specError("STAIR_FLIGHT_ZERO_TREAD", "Stair flight treadLength must be positive"));
|
|
8677
|
-
if (!Number.isInteger(spec.numberOfRisers) || spec.numberOfRisers < 1) return err(specError("STAIR_FLIGHT_BAD_RISERS", "Stair flight numberOfRisers must be a positive integer"));
|
|
8678
|
-
const profileResult = polygon(buildSilhouette$1(spec.numberOfRisers, spec.riserHeight, spec.treadLength));
|
|
8679
|
-
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "STAIR_FLIGHT_PROFILE_FAILED", "Failed to create stair flight silhouette profile"));
|
|
8680
|
-
const solidResult = extrude(_usingCtx$4.u(profileResult.value), [
|
|
8681
|
-
0,
|
|
8682
|
-
spec.width,
|
|
8683
|
-
0
|
|
8684
|
-
]);
|
|
8685
|
-
if (!solidResult.ok) return err(fromBrepError(solidResult.error, "STAIR_FLIGHT_EXTRUDE_FAILED", "Failed to extrude stair flight silhouette"));
|
|
8686
|
-
const solid = solidResult.value;
|
|
8687
|
-
if (!isValidSolid(solid)) {
|
|
8688
|
-
solid[Symbol.dispose]();
|
|
8689
|
-
return err(geometryError("STAIR_FLIGHT_INVALID_SOLID", "Stair flight solid failed validity check"));
|
|
8690
|
-
}
|
|
8691
|
-
return ok({
|
|
8692
|
-
solid,
|
|
8693
|
-
geometrySimplified: false
|
|
8694
|
-
});
|
|
8695
|
-
} catch (_) {
|
|
8696
|
-
_usingCtx$4.e = _;
|
|
8697
|
-
} finally {
|
|
8698
|
-
_usingCtx$4.d();
|
|
8699
|
-
}
|
|
8700
|
-
}
|
|
8701
|
-
//#endregion
|
|
8702
9350
|
//#region src/elementFns/rampFns.ts
|
|
8703
9351
|
function buildSilhouette(length, rise, thickness) {
|
|
8704
9352
|
return [
|
|
@@ -9009,7 +9657,7 @@ function writeRampAssembly(w, spec, rampKey, ownerHistoryId, geomSubContextId, p
|
|
|
9009
9657
|
}
|
|
9010
9658
|
//#endregion
|
|
9011
9659
|
//#region src/ifc-writer/railingWriter.ts
|
|
9012
|
-
function writeRailingGeometry(w, spec, geomSubContextId, parentPlacementId) {
|
|
9660
|
+
function writeRailingGeometry(w, spec, solid, geomSubContextId, parentPlacementId) {
|
|
9013
9661
|
const placement3DId = writeAxis2Placement3D(w, spec.origin.map(toIfcLengthM), spec.axisZ, spec.axisX);
|
|
9014
9662
|
const localPlacementId = w.nextId();
|
|
9015
9663
|
w.writeLine({
|
|
@@ -9018,6 +9666,15 @@ function writeRailingGeometry(w, spec, geomSubContextId, parentPlacementId) {
|
|
|
9018
9666
|
PlacementRelTo: parentPlacementId !== null ? w.ref(parentPlacementId) : null,
|
|
9019
9667
|
RelativePlacement: w.ref(placement3DId)
|
|
9020
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
|
+
}
|
|
9021
9678
|
const thicknessM = toIfcLengthM(spec.thickness);
|
|
9022
9679
|
const heightM = toIfcLengthM(spec.height);
|
|
9023
9680
|
const lengthM = toIfcLengthM(spec.length);
|
|
@@ -9094,7 +9751,8 @@ function writeRailingGeometry(w, spec, geomSubContextId, parentPlacementId) {
|
|
|
9094
9751
|
return {
|
|
9095
9752
|
localPlacementId,
|
|
9096
9753
|
productDefinitionShapeId,
|
|
9097
|
-
bodyItemId: extrusionId
|
|
9754
|
+
bodyItemId: extrusionId,
|
|
9755
|
+
usedFallback: false
|
|
9098
9756
|
};
|
|
9099
9757
|
}
|
|
9100
9758
|
function writeRailingEntity(w, guid, name, predefinedType, ownerHistoryId, localPlacementId, productDefinitionShapeId) {
|
|
@@ -11129,35 +11787,6 @@ function checkOpeningExists(issues, elementsById, openingId, code) {
|
|
|
11129
11787
|
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 }));
|
|
11130
11788
|
}
|
|
11131
11789
|
//#endregion
|
|
11132
|
-
//#region src/identity/ifcGuid.ts
|
|
11133
|
-
var IFC_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_$";
|
|
11134
|
-
function newIfcGuid() {
|
|
11135
|
-
const bytes = crypto.getRandomValues(new Uint8Array(16));
|
|
11136
|
-
bytes[6] = (bytes[6] ?? 0) & 15 | 64;
|
|
11137
|
-
bytes[8] = (bytes[8] ?? 0) & 63 | 128;
|
|
11138
|
-
return encodeIfcGuid(bytes);
|
|
11139
|
-
}
|
|
11140
|
-
function isValidIfcGuid(s) {
|
|
11141
|
-
if (s.length !== 22) return false;
|
|
11142
|
-
for (const ch of s) if (!IFC_CHARS.includes(ch)) return false;
|
|
11143
|
-
return true;
|
|
11144
|
-
}
|
|
11145
|
-
function encodeIfcGuid(bytes) {
|
|
11146
|
-
let result = "";
|
|
11147
|
-
let acc = 0;
|
|
11148
|
-
let bits = 0;
|
|
11149
|
-
for (const byte of bytes) {
|
|
11150
|
-
acc = acc << 8 | byte;
|
|
11151
|
-
bits += 8;
|
|
11152
|
-
while (bits >= 6) {
|
|
11153
|
-
bits -= 6;
|
|
11154
|
-
result += IFC_CHARS[acc >> bits & 63] ?? "";
|
|
11155
|
-
}
|
|
11156
|
-
}
|
|
11157
|
-
if (bits > 0) result += IFC_CHARS[acc << 6 - bits & 63] ?? "";
|
|
11158
|
-
return result;
|
|
11159
|
-
}
|
|
11160
|
-
//#endregion
|
|
11161
11790
|
//#region src/validation/schemaCheck.ts
|
|
11162
11791
|
/**
|
|
11163
11792
|
* EXPRESS/STEP self-validation gate.
|
|
@@ -11174,7 +11803,7 @@ function encodeIfcGuid(bytes) {
|
|
|
11174
11803
|
async function checkSchema(bytes) {
|
|
11175
11804
|
if (bytes.byteLength === 0) return appendIssue(emptyReport(), issue("error", "EMPTY_MODEL", "IFC byte buffer is empty; nothing to validate"));
|
|
11176
11805
|
const api = new WebIFC.IfcAPI();
|
|
11177
|
-
await api
|
|
11806
|
+
await initIfcApi(api);
|
|
11178
11807
|
let modelId;
|
|
11179
11808
|
try {
|
|
11180
11809
|
modelId = api.OpenModel(bytes);
|
|
@@ -11268,7 +11897,7 @@ var KEY_ENTITY_TYPES = [
|
|
|
11268
11897
|
*/
|
|
11269
11898
|
async function firstPassCounts(bytes) {
|
|
11270
11899
|
const api = new IfcAPI();
|
|
11271
|
-
await api
|
|
11900
|
+
await initIfcApi(api);
|
|
11272
11901
|
const modelId = api.OpenModel(bytes);
|
|
11273
11902
|
try {
|
|
11274
11903
|
return collectCounts(api, modelId);
|
|
@@ -11282,7 +11911,7 @@ async function firstPassCounts(bytes) {
|
|
|
11282
11911
|
*/
|
|
11283
11912
|
async function secondPassCounts(bytes) {
|
|
11284
11913
|
const api = new IfcAPI();
|
|
11285
|
-
await api
|
|
11914
|
+
await initIfcApi(api);
|
|
11286
11915
|
const sourceModelId = api.OpenModel(bytes);
|
|
11287
11916
|
let resaved;
|
|
11288
11917
|
try {
|
|
@@ -11346,7 +11975,11 @@ async function checkRoundTrip(bytes) {
|
|
|
11346
11975
|
async function toIfc(model, meta) {
|
|
11347
11976
|
const project = model.getProject();
|
|
11348
11977
|
if (!project) return err(ifcError("NO_PROJECT", "BimModel has no project — call model.init() first"));
|
|
11349
|
-
const
|
|
11978
|
+
const authorName = [meta.author?.givenName, meta.author?.familyName].filter((p) => Boolean(p)).join(" ");
|
|
11979
|
+
const writerResult = await IfcWriter.create(meta.mvdViewDefinition, meta.ifcSchema, {
|
|
11980
|
+
author: authorName,
|
|
11981
|
+
organization: meta.organizationName
|
|
11982
|
+
});
|
|
11350
11983
|
if (!writerResult.ok) return writerResult;
|
|
11351
11984
|
const w = writerResult.value;
|
|
11352
11985
|
w.setModelScope(project.guid);
|
|
@@ -11490,7 +12123,8 @@ async function toIfc(model, meta) {
|
|
|
11490
12123
|
for (const [i, roof] of roofs.entries()) {
|
|
11491
12124
|
const containingId = findContainerOf(roof.localId, relationships);
|
|
11492
12125
|
const storeyPlacementId = containingId !== null ? placementMap.get(containingId) ?? null : null;
|
|
11493
|
-
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.`);
|
|
11494
12128
|
const roofExpressId = writeRoofEntity(w, roof.guid, `Roof ${i + 1}`, roof.spec.predefinedType, ownerHistoryId, localPlacementId, productDefinitionShapeId);
|
|
11495
12129
|
idMap.set(roof.localId, roofExpressId);
|
|
11496
12130
|
placementMap.set(roof.localId, localPlacementId);
|
|
@@ -11562,14 +12196,15 @@ async function toIfc(model, meta) {
|
|
|
11562
12196
|
for (const [i, railing] of railings.entries()) {
|
|
11563
12197
|
const containingId = findContainerOf(railing.localId, relationships);
|
|
11564
12198
|
const storeyPlacementId = containingId !== null ? placementMap.get(containingId) ?? null : null;
|
|
11565
|
-
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.`);
|
|
11566
12201
|
const railingExpressId = writeRailingEntity(w, railing.guid, `Railing ${i + 1}`, railing.spec.predefinedType ?? "NOTDEFINED", ownerHistoryId, localPlacementId, productDefinitionShapeId);
|
|
11567
12202
|
idMap.set(railing.localId, railingExpressId);
|
|
11568
12203
|
placementMap.set(railing.localId, localPlacementId);
|
|
11569
12204
|
writeRailingCommonPset(w, ownerHistoryId, railingExpressId, railing.spec);
|
|
11570
12205
|
writeManufacturerPset(w, ownerHistoryId, railingExpressId, railing.spec);
|
|
11571
12206
|
if (railing.spec.customProperties !== void 0) writeCustomPsets(w, ownerHistoryId, railingExpressId, railing.spec.customProperties);
|
|
11572
|
-
applySurfaceStyle(w, model, railing.localId, bodyItemId);
|
|
12207
|
+
if (bodyItemId !== null) applySurfaceStyle(w, model, railing.localId, bodyItemId);
|
|
11573
12208
|
}
|
|
11574
12209
|
for (const [i, covering] of coverings.entries()) {
|
|
11575
12210
|
const containingId = findContainerOf(covering.localId, relationships);
|
|
@@ -11991,7 +12626,7 @@ var SpfReader = class SpfReader {
|
|
|
11991
12626
|
let api;
|
|
11992
12627
|
try {
|
|
11993
12628
|
api = new IfcAPI();
|
|
11994
|
-
await api
|
|
12629
|
+
await initIfcApi(api);
|
|
11995
12630
|
} catch (e) {
|
|
11996
12631
|
return err(importError("OPEN_MODEL_FAILED", "Failed to initialize web-ifc", e));
|
|
11997
12632
|
}
|
|
@@ -12097,120 +12732,6 @@ var SpfReader = class SpfReader {
|
|
|
12097
12732
|
}
|
|
12098
12733
|
};
|
|
12099
12734
|
//#endregion
|
|
12100
|
-
//#region src/import/placement.ts
|
|
12101
|
-
/**
|
|
12102
|
-
* Metres-per-file-unit length scale read from the IfcUnitAssignment's
|
|
12103
|
-
* LENGTHUNIT. Multiply a file-unit length by this to get metres, then by 1000
|
|
12104
|
-
* for brepjs millimetres. Returns 1.0 (assume metres) when no length unit is
|
|
12105
|
-
* declared.
|
|
12106
|
-
*/
|
|
12107
|
-
function readLengthScale(reader) {
|
|
12108
|
-
const assignments = reader.getLinesOfType(WebIFC.IFCUNITASSIGNMENT);
|
|
12109
|
-
for (const assignmentId of assignments) {
|
|
12110
|
-
const units = asRefArray$1(reader.getLine(assignmentId)?.["Units"]);
|
|
12111
|
-
for (const unitId of units) {
|
|
12112
|
-
const scale = lengthScaleFromUnit(reader, unitId);
|
|
12113
|
-
if (scale !== null) return scale;
|
|
12114
|
-
}
|
|
12115
|
-
}
|
|
12116
|
-
return 1;
|
|
12117
|
-
}
|
|
12118
|
-
/**
|
|
12119
|
-
* Radians-per-file-unit plane-angle scale read from the IfcUnitAssignment's
|
|
12120
|
-
* PLANEANGLEUNIT (1 for RADIAN, ~0.0174533 for DEGREE). Defaults to 1 (the IFC
|
|
12121
|
-
* default plane-angle unit is the radian).
|
|
12122
|
-
*/
|
|
12123
|
-
function readPlaneAngleScale(reader) {
|
|
12124
|
-
for (const assignmentId of reader.getLinesOfType(WebIFC.IFCUNITASSIGNMENT)) {
|
|
12125
|
-
const units = asRefArray$1(reader.getLine(assignmentId)?.["Units"]);
|
|
12126
|
-
for (const unitId of units) {
|
|
12127
|
-
const s = planeAngleScaleFromUnit(reader, unitId);
|
|
12128
|
-
if (s !== null) return s;
|
|
12129
|
-
}
|
|
12130
|
-
}
|
|
12131
|
-
return 1;
|
|
12132
|
-
}
|
|
12133
|
-
function planeAngleScaleFromUnit(reader, unitId) {
|
|
12134
|
-
const unit = reader.getLine(unitId);
|
|
12135
|
-
if (unit === null) return null;
|
|
12136
|
-
const type = reader.getLineType(unitId);
|
|
12137
|
-
if (type === WebIFC.IFCSIUNIT) {
|
|
12138
|
-
if (enumValue(unit["UnitType"]) !== "PLANEANGLEUNIT") return null;
|
|
12139
|
-
if (enumValue(unit["Name"]) !== "RADIAN") return null;
|
|
12140
|
-
return 1;
|
|
12141
|
-
}
|
|
12142
|
-
if (type === WebIFC.IFCCONVERSIONBASEDUNIT) {
|
|
12143
|
-
if (enumValue(unit["UnitType"]) !== "PLANEANGLEUNIT") return null;
|
|
12144
|
-
const measureId = refValue$2(unit["ConversionFactor"]);
|
|
12145
|
-
if (measureId === null) return null;
|
|
12146
|
-
const factor = numericValue(reader.getLine(measureId)?.["ValueComponent"]);
|
|
12147
|
-
if (factor === null) return null;
|
|
12148
|
-
return factor;
|
|
12149
|
-
}
|
|
12150
|
-
return null;
|
|
12151
|
-
}
|
|
12152
|
-
function lengthScaleFromUnit(reader, unitId) {
|
|
12153
|
-
const unit = reader.getLine(unitId);
|
|
12154
|
-
if (unit === null) return null;
|
|
12155
|
-
const type = reader.getLineType(unitId);
|
|
12156
|
-
if (type === WebIFC.IFCSIUNIT) {
|
|
12157
|
-
if (enumValue(unit["UnitType"]) !== "LENGTHUNIT") return null;
|
|
12158
|
-
if (enumValue(unit["Name"]) !== "METRE") return null;
|
|
12159
|
-
return siPrefixFactor(enumValue(unit["Prefix"]));
|
|
12160
|
-
}
|
|
12161
|
-
if (type === WebIFC.IFCCONVERSIONBASEDUNIT) {
|
|
12162
|
-
if (enumValue(unit["UnitType"]) !== "LENGTHUNIT") return null;
|
|
12163
|
-
const measureId = refValue$2(unit["ConversionFactor"]);
|
|
12164
|
-
if (measureId === null) return null;
|
|
12165
|
-
const measure = reader.getLine(measureId);
|
|
12166
|
-
const factor = numericValue(measure?.["ValueComponent"]);
|
|
12167
|
-
if (factor === null) return null;
|
|
12168
|
-
const baseId = refValue$2(measure?.["UnitComponent"]);
|
|
12169
|
-
return factor * (baseId !== null ? lengthScaleFromUnit(reader, baseId) ?? 1 : 1);
|
|
12170
|
-
}
|
|
12171
|
-
return null;
|
|
12172
|
-
}
|
|
12173
|
-
function siPrefixFactor(prefix) {
|
|
12174
|
-
switch (prefix) {
|
|
12175
|
-
case null: return 1;
|
|
12176
|
-
case "KILO": return 1e3;
|
|
12177
|
-
case "HECTO": return 100;
|
|
12178
|
-
case "DECA": return 10;
|
|
12179
|
-
case "DECI": return .1;
|
|
12180
|
-
case "CENTI": return .01;
|
|
12181
|
-
case "MILLI": return .001;
|
|
12182
|
-
case "MICRO": return 1e-6;
|
|
12183
|
-
default: return 1;
|
|
12184
|
-
}
|
|
12185
|
-
}
|
|
12186
|
-
function refValue$2(v) {
|
|
12187
|
-
if (v === null || v === void 0) return null;
|
|
12188
|
-
if (typeof v === "number") return v;
|
|
12189
|
-
const value = v.value;
|
|
12190
|
-
return typeof value === "number" ? value : null;
|
|
12191
|
-
}
|
|
12192
|
-
function asRefArray$1(v) {
|
|
12193
|
-
if (!Array.isArray(v)) return [];
|
|
12194
|
-
const out = [];
|
|
12195
|
-
for (const item of v) {
|
|
12196
|
-
const id = refValue$2(item);
|
|
12197
|
-
if (id !== null) out.push(id);
|
|
12198
|
-
}
|
|
12199
|
-
return out;
|
|
12200
|
-
}
|
|
12201
|
-
function numericValue(v) {
|
|
12202
|
-
if (typeof v === "number") return v;
|
|
12203
|
-
if (v === null || v === void 0) return null;
|
|
12204
|
-
const value = v.value;
|
|
12205
|
-
return typeof value === "number" ? value : null;
|
|
12206
|
-
}
|
|
12207
|
-
function enumValue(v) {
|
|
12208
|
-
if (typeof v === "string") return v;
|
|
12209
|
-
if (v === null || v === void 0) return null;
|
|
12210
|
-
const value = v.value;
|
|
12211
|
-
return typeof value === "string" ? value : null;
|
|
12212
|
-
}
|
|
12213
|
-
//#endregion
|
|
12214
12735
|
//#region src/import/spatialTree.ts
|
|
12215
12736
|
var CATEGORY_BY_TYPE = new Map([
|
|
12216
12737
|
[WebIFC.IFCPROJECT, "PROJECT"],
|
|
@@ -13836,6 +14357,7 @@ var RoofSpecSchema = object({
|
|
|
13836
14357
|
fireRating: string().optional(),
|
|
13837
14358
|
thermalTransmittance: number().positive().optional(),
|
|
13838
14359
|
status: string().optional(),
|
|
14360
|
+
pitch: number().positive().max(89).optional(),
|
|
13839
14361
|
materialLayers: array(MaterialLayerSchema).optional(),
|
|
13840
14362
|
layerSetName: string().optional(),
|
|
13841
14363
|
classification: ClassificationRefSchema.optional(),
|
|
@@ -14167,6 +14689,7 @@ var RailingSpecSchema = object({
|
|
|
14167
14689
|
"HANDRAIL",
|
|
14168
14690
|
"NOTDEFINED"
|
|
14169
14691
|
]).optional(),
|
|
14692
|
+
infill: _enum(["PANEL", "POSTED"]).optional(),
|
|
14170
14693
|
materialName: string().min(1),
|
|
14171
14694
|
isExternal: boolean().optional(),
|
|
14172
14695
|
fireRating: string().optional(),
|
|
@@ -15819,4 +16342,4 @@ function assignNum(target, key, value) {
|
|
|
15819
16342
|
if (value !== void 0) target[key] = Number(value);
|
|
15820
16343
|
}
|
|
15821
16344
|
//#endregion
|
|
15822
|
-
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, specError, templateFor, toIfc, toIfcLengthM, toIfcValidated, toLengthMm, writeClassificationRefs, writeElementAssemblyEntity, writeIfcType, writeMaterialLayerSet, writeMaterialProfileSet, writeMaterialSimple, writePresentationLayer, writeRelAggregatesElements, writeRelAssignsToGroup, writeRelConnectsElements, writeRelConnectsPathElements, writeRelNests, writeStyledItem, writeSurfaceStyle, writeSystemEntity, writeZoneEntity };
|
|
16345
|
+
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 };
|