opengeometry 2.0.7 → 2.0.9

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/index.js CHANGED
@@ -77,20 +77,69 @@ function handleError(f, args) {
77
77
  wasm.__wbindgen_exn_store(idx);
78
78
  }
79
79
  }
80
- const cachedTextDecoder = typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', {
81
- ignoreBOM: true,
82
- fatal: true
83
- }) : {
84
- decode: () => {
85
- throw Error('TextDecoder not available');
80
+ function debugString(val) {
81
+ // primitive types
82
+ const type = typeof val;
83
+ if (type == 'number' || type == 'boolean' || val == null) {
84
+ return `${val}`;
85
+ }
86
+ if (type == 'string') {
87
+ return `"${val}"`;
88
+ }
89
+ if (type == 'symbol') {
90
+ const description = val.description;
91
+ if (description == null) {
92
+ return 'Symbol';
93
+ } else {
94
+ return `Symbol(${description})`;
95
+ }
86
96
  }
87
- };
88
- if (typeof TextDecoder !== 'undefined') {
89
- cachedTextDecoder.decode();
90
- }
91
- function getStringFromWasm0(ptr, len) {
92
- ptr = ptr >>> 0;
93
- return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
97
+ if (type == 'function') {
98
+ const name = val.name;
99
+ if (typeof name == 'string' && name.length > 0) {
100
+ return `Function(${name})`;
101
+ } else {
102
+ return 'Function';
103
+ }
104
+ }
105
+ // objects
106
+ if (Array.isArray(val)) {
107
+ const length = val.length;
108
+ let debug = '[';
109
+ if (length > 0) {
110
+ debug += debugString(val[0]);
111
+ }
112
+ for (let i = 1; i < length; i++) {
113
+ debug += ', ' + debugString(val[i]);
114
+ }
115
+ debug += ']';
116
+ return debug;
117
+ }
118
+ // Test for built-in
119
+ const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val));
120
+ let className;
121
+ if (builtInMatches && builtInMatches.length > 1) {
122
+ className = builtInMatches[1];
123
+ } else {
124
+ // Failed to match the standard '[object ClassName]'
125
+ return toString.call(val);
126
+ }
127
+ if (className == 'Object') {
128
+ // we're a user defined class or Object
129
+ // JSON.stringify avoids problems with cycles, and is generally much
130
+ // easier than looping through ownProperties of `val`.
131
+ try {
132
+ return 'Object(' + JSON.stringify(val) + ')';
133
+ } catch (_) {
134
+ return 'Object';
135
+ }
136
+ }
137
+ // errors
138
+ if (val instanceof Error) {
139
+ return `${val.name}: ${val.message}\n${val.stack}`;
140
+ }
141
+ // TODO we could test for more things here, like `Set`s and `Map`s.
142
+ return className;
94
143
  }
95
144
  let WASM_VECTOR_LEN = 0;
96
145
  const cachedTextEncoder = typeof TextEncoder !== 'undefined' ? new TextEncoder('utf-8') : {
@@ -138,6 +187,28 @@ function passStringToWasm0(arg, malloc, realloc) {
138
187
  WASM_VECTOR_LEN = offset;
139
188
  return ptr;
140
189
  }
190
+ let cachedDataViewMemory0 = null;
191
+ function getDataViewMemory0() {
192
+ if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer) {
193
+ cachedDataViewMemory0 = new DataView(wasm.memory.buffer);
194
+ }
195
+ return cachedDataViewMemory0;
196
+ }
197
+ const cachedTextDecoder = typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', {
198
+ ignoreBOM: true,
199
+ fatal: true
200
+ }) : {
201
+ decode: () => {
202
+ throw Error('TextDecoder not available');
203
+ }
204
+ };
205
+ if (typeof TextDecoder !== 'undefined') {
206
+ cachedTextDecoder.decode();
207
+ }
208
+ function getStringFromWasm0(ptr, len) {
209
+ ptr = ptr >>> 0;
210
+ return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
211
+ }
141
212
  function _assertClass(instance, klass) {
142
213
  if (!(instance instanceof klass)) {
143
214
  throw new Error(`expected instance of ${klass.name}`);
@@ -159,39 +230,6 @@ function getArrayF64FromWasm0(ptr, len) {
159
230
  ptr = ptr >>> 0;
160
231
  return getFloat64ArrayMemory0().subarray(ptr / 8, ptr / 8 + len);
161
232
  }
162
- /**
163
- * @param {string} local_brep_serialized
164
- * @param {number} height
165
- * @returns {string}
166
- */
167
- function extrudeBrepFace$1(local_brep_serialized, height) {
168
- let deferred3_0;
169
- let deferred3_1;
170
- try {
171
- const ptr0 = passStringToWasm0(local_brep_serialized, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
172
- const len0 = WASM_VECTOR_LEN;
173
- const ret = wasm.extrudeBrepFace(ptr0, len0, height);
174
- var ptr2 = ret[0];
175
- var len2 = ret[1];
176
- if (ret[3]) {
177
- ptr2 = 0;
178
- len2 = 0;
179
- throw takeFromExternrefTable0(ret[2]);
180
- }
181
- deferred3_0 = ptr2;
182
- deferred3_1 = len2;
183
- return getStringFromWasm0(ptr2, len2);
184
- } finally {
185
- wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
186
- }
187
- }
188
- let cachedDataViewMemory0 = null;
189
- function getDataViewMemory0() {
190
- if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer) {
191
- cachedDataViewMemory0 = new DataView(wasm.memory.buffer);
192
- }
193
- return cachedDataViewMemory0;
194
- }
195
233
  function passArrayJsValueToWasm0(array, malloc) {
196
234
  const ptr = malloc(array.length * 4, 4) >>> 0;
197
235
  for (let i = 0; i < array.length; i++) {
@@ -287,6 +325,33 @@ function booleanSubtractionMany(lhs_brep_serialized, cutters_brep_serialized, op
287
325
  }
288
326
  return OGBooleanResult.__wrap(ret[0]);
289
327
  }
328
+
329
+ /**
330
+ * @param {string} local_brep_serialized
331
+ * @param {number} height
332
+ * @returns {string}
333
+ */
334
+ function extrudeBrepFace$1(local_brep_serialized, height) {
335
+ let deferred3_0;
336
+ let deferred3_1;
337
+ try {
338
+ const ptr0 = passStringToWasm0(local_brep_serialized, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
339
+ const len0 = WASM_VECTOR_LEN;
340
+ const ret = wasm.extrudeBrepFace(ptr0, len0, height);
341
+ var ptr2 = ret[0];
342
+ var len2 = ret[1];
343
+ if (ret[3]) {
344
+ ptr2 = 0;
345
+ len2 = 0;
346
+ throw takeFromExternrefTable0(ret[2]);
347
+ }
348
+ deferred3_0 = ptr2;
349
+ deferred3_1 = len2;
350
+ return getStringFromWasm0(ptr2, len2);
351
+ } finally {
352
+ wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
353
+ }
354
+ }
290
355
  function getArrayJsValueFromWasm0(ptr, len) {
291
356
  ptr = ptr >>> 0;
292
357
  const mem = getDataViewMemory0();
@@ -1666,6 +1731,99 @@ class OGCylinder {
1666
1731
  return Vector3.__wrap(ret);
1667
1732
  }
1668
1733
  }
1734
+ const OGEntityRegistryFinalization = typeof FinalizationRegistry === 'undefined' ? {
1735
+ register: () => {},
1736
+ unregister: () => {}
1737
+ } : new FinalizationRegistry(ptr => wasm.__wbg_ogentityregistry_free(ptr >>> 0, 1));
1738
+ /**
1739
+ * Flat entity registry with a batched multi-view projection API.
1740
+ * Replaces the scene-centric OGSceneManager pattern for export use cases.
1741
+ */
1742
+ class OGEntityRegistry {
1743
+ __destroy_into_raw() {
1744
+ const ptr = this.__wbg_ptr;
1745
+ this.__wbg_ptr = 0;
1746
+ OGEntityRegistryFinalization.unregister(this);
1747
+ return ptr;
1748
+ }
1749
+ free() {
1750
+ const ptr = this.__destroy_into_raw();
1751
+ wasm.__wbg_ogentityregistry_free(ptr, 0);
1752
+ }
1753
+ constructor() {
1754
+ const ret = wasm.ogentityregistry_new();
1755
+ this.__wbg_ptr = ret >>> 0;
1756
+ OGEntityRegistryFinalization.register(this, this.__wbg_ptr, this);
1757
+ return this;
1758
+ }
1759
+ /**
1760
+ * Register (or replace) an entity. `kind` must be an OGEntityKind string.
1761
+ * @param {string} id
1762
+ * @param {string} kind
1763
+ * @param {string} brep_json
1764
+ */
1765
+ registerEntity(id, kind, brep_json) {
1766
+ const ptr0 = passStringToWasm0(id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
1767
+ const len0 = WASM_VECTOR_LEN;
1768
+ const ptr1 = passStringToWasm0(kind, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
1769
+ const len1 = WASM_VECTOR_LEN;
1770
+ const ptr2 = passStringToWasm0(brep_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
1771
+ const len2 = WASM_VECTOR_LEN;
1772
+ const ret = wasm.ogentityregistry_registerEntity(this.__wbg_ptr, ptr0, len0, ptr1, len1, ptr2, len2);
1773
+ if (ret[1]) {
1774
+ throw takeFromExternrefTable0(ret[0]);
1775
+ }
1776
+ }
1777
+ /**
1778
+ * Remove a previously registered entity.
1779
+ * @param {string} id
1780
+ * @returns {boolean}
1781
+ */
1782
+ unregisterEntity(id) {
1783
+ const ptr0 = passStringToWasm0(id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
1784
+ const len0 = WASM_VECTOR_LEN;
1785
+ const ret = wasm.ogentityregistry_unregisterEntity(this.__wbg_ptr, ptr0, len0);
1786
+ return ret !== 0;
1787
+ }
1788
+ /**
1789
+ * Remove all registered entities.
1790
+ */
1791
+ clearEntities() {
1792
+ wasm.ogentityregistry_clearEntities(this.__wbg_ptr);
1793
+ }
1794
+ /**
1795
+ * Batched multi-view projection.
1796
+ *
1797
+ * Input: JSON array of `{ id, camera, hlr?, section_plane? }` (ViewRequest).
1798
+ * Output: JSON map of `{ viewportId: Scene2D }`.
1799
+ *
1800
+ * All viewports are projected in a single WASM call, amortising
1801
+ * serialisation overhead and keeping BRep data in WASM memory.
1802
+ * @param {string} views_json
1803
+ * @returns {string}
1804
+ */
1805
+ projectCurrentToViews(views_json) {
1806
+ let deferred3_0;
1807
+ let deferred3_1;
1808
+ try {
1809
+ const ptr0 = passStringToWasm0(views_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
1810
+ const len0 = WASM_VECTOR_LEN;
1811
+ const ret = wasm.ogentityregistry_projectCurrentToViews(this.__wbg_ptr, ptr0, len0);
1812
+ var ptr2 = ret[0];
1813
+ var len2 = ret[1];
1814
+ if (ret[3]) {
1815
+ ptr2 = 0;
1816
+ len2 = 0;
1817
+ throw takeFromExternrefTable0(ret[2]);
1818
+ }
1819
+ deferred3_0 = ptr2;
1820
+ deferred3_1 = len2;
1821
+ return getStringFromWasm0(ptr2, len2);
1822
+ } finally {
1823
+ wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
1824
+ }
1825
+ }
1826
+ }
1669
1827
  const OGFreeformEditorFinalization = typeof FinalizationRegistry === 'undefined' ? {
1670
1828
  register: () => {},
1671
1829
  unregister: () => {}
@@ -4996,7 +5154,7 @@ class OGSweep {
4996
5154
  * @returns {Vector3}
4997
5155
  */
4998
5156
  get_anchor() {
4999
- const ret = wasm.ogarc_get_anchor(this.__wbg_ptr);
5157
+ const ret = wasm.ogsweep_get_anchor(this.__wbg_ptr);
5000
5158
  return Vector3.__wrap(ret);
5001
5159
  }
5002
5160
  /**
@@ -5613,6 +5771,13 @@ function __wbg_get_imports() {
5613
5771
  const ret = Vector3.__unwrap(arg0);
5614
5772
  return ret;
5615
5773
  };
5774
+ imports.wbg.__wbindgen_debug_string = function (arg0, arg1) {
5775
+ const ret = debugString(arg1);
5776
+ const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
5777
+ const len1 = WASM_VECTOR_LEN;
5778
+ getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
5779
+ getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
5780
+ };
5616
5781
  imports.wbg.__wbindgen_init_externref_table = function () {
5617
5782
  const table = wasm.__wbindgen_export_2;
5618
5783
  const offset = table.grow(4);
@@ -5692,6 +5857,7 @@ var OGKernel = /*#__PURE__*/Object.freeze({
5692
5857
  OGCuboid: OGCuboid,
5693
5858
  OGCurve: OGCurve,
5694
5859
  OGCylinder: OGCylinder,
5860
+ OGEntityRegistry: OGEntityRegistry,
5695
5861
  OGFreeformEditor: OGFreeformEditor,
5696
5862
  OGFreeformGeometry: OGFreeformGeometry,
5697
5863
  OGIfcExportResult: OGIfcExportResult,
@@ -6250,7 +6416,6 @@ function createFreeformGeometry(source, options) {
6250
6416
  return geometry;
6251
6417
  }
6252
6418
 
6253
- /* eslint-enable no-unused-vars */
6254
6419
  /**
6255
6420
  * Simple Line defined by Two Points
6256
6421
  */
@@ -6473,7 +6638,6 @@ class Line extends THREE.Line {
6473
6638
  }
6474
6639
  }
6475
6640
 
6476
- /* eslint-enable no-unused-vars */
6477
6641
  /**
6478
6642
  * Polyline wrapper backed by the kernel OGPolyline primitive.
6479
6643
  */
@@ -6913,7 +7077,6 @@ class Arc extends THREE.Line {
6913
7077
  }
6914
7078
  }
6915
7079
 
6916
- /* eslint-enable no-unused-vars */
6917
7080
  /**
6918
7081
  * Rectangle wrapper backed by the kernel OGRectangle primitive.
6919
7082
  */
@@ -7151,7 +7314,6 @@ class Rectangle extends THREE.Line {
7151
7314
  }
7152
7315
  }
7153
7316
 
7154
- /* eslint-enable no-unused-vars */
7155
7317
  /**
7156
7318
  * Curve wrapper backed by the kernel OGCurve primitive.
7157
7319
  */
@@ -7426,16 +7588,169 @@ function serializeBrepLike$1(value) {
7426
7588
  return typeof value === "string" ? value : JSON.stringify(value);
7427
7589
  }
7428
7590
 
7591
+ /**
7592
+ * Typed error hierarchy for boolean operations.
7593
+ *
7594
+ * The Rust kernel emits a structured `BooleanError` as JSON across the WASM
7595
+ * boundary (see `main/opengeometry/src/booleans/error.rs`). This module parses
7596
+ * that JSON and re-throws it as a typed `BooleanError` (or a more specific
7597
+ * subclass) so app code can `instanceof`-discriminate without parsing message
7598
+ * strings.
7599
+ *
7600
+ * Cutter indices are **0-based** to match `Vec` / array indexing on both
7601
+ * sides of the boundary.
7602
+ */
7603
+ /**
7604
+ * Base class for every typed boolean error. Carries the structured payload the
7605
+ * kernel emitted across WASM. Specialized subclasses (`OverlappingCuttersError`,
7606
+ * `CutterExceedsHostError`, `NonManifoldOutputError`, `CoincidentFacesError`)
7607
+ * narrow the payload for the variants that carry extra fields.
7608
+ */
7609
+ class BooleanError extends Error {
7610
+ constructor(payload) {
7611
+ super(payload.message);
7612
+ this.name = "BooleanError";
7613
+ this.reason = payload.kind.code;
7614
+ this.phase = payload.phase;
7615
+ this.cutterIndex = payload.cutter_index;
7616
+ this.details = payload.details;
7617
+ this.edgeSamples = payload.edge_samples
7618
+ ? payload.edge_samples.map(([a, b]) => [
7619
+ new THREE.Vector3(a.x, a.y, a.z),
7620
+ new THREE.Vector3(b.x, b.y, b.z),
7621
+ ])
7622
+ : null;
7623
+ }
7624
+ }
7625
+ /**
7626
+ * Two cutters overlap each other. `otherIndex` is the 0-based index of the
7627
+ * cutter that conflicts with the one at `cutterIndex`.
7628
+ */
7629
+ class OverlappingCuttersError extends BooleanError {
7630
+ constructor(payload) {
7631
+ var _a;
7632
+ super(payload);
7633
+ this.name = "OverlappingCuttersError";
7634
+ this.otherIndex = (_a = payload.kind.other_index) !== null && _a !== void 0 ? _a : -1;
7635
+ }
7636
+ }
7637
+ /**
7638
+ * A cutter extends past the host's hull along the named axis.
7639
+ */
7640
+ class CutterExceedsHostError extends BooleanError {
7641
+ constructor(payload) {
7642
+ var _a, _b;
7643
+ super(payload);
7644
+ this.name = "CutterExceedsHostError";
7645
+ const axis = ((_a = payload.kind.axis) !== null && _a !== void 0 ? _a : "x").toLowerCase();
7646
+ this.axis = (axis === "y" || axis === "z" ? axis : "x");
7647
+ this.overshoot = (_b = payload.kind.overshoot) !== null && _b !== void 0 ? _b : 0;
7648
+ }
7649
+ }
7650
+ /**
7651
+ * Output mesh has open or over-shared edges after a boolean. `edgeSamples`
7652
+ * carries up to ~8 sample non-manifold edges to aid app-side debugging.
7653
+ */
7654
+ class NonManifoldOutputError extends BooleanError {
7655
+ constructor(payload) {
7656
+ super(payload);
7657
+ this.name = "NonManifoldOutputError";
7658
+ }
7659
+ }
7660
+ /**
7661
+ * Cutter and host share a coplanar face. Distinct from
7662
+ * `DegenerateTriangleError` (zero-area input) — see `06a-snap-tolerance.md`.
7663
+ */
7664
+ class CoincidentFacesError extends BooleanError {
7665
+ constructor(payload) {
7666
+ super(payload);
7667
+ this.name = "CoincidentFacesError";
7668
+ }
7669
+ }
7670
+ /**
7671
+ * Parses a WASM-thrown error and returns the appropriate typed `BooleanError`
7672
+ * subclass. If the error's message is not valid `BooleanError` JSON, returns a
7673
+ * fallback `BooleanError` with `reason: "kernel_failure"` carrying the original
7674
+ * string, so callers always get a typed error and never have to `try/catch`
7675
+ * twice.
7676
+ */
7677
+ function parseBooleanError(raw) {
7678
+ var _a, _b, _c, _d, _e;
7679
+ const message = extractMessage(raw);
7680
+ let payload = null;
7681
+ try {
7682
+ const parsed = JSON.parse(message);
7683
+ if (parsed && typeof parsed === "object" && parsed.kind && typeof parsed.kind === "object") {
7684
+ payload = {
7685
+ kind: parsed.kind,
7686
+ phase: ((_a = parsed.phase) !== null && _a !== void 0 ? _a : "input_validation"),
7687
+ cutter_index: (_b = parsed.cutter_index) !== null && _b !== void 0 ? _b : null,
7688
+ message: (_c = parsed.message) !== null && _c !== void 0 ? _c : message,
7689
+ details: (_d = parsed.details) !== null && _d !== void 0 ? _d : null,
7690
+ edge_samples: (_e = parsed.edge_samples) !== null && _e !== void 0 ? _e : null,
7691
+ };
7692
+ }
7693
+ }
7694
+ catch (_f) {
7695
+ payload = null;
7696
+ }
7697
+ if (!payload) {
7698
+ payload = {
7699
+ kind: { code: "kernel_failure" },
7700
+ phase: "input_validation",
7701
+ cutter_index: null,
7702
+ message,
7703
+ details: null,
7704
+ edge_samples: null,
7705
+ };
7706
+ }
7707
+ switch (payload.kind.code) {
7708
+ case "overlapping_cutters":
7709
+ return new OverlappingCuttersError(payload);
7710
+ case "cutter_exceeds_host":
7711
+ return new CutterExceedsHostError(payload);
7712
+ case "non_manifold_edges":
7713
+ case "open_shell":
7714
+ return new NonManifoldOutputError(payload);
7715
+ case "coincident_faces":
7716
+ return new CoincidentFacesError(payload);
7717
+ default:
7718
+ return new BooleanError(payload);
7719
+ }
7720
+ }
7721
+ function extractMessage(raw) {
7722
+ if (raw === null || raw === undefined) {
7723
+ return "";
7724
+ }
7725
+ if (typeof raw === "string") {
7726
+ return raw;
7727
+ }
7728
+ if (raw instanceof Error) {
7729
+ return raw.message;
7730
+ }
7731
+ if (typeof raw === "object" && "message" in raw) {
7732
+ const message = raw.message;
7733
+ if (typeof message === "string") {
7734
+ return message;
7735
+ }
7736
+ }
7737
+ try {
7738
+ return String(raw);
7739
+ }
7740
+ catch (_a) {
7741
+ return "Unknown boolean error";
7742
+ }
7743
+ }
7744
+
7429
7745
  var _BooleanResult_outlineMesh;
7430
- /* eslint-enable no-unused-vars */
7431
7746
  const BOOLEAN_CREASE_ANGLE = Math.PI / 5;
7432
7747
  const BOOLEAN_OUTLINE_THRESHOLD_DEGREES = 26;
7433
7748
  /**
7434
7749
  * Renderable result mesh returned by the kernel boolean helpers.
7435
7750
  */
7436
7751
  class BooleanResult extends THREE.Mesh {
7437
- constructor(kernelResult, options) {
7438
- var _a, _b, _c;
7752
+ constructor(kernelResult, options, host = null) {
7753
+ var _a, _b;
7439
7754
  super();
7440
7755
  this.report = null;
7441
7756
  this.brepSerialized = "";
@@ -7448,15 +7763,47 @@ class BooleanResult extends THREE.Mesh {
7448
7763
  this.ogid = getUUID();
7449
7764
  this._fatOutlines = (_a = options === null || options === void 0 ? void 0 : options.fatOutlines) !== null && _a !== void 0 ? _a : false;
7450
7765
  this._outlineWidth = sanitizeOutlineWidth(options === null || options === void 0 ? void 0 : options.outlineWidth);
7451
- this.applyKernelResult(kernelResult, (_b = options === null || options === void 0 ? void 0 : options.color) !== null && _b !== void 0 ? _b : 0x2563eb);
7452
- if ((_c = options === null || options === void 0 ? void 0 : options.outline) !== null && _c !== void 0 ? _c : false) {
7766
+ const metadata = resolveOperandMetadata(host);
7767
+ if (metadata.name) {
7768
+ this.name = metadata.name;
7769
+ }
7770
+ if (metadata.userData) {
7771
+ this.userData = Object.assign({}, metadata.userData);
7772
+ }
7773
+ this.applyKernelResult(kernelResult, options, metadata.material);
7774
+ if ((_b = options === null || options === void 0 ? void 0 : options.outline) !== null && _b !== void 0 ? _b : false) {
7453
7775
  this.outline = true;
7454
7776
  }
7455
7777
  }
7778
+ /**
7779
+ * Updates the result mesh's color in place without re-running the boolean
7780
+ * pipeline. Accepts any value Three.js accepts (`number`, hex string, css
7781
+ * color name, or `THREE.Color`). No-op for materials without a `color` field.
7782
+ */
7783
+ set color(value) {
7784
+ const apply = (material) => {
7785
+ if ("color" in material) {
7786
+ material.color.set(value);
7787
+ }
7788
+ material.needsUpdate = true;
7789
+ };
7790
+ if (Array.isArray(this.material)) {
7791
+ this.material.forEach(apply);
7792
+ return;
7793
+ }
7794
+ apply(this.material);
7795
+ }
7796
+ get color() {
7797
+ const material = Array.isArray(this.material) ? this.material[0] : this.material;
7798
+ if (!material || !("color" in material)) {
7799
+ return null;
7800
+ }
7801
+ return material.color;
7802
+ }
7456
7803
  /**
7457
7804
  * Rebuilds the mesh, cached BRep payload, and outline geometry from the kernel output.
7458
7805
  */
7459
- applyKernelResult(kernelResult, color = 0x2563eb) {
7806
+ applyKernelResult(kernelResult, options, sourceMaterial = null) {
7460
7807
  this.disposeGeometry();
7461
7808
  this.brepSerialized = kernelResult.brepSerialized;
7462
7809
  this.brepData = JSON.parse(kernelResult.brepSerialized);
@@ -7467,15 +7814,7 @@ class BooleanResult extends THREE.Mesh {
7467
7814
  const geometry = toCreasedNormals(baseGeometry, BOOLEAN_CREASE_ANGLE);
7468
7815
  geometry.computeBoundingBox();
7469
7816
  this.outlinePositions = getRenderableOutlinePositions(geometry, kernelResult.outlineGeometrySerialized);
7470
- const material = new THREE.MeshStandardMaterial({
7471
- color,
7472
- transparent: true,
7473
- opacity: 0.82,
7474
- side: THREE.FrontSide,
7475
- polygonOffset: true,
7476
- polygonOffsetFactor: 1,
7477
- polygonOffsetUnits: 1,
7478
- });
7817
+ const material = buildResultMaterial(sourceMaterial, options);
7479
7818
  this.geometry = geometry;
7480
7819
  this.material = material;
7481
7820
  if (this._outlineEnabled) {
@@ -7602,12 +7941,21 @@ function executeBooleanSubtractionMany(lhs, cutters, options) {
7602
7941
  }
7603
7942
  const kernelFunction = booleanExport;
7604
7943
  const kernelOptions = serializeKernelOptions(options === null || options === void 0 ? void 0 : options.kernel);
7605
- const kernelResult = kernelFunction(resolveOperandSerialized(lhs), JSON.stringify(cutters.map(resolveOperandPayload)), kernelOptions);
7606
- return createBooleanResult(kernelResult, options);
7944
+ let kernelResult;
7945
+ try {
7946
+ kernelResult = kernelFunction(resolveOperandSerialized(lhs), JSON.stringify(cutters.map(resolveOperandPayload)), kernelOptions);
7947
+ }
7948
+ catch (error) {
7949
+ throw parseBooleanError(error);
7950
+ }
7951
+ return createBooleanResult(kernelResult, options, lhs);
7607
7952
  }
7608
7953
  /**
7609
7954
  * Normalizes mixed wrapper/raw operands, invokes the wasm boolean export, and
7610
7955
  * returns the renderable result wrapper.
7956
+ *
7957
+ * @throws {BooleanError} (or a subclass) when the kernel rejects the operation.
7958
+ * See `boolean-errors.ts` for the typed hierarchy.
7611
7959
  */
7612
7960
  function executeBoolean(exportName, lhs, rhs, options) {
7613
7961
  const booleanExport = OGKernel[exportName];
@@ -7616,8 +7964,14 @@ function executeBoolean(exportName, lhs, rhs, options) {
7616
7964
  }
7617
7965
  const kernelFunction = booleanExport;
7618
7966
  const kernelOptions = serializeKernelOptions(options === null || options === void 0 ? void 0 : options.kernel);
7619
- const kernelResult = kernelFunction(resolveOperandSerialized(lhs), resolveOperandSerialized(rhs), kernelOptions);
7620
- return createBooleanResult(kernelResult, options);
7967
+ let kernelResult;
7968
+ try {
7969
+ kernelResult = kernelFunction(resolveOperandSerialized(lhs), resolveOperandSerialized(rhs), kernelOptions);
7970
+ }
7971
+ catch (error) {
7972
+ throw parseBooleanError(error);
7973
+ }
7974
+ return createBooleanResult(kernelResult, options, lhs);
7621
7975
  }
7622
7976
  /**
7623
7977
  * Accepts wrapper objects, raw BRep data, or serialized JSON and normalizes
@@ -7673,14 +8027,76 @@ function serializeKernelOptions(options) {
7673
8027
  merge_coplanar_faces: options.mergeCoplanarFaces,
7674
8028
  });
7675
8029
  }
7676
- function createBooleanResult(kernelResult, options) {
8030
+ function createBooleanResult(kernelResult, options, host = null) {
7677
8031
  var _a;
7678
- const result = new BooleanResult(kernelResult, options);
8032
+ const result = new BooleanResult(kernelResult, options, host);
7679
8033
  if ((_a = options === null || options === void 0 ? void 0 : options.outline) !== null && _a !== void 0 ? _a : true) {
7680
8034
  result.outline = true;
7681
8035
  }
7682
8036
  return result;
7683
8037
  }
8038
+ /**
8039
+ * Returns the material, name, and userData carried on the operand when it is
8040
+ * a Three mesh subclass (Solid, Polygon, Cuboid, Opening, BooleanResult,
8041
+ * etc.) so the result mesh can drop into a scene graph as-is. Falls back to
8042
+ * empty metadata for serialized JSON, parsed BRep objects, or anything else
8043
+ * without a reachable host mesh.
8044
+ */
8045
+ function resolveOperandMetadata(operand) {
8046
+ var _a, _b, _c;
8047
+ const empty = { material: null, name: "", userData: null };
8048
+ if (operand === null || typeof operand !== "object" || !(operand instanceof THREE.Mesh)) {
8049
+ return empty;
8050
+ }
8051
+ const rawMaterial = operand.material;
8052
+ let material = null;
8053
+ if (Array.isArray(rawMaterial)) {
8054
+ material = (_a = rawMaterial[0]) !== null && _a !== void 0 ? _a : null;
8055
+ }
8056
+ else if (rawMaterial instanceof THREE.Material) {
8057
+ material = rawMaterial;
8058
+ }
8059
+ return {
8060
+ material,
8061
+ name: (_b = operand.name) !== null && _b !== void 0 ? _b : "",
8062
+ userData: (_c = operand.userData) !== null && _c !== void 0 ? _c : null,
8063
+ };
8064
+ }
8065
+ /**
8066
+ * Builds the boolean result mesh's material by cloning the host (LHS) material
8067
+ * when available so the result inherits the host's color, transparency, and
8068
+ * side mode. Caller-provided overrides on `BooleanRenderOptions` win, and the
8069
+ * polygon-offset settings that prevent z-fighting with the result outline are
8070
+ * always reapplied after cloning.
8071
+ */
8072
+ function buildResultMaterial(sourceMaterial, options) {
8073
+ const material = sourceMaterial
8074
+ ? sourceMaterial.clone()
8075
+ : new THREE.MeshStandardMaterial({
8076
+ color: 0x2563eb,
8077
+ transparent: false,
8078
+ opacity: 1,
8079
+ side: THREE.FrontSide,
8080
+ });
8081
+ if ((options === null || options === void 0 ? void 0 : options.color) !== undefined && "color" in material) {
8082
+ material.color.set(options.color);
8083
+ }
8084
+ if ((options === null || options === void 0 ? void 0 : options.opacity) !== undefined) {
8085
+ material.opacity = options.opacity;
8086
+ material.transparent = options.opacity < 1 || material.transparent;
8087
+ }
8088
+ if ((options === null || options === void 0 ? void 0 : options.transparent) !== undefined) {
8089
+ material.transparent = options.transparent;
8090
+ }
8091
+ if ((options === null || options === void 0 ? void 0 : options.side) !== undefined) {
8092
+ material.side = options.side;
8093
+ }
8094
+ material.polygonOffset = true;
8095
+ material.polygonOffsetFactor = 1;
8096
+ material.polygonOffsetUnits = 1;
8097
+ material.needsUpdate = true;
8098
+ return material;
8099
+ }
7684
8100
  /**
7685
8101
  * Builds a display-quality outline buffer from the rendered mesh and falls back
7686
8102
  * to the kernel-provided edges when the client-side extraction is empty.
@@ -9159,7 +9575,6 @@ class Opening extends THREE.Mesh {
9159
9575
  _Opening_outlineMesh = new WeakMap();
9160
9576
 
9161
9577
  var _Sphere_outlineMesh;
9162
- /* eslint-enable no-unused-vars */
9163
9578
  /**
9164
9579
  * Sphere wrapper backed by the kernel sphere primitive.
9165
9580
  */
@@ -9704,7 +10119,6 @@ class Wedge extends THREE.Mesh {
9704
10119
  _Wedge_outlineMesh = new WeakMap();
9705
10120
 
9706
10121
  var _Sweep_outlineMesh;
9707
- /* eslint-enable no-unused-vars */
9708
10122
  /**
9709
10123
  * Sweep wrapper backed by the kernel sweep primitive.
9710
10124
  */
@@ -10302,7 +10716,61 @@ function buildWallOutline(left, right) {
10302
10716
  }
10303
10717
  return outline;
10304
10718
  }
10305
- function createWallFromOffsetsExample(scene) {
10719
+ function midpoint(a, b) {
10720
+ return new Vector3((a.x + b.x) * 0.5, (a.y + b.y) * 0.5, (a.z + b.z) * 0.5);
10721
+ }
10722
+ function buildDoorCutters(centerlinePoints, count, overshootMeters, wallThickness, doorHeight) {
10723
+ const cutters = [];
10724
+ const wallDepth = wallThickness + 2 * overshootMeters;
10725
+ const doorWidth = 0.6;
10726
+ const segments = centerlinePoints.length - 1;
10727
+ const stride = Math.max(1, Math.floor(segments / count));
10728
+ for (let doorIdx = 0; doorIdx < count; doorIdx++) {
10729
+ const segmentIdx = Math.min(segments - 1, doorIdx * stride);
10730
+ const a = centerlinePoints[segmentIdx];
10731
+ const b = centerlinePoints[segmentIdx + 1];
10732
+ const mid = midpoint(a, b);
10733
+ cutters.push(new Cuboid({
10734
+ width: doorWidth,
10735
+ height: doorHeight,
10736
+ depth: wallDepth,
10737
+ center: new Vector3(0, 0, 0),
10738
+ translation: new Vector3(mid.x, doorHeight * 0.5 + 0.001, mid.z),
10739
+ color: 0xef4444,
10740
+ }));
10741
+ }
10742
+ return cutters;
10743
+ }
10744
+ /**
10745
+ * Renders a human-readable description for a typed `BooleanError` (or any
10746
+ * other thrown value). Used by the example HTML to surface kernel failures
10747
+ * in a status panel without losing the structured payload.
10748
+ */
10749
+ function describeWallSubtractError(error) {
10750
+ var _a, _b, _c, _d, _e;
10751
+ if (error instanceof OverlappingCuttersError) {
10752
+ return `OverlappingCuttersError @ cutter #${error.cutterIndex} (other #${error.otherIndex}): ${(_a = error.details) !== null && _a !== void 0 ? _a : error.message}`;
10753
+ }
10754
+ if (error instanceof CutterExceedsHostError) {
10755
+ return `CutterExceedsHostError: cutter overshoots ${error.axis} by ${error.overshoot.toExponential(3)}`;
10756
+ }
10757
+ if (error instanceof CoincidentFacesError) {
10758
+ return `CoincidentFacesError: ${(_b = error.details) !== null && _b !== void 0 ? _b : error.message}`;
10759
+ }
10760
+ if (error instanceof NonManifoldOutputError) {
10761
+ const samples = (_d = (_c = error.edgeSamples) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0;
10762
+ return `NonManifoldOutputError: ${samples} non-manifold edges sampled — ${(_e = error.details) !== null && _e !== void 0 ? _e : error.message}`;
10763
+ }
10764
+ if (error instanceof BooleanError) {
10765
+ return `BooleanError (${error.reason}, phase ${error.phase}): ${error.message}`;
10766
+ }
10767
+ if (error instanceof Error) {
10768
+ return `Untyped error: ${error.message}`;
10769
+ }
10770
+ return `Untyped error: ${String(error)}`;
10771
+ }
10772
+ function createWallFromOffsetsExample(scene, options = {}) {
10773
+ var _a, _b, _c, _d;
10306
10774
  const centerline = new Polyline({
10307
10775
  points: [
10308
10776
  new Vector3(-2.6, 0.0, -1.9),
@@ -10318,6 +10786,7 @@ function createWallFromOffsetsExample(scene) {
10318
10786
  const half = wallThickness * 0.5;
10319
10787
  const acuteThreshold = 35.0;
10320
10788
  const bevel = true;
10789
+ const wallHeight = (_a = options.height) !== null && _a !== void 0 ? _a : 2.6;
10321
10790
  const leftOffset = centerline.getOffset(half, acuteThreshold, bevel);
10322
10791
  const rightOffset = centerline.getOffset(-0.225, acuteThreshold, bevel);
10323
10792
  const leftOffsetPolyline = new Polyline({
@@ -10341,6 +10810,34 @@ function createWallFromOffsetsExample(scene) {
10341
10810
  scene.add(leftOffsetPolyline);
10342
10811
  scene.add(rightOffsetPolyline);
10343
10812
  scene.add(wallPolygon);
10813
+ let cutResult = null;
10814
+ let cutError = null;
10815
+ if (((_b = options.doorCount) !== null && _b !== void 0 ? _b : 0) > 0) {
10816
+ const overshootMeters = ((_c = options.overshootMm) !== null && _c !== void 0 ? _c : 1) * 0.001;
10817
+ const wallSolid = wallPolygon.extrude(wallHeight);
10818
+ const cutters = buildDoorCutters([
10819
+ new Vector3(-2.6, 0.0, -1.9),
10820
+ new Vector3(-1.2, 0.0, -1),
10821
+ new Vector3(-0.2, 0.0, 0.1),
10822
+ new Vector3(0.6, 0.0, 0.2),
10823
+ new Vector3(0.1, 0.0, 1.0),
10824
+ new Vector3(2.6, 0.0, 2.0),
10825
+ ], (_d = options.doorCount) !== null && _d !== void 0 ? _d : 0, overshootMeters, wallThickness, 2.1);
10826
+ try {
10827
+ const cutWall = wallSolid.subtract(cutters, {
10828
+ color: 0x3b82f6,
10829
+ transparent: false,
10830
+ opacity: 1,
10831
+ });
10832
+ scene.add(cutWall);
10833
+ cutResult = { wall: cutWall, cutters };
10834
+ }
10835
+ catch (error) {
10836
+ cutError = error;
10837
+ scene.add(wallSolid);
10838
+ cutResult = { wall: wallSolid, cutters };
10839
+ }
10840
+ }
10344
10841
  return {
10345
10842
  centerline,
10346
10843
  leftOffsetPolyline,
@@ -10348,6 +10845,8 @@ function createWallFromOffsetsExample(scene) {
10348
10845
  wallPolygon,
10349
10846
  leftOffset,
10350
10847
  rightOffset,
10848
+ cutResult,
10849
+ cutError,
10351
10850
  };
10352
10851
  }
10353
10852
 
@@ -10710,5 +11209,5 @@ class OpenGeometry {
10710
11209
  OpenGeometry.version = OPEN_GEOMETRY_THREE_VERSION;
10711
11210
  OpenGeometry.instance = null;
10712
11211
 
10713
- export { Arc, BooleanResult, Cuboid, Curve, Cylinder, FreeformEditor, FreeformGeometry, Line, OGSceneManager, OpenGeometry, Opening, Polygon, Polyline, Rectangle, Solid, Sphere, SpotLabel, Sweep, Vector3, Wedge, booleanIntersection, booleanSubtraction, booleanUnion, clonePlacement, createBooleanExample, createEditorModesExample, createFreeformEditor, createFreeformGeometry, createOffsetExample, createParametricEditCapabilities, createPrimitivesExample, createShapesExample, createSweepExample, createWallFromOffsetsExample, executeBooleanSubtractionMany, extrudeBrepFace, toObjectTransformation$1 as toObjectTransformation };
11212
+ export { Arc, BooleanError, BooleanResult, CoincidentFacesError, Cuboid, Curve, CutterExceedsHostError, Cylinder, FreeformEditor, FreeformGeometry, Line, NonManifoldOutputError, OGSceneManager, OpenGeometry, Opening, OverlappingCuttersError, Polygon, Polyline, Rectangle, Solid, Sphere, SpotLabel, Sweep, Vector3, Wedge, booleanIntersection, booleanSubtraction, booleanUnion, clonePlacement, createBooleanExample, createEditorModesExample, createFreeformEditor, createFreeformGeometry, createOffsetExample, createParametricEditCapabilities, createPrimitivesExample, createShapesExample, createSweepExample, createWallFromOffsetsExample, describeWallSubtractError, executeBooleanSubtractionMany, extrudeBrepFace, parseBooleanError, toObjectTransformation$1 as toObjectTransformation };
10714
11213
  //# sourceMappingURL=index.js.map