Skip to content

Commit 95f37fd

Browse files
Add tol to cut and intersect (#1332)
* Add tol to cut and intersect * Implement compound overloads * Black fix * Add tol to wokrplane methods * Test fuzzy cut and intersect * Cover fuzzy ops for compounds too * More coverage
1 parent 3c576f4 commit 95f37fd

File tree

3 files changed

+68
-11
lines changed

3 files changed

+68
-11
lines changed

cadquery/cq.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3331,13 +3331,17 @@ def __add__(self: T, toUnion: Union["Workplane", Solid, Compound]) -> T:
33313331
return self.union(toUnion)
33323332

33333333
def cut(
3334-
self: T, toCut: Union["Workplane", Solid, Compound], clean: bool = True
3334+
self: T,
3335+
toCut: Union["Workplane", Solid, Compound],
3336+
clean: bool = True,
3337+
tol: Optional[float] = None,
33353338
) -> T:
33363339
"""
33373340
Cuts the provided solid from the current solid, IE, perform a solid subtraction.
33383341
33393342
:param toCut: a solid object, or a Workplane object having a solid
33403343
:param clean: call :meth:`clean` afterwards to have a clean shape
3344+
:param tol: tolerance value for fuzzy bool operation mode (default None)
33413345
:raises ValueError: if there is no solid to subtract from in the chain
33423346
:return: a Workplane object with the resulting object selected
33433347
"""
@@ -3355,7 +3359,7 @@ def cut(
33553359
else:
33563360
raise ValueError("Cannot cut type '{}'".format(type(toCut)))
33573361

3358-
newS = solidRef.cut(*solidToCut)
3362+
newS = solidRef.cut(*solidToCut, tol=tol)
33593363

33603364
if clean:
33613365
newS = newS.clean()
@@ -3377,13 +3381,17 @@ def __sub__(self: T, toUnion: Union["Workplane", Solid, Compound]) -> T:
33773381
return self.cut(toUnion)
33783382

33793383
def intersect(
3380-
self: T, toIntersect: Union["Workplane", Solid, Compound], clean: bool = True,
3384+
self: T,
3385+
toIntersect: Union["Workplane", Solid, Compound],
3386+
clean: bool = True,
3387+
tol: Optional[float] = None,
33813388
) -> T:
33823389
"""
33833390
Intersects the provided solid from the current solid.
33843391
33853392
:param toIntersect: a solid object, or a Workplane object having a solid
33863393
:param clean: call :meth:`clean` afterwards to have a clean shape
3394+
:param tol: tolerance value for fuzzy bool operation mode (default None)
33873395
:raises ValueError: if there is no solid to intersect with in the chain
33883396
:return: a Workplane object with the resulting object selected
33893397
"""
@@ -3401,7 +3409,7 @@ def intersect(
34013409
else:
34023410
raise ValueError("Cannot intersect type '{}'".format(type(toIntersect)))
34033411

3404-
newS = solidRef.intersect(*solidToIntersect)
3412+
newS = solidRef.intersect(*solidToIntersect, tol=tol)
34053413

34063414
if clean:
34073415
newS = newS.clean()

cadquery/occ_impl/shapes.py

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1053,13 +1053,18 @@ def _bool_op(
10531053

10541054
return Shape.cast(op.Shape())
10551055

1056-
def cut(self, *toCut: "Shape") -> "Shape":
1056+
def cut(self, *toCut: "Shape", tol: Optional[float] = None) -> "Shape":
10571057
"""
10581058
Remove the positional arguments from this Shape.
1059+
1060+
:param tol: Fuzzy mode tolerance
10591061
"""
10601062

10611063
cut_op = BRepAlgoAPI_Cut()
10621064

1065+
if tol:
1066+
cut_op.SetFuzzyValue(tol)
1067+
10631068
return self._bool_op((self,), toCut, cut_op)
10641069

10651070
def fuse(
@@ -1070,7 +1075,7 @@ def fuse(
10701075
10711076
:param glue: Sets the glue option for the algorithm, which allows
10721077
increasing performance of the intersection of the input shapes
1073-
:param tol: Additional tolerance
1078+
:param tol: Fuzzy mode tolerance
10741079
"""
10751080

10761081
fuse_op = BRepAlgoAPI_Fuse()
@@ -1083,13 +1088,18 @@ def fuse(
10831088

10841089
return rv
10851090

1086-
def intersect(self, *toIntersect: "Shape") -> "Shape":
1091+
def intersect(self, *toIntersect: "Shape", tol: Optional[float] = None) -> "Shape":
10871092
"""
10881093
Intersection of the positional arguments and this Shape.
1094+
1095+
:param tol: Fuzzy mode tolerance
10891096
"""
10901097

10911098
intersect_op = BRepAlgoAPI_Common()
10921099

1100+
if tol:
1101+
intersect_op.SetFuzzyValue(tol)
1102+
10931103
return self._bool_op((self,), toIntersect, intersect_op)
10941104

10951105
def facesIntersectedByLine(
@@ -3582,13 +3592,18 @@ def __bool__(self) -> bool:
35823592

35833593
return TopoDS_Iterator(self.wrapped).More()
35843594

3585-
def cut(self, *toCut: Shape) -> "Compound":
3595+
def cut(self, *toCut: "Shape", tol: Optional[float] = None) -> "Compound":
35863596
"""
3587-
Remove a shape from another one
3597+
Remove the positional arguments from this Shape.
3598+
3599+
:param tol: Fuzzy mode tolerance
35883600
"""
35893601

35903602
cut_op = BRepAlgoAPI_Cut()
35913603

3604+
if tol:
3605+
cut_op.SetFuzzyValue(tol)
3606+
35923607
return tcast(Compound, self._bool_op(self, toCut, cut_op))
35933608

35943609
def fuse(
@@ -3616,13 +3631,20 @@ def fuse(
36163631

36173632
return tcast(Compound, rv)
36183633

3619-
def intersect(self, *toIntersect: Shape) -> "Compound":
3634+
def intersect(
3635+
self, *toIntersect: "Shape", tol: Optional[float] = None
3636+
) -> "Compound":
36203637
"""
3621-
Construct shape intersection
3638+
Intersection of the positional arguments and this Shape.
3639+
3640+
:param tol: Fuzzy mode tolerance
36223641
"""
36233642

36243643
intersect_op = BRepAlgoAPI_Common()
36253644

3645+
if tol:
3646+
intersect_op.SetFuzzyValue(tol)
3647+
36263648
return tcast(Compound, self._bool_op(self, toIntersect, intersect_op))
36273649

36283650

tests/test_cadquery.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4598,6 +4598,7 @@ def testFuzzyBoolOp(self):
45984598

45994599
eps = 1e-3
46004600

4601+
# test fuse
46014602
box1 = Workplane("XY").box(1, 1, 1)
46024603
box2 = Workplane("XY", origin=(1 + eps, 0.0)).box(1, 1, 1)
46034604
box3 = Workplane("XY", origin=(2, 0, 0)).box(1, 1, 1)
@@ -4610,6 +4611,32 @@ def testFuzzyBoolOp(self):
46104611
self.assertEqual(res_fuzzy.solids().size(), 1)
46114612
self.assertEqual(res_fuzzy2.solids().size(), 1)
46124613

4614+
# test cut and intersect
4615+
box4 = Workplane("XY", origin=(eps, 0.0)).box(1, 1, 1)
4616+
4617+
res_fuzzy_cut = box1.cut(box4, tol=eps)
4618+
res_fuzzy_intersect = box1.intersect(box4, tol=eps)
4619+
4620+
self.assertAlmostEqual(res_fuzzy_cut.val().Volume(), 0)
4621+
self.assertAlmostEqual(res_fuzzy_intersect.val().Volume(), 1)
4622+
4623+
# test with compounds
4624+
box1_cmp = Compound.makeCompound(box1.vals())
4625+
box4_cmp = Compound.makeCompound(box4.vals())
4626+
4627+
res_fuzzy_cut_cmp = box1_cmp.cut(box4_cmp, tol=eps)
4628+
res_fuzzy_intersect_cmp = box1_cmp.intersect(box4_cmp, tol=eps)
4629+
4630+
self.assertAlmostEqual(res_fuzzy_cut_cmp.Volume(), 0)
4631+
self.assertAlmostEqual(res_fuzzy_intersect_cmp.Volume(), 1)
4632+
4633+
# test with solids
4634+
res_fuzzy_cut_val = box1.val().cut(box4.val(), tol=eps)
4635+
res_fuzzy_intersect_val = box1.val().intersect(box4.val(), tol=eps)
4636+
4637+
self.assertAlmostEqual(res_fuzzy_cut_val.Volume(), 0)
4638+
self.assertAlmostEqual(res_fuzzy_intersect_val.Volume(), 1)
4639+
46134640
def testLocatedMoved(self):
46144641

46154642
box = Solid.makeBox(1, 1, 1, Vector(-0.5, -0.5, -0.5))

0 commit comments

Comments
 (0)