okgeometry-api 1.1.3 → 1.1.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "okgeometry-api",
3
- "version": "1.1.3",
3
+ "version": "1.1.5",
4
4
  "description": "Geometry engine API for AEC applications — NURBS, meshes, booleans, intersections. Powered by Rust/WASM.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/src/Mesh.ts CHANGED
@@ -104,10 +104,13 @@ export interface MeshBooleanDebugProbe {
104
104
  resultHits: MeshDebugRayHit[];
105
105
  }
106
106
 
107
+ export type MeshBooleanDebugProbeId =
108
+ "posX" | "negX" | "posY" | "negY" | "posZ" | "negZ";
109
+
107
110
  export interface MeshBooleanDebugOptions {
108
111
  rayPaddingScale?: number;
109
112
  maxRayHits?: number;
110
- probes?: Array<"posX" | "negX" | "posY" | "negY" | "posZ" | "negZ">;
113
+ probes?: MeshBooleanDebugProbeId[];
111
114
  }
112
115
 
113
116
  export interface MeshBooleanDebugReport {
@@ -129,9 +132,14 @@ export class Mesh {
129
132
  private _buffer: Float64Array;
130
133
  private _vertexCount: number;
131
134
  private _trustedBooleanInput: boolean;
132
- private static readonly DEFAULT_BOOLEAN_DEBUG_PROBES: Array<
133
- "posX" | "negX" | "posY" | "negY" | "posZ" | "negZ"
134
- > = ["posX", "negX", "posY", "negY", "posZ", "negZ"];
135
+ private static readonly DEFAULT_BOOLEAN_DEBUG_PROBES: MeshBooleanDebugProbeId[] = [
136
+ "posX",
137
+ "negX",
138
+ "posY",
139
+ "negY",
140
+ "posZ",
141
+ "negZ",
142
+ ];
135
143
 
136
144
  private static readonly DEFAULT_BOOLEAN_LIMITS: MeshBooleanLimits = {
137
145
  maxInputFacesPerMesh: 120_000,
@@ -278,87 +286,159 @@ export class Mesh {
278
286
  }));
279
287
  }
280
288
 
281
- private static buildBooleanDebugProbes(
289
+ private static uniqueDebugProbeIds(
290
+ probeIds: MeshBooleanDebugProbeId[],
291
+ ): MeshBooleanDebugProbeId[] {
292
+ return [...new Set(probeIds)];
293
+ }
294
+
295
+ private static buildInputBProbeOriginOffsets(
296
+ jitter: number,
297
+ ): Record<MeshBooleanDebugProbeId, Vec3> {
298
+ const major = jitter;
299
+ const minor = jitter * 0.61803398875;
300
+ const micro = jitter * 0.38196601125;
301
+ return {
302
+ posX: new Vec3(0, minor, -major),
303
+ negX: new Vec3(0, -minor, major),
304
+ posY: new Vec3(major, 0, micro),
305
+ negY: new Vec3(-major, 0, -micro),
306
+ posZ: new Vec3(major, -minor, 0),
307
+ negZ: new Vec3(-major, minor, 0),
308
+ };
309
+ }
310
+
311
+ private static buildDirectionalDebugProbes(
282
312
  inputA: Mesh,
283
313
  inputB: Mesh,
284
314
  result: Mesh,
285
- options?: MeshBooleanDebugOptions,
315
+ center: Point,
316
+ pad: number,
317
+ maxDistance: number,
318
+ maxHits: number,
319
+ probeIds: MeshBooleanDebugProbeId[],
320
+ labelPrefix = "",
321
+ originOffsets?: Partial<Record<MeshBooleanDebugProbeId, Vec3>>,
286
322
  ): MeshBooleanDebugProbe[] {
287
- const boundsA = Mesh.toDebugBounds(inputA.getBounds());
288
- const boundsB = Mesh.toDebugBounds(inputB.getBounds());
289
- const resultBounds = Mesh.toDebugBounds(result.getBounds());
290
- const min = new Point(
291
- Math.min(boundsA.min.x, boundsB.min.x, resultBounds.min.x),
292
- Math.min(boundsA.min.y, boundsB.min.y, resultBounds.min.y),
293
- Math.min(boundsA.min.z, boundsB.min.z, resultBounds.min.z),
294
- );
295
- const max = new Point(
296
- Math.max(boundsA.max.x, boundsB.max.x, resultBounds.max.x),
297
- Math.max(boundsA.max.y, boundsB.max.y, resultBounds.max.y),
298
- Math.max(boundsA.max.z, boundsB.max.z, resultBounds.max.z),
299
- );
300
- const center = new Point(
301
- (min.x + max.x) * 0.5,
302
- (min.y + max.y) * 0.5,
303
- (min.z + max.z) * 0.5,
304
- );
305
- const diag = Math.max(
306
- 1e-6,
307
- Math.hypot(max.x - min.x, max.y - min.y, max.z - min.z),
308
- );
309
- const padScale = Number.isFinite(options?.rayPaddingScale)
310
- ? Math.max(0.01, options?.rayPaddingScale ?? 0.25)
311
- : 0.25;
312
- const pad = Math.max(1e-3, diag * padScale);
313
- const maxHits = Mesh.clampDebugHitCount(options?.maxRayHits);
314
- const probeIds = options?.probes?.length
315
- ? options.probes
316
- : Mesh.DEFAULT_BOOLEAN_DEBUG_PROBES;
323
+ const prefixed = (id: string) => (labelPrefix ? `${labelPrefix}:${id}` : id);
317
324
  const probeSpecs: Record<
318
- "posX" | "negX" | "posY" | "negY" | "posZ" | "negZ",
325
+ MeshBooleanDebugProbeId,
319
326
  { origin: Point; direction: Vec3 }
320
327
  > = {
321
328
  posX: {
322
- origin: new Point(max.x + pad, center.y, center.z),
329
+ origin: new Point(center.x + pad, center.y, center.z),
323
330
  direction: new Vec3(-1, 0, 0),
324
331
  },
325
332
  negX: {
326
- origin: new Point(min.x - pad, center.y, center.z),
333
+ origin: new Point(center.x - pad, center.y, center.z),
327
334
  direction: new Vec3(1, 0, 0),
328
335
  },
329
336
  posY: {
330
- origin: new Point(center.x, max.y + pad, center.z),
337
+ origin: new Point(center.x, center.y + pad, center.z),
331
338
  direction: new Vec3(0, -1, 0),
332
339
  },
333
340
  negY: {
334
- origin: new Point(center.x, min.y - pad, center.z),
341
+ origin: new Point(center.x, center.y - pad, center.z),
335
342
  direction: new Vec3(0, 1, 0),
336
343
  },
337
344
  posZ: {
338
- origin: new Point(center.x, center.y, max.z + pad),
345
+ origin: new Point(center.x, center.y, center.z + pad),
339
346
  direction: new Vec3(0, 0, -1),
340
347
  },
341
348
  negZ: {
342
- origin: new Point(center.x, center.y, min.z - pad),
349
+ origin: new Point(center.x, center.y, center.z - pad),
343
350
  direction: new Vec3(0, 0, 1),
344
351
  },
345
352
  };
346
- const maxDistance = diag + pad * 2;
347
353
 
348
354
  return probeIds.map((id) => {
349
355
  const spec = probeSpecs[id];
356
+ const originOffset = originOffsets?.[id];
357
+ const origin = originOffset
358
+ ? new Point(
359
+ spec.origin.x + originOffset.x,
360
+ spec.origin.y + originOffset.y,
361
+ spec.origin.z + originOffset.z,
362
+ )
363
+ : spec.origin;
350
364
  return {
351
- label: id,
352
- origin: spec.origin,
365
+ label: prefixed(id),
366
+ origin,
353
367
  direction: spec.direction,
354
368
  maxDistance,
355
- inputAHits: Mesh.collectDebugHits(inputA, spec.origin, spec.direction, maxDistance, maxHits),
356
- inputBHits: Mesh.collectDebugHits(inputB, spec.origin, spec.direction, maxDistance, maxHits),
357
- resultHits: Mesh.collectDebugHits(result, spec.origin, spec.direction, maxDistance, maxHits),
369
+ inputAHits: Mesh.collectDebugHits(inputA, origin, spec.direction, maxDistance, maxHits),
370
+ inputBHits: Mesh.collectDebugHits(inputB, origin, spec.direction, maxDistance, maxHits),
371
+ resultHits: Mesh.collectDebugHits(result, origin, spec.direction, maxDistance, maxHits),
358
372
  };
359
373
  });
360
374
  }
361
375
 
376
+ private static buildBooleanDebugProbes(
377
+ inputA: Mesh,
378
+ inputB: Mesh,
379
+ result: Mesh,
380
+ options?: MeshBooleanDebugOptions,
381
+ ): MeshBooleanDebugProbe[] {
382
+ const boundsA = Mesh.toDebugBounds(inputA.getBounds());
383
+ const boundsB = Mesh.toDebugBounds(inputB.getBounds());
384
+ const resultBounds = Mesh.toDebugBounds(result.getBounds());
385
+ const min = new Point(
386
+ Math.min(boundsA.min.x, boundsB.min.x, resultBounds.min.x),
387
+ Math.min(boundsA.min.y, boundsB.min.y, resultBounds.min.y),
388
+ Math.min(boundsA.min.z, boundsB.min.z, resultBounds.min.z),
389
+ );
390
+ const max = new Point(
391
+ Math.max(boundsA.max.x, boundsB.max.x, resultBounds.max.x),
392
+ Math.max(boundsA.max.y, boundsB.max.y, resultBounds.max.y),
393
+ Math.max(boundsA.max.z, boundsB.max.z, resultBounds.max.z),
394
+ );
395
+ const center = new Point(
396
+ (min.x + max.x) * 0.5,
397
+ (min.y + max.y) * 0.5,
398
+ (min.z + max.z) * 0.5,
399
+ );
400
+ const diag = Math.max(
401
+ 1e-6,
402
+ Math.hypot(max.x - min.x, max.y - min.y, max.z - min.z),
403
+ );
404
+ const padScale = Number.isFinite(options?.rayPaddingScale)
405
+ ? Math.max(0.01, options?.rayPaddingScale ?? 0.25)
406
+ : 0.25;
407
+ const pad = Math.max(1e-3, diag * padScale);
408
+ const maxHits = Mesh.clampDebugHitCount(options?.maxRayHits);
409
+ const sceneProbeIds = Mesh.uniqueDebugProbeIds(
410
+ options?.probes?.length
411
+ ? options.probes
412
+ : Mesh.DEFAULT_BOOLEAN_DEBUG_PROBES,
413
+ );
414
+ const inputBProbeIds = Mesh.uniqueDebugProbeIds(sceneProbeIds.concat(["posZ", "negZ"]));
415
+ const inputBJitter = Math.max(1e-6, Math.min(pad * 0.1, boundsB.diagonal * 0.01));
416
+ const maxDistance = diag + pad * 2;
417
+ const sceneCenterProbes = Mesh.buildDirectionalDebugProbes(
418
+ inputA,
419
+ inputB,
420
+ result,
421
+ center,
422
+ pad,
423
+ maxDistance,
424
+ maxHits,
425
+ sceneProbeIds,
426
+ );
427
+ const inputBCenterProbes = Mesh.buildDirectionalDebugProbes(
428
+ inputA,
429
+ inputB,
430
+ result,
431
+ boundsB.center,
432
+ pad,
433
+ maxDistance,
434
+ maxHits,
435
+ inputBProbeIds,
436
+ "inputB",
437
+ Mesh.buildInputBProbeOriginOffsets(inputBJitter),
438
+ );
439
+ return sceneCenterProbes.concat(inputBCenterProbes);
440
+ }
441
+
362
442
  private static createBooleanDebugReport(
363
443
  inputA: Mesh,
364
444
  inputB: Mesh,
package/src/index.ts CHANGED
@@ -36,6 +36,7 @@ export { NurbsSurface } from "./NurbsSurface.js";
36
36
  export { Mesh, MeshBooleanExecutionError } from "./Mesh.js";
37
37
  export type {
38
38
  MeshBooleanOperation,
39
+ MeshBooleanDebugProbeId,
39
40
  MeshBooleanDebugOptions,
40
41
  MeshBooleanDebugProbe,
41
42
  MeshBooleanDebugReport,