diff --git a/cadquery/cq.py b/cadquery/cq.py index 8b636917a..d1ff73f81 100644 --- a/cadquery/cq.py +++ b/cadquery/cq.py @@ -3800,6 +3800,62 @@ def sphere( else: return self.union(spheres, clean=clean) + def cone( + self: T, + height: float, + radius1: float = None, + radius2: float = None, + radius: float = 0, + direct: Vector = Vector(0, 0, 1), + angle: float = 360, + combine: bool = True, + clean: bool = True, + ) -> T: + """ + Returns a cone with the specified radius and height for each point on the stack + A truncated cone can be created by specifying parameters radius1 and radius2 instead of radius. + :param height: The height of the cone + :type height: float > 0 + :param radius1: The radius of the bottom of the cone + :type radius1: float > 0 + :param radius2: The radius of the top of the cone + :type radius2: float > 0 + :param radius: The radius of the cone + :type radius: float > 0 + :param direct: The direction axis for the creation of the cone + :type direct: A three-tuple + :param angle: The angle to sweep the cone arc through + :type angle: float > 0 + :param combine: Whether the results should be combined with other solids on the stack + (and each other) + :type combine: true to combine shapes, false otherwise + :param clean: call :py:meth:`clean` afterwards to have a clean shape + :return: A cone object for each point on the stack + + One cone is created for each item on the current stack. If no items are on the stack, one + cone is created using the current workplane center. + + If combine is true, the result will be a single object on the stack. If a solid was found + in the chain, the result is that solid with all cones produced fused onto it otherwise, + the result is the combination of all the produced cones. + + If combine is false, the result will be a list of the cones produced. + """ + + r1 = radius if radius1 is None or radius1 == 0 else radius1 + r2 = 0 if radius2 is None else radius2 + offset = Vector() + s = Solid.makeCone(r1, r2, height, offset, direct, angle) + + # We want a cone for each point on the workplane + cones = self.eachpoint(lambda loc: s.moved(loc), True) + + # If we don't need to combine everything, just return the created cones + if not combine: + return cones + else: + return self.union(cones, clean=clean) + def cylinder( self: T, height: float, diff --git a/doc/apireference.rst b/doc/apireference.rst index 8f61fdc63..1d49d36c9 100644 --- a/doc/apireference.rst +++ b/doc/apireference.rst @@ -91,6 +91,7 @@ Some 3D operations also require an active 2D workplane, but some do not. Workplane.box Workplane.sphere Workplane.cylinder + Workplane.cone Workplane.union Workplane.combine Workplane.intersect diff --git a/doc/roadmap.rst b/doc/roadmap.rst index a1bc6cc25..642e5f4eb 100644 --- a/doc/roadmap.rst +++ b/doc/roadmap.rst @@ -80,7 +80,6 @@ rotation/transform that return a copy primitive creation Need primitive creation for: - * cone * torus * wedge diff --git a/tests/test_cadquery.py b/tests/test_cadquery.py index f717ad7f8..312f07e36 100644 --- a/tests/test_cadquery.py +++ b/tests/test_cadquery.py @@ -2476,6 +2476,28 @@ def testCylinderCentering(self): s0.val().Center().toTuple(), s1.val().Center().toTuple(), 3 ) + def testConeDefaults(self): + s = Workplane("XY").cone(40, 10) + self.assertEqual(1, s.size()) + self.assertEqual(1, s.solids().size()) + self.assertEqual(2, s.faces().size()) + self.assertEqual(2, s.vertices().size()) + s1 = Workplane("XY").cone(40, 10, 5) + self.assertEqual(1, s1.size()) + self.assertEqual(1, s1.solids().size()) + self.assertEqual(3, s1.faces().size()) + self.assertEqual(2, s1.vertices().size()) + s2 = Workplane("XY").cone(40, radius1=10, radius2=5) + self.assertEqual(1, s2.size()) + self.assertEqual(1, s2.solids().size()) + self.assertEqual(3, s2.faces().size()) + self.assertEqual(2, s2.vertices().size()) + s3 = Workplane("XY").cone(40, radius1=10, radius2=5, combine=False) + self.assertEqual(1, s3.size()) + self.assertEqual(1, s3.solids().size()) + self.assertEqual(3, s3.faces().size()) + self.assertEqual(2, s3.vertices().size()) + def testWedgeDefaults(self): s = Workplane("XY").wedge(10, 10, 10, 5, 5, 5, 5) self.saveModel(s)