diff --git a/cadquery/cq.py b/cadquery/cq.py index 9e97eff64..e01b1b157 100644 --- a/cadquery/cq.py +++ b/cadquery/cq.py @@ -2715,14 +2715,16 @@ def combine(self, clean=True): return self.newObject([s]) - def union(self, toUnion=None, clean=True): + def union(self, toUnion=None, clean=True, glue=False, tol=None): """ Unions all of the items on the stack of toUnion with the current solid. If there is no current solid, the items in toUnion are unioned together. :param toUnion: :type toUnion: a solid object, or a CQ object having a solid, - :param boolean clean: call :py:meth:`clean` afterwards to have a clean shape + :param boolean clean: call :py:meth:`clean` afterwards to have a clean shape (default True) + :param boolean glue: use a faster gluing mode for non-overlapping shapes (default False) + :param float tol: tolerance value for fuzzy bool operation mode (default None) :raises: ValueError if there is no solid to add to in the chain :return: a CQ object with the resulting object selected """ @@ -2743,9 +2745,9 @@ def union(self, toUnion=None, clean=True): # look for parents to cut from solidRef = self.findSolid(searchStack=True, searchParents=True) if solidRef is not None: - r = solidRef.fuse(*newS) + r = solidRef.fuse(*newS, glue=glue, tol=tol) elif len(newS) > 1: - r = newS.pop(0).fuse(*newS) + r = newS.pop(0).fuse(*newS, glue=glue, tol=tol) else: r = newS[0] diff --git a/cadquery/occ_impl/shapes.py b/cadquery/occ_impl/shapes.py index de73bf2e8..d62a0d4d2 100644 --- a/cadquery/occ_impl/shapes.py +++ b/cadquery/occ_impl/shapes.py @@ -120,6 +120,8 @@ from OCP.BRepOffsetAPI import BRepOffsetAPI_MakeFilling from OCP.BRepOffset import BRepOffset_MakeOffset, BRepOffset_Skin +from OCP.BOPAlgo import BOPAlgo_GlueEnum + from math import pi, sqrt from functools import reduce import warnings @@ -608,13 +610,16 @@ def cut(self, *toCut): return self._bool_op((self,), toCut, cut_op) - def fuse(self, *toFuse): + def fuse(self, *toFuse, glue=False, tol=None): """ Fuse shapes together """ fuse_op = BRepAlgoAPI_Fuse() - # fuse_op.SetFuzzyValue(TOLERANCE) + if glue: + fuse_op.SetGlue(BOPAlgo_GlueEnum.BOPAlgo_GlueShift) + if tol: + fuse_op.SetFuzzyValue(tol) rv = self._bool_op((self,), toFuse, fuse_op) @@ -2000,13 +2005,16 @@ def cut(self, *toCut): return self._bool_op(self, toCut, cut_op) - def fuse(self, *toFuse): + def fuse(self, *toFuse, glue=False, tol=None): """ Fuse shapes together """ fuse_op = BRepAlgoAPI_Fuse() - # fuse_op.SetFuzzyValue(TOLERANCE) + if glue: + fuse_op.SetGlue(BOPAlgo_GlueEnum.BOPAlgo_GlueShift) + if tol: + fuse_op.SetFuzzyValue(tol) args = tuple(self) + toFuse diff --git a/tests/test_cadquery.py b/tests/test_cadquery.py index 5f7639ca4..b59b5fa14 100644 --- a/tests/test_cadquery.py +++ b/tests/test_cadquery.py @@ -3410,3 +3410,34 @@ def testSection(self): with self.assertRaises(ValueError): line.section() + + def testGlue(self): + + box1 = Workplane("XY").rect(1, 1).extrude(2) + box2 = Workplane("XY", origin=(0, 1, 0)).rect(1, 1).extrude(1) + res = box1.union(box2, glue=True) + + self.assertEqual(res.faces().size(), 8) + + obj = obj = ( + Workplane("XY").rect(1, 1).extrude(2).moveTo(0, 2).rect(1, 1).extrude(2) + ) + res = obj.union(box2, glue=True) + + self.assertEqual(res.faces().size(), 10) + + def testFuzzyBoolOp(self): + + eps = 1e-3 + + box1 = Workplane("XY").box(1, 1, 1) + box2 = Workplane("XY", origin=(1 + eps, 0.0)).box(1, 1, 1) + box3 = Workplane("XY", origin=(2, 0, 0)).box(1, 1, 1) + + res = box1.union(box2) + res_fuzzy = box1.union(box2, tol=eps) + res_fuzzy2 = box1.union(box3).union(box2, tol=eps) + + self.assertEqual(res.solids().size(), 2) + self.assertEqual(res_fuzzy.solids().size(), 1) + self.assertEqual(res_fuzzy2.solids().size(), 1)