occt-wasm 3.3.1 → 3.3.2

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/dist/index.js CHANGED
@@ -18,23 +18,31 @@ export { XCAFDocument } from "./xcaf-document.js";
18
18
  import { XCAFDocument as XCAFDocumentImpl } from "./xcaf-document.js";
19
19
  export { renderMultiviewSVG, renderShapeSVG, } from "./svg.js";
20
20
  import { renderMultiviewSVG as renderMultiviewSVGImpl, renderShapeSVG as renderShapeSVGImpl, } from "./svg.js";
21
- import { JoinType, OcctError, SweepMode, TransitionMode } from "./types.js";
21
+ import { JoinType, SweepMode, TransitionMode, wrap } from "./types.js";
22
+ import { SHAPE_TYPES, SHAPE_ORIENTATIONS, POINT_CLASSIFICATIONS } from "./types.js";
22
23
  // ---------------------------------------------------------------------------
23
24
  // Helpers
24
25
  // ---------------------------------------------------------------------------
25
26
  function handle(id) {
26
27
  return id;
27
28
  }
28
- function wrap(operation, fn) {
29
- try {
30
- return fn();
31
- }
32
- catch (e) {
33
- if (e instanceof Error) {
34
- throw new OcctError(operation, e.message);
35
- }
36
- throw new OcctError(operation, String(e));
29
+ // Allowed values for the closed string-union enums returned by the kernel,
30
+ // derived from the single source of truth in types.ts so they can't drift.
31
+ // (SurfaceKind/CurveKind are open unions — `string & {}` — so any string is
32
+ // valid by design and needs no check.)
33
+ const SHAPE_TYPE_VALUES = new Set(SHAPE_TYPES);
34
+ const SHAPE_ORIENTATION_VALUES = new Set(SHAPE_ORIENTATIONS);
35
+ const POINT_CLASSIFICATION_VALUES = new Set(POINT_CLASSIFICATIONS);
36
+ /**
37
+ * Coerce a raw kernel string into a closed union, throwing if the kernel ever
38
+ * returns an unexpected value instead of silently casting it into a lie. Called
39
+ * inside `wrap(...)`, so the throw surfaces as a classified `OcctError`.
40
+ */
41
+ function asEnum(value, allowed, label) {
42
+ if (!allowed.has(value)) {
43
+ throw new Error(`unexpected ${label} from kernel: "${value}"`);
37
44
  }
45
+ return value;
38
46
  }
39
47
  /**
40
48
  * Safety net: releases the raw Embind kernel if an OcctKernel instance is
@@ -203,26 +211,14 @@ export class OcctKernel {
203
211
  * @throws OcctError */
204
212
  fuseAll(shapes) {
205
213
  return wrap("fuseAll", () => {
206
- const vec = this.#makeVectorU32(shapes);
207
- try {
208
- return handle(this.#raw.fuseAll(vec));
209
- }
210
- finally {
211
- vec.delete();
212
- }
214
+ return this.#withU32(shapes, (vec) => handle(this.#raw.fuseAll(vec)));
213
215
  });
214
216
  }
215
217
  /** Subtract all tool shapes from the base shape.
216
218
  * @throws OcctError */
217
219
  cutAll(shape, tools) {
218
220
  return wrap("cutAll", () => {
219
- const vec = this.#makeVectorU32(tools);
220
- try {
221
- return handle(this.#raw.cutAll(shape, vec));
222
- }
223
- finally {
224
- vec.delete();
225
- }
221
+ return this.#withU32(tools, (vec) => handle(this.#raw.cutAll(shape, vec)));
226
222
  });
227
223
  }
228
224
  /** Split a shape using tool shapes as splitting surfaces (BOPAlgo_Splitter).
@@ -230,13 +226,7 @@ export class OcctKernel {
230
226
  * @throws OcctError */
231
227
  split(shape, tools) {
232
228
  return wrap("split", () => {
233
- const vec = this.#makeVectorU32(tools);
234
- try {
235
- return handle(this.#raw.split(shape, vec));
236
- }
237
- finally {
238
- vec.delete();
239
- }
229
+ return this.#withU32(tools, (vec) => handle(this.#raw.split(shape, vec)));
240
230
  });
241
231
  }
242
232
  /**
@@ -246,13 +236,7 @@ export class OcctKernel {
246
236
  */
247
237
  intersectionCells(shapes) {
248
238
  return wrap("intersectionCells", () => {
249
- const vec = this.#makeVectorU32(shapes);
250
- try {
251
- return handle(this.#raw.intersectionCells(vec));
252
- }
253
- finally {
254
- vec.delete();
255
- }
239
+ return this.#withU32(shapes, (vec) => handle(this.#raw.intersectionCells(vec)));
256
240
  });
257
241
  }
258
242
  // =======================================================================
@@ -277,35 +261,17 @@ export class OcctKernel {
277
261
  * @throws OcctError */
278
262
  fillet(solid, edges, radius) {
279
263
  return wrap("fillet", () => {
280
- const vec = this.#makeVectorU32(edges);
281
- try {
282
- return handle(this.#raw.fillet(solid, vec, radius));
283
- }
284
- finally {
285
- vec.delete();
286
- }
264
+ return this.#withU32(edges, (vec) => handle(this.#raw.fillet(solid, vec, radius)));
287
265
  });
288
266
  }
289
267
  chamfer(solid, edges, distance) {
290
268
  return wrap("chamfer", () => {
291
- const vec = this.#makeVectorU32(edges);
292
- try {
293
- return handle(this.#raw.chamfer(solid, vec, distance));
294
- }
295
- finally {
296
- vec.delete();
297
- }
269
+ return this.#withU32(edges, (vec) => handle(this.#raw.chamfer(solid, vec, distance)));
298
270
  });
299
271
  }
300
272
  chamferDistAngle(solid, edges, distance, angleDeg) {
301
273
  return wrap("chamferDistAngle", () => {
302
- const vec = this.#makeVectorU32(edges);
303
- try {
304
- return handle(this.#raw.chamferDistAngle(solid, vec, distance, angleDeg));
305
- }
306
- finally {
307
- vec.delete();
308
- }
274
+ return this.#withU32(edges, (vec) => handle(this.#raw.chamferDistAngle(solid, vec, distance, angleDeg)));
309
275
  });
310
276
  }
311
277
  /**
@@ -319,13 +285,7 @@ export class OcctKernel {
319
285
  */
320
286
  shell(solid, facesToRemove, thickness, tolerance) {
321
287
  return wrap("shell", () => {
322
- const vec = this.#makeVectorU32(facesToRemove);
323
- try {
324
- return handle(this.#raw.shell(solid, vec, thickness, tolerance));
325
- }
326
- finally {
327
- vec.delete();
328
- }
288
+ return this.#withU32(facesToRemove, (vec) => handle(this.#raw.shell(solid, vec, thickness, tolerance)));
329
289
  });
330
290
  }
331
291
  /**
@@ -359,24 +319,12 @@ export class OcctKernel {
359
319
  */
360
320
  loft(wires, isSolid, ruled) {
361
321
  return wrap("loft", () => {
362
- const vec = this.#makeVectorU32(wires);
363
- try {
364
- return handle(this.#raw.loft(vec, isSolid, ruled));
365
- }
366
- finally {
367
- vec.delete();
368
- }
322
+ return this.#withU32(wires, (vec) => handle(this.#raw.loft(vec, isSolid, ruled)));
369
323
  });
370
324
  }
371
325
  loftWithVertices(wires, isSolid, ruled, startVertex, endVertex) {
372
326
  return wrap("loftWithVertices", () => {
373
- const vec = this.#makeVectorU32(wires);
374
- try {
375
- return handle(this.#raw.loftWithVertices(vec, isSolid, ruled, startVertex, endVertex));
376
- }
377
- finally {
378
- vec.delete();
379
- }
327
+ return this.#withU32(wires, (vec) => handle(this.#raw.loftWithVertices(vec, isSolid, ruled, startVertex, endVertex)));
380
328
  });
381
329
  }
382
330
  sweep(wire, spine, transitionMode = TransitionMode.Transformed) {
@@ -443,13 +391,7 @@ export class OcctKernel {
443
391
  }
444
392
  makeWire(edges) {
445
393
  return wrap("makeWire", () => {
446
- const vec = this.#makeVectorU32(edges);
447
- try {
448
- return handle(this.#raw.makeWire(vec));
449
- }
450
- finally {
451
- vec.delete();
452
- }
394
+ return this.#withU32(edges, (vec) => handle(this.#raw.makeWire(vec)));
453
395
  });
454
396
  }
455
397
  makeFace(wire) {
@@ -460,24 +402,12 @@ export class OcctKernel {
460
402
  }
461
403
  addHolesInFace(face, holeWires) {
462
404
  return wrap("addHolesInFace", () => {
463
- const vec = this.#makeVectorU32(holeWires);
464
- try {
465
- return handle(this.#raw.addHolesInFace(face, vec));
466
- }
467
- finally {
468
- vec.delete();
469
- }
405
+ return this.#withU32(holeWires, (vec) => handle(this.#raw.addHolesInFace(face, vec)));
470
406
  });
471
407
  }
472
408
  removeHolesFromFace(face, holeIndices) {
473
409
  return wrap("removeHolesFromFace", () => {
474
- const vec = this.#makeVectorI32(holeIndices);
475
- try {
476
- return handle(this.#raw.removeHolesFromFace(face, vec));
477
- }
478
- finally {
479
- vec.delete();
480
- }
410
+ return this.#withI32(holeIndices, (vec) => handle(this.#raw.removeHolesFromFace(face, vec)));
481
411
  });
482
412
  }
483
413
  makeSolid(shell) {
@@ -485,46 +415,22 @@ export class OcctKernel {
485
415
  }
486
416
  sew(shapes, tolerance = 1e-6) {
487
417
  return wrap("sew", () => {
488
- const vec = this.#makeVectorU32(shapes);
489
- try {
490
- return handle(this.#raw.sew(vec, tolerance));
491
- }
492
- finally {
493
- vec.delete();
494
- }
418
+ return this.#withU32(shapes, (vec) => handle(this.#raw.sew(vec, tolerance)));
495
419
  });
496
420
  }
497
421
  sewAndSolidify(faces, tolerance = 1e-6) {
498
422
  return wrap("sewAndSolidify", () => {
499
- const vec = this.#makeVectorU32(faces);
500
- try {
501
- return handle(this.#raw.sewAndSolidify(vec, tolerance));
502
- }
503
- finally {
504
- vec.delete();
505
- }
423
+ return this.#withU32(faces, (vec) => handle(this.#raw.sewAndSolidify(vec, tolerance)));
506
424
  });
507
425
  }
508
426
  buildSolidFromFaces(faces, tolerance = 1e-6) {
509
427
  return wrap("buildSolidFromFaces", () => {
510
- const vec = this.#makeVectorU32(faces);
511
- try {
512
- return handle(this.#raw.buildSolidFromFaces(vec, tolerance));
513
- }
514
- finally {
515
- vec.delete();
516
- }
428
+ return this.#withU32(faces, (vec) => handle(this.#raw.buildSolidFromFaces(vec, tolerance)));
517
429
  });
518
430
  }
519
431
  makeCompound(shapes) {
520
432
  return wrap("makeCompound", () => {
521
- const vec = this.#makeVectorU32(shapes);
522
- try {
523
- return handle(this.#raw.makeCompound(vec));
524
- }
525
- finally {
526
- vec.delete();
527
- }
433
+ return this.#withU32(shapes, (vec) => handle(this.#raw.makeCompound(vec)));
528
434
  });
529
435
  }
530
436
  buildTriFace(a, b, c) {
@@ -584,25 +490,13 @@ export class OcctKernel {
584
490
  /** Apply a 3x4 row-major affine transformation matrix (12 doubles: [r00,r01,r02,tx, r10,r11,r12,ty, r20,r21,r22,tz]). */
585
491
  transform(shape, matrix) {
586
492
  return wrap("transform", () => {
587
- const vec = this.#makeVectorF64(matrix);
588
- try {
589
- return handle(this.#raw.transform(shape, vec));
590
- }
591
- finally {
592
- vec.delete();
593
- }
493
+ return this.#withF64(matrix, (vec) => handle(this.#raw.transform(shape, vec)));
594
494
  });
595
495
  }
596
496
  /** Apply a general (possibly non-affine) 3x4 row-major transformation matrix (12 doubles). */
597
497
  generalTransform(shape, matrix) {
598
498
  return wrap("generalTransform", () => {
599
- const vec = this.#makeVectorF64(matrix);
600
- try {
601
- return handle(this.#raw.generalTransform(shape, vec));
602
- }
603
- finally {
604
- vec.delete();
605
- }
499
+ return this.#withF64(matrix, (vec) => handle(this.#raw.generalTransform(shape, vec)));
606
500
  });
607
501
  }
608
502
  linearPattern(shape, direction, spacing, count) {
@@ -613,158 +507,64 @@ export class OcctKernel {
613
507
  }
614
508
  /** Compose two 3x4 row-major transformation matrices. Returns a 12-element array. */
615
509
  composeTransform(m1, m2) {
616
- return wrap("composeTransform", () => {
617
- const v1 = this.#makeVectorF64(m1);
618
- const v2 = this.#makeVectorF64(m2);
619
- try {
620
- const result = this.#raw.composeTransform(v1, v2);
621
- return this.#drainVector(result, Float64Array);
622
- }
623
- finally {
624
- v1.delete();
625
- v2.delete();
626
- }
627
- });
510
+ return wrap("composeTransform", () => this.#withF64(m1, (v1) => this.#withF64(m2, (v2) => this.#drainVector(this.#raw.composeTransform(v1, v2), Float64Array))));
628
511
  }
629
512
  // =======================================================================
630
513
  // Batch Operations
631
514
  // =======================================================================
632
515
  /** Translate multiple shapes by their respective offsets in a single WASM call. */
633
516
  translateBatch(shapes, offsets) {
634
- return wrap("translateBatch", () => {
635
- const ids = this.#makeVectorU32(shapes);
636
- const off = this.#makeVectorF64(offsets);
637
- try {
638
- const result = this.#raw.translateBatch(ids, off);
639
- return this.#vecToHandles(result);
640
- }
641
- finally {
642
- ids.delete();
643
- off.delete();
644
- }
645
- });
517
+ return wrap("translateBatch", () => this.#withU32(shapes, (ids) => this.#withF64(offsets, (off) => this.#vecToHandles(this.#raw.translateBatch(ids, off)))));
646
518
  }
647
519
  /** Chain boolean operations in a single WASM call. */
648
520
  booleanPipeline(base, opCodes, tools) {
649
- return wrap("booleanPipeline", () => {
650
- const ops = this.#makeVectorI32(opCodes);
651
- const ids = this.#makeVectorU32(tools);
652
- try {
653
- return handle(this.#raw.booleanPipeline(base, ops, ids));
654
- }
655
- finally {
656
- ops.delete();
657
- ids.delete();
658
- }
659
- });
521
+ return wrap("booleanPipeline", () => this.#withI32(opCodes, (ops) => this.#withU32(tools, (ids) => handle(this.#raw.booleanPipeline(base, ops, ids)))));
660
522
  }
661
523
  /** Query multiple shapes in a single WASM call: bbox, volume, area, center of mass, type, validity. */
662
524
  queryBatch(shapes) {
663
- return wrap("queryBatch", () => {
664
- const ids = this.#makeVectorU32(shapes);
665
- try {
666
- const raw = this.#raw.queryBatch(ids);
667
- const arr = this.#drainVector(raw, Float64Array);
668
- const STRIDE = 14;
669
- const SHAPE_TYPES = ["compound", "compsolid", "solid", "shell", "face", "wire", "edge", "vertex", "shape"];
670
- const results = [];
671
- for (let i = 0; i < shapes.length; i++) {
672
- const o = i * STRIDE;
673
- results.push({
674
- volume: arr[o],
675
- area: arr[o + 1],
676
- bbox: { xmin: arr[o + 2], ymin: arr[o + 3], zmin: arr[o + 4], xmax: arr[o + 5], ymax: arr[o + 6], zmax: arr[o + 7] },
677
- centerOfMass: { x: arr[o + 8], y: arr[o + 9], z: arr[o + 10] },
678
- shapeType: SHAPE_TYPES[arr[o + 11]] ?? "shape",
679
- isValid: arr[o + 12] === 1.0,
680
- });
681
- }
682
- return results;
683
- }
684
- finally {
685
- ids.delete();
686
- }
687
- });
525
+ return wrap("queryBatch", () => this.#withU32(shapes, (ids) => {
526
+ const arr = this.#drainVector(this.#raw.queryBatch(ids), Float64Array);
527
+ const STRIDE = 14;
528
+ const results = [];
529
+ for (let i = 0; i < shapes.length; i++) {
530
+ const o = i * STRIDE;
531
+ results.push({
532
+ volume: arr[o],
533
+ area: arr[o + 1],
534
+ bbox: { xmin: arr[o + 2], ymin: arr[o + 3], zmin: arr[o + 4], xmax: arr[o + 5], ymax: arr[o + 6], zmax: arr[o + 7] },
535
+ centerOfMass: { x: arr[o + 8], y: arr[o + 9], z: arr[o + 10] },
536
+ shapeType: SHAPE_TYPES[arr[o + 11]] ?? "shape",
537
+ isValid: arr[o + 12] === 1.0,
538
+ });
539
+ }
540
+ return results;
541
+ }));
688
542
  }
689
543
  /** Fillet multiple solids in a single WASM call. */
690
544
  filletBatch(ops) {
691
- return wrap("filletBatch", () => {
692
- const solids = this.#makeVectorU32(ops.map(op => op.solid));
693
- const edgeCounts = this.#makeVectorI32(ops.map(op => op.edges.length));
694
- const flatEdges = this.#makeVectorU32(ops.flatMap(op => op.edges));
695
- const radii = this.#makeVectorF64(ops.map(op => op.radius));
696
- try {
697
- return this.#vecToHandles(this.#raw.filletBatch(solids, edgeCounts, flatEdges, radii));
698
- }
699
- finally {
700
- solids.delete();
701
- edgeCounts.delete();
702
- flatEdges.delete();
703
- radii.delete();
704
- }
705
- });
545
+ return wrap("filletBatch", () => this.#withU32(ops.map((op) => op.solid), (solids) => this.#withI32(ops.map((op) => op.edges.length), (edgeCounts) => this.#withU32(ops.flatMap((op) => op.edges), (flatEdges) => this.#withF64(ops.map((op) => op.radius), (radii) => this.#vecToHandles(this.#raw.filletBatch(solids, edgeCounts, flatEdges, radii)))))));
706
546
  }
707
547
  /** Apply 3x4 affine transforms to multiple shapes in a single WASM call. */
708
548
  transformBatch(shapes, matrices) {
709
- return wrap("transformBatch", () => {
710
- const ids = this.#makeVectorU32(shapes);
711
- const mats = this.#makeVectorF64(matrices);
712
- try {
713
- return this.#vecToHandles(this.#raw.transformBatch(ids, mats));
714
- }
715
- finally {
716
- ids.delete();
717
- mats.delete();
718
- }
719
- });
549
+ return wrap("transformBatch", () => this.#withU32(shapes, (ids) => this.#withF64(matrices, (mats) => this.#vecToHandles(this.#raw.transformBatch(ids, mats)))));
720
550
  }
721
551
  /** Rotate multiple shapes in a single WASM call. */
722
552
  rotateBatch(shapes, params) {
723
- return wrap("rotateBatch", () => {
724
- const ids = this.#makeVectorU32(shapes);
725
- const p = this.#makeVectorF64(params);
726
- try {
727
- return this.#vecToHandles(this.#raw.rotateBatch(ids, p));
728
- }
729
- finally {
730
- ids.delete();
731
- p.delete();
732
- }
733
- });
553
+ return wrap("rotateBatch", () => this.#withU32(shapes, (ids) => this.#withF64(params, (p) => this.#vecToHandles(this.#raw.rotateBatch(ids, p)))));
734
554
  }
735
555
  /** Scale multiple shapes in a single WASM call. */
736
556
  scaleBatch(shapes, params) {
737
- return wrap("scaleBatch", () => {
738
- const ids = this.#makeVectorU32(shapes);
739
- const p = this.#makeVectorF64(params);
740
- try {
741
- return this.#vecToHandles(this.#raw.scaleBatch(ids, p));
742
- }
743
- finally {
744
- ids.delete();
745
- p.delete();
746
- }
747
- });
557
+ return wrap("scaleBatch", () => this.#withU32(shapes, (ids) => this.#withF64(params, (p) => this.#vecToHandles(this.#raw.scaleBatch(ids, p)))));
748
558
  }
749
559
  /** Mirror multiple shapes in a single WASM call. */
750
560
  mirrorBatch(shapes, params) {
751
- return wrap("mirrorBatch", () => {
752
- const ids = this.#makeVectorU32(shapes);
753
- const p = this.#makeVectorF64(params);
754
- try {
755
- return this.#vecToHandles(this.#raw.mirrorBatch(ids, p));
756
- }
757
- finally {
758
- ids.delete();
759
- p.delete();
760
- }
761
- });
561
+ return wrap("mirrorBatch", () => this.#withU32(shapes, (ids) => this.#withF64(params, (p) => this.#vecToHandles(this.#raw.mirrorBatch(ids, p)))));
762
562
  }
763
563
  // =======================================================================
764
564
  // Topology
765
565
  // =======================================================================
766
566
  getShapeType(shape) {
767
- return wrap("getShapeType", () => this.#raw.getShapeType(shape));
567
+ return wrap("getShapeType", () => asEnum(this.#raw.getShapeType(shape), SHAPE_TYPE_VALUES, "shape type"));
768
568
  }
769
569
  /** True if the shape is a compound. */
770
570
  isCompound(shape) { return this.getShapeType(shape) === "compound"; }
@@ -804,7 +604,7 @@ export class OcctKernel {
804
604
  return wrap("hashCode", () => this.#raw.hashCode(shape, upperBound));
805
605
  }
806
606
  shapeOrientation(shape) {
807
- return wrap("shapeOrientation", () => this.#raw.shapeOrientation(shape));
607
+ return wrap("shapeOrientation", () => asEnum(this.#raw.shapeOrientation(shape), SHAPE_ORIENTATION_VALUES, "shape orientation"));
808
608
  }
809
609
  sharedEdges(faceA, faceB) {
810
610
  return wrap("sharedEdges", () => this.#vecToHandles(this.#raw.sharedEdges(faceA, faceB)));
@@ -864,35 +664,29 @@ export class OcctKernel {
864
664
  }
865
665
  /** Tessellate multiple shapes in a single WASM call. */
866
666
  meshBatch(shapes, options) {
867
- return wrap("meshBatch", () => {
868
- const ids = this.#makeVectorU32(shapes);
667
+ return wrap("meshBatch", () => this.#withU32(shapes, (ids) => {
869
668
  const linDefl = options?.linearDeflection ?? 0.1;
870
669
  const angDefl = options?.angularDeflection ?? 0.5;
670
+ const raw = this.#raw.meshBatch(ids, linDefl, angDefl);
871
671
  try {
872
- const raw = this.#raw.meshBatch(ids, linDefl, angDefl);
873
- try {
874
- const positions = new Float32Array(this.#module.HEAPF32.buffer.slice(raw.getPositionsPtr(), raw.getPositionsPtr() + raw.positionCount * 4));
875
- const normals = new Float32Array(this.#module.HEAPF32.buffer.slice(raw.getNormalsPtr(), raw.getNormalsPtr() + raw.normalCount * 4));
876
- const indices = new Uint32Array(this.#module.HEAPU32.buffer.slice(raw.getIndicesPtr(), raw.getIndicesPtr() + raw.indexCount * 4));
877
- const shapeOffsets = new Int32Array(this.#module.HEAP32.buffer.slice(raw.getShapeOffsetsPtr(), raw.getShapeOffsetsPtr() + raw.shapeCount * 4 * 4));
878
- return {
879
- positions,
880
- normals,
881
- indices,
882
- shapeOffsets,
883
- shapeCount: raw.shapeCount,
884
- vertexCount: raw.positionCount / 3,
885
- triangleCount: raw.indexCount / 3,
886
- };
887
- }
888
- finally {
889
- raw.delete();
890
- }
672
+ const positions = new Float32Array(this.#module.HEAPF32.buffer.slice(raw.getPositionsPtr(), raw.getPositionsPtr() + raw.positionCount * 4));
673
+ const normals = new Float32Array(this.#module.HEAPF32.buffer.slice(raw.getNormalsPtr(), raw.getNormalsPtr() + raw.normalCount * 4));
674
+ const indices = new Uint32Array(this.#module.HEAPU32.buffer.slice(raw.getIndicesPtr(), raw.getIndicesPtr() + raw.indexCount * 4));
675
+ const shapeOffsets = new Int32Array(this.#module.HEAP32.buffer.slice(raw.getShapeOffsetsPtr(), raw.getShapeOffsetsPtr() + raw.shapeCount * 4 * 4));
676
+ return {
677
+ positions,
678
+ normals,
679
+ indices,
680
+ shapeOffsets,
681
+ shapeCount: raw.shapeCount,
682
+ vertexCount: raw.positionCount / 3,
683
+ triangleCount: raw.indexCount / 3,
684
+ };
891
685
  }
892
686
  finally {
893
- ids.delete();
687
+ raw.delete();
894
688
  }
895
- });
689
+ }));
896
690
  }
897
691
  // =======================================================================
898
692
  // I/O
@@ -944,16 +738,18 @@ export class OcctKernel {
944
738
  });
945
739
  }
946
740
  cacheStep(stepData) {
947
- const shape = this.importStep(stepData);
948
- try {
949
- return this.toBREP(shape);
950
- }
951
- finally {
952
- this.release(shape);
953
- }
741
+ return wrap("cacheStep", () => {
742
+ const shape = this.importStep(stepData);
743
+ try {
744
+ return this.toBREP(shape);
745
+ }
746
+ finally {
747
+ this.release(shape);
748
+ }
749
+ });
954
750
  }
955
751
  loadCached(brep) {
956
- return this.fromBREP(brep);
752
+ return wrap("loadCached", () => this.fromBREP(brep));
957
753
  }
958
754
  // =======================================================================
959
755
  // Query / Measure
@@ -1088,7 +884,7 @@ export class OcctKernel {
1088
884
  }
1089
885
  /** Classify a UV point relative to a face boundary. */
1090
886
  classifyPointOnFace(face, u, v) {
1091
- return wrap("classifyPointOnFace", () => this.#raw.classifyPointOnFace(face, u, v));
887
+ return wrap("classifyPointOnFace", () => asEnum(this.#raw.classifyPointOnFace(face, u, v), POINT_CLASSIFICATION_VALUES, "point classification"));
1092
888
  }
1093
889
  /** Create a BSpline surface from a grid of control points. */
1094
890
  bsplineSurface(controlPoints, rows, cols) {
@@ -1207,13 +1003,7 @@ export class OcctKernel {
1207
1003
  flatArr[j++] = p.x;
1208
1004
  flatArr[j++] = p.y;
1209
1005
  }
1210
- const flat = this.#makeVectorF64(flatArr);
1211
- try {
1212
- return handle(this.#raw.liftCurve2dToPlane(flat, planeOrigin.x, planeOrigin.y, planeOrigin.z, planeZ.x, planeZ.y, planeZ.z, planeX.x, planeX.y, planeX.z));
1213
- }
1214
- finally {
1215
- flat.delete();
1216
- }
1006
+ return this.#withF64(flatArr, (flat) => handle(this.#raw.liftCurve2dToPlane(flat, planeOrigin.x, planeOrigin.y, planeOrigin.z, planeZ.x, planeZ.y, planeZ.z, planeX.x, planeX.y, planeX.z)));
1217
1007
  });
1218
1008
  }
1219
1009
  // =======================================================================
@@ -1273,13 +1063,7 @@ export class OcctKernel {
1273
1063
  */
1274
1064
  defeature(shape, faces, tolerance) {
1275
1065
  return wrap("defeature", () => {
1276
- const vec = this.#makeVectorU32(faces);
1277
- try {
1278
- return handle(this.#raw.defeature(shape, vec, tolerance));
1279
- }
1280
- finally {
1281
- vec.delete();
1282
- }
1066
+ return this.#withU32(faces, (vec) => handle(this.#raw.defeature(shape, vec, tolerance)));
1283
1067
  });
1284
1068
  }
1285
1069
  reverseShape(shape) {
@@ -1300,140 +1084,50 @@ export class OcctKernel {
1300
1084
  // =======================================================================
1301
1085
  translateWithHistory(shape, dx, dy, dz, inputFaceHashes, hashUpperBound) {
1302
1086
  return wrap("translateWithHistory", () => {
1303
- const hashes = this.#makeVectorI32(inputFaceHashes);
1304
- try {
1305
- return this.#extractEvolution(this.#raw.translateWithHistory(shape, dx, dy, dz, hashes, hashUpperBound));
1306
- }
1307
- finally {
1308
- hashes.delete();
1309
- }
1087
+ return this.#withI32(inputFaceHashes, (hashes) => this.#extractEvolution(this.#raw.translateWithHistory(shape, dx, dy, dz, hashes, hashUpperBound)));
1310
1088
  });
1311
1089
  }
1312
1090
  fuseWithHistory(a, b, inputFaceHashes, hashUpperBound) {
1313
1091
  return wrap("fuseWithHistory", () => {
1314
- const hashes = this.#makeVectorI32(inputFaceHashes);
1315
- try {
1316
- return this.#extractEvolution(this.#raw.fuseWithHistory(a, b, hashes, hashUpperBound));
1317
- }
1318
- finally {
1319
- hashes.delete();
1320
- }
1092
+ return this.#withI32(inputFaceHashes, (hashes) => this.#extractEvolution(this.#raw.fuseWithHistory(a, b, hashes, hashUpperBound)));
1321
1093
  });
1322
1094
  }
1323
1095
  cutWithHistory(a, b, inputFaceHashes, hashUpperBound) {
1324
1096
  return wrap("cutWithHistory", () => {
1325
- const hashes = this.#makeVectorI32(inputFaceHashes);
1326
- try {
1327
- return this.#extractEvolution(this.#raw.cutWithHistory(a, b, hashes, hashUpperBound));
1328
- }
1329
- finally {
1330
- hashes.delete();
1331
- }
1097
+ return this.#withI32(inputFaceHashes, (hashes) => this.#extractEvolution(this.#raw.cutWithHistory(a, b, hashes, hashUpperBound)));
1332
1098
  });
1333
1099
  }
1334
1100
  filletWithHistory(solid, edges, radius, inputFaceHashes, hashUpperBound) {
1335
- return wrap("filletWithHistory", () => {
1336
- const edgeVec = this.#makeVectorU32(edges);
1337
- const hashes = this.#makeVectorI32(inputFaceHashes);
1338
- try {
1339
- return this.#extractEvolution(this.#raw.filletWithHistory(solid, edgeVec, radius, hashes, hashUpperBound));
1340
- }
1341
- finally {
1342
- edgeVec.delete();
1343
- hashes.delete();
1344
- }
1345
- });
1101
+ return wrap("filletWithHistory", () => this.#withU32(edges, (edgeVec) => this.#withI32(inputFaceHashes, (hashes) => this.#extractEvolution(this.#raw.filletWithHistory(solid, edgeVec, radius, hashes, hashUpperBound)))));
1346
1102
  }
1347
1103
  rotateWithHistory(shape, axis, angleRad, inputFaceHashes, hashUpperBound) {
1348
- return wrap("rotateWithHistory", () => {
1349
- const hashes = this.#makeVectorI32(inputFaceHashes);
1350
- try {
1351
- return this.#extractEvolution(this.#raw.rotateWithHistory(shape, axis.point.x, axis.point.y, axis.point.z, axis.direction.x, axis.direction.y, axis.direction.z, angleRad, hashes, hashUpperBound));
1352
- }
1353
- finally {
1354
- hashes.delete();
1355
- }
1356
- });
1104
+ return wrap("rotateWithHistory", () => this.#withI32(inputFaceHashes, (hashes) => this.#extractEvolution(this.#raw.rotateWithHistory(shape, axis.point.x, axis.point.y, axis.point.z, axis.direction.x, axis.direction.y, axis.direction.z, angleRad, hashes, hashUpperBound))));
1357
1105
  }
1358
1106
  mirrorWithHistory(shape, point, normal, inputFaceHashes, hashUpperBound) {
1359
- return wrap("mirrorWithHistory", () => {
1360
- const hashes = this.#makeVectorI32(inputFaceHashes);
1361
- try {
1362
- return this.#extractEvolution(this.#raw.mirrorWithHistory(shape, point.x, point.y, point.z, normal.x, normal.y, normal.z, hashes, hashUpperBound));
1363
- }
1364
- finally {
1365
- hashes.delete();
1366
- }
1367
- });
1107
+ return wrap("mirrorWithHistory", () => this.#withI32(inputFaceHashes, (hashes) => this.#extractEvolution(this.#raw.mirrorWithHistory(shape, point.x, point.y, point.z, normal.x, normal.y, normal.z, hashes, hashUpperBound))));
1368
1108
  }
1369
1109
  scaleWithHistory(shape, center, factor, inputFaceHashes, hashUpperBound) {
1370
- return wrap("scaleWithHistory", () => {
1371
- const hashes = this.#makeVectorI32(inputFaceHashes);
1372
- try {
1373
- return this.#extractEvolution(this.#raw.scaleWithHistory(shape, center.x, center.y, center.z, factor, hashes, hashUpperBound));
1374
- }
1375
- finally {
1376
- hashes.delete();
1377
- }
1378
- });
1110
+ return wrap("scaleWithHistory", () => this.#withI32(inputFaceHashes, (hashes) => this.#extractEvolution(this.#raw.scaleWithHistory(shape, center.x, center.y, center.z, factor, hashes, hashUpperBound))));
1379
1111
  }
1380
1112
  intersectWithHistory(a, b, inputFaceHashes, hashUpperBound) {
1381
1113
  return wrap("intersectWithHistory", () => {
1382
- const hashes = this.#makeVectorI32(inputFaceHashes);
1383
- try {
1384
- return this.#extractEvolution(this.#raw.intersectWithHistory(a, b, hashes, hashUpperBound));
1385
- }
1386
- finally {
1387
- hashes.delete();
1388
- }
1114
+ return this.#withI32(inputFaceHashes, (hashes) => this.#extractEvolution(this.#raw.intersectWithHistory(a, b, hashes, hashUpperBound)));
1389
1115
  });
1390
1116
  }
1391
1117
  chamferWithHistory(solid, edges, distance, inputFaceHashes, hashUpperBound) {
1392
- return wrap("chamferWithHistory", () => {
1393
- const edgeVec = this.#makeVectorU32(edges);
1394
- const hashes = this.#makeVectorI32(inputFaceHashes);
1395
- try {
1396
- return this.#extractEvolution(this.#raw.chamferWithHistory(solid, edgeVec, distance, hashes, hashUpperBound));
1397
- }
1398
- finally {
1399
- edgeVec.delete();
1400
- hashes.delete();
1401
- }
1402
- });
1118
+ return wrap("chamferWithHistory", () => this.#withU32(edges, (edgeVec) => this.#withI32(inputFaceHashes, (hashes) => this.#extractEvolution(this.#raw.chamferWithHistory(solid, edgeVec, distance, hashes, hashUpperBound)))));
1403
1119
  }
1404
1120
  shellWithHistory(solid, faces, thickness, tolerance, inputFaceHashes, hashUpperBound) {
1405
- return wrap("shellWithHistory", () => {
1406
- const faceVec = this.#makeVectorU32(faces);
1407
- const hashes = this.#makeVectorI32(inputFaceHashes);
1408
- try {
1409
- return this.#extractEvolution(this.#raw.shellWithHistory(solid, faceVec, thickness, tolerance, hashes, hashUpperBound));
1410
- }
1411
- finally {
1412
- faceVec.delete();
1413
- hashes.delete();
1414
- }
1415
- });
1121
+ return wrap("shellWithHistory", () => this.#withU32(faces, (faceVec) => this.#withI32(inputFaceHashes, (hashes) => this.#extractEvolution(this.#raw.shellWithHistory(solid, faceVec, thickness, tolerance, hashes, hashUpperBound)))));
1416
1122
  }
1417
1123
  offsetWithHistory(solid, distance, tolerance, inputFaceHashes, hashUpperBound) {
1418
1124
  return wrap("offsetWithHistory", () => {
1419
- const hashes = this.#makeVectorI32(inputFaceHashes);
1420
- try {
1421
- return this.#extractEvolution(this.#raw.offsetWithHistory(solid, distance, tolerance, hashes, hashUpperBound));
1422
- }
1423
- finally {
1424
- hashes.delete();
1425
- }
1125
+ return this.#withI32(inputFaceHashes, (hashes) => this.#extractEvolution(this.#raw.offsetWithHistory(solid, distance, tolerance, hashes, hashUpperBound)));
1426
1126
  });
1427
1127
  }
1428
1128
  thickenWithHistory(shape, thickness, tolerance, inputFaceHashes, hashUpperBound) {
1429
1129
  return wrap("thickenWithHistory", () => {
1430
- const hashes = this.#makeVectorI32(inputFaceHashes);
1431
- try {
1432
- return this.#extractEvolution(this.#raw.thickenWithHistory(shape, thickness, tolerance, hashes, hashUpperBound));
1433
- }
1434
- finally {
1435
- hashes.delete();
1436
- }
1130
+ return this.#withI32(inputFaceHashes, (hashes) => this.#extractEvolution(this.#raw.thickenWithHistory(shape, thickness, tolerance, hashes, hashUpperBound)));
1437
1131
  });
1438
1132
  }
1439
1133
  // =======================================================================
@@ -1672,6 +1366,36 @@ export class OcctKernel {
1672
1366
  }
1673
1367
  return this.#bulkI32(values);
1674
1368
  }
1369
+ // Scope guards: build a vector, run `fn` with it, and always delete it.
1370
+ // Replaces the make/try/finally/delete boilerplate at every vector-arg call
1371
+ // site so the cleanup can't be forgotten or mis-copied.
1372
+ #withU32(ids, fn) {
1373
+ const vec = this.#makeVectorU32(ids);
1374
+ try {
1375
+ return fn(vec);
1376
+ }
1377
+ finally {
1378
+ vec.delete();
1379
+ }
1380
+ }
1381
+ #withF64(values, fn) {
1382
+ const vec = this.#makeVectorF64(values);
1383
+ try {
1384
+ return fn(vec);
1385
+ }
1386
+ finally {
1387
+ vec.delete();
1388
+ }
1389
+ }
1390
+ #withI32(values, fn) {
1391
+ const vec = this.#makeVectorI32(values);
1392
+ try {
1393
+ return fn(vec);
1394
+ }
1395
+ finally {
1396
+ vec.delete();
1397
+ }
1398
+ }
1675
1399
  #flattenPoints(points) {
1676
1400
  if (points.length * 3 < _a.#BULK_THRESHOLD) {
1677
1401
  const vec = new this.#module.VectorDouble();