diff --git a/cadquery/hull.py b/cadquery/hull.py index 88f40d6c8..2ea396642 100644 --- a/cadquery/hull.py +++ b/cadquery/hull.py @@ -73,8 +73,8 @@ def __init__(self, c: Point, r: float, a1: float, a2: float): self.a1 = a1 self.a2 = a2 - self.s = Point(r * cos(a1), r * sin(a1)) - self.e = Point(r * cos(a2), r * sin(a2)) + self.s = Point(c.x + r * cos(a1), c.y + r * sin(a1)) + self.e = Point(c.x + r * cos(a2), c.y + r * sin(a2)) self.ac = 2 * pi - (a1 - a2) def __hash__(self): @@ -386,6 +386,11 @@ def find_hull(edges: Iterable[Edge]) -> Wire: # split into arcs and points arcs, points = convert_and_validate(edges) + # a lone circle is its own hull + if len(arcs) == 1 and not points: + a = arcs[0] + return Wire.assembleEdges([Edge.makeCircle(a.r, Vector(a.c.x, a.c.y))]) + # select the starting element start = select_lowest(arcs, points) rv.append(start) @@ -411,6 +416,10 @@ def find_hull(edges: Iterable[Edge]) -> Wire: segments.append(segment) next_ix = int(argmin(angles)) + + if angles[next_ix] == inf: + raise ValueError("Hull could not be closed") + current_e, current_angle, finished = update_hull( current_e, next_ix, entities, angles, segments, rv ) diff --git a/tests/test_hull.py b/tests/test_hull.py index 6b83e2476..aecdb5511 100644 --- a/tests/test_hull.py +++ b/tests/test_hull.py @@ -7,6 +7,10 @@ from cadquery import hull +def area(edges): + return cq.Face.makeFromWires(hull.find_hull(edges)).Area() + + def test_hull(): c1 = cq.Edge.makeCircle(0.5, (-1.5, 0.5, 0)) @@ -71,3 +75,47 @@ def test_eq(): assert a != p assert p != a assert a != None + + +def test_lines_only(): + edges = [ + cq.Edge.makeLine(cq.Vector(0, 0), cq.Vector(4, 0)), + cq.Edge.makeLine(cq.Vector(4, 0), cq.Vector(0, 3)), + cq.Edge.makeLine(cq.Vector(0, 3), cq.Vector(0, 0)), + ] + + assert area(edges) == pytest.approx(6.0) + + +def test_empty(): + with pytest.raises(ValueError): + hull.find_hull([]) + + +def test_arc_inside_hull(): + outer = [cq.Edge.makeCircle(20.0, (0, 0, 0)), cq.Edge.makeCircle(20.0, (60, 0, 0))] + arc = cq.Edge.makeCircle(5.0, (30, 0, 0), angle1=0, angle2=180) + + assert area(outer + [arc]) == pytest.approx(area(outer)) + + +def test_single_circle(): + assert area([cq.Edge.makeCircle(5.0, (0, 0, 0))]) == pytest.approx(25 * pi) + + +def test_stalled_march(): + # valid input the march cannot close; it used to loop forever + edges = [ + cq.Edge.makeCircle(6.0, (0, 12, 0)), + cq.Edge.makeLine(cq.Vector(-2, 5), cq.Vector(9, 10)), + ] + + with pytest.raises(ValueError): + hull.find_hull(edges) + + +def test_arc_endpoints(): + a = hull.Arc(hull.Point(10.0, 20.0), 1.0, 0.0, pi) + + assert (a.s.x, a.s.y) == pytest.approx((11.0, 20.0)) + assert (a.e.x, a.e.y) == pytest.approx((9.0, 20.0))