Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions cadquery/hull.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def atan2p(x, y):

def convert_and_validate(edges: Iterable[Edge]) -> Tuple[List[Arc], List[Point]]:

arcs: Set[Arc] = set()
arcs_by_center: dict = {}
points: Set[Point] = set()

for e in edges:
Expand All @@ -102,14 +102,16 @@ def convert_and_validate(edges: Iterable[Edge]) -> Tuple[List[Arc], List[Point]]
elif gt == "CIRCLE":
c = e.arcCenter()
r = e.radius()
a1, a2 = e._bounds()
key = (c.x, c.y)

arcs.add(Arc(Point(c.x, c.y), r, a1, a2))
# keep one arc per center (largest radius for the hull)
if key not in arcs_by_center or r > arcs_by_center[key].r:
arcs_by_center[key] = Arc(Point(c.x, c.y), r, 0, 2 * pi)

else:
raise ValueError("Unsupported geometry {gt}")

return list(arcs), list(points)
return list(arcs_by_center.values()), list(points)


def select_lowest_point(points: Points) -> Tuple[Point, int]:
Expand Down
30 changes: 30 additions & 0 deletions tests/test_hull.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,36 @@ def test_hull():
assert h.isValid()


def test_hull_overlapping_circles():
"""Hull of overlapping circles should not raise ZeroDivisionError.

When two circles overlap, boolean face fusion splits them into
multiple arc segments sharing the same center. arc_arc() must
handle these concentric arcs without dividing by zero.
"""
from cadquery import Sketch

s = Sketch().push([(-19, 0), (19, 0)]).circle(35).reset().hull()

assert s._faces.Area() > 0
assert len(s._faces.Faces()) >= 1


def test_hull_overlapping_circles_equal_radii_via_face():
"""Hull via .face() with overlapping equal-radii circles."""
from cadquery import Sketch, Location, Vector

s = (
Sketch()
.face(Sketch().circle(35).moved(Location(Vector(-19, 0, 0))))
.face(Sketch().circle(35).moved(Location(Vector(19, 0, 0))))
.hull()
)

assert s._faces.Area() > 0
assert len(s._faces.Faces()) >= 1


def test_validation():

with pytest.raises(ValueError):
Expand Down