brepjs 14.1.3 → 14.2.1

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.
Files changed (91) hide show
  1. package/dist/2d.cjs +5 -5
  2. package/dist/2d.js +5 -5
  3. package/dist/{blueprint-C6B--LQ6.cjs → blueprint-C8nlHdUE.cjs} +5 -5
  4. package/dist/{blueprint-CzFYcmzV.js → blueprint-Dsp-f4De.js} +5 -5
  5. package/dist/{blueprintFns-B2AvzIXt.cjs → blueprintFns-CvtakKGf.cjs} +2 -2
  6. package/dist/{blueprintFns-wka8INr_.js → blueprintFns-D1QUp5y4.js} +2 -2
  7. package/dist/{boolean2D-CfCt_m29.js → boolean2D-D1MYwspP.js} +49 -8
  8. package/dist/{boolean2D-6bQx64bR.cjs → boolean2D-DE2axG6W.cjs} +54 -7
  9. package/dist/{booleanFns-B6zxpf96.js → booleanFns-DViuH9nW.js} +32 -16
  10. package/dist/{booleanFns-C2pjUC6-.cjs → booleanFns-Dmvv9VVT.cjs} +32 -16
  11. package/dist/brepjs.cjs +23 -19
  12. package/dist/brepjs.js +20 -20
  13. package/dist/core.cjs +1 -1
  14. package/dist/core.js +1 -1
  15. package/dist/{cornerFinder-DBXma4vL.js → cornerFinder-CyvfiiYv.js} +1 -1
  16. package/dist/{cornerFinder-DrHIlWCS.cjs → cornerFinder-Du1Q1hgu.cjs} +1 -1
  17. package/dist/{curveFns-C_vQNb7-.cjs → curveFns--4Nh1ZtB.cjs} +1 -1
  18. package/dist/{curveFns-DmID8AQO.js → curveFns-D9GbWpcl.js} +1 -1
  19. package/dist/{drawFns-rLQIZezc.js → drawFns-BDqEWrCy.js} +11 -11
  20. package/dist/{drawFns-DRG-UpkW.cjs → drawFns-DTqCnYeo.cjs} +11 -11
  21. package/dist/{extrudeFns-BP7fNL5O.cjs → extrudeFns-CIUq5ZQW.cjs} +44 -1
  22. package/dist/{extrudeFns-1qcdK9dq.js → extrudeFns-DxS_UOMr.js} +39 -2
  23. package/dist/{faceFns-BwzwlBjE.cjs → faceFns-D7i9yEts.cjs} +1 -1
  24. package/dist/{faceFns-BHT5DAOL.js → faceFns-lcEYbLOs.js} +1 -1
  25. package/dist/{helpers-C4Ajoc6x.cjs → helpers-BK3DVd3M.cjs} +5 -5
  26. package/dist/{helpers-B94CkHQe.js → helpers-BwzOwHWl.js} +5 -5
  27. package/dist/{historyFns-CrsjeD69.cjs → historyFns-BO9c4AgQ.cjs} +4 -4
  28. package/dist/{historyFns-BsSuAQ0m.js → historyFns-C_EoZWD5.js} +4 -4
  29. package/dist/{importFns-DPwXjtkm.cjs → importFns-Ca7DsY-k.cjs} +2 -2
  30. package/dist/{importFns-DQsQOpRY.js → importFns-NY1stg0C.js} +2 -2
  31. package/dist/index.d.ts +4 -1
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/io.cjs +2 -2
  34. package/dist/io.js +2 -2
  35. package/dist/kernel/index.d.ts +2 -0
  36. package/dist/kernel/index.d.ts.map +1 -1
  37. package/dist/kernel/interfaces/modifierOps.d.ts +16 -0
  38. package/dist/kernel/interfaces/modifierOps.d.ts.map +1 -1
  39. package/dist/kernel/interfaces/sweepOps.d.ts +15 -0
  40. package/dist/kernel/interfaces/sweepOps.d.ts.map +1 -1
  41. package/dist/kernel/occt/booleanBatchOps.d.ts +13 -0
  42. package/dist/kernel/occt/booleanBatchOps.d.ts.map +1 -0
  43. package/dist/kernel/occt/booleanOps.d.ts.map +1 -1
  44. package/dist/kernel/occt/defaultAdapter.d.ts +6 -0
  45. package/dist/kernel/occt/defaultAdapter.d.ts.map +1 -1
  46. package/dist/kernel/occt/meshOps.d.ts.map +1 -1
  47. package/dist/kernel/occt/modifierOps.d.ts +19 -0
  48. package/dist/kernel/occt/modifierOps.d.ts.map +1 -1
  49. package/dist/kernel/occt/sweepOps.d.ts +17 -0
  50. package/dist/kernel/occt/sweepOps.d.ts.map +1 -1
  51. package/dist/kernel/occt/transformOps.d.ts.map +1 -1
  52. package/dist/kernel/perfStats.d.ts +27 -0
  53. package/dist/kernel/perfStats.d.ts.map +1 -0
  54. package/dist/kernel/types.d.ts +6 -0
  55. package/dist/kernel/types.d.ts.map +1 -1
  56. package/dist/{measureFns-iarjC69-.js → measureFns-Dsan3QcB.js} +2 -2
  57. package/dist/{measureFns-j1ze9DDM.cjs → measureFns-rVSankqO.cjs} +2 -2
  58. package/dist/measurement.cjs +1 -1
  59. package/dist/measurement.js +1 -1
  60. package/dist/{meshFns-DRl9xnbO.js → meshFns-CKs4H-CH.js} +2 -2
  61. package/dist/{meshFns-CONktkIA.cjs → meshFns-Z5n8gFAS.cjs} +2 -2
  62. package/dist/operations/extrudeFns.d.ts +16 -0
  63. package/dist/operations/extrudeFns.d.ts.map +1 -1
  64. package/dist/operations/loftFns.d.ts +22 -0
  65. package/dist/operations/loftFns.d.ts.map +1 -1
  66. package/dist/operations.cjs +2 -2
  67. package/dist/operations.js +2 -2
  68. package/dist/{primitiveFns-CMIscOrN.js → primitiveFns-DmIP-qnU.js} +17 -7
  69. package/dist/{primitiveFns-CGg6EBKg.cjs → primitiveFns-w2otksRk.cjs} +17 -7
  70. package/dist/query.cjs +2 -2
  71. package/dist/query.js +2 -2
  72. package/dist/{shapeFns-DY7pjzrz.cjs → shapeFns-DZ2c5Nqo.cjs} +2 -2
  73. package/dist/{shapeFns-Brq2J-mW.js → shapeFns-E3qTx3nN.js} +2 -2
  74. package/dist/shapeRef.cjs +1 -1
  75. package/dist/shapeRef.js +1 -1
  76. package/dist/{shapeRefFns-BqOlRA7C.js → shapeRefFns-CEYgkZ1v.js} +3 -3
  77. package/dist/{shapeRefFns-BOljxSxd.cjs → shapeRefFns-Dl7v17MC.cjs} +3 -3
  78. package/dist/{shapeTypes-B3kwoJWf.cjs → shapeTypes-B5Vx1Wsw.cjs} +538 -171
  79. package/dist/{shapeTypes-CoNj-AHu.js → shapeTypes-DUJg_N3P.js} +527 -172
  80. package/dist/sketching.cjs +2 -2
  81. package/dist/sketching.js +2 -2
  82. package/dist/{solidBuilders-BzIatk9a.js → solidBuilders-0gTTuSxw.js} +2 -2
  83. package/dist/{solidBuilders-V8qvmW7h.cjs → solidBuilders-Bvq6br4f.cjs} +2 -2
  84. package/dist/{surfaceBuilders-CoiofY0Y.cjs → surfaceBuilders-CYWopaht.cjs} +2 -2
  85. package/dist/{surfaceBuilders-Cfo4nH6A.js → surfaceBuilders-DMI0n7Bf.js} +2 -2
  86. package/dist/topology/booleanFns.d.ts.map +1 -1
  87. package/dist/topology/modifierFns.d.ts +6 -2
  88. package/dist/topology/modifierFns.d.ts.map +1 -1
  89. package/dist/topology.cjs +6 -6
  90. package/dist/topology.js +6 -6
  91. package/package.json +2 -2
@@ -337,6 +337,60 @@ function classifyPointOnFace$1(oc, face, u, v, tolerance = 1e-6) {
337
337
  return "out";
338
338
  }
339
339
  //#endregion
340
+ //#region src/kernel/perfStats.ts
341
+ /**
342
+ * Operation-level performance instrumentation.
343
+ *
344
+ * Lightweight timing module with zero allocation on the hot path.
345
+ * Each operation category has a cumulative duration counter.
346
+ *
347
+ * Used by kernel adapters to record timing for boolean, loft,
348
+ * extrude, shell, fillet, mesh, edge mesh, and transform operations.
349
+ */
350
+ var CATEGORIES = [
351
+ "boolean",
352
+ "loft",
353
+ "extrude",
354
+ "shell",
355
+ "fillet",
356
+ "mesh",
357
+ "edgeMesh",
358
+ "transform"
359
+ ];
360
+ var _totals = Object.create(null);
361
+ var _counts = Object.create(null);
362
+ function _init() {
363
+ for (const c of CATEGORIES) {
364
+ _totals[c] = 0;
365
+ _counts[c] = 0;
366
+ }
367
+ }
368
+ _init();
369
+ /**
370
+ * Start timing an operation. Returns a function to call when the operation completes.
371
+ * Uses `performance.now()` for sub-millisecond precision.
372
+ */
373
+ function perfTimer(category) {
374
+ const start = performance.now();
375
+ return () => {
376
+ _totals[category] = (_totals[category] ?? 0) + (performance.now() - start);
377
+ _counts[category] = (_counts[category] ?? 0) + 1;
378
+ };
379
+ }
380
+ /** Read accumulated stats (non-destructive). */
381
+ function getPerformanceStats() {
382
+ const result = {};
383
+ for (const c of CATEGORIES) result[c] = {
384
+ totalMs: _totals[c] ?? 0,
385
+ count: _counts[c] ?? 0
386
+ };
387
+ return result;
388
+ }
389
+ /** Reset all counters to zero. */
390
+ function resetPerformanceStats() {
391
+ _init();
392
+ }
393
+ //#endregion
340
394
  //#region src/kernel/occt/transformOps.ts
341
395
  /** Cached flag: does the WASM build include TransformBatch? */
342
396
  var hasCppTransformBatch;
@@ -354,44 +408,49 @@ function detectCppTransformBatch(oc) {
354
408
  */
355
409
  function transformBatch(oc, entries) {
356
410
  if (entries.length === 0) return [];
357
- /* v8 ignore start -- C++ extractor not available in test WASM build */
358
- if (detectCppTransformBatch(oc)) {
359
- const batch = new oc.TransformBatch();
360
- try {
361
- for (const e of entries) switch (e.type) {
362
- case "translate":
363
- batch.addTranslate(e.shape, e.x, e.y, e.z);
364
- break;
365
- case "rotate":
366
- batch.addRotate(e.shape, e.angle * Math.PI / 180, ...e.axis, ...e.center);
367
- break;
368
- case "scale":
369
- batch.addScale(e.shape, ...e.center, e.factor);
370
- break;
371
- case "mirror":
372
- batch.addMirror(e.shape, ...e.origin, ...e.normal);
373
- break;
374
- }
375
- const result = batch.execute();
411
+ const endPerf = perfTimer("transform");
412
+ try {
413
+ /* v8 ignore start -- C++ extractor not available in test WASM build */
414
+ if (detectCppTransformBatch(oc)) {
415
+ const batch = new oc.TransformBatch();
376
416
  try {
377
- const count = result.getShapesCount();
378
- return Array.from({ length: count }, (_, i) => result.getShape(i));
417
+ for (const e of entries) switch (e.type) {
418
+ case "translate":
419
+ batch.addTranslate(e.shape, e.x, e.y, e.z);
420
+ break;
421
+ case "rotate":
422
+ batch.addRotate(e.shape, e.angle * Math.PI / 180, ...e.axis, ...e.center);
423
+ break;
424
+ case "scale":
425
+ batch.addScale(e.shape, ...e.center, e.factor);
426
+ break;
427
+ case "mirror":
428
+ batch.addMirror(e.shape, ...e.origin, ...e.normal);
429
+ break;
430
+ }
431
+ const result = batch.execute();
432
+ try {
433
+ const count = result.getShapesCount();
434
+ return Array.from({ length: count }, (_, i) => result.getShape(i));
435
+ } finally {
436
+ result.delete();
437
+ }
379
438
  } finally {
380
- result.delete();
439
+ batch.delete();
381
440
  }
382
- } finally {
383
- batch.delete();
384
441
  }
442
+ /* v8 ignore stop */
443
+ return entries.map((e) => {
444
+ switch (e.type) {
445
+ case "translate": return translate$1(oc, e.shape, e.x, e.y, e.z);
446
+ case "rotate": return rotate$1(oc, e.shape, e.angle, [...e.axis], [...e.center]);
447
+ case "scale": return scale$1(oc, e.shape, [...e.center], e.factor);
448
+ case "mirror": return mirror$1(oc, e.shape, [...e.origin], [...e.normal]);
449
+ }
450
+ });
451
+ } finally {
452
+ endPerf();
385
453
  }
386
- /* v8 ignore stop */
387
- return entries.map((e) => {
388
- switch (e.type) {
389
- case "translate": return translate$1(oc, e.shape, e.x, e.y, e.z);
390
- case "rotate": return rotate$1(oc, e.shape, e.angle, [...e.axis], [...e.center]);
391
- case "scale": return scale$1(oc, e.shape, [...e.center], e.factor);
392
- case "mirror": return mirror$1(oc, e.shape, [...e.origin], [...e.normal]);
393
- }
394
- });
395
454
  }
396
455
  /**
397
456
  * Applies a transformation matrix to a shape.
@@ -507,6 +566,58 @@ function simplify$1(oc, shape) {
507
566
  return result;
508
567
  }
509
568
  //#endregion
569
+ //#region src/kernel/occt/booleanBatchOps.ts
570
+ var hasCppBooleanBatch;
571
+ function resetBooleanBatchDetectionCache() {
572
+ hasCppBooleanBatch = void 0;
573
+ }
574
+ function detectCppBooleanBatch(oc) {
575
+ hasCppBooleanBatch ??= typeof oc.BooleanBatch === "function";
576
+ return hasCppBooleanBatch;
577
+ }
578
+ function glueToInt(optimisation) {
579
+ if (optimisation === "commonFace") return 1;
580
+ if (optimisation === "sameFace") return 2;
581
+ return 0;
582
+ }
583
+ /**
584
+ * Attempt fuseAll via C++ BooleanBatch extractor.
585
+ * Returns null if C++ extractor is not available.
586
+ */
587
+ function cppFuseAll(oc, shapes, options = {}) {
588
+ /* v8 ignore start -- C++ extractor not available in test WASM build */
589
+ if (!detectCppBooleanBatch(oc)) return null;
590
+ const end = perfTimer("boolean");
591
+ const batch = new oc.BooleanBatch();
592
+ try {
593
+ for (const s of shapes) batch.addShape(s);
594
+ return batch.fuseAll(glueToInt(options.optimisation), !!options.simplify, options.fuzzyValue ?? 0);
595
+ } finally {
596
+ batch.delete();
597
+ end();
598
+ }
599
+ /* v8 ignore stop */
600
+ }
601
+ /**
602
+ * Attempt cutAll via C++ BooleanBatch extractor.
603
+ * Returns null if C++ extractor is not available.
604
+ */
605
+ function cppCutAll(oc, base, tools, options = {}) {
606
+ /* v8 ignore start -- C++ extractor not available in test WASM build */
607
+ if (!detectCppBooleanBatch(oc)) return null;
608
+ if (tools.length === 0) return base;
609
+ const end = perfTimer("boolean");
610
+ const batch = new oc.BooleanBatch();
611
+ try {
612
+ for (const t of tools) batch.addShape(t);
613
+ return batch.cutAll(base, glueToInt(options.optimisation), !!options.simplify, options.fuzzyValue ?? 0);
614
+ } finally {
615
+ batch.delete();
616
+ end();
617
+ }
618
+ /* v8 ignore stop */
619
+ }
620
+ //#endregion
510
621
  //#region src/kernel/occt/booleanOps.ts
511
622
  /** Tolerance passed to OCCT SimplifyResult (ShapeUpgrade_UnifySameDomain). */
512
623
  var SIMPLIFY_TOLERANCE = .001;
@@ -531,6 +642,34 @@ function applyBooleanDefaults(op, fuzzyValue) {
531
642
  if (fuzzyValue !== void 0 && fuzzyValue > 0) op.SetFuzzyValue?.(fuzzyValue);
532
643
  }
533
644
  /**
645
+ * Compute a sensible fuzzy value based on shape bounding box diagonal.
646
+ * Returns 0 (no fuzzy) for very small shapes; 1e-5 for mm-scale geometry.
647
+ *
648
+ * Only fires for multi-shape operations (≥3 shapes) where vertex merging
649
+ * during intersection is the bottleneck. For 2-shape operations the overhead
650
+ * of computing a bounding box exceeds the benefit.
651
+ */
652
+ function autoFuzzyValue(oc, shapes) {
653
+ if (shapes.length < 3) return 0;
654
+ const firstShape = shapes[0];
655
+ if (!firstShape) return 0;
656
+ const box = new oc.Bnd_Box_1();
657
+ oc.BRepBndLib.Add(firstShape, box, true);
658
+ if (box.IsVoid()) {
659
+ box.delete();
660
+ return 0;
661
+ }
662
+ const min = box.CornerMin();
663
+ const max = box.CornerMax();
664
+ const dx = max.X() - min.X();
665
+ const dy = max.Y() - min.Y();
666
+ const dz = max.Z() - min.Z();
667
+ min.delete();
668
+ max.delete();
669
+ box.delete();
670
+ return Math.sqrt(dx * dx + dy * dy + dz * dz) > 1 ? 1e-5 : 0;
671
+ }
672
+ /**
534
673
  * Builds a compound from multiple shapes.
535
674
  */
536
675
  function buildCompound(oc, shapes) {
@@ -545,49 +684,64 @@ function buildCompound(oc, shapes) {
545
684
  * Fuses two shapes together.
546
685
  */
547
686
  function fuse$1(oc, shape, tool, options = {}) {
548
- const { optimisation, simplify = false, fuzzyValue } = options;
549
- const progress = new oc.Message_ProgressRange_1();
550
- const fuseOp = new oc.BRepAlgoAPI_Fuse_3(shape, tool, progress);
551
- applyGlue(oc, fuseOp, optimisation);
552
- applyBooleanDefaults(fuseOp, fuzzyValue);
553
- fuseOp.Build(progress);
554
- if (simplify) fuseOp.SimplifyResult(true, true, SIMPLIFY_TOLERANCE);
555
- const result = fuseOp.Shape();
556
- fuseOp.delete();
557
- progress.delete();
558
- return result;
687
+ const end = perfTimer("boolean");
688
+ try {
689
+ const { optimisation, simplify = false, fuzzyValue } = options;
690
+ const progress = new oc.Message_ProgressRange_1();
691
+ const fuseOp = new oc.BRepAlgoAPI_Fuse_3(shape, tool, progress);
692
+ applyGlue(oc, fuseOp, optimisation);
693
+ applyBooleanDefaults(fuseOp, fuzzyValue);
694
+ fuseOp.Build(progress);
695
+ if (simplify) fuseOp.SimplifyResult(true, true, SIMPLIFY_TOLERANCE);
696
+ const result = fuseOp.Shape();
697
+ fuseOp.delete();
698
+ progress.delete();
699
+ return result;
700
+ } finally {
701
+ end();
702
+ }
559
703
  }
560
704
  /**
561
705
  * Cuts a tool shape from a base shape.
562
706
  */
563
707
  function cut$1(oc, shape, tool, options = {}) {
564
- const { optimisation, simplify = false, fuzzyValue } = options;
565
- const progress = new oc.Message_ProgressRange_1();
566
- const cutOp = new oc.BRepAlgoAPI_Cut_3(shape, tool, progress);
567
- applyGlue(oc, cutOp, optimisation);
568
- applyBooleanDefaults(cutOp, fuzzyValue);
569
- cutOp.Build(progress);
570
- if (simplify) cutOp.SimplifyResult(true, true, SIMPLIFY_TOLERANCE);
571
- const result = cutOp.Shape();
572
- cutOp.delete();
573
- progress.delete();
574
- return result;
708
+ const end = perfTimer("boolean");
709
+ try {
710
+ const { optimisation, simplify = false, fuzzyValue } = options;
711
+ const progress = new oc.Message_ProgressRange_1();
712
+ const cutOp = new oc.BRepAlgoAPI_Cut_3(shape, tool, progress);
713
+ applyGlue(oc, cutOp, optimisation);
714
+ applyBooleanDefaults(cutOp, fuzzyValue);
715
+ cutOp.Build(progress);
716
+ if (simplify) cutOp.SimplifyResult(true, true, SIMPLIFY_TOLERANCE);
717
+ const result = cutOp.Shape();
718
+ cutOp.delete();
719
+ progress.delete();
720
+ return result;
721
+ } finally {
722
+ end();
723
+ }
575
724
  }
576
725
  /**
577
726
  * Intersects two shapes.
578
727
  */
579
728
  function intersect$1(oc, shape, tool, options = {}) {
580
- const { optimisation, simplify = false, fuzzyValue } = options;
581
- const progress = new oc.Message_ProgressRange_1();
582
- const commonOp = new oc.BRepAlgoAPI_Common_3(shape, tool, progress);
583
- applyGlue(oc, commonOp, optimisation);
584
- applyBooleanDefaults(commonOp, fuzzyValue);
585
- commonOp.Build(progress);
586
- if (simplify) commonOp.SimplifyResult(true, true, SIMPLIFY_TOLERANCE);
587
- const result = commonOp.Shape();
588
- commonOp.delete();
589
- progress.delete();
590
- return result;
729
+ const end = perfTimer("boolean");
730
+ try {
731
+ const { optimisation, simplify = false, fuzzyValue } = options;
732
+ const progress = new oc.Message_ProgressRange_1();
733
+ const commonOp = new oc.BRepAlgoAPI_Common_3(shape, tool, progress);
734
+ applyGlue(oc, commonOp, optimisation);
735
+ applyBooleanDefaults(commonOp, fuzzyValue);
736
+ commonOp.Build(progress);
737
+ if (simplify) commonOp.SimplifyResult(true, true, SIMPLIFY_TOLERANCE);
738
+ const result = commonOp.Shape();
739
+ commonOp.delete();
740
+ progress.delete();
741
+ return result;
742
+ } finally {
743
+ end();
744
+ }
591
745
  }
592
746
  /**
593
747
  * Sections a shape with another shape (typically a planar face), returning
@@ -613,26 +767,34 @@ function section$1(oc, shape, tool, approximation = true) {
613
767
  * Fuses multiple shapes using native OCCT N-way general fuse.
614
768
  */
615
769
  function fuseAllNative(oc, shapes, options = {}) {
616
- const { optimisation, simplify = false, fuzzyValue } = options;
617
- const argList = new oc.TopTools_ListOfShape_1();
618
- for (const s of shapes) argList.Append_1(s);
619
- const builder = new oc.BRepAlgoAPI_BuilderAlgo_1();
620
- builder.SetArguments(argList);
621
- applyGlue(oc, builder, optimisation);
622
- applyBooleanDefaults(builder, fuzzyValue);
623
- const progress = new oc.Message_ProgressRange_1();
624
- builder.Build(progress);
625
- let result = builder.Shape();
626
- if (simplify) {
627
- const upgrader = new oc.ShapeUpgrade_UnifySameDomain_2(result, true, true, false);
628
- upgrader.Build();
629
- result = upgrader.Shape();
630
- upgrader.delete();
770
+ const cppResult = cppFuseAll(oc, shapes, options);
771
+ if (cppResult !== null) return cppResult;
772
+ const end = perfTimer("boolean");
773
+ try {
774
+ const { optimisation, simplify = false } = options;
775
+ const fuzzyValue = options.fuzzyValue ?? autoFuzzyValue(oc, shapes);
776
+ const argList = new oc.TopTools_ListOfShape_1();
777
+ for (const s of shapes) argList.Append_1(s);
778
+ const builder = new oc.BRepAlgoAPI_BuilderAlgo_1();
779
+ builder.SetArguments(argList);
780
+ applyGlue(oc, builder, optimisation);
781
+ applyBooleanDefaults(builder, fuzzyValue);
782
+ const progress = new oc.Message_ProgressRange_1();
783
+ builder.Build(progress);
784
+ let result = builder.Shape();
785
+ if (simplify) {
786
+ const upgrader = new oc.ShapeUpgrade_UnifySameDomain_2(result, true, true, false);
787
+ upgrader.Build();
788
+ result = upgrader.Shape();
789
+ upgrader.delete();
790
+ }
791
+ argList.delete();
792
+ builder.delete();
793
+ progress.delete();
794
+ return result;
795
+ } finally {
796
+ end();
631
797
  }
632
- argList.delete();
633
- builder.delete();
634
- progress.delete();
635
- return result;
636
798
  }
637
799
  /**
638
800
  * Fuses multiple shapes using recursive pairwise fusion with index ranges.
@@ -704,6 +866,8 @@ function split$1(oc, shape, tools) {
704
866
  */
705
867
  function cutAll$1(oc, shape, tools, options = {}) {
706
868
  if (tools.length === 0) return shape;
869
+ const cppResult = cppCutAll(oc, shape, tools, options);
870
+ if (cppResult !== null) return cppResult;
707
871
  const toolCompound = buildCompound(oc, tools);
708
872
  const result = cut$1(oc, shape, toolCompound, options);
709
873
  toolCompound.delete();
@@ -760,59 +924,69 @@ function sliceF32(heap, ptr, size) {
760
924
  * grouping, and triangle winding correction.
761
925
  */
762
926
  function mesh$1(oc, shape, options) {
763
- const raw = oc.MeshExtractor.extract(shape, options.tolerance, options.angularTolerance, !!options.skipNormals, !!options.includeUVs);
764
- const verticesSize = raw.getVerticesSize();
765
- const normalsSize = raw.getNormalsSize();
766
- const trianglesSize = raw.getTrianglesSize();
767
- const faceGroupsSize = raw.getFaceGroupsSize();
768
- const uvsSize = raw.getUvsSize();
769
- const vertices = sliceF32(oc.HEAPF32, raw.getVerticesPtr(), verticesSize);
770
- const normals = options.skipNormals || normalsSize === 0 ? new Float32Array(0) : sliceF32(oc.HEAPF32, raw.getNormalsPtr(), normalsSize);
771
- const trianglesPtr = raw.getTrianglesPtr() / 4;
772
- const triangles = oc.HEAPU32.slice(trianglesPtr, trianglesPtr + trianglesSize);
773
- const uvs = uvsSize > 0 ? sliceF32(oc.HEAPF32, raw.getUvsPtr(), uvsSize) : new Float32Array(0);
774
- const faceGroups = [];
775
- if (faceGroupsSize > 0) {
776
- const fgPtr = raw.getFaceGroupsPtr() / 4;
777
- const fgRaw = oc.HEAP32.slice(fgPtr, fgPtr + faceGroupsSize);
778
- for (let i = 0; i < fgRaw.length; i += 3) faceGroups.push({
779
- start: fgRaw[i],
780
- count: fgRaw[i + 1],
781
- faceHash: fgRaw[i + 2]
782
- });
927
+ const end = perfTimer("mesh");
928
+ try {
929
+ const raw = oc.MeshExtractor.extract(shape, options.tolerance, options.angularTolerance, !!options.skipNormals, !!options.includeUVs);
930
+ const verticesSize = raw.getVerticesSize();
931
+ const normalsSize = raw.getNormalsSize();
932
+ const trianglesSize = raw.getTrianglesSize();
933
+ const faceGroupsSize = raw.getFaceGroupsSize();
934
+ const uvsSize = raw.getUvsSize();
935
+ const vertices = sliceF32(oc.HEAPF32, raw.getVerticesPtr(), verticesSize);
936
+ const normals = options.skipNormals || normalsSize === 0 ? new Float32Array(0) : sliceF32(oc.HEAPF32, raw.getNormalsPtr(), normalsSize);
937
+ const trianglesPtr = raw.getTrianglesPtr() / 4;
938
+ const triangles = oc.HEAPU32.slice(trianglesPtr, trianglesPtr + trianglesSize);
939
+ const uvs = uvsSize > 0 ? sliceF32(oc.HEAPF32, raw.getUvsPtr(), uvsSize) : new Float32Array(0);
940
+ const faceGroups = [];
941
+ if (faceGroupsSize > 0) {
942
+ const fgPtr = raw.getFaceGroupsPtr() / 4;
943
+ const fgRaw = oc.HEAP32.slice(fgPtr, fgPtr + faceGroupsSize);
944
+ for (let i = 0; i < fgRaw.length; i += 3) faceGroups.push({
945
+ start: fgRaw[i],
946
+ count: fgRaw[i + 1],
947
+ faceHash: fgRaw[i + 2]
948
+ });
949
+ }
950
+ raw.delete();
951
+ return {
952
+ vertices,
953
+ normals,
954
+ triangles,
955
+ uvs,
956
+ faceGroups
957
+ };
958
+ } finally {
959
+ end();
783
960
  }
784
- raw.delete();
785
- return {
786
- vertices,
787
- normals,
788
- triangles,
789
- uvs,
790
- faceGroups
791
- };
792
961
  }
793
962
  /**
794
963
  * Extracts edge meshes using C++ bulk extraction.
795
964
  */
796
965
  function meshEdges$1(oc, shape, tolerance, angularTolerance) {
797
- const raw = oc.EdgeMeshExtractor.extract(shape, tolerance, angularTolerance);
798
- const linesSize = raw.getLinesSize();
799
- const edgeGroupsSize = raw.getEdgeGroupsSize();
800
- const lines = sliceF32(oc.HEAPF32, raw.getLinesPtr(), linesSize);
801
- const edgeGroups = [];
802
- if (edgeGroupsSize > 0) {
803
- const egPtr = raw.getEdgeGroupsPtr() / 4;
804
- const egRaw = oc.HEAP32.slice(egPtr, egPtr + edgeGroupsSize);
805
- for (let i = 0; i < egRaw.length; i += 3) edgeGroups.push({
806
- start: egRaw[i],
807
- count: egRaw[i + 1],
808
- edgeHash: egRaw[i + 2]
809
- });
966
+ const end = perfTimer("edgeMesh");
967
+ try {
968
+ const raw = oc.EdgeMeshExtractor.extract(shape, tolerance, angularTolerance);
969
+ const linesSize = raw.getLinesSize();
970
+ const edgeGroupsSize = raw.getEdgeGroupsSize();
971
+ const lines = sliceF32(oc.HEAPF32, raw.getLinesPtr(), linesSize);
972
+ const edgeGroups = [];
973
+ if (edgeGroupsSize > 0) {
974
+ const egPtr = raw.getEdgeGroupsPtr() / 4;
975
+ const egRaw = oc.HEAP32.slice(egPtr, egPtr + edgeGroupsSize);
976
+ for (let i = 0; i < egRaw.length; i += 3) edgeGroups.push({
977
+ start: egRaw[i],
978
+ count: egRaw[i + 1],
979
+ edgeHash: egRaw[i + 2]
980
+ });
981
+ }
982
+ raw.delete();
983
+ return {
984
+ lines,
985
+ edgeGroups
986
+ };
987
+ } finally {
988
+ end();
810
989
  }
811
- raw.delete();
812
- return {
813
- lines,
814
- edgeGroups
815
- };
816
990
  }
817
991
  //#endregion
818
992
  //#region src/kernel/occt/topologyOps.ts
@@ -1179,12 +1353,17 @@ function makeTorus$1(oc, majorRadius, minorRadius, center = [
1179
1353
  * Extrudes a face along a direction.
1180
1354
  */
1181
1355
  function extrude$1(oc, face, direction, length) {
1182
- const vec = new oc.gp_Vec_4(direction[0] * length, direction[1] * length, direction[2] * length);
1183
- const maker = new oc.BRepPrimAPI_MakePrism_1(face, vec, false, true);
1184
- const result = maker.Shape();
1185
- maker.delete();
1186
- vec.delete();
1187
- return result;
1356
+ const end = perfTimer("extrude");
1357
+ try {
1358
+ const vec = new oc.gp_Vec_4(direction[0] * length, direction[1] * length, direction[2] * length);
1359
+ const maker = new oc.BRepPrimAPI_MakePrism_1(face, vec, false, true);
1360
+ const result = maker.Shape();
1361
+ maker.delete();
1362
+ vec.delete();
1363
+ return result;
1364
+ } finally {
1365
+ end();
1366
+ }
1188
1367
  }
1189
1368
  /**
1190
1369
  * Revolves a shape around an axis.
@@ -1199,16 +1378,21 @@ function revolve$1(oc, shape, axis, angle) {
1199
1378
  * Creates a loft through multiple wires.
1200
1379
  */
1201
1380
  function loft$1(oc, wires, ruled = false, startShape, endShape) {
1202
- const loftBuilder = new oc.BRepOffsetAPI_ThruSections(true, ruled, 1e-6);
1203
- if (startShape) loftBuilder.AddVertex(startShape);
1204
- for (const wire of wires) loftBuilder.AddWire(wire);
1205
- if (endShape) loftBuilder.AddVertex(endShape);
1206
- const progress = new oc.Message_ProgressRange_1();
1207
- loftBuilder.Build(progress);
1208
- const result = loftBuilder.Shape();
1209
- loftBuilder.delete();
1210
- progress.delete();
1211
- return result;
1381
+ const end = perfTimer("loft");
1382
+ try {
1383
+ const loftBuilder = new oc.BRepOffsetAPI_ThruSections(true, ruled, 1e-6);
1384
+ if (startShape) loftBuilder.AddVertex(startShape);
1385
+ for (const wire of wires) loftBuilder.AddWire(wire);
1386
+ if (endShape) loftBuilder.AddVertex(endShape);
1387
+ const progress = new oc.Message_ProgressRange_1();
1388
+ loftBuilder.Build(progress);
1389
+ const result = loftBuilder.Shape();
1390
+ loftBuilder.delete();
1391
+ progress.delete();
1392
+ return result;
1393
+ } finally {
1394
+ end();
1395
+ }
1212
1396
  }
1213
1397
  /**
1214
1398
  * Sweeps a wire along a spine.
@@ -1251,6 +1435,73 @@ function simplePipe$1(oc, profile, spine) {
1251
1435
  maker.delete();
1252
1436
  return result;
1253
1437
  }
1438
+ var hasCppLoftBatch;
1439
+ var hasCppExtrudeBatch;
1440
+ function resetLoftBatchDetectionCache() {
1441
+ hasCppLoftBatch = void 0;
1442
+ }
1443
+ function resetExtrudeBatchDetectionCache() {
1444
+ hasCppExtrudeBatch = void 0;
1445
+ }
1446
+ function detectCppLoftBatch(oc) {
1447
+ hasCppLoftBatch ??= typeof oc.LoftBatch === "function";
1448
+ return hasCppLoftBatch;
1449
+ }
1450
+ function detectCppExtrudeBatch(oc) {
1451
+ hasCppExtrudeBatch ??= typeof oc.ExtrudeBatch === "function";
1452
+ return hasCppExtrudeBatch;
1453
+ }
1454
+ function loftBatch(oc, entries) {
1455
+ if (entries.length === 0) return [];
1456
+ /* v8 ignore start -- C++ extractor not available in test WASM build */
1457
+ if (detectCppLoftBatch(oc)) {
1458
+ const end = perfTimer("loft");
1459
+ const batch = new oc.LoftBatch();
1460
+ try {
1461
+ for (const e of entries) {
1462
+ const idx = batch.beginLoft(e.solid ?? true, e.ruled ?? false, e.tolerance ?? 1e-6);
1463
+ if (e.startVertex) batch.setStartVertex(idx, e.startVertex);
1464
+ for (const wire of e.wires) batch.addWire(idx, wire);
1465
+ if (e.endVertex) batch.setEndVertex(idx, e.endVertex);
1466
+ }
1467
+ const result = batch.execute();
1468
+ try {
1469
+ const count = result.getShapesCount();
1470
+ return Array.from({ length: count }, (_, i) => result.getShape(i));
1471
+ } finally {
1472
+ result.delete();
1473
+ }
1474
+ } finally {
1475
+ batch.delete();
1476
+ end();
1477
+ }
1478
+ }
1479
+ /* v8 ignore stop */
1480
+ return entries.map((e) => loft$1(oc, e.wires, e.ruled ?? false, e.startVertex, e.endVertex));
1481
+ }
1482
+ function extrudeBatch(oc, entries) {
1483
+ if (entries.length === 0) return [];
1484
+ /* v8 ignore start -- C++ extractor not available in test WASM build */
1485
+ if (detectCppExtrudeBatch(oc)) {
1486
+ const end = perfTimer("extrude");
1487
+ const batch = new oc.ExtrudeBatch();
1488
+ try {
1489
+ for (const e of entries) batch.addExtrude(e.face, e.direction[0] * e.length, e.direction[1] * e.length, e.direction[2] * e.length);
1490
+ const result = batch.execute();
1491
+ try {
1492
+ const count = result.getShapesCount();
1493
+ return Array.from({ length: count }, (_, i) => result.getShape(i));
1494
+ } finally {
1495
+ result.delete();
1496
+ }
1497
+ } finally {
1498
+ batch.delete();
1499
+ end();
1500
+ }
1501
+ }
1502
+ /* v8 ignore stop */
1503
+ return entries.map((e) => extrude$1(oc, e.face, e.direction, e.length));
1504
+ }
1254
1505
  //#endregion
1255
1506
  //#region src/kernel/occt/healingOps.ts
1256
1507
  /**
@@ -1302,20 +1553,25 @@ function healWire$1(oc, wire, face) {
1302
1553
  * Supports constant radius, variable radius [r1, r2], and per-edge callbacks.
1303
1554
  */
1304
1555
  function fillet$1(oc, shape, edges, radius) {
1305
- const builder = new oc.BRepFilletAPI_MakeFillet(shape, oc.ChFi3d_FilletShape.ChFi3d_Rational);
1306
- for (const edge of edges) {
1307
- const r = typeof radius === "function" ? radius(edge) : radius;
1308
- const downcast = oc.TopoDS.Edge_1(edge);
1309
- if (typeof r === "number") {
1310
- if (r > 0) builder.Add_2(r, downcast);
1311
- } else {
1312
- const [r1, r2] = r;
1313
- if (r1 > 0 && r2 > 0) builder.Add_3(r1, r2, downcast);
1556
+ const end = perfTimer("fillet");
1557
+ try {
1558
+ const builder = new oc.BRepFilletAPI_MakeFillet(shape, oc.ChFi3d_FilletShape.ChFi3d_Rational);
1559
+ for (const edge of edges) {
1560
+ const r = typeof radius === "function" ? radius(edge) : radius;
1561
+ const downcast = oc.TopoDS.Edge_1(edge);
1562
+ if (typeof r === "number") {
1563
+ if (r > 0) builder.Add_2(r, downcast);
1564
+ } else {
1565
+ const [r1, r2] = r;
1566
+ if (r1 > 0 && r2 > 0) builder.Add_3(r1, r2, downcast);
1567
+ }
1314
1568
  }
1569
+ const result = builder.Shape();
1570
+ builder.delete();
1571
+ return result;
1572
+ } finally {
1573
+ end();
1315
1574
  }
1316
- const result = builder.Shape();
1317
- builder.delete();
1318
- return result;
1319
1575
  }
1320
1576
  /**
1321
1577
  * Applies a chamfer (beveled edge) to selected edges of a shape.
@@ -1366,16 +1622,21 @@ function chamfer$1(oc, shape, edges, distance) {
1366
1622
  * Creates a shell (hollow shape) by removing faces and offsetting the remaining walls.
1367
1623
  */
1368
1624
  function shell$1(oc, shape, faces, thickness, tolerance = .001) {
1369
- const facesToRemove = new oc.TopTools_ListOfShape_1();
1370
- for (const face of faces) facesToRemove.Append_1(face);
1371
- const progress = new oc.Message_ProgressRange_1();
1372
- const builder = new oc.BRepOffsetAPI_MakeThickSolid();
1373
- builder.MakeThickSolidByJoin(shape, facesToRemove, -thickness, tolerance, oc.BRepOffset_Mode.BRepOffset_Skin, false, false, oc.GeomAbs_JoinType.GeomAbs_Arc, false, progress);
1374
- const result = builder.Shape();
1375
- builder.delete();
1376
- facesToRemove.delete();
1377
- progress.delete();
1378
- return result;
1625
+ const end = perfTimer("shell");
1626
+ try {
1627
+ const facesToRemove = new oc.TopTools_ListOfShape_1();
1628
+ for (const face of faces) facesToRemove.Append_1(face);
1629
+ const progress = new oc.Message_ProgressRange_1();
1630
+ const builder = new oc.BRepOffsetAPI_MakeThickSolid();
1631
+ builder.MakeThickSolidByJoin(shape, facesToRemove, -thickness, tolerance, oc.BRepOffset_Mode.BRepOffset_Skin, false, false, oc.GeomAbs_JoinType.GeomAbs_Arc, false, progress);
1632
+ const result = builder.Shape();
1633
+ builder.delete();
1634
+ facesToRemove.delete();
1635
+ progress.delete();
1636
+ return result;
1637
+ } finally {
1638
+ end();
1639
+ }
1379
1640
  }
1380
1641
  /**
1381
1642
  * Thickens a surface (face/shell) into a solid by offsetting it.
@@ -1452,6 +1713,83 @@ function offset$1(oc, shape, distance, tolerance = 1e-6) {
1452
1713
  progress.delete();
1453
1714
  return result;
1454
1715
  }
1716
+ var hasCppShellBatch;
1717
+ var hasCppFilletBatch;
1718
+ function resetShellBatchDetectionCache() {
1719
+ hasCppShellBatch = void 0;
1720
+ }
1721
+ function resetFilletBatchDetectionCache() {
1722
+ hasCppFilletBatch = void 0;
1723
+ }
1724
+ function detectCppShellBatch(oc) {
1725
+ hasCppShellBatch ??= typeof oc.ShellBatch === "function";
1726
+ return hasCppShellBatch;
1727
+ }
1728
+ function detectCppFilletBatch(oc) {
1729
+ hasCppFilletBatch ??= typeof oc.FilletBatch === "function";
1730
+ return hasCppFilletBatch;
1731
+ }
1732
+ function shellBatch(oc, entries) {
1733
+ if (entries.length === 0) return [];
1734
+ /* v8 ignore start -- C++ extractor not available in test WASM build */
1735
+ if (detectCppShellBatch(oc)) {
1736
+ const end = perfTimer("shell");
1737
+ const batch = new oc.ShellBatch();
1738
+ try {
1739
+ for (const e of entries) {
1740
+ const idx = batch.beginShell(e.shape, e.thickness, e.tolerance ?? .001);
1741
+ for (const face of e.faces) batch.addFaceToRemove(idx, face);
1742
+ }
1743
+ const result = batch.execute();
1744
+ try {
1745
+ const count = result.getShapesCount();
1746
+ return Array.from({ length: count }, (_, i) => result.getShape(i));
1747
+ } finally {
1748
+ result.delete();
1749
+ }
1750
+ } finally {
1751
+ batch.delete();
1752
+ end();
1753
+ }
1754
+ }
1755
+ /* v8 ignore stop */
1756
+ return entries.map((e) => shell$1(oc, e.shape, e.faces, e.thickness, e.tolerance ?? .001));
1757
+ }
1758
+ function filletBatch(oc, entries) {
1759
+ if (entries.length === 0) return [];
1760
+ /* v8 ignore start -- C++ extractor not available in test WASM build */
1761
+ if (detectCppFilletBatch(oc)) {
1762
+ const end = perfTimer("fillet");
1763
+ const batch = new oc.FilletBatch();
1764
+ try {
1765
+ for (const e of entries) {
1766
+ const idx = batch.beginFillet(e.shape);
1767
+ for (const ei of e.edges) if (ei.r2 !== void 0) batch.addEdgeVariable(idx, ei.edge, ei.radius, ei.r2);
1768
+ else batch.addEdge(idx, ei.edge, ei.radius);
1769
+ }
1770
+ const result = batch.execute();
1771
+ try {
1772
+ const count = result.getShapesCount();
1773
+ return Array.from({ length: count }, (_, i) => result.getShape(i));
1774
+ } finally {
1775
+ result.delete();
1776
+ }
1777
+ } finally {
1778
+ batch.delete();
1779
+ end();
1780
+ }
1781
+ }
1782
+ /* v8 ignore stop */
1783
+ return entries.map((e) => {
1784
+ const edges = e.edges.map((ei) => ei.edge);
1785
+ const radiusFn = (edge) => {
1786
+ const match = e.edges.find((ei) => ei.edge === edge);
1787
+ if (!match) return 0;
1788
+ return match.r2 !== void 0 ? [match.radius, match.r2] : match.radius;
1789
+ };
1790
+ return fillet$1(oc, e.shape, edges, radiusFn);
1791
+ });
1792
+ }
1455
1793
  //#endregion
1456
1794
  //#region src/kernel/occt/curveOps.ts
1457
1795
  /**
@@ -4545,11 +4883,11 @@ function draftWithHistory$1(oc, shape, faces, pullDirection, neutralPlane, angle
4545
4883
  const dir = new oc.gp_Dir_4(px, py, pz);
4546
4884
  const origin = new oc.gp_Pnt_3(ox, oy, oz);
4547
4885
  const pln = new oc.gp_Pln_3(origin, dir);
4548
- const builder = new oc.BRepOffsetAPI_DraftAngle(shape);
4886
+ const builder = new oc.BRepOffsetAPI_DraftAngle_2(shape);
4549
4887
  try {
4550
4888
  for (const face of faces) {
4551
4889
  const angleRad = (typeof angleDeg === "function" ? angleDeg(face) : angleDeg) * Math.PI / 180;
4552
- builder.Add(oc.TopoDS.Face_1(face), dir, angleRad, pln);
4890
+ builder.Add(oc.TopoDS.Face_1(face), dir, angleRad, pln, true);
4553
4891
  }
4554
4892
  const progress = new oc.Message_ProgressRange_1();
4555
4893
  builder.Build(progress);
@@ -4734,6 +5072,12 @@ var DefaultAdapter = class {
4734
5072
  simplePipe(profile, spine) {
4735
5073
  return simplePipe$1(this.oc, profile, spine);
4736
5074
  }
5075
+ loftBatch(entries) {
5076
+ return loftBatch(this.oc, entries);
5077
+ }
5078
+ extrudeBatch(entries) {
5079
+ return extrudeBatch(this.oc, entries);
5080
+ }
4737
5081
  fillet(shape, edges, radius) {
4738
5082
  return fillet$1(this.oc, shape, edges, radius);
4739
5083
  }
@@ -4752,6 +5096,12 @@ var DefaultAdapter = class {
4752
5096
  offset(shape, distance, tolerance = 1e-6) {
4753
5097
  return offset$1(this.oc, shape, distance, tolerance);
4754
5098
  }
5099
+ shellBatch(entries) {
5100
+ return shellBatch(this.oc, entries);
5101
+ }
5102
+ filletBatch(entries) {
5103
+ return filletBatch(this.oc, entries);
5104
+ }
4755
5105
  transform(shape, trsf) {
4756
5106
  return transform$1(this.oc, shape, trsf);
4757
5107
  }
@@ -10876,6 +11226,11 @@ function withKernel(id, fn) {
10876
11226
  function initFromOC(oc) {
10877
11227
  resetMeasureDetectionCache();
10878
11228
  resetTransformDetectionCache();
11229
+ resetBooleanBatchDetectionCache();
11230
+ resetLoftBatchDetectionCache();
11231
+ resetExtrudeBatchDetectionCache();
11232
+ resetShellBatchDetectionCache();
11233
+ resetFilletBatchDetectionCache();
10879
11234
  const adapter = new DefaultAdapter(oc);
10880
11235
  registerKernel("occt", adapter);
10881
11236
  _defaultKernelId = "occt";
@@ -11621,6 +11976,12 @@ Object.defineProperty(exports, "getKernel2D", {
11621
11976
  return getKernel2D;
11622
11977
  }
11623
11978
  });
11979
+ Object.defineProperty(exports, "getPerformanceStats", {
11980
+ enumerable: true,
11981
+ get: function() {
11982
+ return getPerformanceStats;
11983
+ }
11984
+ });
11624
11985
  Object.defineProperty(exports, "getShapeKind", {
11625
11986
  enumerable: true,
11626
11987
  get: function() {
@@ -11795,6 +12156,12 @@ Object.defineProperty(exports, "resetDisposalStats", {
11795
12156
  return resetDisposalStats;
11796
12157
  }
11797
12158
  });
12159
+ Object.defineProperty(exports, "resetPerformanceStats", {
12160
+ enumerable: true,
12161
+ get: function() {
12162
+ return resetPerformanceStats;
12163
+ }
12164
+ });
11798
12165
  Object.defineProperty(exports, "supportsConstraintSketch", {
11799
12166
  enumerable: true,
11800
12167
  get: function() {