brepjs-bim 0.3.1 → 0.5.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/dist/brepjs-bim.cjs +384 -107
- package/dist/brepjs-bim.js +383 -107
- package/dist/familiesAdapter.d.ts +22 -0
- package/dist/index.d.ts +2 -1
- package/dist/model/bimModel.d.ts +17 -7
- package/package.json +5 -2
package/dist/brepjs-bim.js
CHANGED
|
@@ -4,7 +4,7 @@ import { Handle, IfcAPI } from "web-ifc";
|
|
|
4
4
|
//#region src/identity/ifcGuid.ts
|
|
5
5
|
var IFC_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_$";
|
|
6
6
|
function newIfcGuid() {
|
|
7
|
-
const bytes = crypto.getRandomValues(new Uint8Array(16));
|
|
7
|
+
const bytes = crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(16));
|
|
8
8
|
bytes[6] = (bytes[6] ?? 0) & 15 | 64;
|
|
9
9
|
bytes[8] = (bytes[8] ?? 0) & 63 | 128;
|
|
10
10
|
return encodeIfcGuid(bytes);
|
|
@@ -46,7 +46,7 @@ function fnv1a(text) {
|
|
|
46
46
|
}
|
|
47
47
|
function digest16(stableKey) {
|
|
48
48
|
const seed = `${NAMESPACE}::${stableKey}`;
|
|
49
|
-
const bytes = new Uint8Array(16);
|
|
49
|
+
const bytes = /* @__PURE__ */ new Uint8Array(16);
|
|
50
50
|
for (let block = 0; block < 4; block++) {
|
|
51
51
|
const h = fnv1a(`b${block}:${seed}`);
|
|
52
52
|
bytes[block * 4 + 0] = h >>> 24 & 255;
|
|
@@ -183,7 +183,7 @@ function fromBrepError(inner, code, message) {
|
|
|
183
183
|
};
|
|
184
184
|
}
|
|
185
185
|
//#endregion
|
|
186
|
-
//#region \0@oxc-project+runtime@0.
|
|
186
|
+
//#region \0@oxc-project+runtime@0.143.0/helpers/esm/usingCtx.js
|
|
187
187
|
function _usingCtx() {
|
|
188
188
|
var r = "function" == typeof SuppressedError ? SuppressedError : function(r, e) {
|
|
189
189
|
var n = Error();
|
|
@@ -270,7 +270,8 @@ function wallToSolid(spec) {
|
|
|
270
270
|
]
|
|
271
271
|
]);
|
|
272
272
|
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "WALL_PROFILE_FAILED", "Failed to create wall profile"));
|
|
273
|
-
const
|
|
273
|
+
const profile = _usingCtx$19.u(profileResult.value);
|
|
274
|
+
const solidResult = extrude(profile, [
|
|
274
275
|
length,
|
|
275
276
|
0,
|
|
276
277
|
0
|
|
@@ -320,7 +321,8 @@ function slabToSolid(spec) {
|
|
|
320
321
|
]
|
|
321
322
|
]);
|
|
322
323
|
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "SLAB_PROFILE_FAILED", "Failed to create slab profile"));
|
|
323
|
-
const
|
|
324
|
+
const profile = _usingCtx$18.u(profileResult.value);
|
|
325
|
+
const solidResult = extrude(profile, [
|
|
324
326
|
0,
|
|
325
327
|
0,
|
|
326
328
|
thickness
|
|
@@ -414,7 +416,6 @@ function cached(getter) {
|
|
|
414
416
|
Object.defineProperty(this, "value", { value });
|
|
415
417
|
return value;
|
|
416
418
|
}
|
|
417
|
-
throw new Error("cached value already set");
|
|
418
419
|
} };
|
|
419
420
|
}
|
|
420
421
|
function nullish(input) {
|
|
@@ -460,7 +461,10 @@ function assignProp(target, prop, value) {
|
|
|
460
461
|
}
|
|
461
462
|
function mergeDefs(...defs) {
|
|
462
463
|
const mergedDescriptors = {};
|
|
463
|
-
for (const def of defs)
|
|
464
|
+
for (const def of defs) {
|
|
465
|
+
const descriptors = Object.getOwnPropertyDescriptors(def);
|
|
466
|
+
Object.assign(mergedDescriptors, descriptors);
|
|
467
|
+
}
|
|
464
468
|
return Object.defineProperties({}, mergedDescriptors);
|
|
465
469
|
}
|
|
466
470
|
function esc(str) {
|
|
@@ -782,7 +786,7 @@ var _parse = (_Err) => (schema, value, _ctx, _params) => {
|
|
|
782
786
|
}, ctx);
|
|
783
787
|
if (result instanceof Promise) throw new $ZodAsyncError();
|
|
784
788
|
if (result.issues.length) {
|
|
785
|
-
const e = new (_params?.Err ?? _Err)(result.issues.map((iss) => finalizeIssue(iss, ctx, config())));
|
|
789
|
+
const e = new ((_params?.Err) ?? _Err)(result.issues.map((iss) => finalizeIssue(iss, ctx, config())));
|
|
786
790
|
captureStackTrace(e, _params?.callee);
|
|
787
791
|
throw e;
|
|
788
792
|
}
|
|
@@ -799,7 +803,7 @@ var _parseAsync = (_Err) => async (schema, value, _ctx, params) => {
|
|
|
799
803
|
}, ctx);
|
|
800
804
|
if (result instanceof Promise) result = await result;
|
|
801
805
|
if (result.issues.length) {
|
|
802
|
-
const e = new (params?.Err ?? _Err)(result.issues.map((iss) => finalizeIssue(iss, ctx, config())));
|
|
806
|
+
const e = new ((params?.Err) ?? _Err)(result.issues.map((iss) => finalizeIssue(iss, ctx, config())));
|
|
803
807
|
captureStackTrace(e, params?.callee);
|
|
804
808
|
throw e;
|
|
805
809
|
}
|
|
@@ -966,8 +970,10 @@ var $ZodCheckLessThan = /*@__PURE__*/ $constructor("$ZodCheckLessThan", (inst, d
|
|
|
966
970
|
inst._zod.onattach.push((inst) => {
|
|
967
971
|
const bag = inst._zod.bag;
|
|
968
972
|
const curr = (def.inclusive ? bag.maximum : bag.exclusiveMaximum) ?? Number.POSITIVE_INFINITY;
|
|
969
|
-
if (def.value < curr)
|
|
970
|
-
|
|
973
|
+
if (def.value < curr) {
|
|
974
|
+
if (def.inclusive) bag.maximum = def.value;
|
|
975
|
+
else bag.exclusiveMaximum = def.value;
|
|
976
|
+
}
|
|
971
977
|
});
|
|
972
978
|
inst._zod.check = (payload) => {
|
|
973
979
|
if (def.inclusive ? payload.value <= def.value : payload.value < def.value) return;
|
|
@@ -988,8 +994,10 @@ var $ZodCheckGreaterThan = /*@__PURE__*/ $constructor("$ZodCheckGreaterThan", (i
|
|
|
988
994
|
inst._zod.onattach.push((inst) => {
|
|
989
995
|
const bag = inst._zod.bag;
|
|
990
996
|
const curr = (def.inclusive ? bag.minimum : bag.exclusiveMinimum) ?? Number.NEGATIVE_INFINITY;
|
|
991
|
-
if (def.value > curr)
|
|
992
|
-
|
|
997
|
+
if (def.value > curr) {
|
|
998
|
+
if (def.inclusive) bag.minimum = def.value;
|
|
999
|
+
else bag.exclusiveMinimum = def.value;
|
|
1000
|
+
}
|
|
993
1001
|
});
|
|
994
1002
|
inst._zod.check = (payload) => {
|
|
995
1003
|
if (def.inclusive ? payload.value >= def.value : payload.value > def.value) return;
|
|
@@ -2522,7 +2530,7 @@ var $ZodOptional = /*@__PURE__*/ $constructor("$ZodOptional", (inst, def) => {
|
|
|
2522
2530
|
inst._zod.optin = "optional";
|
|
2523
2531
|
inst._zod.optout = "optional";
|
|
2524
2532
|
defineLazy(inst._zod, "values", () => {
|
|
2525
|
-
return def.innerType._zod.values ? new Set([...def.innerType._zod.values, void 0]) : void 0;
|
|
2533
|
+
return def.innerType._zod.values ? /* @__PURE__ */ new Set([...def.innerType._zod.values, void 0]) : void 0;
|
|
2526
2534
|
});
|
|
2527
2535
|
defineLazy(inst._zod, "pattern", () => {
|
|
2528
2536
|
const pattern = def.innerType._zod.pattern;
|
|
@@ -2556,7 +2564,7 @@ var $ZodNullable = /*@__PURE__*/ $constructor("$ZodNullable", (inst, def) => {
|
|
|
2556
2564
|
return pattern ? new RegExp(`^(${cleanRegex(pattern.source)}|null)$`) : void 0;
|
|
2557
2565
|
});
|
|
2558
2566
|
defineLazy(inst._zod, "values", () => {
|
|
2559
|
-
return def.innerType._zod.values ? new Set([...def.innerType._zod.values, null]) : void 0;
|
|
2567
|
+
return def.innerType._zod.values ? /* @__PURE__ */ new Set([...def.innerType._zod.values, null]) : void 0;
|
|
2560
2568
|
});
|
|
2561
2569
|
inst._zod.parse = (payload, ctx) => {
|
|
2562
2570
|
if (payload.value === null) return payload;
|
|
@@ -3490,8 +3498,10 @@ function finalize$1(ctx, schema) {
|
|
|
3490
3498
|
defs[seen.defId] = seen.def;
|
|
3491
3499
|
}
|
|
3492
3500
|
}
|
|
3493
|
-
if (ctx.external) {} else if (Object.keys(defs).length > 0)
|
|
3494
|
-
|
|
3501
|
+
if (ctx.external) {} else if (Object.keys(defs).length > 0) {
|
|
3502
|
+
if (ctx.target === "draft-2020-12") result.$defs = defs;
|
|
3503
|
+
else result.definitions = defs;
|
|
3504
|
+
}
|
|
3495
3505
|
try {
|
|
3496
3506
|
const finalized = JSON.parse(JSON.stringify(result));
|
|
3497
3507
|
Object.defineProperty(finalized, "~standard", {
|
|
@@ -3604,16 +3614,18 @@ var numberProcessor = (schema, ctx, _json, _params) => {
|
|
|
3604
3614
|
const exMin = typeof exclusiveMinimum === "number" && exclusiveMinimum >= (minimum ?? Number.NEGATIVE_INFINITY);
|
|
3605
3615
|
const exMax = typeof exclusiveMaximum === "number" && exclusiveMaximum <= (maximum ?? Number.POSITIVE_INFINITY);
|
|
3606
3616
|
const legacy = ctx.target === "draft-04" || ctx.target === "openapi-3.0";
|
|
3607
|
-
if (exMin)
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
if (
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
+
if (exMin) {
|
|
3618
|
+
if (legacy) {
|
|
3619
|
+
json.minimum = exclusiveMinimum;
|
|
3620
|
+
json.exclusiveMinimum = true;
|
|
3621
|
+
} else json.exclusiveMinimum = exclusiveMinimum;
|
|
3622
|
+
} else if (typeof minimum === "number") json.minimum = minimum;
|
|
3623
|
+
if (exMax) {
|
|
3624
|
+
if (legacy) {
|
|
3625
|
+
json.maximum = exclusiveMaximum;
|
|
3626
|
+
json.exclusiveMaximum = true;
|
|
3627
|
+
} else json.exclusiveMaximum = exclusiveMaximum;
|
|
3628
|
+
} else if (typeof maximum === "number") json.maximum = maximum;
|
|
3617
3629
|
if (typeof multipleOf === "number") json.multipleOf = multipleOf;
|
|
3618
3630
|
};
|
|
3619
3631
|
var booleanProcessor = (_schema, _ctx, json, _params) => {
|
|
@@ -3634,9 +3646,10 @@ var literalProcessor = (schema, ctx, json, _params) => {
|
|
|
3634
3646
|
const vals = [];
|
|
3635
3647
|
for (const val of def.values) if (val === void 0) {
|
|
3636
3648
|
if (ctx.unrepresentable === "throw") throw new Error("Literal `undefined` cannot be represented in JSON Schema");
|
|
3637
|
-
} else if (typeof val === "bigint")
|
|
3638
|
-
|
|
3639
|
-
|
|
3649
|
+
} else if (typeof val === "bigint") {
|
|
3650
|
+
if (ctx.unrepresentable === "throw") throw new Error("BigInt literals cannot be represented in JSON Schema");
|
|
3651
|
+
else vals.push(Number(val));
|
|
3652
|
+
} else vals.push(val);
|
|
3640
3653
|
if (vals.length === 0) {} else if (vals.length === 1) {
|
|
3641
3654
|
const val = vals[0];
|
|
3642
3655
|
json.type = val === null ? "null" : typeof val;
|
|
@@ -5051,7 +5064,7 @@ function extendedProfileToFace(profile) {
|
|
|
5051
5064
|
}
|
|
5052
5065
|
//#endregion
|
|
5053
5066
|
//#region src/specs/profile.ts
|
|
5054
|
-
var EXTENDED_PROFILE_KIND_SET = new Set([
|
|
5067
|
+
var EXTENDED_PROFILE_KIND_SET = /* @__PURE__ */ new Set([
|
|
5055
5068
|
"L_SHAPE",
|
|
5056
5069
|
"T_SHAPE",
|
|
5057
5070
|
"U_SHAPE",
|
|
@@ -5275,7 +5288,7 @@ function profileToPolygon(profile, circleSegments = 32) {
|
|
|
5275
5288
|
case "RECTANGULAR": {
|
|
5276
5289
|
const halfW = profile.width / 2;
|
|
5277
5290
|
const halfH = profile.height / 2;
|
|
5278
|
-
|
|
5291
|
+
const pts = [
|
|
5279
5292
|
[
|
|
5280
5293
|
-halfW,
|
|
5281
5294
|
-halfH,
|
|
@@ -5296,7 +5309,8 @@ function profileToPolygon(profile, circleSegments = 32) {
|
|
|
5296
5309
|
halfH,
|
|
5297
5310
|
0
|
|
5298
5311
|
]
|
|
5299
|
-
]
|
|
5312
|
+
];
|
|
5313
|
+
return ok(pts);
|
|
5300
5314
|
}
|
|
5301
5315
|
case "CIRCULAR": {
|
|
5302
5316
|
if (circleSegments < 3) return err(specError("PROFILE_CIRCLE_SEGMENTS", `profileToPolygon: circleSegments must be >= 3, got ${circleSegments}`));
|
|
@@ -5331,7 +5345,7 @@ function profileToPolygon(profile, circleSegments = 32) {
|
|
|
5331
5345
|
[-halfW, -flangeInnerY]
|
|
5332
5346
|
];
|
|
5333
5347
|
const r = profile.filletRadius ?? 0;
|
|
5334
|
-
const rootCorners = new Set([
|
|
5348
|
+
const rootCorners = /* @__PURE__ */ new Set([
|
|
5335
5349
|
3,
|
|
5336
5350
|
4,
|
|
5337
5351
|
9,
|
|
@@ -5373,13 +5387,15 @@ function beamToSolid(spec) {
|
|
|
5373
5387
|
var _usingCtx3 = _usingCtx();
|
|
5374
5388
|
const faceResult = extendedProfileToFace(spec.profile);
|
|
5375
5389
|
if (!faceResult.ok) return err(faceResult.error);
|
|
5376
|
-
const
|
|
5390
|
+
const face = _usingCtx3.u(faceResult.value);
|
|
5391
|
+
const prismResult = extrude(face, [
|
|
5377
5392
|
0,
|
|
5378
5393
|
0,
|
|
5379
5394
|
spec.length
|
|
5380
5395
|
]);
|
|
5381
5396
|
if (!prismResult.ok) return err(fromBrepError(prismResult.error, "BEAM_EXTRUDE_FAILED", "Failed to extrude beam profile"));
|
|
5382
|
-
const
|
|
5397
|
+
const prism = _usingCtx3.u(prismResult.value);
|
|
5398
|
+
const solid = rotate(prism, 90, { axis: [
|
|
5383
5399
|
0,
|
|
5384
5400
|
1,
|
|
5385
5401
|
0
|
|
@@ -5396,13 +5412,15 @@ function beamToSolid(spec) {
|
|
|
5396
5412
|
}
|
|
5397
5413
|
const profilePtsResult = profileToPolygon(spec.profile);
|
|
5398
5414
|
if (!profilePtsResult.ok) return err(profilePtsResult.error);
|
|
5399
|
-
const
|
|
5415
|
+
const profilePts = profilePtsResult.value.map(([px, py]) => [
|
|
5400
5416
|
0,
|
|
5401
5417
|
px,
|
|
5402
5418
|
py
|
|
5403
|
-
])
|
|
5419
|
+
]);
|
|
5420
|
+
const profileResult = polygon(profilePts);
|
|
5404
5421
|
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "BEAM_PROFILE_FAILED", "Failed to create beam profile"));
|
|
5405
|
-
const
|
|
5422
|
+
const profile = _usingCtx$16.u(profileResult.value);
|
|
5423
|
+
const solidResult = extrude(profile, [
|
|
5406
5424
|
spec.length,
|
|
5407
5425
|
0,
|
|
5408
5426
|
0
|
|
@@ -5430,7 +5448,8 @@ function columnToSolid(spec) {
|
|
|
5430
5448
|
var _usingCtx3 = _usingCtx();
|
|
5431
5449
|
const faceResult = extendedProfileToFace(spec.profile);
|
|
5432
5450
|
if (!faceResult.ok) return err(faceResult.error);
|
|
5433
|
-
const
|
|
5451
|
+
const face = _usingCtx3.u(faceResult.value);
|
|
5452
|
+
const extResult = extrude(face, [
|
|
5434
5453
|
0,
|
|
5435
5454
|
0,
|
|
5436
5455
|
spec.height
|
|
@@ -5452,7 +5471,8 @@ function columnToSolid(spec) {
|
|
|
5452
5471
|
const profilePts = profilePtsResult.value;
|
|
5453
5472
|
const profileResult = polygon(profilePts);
|
|
5454
5473
|
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "COLUMN_PROFILE_FAILED", "Failed to create column profile"));
|
|
5455
|
-
const
|
|
5474
|
+
const profile = _usingCtx$15.u(profileResult.value);
|
|
5475
|
+
const solidResult = extrude(profile, [
|
|
5456
5476
|
0,
|
|
5457
5477
|
0,
|
|
5458
5478
|
spec.height
|
|
@@ -5472,7 +5492,7 @@ function columnToSolid(spec) {
|
|
|
5472
5492
|
}
|
|
5473
5493
|
//#endregion
|
|
5474
5494
|
//#region src/elementFns/openingFns.ts
|
|
5475
|
-
var EPSILON_MM
|
|
5495
|
+
var EPSILON_MM = 1;
|
|
5476
5496
|
function openingToSolid(spec, wallThickness) {
|
|
5477
5497
|
try {
|
|
5478
5498
|
var _usingCtx$14 = _usingCtx();
|
|
@@ -5481,7 +5501,7 @@ function openingToSolid(spec, wallThickness) {
|
|
|
5481
5501
|
if (wallThickness <= 0) return err(specError("OPENING_ZERO_WALL_THICKNESS", "Wall thickness must be positive"));
|
|
5482
5502
|
const x0 = spec.offsetAlongWall;
|
|
5483
5503
|
const yLow = -1;
|
|
5484
|
-
const yHigh = wallThickness + EPSILON_MM
|
|
5504
|
+
const yHigh = wallThickness + EPSILON_MM;
|
|
5485
5505
|
const zLow = spec.offsetFromFloor;
|
|
5486
5506
|
const zHigh = spec.offsetFromFloor + spec.height;
|
|
5487
5507
|
const profileResult = polygon([
|
|
@@ -5507,7 +5527,8 @@ function openingToSolid(spec, wallThickness) {
|
|
|
5507
5527
|
]
|
|
5508
5528
|
]);
|
|
5509
5529
|
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "OPENING_PROFILE_FAILED", "Failed to create opening profile"));
|
|
5510
|
-
const
|
|
5530
|
+
const profile = _usingCtx$14.u(profileResult.value);
|
|
5531
|
+
const solidResult = extrude(profile, [
|
|
5511
5532
|
spec.width,
|
|
5512
5533
|
0,
|
|
5513
5534
|
0
|
|
@@ -5527,7 +5548,6 @@ function openingToSolid(spec, wallThickness) {
|
|
|
5527
5548
|
}
|
|
5528
5549
|
//#endregion
|
|
5529
5550
|
//#region src/elementFns/slabOpeningFns.ts
|
|
5530
|
-
var EPSILON_MM = 1;
|
|
5531
5551
|
function slabOpeningToSolid(spec, slabThickness) {
|
|
5532
5552
|
try {
|
|
5533
5553
|
var _usingCtx$13 = _usingCtx();
|
|
@@ -5562,10 +5582,11 @@ function slabOpeningToSolid(spec, slabThickness) {
|
|
|
5562
5582
|
]
|
|
5563
5583
|
]);
|
|
5564
5584
|
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "SLAB_OPENING_PROFILE_FAILED", "Failed to create slab opening profile"));
|
|
5565
|
-
const
|
|
5585
|
+
const profile = _usingCtx$13.u(profileResult.value);
|
|
5586
|
+
const solidResult = extrude(profile, [
|
|
5566
5587
|
0,
|
|
5567
5588
|
0,
|
|
5568
|
-
slabThickness + 2
|
|
5589
|
+
slabThickness + 2
|
|
5569
5590
|
]);
|
|
5570
5591
|
if (!solidResult.ok) return err(fromBrepError(solidResult.error, "SLAB_OPENING_EXTRUDE_FAILED", "Failed to extrude slab opening profile"));
|
|
5571
5592
|
const solid = solidResult.value;
|
|
@@ -5612,7 +5633,8 @@ function spaceToSolid(spec) {
|
|
|
5612
5633
|
]
|
|
5613
5634
|
]);
|
|
5614
5635
|
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "SPACE_PROFILE_FAILED", "Failed to create space footprint"));
|
|
5615
|
-
const
|
|
5636
|
+
const profile = _usingCtx$12.u(profileResult.value);
|
|
5637
|
+
const solidResult = extrude(profile, [
|
|
5616
5638
|
0,
|
|
5617
5639
|
0,
|
|
5618
5640
|
height
|
|
@@ -5667,7 +5689,8 @@ function flatRoof(spec) {
|
|
|
5667
5689
|
]
|
|
5668
5690
|
]);
|
|
5669
5691
|
if (!face.ok) return err(fromBrepError(face.error, "ROOF_PROFILE_FAILED", "Failed to create roof profile"));
|
|
5670
|
-
const
|
|
5692
|
+
const profile = _usingCtx$11.u(face.value);
|
|
5693
|
+
const solid = extrude(profile, [
|
|
5671
5694
|
0,
|
|
5672
5695
|
0,
|
|
5673
5696
|
thickness
|
|
@@ -5708,7 +5731,8 @@ function shedRoof(spec, pitch) {
|
|
|
5708
5731
|
]
|
|
5709
5732
|
]);
|
|
5710
5733
|
if (!face.ok) return err(fromBrepError(face.error, "ROOF_PROFILE_FAILED", "Failed to create shed profile"));
|
|
5711
|
-
const
|
|
5734
|
+
const profile = _usingCtx3.u(face.value);
|
|
5735
|
+
const solid = extrude(profile, [
|
|
5712
5736
|
length,
|
|
5713
5737
|
0,
|
|
5714
5738
|
0
|
|
@@ -5754,7 +5778,8 @@ function gableRoof(spec, pitch) {
|
|
|
5754
5778
|
]
|
|
5755
5779
|
]);
|
|
5756
5780
|
if (!face.ok) return err(fromBrepError(face.error, "ROOF_PROFILE_FAILED", "Failed to create gable profile"));
|
|
5757
|
-
const
|
|
5781
|
+
const profile = _usingCtx4.u(face.value);
|
|
5782
|
+
const solid = extrude(profile, [
|
|
5758
5783
|
length,
|
|
5759
5784
|
0,
|
|
5760
5785
|
0
|
|
@@ -5895,7 +5920,8 @@ function boxSolid(sizeX, sizeY, sizeZ) {
|
|
|
5895
5920
|
]
|
|
5896
5921
|
]);
|
|
5897
5922
|
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "CURTAIN_WALL_PROFILE_FAILED", "Failed to create component profile"));
|
|
5898
|
-
const
|
|
5923
|
+
const profile = _usingCtx$10.u(profileResult.value);
|
|
5924
|
+
const solidResult = extrude(profile, [
|
|
5899
5925
|
0,
|
|
5900
5926
|
0,
|
|
5901
5927
|
sizeZ
|
|
@@ -6037,7 +6063,8 @@ function footingToSolid(spec) {
|
|
|
6037
6063
|
]
|
|
6038
6064
|
]);
|
|
6039
6065
|
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "FOOTING_PROFILE_FAILED", "Failed to create footing profile"));
|
|
6040
|
-
const
|
|
6066
|
+
const profile = _usingCtx$9.u(profileResult.value);
|
|
6067
|
+
const solidResult = extrude(profile, [
|
|
6041
6068
|
0,
|
|
6042
6069
|
0,
|
|
6043
6070
|
thickness
|
|
@@ -6063,7 +6090,8 @@ function pileToSolid(spec) {
|
|
|
6063
6090
|
var _usingCtx4 = _usingCtx();
|
|
6064
6091
|
const faceResult = extendedProfileToFace(spec.profile);
|
|
6065
6092
|
if (!faceResult.ok) return err(faceResult.error);
|
|
6066
|
-
const
|
|
6093
|
+
const face = _usingCtx4.u(faceResult.value);
|
|
6094
|
+
const extResult = extrude(face, [
|
|
6067
6095
|
0,
|
|
6068
6096
|
0,
|
|
6069
6097
|
spec.length
|
|
@@ -6085,7 +6113,8 @@ function pileToSolid(spec) {
|
|
|
6085
6113
|
const profilePts = profilePtsResult.value;
|
|
6086
6114
|
const profileResult = polygon(profilePts);
|
|
6087
6115
|
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "PILE_PROFILE_FAILED", "Failed to create pile profile"));
|
|
6088
|
-
const
|
|
6116
|
+
const profile = _usingCtx3.u(profileResult.value);
|
|
6117
|
+
const solidResult = extrude(profile, [
|
|
6089
6118
|
0,
|
|
6090
6119
|
0,
|
|
6091
6120
|
spec.length
|
|
@@ -6132,7 +6161,8 @@ function panelRailing(spec) {
|
|
|
6132
6161
|
]
|
|
6133
6162
|
]);
|
|
6134
6163
|
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "RAILING_PROFILE_FAILED", "Failed to create railing profile"));
|
|
6135
|
-
const
|
|
6164
|
+
const profile = _usingCtx$8.u(profileResult.value);
|
|
6165
|
+
const solidResult = extrude(profile, [
|
|
6136
6166
|
length,
|
|
6137
6167
|
0,
|
|
6138
6168
|
0
|
|
@@ -6238,7 +6268,8 @@ function coveringToSolid(spec) {
|
|
|
6238
6268
|
]
|
|
6239
6269
|
]);
|
|
6240
6270
|
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "COVERING_PROFILE_FAILED", "Failed to create covering profile"));
|
|
6241
|
-
const
|
|
6271
|
+
const profile = _usingCtx$7.u(profileResult.value);
|
|
6272
|
+
const solidResult = extrude(profile, [
|
|
6242
6273
|
0,
|
|
6243
6274
|
0,
|
|
6244
6275
|
thickness
|
|
@@ -6265,6 +6296,7 @@ var BimModel = class {
|
|
|
6265
6296
|
#counter = makeLocalIdCounter();
|
|
6266
6297
|
#projectId = null;
|
|
6267
6298
|
#modelScope = "";
|
|
6299
|
+
#usedStableKeys = /* @__PURE__ */ new Set();
|
|
6268
6300
|
init(spec) {
|
|
6269
6301
|
if (this.#projectId !== null) return err(specError("DUPLICATE_PROJECT", "BimModel.init() called twice — only one project per model"));
|
|
6270
6302
|
this.#modelScope = spec.projectId ?? `${spec.name}::${spec.description ?? ""}`;
|
|
@@ -6282,24 +6314,43 @@ var BimModel = class {
|
|
|
6282
6314
|
addSite(spec) {
|
|
6283
6315
|
return this.#makeElement("SITE", spec, null);
|
|
6284
6316
|
}
|
|
6285
|
-
addBuilding(spec) {
|
|
6286
|
-
return this.#makeElement("BUILDING", spec, null);
|
|
6287
|
-
}
|
|
6288
|
-
addStorey(spec) {
|
|
6289
|
-
return this.#makeElement("STOREY", spec, null);
|
|
6290
|
-
}
|
|
6291
|
-
|
|
6317
|
+
addBuilding(spec, options) {
|
|
6318
|
+
return this.#makeElement("BUILDING", spec, null, options?.stableKey);
|
|
6319
|
+
}
|
|
6320
|
+
addStorey(spec, options) {
|
|
6321
|
+
return this.#makeElement("STOREY", spec, null, options?.stableKey);
|
|
6322
|
+
}
|
|
6323
|
+
/** Reject a duplicate stableKey BEFORE any geometry is built, so the
|
|
6324
|
+
* Result-returning adders never allocate a solid they cannot store. */
|
|
6325
|
+
#checkStableKey(options) {
|
|
6326
|
+
const key = options?.stableKey;
|
|
6327
|
+
if (key !== void 0 && this.#usedStableKeys.has(key)) return err(specError("DUPLICATE_STABLE_KEY", `BimModel: duplicate stableKey '${key}'`));
|
|
6328
|
+
return ok(void 0);
|
|
6329
|
+
}
|
|
6330
|
+
#checkOpeningKeys(options) {
|
|
6331
|
+
const filler = this.#checkStableKey(options);
|
|
6332
|
+
if (!filler.ok) return filler;
|
|
6333
|
+
const opening = this.#checkStableKey({ stableKey: options?.openingStableKey });
|
|
6334
|
+
if (!opening.ok) return opening;
|
|
6335
|
+
if (options?.stableKey !== void 0 && options.stableKey === options.openingStableKey) return err(specError("DUPLICATE_STABLE_KEY", `BimModel: stableKey and openingStableKey are both '${options.stableKey}'`));
|
|
6336
|
+
return ok(void 0);
|
|
6337
|
+
}
|
|
6338
|
+
addWall(spec, options) {
|
|
6339
|
+
const keyCheck = this.#checkStableKey(options);
|
|
6340
|
+
if (!keyCheck.ok) return keyCheck;
|
|
6292
6341
|
const geomResult = wallToSolid(spec);
|
|
6293
6342
|
if (!geomResult.ok) return err(geomResult.error);
|
|
6294
|
-
const id = this.#makeElement("WALL", spec, geomResult.value);
|
|
6343
|
+
const id = this.#makeElement("WALL", spec, geomResult.value, options?.stableKey);
|
|
6295
6344
|
this.#associateMaterial(id, spec);
|
|
6296
6345
|
this.#associateClassification(id, spec);
|
|
6297
6346
|
return ok(id);
|
|
6298
6347
|
}
|
|
6299
|
-
addSlab(spec) {
|
|
6348
|
+
addSlab(spec, options) {
|
|
6349
|
+
const keyCheck = this.#checkStableKey(options);
|
|
6350
|
+
if (!keyCheck.ok) return keyCheck;
|
|
6300
6351
|
const geomResult = slabToSolid(spec);
|
|
6301
6352
|
if (!geomResult.ok) return err(geomResult.error);
|
|
6302
|
-
const id = this.#makeElement("SLAB", spec, geomResult.value);
|
|
6353
|
+
const id = this.#makeElement("SLAB", spec, geomResult.value, options?.stableKey);
|
|
6303
6354
|
this.#associateMaterial(id, spec);
|
|
6304
6355
|
this.#associateClassification(id, spec);
|
|
6305
6356
|
return ok(id);
|
|
@@ -6575,7 +6626,9 @@ var BimModel = class {
|
|
|
6575
6626
|
});
|
|
6576
6627
|
return ok(id);
|
|
6577
6628
|
}
|
|
6578
|
-
addDoor(spec) {
|
|
6629
|
+
addDoor(spec, options) {
|
|
6630
|
+
const keyCheck = this.#checkOpeningKeys(options);
|
|
6631
|
+
if (!keyCheck.ok) return keyCheck;
|
|
6579
6632
|
const wall = this.#elements.get(spec.wallLocalId);
|
|
6580
6633
|
if (wall === void 0 || wall.category !== "WALL") return err(specError("DOOR_WALL_NOT_FOUND", `No wall found for localId ${spec.wallLocalId}`));
|
|
6581
6634
|
if (spec.offsetAlongWall + spec.width > wall.spec.length) return err(specError("DOOR_EXCEEDS_WALL_BOUNDS", "Door (offsetAlongWall + width) exceeds wall length"));
|
|
@@ -6590,13 +6643,13 @@ var BimModel = class {
|
|
|
6590
6643
|
const cutResult = this.#cutWallGeometry(wall, openingSpec);
|
|
6591
6644
|
if (!cutResult.ok) return err(cutResult.error);
|
|
6592
6645
|
this.#replaceWallGeometry(wall, cutResult.value);
|
|
6593
|
-
const openingId = this.#makeElement("OPENING", openingSpec, null);
|
|
6646
|
+
const openingId = this.#makeElement("OPENING", openingSpec, null, options?.openingStableKey);
|
|
6594
6647
|
this.#makeRel({
|
|
6595
6648
|
kind: "VOIDS_WALL",
|
|
6596
6649
|
wallLocalId: spec.wallLocalId,
|
|
6597
6650
|
openingLocalId: openingId
|
|
6598
6651
|
});
|
|
6599
|
-
const doorId = this.#makeElement("DOOR", spec, null);
|
|
6652
|
+
const doorId = this.#makeElement("DOOR", spec, null, options?.stableKey);
|
|
6600
6653
|
this.#makeRel({
|
|
6601
6654
|
kind: "FILLS_OPENING",
|
|
6602
6655
|
openingLocalId: openingId,
|
|
@@ -6609,7 +6662,9 @@ var BimModel = class {
|
|
|
6609
6662
|
});
|
|
6610
6663
|
return ok(doorId);
|
|
6611
6664
|
}
|
|
6612
|
-
addWindow(spec) {
|
|
6665
|
+
addWindow(spec, options) {
|
|
6666
|
+
const keyCheck = this.#checkOpeningKeys(options);
|
|
6667
|
+
if (!keyCheck.ok) return keyCheck;
|
|
6613
6668
|
const wall = this.#elements.get(spec.wallLocalId);
|
|
6614
6669
|
if (wall === void 0 || wall.category !== "WALL") return err(specError("WINDOW_WALL_NOT_FOUND", `No wall found for localId ${spec.wallLocalId}`));
|
|
6615
6670
|
if (spec.offsetAlongWall + spec.width > wall.spec.length) return err(specError("WINDOW_EXCEEDS_WALL_BOUNDS", "Window (offsetAlongWall + width) exceeds wall length"));
|
|
@@ -6624,13 +6679,13 @@ var BimModel = class {
|
|
|
6624
6679
|
const cutResult = this.#cutWallGeometry(wall, openingSpec);
|
|
6625
6680
|
if (!cutResult.ok) return err(cutResult.error);
|
|
6626
6681
|
this.#replaceWallGeometry(wall, cutResult.value);
|
|
6627
|
-
const openingId = this.#makeElement("OPENING", openingSpec, null);
|
|
6682
|
+
const openingId = this.#makeElement("OPENING", openingSpec, null, options?.openingStableKey);
|
|
6628
6683
|
this.#makeRel({
|
|
6629
6684
|
kind: "VOIDS_WALL",
|
|
6630
6685
|
wallLocalId: spec.wallLocalId,
|
|
6631
6686
|
openingLocalId: openingId
|
|
6632
6687
|
});
|
|
6633
|
-
const windowId = this.#makeElement("WINDOW", spec, null);
|
|
6688
|
+
const windowId = this.#makeElement("WINDOW", spec, null, options?.stableKey);
|
|
6634
6689
|
this.#makeRel({
|
|
6635
6690
|
kind: "FILLS_OPENING",
|
|
6636
6691
|
openingLocalId: openingId,
|
|
@@ -6643,7 +6698,9 @@ var BimModel = class {
|
|
|
6643
6698
|
});
|
|
6644
6699
|
return ok(windowId);
|
|
6645
6700
|
}
|
|
6646
|
-
addSlabOpening(input) {
|
|
6701
|
+
addSlabOpening(input, options) {
|
|
6702
|
+
const keyCheck = this.#checkStableKey(options);
|
|
6703
|
+
if (!keyCheck.ok) return keyCheck;
|
|
6647
6704
|
const slab = this.#elements.get(input.slabLocalId);
|
|
6648
6705
|
if (slab === void 0 || slab.category !== "SLAB") return err(specError("SLAB_OPENING_SLAB_NOT_FOUND", `No slab found for localId ${input.slabLocalId}`));
|
|
6649
6706
|
if (input.offsetX + input.sizeX > slab.spec.length) return err(specError("SLAB_OPENING_EXCEEDS_SLAB_BOUNDS", "Opening (offsetX + sizeX) exceeds slab length"));
|
|
@@ -6673,7 +6730,7 @@ var BimModel = class {
|
|
|
6673
6730
|
const cutResult = this.#cutSlabGeometry(slab, openingSpec);
|
|
6674
6731
|
if (!cutResult.ok) return err(cutResult.error);
|
|
6675
6732
|
this.#replaceSlabGeometry(slab, cutResult.value);
|
|
6676
|
-
const openingId = this.#makeElement("OPENING", openingSpec, null);
|
|
6733
|
+
const openingId = this.#makeElement("OPENING", openingSpec, null, options?.stableKey);
|
|
6677
6734
|
this.#makeRel({
|
|
6678
6735
|
kind: "VOIDS_SLAB",
|
|
6679
6736
|
slabLocalId: input.slabLocalId,
|
|
@@ -6918,10 +6975,14 @@ var BimModel = class {
|
|
|
6918
6975
|
getAllRelationships() {
|
|
6919
6976
|
return [...this.#relationships.values()];
|
|
6920
6977
|
}
|
|
6921
|
-
#makeElement(category, spec, geometry) {
|
|
6978
|
+
#makeElement(category, spec, geometry, stableKey) {
|
|
6922
6979
|
const localId = this.#counter.next();
|
|
6980
|
+
if (stableKey !== void 0) {
|
|
6981
|
+
if (this.#usedStableKeys.has(stableKey)) throw new Error(`BimModel: duplicate stableKey '${stableKey}'`);
|
|
6982
|
+
this.#usedStableKeys.add(stableKey);
|
|
6983
|
+
}
|
|
6923
6984
|
const el = {
|
|
6924
|
-
guid: deriveIfcGuidSync(makeElementKey(this.#modelScope, category, localId)),
|
|
6985
|
+
guid: deriveIfcGuidSync(stableKey !== void 0 ? `elem:${this.#modelScope}:${stableKey}` : makeElementKey(this.#modelScope, category, localId)),
|
|
6925
6986
|
localId,
|
|
6926
6987
|
category,
|
|
6927
6988
|
spec,
|
|
@@ -7168,9 +7229,11 @@ function stairFlightToSolid(spec) {
|
|
|
7168
7229
|
if (spec.riserHeight <= 0) return err(specError("STAIR_FLIGHT_ZERO_RISER", "Stair flight riserHeight must be positive"));
|
|
7169
7230
|
if (spec.treadLength <= 0) return err(specError("STAIR_FLIGHT_ZERO_TREAD", "Stair flight treadLength must be positive"));
|
|
7170
7231
|
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
|
|
7232
|
+
const silhouette = buildSilhouette$1(spec.numberOfRisers, spec.riserHeight, spec.treadLength);
|
|
7233
|
+
const profileResult = polygon(silhouette);
|
|
7172
7234
|
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "STAIR_FLIGHT_PROFILE_FAILED", "Failed to create stair flight silhouette profile"));
|
|
7173
|
-
const
|
|
7235
|
+
const profile = _usingCtx$5.u(profileResult.value);
|
|
7236
|
+
const solidResult = extrude(profile, [
|
|
7174
7237
|
0,
|
|
7175
7238
|
spec.width,
|
|
7176
7239
|
0
|
|
@@ -7326,7 +7389,7 @@ function isIfcSchema(value) {
|
|
|
7326
7389
|
* linear-infrastructure entities). Entities absent from every per-schema set are
|
|
7327
7390
|
* treated as schema-agnostic and supported everywhere (open-world default).
|
|
7328
7391
|
*/
|
|
7329
|
-
var IFC4X3_ONLY_ENTITIES = new Set([
|
|
7392
|
+
var IFC4X3_ONLY_ENTITIES = /* @__PURE__ */ new Set([
|
|
7330
7393
|
"IfcAlignment",
|
|
7331
7394
|
"IfcAlignmentHorizontal",
|
|
7332
7395
|
"IfcAlignmentVertical",
|
|
@@ -7406,7 +7469,8 @@ var IfcWriter = class IfcWriter {
|
|
|
7406
7469
|
try {
|
|
7407
7470
|
const api = new IfcAPI();
|
|
7408
7471
|
await initIfcApi(api);
|
|
7409
|
-
|
|
7472
|
+
const modelId = api.CreateModel({ schema: fileSchemaString(ifcSchema) });
|
|
7473
|
+
return ok(new IfcWriter(api, modelId, mvdViewDefinition, header));
|
|
7410
7474
|
} catch (e) {
|
|
7411
7475
|
return err(ifcError("IFC_INIT_FAILED", "Failed to initialize web-ifc", e));
|
|
7412
7476
|
}
|
|
@@ -7460,8 +7524,10 @@ var IfcWriter = class IfcWriter {
|
|
|
7460
7524
|
let head = new TextDecoder().decode(bytes.subarray(0, HEADER_SCAN));
|
|
7461
7525
|
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
7526
|
else console.warn("IfcWriter: FILE_NAME null-field pattern not found; author/organization/authorization left unpatched");
|
|
7463
|
-
if (this.#mvdViewDefinition.length > 0)
|
|
7464
|
-
|
|
7527
|
+
if (this.#mvdViewDefinition.length > 0) {
|
|
7528
|
+
if (VIEW_DEFINITION_RE.test(head)) head = head.replace(VIEW_DEFINITION_RE, `ViewDefinition [${this.#mvdViewDefinition}]`);
|
|
7529
|
+
else console.warn(`IfcWriter: FILE_DESCRIPTION ViewDefinition not found; MVD "${this.#mvdViewDefinition}" not declared`);
|
|
7530
|
+
}
|
|
7465
7531
|
const patchedHeadBytes = new TextEncoder().encode(head);
|
|
7466
7532
|
const tail = bytes.subarray(HEADER_SCAN);
|
|
7467
7533
|
const out = new Uint8Array(patchedHeadBytes.byteLength + tail.byteLength);
|
|
@@ -9380,9 +9446,11 @@ function rampFlightToSolid(spec) {
|
|
|
9380
9446
|
if (spec.slope <= 0) return err(specError("RAMP_FLIGHT_ZERO_SLOPE", "Ramp flight slope must be positive"));
|
|
9381
9447
|
if (spec.thickness <= 0) return err(specError("RAMP_FLIGHT_ZERO_THICKNESS", "Ramp flight thickness must be positive"));
|
|
9382
9448
|
const rise = spec.length * spec.slope;
|
|
9383
|
-
const
|
|
9449
|
+
const silhouette = buildSilhouette(spec.length, rise, spec.thickness);
|
|
9450
|
+
const profileResult = polygon(silhouette);
|
|
9384
9451
|
if (!profileResult.ok) return err(fromBrepError(profileResult.error, "RAMP_FLIGHT_PROFILE_FAILED", "Failed to create ramp flight silhouette profile"));
|
|
9385
|
-
const
|
|
9452
|
+
const profile = _usingCtx$3.u(profileResult.value);
|
|
9453
|
+
const solidResult = extrude(profile, [
|
|
9386
9454
|
0,
|
|
9387
9455
|
spec.width,
|
|
9388
9456
|
0
|
|
@@ -11708,7 +11776,7 @@ function isSlabOpening(spec) {
|
|
|
11708
11776
|
* instead, and OPENINGs are tied to their host through VOIDS rels — neither
|
|
11709
11777
|
* participates in containment, so excluding them here avoids false orphans.
|
|
11710
11778
|
*/
|
|
11711
|
-
var CONTAINABLE_CATEGORIES = new Set([
|
|
11779
|
+
var CONTAINABLE_CATEGORIES = /* @__PURE__ */ new Set([
|
|
11712
11780
|
"WALL",
|
|
11713
11781
|
"SLAB",
|
|
11714
11782
|
"BEAM",
|
|
@@ -12505,14 +12573,15 @@ async function toIfcValidated(model, meta) {
|
|
|
12505
12573
|
const geometry = collectGeometryIssues(model);
|
|
12506
12574
|
const schema = await checkSchema(bytes);
|
|
12507
12575
|
const roundTrip = await checkRoundTrip(bytes);
|
|
12576
|
+
const report = { issues: [
|
|
12577
|
+
...integrity.issues,
|
|
12578
|
+
...geometry.issues,
|
|
12579
|
+
...schema.issues,
|
|
12580
|
+
...roundTrip.issues
|
|
12581
|
+
] };
|
|
12508
12582
|
return ok({
|
|
12509
12583
|
bytes,
|
|
12510
|
-
report
|
|
12511
|
-
...integrity.issues,
|
|
12512
|
-
...geometry.issues,
|
|
12513
|
-
...schema.issues,
|
|
12514
|
-
...roundTrip.issues
|
|
12515
|
-
] }
|
|
12584
|
+
report
|
|
12516
12585
|
});
|
|
12517
12586
|
}
|
|
12518
12587
|
/**
|
|
@@ -12733,7 +12802,7 @@ var SpfReader = class SpfReader {
|
|
|
12733
12802
|
};
|
|
12734
12803
|
//#endregion
|
|
12735
12804
|
//#region src/import/spatialTree.ts
|
|
12736
|
-
var CATEGORY_BY_TYPE = new Map([
|
|
12805
|
+
var CATEGORY_BY_TYPE = /* @__PURE__ */ new Map([
|
|
12737
12806
|
[WebIFC.IFCPROJECT, "PROJECT"],
|
|
12738
12807
|
[WebIFC.IFCSITE, "SITE"],
|
|
12739
12808
|
[WebIFC.IFCBUILDING, "BUILDING"],
|
|
@@ -12915,7 +12984,8 @@ function reconstructExtrusion(reader, extrusionId, scale, worldTransform, diagno
|
|
|
12915
12984
|
const solidResult = (() => {
|
|
12916
12985
|
try {
|
|
12917
12986
|
var _usingCtx$1 = _usingCtx();
|
|
12918
|
-
|
|
12987
|
+
const face = _usingCtx$1.u(faceResult.value);
|
|
12988
|
+
return extrude(face, extrudeVec);
|
|
12919
12989
|
} catch (_) {
|
|
12920
12990
|
_usingCtx$1.e = _;
|
|
12921
12991
|
} finally {
|
|
@@ -12968,10 +13038,12 @@ function reconstructRevolution(reader, revolutionId, scale, worldTransform, diag
|
|
|
12968
13038
|
const revolved = (() => {
|
|
12969
13039
|
try {
|
|
12970
13040
|
var _usingCtx3 = _usingCtx();
|
|
12971
|
-
|
|
13041
|
+
const face = _usingCtx3.u(faceResult.value);
|
|
13042
|
+
const angleDeg = angleRaw * readPlaneAngleScale(reader) * (180 / Math.PI);
|
|
13043
|
+
return revolve(face, {
|
|
12972
13044
|
at: center,
|
|
12973
13045
|
axis: direction,
|
|
12974
|
-
angle:
|
|
13046
|
+
angle: angleDeg
|
|
12975
13047
|
});
|
|
12976
13048
|
} catch (_) {
|
|
12977
13049
|
_usingCtx3.e = _;
|
|
@@ -13086,7 +13158,7 @@ function packBinaryStl(vertices, indices, scaleToMm) {
|
|
|
13086
13158
|
}
|
|
13087
13159
|
return new Uint8Array(buffer);
|
|
13088
13160
|
}
|
|
13089
|
-
var PARAMETRIC_PROFILE_BUILDERS = new Map([
|
|
13161
|
+
var PARAMETRIC_PROFILE_BUILDERS = /* @__PURE__ */ new Map([
|
|
13090
13162
|
[WebIFC.IFCRECTANGLEPROFILEDEF, (f) => ({
|
|
13091
13163
|
kind: "RECTANGULAR",
|
|
13092
13164
|
width: f("XDim"),
|
|
@@ -13854,14 +13926,15 @@ async function fromIfc(bytes, options = {}) {
|
|
|
13854
13926
|
}
|
|
13855
13927
|
const report = appendIssues(emptyReport(), diagnostics);
|
|
13856
13928
|
const applicationName = readApplicationName(reader);
|
|
13857
|
-
|
|
13929
|
+
const model = {
|
|
13858
13930
|
schema: reader.schema,
|
|
13859
13931
|
spatialTree: spatialRoot === null ? null : mapSpatialNode(spatialRoot),
|
|
13860
13932
|
elements,
|
|
13861
13933
|
byExpressId,
|
|
13862
13934
|
diagnostics: report,
|
|
13863
13935
|
...applicationName !== void 0 ? { applicationName } : {}
|
|
13864
|
-
}
|
|
13936
|
+
};
|
|
13937
|
+
return ok(model);
|
|
13865
13938
|
} catch (e) {
|
|
13866
13939
|
return err(importError("IMPORT_FAILED", "Unexpected failure during IFC import", e));
|
|
13867
13940
|
} finally {
|
|
@@ -14888,7 +14961,7 @@ function parseSlabOpeningInput(input) {
|
|
|
14888
14961
|
* containers (PROJECT/SITE/BUILDING/STOREY), abstract groupings, and the SPACE
|
|
14889
14962
|
* category (which maps to the Space sheet, not Component) are excluded.
|
|
14890
14963
|
*/
|
|
14891
|
-
var COMPONENT_CATEGORIES = new Set([
|
|
14964
|
+
var COMPONENT_CATEGORIES = /* @__PURE__ */ new Set([
|
|
14892
14965
|
"WALL",
|
|
14893
14966
|
"SLAB",
|
|
14894
14967
|
"BEAM",
|
|
@@ -15513,9 +15586,10 @@ function parseIdsXml(xml) {
|
|
|
15513
15586
|
const title = info ? firstChild(info, "title")?.text ?? "" : "";
|
|
15514
15587
|
const specsContainer = firstChild(root, "specifications");
|
|
15515
15588
|
if (specsContainer === void 0) return err(idsError("IDS_INVALID_SCHEMA", "IDS document has no <specifications> element"));
|
|
15589
|
+
const specifications = childrenNamed(specsContainer, "specification").map(parseSpecification);
|
|
15516
15590
|
return ok({
|
|
15517
15591
|
title,
|
|
15518
|
-
specifications
|
|
15592
|
+
specifications
|
|
15519
15593
|
});
|
|
15520
15594
|
}
|
|
15521
15595
|
function parseSpecification(el) {
|
|
@@ -15872,10 +15946,7 @@ function restrictionsOf(facet) {
|
|
|
15872
15946
|
push(facet.system);
|
|
15873
15947
|
push(facet.value);
|
|
15874
15948
|
break;
|
|
15875
|
-
case "Material":
|
|
15876
|
-
push(facet.value);
|
|
15877
|
-
break;
|
|
15878
|
-
case "PartOf": break;
|
|
15949
|
+
case "Material": push(facet.value);
|
|
15879
15950
|
}
|
|
15880
15951
|
return out;
|
|
15881
15952
|
}
|
|
@@ -16275,7 +16346,8 @@ function parseBcfFiles(files) {
|
|
|
16275
16346
|
topics
|
|
16276
16347
|
});
|
|
16277
16348
|
} catch (cause) {
|
|
16278
|
-
|
|
16349
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
16350
|
+
return err(bcfError("BCF_PARSE_FAILED", `Failed to parse BCF container: ${message}`, cause));
|
|
16279
16351
|
}
|
|
16280
16352
|
}
|
|
16281
16353
|
function parseVersion(xml) {
|
|
@@ -16390,4 +16462,208 @@ function assignNum(target, key, value) {
|
|
|
16390
16462
|
if (value !== void 0) target[key] = Number(value);
|
|
16391
16463
|
}
|
|
16392
16464
|
//#endregion
|
|
16393
|
-
|
|
16465
|
+
//#region src/familiesAdapter.ts
|
|
16466
|
+
/**
|
|
16467
|
+
* brepjs-families -> BimModel adapter. Consumes a resolved element tree and
|
|
16468
|
+
* feeds each element's PRE-DESUGARED props into the parametric specs — the
|
|
16469
|
+
* spec path stays authoritative for IFC (IfcExtrudedAreaSolid + placement),
|
|
16470
|
+
* while the IR path serves the viewport and dedup. GlobalIds derive from
|
|
16471
|
+
* families key paths (stable under reordering), not insertion order.
|
|
16472
|
+
*
|
|
16473
|
+
* Scope: Storey containers, Wall/Slab elements, and wall openings — a
|
|
16474
|
+
* fill-role void (Door/Window family) maps onto addDoor/addWindow, which cut
|
|
16475
|
+
* the wall and wire IfcRelVoidsElement + IfcRelFillsElement; the opening and
|
|
16476
|
+
* filler GlobalIds derive from the synthesized key paths. Anonymous (non-fill)
|
|
16477
|
+
* voids stay IR-only and never reach the spec path.
|
|
16478
|
+
*/
|
|
16479
|
+
var SPEC_DEFAULTS = {
|
|
16480
|
+
origin: [
|
|
16481
|
+
0,
|
|
16482
|
+
0,
|
|
16483
|
+
0
|
|
16484
|
+
],
|
|
16485
|
+
axisX: [
|
|
16486
|
+
1,
|
|
16487
|
+
0,
|
|
16488
|
+
0
|
|
16489
|
+
],
|
|
16490
|
+
axisZ: [
|
|
16491
|
+
0,
|
|
16492
|
+
0,
|
|
16493
|
+
1
|
|
16494
|
+
],
|
|
16495
|
+
materialName: "Default"
|
|
16496
|
+
};
|
|
16497
|
+
var GEOMETRY_PROPS = /* @__PURE__ */ new Set([
|
|
16498
|
+
"voids",
|
|
16499
|
+
"fuse",
|
|
16500
|
+
"transform",
|
|
16501
|
+
"psets"
|
|
16502
|
+
]);
|
|
16503
|
+
/** Total of the resolved geometry's OUTER literal translate chain. The
|
|
16504
|
+
* transform vocabulary is translate-only, so frame differences are exact
|
|
16505
|
+
* subtractions. Parameter-driven translations stop the peel. */
|
|
16506
|
+
function peelTranslates(node) {
|
|
16507
|
+
const total = [
|
|
16508
|
+
0,
|
|
16509
|
+
0,
|
|
16510
|
+
0
|
|
16511
|
+
];
|
|
16512
|
+
let moved = false;
|
|
16513
|
+
let cur = node;
|
|
16514
|
+
while (cur.kind === "Translate") {
|
|
16515
|
+
const v = cur.vector;
|
|
16516
|
+
if (v.kind !== "Vec3Lit") break;
|
|
16517
|
+
total[0] += v.value[0];
|
|
16518
|
+
total[1] += v.value[1];
|
|
16519
|
+
total[2] += v.value[2];
|
|
16520
|
+
moved = true;
|
|
16521
|
+
cur = cur.target;
|
|
16522
|
+
}
|
|
16523
|
+
return {
|
|
16524
|
+
total,
|
|
16525
|
+
moved
|
|
16526
|
+
};
|
|
16527
|
+
}
|
|
16528
|
+
/** Fold the resolved geometry's outer translate chain into the spec placement
|
|
16529
|
+
* origin, so IfcLocalPlacement matches the IR world frame no matter where the
|
|
16530
|
+
* transform came from (family-internal or prop-level). */
|
|
16531
|
+
function composedOrigin(el) {
|
|
16532
|
+
const base = el.props["origin"] ?? [
|
|
16533
|
+
0,
|
|
16534
|
+
0,
|
|
16535
|
+
0
|
|
16536
|
+
];
|
|
16537
|
+
const { total, moved } = peelTranslates(el.geometry);
|
|
16538
|
+
if (!moved && el.props["origin"] === void 0) return void 0;
|
|
16539
|
+
return [
|
|
16540
|
+
base[0] + total[0],
|
|
16541
|
+
base[1] + total[1],
|
|
16542
|
+
base[2] + total[2]
|
|
16543
|
+
];
|
|
16544
|
+
}
|
|
16545
|
+
function stripGeometryProps(props) {
|
|
16546
|
+
const out = {};
|
|
16547
|
+
for (const [k, v] of Object.entries(props)) if (!GEOMETRY_PROPS.has(k)) out[k] = v;
|
|
16548
|
+
return out;
|
|
16549
|
+
}
|
|
16550
|
+
function specInput(el) {
|
|
16551
|
+
const origin = composedOrigin(el);
|
|
16552
|
+
return {
|
|
16553
|
+
...SPEC_DEFAULTS,
|
|
16554
|
+
...stripGeometryProps(el.props),
|
|
16555
|
+
...origin ? { origin } : {},
|
|
16556
|
+
...collectSpecProps(el)
|
|
16557
|
+
};
|
|
16558
|
+
}
|
|
16559
|
+
function collectSpecProps(el) {
|
|
16560
|
+
const psets = el.attributes["psets"];
|
|
16561
|
+
const out = {};
|
|
16562
|
+
if (psets && typeof psets === "object") {
|
|
16563
|
+
const common = psets["Pset_WallCommon"];
|
|
16564
|
+
if (common && typeof common === "object") {
|
|
16565
|
+
const c = common;
|
|
16566
|
+
if (typeof c["IsExternal"] === "boolean") out["isExternal"] = c["IsExternal"];
|
|
16567
|
+
if (typeof c["FireRating"] === "string") out["fireRating"] = c["FireRating"];
|
|
16568
|
+
}
|
|
16569
|
+
}
|
|
16570
|
+
delete out["psets"];
|
|
16571
|
+
return out;
|
|
16572
|
+
}
|
|
16573
|
+
function addFill(model, fill, input, identity) {
|
|
16574
|
+
if (fill.type === "Door") {
|
|
16575
|
+
const parsed = parseDoorSpec(input);
|
|
16576
|
+
if (!parsed.ok) return parsed;
|
|
16577
|
+
return model.addDoor(parsed.value, identity);
|
|
16578
|
+
}
|
|
16579
|
+
if (fill.type === "Window") {
|
|
16580
|
+
const parsed = parseWindowSpec(input);
|
|
16581
|
+
if (!parsed.ok) return parsed;
|
|
16582
|
+
return model.addWindow(parsed.value, identity);
|
|
16583
|
+
}
|
|
16584
|
+
return err(specError("FAMILIES_UNSUPPORTED_FILL", `familiesToBim: no fill mapping for element type '${fill.type}' at '${fill.keyPath}'`));
|
|
16585
|
+
}
|
|
16586
|
+
/** Map a wall's synthesized Opening children onto addDoor/addWindow. The
|
|
16587
|
+
* wall-relative offsets come from the void geometry's frame minus the host's:
|
|
16588
|
+
* exact because both carry the same outer host transform. */
|
|
16589
|
+
function addOpenings(model, host, wallId, containerId, idByKeyPath) {
|
|
16590
|
+
const hostT = peelTranslates(host.geometry).total;
|
|
16591
|
+
for (const opening of host.children) {
|
|
16592
|
+
if (opening.type !== "Opening") continue;
|
|
16593
|
+
const fill = opening.children[0];
|
|
16594
|
+
if (fill === void 0) return err(specError("FAMILIES_OPENING_NO_FILL", `familiesToBim: opening '${opening.keyPath}' has no fill element`));
|
|
16595
|
+
const fillT = peelTranslates(opening.geometry).total;
|
|
16596
|
+
const added = addFill(model, fill, {
|
|
16597
|
+
materialName: SPEC_DEFAULTS.materialName,
|
|
16598
|
+
...stripGeometryProps(fill.props),
|
|
16599
|
+
wallLocalId: wallId,
|
|
16600
|
+
offsetAlongWall: fillT[0] - hostT[0],
|
|
16601
|
+
offsetFromFloor: fillT[2] - hostT[2]
|
|
16602
|
+
}, {
|
|
16603
|
+
stableKey: fill.keyPath,
|
|
16604
|
+
openingStableKey: opening.keyPath
|
|
16605
|
+
});
|
|
16606
|
+
if (!added.ok) return added;
|
|
16607
|
+
model.placeIn(added.value, containerId);
|
|
16608
|
+
idByKeyPath.set(fill.keyPath, added.value);
|
|
16609
|
+
const fillsRel = model.getAllRelationships().find((r) => r.kind === "FILLS_OPENING" && r.fillerLocalId === added.value);
|
|
16610
|
+
if (fillsRel !== void 0) idByKeyPath.set(opening.keyPath, fillsRel.openingLocalId);
|
|
16611
|
+
}
|
|
16612
|
+
return ok(void 0);
|
|
16613
|
+
}
|
|
16614
|
+
/**
|
|
16615
|
+
* Project a resolved families tree into an eager BimModel. The caller owns
|
|
16616
|
+
* the returned model (`using`); families stays domain-neutral — this adapter
|
|
16617
|
+
* is where families types meet the IFC vocabulary.
|
|
16618
|
+
*/
|
|
16619
|
+
function familiesToBim(root, options) {
|
|
16620
|
+
const model = new BimModel();
|
|
16621
|
+
const initResult = model.init(options.project);
|
|
16622
|
+
if (!initResult.ok) return initResult;
|
|
16623
|
+
const siteId = model.addSite({ name: options.siteName ?? "Site" });
|
|
16624
|
+
const buildingId = model.addBuilding({ name: options.buildingName ?? "Building" });
|
|
16625
|
+
model.aggregate(siteId, buildingId);
|
|
16626
|
+
const idByKeyPath = /* @__PURE__ */ new Map();
|
|
16627
|
+
const walk = (el, storeyId) => {
|
|
16628
|
+
let containerId = storeyId;
|
|
16629
|
+
if (el.type === "Storey") {
|
|
16630
|
+
const id = model.addStorey({
|
|
16631
|
+
name: el.attributes["name"] ?? el.keyPath,
|
|
16632
|
+
elevation: el.props["elevation"] ?? 0
|
|
16633
|
+
}, { stableKey: el.keyPath });
|
|
16634
|
+
model.aggregate(buildingId, id);
|
|
16635
|
+
idByKeyPath.set(el.keyPath, id);
|
|
16636
|
+
containerId = id;
|
|
16637
|
+
} else if (el.type === "Wall" || el.type === "Slab") {
|
|
16638
|
+
const parsed = el.type === "Wall" ? parseWallSpec(specInput(el)) : parseSlabSpec(specInput(el));
|
|
16639
|
+
if (!parsed.ok) return parsed;
|
|
16640
|
+
const added = el.type === "Wall" ? model.addWall(parsed.value, { stableKey: el.keyPath }) : model.addSlab(parsed.value, { stableKey: el.keyPath });
|
|
16641
|
+
if (!added.ok) return added;
|
|
16642
|
+
idByKeyPath.set(el.keyPath, added.value);
|
|
16643
|
+
if (containerId === null) return err(specError("FAMILIES_NO_STOREY", `familiesToBim: '${el.keyPath}' has no Storey ancestor — IFC elements need spatial containment`));
|
|
16644
|
+
model.placeIn(added.value, containerId);
|
|
16645
|
+
if (el.type === "Wall") {
|
|
16646
|
+
const opened = addOpenings(model, el, added.value, containerId, idByKeyPath);
|
|
16647
|
+
if (!opened.ok) return opened;
|
|
16648
|
+
}
|
|
16649
|
+
} else if (el.type === "Opening") return err(specError("FAMILIES_OPENING_OUTSIDE_WALL", `familiesToBim: opening '${el.keyPath}' is not hosted by a Wall — only wall openings are mapped`));
|
|
16650
|
+
else if (el.type !== "Group" && el.geometry.kind !== "Empty") return err(specError("FAMILIES_UNSUPPORTED_TYPE", `familiesToBim: no spec mapping for element type '${el.type}' at '${el.keyPath}'`));
|
|
16651
|
+
for (const child of el.children) {
|
|
16652
|
+
if (el.type === "Wall" && child.type === "Opening") continue;
|
|
16653
|
+
const r = walk(child, containerId);
|
|
16654
|
+
if (!r.ok) return r;
|
|
16655
|
+
}
|
|
16656
|
+
return ok(void 0);
|
|
16657
|
+
};
|
|
16658
|
+
const walked = walk(root, null);
|
|
16659
|
+
if (!walked.ok) {
|
|
16660
|
+
model[Symbol.dispose]();
|
|
16661
|
+
return walked;
|
|
16662
|
+
}
|
|
16663
|
+
return ok({
|
|
16664
|
+
model,
|
|
16665
|
+
idByKeyPath
|
|
16666
|
+
});
|
|
16667
|
+
}
|
|
16668
|
+
//#endregion
|
|
16669
|
+
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, familiesToBim, 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 };
|