occt-wasm 3.3.1 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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) {
@@ -435,6 +383,9 @@ export class OcctKernel {
435
383
  }
436
384
  });
437
385
  }
386
+ makeBSplineEdge(poles, weights, knots, multiplicities, degree, periodic = false) {
387
+ return wrap("makeBSplineEdge", () => this.#withF64(poles, (polesVec) => this.#withF64(weights, (weightsVec) => this.#withF64(knots, (knotsVec) => this.#withI32(multiplicities, (multsVec) => handle(this.#raw.makeBSplineEdge(polesVec, weightsVec, knotsVec, multsVec, degree, periodic)))))));
388
+ }
438
389
  makeTangentArc(start, tangent, end) {
439
390
  return wrap("makeTangentArc", () => handle(this.#raw.makeTangentArc(start.x, start.y, start.z, tangent.x, tangent.y, tangent.z, end.x, end.y, end.z)));
440
391
  }
@@ -443,13 +394,7 @@ export class OcctKernel {
443
394
  }
444
395
  makeWire(edges) {
445
396
  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
- }
397
+ return this.#withU32(edges, (vec) => handle(this.#raw.makeWire(vec)));
453
398
  });
454
399
  }
455
400
  makeFace(wire) {
@@ -460,24 +405,12 @@ export class OcctKernel {
460
405
  }
461
406
  addHolesInFace(face, holeWires) {
462
407
  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
- }
408
+ return this.#withU32(holeWires, (vec) => handle(this.#raw.addHolesInFace(face, vec)));
470
409
  });
471
410
  }
472
411
  removeHolesFromFace(face, holeIndices) {
473
412
  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
- }
413
+ return this.#withI32(holeIndices, (vec) => handle(this.#raw.removeHolesFromFace(face, vec)));
481
414
  });
482
415
  }
483
416
  makeSolid(shell) {
@@ -485,46 +418,22 @@ export class OcctKernel {
485
418
  }
486
419
  sew(shapes, tolerance = 1e-6) {
487
420
  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
- }
421
+ return this.#withU32(shapes, (vec) => handle(this.#raw.sew(vec, tolerance)));
495
422
  });
496
423
  }
497
424
  sewAndSolidify(faces, tolerance = 1e-6) {
498
425
  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
- }
426
+ return this.#withU32(faces, (vec) => handle(this.#raw.sewAndSolidify(vec, tolerance)));
506
427
  });
507
428
  }
508
429
  buildSolidFromFaces(faces, tolerance = 1e-6) {
509
430
  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
- }
431
+ return this.#withU32(faces, (vec) => handle(this.#raw.buildSolidFromFaces(vec, tolerance)));
517
432
  });
518
433
  }
519
434
  makeCompound(shapes) {
520
435
  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
- }
436
+ return this.#withU32(shapes, (vec) => handle(this.#raw.makeCompound(vec)));
528
437
  });
529
438
  }
530
439
  buildTriFace(a, b, c) {
@@ -584,25 +493,13 @@ export class OcctKernel {
584
493
  /** Apply a 3x4 row-major affine transformation matrix (12 doubles: [r00,r01,r02,tx, r10,r11,r12,ty, r20,r21,r22,tz]). */
585
494
  transform(shape, matrix) {
586
495
  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
- }
496
+ return this.#withF64(matrix, (vec) => handle(this.#raw.transform(shape, vec)));
594
497
  });
595
498
  }
596
499
  /** Apply a general (possibly non-affine) 3x4 row-major transformation matrix (12 doubles). */
597
500
  generalTransform(shape, matrix) {
598
501
  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
- }
502
+ return this.#withF64(matrix, (vec) => handle(this.#raw.generalTransform(shape, vec)));
606
503
  });
607
504
  }
608
505
  linearPattern(shape, direction, spacing, count) {
@@ -613,158 +510,64 @@ export class OcctKernel {
613
510
  }
614
511
  /** Compose two 3x4 row-major transformation matrices. Returns a 12-element array. */
615
512
  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
- });
513
+ return wrap("composeTransform", () => this.#withF64(m1, (v1) => this.#withF64(m2, (v2) => this.#drainVector(this.#raw.composeTransform(v1, v2), Float64Array))));
628
514
  }
629
515
  // =======================================================================
630
516
  // Batch Operations
631
517
  // =======================================================================
632
518
  /** Translate multiple shapes by their respective offsets in a single WASM call. */
633
519
  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
- });
520
+ return wrap("translateBatch", () => this.#withU32(shapes, (ids) => this.#withF64(offsets, (off) => this.#vecToHandles(this.#raw.translateBatch(ids, off)))));
646
521
  }
647
522
  /** Chain boolean operations in a single WASM call. */
648
523
  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
- });
524
+ return wrap("booleanPipeline", () => this.#withI32(opCodes, (ops) => this.#withU32(tools, (ids) => handle(this.#raw.booleanPipeline(base, ops, ids)))));
660
525
  }
661
526
  /** Query multiple shapes in a single WASM call: bbox, volume, area, center of mass, type, validity. */
662
527
  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
- });
528
+ return wrap("queryBatch", () => this.#withU32(shapes, (ids) => {
529
+ const arr = this.#drainVector(this.#raw.queryBatch(ids), Float64Array);
530
+ const STRIDE = 14;
531
+ const results = [];
532
+ for (let i = 0; i < shapes.length; i++) {
533
+ const o = i * STRIDE;
534
+ results.push({
535
+ volume: arr[o],
536
+ area: arr[o + 1],
537
+ 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] },
538
+ centerOfMass: { x: arr[o + 8], y: arr[o + 9], z: arr[o + 10] },
539
+ shapeType: SHAPE_TYPES[arr[o + 11]] ?? "shape",
540
+ isValid: arr[o + 12] === 1.0,
541
+ });
542
+ }
543
+ return results;
544
+ }));
688
545
  }
689
546
  /** Fillet multiple solids in a single WASM call. */
690
547
  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
- });
548
+ 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
549
  }
707
550
  /** Apply 3x4 affine transforms to multiple shapes in a single WASM call. */
708
551
  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
- });
552
+ return wrap("transformBatch", () => this.#withU32(shapes, (ids) => this.#withF64(matrices, (mats) => this.#vecToHandles(this.#raw.transformBatch(ids, mats)))));
720
553
  }
721
554
  /** Rotate multiple shapes in a single WASM call. */
722
555
  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
- });
556
+ return wrap("rotateBatch", () => this.#withU32(shapes, (ids) => this.#withF64(params, (p) => this.#vecToHandles(this.#raw.rotateBatch(ids, p)))));
734
557
  }
735
558
  /** Scale multiple shapes in a single WASM call. */
736
559
  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
- });
560
+ return wrap("scaleBatch", () => this.#withU32(shapes, (ids) => this.#withF64(params, (p) => this.#vecToHandles(this.#raw.scaleBatch(ids, p)))));
748
561
  }
749
562
  /** Mirror multiple shapes in a single WASM call. */
750
563
  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
- });
564
+ return wrap("mirrorBatch", () => this.#withU32(shapes, (ids) => this.#withF64(params, (p) => this.#vecToHandles(this.#raw.mirrorBatch(ids, p)))));
762
565
  }
763
566
  // =======================================================================
764
567
  // Topology
765
568
  // =======================================================================
766
569
  getShapeType(shape) {
767
- return wrap("getShapeType", () => this.#raw.getShapeType(shape));
570
+ return wrap("getShapeType", () => asEnum(this.#raw.getShapeType(shape), SHAPE_TYPE_VALUES, "shape type"));
768
571
  }
769
572
  /** True if the shape is a compound. */
770
573
  isCompound(shape) { return this.getShapeType(shape) === "compound"; }
@@ -804,7 +607,7 @@ export class OcctKernel {
804
607
  return wrap("hashCode", () => this.#raw.hashCode(shape, upperBound));
805
608
  }
806
609
  shapeOrientation(shape) {
807
- return wrap("shapeOrientation", () => this.#raw.shapeOrientation(shape));
610
+ return wrap("shapeOrientation", () => asEnum(this.#raw.shapeOrientation(shape), SHAPE_ORIENTATION_VALUES, "shape orientation"));
808
611
  }
809
612
  sharedEdges(faceA, faceB) {
810
613
  return wrap("sharedEdges", () => this.#vecToHandles(this.#raw.sharedEdges(faceA, faceB)));
@@ -864,35 +667,29 @@ export class OcctKernel {
864
667
  }
865
668
  /** Tessellate multiple shapes in a single WASM call. */
866
669
  meshBatch(shapes, options) {
867
- return wrap("meshBatch", () => {
868
- const ids = this.#makeVectorU32(shapes);
670
+ return wrap("meshBatch", () => this.#withU32(shapes, (ids) => {
869
671
  const linDefl = options?.linearDeflection ?? 0.1;
870
672
  const angDefl = options?.angularDeflection ?? 0.5;
673
+ const raw = this.#raw.meshBatch(ids, linDefl, angDefl);
871
674
  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
- }
675
+ const positions = new Float32Array(this.#module.HEAPF32.buffer.slice(raw.getPositionsPtr(), raw.getPositionsPtr() + raw.positionCount * 4));
676
+ const normals = new Float32Array(this.#module.HEAPF32.buffer.slice(raw.getNormalsPtr(), raw.getNormalsPtr() + raw.normalCount * 4));
677
+ const indices = new Uint32Array(this.#module.HEAPU32.buffer.slice(raw.getIndicesPtr(), raw.getIndicesPtr() + raw.indexCount * 4));
678
+ const shapeOffsets = new Int32Array(this.#module.HEAP32.buffer.slice(raw.getShapeOffsetsPtr(), raw.getShapeOffsetsPtr() + raw.shapeCount * 4 * 4));
679
+ return {
680
+ positions,
681
+ normals,
682
+ indices,
683
+ shapeOffsets,
684
+ shapeCount: raw.shapeCount,
685
+ vertexCount: raw.positionCount / 3,
686
+ triangleCount: raw.indexCount / 3,
687
+ };
891
688
  }
892
689
  finally {
893
- ids.delete();
690
+ raw.delete();
894
691
  }
895
- });
692
+ }));
896
693
  }
897
694
  // =======================================================================
898
695
  // I/O
@@ -944,16 +741,18 @@ export class OcctKernel {
944
741
  });
945
742
  }
946
743
  cacheStep(stepData) {
947
- const shape = this.importStep(stepData);
948
- try {
949
- return this.toBREP(shape);
950
- }
951
- finally {
952
- this.release(shape);
953
- }
744
+ return wrap("cacheStep", () => {
745
+ const shape = this.importStep(stepData);
746
+ try {
747
+ return this.toBREP(shape);
748
+ }
749
+ finally {
750
+ this.release(shape);
751
+ }
752
+ });
954
753
  }
955
754
  loadCached(brep) {
956
- return this.fromBREP(brep);
755
+ return wrap("loadCached", () => this.fromBREP(brep));
957
756
  }
958
757
  // =======================================================================
959
758
  // Query / Measure
@@ -1088,7 +887,7 @@ export class OcctKernel {
1088
887
  }
1089
888
  /** Classify a UV point relative to a face boundary. */
1090
889
  classifyPointOnFace(face, u, v) {
1091
- return wrap("classifyPointOnFace", () => this.#raw.classifyPointOnFace(face, u, v));
890
+ return wrap("classifyPointOnFace", () => asEnum(this.#raw.classifyPointOnFace(face, u, v), POINT_CLASSIFICATION_VALUES, "point classification"));
1092
891
  }
1093
892
  /** Create a BSpline surface from a grid of control points. */
1094
893
  bsplineSurface(controlPoints, rows, cols) {
@@ -1199,6 +998,24 @@ export class OcctKernel {
1199
998
  return result;
1200
999
  });
1201
1000
  }
1001
+ curveDegreeElevate(edge, elevateBy) {
1002
+ return wrap("curveDegreeElevate", () => handle(this.#raw.curveDegreeElevate(edge, elevateBy)));
1003
+ }
1004
+ curveKnotInsert(edge, knot, times) {
1005
+ return wrap("curveKnotInsert", () => handle(this.#raw.curveKnotInsert(edge, knot, times)));
1006
+ }
1007
+ curveKnotRemove(edge, knot, tolerance) {
1008
+ return wrap("curveKnotRemove", () => handle(this.#raw.curveKnotRemove(edge, knot, tolerance)));
1009
+ }
1010
+ curveSplit(edge, param) {
1011
+ return wrap("curveSplit", () => {
1012
+ const parts = this.#vecToHandles(this.#raw.curveSplit(edge, param));
1013
+ if (parts.length !== 2) {
1014
+ throw new Error(`curveSplit: expected 2 edges, got ${parts.length}`);
1015
+ }
1016
+ return [parts[0], parts[1]];
1017
+ });
1018
+ }
1202
1019
  liftCurve2dToPlane(points2d, planeOrigin, planeZ, planeX) {
1203
1020
  return wrap("liftCurve2dToPlane", () => {
1204
1021
  const flatArr = new Array(points2d.length * 2);
@@ -1207,13 +1024,7 @@ export class OcctKernel {
1207
1024
  flatArr[j++] = p.x;
1208
1025
  flatArr[j++] = p.y;
1209
1026
  }
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
- }
1027
+ 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
1028
  });
1218
1029
  }
1219
1030
  // =======================================================================
@@ -1273,13 +1084,7 @@ export class OcctKernel {
1273
1084
  */
1274
1085
  defeature(shape, faces, tolerance) {
1275
1086
  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
- }
1087
+ return this.#withU32(faces, (vec) => handle(this.#raw.defeature(shape, vec, tolerance)));
1283
1088
  });
1284
1089
  }
1285
1090
  reverseShape(shape) {
@@ -1300,140 +1105,50 @@ export class OcctKernel {
1300
1105
  // =======================================================================
1301
1106
  translateWithHistory(shape, dx, dy, dz, inputFaceHashes, hashUpperBound) {
1302
1107
  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
- }
1108
+ return this.#withI32(inputFaceHashes, (hashes) => this.#extractEvolution(this.#raw.translateWithHistory(shape, dx, dy, dz, hashes, hashUpperBound)));
1310
1109
  });
1311
1110
  }
1312
1111
  fuseWithHistory(a, b, inputFaceHashes, hashUpperBound) {
1313
1112
  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
- }
1113
+ return this.#withI32(inputFaceHashes, (hashes) => this.#extractEvolution(this.#raw.fuseWithHistory(a, b, hashes, hashUpperBound)));
1321
1114
  });
1322
1115
  }
1323
1116
  cutWithHistory(a, b, inputFaceHashes, hashUpperBound) {
1324
1117
  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
- }
1118
+ return this.#withI32(inputFaceHashes, (hashes) => this.#extractEvolution(this.#raw.cutWithHistory(a, b, hashes, hashUpperBound)));
1332
1119
  });
1333
1120
  }
1334
1121
  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
- });
1122
+ return wrap("filletWithHistory", () => this.#withU32(edges, (edgeVec) => this.#withI32(inputFaceHashes, (hashes) => this.#extractEvolution(this.#raw.filletWithHistory(solid, edgeVec, radius, hashes, hashUpperBound)))));
1346
1123
  }
1347
1124
  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
- });
1125
+ 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
1126
  }
1358
1127
  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
- });
1128
+ 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
1129
  }
1369
1130
  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
- });
1131
+ return wrap("scaleWithHistory", () => this.#withI32(inputFaceHashes, (hashes) => this.#extractEvolution(this.#raw.scaleWithHistory(shape, center.x, center.y, center.z, factor, hashes, hashUpperBound))));
1379
1132
  }
1380
1133
  intersectWithHistory(a, b, inputFaceHashes, hashUpperBound) {
1381
1134
  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
- }
1135
+ return this.#withI32(inputFaceHashes, (hashes) => this.#extractEvolution(this.#raw.intersectWithHistory(a, b, hashes, hashUpperBound)));
1389
1136
  });
1390
1137
  }
1391
1138
  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
- });
1139
+ return wrap("chamferWithHistory", () => this.#withU32(edges, (edgeVec) => this.#withI32(inputFaceHashes, (hashes) => this.#extractEvolution(this.#raw.chamferWithHistory(solid, edgeVec, distance, hashes, hashUpperBound)))));
1403
1140
  }
1404
1141
  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
- });
1142
+ return wrap("shellWithHistory", () => this.#withU32(faces, (faceVec) => this.#withI32(inputFaceHashes, (hashes) => this.#extractEvolution(this.#raw.shellWithHistory(solid, faceVec, thickness, tolerance, hashes, hashUpperBound)))));
1416
1143
  }
1417
1144
  offsetWithHistory(solid, distance, tolerance, inputFaceHashes, hashUpperBound) {
1418
1145
  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
- }
1146
+ return this.#withI32(inputFaceHashes, (hashes) => this.#extractEvolution(this.#raw.offsetWithHistory(solid, distance, tolerance, hashes, hashUpperBound)));
1426
1147
  });
1427
1148
  }
1428
1149
  thickenWithHistory(shape, thickness, tolerance, inputFaceHashes, hashUpperBound) {
1429
1150
  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
- }
1151
+ return this.#withI32(inputFaceHashes, (hashes) => this.#extractEvolution(this.#raw.thickenWithHistory(shape, thickness, tolerance, hashes, hashUpperBound)));
1437
1152
  });
1438
1153
  }
1439
1154
  // =======================================================================
@@ -1672,6 +1387,36 @@ export class OcctKernel {
1672
1387
  }
1673
1388
  return this.#bulkI32(values);
1674
1389
  }
1390
+ // Scope guards: build a vector, run `fn` with it, and always delete it.
1391
+ // Replaces the make/try/finally/delete boilerplate at every vector-arg call
1392
+ // site so the cleanup can't be forgotten or mis-copied.
1393
+ #withU32(ids, fn) {
1394
+ const vec = this.#makeVectorU32(ids);
1395
+ try {
1396
+ return fn(vec);
1397
+ }
1398
+ finally {
1399
+ vec.delete();
1400
+ }
1401
+ }
1402
+ #withF64(values, fn) {
1403
+ const vec = this.#makeVectorF64(values);
1404
+ try {
1405
+ return fn(vec);
1406
+ }
1407
+ finally {
1408
+ vec.delete();
1409
+ }
1410
+ }
1411
+ #withI32(values, fn) {
1412
+ const vec = this.#makeVectorI32(values);
1413
+ try {
1414
+ return fn(vec);
1415
+ }
1416
+ finally {
1417
+ vec.delete();
1418
+ }
1419
+ }
1675
1420
  #flattenPoints(points) {
1676
1421
  if (points.length * 3 < _a.#BULK_THRESHOLD) {
1677
1422
  const vec = new this.#module.VectorDouble();