diff --git a/cadquery/func.py b/cadquery/func.py index 949bc809b..3e7d929ed 100644 --- a/cadquery/func.py +++ b/cadquery/func.py @@ -59,6 +59,7 @@ chamfer2D, draft, History, + enclose, ) __all__ = [ @@ -124,4 +125,5 @@ "fillet2D", "draft", "History", + "enclose", ] diff --git a/cadquery/occ_impl/shapes.py b/cadquery/occ_impl/shapes.py index 86af4afe9..49d1f0ac8 100644 --- a/cadquery/occ_impl/shapes.py +++ b/cadquery/occ_impl/shapes.py @@ -266,6 +266,8 @@ BOPAlgo_FUSE, BOPAlgo_CUT, BOPAlgo_COMMON, + BOPAlgo_MakerVolume, + BOPAlgo_Splitter, ) from OCP.IFSelect import IFSelect_ReturnStatus @@ -6879,14 +6881,45 @@ def split( Split one shape with another. """ - builder = BRepAlgoAPI_Splitter() - _bool_op(s1, s2, builder, tol) + builder = BOPAlgo_Splitter() + _set_builder_options(builder, tol) + + builder.AddArgument(s1.wrapped) + builder.AddTool(s2.wrapped) + + builder.Perform() _update_history(history, name, [s1, s2], builder) return _compound_or_shape(builder.Shape()) +def enclose( + *shapes: Shape, + tol: float = 0.0, + history: History | None = None, + name: str | None = None, +) -> Shape: + """ + Build a solid enclosed by the specified faces. Faces can intersect or touch. + If all faces are touching, solid() has better performance. + """ + + builder = BOPAlgo_MakerVolume() + # ignore non-intersecting internal faces + builder.SetAvoidInternalShapes(True) + _set_builder_options(builder, tol) + + for s in shapes: + builder.AddArgument(s.wrapped) + + builder.Perform() + + _update_history(history, name, shapes, builder) + + return _compound_or_shape(builder.Shape()) + + def imprint( *shapes: Shape, tol: float = 0.0, diff --git a/tests/test_free_functions.py b/tests/test_free_functions.py index 958363254..719675472 100644 --- a/tests/test_free_functions.py +++ b/tests/test_free_functions.py @@ -66,6 +66,8 @@ _shape_to_faces_shells, _get_faces, _combine_hist_dict, + enclose, + split, ) from OCP.BOPAlgo import BOPAlgo_CheckStatus @@ -592,6 +594,36 @@ def test_operators(): assert len(intersect(b1, b3, tol=1e-3).Faces()) == 6 +def test_split(): + + c = cylinder(2, 10) + p = plane(2, 2).moved(z=1) + + res = split(c, p) + + assert res.isValid() + assert res.solids().size() == 2 + + +def test_enclose(): + + c = cylinder(2, 10).face("%CYLINDER") + p1 = plane(3, 3) + p2 = plane(2, 2).moved(z=1) + p3 = plane(0.1, 0.1).moved(z=1.5) # internal face to be ignored + + res = enclose(c, p1, p2, p3) + + assert res.isValid() + assert res.ShapeType() == "Solid" + assert res.Volume() == approx(pi) + + res = enclose(c, p1) + + assert isinstance(res, Compound) + assert res.size() == 0 + + def test_imprint(): b1 = box(1, 1, 1)