Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added cylinder 3D primitive to Workplane. #858

Merged
merged 6 commits into from
Sep 1, 2021
Merged
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
63 changes: 63 additions & 0 deletions cadquery/cq.py
Original file line number Diff line number Diff line change
Expand Up @@ -3800,6 +3800,69 @@ def sphere(
else:
return self.union(spheres, clean=clean)

def cylinder(
self: T,
height: float,
radius: float,
direct: Vector = Vector(0, 0, 1),
angle: float = 360,
centered: Union[bool, Tuple[bool, bool, bool]] = True,
combine: bool = True,
clean: bool = True,
) -> T:
"""
Returns a cylinder with the specified radius and height for each point on the stack

:param height: The height of the cylinder
:type height: float > 0
:param radius: The radius of the cylinder
:type radius: float > 0
:param direct: The direction axis for the creation of the cylinder
:type direct: A three-tuple
:param angle: The angle to sweep the cylinder arc through
:type angle: float > 0
:param centered: If True, the cylinder will be centered around the reference point. If False,
the corner of a bounding box around the cylinder will be on the reference point and it
will extend in the positive x, y and z directions. Can also use a 3-tuple to specify
centering along each axis.
: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 cylinder object for each point on the stack

One cylinder is created for each item on the current stack. If no items are on the stack, one
cylinder 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 cylinders produced fused onto it otherwise,
the result is the combination of all the produced cylinders.

If combine is false, the result will be a list of the cylinders produced.
"""

if isinstance(centered, bool):
centered = (centered, centered, centered)

offset = Vector()
if not centered[0]:
offset += Vector(radius, 0, 0)
if not centered[1]:
offset += Vector(0, radius, 0)
if centered[2]:
offset += Vector(0, 0, -height / 2)

s = Solid.makeCylinder(radius, height, offset, direct, angle)

# We want a cylinder for each point on the workplane
cylinders = self.eachpoint(lambda loc: s.moved(loc), True)

# If we don't need to combine everything, just return the created cylinders
if not combine:
return cylinders
else:
return self.union(cylinders, clean=clean)

def wedge(
self: T,
dx: float,
Expand Down
1 change: 1 addition & 0 deletions doc/apireference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ Some 3-d operations also require an active 2-d workplane, but some do not.
Workplane.cutThruAll
Workplane.box
Workplane.sphere
Workplane.cylinder
Workplane.union
Workplane.combine
Workplane.intersect
Expand Down
3 changes: 1 addition & 2 deletions doc/roadmap.rst
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,8 @@ rotation/transform that return a copy
primitive creation
Need primitive creation for:
* cone
* cylinder
* torus
* wedge

extrude/cut up to surface
allow a cut or extrude to terminate at another surface, rather than either through all or a fixed distance
allow a cut or extrude to terminate at another surface, rather than either through all or a fixed distance
33 changes: 33 additions & 0 deletions tests/test_cadquery.py
Original file line number Diff line number Diff line change
Expand Up @@ -2443,6 +2443,39 @@ def testSphereCombine(self):
self.assertEqual(1, s.solids().size())
self.assertEqual(4, s.faces().size())

def testCylinderDefaults(self):
s = Workplane("XY").cylinder(20, 10)
self.assertEqual(1, s.size())
self.assertEqual(1, s.solids().size())
self.assertEqual(3, s.faces().size())
self.assertEqual(2, s.vertices().size())
self.assertTupleAlmostEquals(s.val().Center().toTuple(), (0, 0, 0), 3)

def testCylinderCentering(self):
radius = 10
height = 40
b = (True, False)
expected_x = (0, radius)
expected_y = (0, radius)
expected_z = (0, height / 2)
for (xopt, xval), (yopt, yval), (zopt, zval) in product(
zip(b, expected_x), zip(b, expected_y), zip(b, expected_z)
):
s = Workplane("XY").cylinder(height, radius, centered=(xopt, yopt, zopt))
self.assertEqual(1, s.size())
self.assertTupleAlmostEquals(
s.val().Center().toTuple(), (xval, yval, zval), 3
)
# check centered=True produces the same result as centered=(True, True, True)
for val in b:
s0 = Workplane("XY").cylinder(height, radius, centered=val)
self.assertEqual(s0.size(), 1)
s1 = Workplane("XY").cylinder(height, radius, centered=(val, val, val))
self.assertEqual(s1.size(), 1)
self.assertTupleAlmostEquals(
s0.val().Center().toTuple(), s1.val().Center().toTuple(), 3
)

def testWedgeDefaults(self):
s = Workplane("XY").wedge(10, 10, 10, 5, 5, 5, 5)
self.saveModel(s)
Expand Down