brepjs-bim 0.18.0 → 0.19.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.
@@ -535,7 +535,7 @@ var propertyKeyTypes = /* @__PURE__*/ new Set([
535
535
  function escapeRegex(str) {
536
536
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
537
537
  }
538
- function clone(inst, def, params) {
538
+ function clone$1(inst, def, params) {
539
539
  const cl = new inst._zod.constr(def ?? inst._zod.def);
540
540
  if (!def || params?.parent) cl._zod.parent = inst;
541
541
  return cl;
@@ -571,7 +571,7 @@ function pick(schema, mask) {
571
571
  const currDef = schema._zod.def;
572
572
  const checks = currDef.checks;
573
573
  if (checks && checks.length > 0) throw new Error(".pick() cannot be used on object schemas containing refinements");
574
- return clone(schema, mergeDefs(schema._zod.def, {
574
+ return clone$1(schema, mergeDefs(schema._zod.def, {
575
575
  get shape() {
576
576
  const newShape = {};
577
577
  for (const key in mask) {
@@ -589,7 +589,7 @@ function omit(schema, mask) {
589
589
  const currDef = schema._zod.def;
590
590
  const checks = currDef.checks;
591
591
  if (checks && checks.length > 0) throw new Error(".omit() cannot be used on object schemas containing refinements");
592
- return clone(schema, mergeDefs(schema._zod.def, {
592
+ return clone$1(schema, mergeDefs(schema._zod.def, {
593
593
  get shape() {
594
594
  const newShape = { ...schema._zod.def.shape };
595
595
  for (const key in mask) {
@@ -610,7 +610,7 @@ function extend(schema, shape) {
610
610
  const existingShape = schema._zod.def.shape;
611
611
  for (const key in shape) if (Object.getOwnPropertyDescriptor(existingShape, key) !== void 0) throw new Error("Cannot overwrite keys on object schemas containing refinements. Use `.safeExtend()` instead.");
612
612
  }
613
- return clone(schema, mergeDefs(schema._zod.def, { get shape() {
613
+ return clone$1(schema, mergeDefs(schema._zod.def, { get shape() {
614
614
  const _shape = {
615
615
  ...schema._zod.def.shape,
616
616
  ...shape
@@ -621,7 +621,7 @@ function extend(schema, shape) {
621
621
  }
622
622
  function safeExtend(schema, shape) {
623
623
  if (!isPlainObject(shape)) throw new Error("Invalid input to safeExtend: expected a plain object");
624
- return clone(schema, mergeDefs(schema._zod.def, { get shape() {
624
+ return clone$1(schema, mergeDefs(schema._zod.def, { get shape() {
625
625
  const _shape = {
626
626
  ...schema._zod.def.shape,
627
627
  ...shape
@@ -632,7 +632,7 @@ function safeExtend(schema, shape) {
632
632
  }
633
633
  function merge(a, b) {
634
634
  if (a._zod.def.checks?.length) throw new Error(".merge() cannot be used on object schemas containing refinements. Use .safeExtend() instead.");
635
- return clone(a, mergeDefs(a._zod.def, {
635
+ return clone$1(a, mergeDefs(a._zod.def, {
636
636
  get shape() {
637
637
  const _shape = {
638
638
  ...a._zod.def.shape,
@@ -650,7 +650,7 @@ function merge(a, b) {
650
650
  function partial(Class, schema, mask) {
651
651
  const checks = schema._zod.def.checks;
652
652
  if (checks && checks.length > 0) throw new Error(".partial() cannot be used on object schemas containing refinements");
653
- return clone(schema, mergeDefs(schema._zod.def, {
653
+ return clone$1(schema, mergeDefs(schema._zod.def, {
654
654
  get shape() {
655
655
  const oldShape = schema._zod.def.shape;
656
656
  const shape = { ...oldShape };
@@ -673,7 +673,7 @@ function partial(Class, schema, mask) {
673
673
  }));
674
674
  }
675
675
  function required(Class, schema, mask) {
676
- return clone(schema, mergeDefs(schema._zod.def, { get shape() {
676
+ return clone$1(schema, mergeDefs(schema._zod.def, { get shape() {
677
677
  const oldShape = schema._zod.def.shape;
678
678
  const shape = { ...oldShape };
679
679
  if (mask) for (const key in mask) {
@@ -4042,7 +4042,7 @@ var ZodType = /*@__PURE__*/ $constructor("ZodType", (inst, def) => {
4042
4042
  return this.check(...chks);
4043
4043
  },
4044
4044
  clone(def, params) {
4045
- return clone(this, def, params);
4045
+ return clone$1(this, def, params);
4046
4046
  },
4047
4047
  brand() {
4048
4048
  return this;
@@ -27828,6 +27828,36 @@ function addFill(model, fill, input, identity) {
27828
27828
  }
27829
27829
  return (0, brepjs.err)(specError("FAMILIES_UNSUPPORTED_FILL", `familiesToBim: no fill mapping for element type '${fill.type}' at '${fill.keyPath}'`));
27830
27830
  }
27831
+ /** Materialize an unrouted element's IR and add it as a proxy. The body is
27832
+ * authoritative for a proxy (no parametric spec to diverge from), so baked
27833
+ * transforms — rotations included — are fine here. */
27834
+ function addProxyElement(model, el, evaluator) {
27835
+ const evaluated = evaluator.evaluate(el.geometry);
27836
+ if (!evaluated.ok) return (0, brepjs.err)(specError("FAMILIES_PROXY_EVAL_FAILED", `familiesToBim: '${el.keyPath}' failed to materialize for the proxy route: ${evaluated.error.message}`, evaluated.error));
27837
+ let source = evaluated.value;
27838
+ if (!(0, brepjs.isSolid)(source)) {
27839
+ const solids = (0, brepjs.getSolids)(source);
27840
+ const only = solids.length === 1 ? solids[0] : void 0;
27841
+ if (only === void 0) return (0, brepjs.err)(specError("FAMILIES_PROXY_NOT_SOLID", `familiesToBim: '${el.keyPath}' materialized to ${solids.length} solids — a proxy body must be exactly one`));
27842
+ source = only;
27843
+ }
27844
+ const copy = (0, brepjs.clone)(source);
27845
+ if (!copy.ok) return (0, brepjs.err)(specError("FAMILIES_PROXY_EVAL_FAILED", `familiesToBim: '${el.keyPath}' could not copy the materialized body`, copy.error));
27846
+ const valid = (0, brepjs.validSolid)(copy.value);
27847
+ if (!valid.ok) {
27848
+ copy.value[Symbol.dispose]();
27849
+ return (0, brepjs.err)(specError("FAMILIES_PROXY_INVALID", `familiesToBim: '${el.keyPath}' materialized to an invalid solid: ${valid.error}`));
27850
+ }
27851
+ const nameAttr = el.attributes["name"];
27852
+ const materialProp = el.props["materialName"];
27853
+ const specProps = collectSpecProps(el);
27854
+ return model.addProxy({
27855
+ name: typeof nameAttr === "string" ? nameAttr : el.type,
27856
+ solid: valid.value,
27857
+ materialName: typeof materialProp === "string" ? materialProp : specProps["materialName"],
27858
+ customProperties: specProps["customProperties"]
27859
+ }, { stableKey: el.keyPath });
27860
+ }
27831
27861
  /** Map a wall's synthesized Opening children onto addDoor/addWindow. The
27832
27862
  * wall-relative offsets come from the void geometry's frame minus the host's:
27833
27863
  * exact because both carry the same outer host transform. */
@@ -27902,6 +27932,7 @@ function familiesToBim(root, options) {
27902
27932
  const idByKeyPath = /* @__PURE__ */ new Map();
27903
27933
  const walk = (el, storeyId, rotated) => {
27904
27934
  const rotatedHere = rotated || hasRotateOp(el);
27935
+ let proxied = false;
27905
27936
  let containerId = storeyId;
27906
27937
  const route = specRoute(el.type);
27907
27938
  if (el.type === "Storey") {
@@ -27936,9 +27967,19 @@ function familiesToBim(root, options) {
27936
27967
  if (!opened.ok) return opened;
27937
27968
  }
27938
27969
  } else if (el.type === "Opening") return (0, brepjs.err)(specError("FAMILIES_OPENING_OUTSIDE_WALL", `familiesToBim: opening '${el.keyPath}' is not hosted by a Wall — only wall openings are mapped`));
27939
- else if (el.type !== "Group" && el.geometry.kind !== "Empty") return (0, brepjs.err)(specError("FAMILIES_UNSUPPORTED_TYPE", `familiesToBim: no spec mapping for element type '${el.type}' at '${el.keyPath}'`));
27970
+ else if (el.type !== "Group" && el.geometry.kind !== "Empty") {
27971
+ if (options.proxyEvaluator === void 0) return (0, brepjs.err)(specError("FAMILIES_UNSUPPORTED_TYPE", `familiesToBim: no spec mapping for element type '${el.type}' at '${el.keyPath}' — add a spec route, or pass proxyEvaluator to export it as an IfcBuildingElementProxy`));
27972
+ const keyed = requireKeyed(el);
27973
+ if (!keyed.ok) return keyed;
27974
+ if (containerId === null) return (0, brepjs.err)(specError("FAMILIES_NO_STOREY", `familiesToBim: '${el.keyPath}' has no Storey ancestor — IFC elements need spatial containment`));
27975
+ const added = addProxyElement(model, el, options.proxyEvaluator);
27976
+ if (!added.ok) return added;
27977
+ model.placeIn(added.value, containerId);
27978
+ idByKeyPath.set(el.keyPath, added.value);
27979
+ proxied = true;
27980
+ }
27940
27981
  for (const child of el.children) {
27941
- if (el.type === "Wall" && child.type === "Opening") continue;
27982
+ if ((el.type === "Wall" || proxied) && child.type === "Opening") continue;
27942
27983
  const r = walk(child, containerId, rotatedHere);
27943
27984
  if (!r.ok) return r;
27944
27985
  }
@@ -1,4 +1,4 @@
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";
1
+ import { addHoles, applyMatrix, autoHeal, box, castShape, clone, convexHull, cut, err, extrude, fuse, getKernel, getSolids, isClosedWire, isOk, isPlanarWire, isSolid, isValid, isValidSolid, measureVolume, mesh, ok, outerWire, polygon, revolve, rotate, validSolid } from "brepjs";
2
2
  import * as WebIFC from "web-ifc";
3
3
  import { Handle, IfcAPI } from "web-ifc";
4
4
  //#region src/identity/ifcGuid.ts
@@ -512,7 +512,7 @@ var propertyKeyTypes = /* @__PURE__*/ new Set([
512
512
  function escapeRegex(str) {
513
513
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
514
514
  }
515
- function clone(inst, def, params) {
515
+ function clone$1(inst, def, params) {
516
516
  const cl = new inst._zod.constr(def ?? inst._zod.def);
517
517
  if (!def || params?.parent) cl._zod.parent = inst;
518
518
  return cl;
@@ -548,7 +548,7 @@ function pick(schema, mask) {
548
548
  const currDef = schema._zod.def;
549
549
  const checks = currDef.checks;
550
550
  if (checks && checks.length > 0) throw new Error(".pick() cannot be used on object schemas containing refinements");
551
- return clone(schema, mergeDefs(schema._zod.def, {
551
+ return clone$1(schema, mergeDefs(schema._zod.def, {
552
552
  get shape() {
553
553
  const newShape = {};
554
554
  for (const key in mask) {
@@ -566,7 +566,7 @@ function omit(schema, mask) {
566
566
  const currDef = schema._zod.def;
567
567
  const checks = currDef.checks;
568
568
  if (checks && checks.length > 0) throw new Error(".omit() cannot be used on object schemas containing refinements");
569
- return clone(schema, mergeDefs(schema._zod.def, {
569
+ return clone$1(schema, mergeDefs(schema._zod.def, {
570
570
  get shape() {
571
571
  const newShape = { ...schema._zod.def.shape };
572
572
  for (const key in mask) {
@@ -587,7 +587,7 @@ function extend(schema, shape) {
587
587
  const existingShape = schema._zod.def.shape;
588
588
  for (const key in shape) if (Object.getOwnPropertyDescriptor(existingShape, key) !== void 0) throw new Error("Cannot overwrite keys on object schemas containing refinements. Use `.safeExtend()` instead.");
589
589
  }
590
- return clone(schema, mergeDefs(schema._zod.def, { get shape() {
590
+ return clone$1(schema, mergeDefs(schema._zod.def, { get shape() {
591
591
  const _shape = {
592
592
  ...schema._zod.def.shape,
593
593
  ...shape
@@ -598,7 +598,7 @@ function extend(schema, shape) {
598
598
  }
599
599
  function safeExtend(schema, shape) {
600
600
  if (!isPlainObject(shape)) throw new Error("Invalid input to safeExtend: expected a plain object");
601
- return clone(schema, mergeDefs(schema._zod.def, { get shape() {
601
+ return clone$1(schema, mergeDefs(schema._zod.def, { get shape() {
602
602
  const _shape = {
603
603
  ...schema._zod.def.shape,
604
604
  ...shape
@@ -609,7 +609,7 @@ function safeExtend(schema, shape) {
609
609
  }
610
610
  function merge(a, b) {
611
611
  if (a._zod.def.checks?.length) throw new Error(".merge() cannot be used on object schemas containing refinements. Use .safeExtend() instead.");
612
- return clone(a, mergeDefs(a._zod.def, {
612
+ return clone$1(a, mergeDefs(a._zod.def, {
613
613
  get shape() {
614
614
  const _shape = {
615
615
  ...a._zod.def.shape,
@@ -627,7 +627,7 @@ function merge(a, b) {
627
627
  function partial(Class, schema, mask) {
628
628
  const checks = schema._zod.def.checks;
629
629
  if (checks && checks.length > 0) throw new Error(".partial() cannot be used on object schemas containing refinements");
630
- return clone(schema, mergeDefs(schema._zod.def, {
630
+ return clone$1(schema, mergeDefs(schema._zod.def, {
631
631
  get shape() {
632
632
  const oldShape = schema._zod.def.shape;
633
633
  const shape = { ...oldShape };
@@ -650,7 +650,7 @@ function partial(Class, schema, mask) {
650
650
  }));
651
651
  }
652
652
  function required(Class, schema, mask) {
653
- return clone(schema, mergeDefs(schema._zod.def, { get shape() {
653
+ return clone$1(schema, mergeDefs(schema._zod.def, { get shape() {
654
654
  const oldShape = schema._zod.def.shape;
655
655
  const shape = { ...oldShape };
656
656
  if (mask) for (const key in mask) {
@@ -4019,7 +4019,7 @@ var ZodType = /*@__PURE__*/ $constructor("ZodType", (inst, def) => {
4019
4019
  return this.check(...chks);
4020
4020
  },
4021
4021
  clone(def, params) {
4022
- return clone(this, def, params);
4022
+ return clone$1(this, def, params);
4023
4023
  },
4024
4024
  brand() {
4025
4025
  return this;
@@ -27805,6 +27805,36 @@ function addFill(model, fill, input, identity) {
27805
27805
  }
27806
27806
  return err(specError("FAMILIES_UNSUPPORTED_FILL", `familiesToBim: no fill mapping for element type '${fill.type}' at '${fill.keyPath}'`));
27807
27807
  }
27808
+ /** Materialize an unrouted element's IR and add it as a proxy. The body is
27809
+ * authoritative for a proxy (no parametric spec to diverge from), so baked
27810
+ * transforms — rotations included — are fine here. */
27811
+ function addProxyElement(model, el, evaluator) {
27812
+ const evaluated = evaluator.evaluate(el.geometry);
27813
+ if (!evaluated.ok) return err(specError("FAMILIES_PROXY_EVAL_FAILED", `familiesToBim: '${el.keyPath}' failed to materialize for the proxy route: ${evaluated.error.message}`, evaluated.error));
27814
+ let source = evaluated.value;
27815
+ if (!isSolid(source)) {
27816
+ const solids = getSolids(source);
27817
+ const only = solids.length === 1 ? solids[0] : void 0;
27818
+ if (only === void 0) return err(specError("FAMILIES_PROXY_NOT_SOLID", `familiesToBim: '${el.keyPath}' materialized to ${solids.length} solids — a proxy body must be exactly one`));
27819
+ source = only;
27820
+ }
27821
+ const copy = clone(source);
27822
+ if (!copy.ok) return err(specError("FAMILIES_PROXY_EVAL_FAILED", `familiesToBim: '${el.keyPath}' could not copy the materialized body`, copy.error));
27823
+ const valid = validSolid(copy.value);
27824
+ if (!valid.ok) {
27825
+ copy.value[Symbol.dispose]();
27826
+ return err(specError("FAMILIES_PROXY_INVALID", `familiesToBim: '${el.keyPath}' materialized to an invalid solid: ${valid.error}`));
27827
+ }
27828
+ const nameAttr = el.attributes["name"];
27829
+ const materialProp = el.props["materialName"];
27830
+ const specProps = collectSpecProps(el);
27831
+ return model.addProxy({
27832
+ name: typeof nameAttr === "string" ? nameAttr : el.type,
27833
+ solid: valid.value,
27834
+ materialName: typeof materialProp === "string" ? materialProp : specProps["materialName"],
27835
+ customProperties: specProps["customProperties"]
27836
+ }, { stableKey: el.keyPath });
27837
+ }
27808
27838
  /** Map a wall's synthesized Opening children onto addDoor/addWindow. The
27809
27839
  * wall-relative offsets come from the void geometry's frame minus the host's:
27810
27840
  * exact because both carry the same outer host transform. */
@@ -27879,6 +27909,7 @@ function familiesToBim(root, options) {
27879
27909
  const idByKeyPath = /* @__PURE__ */ new Map();
27880
27910
  const walk = (el, storeyId, rotated) => {
27881
27911
  const rotatedHere = rotated || hasRotateOp(el);
27912
+ let proxied = false;
27882
27913
  let containerId = storeyId;
27883
27914
  const route = specRoute(el.type);
27884
27915
  if (el.type === "Storey") {
@@ -27913,9 +27944,19 @@ function familiesToBim(root, options) {
27913
27944
  if (!opened.ok) return opened;
27914
27945
  }
27915
27946
  } 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`));
27916
- 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}'`));
27947
+ else if (el.type !== "Group" && el.geometry.kind !== "Empty") {
27948
+ if (options.proxyEvaluator === void 0) return err(specError("FAMILIES_UNSUPPORTED_TYPE", `familiesToBim: no spec mapping for element type '${el.type}' at '${el.keyPath}' — add a spec route, or pass proxyEvaluator to export it as an IfcBuildingElementProxy`));
27949
+ const keyed = requireKeyed(el);
27950
+ if (!keyed.ok) return keyed;
27951
+ if (containerId === null) return err(specError("FAMILIES_NO_STOREY", `familiesToBim: '${el.keyPath}' has no Storey ancestor — IFC elements need spatial containment`));
27952
+ const added = addProxyElement(model, el, options.proxyEvaluator);
27953
+ if (!added.ok) return added;
27954
+ model.placeIn(added.value, containerId);
27955
+ idByKeyPath.set(el.keyPath, added.value);
27956
+ proxied = true;
27957
+ }
27917
27958
  for (const child of el.children) {
27918
- if (el.type === "Wall" && child.type === "Opening") continue;
27959
+ if ((el.type === "Wall" || proxied) && child.type === "Opening") continue;
27919
27960
  const r = walk(child, containerId, rotatedHere);
27920
27961
  if (!r.ok) return r;
27921
27962
  }
@@ -1,4 +1,4 @@
1
- import { Result } from 'brepjs';
1
+ import { Result, csg } from 'brepjs';
2
2
  import { ResolvedElement } from 'brepjs-families';
3
3
  import { BimModel } from './model/bimModel.js';
4
4
  import { LocalId } from './identity/localId.js';
@@ -8,6 +8,15 @@ export interface FamiliesToBimOptions {
8
8
  readonly project: ProjectSpec;
9
9
  readonly siteName?: string | undefined;
10
10
  readonly buildingName?: string | undefined;
11
+ /**
12
+ * Enables the proxy route: an unrouted geometry-bearing element is
13
+ * materialized through this evaluator and exported as an
14
+ * IfcBuildingElementProxy (tessellated, world-frame body). Without it,
15
+ * unrouted types stay a hard FAMILIES_UNSUPPORTED_TYPE error. The
16
+ * evaluator's handles stay borrowed; the adapter clones what it hands the
17
+ * model.
18
+ */
19
+ readonly proxyEvaluator?: csg.Evaluator | undefined;
11
20
  }
12
21
  export interface FamiliesBimResult {
13
22
  readonly model: BimModel;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brepjs-bim",
3
- "version": "0.18.0",
3
+ "version": "0.19.0",
4
4
  "description": "BIM layer for brepjs — IFC4-aligned parametric building elements",
5
5
  "keywords": [
6
6
  "bim",