Skip to content

Commit 75ea778

Browse files
authored
Merge pull request #858 from martinbudden/cylinder
Added cylinder 3D primitive to Workplane.
2 parents fe81d33 + 00083ed commit 75ea778

File tree

4 files changed

+97
-1
lines changed

4 files changed

+97
-1
lines changed

cadquery/cq.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3800,6 +3800,69 @@ def sphere(
38003800
else:
38013801
return self.union(spheres, clean=clean)
38023802

3803+
def cylinder(
3804+
self: T,
3805+
height: float,
3806+
radius: float,
3807+
direct: Vector = Vector(0, 0, 1),
3808+
angle: float = 360,
3809+
centered: Union[bool, Tuple[bool, bool, bool]] = True,
3810+
combine: bool = True,
3811+
clean: bool = True,
3812+
) -> T:
3813+
"""
3814+
Returns a cylinder with the specified radius and height for each point on the stack
3815+
3816+
:param height: The height of the cylinder
3817+
:type height: float > 0
3818+
:param radius: The radius of the cylinder
3819+
:type radius: float > 0
3820+
:param direct: The direction axis for the creation of the cylinder
3821+
:type direct: A three-tuple
3822+
:param angle: The angle to sweep the cylinder arc through
3823+
:type angle: float > 0
3824+
:param centered: If True, the cylinder will be centered around the reference point. If False,
3825+
the corner of a bounding box around the cylinder will be on the reference point and it
3826+
will extend in the positive x, y and z directions. Can also use a 3-tuple to specify
3827+
centering along each axis.
3828+
:param combine: Whether the results should be combined with other solids on the stack
3829+
(and each other)
3830+
:type combine: true to combine shapes, false otherwise
3831+
:param clean: call :py:meth:`clean` afterwards to have a clean shape
3832+
:return: A cylinder object for each point on the stack
3833+
3834+
One cylinder is created for each item on the current stack. If no items are on the stack, one
3835+
cylinder is created using the current workplane center.
3836+
3837+
If combine is true, the result will be a single object on the stack. If a solid was found
3838+
in the chain, the result is that solid with all cylinders produced fused onto it otherwise,
3839+
the result is the combination of all the produced cylinders.
3840+
3841+
If combine is false, the result will be a list of the cylinders produced.
3842+
"""
3843+
3844+
if isinstance(centered, bool):
3845+
centered = (centered, centered, centered)
3846+
3847+
offset = Vector()
3848+
if not centered[0]:
3849+
offset += Vector(radius, 0, 0)
3850+
if not centered[1]:
3851+
offset += Vector(0, radius, 0)
3852+
if centered[2]:
3853+
offset += Vector(0, 0, -height / 2)
3854+
3855+
s = Solid.makeCylinder(radius, height, offset, direct, angle)
3856+
3857+
# We want a cylinder for each point on the workplane
3858+
cylinders = self.eachpoint(lambda loc: s.moved(loc), True)
3859+
3860+
# If we don't need to combine everything, just return the created cylinders
3861+
if not combine:
3862+
return cylinders
3863+
else:
3864+
return self.union(cylinders, clean=clean)
3865+
38033866
def wedge(
38043867
self: T,
38053868
dx: float,

doc/apireference.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ Some 3D operations also require an active 2D workplane, but some do not.
9090
Workplane.cutThruAll
9191
Workplane.box
9292
Workplane.sphere
93+
Workplane.cylinder
9394
Workplane.union
9495
Workplane.combine
9596
Workplane.intersect

doc/roadmap.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,6 @@ rotation/transform that return a copy
8181
primitive creation
8282
Need primitive creation for:
8383
* cone
84-
* cylinder
8584
* torus
8685
* wedge
8786

tests/test_cadquery.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2443,6 +2443,39 @@ def testSphereCombine(self):
24432443
self.assertEqual(1, s.solids().size())
24442444
self.assertEqual(4, s.faces().size())
24452445

2446+
def testCylinderDefaults(self):
2447+
s = Workplane("XY").cylinder(20, 10)
2448+
self.assertEqual(1, s.size())
2449+
self.assertEqual(1, s.solids().size())
2450+
self.assertEqual(3, s.faces().size())
2451+
self.assertEqual(2, s.vertices().size())
2452+
self.assertTupleAlmostEquals(s.val().Center().toTuple(), (0, 0, 0), 3)
2453+
2454+
def testCylinderCentering(self):
2455+
radius = 10
2456+
height = 40
2457+
b = (True, False)
2458+
expected_x = (0, radius)
2459+
expected_y = (0, radius)
2460+
expected_z = (0, height / 2)
2461+
for (xopt, xval), (yopt, yval), (zopt, zval) in product(
2462+
zip(b, expected_x), zip(b, expected_y), zip(b, expected_z)
2463+
):
2464+
s = Workplane("XY").cylinder(height, radius, centered=(xopt, yopt, zopt))
2465+
self.assertEqual(1, s.size())
2466+
self.assertTupleAlmostEquals(
2467+
s.val().Center().toTuple(), (xval, yval, zval), 3
2468+
)
2469+
# check centered=True produces the same result as centered=(True, True, True)
2470+
for val in b:
2471+
s0 = Workplane("XY").cylinder(height, radius, centered=val)
2472+
self.assertEqual(s0.size(), 1)
2473+
s1 = Workplane("XY").cylinder(height, radius, centered=(val, val, val))
2474+
self.assertEqual(s1.size(), 1)
2475+
self.assertTupleAlmostEquals(
2476+
s0.val().Center().toTuple(), s1.val().Center().toTuple(), 3
2477+
)
2478+
24462479
def testWedgeDefaults(self):
24472480
s = Workplane("XY").wedge(10, 10, 10, 5, 5, 5, 5)
24482481
self.saveModel(s)

0 commit comments

Comments
 (0)