brepjs-bim 0.14.0 → 0.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -67,15 +67,16 @@ Author a small model and export IFC:
67
67
 
68
68
  ```ts
69
69
  import { BimModel, toIfc } from 'brepjs-bim';
70
+ import { unwrap } from 'brepjs';
70
71
 
71
72
  const model = new BimModel();
72
73
  model.init({ name: 'Example' });
73
74
 
74
75
  // Spatial structure: project → site → building → storey.
75
76
  const project = model.getProject();
76
- const siteId = model.addSite({ name: 'Site' });
77
- const buildingId = model.addBuilding({ name: 'Building' });
78
- const storeyId = model.addStorey({ name: 'Level 1', elevation: 0 });
77
+ const siteId = unwrap(model.addSite({ name: 'Site' }));
78
+ const buildingId = unwrap(model.addBuilding({ name: 'Building' }));
79
+ const storeyId = unwrap(model.addStorey({ name: 'Level 1', elevation: 0 }));
79
80
  if (project) model.aggregate(project.localId, siteId);
80
81
  model.aggregate(siteId, buildingId);
81
82
  model.aggregate(buildingId, storeyId);
@@ -6334,14 +6334,20 @@ var BimModel = class {
6334
6334
  for (const mullion of el.geometry.mullions) mullion.solid[Symbol.dispose]();
6335
6335
  }
6336
6336
  }
6337
- addSite(spec) {
6338
- return this.#makeElement("SITE", spec, null);
6337
+ addSite(spec, options) {
6338
+ const keyCheck = this.#checkStableKey(options);
6339
+ if (!keyCheck.ok) return keyCheck;
6340
+ return (0, brepjs.ok)(this.#makeElement("SITE", spec, null, options?.stableKey));
6339
6341
  }
6340
6342
  addBuilding(spec, options) {
6341
- return this.#makeElement("BUILDING", spec, null, options?.stableKey);
6343
+ const keyCheck = this.#checkStableKey(options);
6344
+ if (!keyCheck.ok) return keyCheck;
6345
+ return (0, brepjs.ok)(this.#makeElement("BUILDING", spec, null, options?.stableKey));
6342
6346
  }
6343
6347
  addStorey(spec, options) {
6344
- return this.#makeElement("STOREY", spec, null, options?.stableKey);
6348
+ const keyCheck = this.#checkStableKey(options);
6349
+ if (!keyCheck.ok) return keyCheck;
6350
+ return (0, brepjs.ok)(this.#makeElement("STOREY", spec, null, options?.stableKey));
6345
6351
  }
6346
6352
  /** Reject a duplicate stableKey BEFORE any geometry is built, so the
6347
6353
  * Result-returning adders never allocate a solid they cannot store. */
@@ -6508,24 +6514,30 @@ var BimModel = class {
6508
6514
  * attach parts with {@link aggregate} (IfcRelAggregates) or {@link nest}
6509
6515
  * (IfcRelNests, order-preserving). Returns the assembly's localId.
6510
6516
  */
6511
- addElementAssembly(spec) {
6512
- return this.#makeElement("ELEMENT_ASSEMBLY", spec, null);
6517
+ addElementAssembly(spec, options) {
6518
+ const keyCheck = this.#checkStableKey(options);
6519
+ if (!keyCheck.ok) return keyCheck;
6520
+ return (0, brepjs.ok)(this.#makeElement("ELEMENT_ASSEMBLY", spec, null, options?.stableKey));
6513
6521
  }
6514
6522
  /**
6515
6523
  * Adds an IfcZone grouping object (a thermal/fire/occupancy zone). The zone
6516
6524
  * carries no geometry; attach members (spaces or other elements) with
6517
- * {@link assignToGroup}. Returns the zone's localId.
6525
+ * {@link assignToGroup}. Returns the zone's localId as a Result.
6518
6526
  */
6519
- addZone(spec) {
6520
- return this.#makeElement("ZONE", spec, null);
6527
+ addZone(spec, options) {
6528
+ const keyCheck = this.#checkStableKey(options);
6529
+ if (!keyCheck.ok) return keyCheck;
6530
+ return (0, brepjs.ok)(this.#makeElement("ZONE", spec, null, options?.stableKey));
6521
6531
  }
6522
6532
  /**
6523
6533
  * Adds an IfcSystem grouping object (an HVAC/electrical/plumbing system). The
6524
6534
  * system carries no geometry; attach members with {@link assignToGroup}.
6525
- * Returns the system's localId.
6535
+ * Returns the system's localId as a Result.
6526
6536
  */
6527
- addSystem(spec) {
6528
- return this.#makeElement("SYSTEM", spec, null);
6537
+ addSystem(spec, options) {
6538
+ const keyCheck = this.#checkStableKey(options);
6539
+ if (!keyCheck.ok) return keyCheck;
6540
+ return (0, brepjs.ok)(this.#makeElement("SYSTEM", spec, null, options?.stableKey));
6529
6541
  }
6530
6542
  /**
6531
6543
  * Assigns members to a zone or system via IfcRelAssignsToGroup. Repeated calls
@@ -7496,6 +7508,12 @@ var FILE_NAME_RE = /(FILE_NAME\('[^']*','[^']*',)(?:\$|\(\$\)),(?:\$|\(\$\)),('[
7496
7508
  function stepString(value) {
7497
7509
  return `'${value.replace(/'/g, "''")}'`;
7498
7510
  }
7511
+ var STRING_OR_BARE_REAL_RE = /'(?:[^']|'')*'|(?<![\d.])(-?\d+)E([+-]?\d+)/g;
7512
+ function normalizeStepReals(bytes) {
7513
+ const text = new TextDecoder().decode(bytes);
7514
+ const fixed = text.replace(STRING_OR_BARE_REAL_RE, (match, mantissa, exponent) => mantissa === void 0 || exponent === void 0 ? match : `${mantissa}.E${exponent}`);
7515
+ return fixed === text ? bytes : new TextEncoder().encode(fixed);
7516
+ }
7499
7517
  var IfcWriter = class IfcWriter {
7500
7518
  #api;
7501
7519
  #modelId;
@@ -7551,7 +7569,7 @@ var IfcWriter = class IfcWriter {
7551
7569
  if (this.#closed) return (0, brepjs.err)(ifcError("IFC_ALREADY_SAVED", "Model has already been saved and closed"));
7552
7570
  try {
7553
7571
  const bytes = this.#api.SaveModel(this.#modelId);
7554
- return (0, brepjs.ok)(this.#patchHeader(bytes));
7572
+ return (0, brepjs.ok)(normalizeStepReals(this.#patchHeader(bytes)));
7555
7573
  } catch (e) {
7556
7574
  return (0, brepjs.err)(ifcError("IFC_SAVE_FAILED", "Failed to serialize IFC model", e));
7557
7575
  } finally {
@@ -16747,9 +16765,18 @@ function familiesToBim(root, options) {
16747
16765
  const model = new BimModel();
16748
16766
  const initResult = model.init(options.project);
16749
16767
  if (!initResult.ok) return initResult;
16750
- const siteId = model.addSite({ name: options.siteName ?? "Site" });
16751
- const buildingId = model.addBuilding({ name: options.buildingName ?? "Building" });
16752
- model.aggregate(siteId, buildingId);
16768
+ const siteResult = model.addSite({ name: options.siteName ?? "Site" });
16769
+ if (!siteResult.ok) {
16770
+ model[Symbol.dispose]();
16771
+ return siteResult;
16772
+ }
16773
+ const buildingResult = model.addBuilding({ name: options.buildingName ?? "Building" });
16774
+ if (!buildingResult.ok) {
16775
+ model[Symbol.dispose]();
16776
+ return buildingResult;
16777
+ }
16778
+ const buildingId = buildingResult.value;
16779
+ model.aggregate(siteResult.value, buildingId);
16753
16780
  const idByKeyPath = /* @__PURE__ */ new Map();
16754
16781
  const walk = (el, storeyId) => {
16755
16782
  let containerId = storeyId;
@@ -16757,13 +16784,14 @@ function familiesToBim(root, options) {
16757
16784
  if (el.type === "Storey") {
16758
16785
  const keyed = requireKeyed(el);
16759
16786
  if (!keyed.ok) return keyed;
16760
- const id = model.addStorey({
16787
+ const storeyResult = model.addStorey({
16761
16788
  name: el.attributes["name"] ?? el.keyPath,
16762
16789
  elevation: el.props["elevation"] ?? 0
16763
16790
  }, { stableKey: el.keyPath });
16764
- model.aggregate(buildingId, id);
16765
- idByKeyPath.set(el.keyPath, id);
16766
- containerId = id;
16791
+ if (!storeyResult.ok) return storeyResult;
16792
+ model.aggregate(buildingId, storeyResult.value);
16793
+ idByKeyPath.set(el.keyPath, storeyResult.value);
16794
+ containerId = storeyResult.value;
16767
16795
  } else if (route !== void 0) {
16768
16796
  const keyed = requireKeyed(el);
16769
16797
  if (!keyed.ok) return keyed;
@@ -6311,14 +6311,20 @@ var BimModel = class {
6311
6311
  for (const mullion of el.geometry.mullions) mullion.solid[Symbol.dispose]();
6312
6312
  }
6313
6313
  }
6314
- addSite(spec) {
6315
- return this.#makeElement("SITE", spec, null);
6314
+ addSite(spec, options) {
6315
+ const keyCheck = this.#checkStableKey(options);
6316
+ if (!keyCheck.ok) return keyCheck;
6317
+ return ok(this.#makeElement("SITE", spec, null, options?.stableKey));
6316
6318
  }
6317
6319
  addBuilding(spec, options) {
6318
- return this.#makeElement("BUILDING", spec, null, options?.stableKey);
6320
+ const keyCheck = this.#checkStableKey(options);
6321
+ if (!keyCheck.ok) return keyCheck;
6322
+ return ok(this.#makeElement("BUILDING", spec, null, options?.stableKey));
6319
6323
  }
6320
6324
  addStorey(spec, options) {
6321
- return this.#makeElement("STOREY", spec, null, options?.stableKey);
6325
+ const keyCheck = this.#checkStableKey(options);
6326
+ if (!keyCheck.ok) return keyCheck;
6327
+ return ok(this.#makeElement("STOREY", spec, null, options?.stableKey));
6322
6328
  }
6323
6329
  /** Reject a duplicate stableKey BEFORE any geometry is built, so the
6324
6330
  * Result-returning adders never allocate a solid they cannot store. */
@@ -6485,24 +6491,30 @@ var BimModel = class {
6485
6491
  * attach parts with {@link aggregate} (IfcRelAggregates) or {@link nest}
6486
6492
  * (IfcRelNests, order-preserving). Returns the assembly's localId.
6487
6493
  */
6488
- addElementAssembly(spec) {
6489
- return this.#makeElement("ELEMENT_ASSEMBLY", spec, null);
6494
+ addElementAssembly(spec, options) {
6495
+ const keyCheck = this.#checkStableKey(options);
6496
+ if (!keyCheck.ok) return keyCheck;
6497
+ return ok(this.#makeElement("ELEMENT_ASSEMBLY", spec, null, options?.stableKey));
6490
6498
  }
6491
6499
  /**
6492
6500
  * Adds an IfcZone grouping object (a thermal/fire/occupancy zone). The zone
6493
6501
  * carries no geometry; attach members (spaces or other elements) with
6494
- * {@link assignToGroup}. Returns the zone's localId.
6502
+ * {@link assignToGroup}. Returns the zone's localId as a Result.
6495
6503
  */
6496
- addZone(spec) {
6497
- return this.#makeElement("ZONE", spec, null);
6504
+ addZone(spec, options) {
6505
+ const keyCheck = this.#checkStableKey(options);
6506
+ if (!keyCheck.ok) return keyCheck;
6507
+ return ok(this.#makeElement("ZONE", spec, null, options?.stableKey));
6498
6508
  }
6499
6509
  /**
6500
6510
  * Adds an IfcSystem grouping object (an HVAC/electrical/plumbing system). The
6501
6511
  * system carries no geometry; attach members with {@link assignToGroup}.
6502
- * Returns the system's localId.
6512
+ * Returns the system's localId as a Result.
6503
6513
  */
6504
- addSystem(spec) {
6505
- return this.#makeElement("SYSTEM", spec, null);
6514
+ addSystem(spec, options) {
6515
+ const keyCheck = this.#checkStableKey(options);
6516
+ if (!keyCheck.ok) return keyCheck;
6517
+ return ok(this.#makeElement("SYSTEM", spec, null, options?.stableKey));
6506
6518
  }
6507
6519
  /**
6508
6520
  * Assigns members to a zone or system via IfcRelAssignsToGroup. Repeated calls
@@ -7473,6 +7485,12 @@ var FILE_NAME_RE = /(FILE_NAME\('[^']*','[^']*',)(?:\$|\(\$\)),(?:\$|\(\$\)),('[
7473
7485
  function stepString(value) {
7474
7486
  return `'${value.replace(/'/g, "''")}'`;
7475
7487
  }
7488
+ var STRING_OR_BARE_REAL_RE = /'(?:[^']|'')*'|(?<![\d.])(-?\d+)E([+-]?\d+)/g;
7489
+ function normalizeStepReals(bytes) {
7490
+ const text = new TextDecoder().decode(bytes);
7491
+ const fixed = text.replace(STRING_OR_BARE_REAL_RE, (match, mantissa, exponent) => mantissa === void 0 || exponent === void 0 ? match : `${mantissa}.E${exponent}`);
7492
+ return fixed === text ? bytes : new TextEncoder().encode(fixed);
7493
+ }
7476
7494
  var IfcWriter = class IfcWriter {
7477
7495
  #api;
7478
7496
  #modelId;
@@ -7528,7 +7546,7 @@ var IfcWriter = class IfcWriter {
7528
7546
  if (this.#closed) return err(ifcError("IFC_ALREADY_SAVED", "Model has already been saved and closed"));
7529
7547
  try {
7530
7548
  const bytes = this.#api.SaveModel(this.#modelId);
7531
- return ok(this.#patchHeader(bytes));
7549
+ return ok(normalizeStepReals(this.#patchHeader(bytes)));
7532
7550
  } catch (e) {
7533
7551
  return err(ifcError("IFC_SAVE_FAILED", "Failed to serialize IFC model", e));
7534
7552
  } finally {
@@ -16724,9 +16742,18 @@ function familiesToBim(root, options) {
16724
16742
  const model = new BimModel();
16725
16743
  const initResult = model.init(options.project);
16726
16744
  if (!initResult.ok) return initResult;
16727
- const siteId = model.addSite({ name: options.siteName ?? "Site" });
16728
- const buildingId = model.addBuilding({ name: options.buildingName ?? "Building" });
16729
- model.aggregate(siteId, buildingId);
16745
+ const siteResult = model.addSite({ name: options.siteName ?? "Site" });
16746
+ if (!siteResult.ok) {
16747
+ model[Symbol.dispose]();
16748
+ return siteResult;
16749
+ }
16750
+ const buildingResult = model.addBuilding({ name: options.buildingName ?? "Building" });
16751
+ if (!buildingResult.ok) {
16752
+ model[Symbol.dispose]();
16753
+ return buildingResult;
16754
+ }
16755
+ const buildingId = buildingResult.value;
16756
+ model.aggregate(siteResult.value, buildingId);
16730
16757
  const idByKeyPath = /* @__PURE__ */ new Map();
16731
16758
  const walk = (el, storeyId) => {
16732
16759
  let containerId = storeyId;
@@ -16734,13 +16761,14 @@ function familiesToBim(root, options) {
16734
16761
  if (el.type === "Storey") {
16735
16762
  const keyed = requireKeyed(el);
16736
16763
  if (!keyed.ok) return keyed;
16737
- const id = model.addStorey({
16764
+ const storeyResult = model.addStorey({
16738
16765
  name: el.attributes["name"] ?? el.keyPath,
16739
16766
  elevation: el.props["elevation"] ?? 0
16740
16767
  }, { stableKey: el.keyPath });
16741
- model.aggregate(buildingId, id);
16742
- idByKeyPath.set(el.keyPath, id);
16743
- containerId = id;
16768
+ if (!storeyResult.ok) return storeyResult;
16769
+ model.aggregate(buildingId, storeyResult.value);
16770
+ idByKeyPath.set(el.keyPath, storeyResult.value);
16771
+ containerId = storeyResult.value;
16744
16772
  } else if (route !== void 0) {
16745
16773
  const keyed = requireKeyed(el);
16746
16774
  if (!keyed.ok) return keyed;
@@ -37,9 +37,9 @@ export declare class BimModel {
37
37
  #private;
38
38
  init(spec: ProjectSpec): Result<LocalId, BimError>;
39
39
  [Symbol.dispose](): void;
40
- addSite(spec: SiteSpec): LocalId;
41
- addBuilding(spec: BuildingSpec, options?: ElementIdentityOptions): LocalId;
42
- addStorey(spec: StoreySpec, options?: ElementIdentityOptions): LocalId;
40
+ addSite(spec: SiteSpec, options?: ElementIdentityOptions): Result<LocalId, BimError>;
41
+ addBuilding(spec: BuildingSpec, options?: ElementIdentityOptions): Result<LocalId, BimError>;
42
+ addStorey(spec: StoreySpec, options?: ElementIdentityOptions): Result<LocalId, BimError>;
43
43
  addWall(spec: WallSpec, options?: ElementIdentityOptions): Result<LocalId, BimError>;
44
44
  addSlab(spec: SlabSpec, options?: ElementIdentityOptions): Result<LocalId, BimError>;
45
45
  addBeam(spec: BeamSpec, options?: ElementIdentityOptions): Result<LocalId, BimError>;
@@ -72,19 +72,19 @@ export declare class BimModel {
72
72
  * attach parts with {@link aggregate} (IfcRelAggregates) or {@link nest}
73
73
  * (IfcRelNests, order-preserving). Returns the assembly's localId.
74
74
  */
75
- addElementAssembly(spec: ElementAssemblySpec): LocalId;
75
+ addElementAssembly(spec: ElementAssemblySpec, options?: ElementIdentityOptions): Result<LocalId, BimError>;
76
76
  /**
77
77
  * Adds an IfcZone grouping object (a thermal/fire/occupancy zone). The zone
78
78
  * carries no geometry; attach members (spaces or other elements) with
79
- * {@link assignToGroup}. Returns the zone's localId.
79
+ * {@link assignToGroup}. Returns the zone's localId as a Result.
80
80
  */
81
- addZone(spec: ZoneSpec): LocalId;
81
+ addZone(spec: ZoneSpec, options?: ElementIdentityOptions): Result<LocalId, BimError>;
82
82
  /**
83
83
  * Adds an IfcSystem grouping object (an HVAC/electrical/plumbing system). The
84
84
  * system carries no geometry; attach members with {@link assignToGroup}.
85
- * Returns the system's localId.
85
+ * Returns the system's localId as a Result.
86
86
  */
87
- addSystem(spec: SystemSpec): LocalId;
87
+ addSystem(spec: SystemSpec, options?: ElementIdentityOptions): Result<LocalId, BimError>;
88
88
  /**
89
89
  * Assigns members to a zone or system via IfcRelAssignsToGroup. Repeated calls
90
90
  * for the same group extend the single relationship in call order. Returns the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brepjs-bim",
3
- "version": "0.14.0",
3
+ "version": "0.15.1",
4
4
  "description": "BIM layer for brepjs — IFC4-aligned parametric building elements",
5
5
  "keywords": [
6
6
  "bim",