opengeometry 2.0.8 → 2.0.10

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,13 +230,6 @@ function getArrayF64FromWasm0(ptr, len) {
159
230
  ptr = ptr >>> 0;
160
231
  return getFloat64ArrayMemory0().subarray(ptr / 8, ptr / 8 + len);
161
232
  }
162
- let cachedDataViewMemory0 = null;
163
- function getDataViewMemory0() {
164
- if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer) {
165
- cachedDataViewMemory0 = new DataView(wasm.memory.buffer);
166
- }
167
- return cachedDataViewMemory0;
168
- }
169
233
  function passArrayJsValueToWasm0(array, malloc) {
170
234
  const ptr = malloc(array.length * 4, 4) >>> 0;
171
235
  for (let i = 0; i < array.length; i++) {
@@ -175,42 +239,6 @@ function passArrayJsValueToWasm0(array, malloc) {
175
239
  WASM_VECTOR_LEN = array.length;
176
240
  return ptr;
177
241
  }
178
- /**
179
- * @param {string} local_brep_serialized
180
- * @param {number} height
181
- * @returns {string}
182
- */
183
- function extrudeBrepFace$1(local_brep_serialized, height) {
184
- let deferred3_0;
185
- let deferred3_1;
186
- try {
187
- const ptr0 = passStringToWasm0(local_brep_serialized, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
188
- const len0 = WASM_VECTOR_LEN;
189
- const ret = wasm.extrudeBrepFace(ptr0, len0, height);
190
- var ptr2 = ret[0];
191
- var len2 = ret[1];
192
- if (ret[3]) {
193
- ptr2 = 0;
194
- len2 = 0;
195
- throw takeFromExternrefTable0(ret[2]);
196
- }
197
- deferred3_0 = ptr2;
198
- deferred3_1 = len2;
199
- return getStringFromWasm0(ptr2, len2);
200
- } finally {
201
- wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
202
- }
203
- }
204
- function getArrayJsValueFromWasm0(ptr, len) {
205
- ptr = ptr >>> 0;
206
- const mem = getDataViewMemory0();
207
- const result = [];
208
- for (let i = ptr; i < ptr + 4 * len; i += 4) {
209
- result.push(wasm.__wbindgen_export_2.get(mem.getUint32(i, true)));
210
- }
211
- wasm.__externref_drop_slice(ptr, len);
212
- return result;
213
- }
214
242
  function isLikeNone(x) {
215
243
  return x === undefined || x === null;
216
244
  }
@@ -297,6 +325,43 @@ function booleanSubtractionMany(lhs_brep_serialized, cutters_brep_serialized, op
297
325
  }
298
326
  return OGBooleanResult.__wrap(ret[0]);
299
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
+ }
355
+ function getArrayJsValueFromWasm0(ptr, len) {
356
+ ptr = ptr >>> 0;
357
+ const mem = getDataViewMemory0();
358
+ const result = [];
359
+ for (let i = ptr; i < ptr + 4 * len; i += 4) {
360
+ result.push(wasm.__wbindgen_export_2.get(mem.getUint32(i, true)));
361
+ }
362
+ wasm.__externref_drop_slice(ptr, len);
363
+ return result;
364
+ }
300
365
  const Matrix3Finalization = typeof FinalizationRegistry === 'undefined' ? {
301
366
  register: () => {},
302
367
  unregister: () => {}
@@ -5370,7 +5435,7 @@ class OGWedge {
5370
5435
  * @returns {Vector3}
5371
5436
  */
5372
5437
  get_anchor() {
5373
- const ret = wasm.ogsweep_get_anchor(this.__wbg_ptr);
5438
+ const ret = wasm.ogwedge_get_anchor(this.__wbg_ptr);
5374
5439
  return Vector3.__wrap(ret);
5375
5440
  }
5376
5441
  }
@@ -5706,6 +5771,13 @@ function __wbg_get_imports() {
5706
5771
  const ret = Vector3.__unwrap(arg0);
5707
5772
  return ret;
5708
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
+ };
5709
5781
  imports.wbg.__wbindgen_init_externref_table = function () {
5710
5782
  const table = wasm.__wbindgen_export_2;
5711
5783
  const offset = table.grow(4);
@@ -7516,6 +7588,160 @@ function serializeBrepLike$1(value) {
7516
7588
  return typeof value === "string" ? value : JSON.stringify(value);
7517
7589
  }
7518
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
+
7519
7745
  var _BooleanResult_outlineMesh;
7520
7746
  const BOOLEAN_CREASE_ANGLE = Math.PI / 5;
7521
7747
  const BOOLEAN_OUTLINE_THRESHOLD_DEGREES = 26;
@@ -7523,7 +7749,7 @@ const BOOLEAN_OUTLINE_THRESHOLD_DEGREES = 26;
7523
7749
  * Renderable result mesh returned by the kernel boolean helpers.
7524
7750
  */
7525
7751
  class BooleanResult extends THREE.Mesh {
7526
- constructor(kernelResult, options, sourceMaterial = null) {
7752
+ constructor(kernelResult, options, host = null) {
7527
7753
  var _a, _b;
7528
7754
  super();
7529
7755
  this.report = null;
@@ -7537,11 +7763,43 @@ class BooleanResult extends THREE.Mesh {
7537
7763
  this.ogid = getUUID();
7538
7764
  this._fatOutlines = (_a = options === null || options === void 0 ? void 0 : options.fatOutlines) !== null && _a !== void 0 ? _a : false;
7539
7765
  this._outlineWidth = sanitizeOutlineWidth(options === null || options === void 0 ? void 0 : options.outlineWidth);
7540
- this.applyKernelResult(kernelResult, options, sourceMaterial);
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);
7541
7774
  if ((_b = options === null || options === void 0 ? void 0 : options.outline) !== null && _b !== void 0 ? _b : false) {
7542
7775
  this.outline = true;
7543
7776
  }
7544
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
+ }
7545
7803
  /**
7546
7804
  * Rebuilds the mesh, cached BRep payload, and outline geometry from the kernel output.
7547
7805
  */
@@ -7683,12 +7941,21 @@ function executeBooleanSubtractionMany(lhs, cutters, options) {
7683
7941
  }
7684
7942
  const kernelFunction = booleanExport;
7685
7943
  const kernelOptions = serializeKernelOptions(options === null || options === void 0 ? void 0 : options.kernel);
7686
- const kernelResult = kernelFunction(resolveOperandSerialized(lhs), JSON.stringify(cutters.map(resolveOperandPayload)), kernelOptions);
7687
- return createBooleanResult(kernelResult, options, resolveOperandMaterial(lhs));
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);
7688
7952
  }
7689
7953
  /**
7690
7954
  * Normalizes mixed wrapper/raw operands, invokes the wasm boolean export, and
7691
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.
7692
7959
  */
7693
7960
  function executeBoolean(exportName, lhs, rhs, options) {
7694
7961
  const booleanExport = OGKernel[exportName];
@@ -7697,8 +7964,14 @@ function executeBoolean(exportName, lhs, rhs, options) {
7697
7964
  }
7698
7965
  const kernelFunction = booleanExport;
7699
7966
  const kernelOptions = serializeKernelOptions(options === null || options === void 0 ? void 0 : options.kernel);
7700
- const kernelResult = kernelFunction(resolveOperandSerialized(lhs), resolveOperandSerialized(rhs), kernelOptions);
7701
- return createBooleanResult(kernelResult, options, resolveOperandMaterial(lhs));
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);
7702
7975
  }
7703
7976
  /**
7704
7977
  * Accepts wrapper objects, raw BRep data, or serialized JSON and normalizes
@@ -7754,33 +8027,40 @@ function serializeKernelOptions(options) {
7754
8027
  merge_coplanar_faces: options.mergeCoplanarFaces,
7755
8028
  });
7756
8029
  }
7757
- function createBooleanResult(kernelResult, options, sourceMaterial = null) {
8030
+ function createBooleanResult(kernelResult, options, host = null) {
7758
8031
  var _a;
7759
- const result = new BooleanResult(kernelResult, options, sourceMaterial);
8032
+ const result = new BooleanResult(kernelResult, options, host);
7760
8033
  if ((_a = options === null || options === void 0 ? void 0 : options.outline) !== null && _a !== void 0 ? _a : true) {
7761
8034
  result.outline = true;
7762
8035
  }
7763
8036
  return result;
7764
8037
  }
7765
8038
  /**
7766
- * Returns a clonable `THREE.Material` from the operand when it is a Three mesh
7767
- * subclass (Solid, Polygon, Cuboid, Opening, BooleanResult, etc.). Returns
7768
- * `null` for serialized JSON, parsed BRep objects, or anything else without a
7769
- * reachable material so the boolean result can fall back to the legacy default.
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.
7770
8044
  */
7771
- function resolveOperandMaterial(operand) {
7772
- var _a;
7773
- if (typeof operand !== "object" || operand === null) {
7774
- return null;
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;
7775
8050
  }
7776
- if (!(operand instanceof THREE.Mesh)) {
7777
- return null;
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;
7778
8055
  }
7779
- const material = operand.material;
7780
- if (Array.isArray(material)) {
7781
- return (_a = material[0]) !== null && _a !== void 0 ? _a : null;
8056
+ else if (rawMaterial instanceof THREE.Material) {
8057
+ material = rawMaterial;
7782
8058
  }
7783
- return material instanceof THREE.Material ? material : null;
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
+ };
7784
8064
  }
7785
8065
  /**
7786
8066
  * Builds the boolean result mesh's material by cloning the host (LHS) material
@@ -7794,8 +8074,8 @@ function buildResultMaterial(sourceMaterial, options) {
7794
8074
  ? sourceMaterial.clone()
7795
8075
  : new THREE.MeshStandardMaterial({
7796
8076
  color: 0x2563eb,
7797
- transparent: true,
7798
- opacity: 0.82,
8077
+ transparent: false,
8078
+ opacity: 1,
7799
8079
  side: THREE.FrontSide,
7800
8080
  });
7801
8081
  if ((options === null || options === void 0 ? void 0 : options.color) !== undefined && "color" in material) {
@@ -10436,7 +10716,61 @@ function buildWallOutline(left, right) {
10436
10716
  }
10437
10717
  return outline;
10438
10718
  }
10439
- 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;
10440
10774
  const centerline = new Polyline({
10441
10775
  points: [
10442
10776
  new Vector3(-2.6, 0.0, -1.9),
@@ -10452,6 +10786,7 @@ function createWallFromOffsetsExample(scene) {
10452
10786
  const half = wallThickness * 0.5;
10453
10787
  const acuteThreshold = 35.0;
10454
10788
  const bevel = true;
10789
+ const wallHeight = (_a = options.height) !== null && _a !== void 0 ? _a : 2.6;
10455
10790
  const leftOffset = centerline.getOffset(half, acuteThreshold, bevel);
10456
10791
  const rightOffset = centerline.getOffset(-0.225, acuteThreshold, bevel);
10457
10792
  const leftOffsetPolyline = new Polyline({
@@ -10475,6 +10810,34 @@ function createWallFromOffsetsExample(scene) {
10475
10810
  scene.add(leftOffsetPolyline);
10476
10811
  scene.add(rightOffsetPolyline);
10477
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
+ }
10478
10841
  return {
10479
10842
  centerline,
10480
10843
  leftOffsetPolyline,
@@ -10482,6 +10845,8 @@ function createWallFromOffsetsExample(scene) {
10482
10845
  wallPolygon,
10483
10846
  leftOffset,
10484
10847
  rightOffset,
10848
+ cutResult,
10849
+ cutError,
10485
10850
  };
10486
10851
  }
10487
10852
 
@@ -10844,5 +11209,5 @@ class OpenGeometry {
10844
11209
  OpenGeometry.version = OPEN_GEOMETRY_THREE_VERSION;
10845
11210
  OpenGeometry.instance = null;
10846
11211
 
10847
- 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 };
10848
11213
  //# sourceMappingURL=index.js.map