brepjs 2.0.0 → 2.0.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.
package/dist/brepjs.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : Symbol.for("Symbol." + name);
2
+ var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
3
3
  var __typeError = (msg) => {
4
4
  throw TypeError(msg);
5
5
  };
@@ -46,6 +46,13 @@ var __callDispose = (stack, error, hasError) => {
46
46
  };
47
47
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
48
48
  const opentype = require("opentype.js");
49
+ let _counter = 0;
50
+ function uniqueIOFilename(prefix, ext) {
51
+ return `${prefix}_${++_counter}.${ext}`;
52
+ }
53
+ function uniqueId() {
54
+ return `_io_${++_counter}`;
55
+ }
49
56
  const HASH_CODE_MAX$1 = 2147483647;
50
57
  class OCCTAdapter {
51
58
  oc;
@@ -78,9 +85,10 @@ class OCCTAdapter {
78
85
  return result;
79
86
  }
80
87
  intersect(shape, tool, options = {}) {
81
- const { simplify = false } = options;
88
+ const { optimisation, simplify = false } = options;
82
89
  const progress = new this.oc.Message_ProgressRange_1();
83
90
  const commonOp = new this.oc.BRepAlgoAPI_Common_3(shape, tool, progress);
91
+ this._applyGlue(commonOp, optimisation);
84
92
  commonOp.Build(progress);
85
93
  if (simplify) commonOp.SimplifyResult(true, true, 1e-3);
86
94
  const result = commonOp.Shape();
@@ -154,7 +162,9 @@ class OCCTAdapter {
154
162
  return this._cutAllBatch(shape, tools, options);
155
163
  }
156
164
  const toolCompound = this._buildCompound(tools);
157
- return this.cut(shape, toolCompound, options);
165
+ const result = this.cut(shape, toolCompound, options);
166
+ toolCompound.delete();
167
+ return result;
158
168
  }
159
169
  _cutAllBatch(shape, tools, options = {}) {
160
170
  const { optimisation, simplify = false } = options;
@@ -568,37 +578,157 @@ class OCCTAdapter {
568
578
  explorer.delete();
569
579
  return { vertices, normals, triangles, faceGroups };
570
580
  }
571
- meshEdges(shape, tolerance = 1e-3) {
572
- const edgeLines = [];
573
- const explorer = new this.oc.TopExp_Explorer_2(
581
+ meshEdges(shape, tolerance, angularTolerance) {
582
+ if (this.oc.EdgeMeshExtractor) {
583
+ return this._meshEdgesBulk(shape, tolerance, angularTolerance);
584
+ }
585
+ return this._meshEdgesJS(shape, tolerance, angularTolerance);
586
+ }
587
+ _meshEdgesBulk(shape, tolerance, angularTolerance) {
588
+ const raw = this.oc.EdgeMeshExtractor.extract(shape, tolerance, angularTolerance);
589
+ const linesSize = raw.getLinesSize();
590
+ const edgeGroupsSize = raw.getEdgeGroupsSize();
591
+ let lines;
592
+ if (linesSize > 0) {
593
+ const linesPtr = raw.getLinesPtr() / 4;
594
+ lines = this.oc.HEAPF32.slice(linesPtr, linesPtr + linesSize);
595
+ } else {
596
+ lines = new Float32Array(0);
597
+ }
598
+ const edgeGroups = [];
599
+ if (edgeGroupsSize > 0) {
600
+ const egPtr = raw.getEdgeGroupsPtr() / 4;
601
+ const egRaw = this.oc.HEAP32.slice(egPtr, egPtr + edgeGroupsSize);
602
+ for (let i = 0; i < egRaw.length; i += 3) {
603
+ edgeGroups.push({
604
+ start: egRaw[i],
605
+ count: egRaw[i + 1],
606
+ edgeHash: egRaw[i + 2]
607
+ });
608
+ }
609
+ }
610
+ raw.delete();
611
+ return { lines, edgeGroups };
612
+ }
613
+ _meshEdgesJS(shape, tolerance, angularTolerance) {
614
+ const mesher = new this.oc.BRepMesh_IncrementalMesh_2(
615
+ shape,
616
+ tolerance,
617
+ false,
618
+ angularTolerance,
619
+ false
620
+ );
621
+ mesher.delete();
622
+ const lines = [];
623
+ const edgeGroups = [];
624
+ const seenHashes = /* @__PURE__ */ new Set();
625
+ const faceExplorer = new this.oc.TopExp_Explorer_2(
626
+ shape,
627
+ this.oc.TopAbs_ShapeEnum.TopAbs_FACE,
628
+ this.oc.TopAbs_ShapeEnum.TopAbs_SHAPE
629
+ );
630
+ while (faceExplorer.More()) {
631
+ const face = this.oc.TopoDS.Face_1(faceExplorer.Current());
632
+ const faceLoc = new this.oc.TopLoc_Location_1();
633
+ const tri = this.oc.BRep_Tool.Triangulation(face, faceLoc, 0);
634
+ if (!tri.IsNull()) {
635
+ const triObj = tri.get();
636
+ const edgeExplorer2 = new this.oc.TopExp_Explorer_2(
637
+ face,
638
+ this.oc.TopAbs_ShapeEnum.TopAbs_EDGE,
639
+ this.oc.TopAbs_ShapeEnum.TopAbs_SHAPE
640
+ );
641
+ while (edgeExplorer2.More()) {
642
+ const edgeShape = edgeExplorer2.Current();
643
+ const edge = this.oc.TopoDS.Edge_1(edgeShape);
644
+ const edgeHash = edge.HashCode(HASH_CODE_MAX$1);
645
+ if (!seenHashes.has(edgeHash)) {
646
+ seenHashes.add(edgeHash);
647
+ const edgeLoc = new this.oc.TopLoc_Location_1();
648
+ const polygon = this.oc.BRep_Tool.PolygonOnTriangulation_1(edge, tri, edgeLoc);
649
+ const edgeNodes = polygon && !polygon.IsNull() ? polygon.get().Nodes() : null;
650
+ if (edgeNodes) {
651
+ const lineStart = lines.length / 3;
652
+ let prevX = 0, prevY = 0, prevZ = 0;
653
+ let hasPrev = false;
654
+ for (let i = edgeNodes.Lower(); i <= edgeNodes.Upper(); i++) {
655
+ const p = triObj.Node(edgeNodes.Value(i)).Transformed(edgeLoc.Transformation());
656
+ const x = p.X(), y = p.Y(), z = p.Z();
657
+ if (hasPrev) {
658
+ lines.push(prevX, prevY, prevZ, x, y, z);
659
+ }
660
+ prevX = x;
661
+ prevY = y;
662
+ prevZ = z;
663
+ hasPrev = true;
664
+ p.delete();
665
+ }
666
+ edgeGroups.push({
667
+ start: lineStart,
668
+ count: lines.length / 3 - lineStart,
669
+ edgeHash
670
+ });
671
+ edgeNodes.delete();
672
+ }
673
+ if (polygon && !polygon.IsNull()) polygon.delete();
674
+ edgeLoc.delete();
675
+ }
676
+ edgeExplorer2.Next();
677
+ }
678
+ edgeExplorer2.delete();
679
+ }
680
+ tri.delete();
681
+ faceLoc.delete();
682
+ faceExplorer.Next();
683
+ }
684
+ faceExplorer.delete();
685
+ const edgeExplorer = new this.oc.TopExp_Explorer_2(
574
686
  shape,
575
687
  this.oc.TopAbs_ShapeEnum.TopAbs_EDGE,
576
688
  this.oc.TopAbs_ShapeEnum.TopAbs_SHAPE
577
689
  );
578
- while (explorer.More()) {
579
- const edge = explorer.Current();
580
- const adaptor = new this.oc.BRepAdaptor_Curve_2(edge);
581
- const tangDef = new this.oc.GCPnts_TangentialDeflection_2(
582
- adaptor,
583
- tolerance,
584
- 0.1,
585
- 2,
586
- 1e-9,
587
- 1e-7
588
- );
589
- const points = [];
590
- for (let j = 1; j <= tangDef.NbPoints(); j++) {
591
- const p = tangDef.Value(j);
592
- points.push(p.X(), p.Y(), p.Z());
593
- p.delete();
690
+ while (edgeExplorer.More()) {
691
+ const edgeShape = edgeExplorer.Current();
692
+ const edge = this.oc.TopoDS.Edge_1(edgeShape);
693
+ const edgeHash = edge.HashCode(HASH_CODE_MAX$1);
694
+ if (!seenHashes.has(edgeHash)) {
695
+ seenHashes.add(edgeHash);
696
+ const adaptor = new this.oc.BRepAdaptor_Curve_2(edge);
697
+ const tangDef = new this.oc.GCPnts_TangentialDeflection_2(
698
+ adaptor,
699
+ tolerance,
700
+ angularTolerance,
701
+ 2,
702
+ 1e-9,
703
+ 1e-7
704
+ );
705
+ const lineStart = lines.length / 3;
706
+ let prevX = 0, prevY = 0, prevZ = 0;
707
+ let hasPrev = false;
708
+ for (let j = 1; j <= tangDef.NbPoints(); j++) {
709
+ const p = tangDef.Value(j);
710
+ const x = p.X(), y = p.Y(), z = p.Z();
711
+ if (hasPrev) {
712
+ lines.push(prevX, prevY, prevZ, x, y, z);
713
+ }
714
+ prevX = x;
715
+ prevY = y;
716
+ prevZ = z;
717
+ hasPrev = true;
718
+ p.delete();
719
+ }
720
+ edgeGroups.push({
721
+ start: lineStart,
722
+ count: lines.length / 3 - lineStart,
723
+ edgeHash
724
+ });
725
+ tangDef.delete();
726
+ adaptor.delete();
594
727
  }
595
- edgeLines.push(new Float32Array(points));
596
- tangDef.delete();
597
- adaptor.delete();
598
- explorer.Next();
728
+ edgeExplorer.Next();
599
729
  }
600
- explorer.delete();
601
- return edgeLines;
730
+ edgeExplorer.delete();
731
+ return { lines: new Float32Array(lines), edgeGroups };
602
732
  }
603
733
  // --- File I/O ---
604
734
  exportSTEP(shapes) {
@@ -609,7 +739,7 @@ class OCCTAdapter {
609
739
  for (const shape of shapes) {
610
740
  writer.Transfer(shape, this.oc.STEPControl_StepModelType.STEPControl_AsIs, true, progress);
611
741
  }
612
- const filename = "_export.step";
742
+ const filename = uniqueIOFilename("_export", "step");
613
743
  const done = writer.Write(filename);
614
744
  writer.delete();
615
745
  progress.delete();
@@ -621,7 +751,7 @@ class OCCTAdapter {
621
751
  throw new Error("STEP export failed: writer did not complete successfully");
622
752
  }
623
753
  exportSTL(shape, binary = false) {
624
- const filename = "_export.stl";
754
+ const filename = uniqueIOFilename("_export", "stl");
625
755
  const done = this.oc.StlAPI.Write(shape, filename, !binary);
626
756
  if (done) {
627
757
  const file = this.oc.FS.readFile("/" + filename);
@@ -632,7 +762,7 @@ class OCCTAdapter {
632
762
  throw new Error("STL export failed: StlAPI.Write returned false");
633
763
  }
634
764
  importSTEP(data) {
635
- const filename = "_import.step";
765
+ const filename = uniqueIOFilename("_import", "step");
636
766
  const buffer = typeof data === "string" ? new TextEncoder().encode(data) : new Uint8Array(data);
637
767
  this.oc.FS.writeFile("/" + filename, buffer);
638
768
  const reader = new this.oc.STEPControl_Reader_1();
@@ -650,7 +780,7 @@ class OCCTAdapter {
650
780
  throw new Error("Failed to import STEP file: reader could not parse the input data");
651
781
  }
652
782
  importSTL(data) {
653
- const filename = "_import.stl";
783
+ const filename = uniqueIOFilename("_import", "stl");
654
784
  const buffer = typeof data === "string" ? new TextEncoder().encode(data) : new Uint8Array(data);
655
785
  this.oc.FS.writeFile("/" + filename, buffer);
656
786
  const reader = new this.oc.StlAPI_Reader();
@@ -663,12 +793,14 @@ class OCCTAdapter {
663
793
  const solidBuilder = new this.oc.BRepBuilderAPI_MakeSolid_1();
664
794
  solidBuilder.Add(this.oc.TopoDS.Shell_1(upgraded));
665
795
  const solid = solidBuilder.Solid();
796
+ readShape.delete();
666
797
  upgrader.delete();
667
798
  solidBuilder.delete();
668
799
  reader.delete();
669
800
  return solid;
670
801
  }
671
802
  this.oc.FS.unlink("/" + filename);
803
+ readShape.delete();
672
804
  reader.delete();
673
805
  throw new Error("Failed to import STL file: reader could not parse the input data");
674
806
  }
@@ -721,6 +853,32 @@ class OCCTAdapter {
721
853
  }
722
854
  // --- Topology iteration ---
723
855
  iterShapes(shape, type) {
856
+ if (this.oc.TopologyExtractor) {
857
+ return this._iterShapesBulk(shape, type);
858
+ }
859
+ return this._iterShapesJS(shape, type);
860
+ }
861
+ _iterShapesBulk(shape, type) {
862
+ const typeEnumMap = {
863
+ vertex: 7,
864
+ edge: 6,
865
+ wire: 5,
866
+ face: 4,
867
+ shell: 3,
868
+ solid: 2,
869
+ compsolid: 1,
870
+ compound: 0
871
+ };
872
+ const raw = this.oc.TopologyExtractor.extract(shape, typeEnumMap[type]);
873
+ const count = raw.getShapesCount();
874
+ const result = [];
875
+ for (let i = 0; i < count; i++) {
876
+ result.push(raw.getShape(i));
877
+ }
878
+ raw.delete();
879
+ return result;
880
+ }
881
+ _iterShapesJS(shape, type) {
724
882
  const typeMap = {
725
883
  vertex: this.oc.TopAbs_ShapeEnum.TopAbs_VERTEX,
726
884
  edge: this.oc.TopAbs_ShapeEnum.TopAbs_EDGE,
@@ -1064,14 +1222,6 @@ function localGC(debug) {
1064
1222
  debug ? cleaner : void 0
1065
1223
  ];
1066
1224
  }
1067
- if (!globalThis.FinalizationRegistry) {
1068
- console.warn("brepjs: FinalizationRegistry unavailable — garbage collection will not work");
1069
- globalThis.FinalizationRegistry = (() => ({
1070
- register: () => null,
1071
- unregister: () => null
1072
- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- polyfill shim
1073
- }));
1074
- }
1075
1225
  const deletableRegistry = new globalThis.FinalizationRegistry((heldValue) => {
1076
1226
  try {
1077
1227
  heldValue.delete();
@@ -1155,7 +1305,7 @@ function vecDistance(a, b) {
1155
1305
  }
1156
1306
  function vecNormalize(v) {
1157
1307
  const len = vecLength(v);
1158
- if (len === 0) return [0, 0, 0];
1308
+ if (len < 1e-10) return [0, 0, 0];
1159
1309
  return [v[0] / len, v[1] / len, v[2] / len];
1160
1310
  }
1161
1311
  function vecEquals(a, b, tolerance = 1e-5) {
@@ -1284,42 +1434,46 @@ const makeAx3 = (center, dir, xDir) => {
1284
1434
  const oc = getKernel().oc;
1285
1435
  const origin = asPnt(center);
1286
1436
  const direction = asDir(dir);
1287
- let axis;
1288
- if (xDir) {
1289
- const xDirection = asDir(xDir);
1290
- axis = new oc.gp_Ax3_3(origin, direction, xDirection);
1291
- xDirection.delete();
1292
- } else {
1293
- axis = new oc.gp_Ax3_4(origin, direction);
1437
+ const xDirection = xDir ? asDir(xDir) : null;
1438
+ try {
1439
+ if (xDirection) {
1440
+ return new oc.gp_Ax3_3(origin, direction, xDirection);
1441
+ } else {
1442
+ return new oc.gp_Ax3_4(origin, direction);
1443
+ }
1444
+ } finally {
1445
+ origin.delete();
1446
+ direction.delete();
1447
+ if (xDirection) xDirection.delete();
1294
1448
  }
1295
- origin.delete();
1296
- direction.delete();
1297
- return axis;
1298
1449
  };
1299
1450
  const makeAx2 = (center, dir, xDir) => {
1300
1451
  const oc = getKernel().oc;
1301
1452
  const origin = asPnt(center);
1302
1453
  const direction = asDir(dir);
1303
- let axis;
1304
- if (xDir) {
1305
- const xDirection = asDir(xDir);
1306
- axis = new oc.gp_Ax2_2(origin, direction, xDirection);
1307
- xDirection.delete();
1308
- } else {
1309
- axis = new oc.gp_Ax2_3(origin, direction);
1454
+ const xDirection = xDir ? asDir(xDir) : null;
1455
+ try {
1456
+ if (xDirection) {
1457
+ return new oc.gp_Ax2_2(origin, direction, xDirection);
1458
+ } else {
1459
+ return new oc.gp_Ax2_3(origin, direction);
1460
+ }
1461
+ } finally {
1462
+ origin.delete();
1463
+ direction.delete();
1464
+ if (xDirection) xDirection.delete();
1310
1465
  }
1311
- origin.delete();
1312
- direction.delete();
1313
- return axis;
1314
1466
  };
1315
1467
  const makeAx1 = (center, dir) => {
1316
1468
  const oc = getKernel().oc;
1317
1469
  const origin = asPnt(center);
1318
1470
  const direction = asDir(dir);
1319
- const axis = new oc.gp_Ax1_2(origin, direction);
1320
- origin.delete();
1321
- direction.delete();
1322
- return axis;
1471
+ try {
1472
+ return new oc.gp_Ax1_2(origin, direction);
1473
+ } finally {
1474
+ origin.delete();
1475
+ direction.delete();
1476
+ }
1323
1477
  };
1324
1478
  const makeVec = (vector = [0, 0, 0]) => {
1325
1479
  const oc = getKernel().oc;
@@ -1586,24 +1740,24 @@ class Plane {
1586
1740
  const dir = makeDirection(direction);
1587
1741
  const zDir = new Vector(this.zDir).rotate(angle, [0, 0, 0], dir);
1588
1742
  const xDir = new Vector(this.xDir).rotate(angle, [0, 0, 0], dir);
1589
- const result = new Plane(this.origin, xDir, zDir);
1590
- zDir.delete();
1591
- xDir.delete();
1592
- return result;
1743
+ try {
1744
+ return new Plane(this.origin, xDir, zDir);
1745
+ } finally {
1746
+ zDir.delete();
1747
+ xDir.delete();
1748
+ }
1593
1749
  }
1594
1750
  rotate2DAxes(angle) {
1595
1751
  const xDir = new Vector(this.xDir).rotate(angle, [0, 0, 0], this.zDir);
1596
- const result = new Plane(this.origin, xDir, this.zDir);
1597
- xDir.delete();
1598
- return result;
1752
+ try {
1753
+ return new Plane(this.origin, xDir, this.zDir);
1754
+ } finally {
1755
+ xDir.delete();
1756
+ }
1599
1757
  }
1600
1758
  _calcTransforms() {
1601
1759
  if (this.globalToLocal) this.globalToLocal.delete();
1602
1760
  if (this.localToGlobal) this.localToGlobal.delete();
1603
- const _globalCoordSystem = new this.oc.gp_Ax3_1();
1604
- const _localCoordSystem = makeAx3(this.origin, this.zDir, this.xDir);
1605
- const _forwardT = new this.oc.gp_Trsf_1();
1606
- _forwardT.SetTransformation_1(_globalCoordSystem, _localCoordSystem);
1607
1761
  this.globalToLocal = new Transformation();
1608
1762
  this.globalToLocal.coordSystemChange("reference", {
1609
1763
  origin: this.origin,
@@ -1619,9 +1773,6 @@ class Plane {
1619
1773
  },
1620
1774
  "reference"
1621
1775
  );
1622
- _globalCoordSystem.delete();
1623
- _localCoordSystem.delete();
1624
- _forwardT.delete();
1625
1776
  }
1626
1777
  setOrigin2d(x, y) {
1627
1778
  this.origin = this.toWorldCoords([x, y]);
@@ -1715,19 +1866,18 @@ class BoundingBox extends WrappingObj {
1715
1866
  }
1716
1867
  }
1717
1868
  const makePlaneFromFace = (face, originOnSurface = [0, 0]) => {
1718
- const originPoint = face.pointOnSurface(...originOnSurface);
1719
- const normal = face.normalAt(originPoint);
1720
- const v = new Vector([0, 0, 1]);
1869
+ const [r, gc] = localGC();
1870
+ const originPoint = r(face.pointOnSurface(...originOnSurface));
1871
+ const normal = r(face.normalAt(originPoint));
1872
+ const v = r(new Vector([0, 0, 1]));
1721
1873
  let xd = v.cross(normal);
1722
1874
  if (xd.Length < 1e-8) {
1723
1875
  xd.delete();
1724
1876
  xd = new Vector([1, 0, 0]);
1725
1877
  }
1726
- v.delete();
1878
+ r(xd);
1727
1879
  const plane = new Plane(originPoint, xd, normal);
1728
- originPoint.delete();
1729
- normal.delete();
1730
- xd.delete();
1880
+ gc();
1731
1881
  return plane;
1732
1882
  };
1733
1883
  function makePlane(plane = "XY", origin = [0, 0, 0]) {
@@ -1738,31 +1888,35 @@ function makePlane(plane = "XY", origin = [0, 0, 0]) {
1738
1888
  }
1739
1889
  }
1740
1890
  function rotate(shape, angle, position = [0, 0, 0], direction = [0, 0, 1]) {
1741
- const transformation = new Transformation();
1891
+ const [r, gc] = localGC();
1892
+ const transformation = r(new Transformation());
1742
1893
  transformation.rotate(angle, position, direction);
1743
1894
  const newShape = transformation.transform(shape);
1744
- transformation.delete();
1895
+ gc();
1745
1896
  return newShape;
1746
1897
  }
1747
1898
  function translate(shape, vector) {
1748
- const transformation = new Transformation();
1899
+ const [r, gc] = localGC();
1900
+ const transformation = r(new Transformation());
1749
1901
  transformation.translate(vector);
1750
1902
  const newShape = transformation.transform(shape);
1751
- transformation.delete();
1903
+ gc();
1752
1904
  return newShape;
1753
1905
  }
1754
1906
  function mirror(shape, inputPlane, origin) {
1755
- const transformation = new Transformation();
1907
+ const [r, gc] = localGC();
1908
+ const transformation = r(new Transformation());
1756
1909
  transformation.mirror(inputPlane, origin);
1757
1910
  const newShape = transformation.transform(shape);
1758
- transformation.delete();
1911
+ gc();
1759
1912
  return newShape;
1760
1913
  }
1761
1914
  function scale(shape, center, scaleFactor) {
1762
- const transformation = new Transformation();
1915
+ const [r, gc] = localGC();
1916
+ const transformation = r(new Transformation());
1763
1917
  transformation.scale(center, scaleFactor);
1764
1918
  const newShape = transformation.transform(shape);
1765
- transformation.delete();
1919
+ gc();
1766
1920
  return newShape;
1767
1921
  }
1768
1922
  let CURVE_TYPES_MAP = null;
@@ -1789,6 +1943,149 @@ const findCurveType = (type) => {
1789
1943
  if (!shapeType2) return err(typeCastError("UNKNOWN_CURVE_TYPE", "Unknown curve type"));
1790
1944
  return ok(shapeType2);
1791
1945
  };
1946
+ function buildMeshCacheKey(_shapeHash, tolerance, angularTolerance, skipNormals) {
1947
+ return `${tolerance}:${angularTolerance}:${skipNormals}`;
1948
+ }
1949
+ function buildEdgeMeshCacheKey(_shapeHash, tolerance, angularTolerance) {
1950
+ return `edge:${tolerance}:${angularTolerance}`;
1951
+ }
1952
+ let meshCache = /* @__PURE__ */ new WeakMap();
1953
+ let edgeMeshCache = /* @__PURE__ */ new WeakMap();
1954
+ function getMeshForShape(shape, key) {
1955
+ const shapeCache = meshCache.get(shape);
1956
+ if (!shapeCache) return void 0;
1957
+ return shapeCache.get(key);
1958
+ }
1959
+ function setMeshForShape(shape, key, value) {
1960
+ let shapeCache = meshCache.get(shape);
1961
+ if (!shapeCache) {
1962
+ shapeCache = /* @__PURE__ */ new Map();
1963
+ meshCache.set(shape, shapeCache);
1964
+ }
1965
+ shapeCache.set(key, value);
1966
+ }
1967
+ function getEdgeMeshForShape(shape, key) {
1968
+ const shapeCache = edgeMeshCache.get(shape);
1969
+ if (!shapeCache) return void 0;
1970
+ return shapeCache.get(key);
1971
+ }
1972
+ function setEdgeMeshForShape(shape, key, value) {
1973
+ let shapeCache = edgeMeshCache.get(shape);
1974
+ if (!shapeCache) {
1975
+ shapeCache = /* @__PURE__ */ new Map();
1976
+ edgeMeshCache.set(shape, shapeCache);
1977
+ }
1978
+ shapeCache.set(key, value);
1979
+ }
1980
+ function clearMeshCache() {
1981
+ meshCache = /* @__PURE__ */ new WeakMap();
1982
+ edgeMeshCache = /* @__PURE__ */ new WeakMap();
1983
+ }
1984
+ function setMeshCacheSize(_size) {
1985
+ }
1986
+ function meshShape(shape, {
1987
+ tolerance = 1e-3,
1988
+ angularTolerance = 0.1,
1989
+ skipNormals = false,
1990
+ cache = true
1991
+ } = {}) {
1992
+ const cacheKey = buildMeshCacheKey(0, tolerance, angularTolerance, skipNormals);
1993
+ if (cache) {
1994
+ const cached = getMeshForShape(shape.wrapped, cacheKey);
1995
+ if (cached) return cached;
1996
+ }
1997
+ const result = getKernel().mesh(shape.wrapped, {
1998
+ tolerance,
1999
+ angularTolerance,
2000
+ skipNormals
2001
+ });
2002
+ const mesh = {
2003
+ vertices: result.vertices,
2004
+ normals: result.normals,
2005
+ triangles: result.triangles,
2006
+ faceGroups: result.faceGroups.map((g) => ({
2007
+ start: g.start,
2008
+ count: g.count,
2009
+ faceId: g.faceHash
2010
+ }))
2011
+ };
2012
+ if (cache) {
2013
+ setMeshForShape(shape.wrapped, cacheKey, mesh);
2014
+ }
2015
+ return mesh;
2016
+ }
2017
+ function meshShapeEdges(shape, { tolerance = 1e-3, angularTolerance = 0.1, cache = true } = {}) {
2018
+ const cacheKey = buildEdgeMeshCacheKey(0, tolerance, angularTolerance);
2019
+ if (cache) {
2020
+ const cached = getEdgeMeshForShape(shape.wrapped, cacheKey);
2021
+ if (cached) return cached;
2022
+ }
2023
+ const kernelResult = getKernel().meshEdges(shape.wrapped, tolerance, angularTolerance);
2024
+ const result = {
2025
+ lines: Array.from(kernelResult.lines),
2026
+ edgeGroups: kernelResult.edgeGroups.map((g) => ({
2027
+ start: g.start,
2028
+ count: g.count,
2029
+ edgeId: g.edgeHash
2030
+ }))
2031
+ };
2032
+ if (cache) {
2033
+ setEdgeMeshForShape(shape.wrapped, cacheKey, result);
2034
+ }
2035
+ return result;
2036
+ }
2037
+ function exportSTEP$1(shape) {
2038
+ const oc = getKernel().oc;
2039
+ const filename = uniqueIOFilename("_blob", "step");
2040
+ const writer = new oc.STEPControl_Writer_1();
2041
+ const progress = new oc.Message_ProgressRange_1();
2042
+ try {
2043
+ oc.Interface_Static.SetIVal("write.step.schema", 5);
2044
+ writer.Model(true).delete();
2045
+ writer.Transfer(shape.wrapped, oc.STEPControl_StepModelType.STEPControl_AsIs, true, progress);
2046
+ const done = writer.Write(filename);
2047
+ if (done === oc.IFSelect_ReturnStatus.IFSelect_RetDone) {
2048
+ try {
2049
+ const file = oc.FS.readFile("/" + filename);
2050
+ oc.FS.unlink("/" + filename);
2051
+ return ok(new Blob([file], { type: "application/STEP" }));
2052
+ } catch (e) {
2053
+ return err(ioError("STEP_FILE_READ_ERROR", "Failed to read exported STEP file", e));
2054
+ }
2055
+ }
2056
+ return err(ioError("STEP_EXPORT_FAILED", "Failed to write STEP file"));
2057
+ } finally {
2058
+ writer.delete();
2059
+ progress.delete();
2060
+ }
2061
+ }
2062
+ function exportSTL(shape, {
2063
+ tolerance = 1e-3,
2064
+ angularTolerance = 0.1,
2065
+ binary = false
2066
+ } = {}) {
2067
+ const oc = getKernel().oc;
2068
+ const mesher = new oc.BRepMesh_IncrementalMesh_2(
2069
+ shape.wrapped,
2070
+ tolerance,
2071
+ false,
2072
+ angularTolerance,
2073
+ false
2074
+ );
2075
+ mesher.delete();
2076
+ const filename = uniqueIOFilename("_blob", "stl");
2077
+ const done = oc.StlAPI.Write(shape.wrapped, filename, !binary);
2078
+ if (done) {
2079
+ try {
2080
+ const file = oc.FS.readFile("/" + filename);
2081
+ oc.FS.unlink("/" + filename);
2082
+ return ok(new Blob([file], { type: "application/sla" }));
2083
+ } catch (e) {
2084
+ return err(ioError("STL_FILE_READ_ERROR", "Failed to read exported STL file", e));
2085
+ }
2086
+ }
2087
+ return err(ioError("STL_EXPORT_FAILED", "Failed to write STL file"));
2088
+ }
1792
2089
  let _shapesModule = null;
1793
2090
  function getShapesModuleSync() {
1794
2091
  if (!_shapesModule) {
@@ -1817,23 +2114,23 @@ const asTopo = (entity) => {
1817
2114
  }[entity];
1818
2115
  };
1819
2116
  const iterTopo = function* iterTopo2(shape, topo) {
1820
- const oc = getKernel().oc;
1821
- const explorer = new oc.TopExp_Explorer_2(shape, asTopo(topo), asTopo("shape"));
1822
- const seen = /* @__PURE__ */ new Map();
1823
- while (explorer.More()) {
1824
- const item = explorer.Current();
1825
- const hash = item.HashCode(HASH_CODE_MAX);
1826
- const bucket = seen.get(hash);
1827
- if (!bucket) {
1828
- seen.set(hash, [item]);
1829
- yield item;
1830
- } else if (!bucket.some((s) => s.IsSame(item))) {
1831
- bucket.push(item);
1832
- yield item;
1833
- }
1834
- explorer.Next();
2117
+ const topoToShapeType = {
2118
+ vertex: "vertex",
2119
+ edge: "edge",
2120
+ wire: "wire",
2121
+ face: "face",
2122
+ shell: "shell",
2123
+ solid: "solid",
2124
+ solidCompound: "compsolid",
2125
+ compound: "compound",
2126
+ shape: "compound"
2127
+ // fallback; 'shape' isn't used in iterShapes
2128
+ };
2129
+ const shapeType2 = topoToShapeType[topo];
2130
+ if (shapeType2) {
2131
+ const shapes = getKernel().iterShapes(shape, shapeType2);
2132
+ for (const s of shapes) yield s;
1835
2133
  }
1836
- explorer.delete();
1837
2134
  };
1838
2135
  const shapeType = (shape) => {
1839
2136
  if (shape.IsNull()) return err(typeCastError("NULL_SHAPE", "This shape has no type, it is null"));
@@ -2067,7 +2364,14 @@ class Shape extends WrappingObj {
2067
2364
  return bbox;
2068
2365
  }
2069
2366
  _mesh({ tolerance = 1e-3, angularTolerance = 0.1 } = {}) {
2070
- new this.oc.BRepMesh_IncrementalMesh_2(this.wrapped, tolerance, false, angularTolerance, false);
2367
+ const mesher = new this.oc.BRepMesh_IncrementalMesh_2(
2368
+ this.wrapped,
2369
+ tolerance,
2370
+ false,
2371
+ angularTolerance,
2372
+ false
2373
+ );
2374
+ mesher.delete();
2071
2375
  }
2072
2376
  /**
2073
2377
  * Exports the current shape as a set of triangles for rendering.
@@ -2100,93 +2404,12 @@ class Shape extends WrappingObj {
2100
2404
  *
2101
2405
  * @category Shape Export
2102
2406
  */
2103
- meshEdges({ tolerance = 1e-3, angularTolerance = 0.1 } = {}) {
2104
- const r = gcWithScope();
2105
- const recordedEdges = /* @__PURE__ */ new Set();
2106
- const lines = [];
2107
- const edgeGroups = [];
2108
- const addEdge = () => {
2109
- const start = lines.length;
2110
- let prevX = 0;
2111
- let prevY = 0;
2112
- let prevZ = 0;
2113
- let hasPrev = false;
2114
- return [
2115
- (p) => {
2116
- const x = p.X();
2117
- const y = p.Y();
2118
- const z = p.Z();
2119
- if (hasPrev) {
2120
- lines.push(prevX, prevY, prevZ, x, y, z);
2121
- }
2122
- prevX = x;
2123
- prevY = y;
2124
- prevZ = z;
2125
- hasPrev = true;
2126
- },
2127
- (edgeHash) => {
2128
- edgeGroups.push({
2129
- start: start / 3,
2130
- count: (lines.length - start) / 3,
2131
- edgeId: edgeHash
2132
- });
2133
- recordedEdges.add(edgeHash);
2134
- }
2135
- ];
2136
- };
2137
- const aLocation = r(new this.oc.TopLoc_Location_1());
2138
- const faces = this.faces;
2139
- for (const face of faces) {
2140
- const triangulation = r(this.oc.BRep_Tool.Triangulation(face.wrapped, aLocation, 0));
2141
- if (triangulation.IsNull()) {
2142
- continue;
2143
- }
2144
- const tri = triangulation.get();
2145
- const faceEdges = face.edges;
2146
- for (const edge of faceEdges) {
2147
- r(edge);
2148
- if (recordedEdges.has(edge.hashCode)) continue;
2149
- const edgeLoc = r(new this.oc.TopLoc_Location_1());
2150
- const polygon = r(
2151
- this.oc.BRep_Tool.PolygonOnTriangulation_1(edge.wrapped, triangulation, edgeLoc)
2152
- );
2153
- const edgeNodes = polygon?.get()?.Nodes();
2154
- if (!edgeNodes) {
2155
- continue;
2156
- }
2157
- r(edgeNodes);
2158
- const [recordPoint, done] = addEdge();
2159
- for (let i = edgeNodes.Lower(); i <= edgeNodes.Upper(); i++) {
2160
- const p = r(r(tri.Node(edgeNodes.Value(i))).Transformed(edgeLoc.Transformation()));
2161
- recordPoint(p);
2162
- }
2163
- done(edge.hashCode);
2164
- }
2165
- }
2166
- const allEdges = this.edges;
2167
- for (const edge of allEdges) {
2168
- r(edge);
2169
- const edgeHash = edge.hashCode;
2170
- if (recordedEdges.has(edgeHash)) continue;
2171
- const adaptorCurve = r(new this.oc.BRepAdaptor_Curve_2(edge.wrapped));
2172
- const tangDef = r(
2173
- new this.oc.GCPnts_TangentialDeflection_2(
2174
- adaptorCurve,
2175
- tolerance,
2176
- angularTolerance,
2177
- 2,
2178
- 1e-9,
2179
- 1e-7
2180
- )
2181
- );
2182
- const [recordPoint, done] = addEdge();
2183
- for (let j = 0; j < tangDef.NbPoints(); j++) {
2184
- const p = r(tangDef.Value(j + 1).Transformed(aLocation.Transformation()));
2185
- recordPoint(p);
2186
- }
2187
- done(edgeHash);
2188
- }
2189
- return { lines, edgeGroups };
2407
+ meshEdges({
2408
+ tolerance = 1e-3,
2409
+ angularTolerance = 0.1,
2410
+ cache = true
2411
+ } = {}) {
2412
+ return meshShapeEdges(this, { tolerance, angularTolerance, cache });
2190
2413
  }
2191
2414
  /**
2192
2415
  * Exports the current shape as a STEP file Blob.
@@ -2194,7 +2417,7 @@ class Shape extends WrappingObj {
2194
2417
  * @category Shape Export
2195
2418
  */
2196
2419
  blobSTEP() {
2197
- const filename = "blob.step";
2420
+ const filename = uniqueIOFilename("_blob", "step");
2198
2421
  const writer = new this.oc.STEPControl_Writer_1();
2199
2422
  this.oc.Interface_Static.SetIVal("write.step.schema", 5);
2200
2423
  writer.Model(true).delete();
@@ -2228,7 +2451,7 @@ class Shape extends WrappingObj {
2228
2451
  */
2229
2452
  blobSTL({ tolerance = 1e-3, angularTolerance = 0.1, binary = false } = {}) {
2230
2453
  this._mesh({ tolerance, angularTolerance });
2231
- const filename = "blob.stl";
2454
+ const filename = uniqueIOFilename("_blob", "stl");
2232
2455
  const done = this.oc.StlAPI.Write(this.wrapped, filename, !binary);
2233
2456
  if (done) {
2234
2457
  try {
@@ -2312,28 +2535,52 @@ class _1DShape extends Shape {
2312
2535
  return new Curve(this._geomAdaptor());
2313
2536
  }
2314
2537
  get startPoint() {
2315
- return this.curve.startPoint;
2538
+ const c = this.curve;
2539
+ const result = c.startPoint;
2540
+ c.delete();
2541
+ return result;
2316
2542
  }
2317
2543
  get endPoint() {
2318
- return this.curve.endPoint;
2544
+ const c = this.curve;
2545
+ const result = c.endPoint;
2546
+ c.delete();
2547
+ return result;
2319
2548
  }
2320
2549
  tangentAt(position = 0) {
2321
- return this.curve.tangentAt(position);
2550
+ const c = this.curve;
2551
+ const result = c.tangentAt(position);
2552
+ c.delete();
2553
+ return result;
2322
2554
  }
2323
2555
  pointAt(position = 0) {
2324
- return this.curve.pointAt(position);
2556
+ const c = this.curve;
2557
+ const result = c.pointAt(position);
2558
+ c.delete();
2559
+ return result;
2325
2560
  }
2326
2561
  get isClosed() {
2327
- return this.curve.isClosed;
2562
+ const c = this.curve;
2563
+ const result = c.isClosed;
2564
+ c.delete();
2565
+ return result;
2328
2566
  }
2329
2567
  get isPeriodic() {
2330
- return this.curve.isPeriodic;
2568
+ const c = this.curve;
2569
+ const result = c.isPeriodic;
2570
+ c.delete();
2571
+ return result;
2331
2572
  }
2332
2573
  get period() {
2333
- return this.curve.period;
2574
+ const c = this.curve;
2575
+ const result = c.period;
2576
+ c.delete();
2577
+ return result;
2334
2578
  }
2335
2579
  get geomType() {
2336
- return this.curve.curveType;
2580
+ const c = this.curve;
2581
+ const result = c.curveType;
2582
+ c.delete();
2583
+ return result;
2337
2584
  }
2338
2585
  get length() {
2339
2586
  const properties = new this.oc.GProp_GProps_1();
@@ -2425,11 +2672,13 @@ class Face extends Shape {
2425
2672
  }
2426
2673
  get geomType() {
2427
2674
  const surface = this.surface;
2428
- const geomType = unwrap(surface.surfaceType);
2429
- surface.delete();
2430
- return geomType;
2431
- }
2432
- get UVBounds() {
2675
+ try {
2676
+ return unwrap(surface.surfaceType);
2677
+ } finally {
2678
+ surface.delete();
2679
+ }
2680
+ }
2681
+ get UVBounds() {
2433
2682
  const uMin = { current: 0 };
2434
2683
  const uMax = { current: 0 };
2435
2684
  const vMin = { current: 0 };
@@ -2891,7 +3140,13 @@ function zip(arrays) {
2891
3140
  }
2892
3141
  const makeLine = (v1, v2) => {
2893
3142
  const oc = getKernel().oc;
2894
- return new Edge(new oc.BRepBuilderAPI_MakeEdge_3(asPnt(v1), asPnt(v2)).Edge());
3143
+ const [r, gc] = localGC();
3144
+ const p1 = r(asPnt(v1));
3145
+ const p2 = r(asPnt(v2));
3146
+ const maker = r(new oc.BRepBuilderAPI_MakeEdge_3(p1, p2));
3147
+ const edge = new Edge(maker.Edge());
3148
+ gc();
3149
+ return edge;
2895
3150
  };
2896
3151
  const makeCircle = (radius, center = [0, 0, 0], normal = [0, 0, 1]) => {
2897
3152
  const oc = getKernel().oc;
@@ -2930,8 +3185,8 @@ const makeHelix = (pitch, height, radius, center = [0, 0, 0], dir = [0, 0, 1], l
2930
3185
  new oc.Geom2d_Line_3(r(new oc.gp_Pnt2d_3(0, 0)), r(new oc.gp_Dir2d_4(myDir, pitch)))
2931
3186
  );
2932
3187
  const nTurns = height / pitch;
2933
- const uStart = geomLine.Value(0);
2934
- const uStop = geomLine.Value(nTurns * Math.sqrt((2 * Math.PI) ** 2 + pitch ** 2));
3188
+ const uStart = r(geomLine.Value(0));
3189
+ const uStop = r(geomLine.Value(nTurns * Math.sqrt((2 * Math.PI) ** 2 + pitch ** 2)));
2935
3190
  const geomSeg = r(new oc.GCE2d_MakeSegment_1(uStart, uStop));
2936
3191
  const geomSurf = new oc.Geom_CylindricalSurface_1(r(makeAx3(center, dir)), radius);
2937
3192
  const e = r(
@@ -2947,9 +3202,17 @@ const makeHelix = (pitch, height, radius, center = [0, 0, 0], dir = [0, 0, 1], l
2947
3202
  };
2948
3203
  const makeThreePointArc = (v1, v2, v3) => {
2949
3204
  const oc = getKernel().oc;
2950
- const circleGeom = new oc.GC_MakeArcOfCircle_4(asPnt(v1), asPnt(v2), asPnt(v3)).Value();
2951
- const curve = new oc.Handle_Geom_Curve_2(circleGeom.get());
2952
- return new Edge(new oc.BRepBuilderAPI_MakeEdge_24(curve).Edge());
3205
+ const [r, gc] = localGC();
3206
+ const p1 = r(asPnt(v1));
3207
+ const p2 = r(asPnt(v2));
3208
+ const p3 = r(asPnt(v3));
3209
+ const arcMaker = r(new oc.GC_MakeArcOfCircle_4(p1, p2, p3));
3210
+ const circleGeom = r(arcMaker.Value());
3211
+ const curve = r(new oc.Handle_Geom_Curve_2(circleGeom.get()));
3212
+ const edgeMaker = r(new oc.BRepBuilderAPI_MakeEdge_24(curve));
3213
+ const edge = new Edge(edgeMaker.Edge());
3214
+ gc();
3215
+ return edge;
2953
3216
  };
2954
3217
  const makeEllipseArc = (majorRadius, minorRadius, startAngle, endAngle, center = [0, 0, 0], normal = [0, 0, 1], xDir) => {
2955
3218
  const oc = getKernel().oc;
@@ -3013,13 +3276,17 @@ const makeBezierCurve = (points) => {
3013
3276
  bug("makeBezierCurve", `Need at least 2 points for a Bezier curve, got ${points.length}`);
3014
3277
  }
3015
3278
  const oc = getKernel().oc;
3016
- const arrayOfPoints = new oc.TColgp_Array1OfPnt_2(1, points.length);
3279
+ const [r, gc] = localGC();
3280
+ const arrayOfPoints = r(new oc.TColgp_Array1OfPnt_2(1, points.length));
3017
3281
  points.forEach((p, i) => {
3018
- arrayOfPoints.SetValue(i + 1, asPnt(p));
3282
+ arrayOfPoints.SetValue(i + 1, r(asPnt(p)));
3019
3283
  });
3020
3284
  const bezCurve = new oc.Geom_BezierCurve_1(arrayOfPoints);
3021
- const curve = new oc.Handle_Geom_Curve_2(bezCurve);
3022
- return new Edge(new oc.BRepBuilderAPI_MakeEdge_24(curve).Edge());
3285
+ const curve = r(new oc.Handle_Geom_Curve_2(bezCurve));
3286
+ const edgeMaker = r(new oc.BRepBuilderAPI_MakeEdge_24(curve));
3287
+ const edge = new Edge(edgeMaker.Edge());
3288
+ gc();
3289
+ return edge;
3023
3290
  };
3024
3291
  const makeTangentArc = (startPoint, startTgt, endPoint) => {
3025
3292
  const oc = getKernel().oc;
@@ -3038,7 +3305,8 @@ const makeTangentArc = (startPoint, startTgt, endPoint) => {
3038
3305
  };
3039
3306
  const assembleWire$1 = (listOfEdges) => {
3040
3307
  const oc = getKernel().oc;
3041
- const wireBuilder = new oc.BRepBuilderAPI_MakeWire_1();
3308
+ const [r, gc] = localGC();
3309
+ const wireBuilder = r(new oc.BRepBuilderAPI_MakeWire_1());
3042
3310
  listOfEdges.forEach((e) => {
3043
3311
  if (e instanceof Edge) {
3044
3312
  wireBuilder.Add_1(e.wrapped);
@@ -3047,7 +3315,7 @@ const assembleWire$1 = (listOfEdges) => {
3047
3315
  wireBuilder.Add_2(e.wrapped);
3048
3316
  }
3049
3317
  });
3050
- const progress = new oc.Message_ProgressRange_1();
3318
+ const progress = r(new oc.Message_ProgressRange_1());
3051
3319
  wireBuilder.Build(progress);
3052
3320
  const res = wireBuilder.Error();
3053
3321
  if (res !== oc.BRepBuilderAPI_WireError.BRepBuilderAPI_WireDone) {
@@ -3056,8 +3324,7 @@ const assembleWire$1 = (listOfEdges) => {
3056
3324
  [oc.BRepBuilderAPI_WireError.BRepBuilderAPI_NonManifoldWire, "non manifold wire"],
3057
3325
  [oc.BRepBuilderAPI_WireError.BRepBuilderAPI_DisconnectedWire, "disconnected wire"]
3058
3326
  ]);
3059
- wireBuilder.delete();
3060
- progress.delete();
3327
+ gc();
3061
3328
  return err(
3062
3329
  occtError(
3063
3330
  "WIRE_BUILD_FAILED",
@@ -3066,8 +3333,7 @@ const assembleWire$1 = (listOfEdges) => {
3066
3333
  );
3067
3334
  }
3068
3335
  const wire = new Wire(wireBuilder.Wire());
3069
- wireBuilder.delete();
3070
- progress.delete();
3336
+ gc();
3071
3337
  return ok(wire);
3072
3338
  };
3073
3339
  const makeFace = (wire, holes) => {
@@ -3120,33 +3386,37 @@ const makeNonPlanarFace = (wire) => {
3120
3386
  };
3121
3387
  const makeCylinder = (radius, height, location = [0, 0, 0], direction = [0, 0, 1]) => {
3122
3388
  const oc = getKernel().oc;
3123
- const axis = makeAx2(location, direction);
3124
- const cylinder = new oc.BRepPrimAPI_MakeCylinder_3(axis, radius, height);
3389
+ const [r, gc] = localGC();
3390
+ const axis = r(makeAx2(location, direction));
3391
+ const cylinder = r(new oc.BRepPrimAPI_MakeCylinder_3(axis, radius, height));
3125
3392
  const solid = new Solid(cylinder.Shape());
3126
- axis.delete();
3127
- cylinder.delete();
3393
+ gc();
3128
3394
  return solid;
3129
3395
  };
3130
3396
  const makeSphere = (radius) => {
3131
3397
  const oc = getKernel().oc;
3132
- const sphereMaker = new oc.BRepPrimAPI_MakeSphere_1(radius);
3398
+ const [r, gc] = localGC();
3399
+ const sphereMaker = r(new oc.BRepPrimAPI_MakeSphere_1(radius));
3133
3400
  const sphere = new Solid(sphereMaker.Shape());
3134
- sphereMaker.delete();
3401
+ gc();
3135
3402
  return sphere;
3136
3403
  };
3137
- class EllpsoidTransform extends WrappingObj {
3404
+ class EllipsoidTransform extends WrappingObj {
3138
3405
  constructor(x, y, z) {
3139
3406
  const oc = getKernel().oc;
3140
3407
  const r = gcWithScope();
3141
3408
  const xyRatio = Math.sqrt(x * y / z);
3142
3409
  const xzRatio = x / xyRatio;
3143
3410
  const yzRatio = y / xyRatio;
3411
+ const ax1 = r(makeAx1([0, 0, 0], [0, 1, 0]));
3412
+ const ax2 = r(makeAx1([0, 0, 0], [0, 0, 1]));
3413
+ const ax3 = r(makeAx1([0, 0, 0], [1, 0, 0]));
3144
3414
  const transform = new oc.gp_GTrsf_1();
3145
- transform.SetAffinity_1(makeAx1([0, 0, 0], [0, 1, 0]), xzRatio);
3415
+ transform.SetAffinity_1(ax1, xzRatio);
3146
3416
  const xy = r(new oc.gp_GTrsf_1());
3147
- xy.SetAffinity_1(makeAx1([0, 0, 0], [0, 0, 1]), xyRatio);
3417
+ xy.SetAffinity_1(ax2, xyRatio);
3148
3418
  const yz = r(new oc.gp_GTrsf_1());
3149
- yz.SetAffinity_1(makeAx1([0, 0, 0], [1, 0, 0]), yzRatio);
3419
+ yz.SetAffinity_1(ax3, yzRatio);
3150
3420
  transform.Multiply(xy);
3151
3421
  transform.Multiply(yz);
3152
3422
  super(transform);
@@ -3178,60 +3448,71 @@ const makeEllipsoid = (aLength, bLength, cLength) => {
3178
3448
  sphere.SetRadius(1);
3179
3449
  const sphericalSurface = r(new oc.Geom_SphericalSurface_2(sphere));
3180
3450
  const baseSurface = oc.GeomConvert.SurfaceToBSplineSurface(sphericalSurface.UReversed()).get();
3181
- const poles = convertToJSArray(baseSurface.Poles_2());
3182
- const transform = new EllpsoidTransform(aLength, bLength, cLength);
3183
- poles.forEach((columns, rowIdx) => {
3184
- columns.forEach((value, colIdx) => {
3185
- const newPoint = transform.applyToPoint(value);
3186
- baseSurface.SetPole_1(rowIdx + 1, colIdx + 1, newPoint);
3451
+ try {
3452
+ const poles = convertToJSArray(baseSurface.Poles_2());
3453
+ const transform = new EllipsoidTransform(aLength, bLength, cLength);
3454
+ poles.forEach((columns, rowIdx) => {
3455
+ columns.forEach((value, colIdx) => {
3456
+ const newPoint = transform.applyToPoint(value);
3457
+ baseSurface.SetPole_1(rowIdx + 1, colIdx + 1, newPoint);
3458
+ newPoint.delete();
3459
+ });
3187
3460
  });
3188
- });
3189
- const shell = unwrap(
3190
- cast(r(new oc.BRepBuilderAPI_MakeShell_2(baseSurface.UReversed(), false)).Shell())
3191
- );
3192
- return unwrap(makeSolid([shell]));
3461
+ const shell = unwrap(
3462
+ cast(r(new oc.BRepBuilderAPI_MakeShell_2(baseSurface.UReversed(), false)).Shell())
3463
+ );
3464
+ return unwrap(makeSolid([shell]));
3465
+ } finally {
3466
+ baseSurface.delete();
3467
+ }
3193
3468
  };
3194
3469
  const makeBox = (corner1, corner2) => {
3195
3470
  const oc = getKernel().oc;
3196
- const boxMaker = new oc.BRepPrimAPI_MakeBox_4(asPnt(corner1), asPnt(corner2));
3471
+ const [r, gc] = localGC();
3472
+ const p1 = r(asPnt(corner1));
3473
+ const p2 = r(asPnt(corner2));
3474
+ const boxMaker = r(new oc.BRepPrimAPI_MakeBox_4(p1, p2));
3197
3475
  const box = new Solid(boxMaker.Solid());
3198
- boxMaker.delete();
3476
+ gc();
3199
3477
  return box;
3200
3478
  };
3201
3479
  const makeVertex = (point) => {
3202
3480
  const oc = getKernel().oc;
3203
- const pnt2 = asPnt(point);
3204
- const vertexMaker = new oc.BRepBuilderAPI_MakeVertex(pnt2);
3481
+ const [r, gc] = localGC();
3482
+ const pnt2 = r(asPnt(point));
3483
+ const vertexMaker = r(new oc.BRepBuilderAPI_MakeVertex(pnt2));
3205
3484
  const vertex = vertexMaker.Vertex();
3206
- vertexMaker.delete();
3485
+ gc();
3207
3486
  return new Vertex(vertex);
3208
3487
  };
3209
3488
  const makeOffset = (face, offset2, tolerance = 1e-6) => {
3210
3489
  const oc = getKernel().oc;
3211
3490
  const progress = new oc.Message_ProgressRange_1();
3212
3491
  const offsetBuilder = new oc.BRepOffsetAPI_MakeOffsetShape();
3213
- offsetBuilder.PerformByJoin(
3214
- face.wrapped,
3215
- offset2,
3216
- tolerance,
3217
- oc.BRepOffset_Mode.BRepOffset_Skin,
3218
- false,
3219
- false,
3220
- oc.GeomAbs_JoinType.GeomAbs_Arc,
3221
- false,
3222
- progress
3223
- );
3224
- const result = andThen(
3225
- downcast(offsetBuilder.Shape()),
3226
- (downcasted) => andThen(cast(downcasted), (newShape) => {
3227
- if (!isShape3D$1(newShape))
3228
- return err(typeCastError("OFFSET_NOT_3D", "Could not offset to a 3d shape"));
3229
- return ok(newShape);
3230
- })
3231
- );
3232
- offsetBuilder.delete();
3233
- progress.delete();
3234
- return result;
3492
+ try {
3493
+ offsetBuilder.PerformByJoin(
3494
+ face.wrapped,
3495
+ offset2,
3496
+ tolerance,
3497
+ oc.BRepOffset_Mode.BRepOffset_Skin,
3498
+ false,
3499
+ false,
3500
+ oc.GeomAbs_JoinType.GeomAbs_Arc,
3501
+ false,
3502
+ progress
3503
+ );
3504
+ return andThen(
3505
+ downcast(offsetBuilder.Shape()),
3506
+ (downcasted) => andThen(cast(downcasted), (newShape) => {
3507
+ if (!isShape3D$1(newShape))
3508
+ return err(typeCastError("OFFSET_NOT_3D", "Could not offset to a 3d shape"));
3509
+ return ok(newShape);
3510
+ })
3511
+ );
3512
+ } finally {
3513
+ offsetBuilder.delete();
3514
+ progress.delete();
3515
+ }
3235
3516
  };
3236
3517
  const compoundShapes = (shapeArray) => {
3237
3518
  const oc = getKernel().oc;
@@ -3615,13 +3896,15 @@ function offsetWire2D(wire, offset2, kind = "arc") {
3615
3896
  const wrapped = castShape(resultShape);
3616
3897
  offsetter.delete();
3617
3898
  if (!isWire(wrapped)) {
3899
+ wrapped[Symbol.dispose]();
3618
3900
  return err(typeCastError("OFFSET_NOT_WIRE", "Offset did not produce a Wire"));
3619
3901
  }
3620
3902
  return ok(wrapped);
3621
3903
  }
3622
3904
  function getSurfaceType(face) {
3623
3905
  const oc = getKernel().oc;
3624
- const adaptor = new oc.BRepAdaptor_Surface_2(face.wrapped, false);
3906
+ const r = gcWithScope();
3907
+ const adaptor = r(new oc.BRepAdaptor_Surface_2(face.wrapped, false));
3625
3908
  const ga = oc.GeomAbs_SurfaceType;
3626
3909
  const CAST_MAP = /* @__PURE__ */ new Map([
3627
3910
  [ga.GeomAbs_Plane, "PLANE"],
@@ -3637,7 +3920,6 @@ function getSurfaceType(face) {
3637
3920
  [ga.GeomAbs_OtherSurface, "OTHER_SURFACE"]
3638
3921
  ]);
3639
3922
  const surfType = CAST_MAP.get(adaptor.GetType());
3640
- adaptor.delete();
3641
3923
  if (!surfType) {
3642
3924
  return err(
3643
3925
  typeCastError("UNKNOWN_SURFACE_TYPE", "Unrecognized surface type from OCCT adapter")
@@ -3672,16 +3954,14 @@ function uvBounds(face) {
3672
3954
  }
3673
3955
  function pointOnSurface(face, u, v) {
3674
3956
  const oc = getKernel().oc;
3957
+ const r = gcWithScope();
3675
3958
  const bounds = uvBounds(face);
3676
- const adaptor = new oc.BRepAdaptor_Surface_2(face.wrapped, false);
3677
- const p = new oc.gp_Pnt_1();
3959
+ const adaptor = r(new oc.BRepAdaptor_Surface_2(face.wrapped, false));
3960
+ const p = r(new oc.gp_Pnt_1());
3678
3961
  const absU = u * (bounds.uMax - bounds.uMin) + bounds.uMin;
3679
3962
  const absV = v * (bounds.vMax - bounds.vMin) + bounds.vMin;
3680
3963
  adaptor.D0(absU, absV, p);
3681
- const result = [p.X(), p.Y(), p.Z()];
3682
- p.delete();
3683
- adaptor.delete();
3684
- return result;
3964
+ return [p.X(), p.Y(), p.Z()];
3685
3965
  }
3686
3966
  function uvCoordinates(face, point) {
3687
3967
  const oc = getKernel().oc;
@@ -3720,13 +4000,11 @@ function normalAt(face, locationPoint) {
3720
4000
  }
3721
4001
  function faceCenter(face) {
3722
4002
  const oc = getKernel().oc;
3723
- const props = new oc.GProp_GProps_1();
4003
+ const r = gcWithScope();
4004
+ const props = r(new oc.GProp_GProps_1());
3724
4005
  oc.BRepGProp.SurfaceProperties_2(face.wrapped, props, 1e-7, true);
3725
- const center = props.CentreOfMass();
3726
- const result = [center.X(), center.Y(), center.Z()];
3727
- center.delete();
3728
- props.delete();
3729
- return result;
4006
+ const center = r(props.CentreOfMass());
4007
+ return [center.X(), center.Y(), center.Z()];
3730
4008
  }
3731
4009
  function outerWire(face) {
3732
4010
  const oc = getKernel().oc;
@@ -3737,8 +4015,7 @@ function innerWires(face) {
3737
4015
  const allWires = Array.from(iterTopo(face.wrapped, "wire")).map(
3738
4016
  (w) => castShape(unwrap(downcast(w)))
3739
4017
  );
3740
- const outerHash = outer.wrapped.HashCode(2147483647);
3741
- const result = allWires.filter((w) => w.wrapped.HashCode(2147483647) !== outerHash);
4018
+ const result = allWires.filter((w) => !w.wrapped.IsSame(outer.wrapped));
3742
4019
  return result;
3743
4020
  }
3744
4021
  function triangulateFace(face, index0 = 0, skipNormals = false) {
@@ -3794,195 +4071,6 @@ function triangulateFace(face, index0 = 0, skipNormals = false) {
3794
4071
  }
3795
4072
  return result;
3796
4073
  }
3797
- const DEFAULT_MAX_SIZE = 128;
3798
- let cache = /* @__PURE__ */ new Map();
3799
- let maxSize = DEFAULT_MAX_SIZE;
3800
- function buildMeshCacheKey(shapeHash, tolerance, angularTolerance, skipNormals) {
3801
- return `${shapeHash}:${tolerance}:${angularTolerance}:${skipNormals}`;
3802
- }
3803
- function getMesh(key) {
3804
- const entry = cache.get(key);
3805
- if (!entry) return void 0;
3806
- cache.delete(key);
3807
- cache.set(key, entry);
3808
- return entry.value;
3809
- }
3810
- function setMesh(key, value) {
3811
- if (cache.has(key)) {
3812
- cache.delete(key);
3813
- }
3814
- if (cache.size >= maxSize) {
3815
- const oldest = cache.keys().next().value;
3816
- if (oldest !== void 0) {
3817
- cache.delete(oldest);
3818
- }
3819
- }
3820
- cache.set(key, { key, value });
3821
- }
3822
- function clearMeshCache() {
3823
- cache = /* @__PURE__ */ new Map();
3824
- }
3825
- function setMeshCacheSize(size) {
3826
- maxSize = size;
3827
- while (cache.size > maxSize) {
3828
- const oldest = cache.keys().next().value;
3829
- if (oldest !== void 0) {
3830
- cache.delete(oldest);
3831
- }
3832
- }
3833
- }
3834
- function meshShape(shape, {
3835
- tolerance = 1e-3,
3836
- angularTolerance = 0.1,
3837
- skipNormals = false,
3838
- cache: cache2 = true
3839
- } = {}) {
3840
- const shapeHash = shape.wrapped.HashCode(HASH_CODE_MAX);
3841
- if (cache2) {
3842
- const cacheKey = buildMeshCacheKey(shapeHash, tolerance, angularTolerance, skipNormals);
3843
- const cached = getMesh(cacheKey);
3844
- if (cached) return cached;
3845
- }
3846
- const result = getKernel().mesh(shape.wrapped, {
3847
- tolerance,
3848
- angularTolerance,
3849
- skipNormals
3850
- });
3851
- const mesh = {
3852
- vertices: result.vertices,
3853
- normals: result.normals,
3854
- triangles: result.triangles,
3855
- faceGroups: result.faceGroups.map((g) => ({
3856
- start: g.start,
3857
- count: g.count,
3858
- faceId: g.faceHash
3859
- }))
3860
- };
3861
- if (cache2) {
3862
- const cacheKey = buildMeshCacheKey(shapeHash, tolerance, angularTolerance, skipNormals);
3863
- setMesh(cacheKey, mesh);
3864
- }
3865
- return mesh;
3866
- }
3867
- function meshShapeEdges(shape, { tolerance = 1e-3, angularTolerance = 0.1 } = {}) {
3868
- const oc = getKernel().oc;
3869
- const r = gcWithScope();
3870
- const recordedEdges = /* @__PURE__ */ new Set();
3871
- const lines = [];
3872
- const edgeGroups = [];
3873
- const addEdge = () => {
3874
- const start = lines.length;
3875
- let prevX = 0;
3876
- let prevY = 0;
3877
- let prevZ = 0;
3878
- let hasPrev = false;
3879
- return [
3880
- (p) => {
3881
- const x = p.X();
3882
- const y = p.Y();
3883
- const z = p.Z();
3884
- if (hasPrev) {
3885
- lines.push(prevX, prevY, prevZ, x, y, z);
3886
- }
3887
- prevX = x;
3888
- prevY = y;
3889
- prevZ = z;
3890
- hasPrev = true;
3891
- },
3892
- (edgeHash) => {
3893
- edgeGroups.push({
3894
- start: start / 3,
3895
- count: (lines.length - start) / 3,
3896
- edgeId: edgeHash
3897
- });
3898
- recordedEdges.add(edgeHash);
3899
- }
3900
- ];
3901
- };
3902
- const aLocation = r(new oc.TopLoc_Location_1());
3903
- const faces = getFaces(shape);
3904
- for (const face of faces) {
3905
- const triangulation = r(oc.BRep_Tool.Triangulation(face.wrapped, aLocation, 0));
3906
- if (triangulation.IsNull()) continue;
3907
- const tri = triangulation.get();
3908
- const faceEdges = getEdges$1(face);
3909
- for (const edge of faceEdges) {
3910
- const edgeHash = edge.wrapped.HashCode(HASH_CODE_MAX);
3911
- if (recordedEdges.has(edgeHash)) continue;
3912
- const edgeLoc = r(new oc.TopLoc_Location_1());
3913
- const polygon = r(
3914
- oc.BRep_Tool.PolygonOnTriangulation_1(edge.wrapped, triangulation, edgeLoc)
3915
- );
3916
- const edgeNodes = polygon?.get()?.Nodes();
3917
- if (!edgeNodes) continue;
3918
- r(edgeNodes);
3919
- const [recordPoint, done] = addEdge();
3920
- for (let i = edgeNodes.Lower(); i <= edgeNodes.Upper(); i++) {
3921
- const p = r(r(tri.Node(edgeNodes.Value(i))).Transformed(edgeLoc.Transformation()));
3922
- recordPoint(p);
3923
- }
3924
- done(edgeHash);
3925
- }
3926
- }
3927
- const allEdges = getEdges$1(shape);
3928
- for (const edge of allEdges) {
3929
- const edgeHash = edge.wrapped.HashCode(HASH_CODE_MAX);
3930
- if (recordedEdges.has(edgeHash)) continue;
3931
- const adaptorCurve = r(new oc.BRepAdaptor_Curve_2(edge.wrapped));
3932
- const tangDef = r(
3933
- new oc.GCPnts_TangentialDeflection_2(adaptorCurve, tolerance, angularTolerance, 2, 1e-9, 1e-7)
3934
- );
3935
- const [recordPoint, done] = addEdge();
3936
- for (let j = 0; j < tangDef.NbPoints(); j++) {
3937
- const p = r(tangDef.Value(j + 1).Transformed(aLocation.Transformation()));
3938
- recordPoint(p);
3939
- }
3940
- done(edgeHash);
3941
- }
3942
- return { lines, edgeGroups };
3943
- }
3944
- function exportSTEP$1(shape) {
3945
- const oc = getKernel().oc;
3946
- const filename = "blob.step";
3947
- const writer = new oc.STEPControl_Writer_1();
3948
- oc.Interface_Static.SetIVal("write.step.schema", 5);
3949
- writer.Model(true).delete();
3950
- const progress = new oc.Message_ProgressRange_1();
3951
- writer.Transfer(shape.wrapped, oc.STEPControl_StepModelType.STEPControl_AsIs, true, progress);
3952
- const done = writer.Write(filename);
3953
- writer.delete();
3954
- progress.delete();
3955
- if (done === oc.IFSelect_ReturnStatus.IFSelect_RetDone) {
3956
- try {
3957
- const file = oc.FS.readFile("/" + filename);
3958
- oc.FS.unlink("/" + filename);
3959
- return ok(new Blob([file], { type: "application/STEP" }));
3960
- } catch (e) {
3961
- return err(ioError("STEP_FILE_READ_ERROR", "Failed to read exported STEP file", e));
3962
- }
3963
- }
3964
- return err(ioError("STEP_EXPORT_FAILED", "Failed to write STEP file"));
3965
- }
3966
- function exportSTL(shape, {
3967
- tolerance = 1e-3,
3968
- angularTolerance = 0.1,
3969
- binary = false
3970
- } = {}) {
3971
- const oc = getKernel().oc;
3972
- new oc.BRepMesh_IncrementalMesh_2(shape.wrapped, tolerance, false, angularTolerance, false);
3973
- const filename = "blob.stl";
3974
- const done = oc.StlAPI.Write(shape.wrapped, filename, !binary);
3975
- if (done) {
3976
- try {
3977
- const file = oc.FS.readFile("/" + filename);
3978
- oc.FS.unlink("/" + filename);
3979
- return ok(new Blob([file], { type: "application/sla" }));
3980
- } catch (e) {
3981
- return err(ioError("STL_FILE_READ_ERROR", "Failed to read exported STL file", e));
3982
- }
3983
- }
3984
- return err(ioError("STL_EXPORT_FAILED", "Failed to write STL file"));
3985
- }
3986
4074
  function applyGlue(op, optimisation) {
3987
4075
  const oc = getKernel().oc;
3988
4076
  if (optimisation === "commonFace") {
@@ -4006,6 +4094,7 @@ function buildCompoundOcInternal(shapes) {
4006
4094
  function castToShape3D(shape, errorCode, errorMsg) {
4007
4095
  const wrapped = castShape(shape);
4008
4096
  if (!isShape3D(wrapped)) {
4097
+ wrapped[Symbol.dispose]();
4009
4098
  return err(typeCastError(errorCode, errorMsg));
4010
4099
  }
4011
4100
  return ok(wrapped);
@@ -4074,29 +4163,44 @@ function buildCompound(shapes) {
4074
4163
  return createCompound(compound);
4075
4164
  }
4076
4165
  initCast(shapesModule);
4166
+ function buildLawFromProfile(extrusionLength, { profile, endFactor = 1 }) {
4167
+ const oc = getKernel().oc;
4168
+ const r = gcWithScope();
4169
+ let law;
4170
+ if (profile === "s-curve") {
4171
+ law = r(new oc.Law_S());
4172
+ law.Set_1(0, 1, extrusionLength, endFactor);
4173
+ } else if (profile === "linear") {
4174
+ law = r(new oc.Law_Linear());
4175
+ law.Set(0, 1, extrusionLength, endFactor);
4176
+ } else {
4177
+ return err(
4178
+ validationError("UNSUPPORTED_PROFILE", `Unsupported extrusion profile: ${String(profile)}`)
4179
+ );
4180
+ }
4181
+ return ok(law.Trim(0, extrusionLength, 1e-6));
4182
+ }
4077
4183
  const basicFaceExtrusion = (face, extrusionVec) => {
4078
4184
  const oc = getKernel().oc;
4079
- const solidBuilder = new oc.BRepPrimAPI_MakePrism_1(
4080
- face.wrapped,
4081
- extrusionVec.wrapped,
4082
- false,
4083
- true
4185
+ const [r, gc] = localGC();
4186
+ const solidBuilder = r(
4187
+ new oc.BRepPrimAPI_MakePrism_1(face.wrapped, extrusionVec.wrapped, false, true)
4084
4188
  );
4085
4189
  const solid = new Solid(unwrap(downcast(solidBuilder.Shape())));
4086
- solidBuilder.delete();
4190
+ gc();
4087
4191
  return solid;
4088
4192
  };
4089
4193
  const revolution = (face, center = [0, 0, 0], direction = [0, 0, 1], angle = 360) => {
4090
4194
  const oc = getKernel().oc;
4091
- const ax = makeAx1(center, direction);
4092
- const revolBuilder = new oc.BRepPrimAPI_MakeRevol_1(face.wrapped, ax, angle * DEG2RAD, false);
4195
+ const [r, gc] = localGC();
4196
+ const ax = r(makeAx1(center, direction));
4197
+ const revolBuilder = r(new oc.BRepPrimAPI_MakeRevol_1(face.wrapped, ax, angle * DEG2RAD, false));
4093
4198
  const result = andThen(cast(revolBuilder.Shape()), (shape) => {
4094
4199
  if (!isShape3D$2(shape))
4095
4200
  return err(typeCastError("REVOLUTION_NOT_3D", "Revolution did not produce a 3D shape"));
4096
4201
  return ok(shape);
4097
4202
  });
4098
- ax.delete();
4099
- revolBuilder.delete();
4203
+ gc();
4100
4204
  return result;
4101
4205
  };
4102
4206
  function genericSweep(wire, spine, {
@@ -4109,8 +4213,9 @@ function genericSweep(wire, spine, {
4109
4213
  forceProfileSpineOthogonality
4110
4214
  } = {}, shellMode = false) {
4111
4215
  const oc = getKernel().oc;
4216
+ const [r, gc] = localGC();
4112
4217
  const withCorrection = transitionMode === "round" ? true : !!forceProfileSpineOthogonality;
4113
- const sweepBuilder = new oc.BRepOffsetAPI_MakePipeShell(spine.wrapped);
4218
+ const sweepBuilder = r(new oc.BRepOffsetAPI_MakePipeShell(spine.wrapped));
4114
4219
  {
4115
4220
  const mode = {
4116
4221
  transformed: oc.BRepBuilderAPI_TransitionMode.BRepBuilderAPI_Transformed,
@@ -4133,91 +4238,71 @@ function genericSweep(wire, spine, {
4133
4238
  }
4134
4239
  if (!law) sweepBuilder.Add_1(wire.wrapped, !!withContact, withCorrection);
4135
4240
  else sweepBuilder.SetLaw_1(wire.wrapped, law, !!withContact, withCorrection);
4136
- const progress = new oc.Message_ProgressRange_1();
4241
+ const progress = r(new oc.Message_ProgressRange_1());
4137
4242
  sweepBuilder.Build(progress);
4138
4243
  if (!shellMode) {
4139
4244
  sweepBuilder.MakeSolid();
4140
4245
  }
4141
4246
  const shape = unwrap(cast(sweepBuilder.Shape()));
4142
4247
  if (!isShape3D$2(shape)) {
4143
- sweepBuilder.delete();
4144
- progress.delete();
4248
+ gc();
4145
4249
  return err(typeCastError("SWEEP_NOT_3D", "Sweep did not produce a 3D shape"));
4146
4250
  }
4147
4251
  if (shellMode) {
4148
4252
  const startWire = unwrap(cast(sweepBuilder.FirstShape()));
4149
4253
  const endWire = unwrap(cast(sweepBuilder.LastShape()));
4150
4254
  if (!isWire$1(startWire)) {
4151
- sweepBuilder.delete();
4255
+ gc();
4152
4256
  return err(typeCastError("SWEEP_START_NOT_WIRE", "Sweep did not produce a start Wire"));
4153
4257
  }
4154
4258
  if (!isWire$1(endWire)) {
4155
- sweepBuilder.delete();
4259
+ gc();
4156
4260
  return err(typeCastError("SWEEP_END_NOT_WIRE", "Sweep did not produce an end Wire"));
4157
4261
  }
4158
- sweepBuilder.delete();
4262
+ gc();
4159
4263
  return ok([shape, startWire, endWire]);
4160
4264
  }
4161
- sweepBuilder.delete();
4162
- progress.delete();
4265
+ gc();
4163
4266
  return ok(shape);
4164
4267
  }
4165
- const buildLawFromProfile$1 = (extrusionLength, { profile, endFactor = 1 }) => {
4166
- const oc = getKernel().oc;
4167
- let law;
4168
- if (profile === "s-curve") {
4169
- law = new oc.Law_S();
4170
- law.Set_1(0, 1, extrusionLength, endFactor);
4171
- } else if (profile === "linear") {
4172
- law = new oc.Law_Linear();
4173
- law.Set(0, 1, extrusionLength, endFactor);
4174
- } else {
4175
- return err(
4176
- validationError("UNSUPPORTED_PROFILE", `Unsupported extrusion profile: ${String(profile)}`)
4177
- );
4178
- }
4179
- const trimmed = law.Trim(0, extrusionLength, 1e-6);
4180
- law.delete();
4181
- return ok(trimmed);
4182
- };
4183
4268
  const supportExtrude$1 = (wire, center, normal, support) => {
4184
- const centerVec = new Vector(center);
4185
- const normalVec = new Vector(normal);
4186
- const endVec = centerVec.add(normalVec);
4187
- const mainSpineEdge = makeLine(centerVec, endVec);
4188
- const spine = unwrap(assembleWire$1([mainSpineEdge]));
4189
- centerVec.delete();
4190
- normalVec.delete();
4191
- endVec.delete();
4192
- return genericSweep(wire, spine, { support });
4269
+ const [r, gc] = localGC();
4270
+ const centerVec = r(new Vector(center));
4271
+ const normalVec = r(new Vector(normal));
4272
+ const endVec = r(centerVec.add(normalVec));
4273
+ const mainSpineEdge = r(makeLine(centerVec, endVec));
4274
+ const spine = r(unwrap(assembleWire$1([mainSpineEdge])));
4275
+ const result = genericSweep(wire, spine, { support });
4276
+ gc();
4277
+ return result;
4193
4278
  };
4194
4279
  function complexExtrude$1(wire, center, normal, profileShape, shellMode = false) {
4195
- const centerVec = new Vector(center);
4196
- const normalVec = new Vector(normal);
4197
- const endVec = centerVec.add(normalVec);
4198
- const mainSpineEdge = makeLine(centerVec, endVec);
4199
- const spine = unwrap(assembleWire$1([mainSpineEdge]));
4200
- const law = profileShape ? unwrap(buildLawFromProfile$1(normalVec.Length, profileShape)) : null;
4201
- centerVec.delete();
4202
- endVec.delete();
4203
- normalVec.delete();
4204
- return shellMode ? genericSweep(wire, spine, { law }, shellMode) : genericSweep(wire, spine, { law }, shellMode);
4280
+ const [r, gc] = localGC();
4281
+ const centerVec = r(new Vector(center));
4282
+ const normalVec = r(new Vector(normal));
4283
+ const endVec = r(centerVec.add(normalVec));
4284
+ const mainSpineEdge = r(makeLine(centerVec, endVec));
4285
+ const spine = r(unwrap(assembleWire$1([mainSpineEdge])));
4286
+ const law = profileShape ? r(unwrap(buildLawFromProfile(normalVec.Length, profileShape))) : null;
4287
+ const result = shellMode ? genericSweep(wire, spine, { law }, shellMode) : genericSweep(wire, spine, { law }, shellMode);
4288
+ gc();
4289
+ return result;
4205
4290
  }
4206
4291
  function twistExtrude$1(wire, angleDegrees, center, normal, profileShape, shellMode = false) {
4207
- const centerVec = new Vector(center);
4208
- const normalVec = new Vector(normal);
4209
- const endVec = centerVec.add(normalVec);
4210
- const mainSpineEdge = makeLine(centerVec, endVec);
4211
- const spine = unwrap(assembleWire$1([mainSpineEdge]));
4292
+ const [r, gc] = localGC();
4293
+ const centerVec = r(new Vector(center));
4294
+ const normalVec = r(new Vector(normal));
4295
+ const endVec = r(centerVec.add(normalVec));
4296
+ const mainSpineEdge = r(makeLine(centerVec, endVec));
4297
+ const spine = r(unwrap(assembleWire$1([mainSpineEdge])));
4212
4298
  const extrusionLength = normalVec.Length;
4213
4299
  const pitch = 360 / angleDegrees * extrusionLength;
4214
4300
  const radius = 1;
4215
- const auxiliarySpine = makeHelix(pitch, extrusionLength, radius, center, normal);
4216
- const law = profileShape ? unwrap(buildLawFromProfile$1(extrusionLength, profileShape)) : null;
4217
- centerVec.delete();
4218
- normalVec.delete();
4219
- endVec.delete();
4220
- return shellMode ? genericSweep(wire, spine, { auxiliarySpine, law }, shellMode) : genericSweep(wire, spine, { auxiliarySpine, law }, shellMode);
4301
+ const auxiliarySpine = r(makeHelix(pitch, extrusionLength, radius, center, normal));
4302
+ const law = profileShape ? r(unwrap(buildLawFromProfile(extrusionLength, profileShape))) : null;
4303
+ const result = shellMode ? genericSweep(wire, spine, { auxiliarySpine, law }, shellMode) : genericSweep(wire, spine, { auxiliarySpine, law }, shellMode);
4304
+ gc();
4305
+ return result;
4221
4306
  }
4222
4307
  const loft = (wires, { ruled = true, startPoint, endPoint } = {}, returnShell = false) => {
4223
4308
  const oc = getKernel().oc;
@@ -4247,31 +4332,48 @@ function uuidv() {
4247
4332
  crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> Number(c) / 4).toString(16)
4248
4333
  );
4249
4334
  }
4250
- const wrapString$1 = (str) => {
4335
+ function wrapString(str) {
4251
4336
  const oc = getKernel().oc;
4252
4337
  return new oc.TCollection_ExtendedString_2(str, true);
4253
- };
4254
- function parseSlice$1(hex, index) {
4338
+ }
4339
+ function parseSlice(hex, index) {
4255
4340
  return parseInt(hex.slice(index * 2, (index + 1) * 2), 16);
4256
4341
  }
4257
- function colorFromHex$1(hex) {
4342
+ function colorFromHex(hex) {
4258
4343
  let color = hex;
4259
4344
  if (color.indexOf("#") === 0) color = color.slice(1);
4260
4345
  if (color.length === 3) {
4261
4346
  color = color.replace(/([0-9a-f])/gi, "$1$1");
4262
4347
  }
4263
- return [parseSlice$1(color, 0), parseSlice$1(color, 1), parseSlice$1(color, 2)];
4348
+ return [parseSlice(color, 0), parseSlice(color, 1), parseSlice(color, 2)];
4264
4349
  }
4265
- const wrapColor$1 = (hex, alpha = 1) => {
4350
+ function wrapColor(hex, alpha = 1) {
4266
4351
  const oc = getKernel().oc;
4267
- const [r, g, b] = colorFromHex$1(hex);
4268
- return new oc.Quantity_ColorRGBA_5(r / 255, g / 255, b / 255, alpha);
4269
- };
4352
+ const [red, green, blue] = colorFromHex(hex);
4353
+ return new oc.Quantity_ColorRGBA_5(red / 255, green / 255, blue / 255, alpha);
4354
+ }
4355
+ function configureStepUnits(unit, modelUnit, r) {
4356
+ if (!unit && !modelUnit) return;
4357
+ const oc = getKernel().oc;
4358
+ r(new oc.STEPCAFControl_Writer_1());
4359
+ oc.Interface_Static.SetCVal("xstep.cascade.unit", (modelUnit || unit || "MM").toUpperCase());
4360
+ oc.Interface_Static.SetCVal("write.step.unit", (unit || modelUnit || "MM").toUpperCase());
4361
+ }
4362
+ function configureStepWriter(writer) {
4363
+ const oc = getKernel().oc;
4364
+ writer.SetColorMode(true);
4365
+ writer.SetLayerMode(true);
4366
+ writer.SetNameMode(true);
4367
+ oc.Interface_Static.SetIVal("write.surfacecurve.mode", true);
4368
+ oc.Interface_Static.SetIVal("write.precision.mode", 0);
4369
+ oc.Interface_Static.SetIVal("write.step.assembly", 2);
4370
+ oc.Interface_Static.SetIVal("write.step.schema", 5);
4371
+ }
4270
4372
  class AssemblyExporter extends WrappingObj {
4271
4373
  }
4272
4374
  function createAssembly(shapes = []) {
4273
4375
  const oc = getKernel().oc;
4274
- const doc = new oc.TDocStd_Document(wrapString$1("XmlOcaf"));
4376
+ const doc = new oc.TDocStd_Document(wrapString("XmlOcaf"));
4275
4377
  oc.XCAFDoc_ShapeTool.SetAutoNaming(false);
4276
4378
  const mainLabel = doc.Main();
4277
4379
  const tool = oc.XCAFDoc_DocumentTool.ShapeTool(mainLabel).get();
@@ -4279,10 +4381,10 @@ function createAssembly(shapes = []) {
4279
4381
  for (const { shape, name, color, alpha } of shapes) {
4280
4382
  const shapeNode = tool.NewShape();
4281
4383
  tool.SetShape(shapeNode, shape.wrapped);
4282
- oc.TDataStd_Name.Set_1(shapeNode, wrapString$1(name || uuidv()));
4384
+ oc.TDataStd_Name.Set_1(shapeNode, wrapString(name || uuidv()));
4283
4385
  ctool.SetColor_3(
4284
4386
  shapeNode,
4285
- wrapColor$1(color || "#f00", alpha ?? 1),
4387
+ wrapColor(color || "#f00", alpha ?? 1),
4286
4388
  oc.XCAFDoc_ColorType.XCAFDoc_ColorSurf
4287
4389
  );
4288
4390
  }
@@ -4293,38 +4395,32 @@ function exportSTEP(shapes = [], { unit, modelUnit } = {}) {
4293
4395
  const oc = getKernel().oc;
4294
4396
  const r = gcWithScope();
4295
4397
  const doc = createAssembly(shapes);
4296
- if (unit || modelUnit) {
4297
- r(new oc.STEPCAFControl_Writer_1());
4298
- oc.Interface_Static.SetCVal("xstep.cascade.unit", (modelUnit || unit || "MM").toUpperCase());
4299
- oc.Interface_Static.SetCVal("write.step.unit", (unit || modelUnit || "MM").toUpperCase());
4300
- }
4301
- const session = r(new oc.XSControl_WorkSession());
4302
- const writer = r(
4303
- new oc.STEPCAFControl_Writer_2(r(new oc.Handle_XSControl_WorkSession_2(session)), false)
4304
- );
4305
- writer.SetColorMode(true);
4306
- writer.SetLayerMode(true);
4307
- writer.SetNameMode(true);
4308
- oc.Interface_Static.SetIVal("write.surfacecurve.mode", true);
4309
- oc.Interface_Static.SetIVal("write.precision.mode", 0);
4310
- oc.Interface_Static.SetIVal("write.step.assembly", 2);
4311
- oc.Interface_Static.SetIVal("write.step.schema", 5);
4312
- const progress = r(new oc.Message_ProgressRange_1());
4313
- writer.Transfer_1(
4314
- new oc.Handle_TDocStd_Document_2(doc.wrapped),
4315
- oc.STEPControl_StepModelType.STEPControl_AsIs,
4316
- null,
4317
- progress
4318
- );
4319
- const filename = "export.step";
4320
- const done = writer.Write(filename);
4321
- if (done === oc.IFSelect_ReturnStatus.IFSelect_RetDone) {
4322
- const file = oc.FS.readFile("/" + filename);
4323
- oc.FS.unlink("/" + filename);
4324
- const blob = new Blob([file], { type: "application/STEP" });
4325
- return ok(blob);
4326
- } else {
4327
- return err(ioError("STEP_EXPORT_FAILED", "Failed to write STEP file"));
4398
+ try {
4399
+ configureStepUnits(unit, modelUnit, r);
4400
+ const session = r(new oc.XSControl_WorkSession());
4401
+ const writer = r(
4402
+ new oc.STEPCAFControl_Writer_2(r(new oc.Handle_XSControl_WorkSession_2(session)), false)
4403
+ );
4404
+ configureStepWriter(writer);
4405
+ const progress = r(new oc.Message_ProgressRange_1());
4406
+ writer.Transfer_1(
4407
+ new oc.Handle_TDocStd_Document_2(doc.wrapped),
4408
+ oc.STEPControl_StepModelType.STEPControl_AsIs,
4409
+ null,
4410
+ progress
4411
+ );
4412
+ const filename = uniqueIOFilename("_export", "step");
4413
+ const done = writer.Write(filename);
4414
+ if (done === oc.IFSelect_ReturnStatus.IFSelect_RetDone) {
4415
+ const file = oc.FS.readFile("/" + filename);
4416
+ oc.FS.unlink("/" + filename);
4417
+ const blob = new Blob([file], { type: "application/STEP" });
4418
+ return ok(blob);
4419
+ } else {
4420
+ return err(ioError("STEP_EXPORT_FAILED", "Failed to write STEP file"));
4421
+ }
4422
+ } finally {
4423
+ doc.delete();
4328
4424
  }
4329
4425
  }
4330
4426
  function fuseAllShapes(shapes, { optimisation = "none", simplify = false, strategy = "native" } = {}) {
@@ -4532,9 +4628,6 @@ const cartesianToPolar = ([x, y]) => {
4532
4628
  const theta = Math.atan2(y, x);
4533
4629
  return [r, theta];
4534
4630
  };
4535
- const determinant2x2 = (matrix) => {
4536
- return matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0];
4537
- };
4538
4631
  function deserializeCurve2D(data) {
4539
4632
  const oc = getKernel().oc;
4540
4633
  const handle = oc.GeomToolsWrapper.Read(data);
@@ -4831,23 +4924,24 @@ function* commonSegmentsIteration(intersector) {
4831
4924
  const nSegments = intersector.NbSegments();
4832
4925
  if (!nSegments) return;
4833
4926
  const oc = getKernel().oc;
4927
+ const r = gcWithScope();
4834
4928
  for (let i = 1; i <= nSegments; i++) {
4835
4929
  const h1 = new oc.Handle_Geom2d_Curve_1();
4836
- const h2 = new oc.Handle_Geom2d_Curve_1();
4930
+ const h2 = r(new oc.Handle_Geom2d_Curve_1());
4837
4931
  try {
4838
4932
  intersector.Segment(i, h1, h2);
4839
4933
  } catch {
4840
4934
  continue;
4841
4935
  }
4842
4936
  yield new Curve2D(h1);
4843
- h2.delete();
4844
4937
  }
4845
4938
  }
4846
4939
  const intersectCurves = (first, second, precision = 1e-9) => {
4847
4940
  if (first.boundingBox.isOut(second.boundingBox))
4848
4941
  return ok({ intersections: [], commonSegments: [], commonSegmentsPoints: [] });
4849
4942
  const oc = getKernel().oc;
4850
- const intersector = new oc.Geom2dAPI_InterCurveCurve_1();
4943
+ const r = gcWithScope();
4944
+ const intersector = r(new oc.Geom2dAPI_InterCurveCurve_1());
4851
4945
  let intersections;
4852
4946
  let commonSegments;
4853
4947
  try {
@@ -4856,8 +4950,6 @@ const intersectCurves = (first, second, precision = 1e-9) => {
4856
4950
  commonSegments = Array.from(commonSegmentsIteration(intersector));
4857
4951
  } catch (e) {
4858
4952
  return err(computationError("INTERSECTION_FAILED", "Intersections failed between curves", e));
4859
- } finally {
4860
- intersector.delete();
4861
4953
  }
4862
4954
  const segmentsAsPoints = commonSegments.filter((c) => samePoint$2(c.firstPoint, c.lastPoint, precision)).map((c) => c.firstPoint);
4863
4955
  if (segmentsAsPoints.length) {
@@ -4869,15 +4961,14 @@ const intersectCurves = (first, second, precision = 1e-9) => {
4869
4961
  };
4870
4962
  const selfIntersections = (curve, precision = 1e-9) => {
4871
4963
  const oc = getKernel().oc;
4872
- const intersector = new oc.Geom2dAPI_InterCurveCurve_1();
4964
+ const r = gcWithScope();
4965
+ const intersector = r(new oc.Geom2dAPI_InterCurveCurve_1());
4873
4966
  let intersections;
4874
4967
  try {
4875
4968
  intersector.Init_1(curve.wrapped, curve.wrapped, precision);
4876
4969
  intersections = Array.from(pointsIteration(intersector));
4877
4970
  } catch (e) {
4878
4971
  return err(computationError("SELF_INTERSECTION_FAILED", "Self intersection failed", e));
4879
- } finally {
4880
- intersector.delete();
4881
4972
  }
4882
4973
  return ok(intersections);
4883
4974
  };
@@ -5636,6 +5727,7 @@ function round5(v) {
5636
5727
  const fromPnt = (pnt2) => `${round2(pnt2.X())} ${round2(pnt2.Y())}`;
5637
5728
  const adaptedCurveToPathElem = (adaptor, lastPoint) => {
5638
5729
  const oc = getKernel().oc;
5730
+ const r = gcWithScope();
5639
5731
  const curveType = unwrap(findCurveType(adaptor.GetType()));
5640
5732
  const [endX, endY] = lastPoint;
5641
5733
  const endpoint = `${round5(endX)} ${round5(endY)}`;
@@ -5643,22 +5735,26 @@ const adaptedCurveToPathElem = (adaptor, lastPoint) => {
5643
5735
  return `L ${endpoint}`;
5644
5736
  }
5645
5737
  if (curveType === "BEZIER_CURVE") {
5646
- const curve = adaptor.Bezier().get();
5738
+ const bezierHandle = r(adaptor.Bezier());
5739
+ const curve = bezierHandle.get();
5647
5740
  const deg = curve.Degree();
5648
5741
  if (deg === 1) {
5649
5742
  return `L ${endpoint}`;
5650
5743
  }
5651
5744
  if (deg === 2) {
5652
- return `Q ${fromPnt(curve.Pole(2))} ${endpoint}`;
5745
+ const pole2 = r(curve.Pole(2));
5746
+ return `Q ${fromPnt(pole2)} ${endpoint}`;
5653
5747
  }
5654
5748
  if (deg === 3) {
5655
- const p1 = fromPnt(curve.Pole(2));
5656
- const p2 = fromPnt(curve.Pole(3));
5749
+ const pole2 = r(curve.Pole(2));
5750
+ const pole3 = r(curve.Pole(3));
5751
+ const p1 = fromPnt(pole2);
5752
+ const p2 = fromPnt(pole3);
5657
5753
  return `C ${p1} ${p2} ${endpoint}`;
5658
5754
  }
5659
5755
  }
5660
5756
  if (curveType === "CIRCLE") {
5661
- const curve = adaptor.Circle();
5757
+ const curve = r(adaptor.Circle());
5662
5758
  const radius = curve.Radius();
5663
5759
  const p1 = adaptor.FirstParameter();
5664
5760
  const p2 = adaptor.LastParameter();
@@ -5667,16 +5763,17 @@ const adaptedCurveToPathElem = (adaptor, lastPoint) => {
5667
5763
  return `A ${radius} ${radius} 0 ${Math.abs(paramAngle) > 180 ? "1" : "0"} ${curve.IsDirect() ? "1" : "0"} ${end}`;
5668
5764
  }
5669
5765
  if (curveType === "ELLIPSE") {
5670
- const curve = adaptor.Ellipse();
5766
+ const curve = r(adaptor.Ellipse());
5671
5767
  const rx = curve.MajorRadius();
5672
5768
  const ry = curve.MinorRadius();
5673
5769
  const p1 = adaptor.FirstParameter();
5674
5770
  const p2 = adaptor.LastParameter();
5675
5771
  const paramAngle = (p2 - p1) * RAD2DEG;
5676
5772
  const end = paramAngle !== 360 ? endpoint : `${round5(endX)} ${round5(endY + 1e-4)}`;
5677
- const dir0 = new oc.gp_Dir2d_1();
5678
- const angle = 180 - curve.XAxis().Direction().Angle(dir0) * RAD2DEG;
5679
- dir0.delete();
5773
+ const dir0 = r(new oc.gp_Dir2d_1());
5774
+ const xAxis = r(curve.XAxis());
5775
+ const xDir = r(xAxis.Direction());
5776
+ const angle = 180 - xDir.Angle(dir0) * RAD2DEG;
5680
5777
  return `A ${round5(rx)} ${round5(ry)} ${round5(angle)} ${Math.abs(paramAngle) > 180 ? "1" : "0"} ${curve.IsDirect() ? "1" : "0"} ${end}`;
5681
5778
  }
5682
5779
  bug("adaptedCurveToPathElem", `Unsupported curve type: ${curveType}`);
@@ -5694,8 +5791,9 @@ function curvesAsEdgesOnPlane(curves, plane) {
5694
5791
  const ax = r(makeAx2(plane.origin, plane.zDir, plane.xDir));
5695
5792
  const oc = getKernel().oc;
5696
5793
  const edges = curves.map((curve) => {
5697
- const curve3d = oc.GeomLib.To3d(ax, curve.wrapped);
5698
- return new Edge(new oc.BRepBuilderAPI_MakeEdge_24(curve3d).Edge());
5794
+ const curve3d = r(oc.GeomLib.To3d(ax, curve.wrapped));
5795
+ const edgeBuilder = r(new oc.BRepBuilderAPI_MakeEdge_24(curve3d));
5796
+ return new Edge(edgeBuilder.Edge());
5699
5797
  });
5700
5798
  gc();
5701
5799
  return edges;
@@ -6107,12 +6205,15 @@ class FaceFinder extends Finder3d {
6107
6205
  * @category Filter
6108
6206
  */
6109
6207
  parallelTo(plane) {
6208
+ const [r, gc] = localGC();
6110
6209
  if (typeof plane === "string") return this.atAngleWith(PLANE_TO_DIR[plane]);
6111
6210
  if (typeof plane !== "string" && plane instanceof Plane)
6112
6211
  return this.atAngleWith(plane.zDir);
6113
6212
  if (typeof plane !== "string" && "normalAt" in plane) {
6114
- const normal = plane.normalAt();
6115
- return this.atAngleWith(normal);
6213
+ const normal = r(plane.normalAt());
6214
+ const normalPoint = [normal.x, normal.y, normal.z];
6215
+ gc();
6216
+ return this.atAngleWith(normalPoint);
6116
6217
  }
6117
6218
  return this;
6118
6219
  }
@@ -6138,13 +6239,22 @@ class FaceFinder extends Finder3d {
6138
6239
  inPlane(inputPlane, origin) {
6139
6240
  const plane = inputPlane instanceof Plane ? makePlane(inputPlane) : makePlane(inputPlane, origin);
6140
6241
  this.parallelTo(plane);
6141
- const centerInPlane = ({ element }) => element.center.equals(element.center.projectToPlane(plane));
6242
+ const centerInPlane = ({ element }) => {
6243
+ const [r, gc] = localGC();
6244
+ const center = element.center;
6245
+ const projected = r(center.projectToPlane(plane));
6246
+ const result = center.equals(projected);
6247
+ gc();
6248
+ return result;
6249
+ };
6142
6250
  this.filters.push(centerInPlane);
6143
6251
  return this;
6144
6252
  }
6145
6253
  shouldKeep(element) {
6146
- const normal = element.normalAt();
6254
+ const [r, gc] = localGC();
6255
+ const normal = r(element.normalAt());
6147
6256
  const shouldKeep = this.filters.every((filter) => filter({ normal, element }));
6257
+ gc();
6148
6258
  return shouldKeep;
6149
6259
  }
6150
6260
  applyFilter(shape) {
@@ -6155,7 +6265,7 @@ class FaceFinder extends Finder3d {
6155
6265
  }
6156
6266
  }
6157
6267
  function getSingleFace(f, shape) {
6158
- if ("normalAt" in f && typeof f.normalAt === "function") return ok(f);
6268
+ if (f instanceof Face) return ok(f);
6159
6269
  const finder = f instanceof FaceFinder ? f : f(new FaceFinder());
6160
6270
  return finder.find(shape, { unique: true });
6161
6271
  }
@@ -6173,6 +6283,9 @@ class Blueprint {
6173
6283
  _orientation;
6174
6284
  _guessedOrientation;
6175
6285
  constructor(curves) {
6286
+ if (curves.length === 0) {
6287
+ throw new Error("Blueprint requires at least one curve");
6288
+ }
6176
6289
  this.curves = curves;
6177
6290
  this._boundingBox = null;
6178
6291
  this._orientation = null;
@@ -6185,7 +6298,7 @@ class Blueprint {
6185
6298
  if (this._boundingBox) this._boundingBox.delete();
6186
6299
  }
6187
6300
  clone() {
6188
- return new Blueprint(this.curves);
6301
+ return new Blueprint(this.curves.map((c) => c.clone()));
6189
6302
  }
6190
6303
  get repr() {
6191
6304
  return ["Blueprint", ...this.curves.map((c) => c.repr)].join("\n");
@@ -6319,45 +6432,54 @@ class Blueprint {
6319
6432
  if (!this.boundingBox.containsPoint(point)) return false;
6320
6433
  const oc = getKernel().oc;
6321
6434
  const intersector = new oc.Geom2dAPI_InterCurveCurve_1();
6322
- const segment = make2dSegmentCurve(point, this.boundingBox.outsidePoint());
6323
- let crossCounts = 0;
6324
- const onCurve = this.curves.find((c) => c.isOnCurve(point));
6325
- if (onCurve) return false;
6326
- this.curves.forEach((c) => {
6327
- if (c.boundingBox.isOut(segment.boundingBox)) return;
6328
- intersector.Init_1(segment.wrapped, c.wrapped, 1e-9);
6329
- crossCounts += Number(intersector.NbPoints());
6330
- });
6331
- intersector.delete();
6332
- return !!(crossCounts % 2);
6435
+ try {
6436
+ const segment = make2dSegmentCurve(point, this.boundingBox.outsidePoint());
6437
+ let crossCounts = 0;
6438
+ const onCurve = this.curves.find((c) => c.isOnCurve(point));
6439
+ if (onCurve) return false;
6440
+ this.curves.forEach((c) => {
6441
+ if (c.boundingBox.isOut(segment.boundingBox)) return;
6442
+ intersector.Init_1(segment.wrapped, c.wrapped, 1e-9);
6443
+ crossCounts += Number(intersector.NbPoints());
6444
+ });
6445
+ return !!(crossCounts % 2);
6446
+ } finally {
6447
+ intersector.delete();
6448
+ }
6333
6449
  }
6334
6450
  isClosed() {
6335
6451
  return samePoint$2(this.firstPoint, this.lastPoint);
6336
6452
  }
6337
6453
  intersects(other) {
6454
+ if (this.boundingBox.isOut(other.boundingBox)) return false;
6338
6455
  const oc = getKernel().oc;
6339
6456
  const intersector = new oc.Geom2dAPI_InterCurveCurve_1();
6340
- if (this.boundingBox.isOut(other.boundingBox)) return false;
6341
- for (const myCurve of this.curves) {
6342
- for (const otherCurve of other.curves) {
6343
- if (myCurve.boundingBox.isOut(otherCurve.boundingBox)) continue;
6344
- intersector.Init_1(myCurve.wrapped, otherCurve.wrapped, 1e-9);
6345
- if (intersector.NbPoints() || intersector.NbSegments()) return true;
6457
+ try {
6458
+ for (const myCurve of this.curves) {
6459
+ for (const otherCurve of other.curves) {
6460
+ if (myCurve.boundingBox.isOut(otherCurve.boundingBox)) continue;
6461
+ intersector.Init_1(myCurve.wrapped, otherCurve.wrapped, 1e-9);
6462
+ if (intersector.NbPoints() || intersector.NbSegments()) return true;
6463
+ }
6346
6464
  }
6465
+ return false;
6466
+ } finally {
6467
+ intersector.delete();
6347
6468
  }
6348
- intersector.delete();
6349
- return false;
6350
6469
  }
6351
6470
  }
6352
6471
  class CompoundBlueprint {
6353
6472
  blueprints;
6354
6473
  _boundingBox;
6355
6474
  constructor(blueprints) {
6475
+ if (blueprints.length === 0) {
6476
+ throw new Error("CompoundBlueprint requires at least one blueprint (the outer boundary)");
6477
+ }
6356
6478
  this.blueprints = blueprints;
6357
6479
  this._boundingBox = null;
6358
6480
  }
6359
6481
  clone() {
6360
- return new CompoundBlueprint(this.blueprints);
6482
+ return new CompoundBlueprint(this.blueprints.map((bp) => bp.clone()));
6361
6483
  }
6362
6484
  get boundingBox() {
6363
6485
  if (!this._boundingBox) {
@@ -6433,7 +6555,7 @@ class Blueprints {
6433
6555
  return ["Blueprints", ...this.blueprints.map((b) => b.repr)].join("\n");
6434
6556
  }
6435
6557
  clone() {
6436
- return new Blueprints(this.blueprints);
6558
+ return new Blueprints(this.blueprints.map((bp) => bp.clone()));
6437
6559
  }
6438
6560
  get boundingBox() {
6439
6561
  if (!this._boundingBox) {
@@ -6591,8 +6713,7 @@ class Sketch {
6591
6713
  }
6592
6714
  set baseFace(newFace) {
6593
6715
  if (this._baseFace) this._baseFace.delete();
6594
- if (!newFace) this._baseFace = newFace;
6595
- else this._baseFace = newFace.clone();
6716
+ this._baseFace = newFace ? newFace.clone() : newFace;
6596
6717
  }
6597
6718
  delete() {
6598
6719
  this.wire.delete();
@@ -7180,7 +7301,7 @@ class BaseSketcher2d {
7180
7301
  (c) => new Curve2D(c.innerCurve.Mirrored_2(mirrorAxis))
7181
7302
  );
7182
7303
  mirroredCurves.reverse();
7183
- mirroredCurves.map((c) => {
7304
+ mirroredCurves.forEach((c) => {
7184
7305
  c.reverse();
7185
7306
  });
7186
7307
  this.pendingCurves.push(...mirroredCurves);
@@ -7365,16 +7486,22 @@ const rotateToStartAtSegment = (curves, segment) => {
7365
7486
  const end = curves.slice(startIndex);
7366
7487
  return end.concat(start);
7367
7488
  };
7489
+ const hashPoint = (p) => `${p[0].toFixed(6)},${p[1].toFixed(6)}`;
7490
+ const hashSegment = (first, last) => {
7491
+ const h1 = hashPoint(first);
7492
+ const h2 = hashPoint(last);
7493
+ return h1 < h2 ? `${h1}|${h2}` : `${h2}|${h1}`;
7494
+ };
7368
7495
  function* createSegmentOnPoints(curves, allIntersections, allCommonSegments) {
7496
+ const intersectionSet = new Set(allIntersections.map(hashPoint));
7497
+ const commonSegmentSet = new Set(
7498
+ allCommonSegments.map((seg) => hashSegment(seg.firstPoint, seg.lastPoint))
7499
+ );
7369
7500
  const endsAtIntersection = (curve) => {
7370
- return !!allIntersections.find((intersection) => {
7371
- return samePoint$1(intersection, curve.lastPoint);
7372
- });
7501
+ return intersectionSet.has(hashPoint(curve.lastPoint));
7373
7502
  };
7374
7503
  const isCommonSegment = (curve) => {
7375
- return !!allCommonSegments.find((segment) => {
7376
- return samePoint$1(segment.firstPoint, curve.firstPoint) && samePoint$1(segment.lastPoint, curve.lastPoint) || samePoint$1(segment.firstPoint, curve.lastPoint) && samePoint$1(segment.lastPoint, curve.firstPoint);
7377
- });
7504
+ return commonSegmentSet.has(hashSegment(curve.firstPoint, curve.lastPoint));
7378
7505
  };
7379
7506
  let currentCurves = [];
7380
7507
  for (const curve of curves) {
@@ -7881,16 +8008,30 @@ function intersect2D(first, second) {
7881
8008
  }
7882
8009
  if (first instanceof CompoundBlueprint) {
7883
8010
  const wrapper = first.blueprints[0];
7884
- const cut = first.blueprints[1];
7885
- return cut2D(intersect2D(wrapper, second), cut);
8011
+ const cuts = first.blueprints.slice(1);
8012
+ if (cuts.length === 0) {
8013
+ return intersect2D(wrapper, second);
8014
+ }
8015
+ let result = intersect2D(wrapper, second);
8016
+ for (const cut of cuts) {
8017
+ result = cut2D(result, cut);
8018
+ }
8019
+ return result;
7886
8020
  }
7887
8021
  if (second instanceof Blueprints) {
7888
8022
  return mergeNonIntersecting(second.blueprints.map((bp) => intersect2D(first, bp)));
7889
8023
  }
7890
8024
  if (second instanceof CompoundBlueprint) {
7891
8025
  const wrapper = second.blueprints[0];
7892
- const cut = second.blueprints[1];
7893
- return cut2D(intersect2D(wrapper, first), cut);
8026
+ const cuts = second.blueprints.slice(1);
8027
+ if (cuts.length === 0) {
8028
+ return intersect2D(wrapper, first);
8029
+ }
8030
+ let result = intersect2D(wrapper, first);
8031
+ for (const cut of cuts) {
8032
+ result = cut2D(result, cut);
8033
+ }
8034
+ return result;
7894
8035
  }
7895
8036
  bug("intersect2D", "Unhandled Shape2D combination");
7896
8037
  }
@@ -8011,12 +8152,15 @@ class EdgeFinder extends Finder3d {
8011
8152
  * @category Filter
8012
8153
  */
8013
8154
  parallelTo(plane) {
8155
+ const [r, gc] = localGC();
8014
8156
  if (typeof plane === "string") return this.atAngleWith(PLANE_TO_DIR[plane], 90);
8015
8157
  if (typeof plane !== "string" && plane instanceof Plane)
8016
8158
  return this.atAngleWith(plane.zDir, 90);
8017
8159
  if (typeof plane !== "string" && "normalAt" in plane) {
8018
- const normal = plane.normalAt();
8019
- return this.atAngleWith(normal, 90);
8160
+ const normal = r(plane.normalAt());
8161
+ const normalPoint = [normal.x, normal.y, normal.z];
8162
+ gc();
8163
+ return this.atAngleWith(normalPoint, 90);
8020
8164
  }
8021
8165
  return this;
8022
8166
  }
@@ -8031,17 +8175,29 @@ class EdgeFinder extends Finder3d {
8031
8175
  inPlane(inputPlane, origin) {
8032
8176
  const plane = inputPlane instanceof Plane ? makePlane(inputPlane) : makePlane(inputPlane, origin);
8033
8177
  this.parallelTo(plane);
8034
- const firstPointInPlane = ({ element }) => element.startPoint.equals(element.startPoint.projectToPlane(plane));
8178
+ const firstPointInPlane = ({ element }) => {
8179
+ const [r, gc] = localGC();
8180
+ const startPoint = element.startPoint;
8181
+ const projected = r(startPoint.projectToPlane(plane));
8182
+ const result = startPoint.equals(projected);
8183
+ gc();
8184
+ return result;
8185
+ };
8035
8186
  this.filters.push(firstPointInPlane);
8036
8187
  return this;
8037
8188
  }
8038
8189
  shouldKeep(element) {
8190
+ const [r, gc] = localGC();
8039
8191
  let normal = null;
8192
+ let tangent = null;
8040
8193
  try {
8041
- normal = element.tangentAt().normalized();
8194
+ tangent = r(element.tangentAt());
8195
+ normal = r(tangent.normalized());
8042
8196
  } catch {
8043
8197
  }
8044
- return this.filters.every((filter) => filter({ normal, element }));
8198
+ const result = this.filters.every((filter) => filter({ normal, element }));
8199
+ gc();
8200
+ return result;
8045
8201
  }
8046
8202
  applyFilter(shape) {
8047
8203
  return shape.edges.filter((edge) => {
@@ -8207,13 +8363,25 @@ function measureShapeVolumeProperties(shape) {
8207
8363
  return new VolumePhysicalProperties(properties);
8208
8364
  }
8209
8365
  function measureVolume$1(shape) {
8210
- return measureShapeVolumeProperties(shape).volume;
8366
+ const [r, gc] = localGC();
8367
+ const props = r(measureShapeVolumeProperties(shape));
8368
+ const v = props.volume;
8369
+ gc();
8370
+ return v;
8211
8371
  }
8212
8372
  function measureArea$1(shape) {
8213
- return measureShapeSurfaceProperties(shape).area;
8373
+ const [r, gc] = localGC();
8374
+ const props = r(measureShapeSurfaceProperties(shape));
8375
+ const a = props.area;
8376
+ gc();
8377
+ return a;
8214
8378
  }
8215
8379
  function measureLength$1(shape) {
8216
- return measureShapeLinearProperties(shape).length;
8380
+ const [r, gc] = localGC();
8381
+ const props = r(measureShapeLinearProperties(shape));
8382
+ const l = props.length;
8383
+ gc();
8384
+ return l;
8217
8385
  }
8218
8386
  class DistanceTool extends WrappingObj {
8219
8387
  constructor() {
@@ -8221,17 +8389,22 @@ class DistanceTool extends WrappingObj {
8221
8389
  super(new oc.BRepExtrema_DistShapeShape_1());
8222
8390
  }
8223
8391
  distanceBetween(shape1, shape2) {
8392
+ const [r, gc] = localGC();
8224
8393
  this.wrapped.LoadS1(shape1.wrapped);
8225
8394
  this.wrapped.LoadS2(shape2.wrapped);
8226
8395
  const oc = getKernel().oc;
8227
- const progress = new oc.Message_ProgressRange_1();
8396
+ const progress = r(new oc.Message_ProgressRange_1());
8228
8397
  this.wrapped.Perform(progress);
8229
- progress.delete();
8398
+ gc();
8230
8399
  return this.wrapped.Value();
8231
8400
  }
8232
8401
  }
8233
8402
  function measureDistanceBetween(shape1, shape2) {
8234
- return new DistanceTool().distanceBetween(shape1, shape2);
8403
+ const [r, gc] = localGC();
8404
+ const tool = r(new DistanceTool());
8405
+ const dist = tool.distanceBetween(shape1, shape2);
8406
+ gc();
8407
+ return dist;
8235
8408
  }
8236
8409
  class DistanceQuery extends WrappingObj {
8237
8410
  constructor(shape) {
@@ -8240,19 +8413,19 @@ class DistanceQuery extends WrappingObj {
8240
8413
  this.wrapped.LoadS1(shape.wrapped);
8241
8414
  }
8242
8415
  distanceTo(shape) {
8416
+ const [r, gc] = localGC();
8243
8417
  this.wrapped.LoadS2(shape.wrapped);
8244
8418
  const oc = getKernel().oc;
8245
- const progress = new oc.Message_ProgressRange_1();
8419
+ const progress = r(new oc.Message_ProgressRange_1());
8246
8420
  this.wrapped.Perform(progress);
8247
- progress.delete();
8421
+ gc();
8248
8422
  return this.wrapped.Value();
8249
8423
  }
8250
8424
  }
8251
- const uniqueId$1 = () => Date.now().toString(36) + Math.random().toString(36).substring(2);
8252
8425
  async function importSTEP$1(STEPBlob) {
8253
8426
  const oc = getKernel().oc;
8254
8427
  const [r, gc] = localGC();
8255
- const fileName = uniqueId$1();
8428
+ const fileName = uniqueId();
8256
8429
  try {
8257
8430
  const bufferView = new Uint8Array(await STEPBlob.arrayBuffer());
8258
8431
  oc.FS.writeFile(`/${fileName}`, bufferView);
@@ -8274,7 +8447,7 @@ async function importSTEP$1(STEPBlob) {
8274
8447
  async function importSTL$1(STLBlob) {
8275
8448
  const oc = getKernel().oc;
8276
8449
  const [r, gc] = localGC();
8277
- const fileName = uniqueId$1();
8450
+ const fileName = uniqueId();
8278
8451
  try {
8279
8452
  const bufferView = new Uint8Array(await STLBlob.arrayBuffer());
8280
8453
  oc.FS.writeFile(`/${fileName}`, bufferView);
@@ -8315,6 +8488,10 @@ class Sketcher {
8315
8488
  this.plane.delete();
8316
8489
  this.pointer.delete();
8317
8490
  this.firstPoint.delete();
8491
+ for (const edge of this.pendingEdges) {
8492
+ edge.delete();
8493
+ }
8494
+ this.pendingEdges = [];
8318
8495
  }
8319
8496
  _updatePointer(newPointer) {
8320
8497
  this.pointer.delete();
@@ -8389,12 +8566,16 @@ class Sketcher {
8389
8566
  );
8390
8567
  }
8391
8568
  tangentArcTo(end) {
8569
+ const [r, gc] = localGC();
8392
8570
  const endPoint = this.plane.toWorldCoords(end);
8393
- const previousEdge = this.pendingEdges[this.pendingEdges.length - 1];
8394
- this.pendingEdges.push(
8395
- makeTangentArc(previousEdge.endPoint, previousEdge.tangentAt(1), endPoint)
8396
- );
8571
+ const previousEdge = this.pendingEdges.length ? this.pendingEdges[this.pendingEdges.length - 1] : null;
8572
+ if (!previousEdge)
8573
+ bug("Sketcher.tangentArcTo", "You need a previous edge to create a tangent arc");
8574
+ const prevEnd = r(previousEdge.endPoint);
8575
+ const prevTangent = r(previousEdge.tangentAt(1));
8576
+ this.pendingEdges.push(makeTangentArc(prevEnd, prevTangent, endPoint));
8397
8577
  this._updatePointer(endPoint);
8578
+ gc();
8398
8579
  return this;
8399
8580
  }
8400
8581
  tangentArc(xDist, yDist) {
@@ -8402,16 +8583,18 @@ class Sketcher {
8402
8583
  return this.tangentArcTo([xDist + pointer.x, yDist + pointer.y]);
8403
8584
  }
8404
8585
  sagittaArcTo(end, sagitta) {
8586
+ const [r, gc] = localGC();
8405
8587
  const startPoint = this.pointer;
8406
8588
  const endPoint = this.plane.toWorldCoords(end);
8407
- let p = endPoint.add(startPoint);
8408
- const midPoint = p.multiply(0.5);
8409
- p = endPoint.sub(startPoint);
8410
- const sagDirection = p.cross(this.plane.zDir).normalized();
8411
- const sagVector = sagDirection.multiply(sagitta);
8412
- const sagPoint = midPoint.add(sagVector);
8589
+ const sum = r(endPoint.add(startPoint));
8590
+ const midPoint = r(sum.multiply(0.5));
8591
+ const diff = r(endPoint.sub(startPoint));
8592
+ const sagDirection = r(r(diff.cross(this.plane.zDir)).normalized());
8593
+ const sagVector = r(sagDirection.multiply(sagitta));
8594
+ const sagPoint = r(midPoint.add(sagVector));
8413
8595
  this.pendingEdges.push(makeThreePointArc(this.pointer, sagPoint, endPoint));
8414
8596
  this._updatePointer(endPoint);
8597
+ gc();
8415
8598
  return this;
8416
8599
  }
8417
8600
  sagittaArc(xDist, yDist, sagitta) {
@@ -8569,10 +8752,13 @@ class Sketcher {
8569
8752
  return this.smoothSplineTo([xDist + pointer.x, yDist + pointer.y], splineConfig);
8570
8753
  }
8571
8754
  _mirrorWireOnStartEnd(wire) {
8572
- const startToEndVector = this.pointer.sub(this.firstPoint).normalize();
8573
- const normal = startToEndVector.cross(this.plane.zDir);
8755
+ const [r, gc] = localGC();
8756
+ const diff = r(this.pointer.sub(this.firstPoint));
8757
+ const startToEndVector = r(diff.normalize());
8758
+ const normal = r(startToEndVector.cross(this.plane.zDir));
8574
8759
  const mirroredWire = wire.clone().mirror(normal, this.pointer);
8575
8760
  const combinedWire = unwrap(assembleWire$1([wire, mirroredWire]));
8761
+ gc();
8576
8762
  return combinedWire;
8577
8763
  }
8578
8764
  buildWire() {
@@ -8607,32 +8793,19 @@ class Sketcher {
8607
8793
  }
8608
8794
  const guessFaceFromWires = (wires) => {
8609
8795
  const oc = getKernel().oc;
8610
- const faceBuilder = new oc.BRepOffsetAPI_MakeFilling(
8611
- 3,
8612
- 15,
8613
- 2,
8614
- false,
8615
- 1e-5,
8616
- 1e-4,
8617
- 0.01,
8618
- 0.1,
8619
- 8,
8620
- 9
8796
+ const [r, gc] = localGC();
8797
+ const faceBuilder = r(
8798
+ new oc.BRepOffsetAPI_MakeFilling(3, 15, 2, false, 1e-5, 1e-4, 0.01, 0.1, 8, 9)
8621
8799
  );
8622
8800
  wires.forEach((wire, wireIndex) => {
8623
8801
  wire.edges.forEach((edge) => {
8624
- faceBuilder.Add_1(
8625
- edge.wrapped,
8626
- oc.GeomAbs_Shape.GeomAbs_C0,
8627
- wireIndex === 0
8628
- );
8802
+ faceBuilder.Add_1(edge.wrapped, oc.GeomAbs_Shape.GeomAbs_C0, wireIndex === 0);
8629
8803
  });
8630
8804
  });
8631
- const progress = new oc.Message_ProgressRange_1();
8805
+ const progress = r(new oc.Message_ProgressRange_1());
8632
8806
  faceBuilder.Build(progress);
8633
- progress.delete();
8634
8807
  const newFace = unwrap(cast(faceBuilder.Shape()));
8635
- faceBuilder.delete();
8808
+ gc();
8636
8809
  if (!(newFace instanceof Face)) {
8637
8810
  bug("guessFaceFromWires", "Failed to create a face");
8638
8811
  }
@@ -8640,9 +8813,10 @@ const guessFaceFromWires = (wires) => {
8640
8813
  };
8641
8814
  const fixWire = (wire, baseFace) => {
8642
8815
  const oc = getKernel().oc;
8643
- const wireFixer = new oc.ShapeFix_Wire_2(wire.wrapped, baseFace.wrapped, 1e-9);
8816
+ const [r, gc] = localGC();
8817
+ const wireFixer = r(new oc.ShapeFix_Wire_2(wire.wrapped, baseFace.wrapped, 1e-9));
8644
8818
  wireFixer.FixEdgeCurves();
8645
- wireFixer.delete();
8819
+ gc();
8646
8820
  return wire;
8647
8821
  };
8648
8822
  const faceFromWires = (wires) => {
@@ -8710,41 +8884,39 @@ class CompoundSketch {
8710
8884
  twistAngle,
8711
8885
  origin
8712
8886
  } = {}) {
8713
- const rawVec = new Vector(extrusionDirection || this.outerSketch.defaultDirection);
8714
- const normVec = rawVec.normalized();
8715
- const extrusionVec = normVec.multiply(extrusionDistance);
8716
- rawVec.delete();
8717
- normVec.delete();
8718
- try {
8719
- if (extrusionProfile && !twistAngle) {
8720
- return solidFromShellGenerator(
8721
- this.sketches,
8722
- (sketch) => complexExtrude$1(
8723
- sketch.wire,
8724
- origin || this.outerSketch.defaultOrigin,
8725
- extrusionVec,
8726
- extrusionProfile,
8727
- true
8728
- )
8729
- );
8730
- }
8731
- if (twistAngle) {
8732
- return solidFromShellGenerator(
8733
- this.sketches,
8734
- (sketch) => twistExtrude$1(
8735
- sketch.wire,
8736
- twistAngle,
8737
- origin || this.outerSketch.defaultOrigin,
8738
- extrusionVec,
8739
- extrusionProfile,
8740
- true
8741
- )
8742
- );
8743
- }
8744
- return basicFaceExtrusion(this.face(), extrusionVec);
8745
- } finally {
8746
- extrusionVec.delete();
8887
+ const [r, gc] = localGC();
8888
+ const rawVec = r(new Vector(extrusionDirection || this.outerSketch.defaultDirection));
8889
+ const normVec = r(rawVec.normalized());
8890
+ const extrusionVec = r(normVec.multiply(extrusionDistance));
8891
+ let result;
8892
+ if (extrusionProfile && !twistAngle) {
8893
+ result = solidFromShellGenerator(
8894
+ this.sketches,
8895
+ (sketch) => complexExtrude$1(
8896
+ sketch.wire,
8897
+ origin || this.outerSketch.defaultOrigin,
8898
+ extrusionVec,
8899
+ extrusionProfile,
8900
+ true
8901
+ )
8902
+ );
8903
+ } else if (twistAngle) {
8904
+ result = solidFromShellGenerator(
8905
+ this.sketches,
8906
+ (sketch) => twistExtrude$1(
8907
+ sketch.wire,
8908
+ twistAngle,
8909
+ origin || this.outerSketch.defaultOrigin,
8910
+ extrusionVec,
8911
+ extrusionProfile,
8912
+ true
8913
+ )
8914
+ );
8915
+ } else {
8916
+ result = basicFaceExtrusion(this.face(), extrusionVec);
8747
8917
  }
8918
+ gc();
8919
+ return result;
8748
8920
  }
8749
8921
  /**
8750
8922
  * Revolves the drawing on an axis (defined by its direction and an origin
@@ -8913,69 +9085,16 @@ const makeBaseBox = (xLength, yLength, zLength) => {
8913
9085
  };
8914
9086
  const samePoint = (x, y) => samePoint$2(x, y, PRECISION_OFFSET * 10);
8915
9087
  const getIntersectionPoint = (line1Start, line1End, line2Start, line2End) => {
8916
- const denom = determinant2x2([
8917
- [
8918
- determinant2x2([
8919
- [line1Start[0], 1],
8920
- [line1End[0], 1]
8921
- ]),
8922
- determinant2x2([
8923
- [line1Start[1], 1],
8924
- [line1End[1], 1]
8925
- ])
8926
- ],
8927
- [
8928
- determinant2x2([
8929
- [line2Start[0], 1],
8930
- [line2End[0], 1]
8931
- ]),
8932
- determinant2x2([
8933
- [line2Start[1], 1],
8934
- [line2End[1], 1]
8935
- ])
8936
- ]
8937
- ]);
9088
+ const dx1 = line1Start[0] - line1End[0];
9089
+ const dy1 = line1Start[1] - line1End[1];
9090
+ const dx2 = line2Start[0] - line2End[0];
9091
+ const dy2 = line2Start[1] - line2End[1];
9092
+ const denom = dx1 * dy2 - dy1 * dx2;
8938
9093
  if (Math.abs(denom) < 1e-12) return null;
8939
- const cross1 = determinant2x2([
8940
- [line1Start[0], line1Start[1]],
8941
- [line1End[0], line1End[1]]
8942
- ]);
8943
- const cross2 = determinant2x2([
8944
- [line2Start[0], line2Start[1]],
8945
- [line2End[0], line2End[1]]
8946
- ]);
8947
- const x = determinant2x2([
8948
- [
8949
- cross1,
8950
- determinant2x2([
8951
- [line1Start[0], 1],
8952
- [line1End[0], 1]
8953
- ])
8954
- ],
8955
- [
8956
- cross2,
8957
- determinant2x2([
8958
- [line2Start[0], 1],
8959
- [line2End[0], 1]
8960
- ])
8961
- ]
8962
- ]) / denom;
8963
- const y = determinant2x2([
8964
- [
8965
- cross1,
8966
- determinant2x2([
8967
- [line1Start[1], 1],
8968
- [line1End[1], 1]
8969
- ])
8970
- ],
8971
- [
8972
- cross2,
8973
- determinant2x2([
8974
- [line2Start[1], 1],
8975
- [line2End[1], 1]
8976
- ])
8977
- ]
8978
- ]) / denom;
9094
+ const cross1 = line1Start[0] * line1End[1] - line1Start[1] * line1End[0];
9095
+ const cross2 = line2Start[0] * line2End[1] - line2Start[1] * line2End[0];
9096
+ const x = (cross1 * dx2 - cross2 * dx1) / denom;
9097
+ const y = (cross1 * dy2 - cross2 * dy1) / denom;
8979
9098
  return [x, y];
8980
9099
  };
8981
9100
  function joinRound(appendCurve, previousLastPoint, firstPoint, previousCurve, _curve) {
@@ -9028,7 +9147,6 @@ function rawOffsets(blueprint, offset2, offsetConfig = {}) {
9028
9147
  let savedLastCurve = null;
9029
9148
  let previousCurve = offsetCurves.at(-1);
9030
9149
  if (!previousCurve) return [];
9031
- if (offsettedArray.length === 1) return offsettedArray;
9032
9150
  function appendCurve(curve) {
9033
9151
  if (curve instanceof Curve2D) {
9034
9152
  offsettedArray.push(curve);
@@ -9231,6 +9349,17 @@ function fillet2D(shape, radius, finder) {
9231
9349
  function chamfer2D(shape, radius, finder) {
9232
9350
  return modifyCorner2D(chamferCurves, shape, radius, finder);
9233
9351
  }
9352
+ function wrapSketchData(data) {
9353
+ const opts = {};
9354
+ if (data.defaultOrigin) opts.defaultOrigin = data.defaultOrigin;
9355
+ if (data.defaultDirection) opts.defaultDirection = data.defaultDirection;
9356
+ const sketch = new Sketch(data.wire, opts);
9357
+ if (data.baseFace) sketch.baseFace = data.baseFace;
9358
+ return sketch;
9359
+ }
9360
+ function wrapSketchDataArray(dataArr) {
9361
+ return new CompoundSketch(dataArr.map(wrapSketchData));
9362
+ }
9234
9363
  const FONT_REGISTER = {};
9235
9364
  async function loadFont(fontPath, fontFamily = "default", force = false) {
9236
9365
  if (!force && FONT_REGISTER[fontFamily]) {
@@ -9294,20 +9423,12 @@ function textBlueprints(text, { startX = 0, startY = 0, fontSize = 16, fontFamil
9294
9423
  const blueprints = Array.from(sketchFontCommands(writtenText.commands));
9295
9424
  return organiseBlueprints(blueprints).mirror([0, 0]);
9296
9425
  }
9297
- function wrapSketchData$1(data) {
9298
- const opts = {};
9299
- if (data.defaultOrigin) opts.defaultOrigin = data.defaultOrigin;
9300
- if (data.defaultDirection) opts.defaultDirection = data.defaultDirection;
9301
- const sketch = new Sketch(data.wire, opts);
9302
- if (data.baseFace) sketch.baseFace = data.baseFace;
9303
- return sketch;
9304
- }
9305
9426
  function sketchText(text, textConfig, planeConfig = {}) {
9306
9427
  const textBp = textBlueprints(text, textConfig);
9307
9428
  const results = typeof planeConfig.plane === "string" || planeConfig.plane === void 0 ? textBp.sketchOnPlane(planeConfig.plane, planeConfig.origin) : textBp.sketchOnPlane(planeConfig.plane);
9308
9429
  return new Sketches(
9309
9430
  results.map(
9310
- (item) => Array.isArray(item) ? new CompoundSketch(item.map(wrapSketchData$1)) : wrapSketchData$1(item)
9431
+ (item) => Array.isArray(item) ? new CompoundSketch(item.map(wrapSketchData)) : wrapSketchData(item)
9311
9432
  )
9312
9433
  );
9313
9434
  }
@@ -9330,28 +9451,32 @@ function isProjectionPlane(plane) {
9330
9451
  }
9331
9452
  function lookFromPlane(projectionPlane) {
9332
9453
  const { dir, xAxis } = PROJECTION_PLANES[projectionPlane];
9333
- return new ProjectionCamera([0, 0, 0], dir, xAxis);
9454
+ return new ProjectionCamera(
9455
+ [0, 0, 0],
9456
+ dir,
9457
+ xAxis
9458
+ );
9334
9459
  }
9335
9460
  function defaultXDir(direction) {
9336
- const dir = new Vector(direction);
9337
- let yAxis = new Vector([0, 0, 1]);
9461
+ const [r, gc] = localGC();
9462
+ const dir = r(new Vector(direction));
9463
+ let yAxis = r(new Vector([0, 0, 1]));
9338
9464
  let xAxis = yAxis.cross(dir);
9339
9465
  if (xAxis.Length === 0) {
9340
- yAxis.delete();
9341
9466
  xAxis.delete();
9342
- yAxis = new Vector([0, 1, 0]);
9467
+ yAxis = r(new Vector([0, 1, 0]));
9343
9468
  xAxis = yAxis.cross(dir);
9344
9469
  }
9345
- dir.delete();
9346
- yAxis.delete();
9470
+ gc();
9347
9471
  return xAxis.normalize();
9348
9472
  }
9349
9473
  class ProjectionCamera extends WrappingObj {
9350
9474
  constructor(position = [0, 0, 0], direction = [0, 0, 1], xAxis) {
9351
- const xDir = xAxis ? new Vector(xAxis) : defaultXDir(direction);
9475
+ const [r, gc] = localGC();
9476
+ const xDir = xAxis ? r(new Vector(xAxis)) : r(defaultXDir(direction));
9352
9477
  const ax2 = makeAx2(position, direction, xDir);
9478
+ gc();
9353
9479
  super(ax2);
9354
- xDir.delete();
9355
9480
  }
9356
9481
  get position() {
9357
9482
  return new Vector(this.wrapped.Location());
@@ -9366,32 +9491,47 @@ class ProjectionCamera extends WrappingObj {
9366
9491
  return new Vector(this.wrapped.YDirection());
9367
9492
  }
9368
9493
  autoAxes() {
9369
- const dir = this.direction;
9370
- const xAxis = defaultXDir(dir);
9371
- this.wrapped.SetXDirection(asDir(xAxis));
9372
- dir.delete();
9373
- xAxis.delete();
9494
+ const [r, gc] = localGC();
9495
+ const dir = r(this.direction);
9496
+ const xAxis = r(defaultXDir(dir));
9497
+ const ocDir = r(asDir(xAxis));
9498
+ this.wrapped.SetXDirection(ocDir);
9499
+ gc();
9374
9500
  }
9375
9501
  setPosition(position) {
9376
- this.wrapped.SetLocation(asPnt(position));
9502
+ const [r, gc] = localGC();
9503
+ const pnt2 = r(asPnt(position));
9504
+ this.wrapped.SetLocation(pnt2);
9505
+ gc();
9377
9506
  return this;
9378
9507
  }
9379
9508
  setXAxis(xAxis) {
9380
- this.wrapped.SetXDirection(asDir(xAxis));
9509
+ const [r, gc] = localGC();
9510
+ const dir = r(asDir(xAxis));
9511
+ this.wrapped.SetXDirection(dir);
9512
+ gc();
9381
9513
  return this;
9382
9514
  }
9383
9515
  setYAxis(yAxis) {
9384
- this.wrapped.SetYDirection(asDir(yAxis));
9516
+ const [r, gc] = localGC();
9517
+ const dir = r(asDir(yAxis));
9518
+ this.wrapped.SetYDirection(dir);
9519
+ gc();
9385
9520
  return this;
9386
9521
  }
9387
9522
  lookAt(shape) {
9388
- const lookAtPoint = new Vector(
9389
- "boundingBox" in shape ? shape.boundingBox.center : shape
9523
+ const [r, gc] = localGC();
9524
+ const lookAtPoint = r(
9525
+ new Vector(
9526
+ "boundingBox" in shape ? shape.boundingBox.center : shape
9527
+ )
9390
9528
  );
9391
- const direction = this.position.sub(lookAtPoint).normalized();
9392
- this.wrapped.SetDirection(direction.toDir());
9393
- lookAtPoint.delete();
9394
- direction.delete();
9529
+ const pos = r(this.position);
9530
+ const diff = r(pos.sub(lookAtPoint));
9531
+ const direction = r(diff.normalized());
9532
+ const ocDir = r(direction.toDir());
9533
+ this.wrapped.SetDirection(ocDir);
9534
+ gc();
9395
9535
  this.autoAxes();
9396
9536
  return this;
9397
9537
  }
@@ -9409,7 +9549,9 @@ function makeProjectedEdges(shape, camera, withHiddenLines = true) {
9409
9549
  hiddenLineRemoval.Projector_1(projector);
9410
9550
  hiddenLineRemoval.Update();
9411
9551
  hiddenLineRemoval.Hide_1();
9412
- const hlrShapes = new oc.HLRBRep_HLRToShape(new oc.Handle_HLRBRep_Algo_2(hiddenLineRemoval));
9552
+ const hlrShapes = r(
9553
+ new oc.HLRBRep_HLRToShape(r(new oc.Handle_HLRBRep_Algo_2(hiddenLineRemoval)))
9554
+ );
9413
9555
  const visible = [
9414
9556
  ...getEdges(hlrShapes.VCompound_1()),
9415
9557
  ...getEdges(hlrShapes.Rg1LineVCompound_1()),
@@ -9424,17 +9566,6 @@ function makeProjectedEdges(shape, camera, withHiddenLines = true) {
9424
9566
  hidden.forEach((e) => oc.BRepLib.BuildCurves3d_2(e.wrapped));
9425
9567
  return { visible, hidden };
9426
9568
  }
9427
- function wrapSketchData(data) {
9428
- const opts = {};
9429
- if (data.defaultOrigin) opts.defaultOrigin = data.defaultOrigin;
9430
- if (data.defaultDirection) opts.defaultDirection = data.defaultDirection;
9431
- const sketch = new Sketch(data.wire, opts);
9432
- if (data.baseFace) sketch.baseFace = data.baseFace;
9433
- return sketch;
9434
- }
9435
- function wrapSketchDataArray(dataArr) {
9436
- return new CompoundSketch(dataArr.map(wrapSketchData));
9437
- }
9438
9569
  function wrapBlueprintResult(shape, result) {
9439
9570
  if (shape instanceof Blueprint) {
9440
9571
  return wrapSketchData(result);
@@ -9686,33 +9817,38 @@ const drawParametricFunction = (func, { pointsCount = 400, start = 0, stop = 1,
9686
9817
  return drawPointsInterpolation(points, approximationConfig, { closeShape });
9687
9818
  };
9688
9819
  const edgesToDrawing = (edges) => {
9820
+ const [r, gc] = localGC();
9689
9821
  const planeSketch = drawRectangle(1e3, 1e3).sketchOnPlane();
9690
- const planeFace = unwrap(makeFace(planeSketch.wire));
9822
+ const planeFace = r(unwrap(makeFace(planeSketch.wire)));
9691
9823
  const curves = edges.map((e) => edgeToCurve(e, planeFace));
9692
- planeFace.delete();
9824
+ gc();
9693
9825
  const stitchedCurves = stitchCurves(curves).map((s) => new Blueprint(s));
9694
9826
  if (stitchedCurves.length === 0) return new Drawing();
9695
9827
  if (stitchedCurves.length === 1) return new Drawing(stitchedCurves[0]);
9696
9828
  return new Drawing(new Blueprints(stitchedCurves));
9697
9829
  };
9698
9830
  function drawProjection(shape, projectionCamera = "front") {
9831
+ const [r, gc] = localGC();
9699
9832
  let camera;
9700
- if (projectionCamera instanceof ProjectionCamera) {
9833
+ const ownCamera = !(projectionCamera instanceof ProjectionCamera);
9834
+ if (!ownCamera) {
9701
9835
  camera = projectionCamera;
9702
9836
  } else {
9703
- camera = lookFromPlane(projectionCamera);
9837
+ camera = r(lookFromPlane(projectionCamera));
9704
9838
  }
9705
9839
  const { visible, hidden } = makeProjectedEdges(shape, camera);
9840
+ gc();
9706
9841
  return {
9707
9842
  visible: edgesToDrawing(visible),
9708
9843
  hidden: edgesToDrawing(hidden)
9709
9844
  };
9710
9845
  }
9711
9846
  function drawFaceOutline(face) {
9712
- const clonedFace = face.clone();
9713
- const outerWire2 = clonedFace.outerWire();
9847
+ const [r, gc] = localGC();
9848
+ const clonedFace = r(face.clone());
9849
+ const outerWire2 = r(clonedFace.outerWire());
9714
9850
  const curves = outerWire2.edges.map((e) => edgeToCurve(e, face));
9715
- outerWire2.delete();
9851
+ gc();
9716
9852
  const stitchedCurves = stitchCurves(curves).map((s) => new Blueprint(s));
9717
9853
  if (stitchedCurves.length === 0) return new Drawing();
9718
9854
  if (stitchedCurves.length === 1) return new Drawing(stitchedCurves[0]);
@@ -9783,7 +9919,7 @@ function mirrorDrawing(drawing, centerOrDirection, origin, mode) {
9783
9919
  }
9784
9920
  function createPlane(origin, xDirection = null, normal = [0, 0, 1]) {
9785
9921
  const zDir = vecNormalize(normal);
9786
- if (vecLength(zDir) === 0) throw new Error("Plane normal must be non-zero");
9922
+ if (vecIsZero(zDir)) throw new Error("Plane normal must be non-zero");
9787
9923
  let xDir;
9788
9924
  if (!xDirection) {
9789
9925
  const ax3 = makeOcAx3(origin, zDir);
@@ -9794,7 +9930,7 @@ function createPlane(origin, xDirection = null, normal = [0, 0, 1]) {
9794
9930
  } else {
9795
9931
  xDir = vecNormalize(xDirection);
9796
9932
  }
9797
- if (vecLength(xDir) === 0) throw new Error("Plane xDir must be non-zero");
9933
+ if (vecIsZero(xDir)) throw new Error("Plane xDir must be non-zero");
9798
9934
  const yDir = vecNormalize(vecCross(zDir, xDir));
9799
9935
  return { origin, xDir, yDir, zDir };
9800
9936
  }
@@ -9843,16 +9979,12 @@ function pivotPlane(plane, angleDeg, axis = [1, 0, 0]) {
9843
9979
  }
9844
9980
  function makeSpineWire(start, end) {
9845
9981
  const oc = getKernel().oc;
9846
- const pnt1 = toOcPnt(start);
9847
- const pnt2 = toOcPnt(end);
9848
- const edgeMaker = new oc.BRepBuilderAPI_MakeEdge_3(pnt1, pnt2);
9849
- const wireMaker = new oc.BRepBuilderAPI_MakeWire_2(edgeMaker.Edge());
9850
- const wire = castShape(wireMaker.Wire());
9851
- edgeMaker.delete();
9852
- wireMaker.delete();
9853
- pnt1.delete();
9854
- pnt2.delete();
9855
- return wire;
9982
+ const r = gcWithScope();
9983
+ const pnt1 = r(toOcPnt(start));
9984
+ const pnt2 = r(toOcPnt(end));
9985
+ const edgeMaker = r(new oc.BRepBuilderAPI_MakeEdge_3(pnt1, pnt2));
9986
+ const wireMaker = r(new oc.BRepBuilderAPI_MakeWire_2(edgeMaker.Edge()));
9987
+ return castShape(wireMaker.Wire());
9856
9988
  }
9857
9989
  function makeHelixWire(pitch, height, radius, center, dir, lefthand = false) {
9858
9990
  const oc = getKernel().oc;
@@ -9863,8 +9995,8 @@ function makeHelixWire(pitch, height, radius, center, dir, lefthand = false) {
9863
9995
  new oc.Geom2d_Line_3(r(new oc.gp_Pnt2d_3(0, 0)), r(new oc.gp_Dir2d_4(myDir, pitch)))
9864
9996
  );
9865
9997
  const nTurns = height / pitch;
9866
- const uStart = geomLine.Value(0);
9867
- const uStop = geomLine.Value(nTurns * Math.sqrt((2 * Math.PI) ** 2 + pitch ** 2));
9998
+ const uStart = r(geomLine.Value(0));
9999
+ const uStop = r(geomLine.Value(nTurns * Math.sqrt((2 * Math.PI) ** 2 + pitch ** 2)));
9868
10000
  const geomSeg = r(new oc.GCE2d_MakeSegment_1(uStart, uStop));
9869
10001
  const ax3 = makeOcAx3(center, dir);
9870
10002
  const geomSurf = new oc.Geom_CylindricalSurface_1(ax3, radius);
@@ -9881,12 +10013,10 @@ function makeHelixWire(pitch, height, radius, center, dir, lefthand = false) {
9881
10013
  }
9882
10014
  function extrudeFace(face, extrusionVec) {
9883
10015
  const oc = getKernel().oc;
9884
- const vec2 = toOcVec(extrusionVec);
9885
- const builder = new oc.BRepPrimAPI_MakePrism_1(face.wrapped, vec2, false, true);
9886
- const solid = createSolid(unwrap(downcast(builder.Shape())));
9887
- builder.delete();
9888
- vec2.delete();
9889
- return solid;
10016
+ const r = gcWithScope();
10017
+ const vec2 = r(toOcVec(extrusionVec));
10018
+ const builder = r(new oc.BRepPrimAPI_MakePrism_1(face.wrapped, vec2, false, true));
10019
+ return createSolid(unwrap(downcast(builder.Shape())));
9890
10020
  }
9891
10021
  function revolveFace(face, center = [0, 0, 0], direction = [0, 0, 1], angle = 360) {
9892
10022
  const oc = getKernel().oc;
@@ -9903,6 +10033,7 @@ function revolveFace(face, center = [0, 0, 0], direction = [0, 0, 1], angle = 36
9903
10033
  }
9904
10034
  function sweep(wire, spine, config = {}, shellMode = false) {
9905
10035
  const oc = getKernel().oc;
10036
+ const r = gcWithScope();
9906
10037
  const {
9907
10038
  frenet = false,
9908
10039
  auxiliarySpine,
@@ -9913,7 +10044,7 @@ function sweep(wire, spine, config = {}, shellMode = false) {
9913
10044
  forceProfileSpineOthogonality
9914
10045
  } = config;
9915
10046
  const withCorrection = transitionMode === "round" ? true : !!forceProfileSpineOthogonality;
9916
- const builder = new oc.BRepOffsetAPI_MakePipeShell(spine.wrapped);
10047
+ const builder = r(new oc.BRepOffsetAPI_MakePipeShell(spine.wrapped));
9917
10048
  {
9918
10049
  const mode = {
9919
10050
  transformed: oc.BRepBuilderAPI_TransitionMode.BRepBuilderAPI_Transformed,
@@ -9932,20 +10063,16 @@ function sweep(wire, spine, config = {}, shellMode = false) {
9932
10063
  }
9933
10064
  if (!law) builder.Add_1(wire.wrapped, !!withContact, withCorrection);
9934
10065
  else builder.SetLaw_1(wire.wrapped, law, !!withContact, withCorrection);
9935
- const progress = new oc.Message_ProgressRange_1();
10066
+ const progress = r(new oc.Message_ProgressRange_1());
9936
10067
  builder.Build(progress);
9937
10068
  if (!shellMode) builder.MakeSolid();
9938
10069
  const shape = castShape(builder.Shape());
9939
10070
  if (!isShape3D(shape)) {
9940
- builder.delete();
9941
- progress.delete();
9942
10071
  return err(typeCastError("SWEEP_NOT_3D", "Sweep did not produce a 3D shape"));
9943
10072
  }
9944
10073
  if (shellMode) {
9945
10074
  const startWire = castShape(builder.FirstShape());
9946
10075
  const endWire = castShape(builder.LastShape());
9947
- builder.delete();
9948
- progress.delete();
9949
10076
  if (!isWire(startWire)) {
9950
10077
  return err(typeCastError("SWEEP_START_NOT_WIRE", "Sweep did not produce a start Wire"));
9951
10078
  }
@@ -9954,28 +10081,8 @@ function sweep(wire, spine, config = {}, shellMode = false) {
9954
10081
  }
9955
10082
  return ok([shape, startWire, endWire]);
9956
10083
  }
9957
- builder.delete();
9958
- progress.delete();
9959
10084
  return ok(shape);
9960
10085
  }
9961
- function buildLawFromProfile(extrusionLength, { profile, endFactor = 1 }) {
9962
- const oc = getKernel().oc;
9963
- let law;
9964
- if (profile === "s-curve") {
9965
- law = new oc.Law_S();
9966
- law.Set_1(0, 1, extrusionLength, endFactor);
9967
- } else if (profile === "linear") {
9968
- law = new oc.Law_Linear();
9969
- law.Set(0, 1, extrusionLength, endFactor);
9970
- } else {
9971
- return err(
9972
- validationError("UNSUPPORTED_PROFILE", `Unsupported extrusion profile: ${String(profile)}`)
9973
- );
9974
- }
9975
- const trimmed = law.Trim(0, extrusionLength, 1e-6);
9976
- law.delete();
9977
- return ok(trimmed);
9978
- }
9979
10086
  function supportExtrude(wire, center, normal, support) {
9980
10087
  const endPoint = vecAdd(center, normal);
9981
10088
  const spine = makeSpineWire(center, endPoint);
@@ -10021,116 +10128,83 @@ function loftWires(wires, { ruled = true, startPoint, endPoint } = {}, returnShe
10021
10128
  }
10022
10129
  return ok(result);
10023
10130
  }
10024
- function wrapString(str) {
10025
- const oc = getKernel().oc;
10026
- return new oc.TCollection_ExtendedString_2(str, true);
10027
- }
10028
- function parseSlice(hex, index) {
10029
- return parseInt(hex.slice(index * 2, (index + 1) * 2), 16);
10030
- }
10031
- function colorFromHex(hex) {
10032
- let color = hex;
10033
- if (color.indexOf("#") === 0) color = color.slice(1);
10034
- if (color.length === 3) {
10035
- color = color.replace(/([0-9a-f])/gi, "$1$1");
10036
- }
10037
- return [parseSlice(color, 0), parseSlice(color, 1), parseSlice(color, 2)];
10038
- }
10039
- function wrapColor(hex, alpha = 1) {
10040
- const oc = getKernel().oc;
10041
- const [red, green, blue] = colorFromHex(hex);
10042
- return new oc.Quantity_ColorRGBA_5(red / 255, green / 255, blue / 255, alpha);
10043
- }
10044
10131
  function exportAssemblySTEP(shapes = [], { unit, modelUnit } = {}) {
10045
10132
  const oc = getKernel().oc;
10046
10133
  const r = gcWithScope();
10047
10134
  const doc = new oc.TDocStd_Document(wrapString("XmlOcaf"));
10048
- oc.XCAFDoc_ShapeTool.SetAutoNaming(false);
10049
- const mainLabel = doc.Main();
10050
- const tool = oc.XCAFDoc_DocumentTool.ShapeTool(mainLabel).get();
10051
- const ctool = oc.XCAFDoc_DocumentTool.ColorTool(mainLabel).get();
10052
- for (const { shape, name, color, alpha } of shapes) {
10053
- const shapeNode = tool.NewShape();
10054
- tool.SetShape(shapeNode, shape.wrapped);
10055
- oc.TDataStd_Name.Set_1(shapeNode, wrapString(name || uuidv()));
10056
- ctool.SetColor_3(
10057
- shapeNode,
10058
- wrapColor(color || "#f00", alpha ?? 1),
10059
- oc.XCAFDoc_ColorType.XCAFDoc_ColorSurf
10135
+ try {
10136
+ oc.XCAFDoc_ShapeTool.SetAutoNaming(false);
10137
+ const mainLabel = doc.Main();
10138
+ const tool = oc.XCAFDoc_DocumentTool.ShapeTool(mainLabel).get();
10139
+ const ctool = oc.XCAFDoc_DocumentTool.ColorTool(mainLabel).get();
10140
+ for (const { shape, name, color, alpha } of shapes) {
10141
+ const shapeNode = tool.NewShape();
10142
+ tool.SetShape(shapeNode, shape.wrapped);
10143
+ oc.TDataStd_Name.Set_1(shapeNode, wrapString(name || uuidv()));
10144
+ ctool.SetColor_3(
10145
+ shapeNode,
10146
+ wrapColor(color || "#f00", alpha ?? 1),
10147
+ oc.XCAFDoc_ColorType.XCAFDoc_ColorSurf
10148
+ );
10149
+ }
10150
+ tool.UpdateAssemblies();
10151
+ configureStepUnits(unit, modelUnit, r);
10152
+ const session = r(new oc.XSControl_WorkSession());
10153
+ const writer = r(
10154
+ new oc.STEPCAFControl_Writer_2(r(new oc.Handle_XSControl_WorkSession_2(session)), false)
10060
10155
  );
10156
+ configureStepWriter(writer);
10157
+ const progress = r(new oc.Message_ProgressRange_1());
10158
+ writer.Transfer_1(
10159
+ new oc.Handle_TDocStd_Document_2(doc),
10160
+ oc.STEPControl_StepModelType.STEPControl_AsIs,
10161
+ null,
10162
+ progress
10163
+ );
10164
+ const filename = uniqueIOFilename("_export", "step");
10165
+ const done = writer.Write(filename);
10166
+ if (done === oc.IFSelect_ReturnStatus.IFSelect_RetDone) {
10167
+ const file = oc.FS.readFile("/" + filename);
10168
+ oc.FS.unlink("/" + filename);
10169
+ return ok(new Blob([file], { type: "application/STEP" }));
10170
+ }
10171
+ return err(ioError("STEP_EXPORT_FAILED", "Failed to write STEP file"));
10172
+ } finally {
10173
+ doc.delete();
10061
10174
  }
10062
- tool.UpdateAssemblies();
10063
- if (unit || modelUnit) {
10064
- r(new oc.STEPCAFControl_Writer_1());
10065
- oc.Interface_Static.SetCVal("xstep.cascade.unit", (modelUnit || unit || "MM").toUpperCase());
10066
- oc.Interface_Static.SetCVal("write.step.unit", (unit || modelUnit || "MM").toUpperCase());
10067
- }
10068
- const session = r(new oc.XSControl_WorkSession());
10069
- const writer = r(
10070
- new oc.STEPCAFControl_Writer_2(r(new oc.Handle_XSControl_WorkSession_2(session)), false)
10071
- );
10072
- writer.SetColorMode(true);
10073
- writer.SetLayerMode(true);
10074
- writer.SetNameMode(true);
10075
- oc.Interface_Static.SetIVal("write.surfacecurve.mode", true);
10076
- oc.Interface_Static.SetIVal("write.precision.mode", 0);
10077
- oc.Interface_Static.SetIVal("write.step.assembly", 2);
10078
- oc.Interface_Static.SetIVal("write.step.schema", 5);
10079
- const progress = r(new oc.Message_ProgressRange_1());
10080
- writer.Transfer_1(
10081
- new oc.Handle_TDocStd_Document_2(doc),
10082
- oc.STEPControl_StepModelType.STEPControl_AsIs,
10083
- null,
10084
- progress
10085
- );
10086
- const filename = "export.step";
10087
- const done = writer.Write(filename);
10088
- doc.delete();
10089
- if (done === oc.IFSelect_ReturnStatus.IFSelect_RetDone) {
10090
- const file = oc.FS.readFile("/" + filename);
10091
- oc.FS.unlink("/" + filename);
10092
- return ok(new Blob([file], { type: "application/STEP" }));
10093
- }
10094
- return err(ioError("STEP_EXPORT_FAILED", "Failed to write STEP file"));
10095
10175
  }
10096
10176
  function measureVolumeProps(shape) {
10097
10177
  const oc = getKernel().oc;
10098
- const props = new oc.GProp_GProps_1();
10178
+ const r = gcWithScope();
10179
+ const props = r(new oc.GProp_GProps_1());
10099
10180
  oc.BRepGProp.VolumeProperties_1(shape.wrapped, props, false, false, false);
10100
- const pnt2 = props.CentreOfMass();
10101
- const result = {
10181
+ const pnt2 = r(props.CentreOfMass());
10182
+ return {
10102
10183
  mass: props.Mass(),
10103
10184
  centerOfMass: [pnt2.X(), pnt2.Y(), pnt2.Z()]
10104
10185
  };
10105
- pnt2.delete();
10106
- props.delete();
10107
- return result;
10108
10186
  }
10109
10187
  function measureSurfaceProps(shape) {
10110
10188
  const oc = getKernel().oc;
10111
- const props = new oc.GProp_GProps_1();
10189
+ const r = gcWithScope();
10190
+ const props = r(new oc.GProp_GProps_1());
10112
10191
  oc.BRepGProp.SurfaceProperties_1(shape.wrapped, props, false, false);
10113
- const pnt2 = props.CentreOfMass();
10114
- const result = {
10192
+ const pnt2 = r(props.CentreOfMass());
10193
+ return {
10115
10194
  mass: props.Mass(),
10116
10195
  centerOfMass: [pnt2.X(), pnt2.Y(), pnt2.Z()]
10117
10196
  };
10118
- pnt2.delete();
10119
- props.delete();
10120
- return result;
10121
10197
  }
10122
10198
  function measureLinearProps(shape) {
10123
10199
  const oc = getKernel().oc;
10124
- const props = new oc.GProp_GProps_1();
10200
+ const r = gcWithScope();
10201
+ const props = r(new oc.GProp_GProps_1());
10125
10202
  oc.BRepGProp.LinearProperties(shape.wrapped, props, false, false);
10126
- const pnt2 = props.CentreOfMass();
10127
- const result = {
10203
+ const pnt2 = r(props.CentreOfMass());
10204
+ return {
10128
10205
  mass: props.Mass(),
10129
10206
  centerOfMass: [pnt2.X(), pnt2.Y(), pnt2.Z()]
10130
10207
  };
10131
- pnt2.delete();
10132
- props.delete();
10133
- return result;
10134
10208
  }
10135
10209
  function measureVolume(shape) {
10136
10210
  return measureVolumeProps(shape).mass;
@@ -10143,15 +10217,13 @@ function measureLength(shape) {
10143
10217
  }
10144
10218
  function measureDistance(shape1, shape2) {
10145
10219
  const oc = getKernel().oc;
10146
- const distTool = new oc.BRepExtrema_DistShapeShape_1();
10220
+ const r = gcWithScope();
10221
+ const distTool = r(new oc.BRepExtrema_DistShapeShape_1());
10147
10222
  distTool.LoadS1(shape1.wrapped);
10148
10223
  distTool.LoadS2(shape2.wrapped);
10149
- const progress = new oc.Message_ProgressRange_1();
10224
+ const progress = r(new oc.Message_ProgressRange_1());
10150
10225
  distTool.Perform(progress);
10151
- const dist = distTool.Value();
10152
- progress.delete();
10153
- distTool.delete();
10154
- return dist;
10226
+ return distTool.Value();
10155
10227
  }
10156
10228
  function createDistanceQuery(referenceShape) {
10157
10229
  const oc = getKernel().oc;
@@ -10161,17 +10233,18 @@ function createDistanceQuery(referenceShape) {
10161
10233
  distanceTo(other) {
10162
10234
  distTool.LoadS2(other.wrapped);
10163
10235
  const progress = new oc.Message_ProgressRange_1();
10164
- distTool.Perform(progress);
10165
- const dist = distTool.Value();
10166
- progress.delete();
10167
- return dist;
10236
+ try {
10237
+ distTool.Perform(progress);
10238
+ return distTool.Value();
10239
+ } finally {
10240
+ progress.delete();
10241
+ }
10168
10242
  },
10169
10243
  dispose() {
10170
10244
  distTool.delete();
10171
10245
  }
10172
10246
  };
10173
10247
  }
10174
- const uniqueId = () => Date.now().toString(36) + Math.random().toString(36).substring(2);
10175
10248
  async function importSTEP(blob) {
10176
10249
  const oc = getKernel().oc;
10177
10250
  const r = gcWithScope();
@@ -10185,6 +10258,9 @@ async function importSTEP(blob) {
10185
10258
  }
10186
10259
  reader.TransferRoots(r(new oc.Message_ProgressRange_1()));
10187
10260
  const stepShape = reader.OneShape();
10261
+ if (stepShape.IsNull()) {
10262
+ return err(ioError("STEP_IMPORT_FAILED", "STEP file contains no valid geometry"));
10263
+ }
10188
10264
  return ok(castShape(stepShape));
10189
10265
  } finally {
10190
10266
  try {
@@ -10293,15 +10369,13 @@ function createEdgeFinder(filters) {
10293
10369
  const d = vecNormalize(resolveDir(dir));
10294
10370
  return withFilter((edge) => {
10295
10371
  const oc = getKernel().oc;
10296
- const adaptor = new oc.BRepAdaptor_Curve_2(edge.wrapped);
10297
- const tmpPnt = new oc.gp_Pnt_1();
10298
- const tmpVec = new oc.gp_Vec_1();
10372
+ const r = gcWithScope();
10373
+ const adaptor = r(new oc.BRepAdaptor_Curve_2(edge.wrapped));
10374
+ const tmpPnt = r(new oc.gp_Pnt_1());
10375
+ const tmpVec = r(new oc.gp_Vec_1());
10299
10376
  const mid = (Number(adaptor.FirstParameter()) + Number(adaptor.LastParameter())) / 2;
10300
10377
  adaptor.D1(mid, tmpPnt, tmpVec);
10301
10378
  const tangent = vecNormalize([tmpVec.X(), tmpVec.Y(), tmpVec.Z()]);
10302
- tmpPnt.delete();
10303
- tmpVec.delete();
10304
- adaptor.delete();
10305
10379
  const ang = Math.acos(Math.min(1, Math.abs(vecDot(tangent, d))));
10306
10380
  return Math.abs(ang - DEG2RAD * angle) < 1e-6;
10307
10381
  });
@@ -10309,24 +10383,20 @@ function createEdgeFinder(filters) {
10309
10383
  ofLength: (length, tolerance = 1e-3) => withFilter((edge) => Math.abs(curveLength(edge) - length) < tolerance),
10310
10384
  ofCurveType: (curveType) => withFilter((edge) => getCurveType(edge) === curveType),
10311
10385
  parallelTo: (dir = "Z") => createEdgeFinder([...filters]).inDirection(dir, 0),
10312
- atDistance: (distance, point = [0, 0, 0]) => {
10386
+ atDistance: (distance, point = [0, 0, 0]) => withFilter((edge) => {
10313
10387
  const oc = getKernel().oc;
10314
- const pnt2 = toOcPnt(point);
10315
- const vtxMaker = new oc.BRepBuilderAPI_MakeVertex(pnt2);
10388
+ const r = gcWithScope();
10389
+ const pnt2 = r(toOcPnt(point));
10390
+ const vtxMaker = r(new oc.BRepBuilderAPI_MakeVertex(pnt2));
10316
10391
  const vtx = vtxMaker.Vertex();
10317
- vtxMaker.delete();
10318
- pnt2.delete();
10319
- const distTool = new oc.BRepExtrema_DistShapeShape_1();
10392
+ const distTool = r(new oc.BRepExtrema_DistShapeShape_1());
10320
10393
  distTool.LoadS1(vtx);
10321
- return withFilter((edge) => {
10322
- distTool.LoadS2(edge.wrapped);
10323
- const progress = new oc.Message_ProgressRange_1();
10324
- distTool.Perform(progress);
10325
- const d = distTool.Value();
10326
- progress.delete();
10327
- return Math.abs(d - distance) < 1e-6;
10328
- });
10329
- }
10394
+ distTool.LoadS2(edge.wrapped);
10395
+ const progress = r(new oc.Message_ProgressRange_1());
10396
+ distTool.Perform(progress);
10397
+ const d = distTool.Value();
10398
+ return Math.abs(d - distance) < 1e-6;
10399
+ })
10330
10400
  };
10331
10401
  }
10332
10402
  function faceFinder() {
@@ -10353,24 +10423,20 @@ function createFaceFinder(filters) {
10353
10423
  },
10354
10424
  parallelTo: (dir = "Z") => createFaceFinder([...filters]).inDirection(dir, 0),
10355
10425
  ofSurfaceType: (surfaceType) => withFilter((face) => unwrap(getSurfaceType(face)) === surfaceType),
10356
- atDistance: (distance, point = [0, 0, 0]) => {
10426
+ atDistance: (distance, point = [0, 0, 0]) => withFilter((face) => {
10357
10427
  const oc = getKernel().oc;
10358
- const pnt2 = toOcPnt(point);
10359
- const vtxMaker = new oc.BRepBuilderAPI_MakeVertex(pnt2);
10428
+ const r = gcWithScope();
10429
+ const pnt2 = r(toOcPnt(point));
10430
+ const vtxMaker = r(new oc.BRepBuilderAPI_MakeVertex(pnt2));
10360
10431
  const vtx = vtxMaker.Vertex();
10361
- vtxMaker.delete();
10362
- pnt2.delete();
10363
- const distTool = new oc.BRepExtrema_DistShapeShape_1();
10432
+ const distTool = r(new oc.BRepExtrema_DistShapeShape_1());
10364
10433
  distTool.LoadS1(vtx);
10365
- return withFilter((face) => {
10366
- distTool.LoadS2(face.wrapped);
10367
- const progress = new oc.Message_ProgressRange_1();
10368
- distTool.Perform(progress);
10369
- const d = distTool.Value();
10370
- progress.delete();
10371
- return Math.abs(d - distance) < 1e-6;
10372
- });
10373
- }
10434
+ distTool.LoadS2(face.wrapped);
10435
+ const progress = r(new oc.Message_ProgressRange_1());
10436
+ distTool.Perform(progress);
10437
+ const d = distTool.Value();
10438
+ return Math.abs(d - distance) < 1e-6;
10439
+ })
10374
10440
  };
10375
10441
  }
10376
10442
  function createCamera(position = [0, 0, 0], direction = [0, 0, 1], xAxis) {
@@ -10396,23 +10462,8 @@ function cameraLookAt(camera, target) {
10396
10462
  const direction = vecNormalize(vecSub(camera.position, target));
10397
10463
  return createCamera(camera.position, direction);
10398
10464
  }
10399
- const PLANE_MAP = {
10400
- XY: { dir: [0, 0, 1], xAxis: [1, 0, 0] },
10401
- XZ: { dir: [0, -1, 0], xAxis: [1, 0, 0] },
10402
- YZ: { dir: [1, 0, 0], xAxis: [0, 1, 0] },
10403
- YX: { dir: [0, 0, -1], xAxis: [0, 1, 0] },
10404
- ZX: { dir: [0, 1, 0], xAxis: [0, 0, 1] },
10405
- ZY: { dir: [-1, 0, 0], xAxis: [0, 0, 1] },
10406
- front: { dir: [0, -1, 0], xAxis: [1, 0, 0] },
10407
- back: { dir: [0, 1, 0], xAxis: [-1, 0, 0] },
10408
- right: { dir: [-1, 0, 0], xAxis: [0, -1, 0] },
10409
- left: { dir: [1, 0, 0], xAxis: [0, 1, 0] },
10410
- bottom: { dir: [0, 0, 1], xAxis: [1, 0, 0] },
10411
- top: { dir: [0, 0, -1], xAxis: [1, 0, 0] }
10412
- };
10413
10465
  function cameraFromPlane(planeName) {
10414
- const config = PLANE_MAP[planeName];
10415
- if (!config) throw new Error(`Unknown projection plane: ${planeName}`);
10466
+ const config = PROJECTION_PLANES[planeName];
10416
10467
  return createCamera([0, 0, 0], config.dir, config.xAxis);
10417
10468
  }
10418
10469
  function cameraToProjectionCamera(camera) {
@@ -10626,6 +10677,8 @@ exports.fuseAllShapes = fuseAllShapes;
10626
10677
  exports.fuseBlueprint2D = fuseBlueprint2D;
10627
10678
  exports.fuseBlueprints = fuseBlueprints;
10628
10679
  exports.fuseShapes = fuseShapes;
10680
+ exports.gcWithObject = gcWithObject;
10681
+ exports.gcWithScope = gcWithScope;
10629
10682
  exports.genericSweep = genericSweep;
10630
10683
  exports.getBounds = getBounds;
10631
10684
  exports.getEdges = getEdges$1;