Skip to content

Commit

Permalink
Centered option (#617)
Browse files Browse the repository at this point in the history
* Added 2d centered option to rect

* Added single bool center option to box

* Add single bool center option to sphere

* Added single bool center option to wedge

* Added 2d centered option to rarray

* Black fix

* Centered docstrings

Change all docstrings for centered options to be consistent and more
clear

* Docstring fixes

* Fix type hint on wedge method
  • Loading branch information
marcus7070 authored Feb 10, 2021
1 parent 184a985 commit b6beba7
Show file tree
Hide file tree
Showing 3 changed files with 245 additions and 95 deletions.
198 changes: 111 additions & 87 deletions cadquery/cq.py
Original file line number Diff line number Diff line change
Expand Up @@ -1320,7 +1320,7 @@ def rarray(
ySpacing: float,
xCount: int,
yCount: int,
center: bool = True,
center: Union[bool, Tuple[bool, bool]] = True,
) -> "Workplane":
"""
Creates an array of points and pushes them onto the stack.
Expand All @@ -1331,26 +1331,30 @@ def rarray(
:param ySpacing: spacing between points in the y direction ( must be > 0)
:param xCount: number of points ( > 0 )
:param yCount: number of points ( > 0 )
:param center: if true, the array will be centered at the center of the workplane. if
false, the lower left corner will be at the center of the work plane
:param center: If True, the array will be centered around the workplane center.
If False, the lower corner will be on the reference point and the array will
extend in the positive x and y directions. Can also use a 2-tuple to specify
centering along each axis.
"""

if xSpacing <= 0 or ySpacing <= 0 or xCount < 1 or yCount < 1:
raise ValueError("Spacing and count must be > 0 ")

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

lpoints = [] # coordinates relative to bottom left point
for x in range(xCount):
for y in range(yCount):
lpoints.append((xSpacing * x, ySpacing * y))
lpoints.append(Vector(xSpacing * x, ySpacing * y))

# shift points down and left relative to origin if requested
if center:
xc = xSpacing * (xCount - 1) * 0.5
yc = ySpacing * (yCount - 1) * 0.5
cpoints = []
for p in lpoints:
cpoints.append((p[0] - xc, p[1] - yc))
lpoints = list(cpoints)
offset = Vector()
if center[0]:
offset += Vector(-xSpacing * (xCount - 1) * 0.5, 0)
if center[1]:
offset += Vector(0, -ySpacing * (yCount - 1) * 0.5)
lpoints = [x + offset for x in lpoints]

return self.pushPoints(lpoints)

Expand Down Expand Up @@ -2214,7 +2218,7 @@ def rect(
self,
xLen: float,
yLen: float,
centered: bool = True,
centered: Union[bool, Tuple[bool, bool]] = True,
forConstruction: bool = False,
) -> "Workplane":
"""
Expand All @@ -2224,8 +2228,10 @@ def rect(
:type xLen: float > 0
:param yLen: length in yDirection ( in workplane coordinates )
:type yLen: float > 0
:param boolean centered: true if the rect is centered on the reference point, false if the
lower-left is on the reference point
:param centered: If True, the rectangle will be centered around the reference
point. If False, the corner of the rectangle will be on the reference point and
it will extend in the positive x and y directions. Can also use a 2-tuple to
specify centering along each axis.
:param forConstruction: should the new wires be reference geometry only?
:type forConstruction: true if the wires are for reference, false if they are creating part
geometry
Expand All @@ -2239,22 +2245,31 @@ def rect(
Creates 4 circles at the corners of a square centered on the origin.
Future Enhancements:
better way to handle forConstruction
project points not in the workplane plane onto the workplane plane
* project points not in the workplane plane onto the workplane plane
"""

if centered:
p1 = Vector(xLen / -2.0, yLen / -2.0, 0)
p2 = Vector(xLen / 2.0, yLen / -2.0, 0)
p3 = Vector(xLen / 2.0, yLen / 2.0, 0)
p4 = Vector(xLen / -2.0, yLen / 2.0, 0)
else:
p1 = Vector()
p2 = Vector(xLen, 0, 0)
p3 = Vector(xLen, yLen, 0)
p4 = Vector(0, yLen, 0)
if isinstance(centered, bool):
centered = (centered, centered)

offset = Vector()
if not centered[0]:
offset += Vector(xLen / 2, 0, 0)
if not centered[1]:
offset += Vector(0, yLen / 2, 0)

points = [
Vector(xLen / -2.0, yLen / -2.0, 0),
Vector(xLen / 2.0, yLen / -2.0, 0),
Vector(xLen / 2.0, yLen / 2.0, 0),
Vector(xLen / -2.0, yLen / 2.0, 0),
]

points = [x + offset for x in points]

# close the wire
points.append(points[0])

w = Wire.makePolygon([p1, p2, p3, p4, p1], forConstruction)
w = Wire.makePolygon(points, forConstruction)

return self.eachpoint(lambda loc: w.moved(loc), True)

Expand Down Expand Up @@ -3342,7 +3357,7 @@ def box(
length: float,
width: float,
height: float,
centered: Tuple[bool, bool, bool] = (True, True, True),
centered: Union[bool, Tuple[bool, bool, bool]] = True,
combine: bool = True,
clean: bool = True,
) -> "Workplane":
Expand All @@ -3355,48 +3370,55 @@ def box(
:type width: float > 0
:param height: box size in Z direction
:type height: float > 0
:param centered: should the box be centered, or should reference point be at the lower
bound of the range?
:param centered: If True, the box will be centered around the reference point.
If False, the corner of the box 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: should the results be combined with other solids on the stack
(and each other)?
:type combine: true to combine shapes, false otherwise.
:param boolean clean: call :py:meth:`clean` afterwards to have a clean shape
Centered is a tuple that describes whether the box should be centered on the x,y, and
z axes. If true, the box is centered on the respective axis relative to the workplane
origin, if false, the workplane center will represent the lower bound of the resulting box
:param clean: call :py:meth:`clean` afterwards to have a clean shape
one box is created for each item on the current stack. If no items are on the stack, one box
One box is created for each item on the current stack. If no items are on the stack, one box
using the current workplane center is created.
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 boxes produced
fused onto it otherwise, the result is the combination of all the produced boxes
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 boxes produced fused onto it otherwise, the
result is the combination of all the produced boxes.
if combine is false, the result will be a list of the boxes produced
If combine is false, the result will be a list of the boxes produced.
Most often boxes form the basis for a part::
#make a single box with lower left corner at origin
s = Workplane().box(1,2,3,centered=(False,False,False)
# make a single box with lower left corner at origin
s = Workplane().box(1, 2, 3, centered=False)
But sometimes it is useful to create an array of them:
But sometimes it is useful to create an array of them::
#create 4 small square bumps on a larger base plate:
s = Workplane().box(4,4,0.5).faces(">Z").workplane()\
.rect(3,3,forConstruction=True).vertices().box(0.25,0.25,0.25,combine=True)
# create 4 small square bumps on a larger base plate:
s = (
Workplane().
box(4, 4, 0.5).
faces(">Z").
workplane().
rect(3, 3, forConstruction=True)
.vertices()
.box(0.25, 0.25, 0.25, combine=True)
)
"""

(xp, yp, zp) = (0.0, 0.0, 0.0)
if isinstance(centered, bool):
centered = (centered, centered, centered)

offset = Vector()
if centered[0]:
xp -= length / 2.0
offset += Vector(-length / 2, 0, 0)
if centered[1]:
yp -= width / 2.0
offset += Vector(0, -width / 2, 0)
if centered[2]:
zp -= height / 2.0
offset += Vector(0, 0, -height / 2)

box = Solid.makeBox(length, width, height, Vector(xp, yp, zp))
box = Solid.makeBox(length, width, height, offset)

boxes = self.eachpoint(lambda loc: box.moved(loc), True)

Expand All @@ -3414,7 +3436,7 @@ def sphere(
angle1: float = -90,
angle2: float = 90,
angle3: float = 360,
centered: Tuple[bool, bool, bool] = (True, True, True),
centered: Union[bool, Tuple[bool, bool, bool]] = True,
combine: bool = True,
clean: bool = True,
) -> "Workplane":
Expand All @@ -3431,44 +3453,42 @@ def sphere(
:type angle2: float > 0
:param angle3: The third angle to sweep the sphere arc through
:type angle3: float > 0
:param centered: A three-tuple of booleans that determines whether the sphere is centered
on each axis origin
:param centered: If True, the sphere will be centered around the reference point. If False,
the corner of a bounding box around the sphere 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 sphere object for each point on the stack
Centered is a tuple that describes whether the sphere should be centered on the x,y, and
z axes. If true, the sphere is centered on the respective axis relative to the workplane
origin, if false, the workplane center will represent the lower bound of the resulting
sphere.
One sphere is created for each item on the current stack. If no items are on the stack, one
box using the current workplane center is created.
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 spheres produced
fused onto it otherwise, the result is the combination of all the produced boxes
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 spheres produced fused onto it otherwise,
the result is the combination of all the produced spheres.
If combine is false, the result will be a list of the spheres produced
If combine is false, the result will be a list of the spheres produced.
"""

# Convert the direction tuple to a vector, if needed
if isinstance(direct, tuple):
direct = Vector(direct)

(xp, yp, zp) = (0.0, 0.0, 0.0)
if isinstance(centered, bool):
centered = (centered, centered, centered)

offset = Vector()
if not centered[0]:
xp += radius

offset += Vector(radius, 0, 0)
if not centered[1]:
yp += radius

offset += Vector(0, radius, 0)
if not centered[2]:
zp += radius
offset += Vector(0, 0, radius)

s = Solid.makeSphere(radius, Vector(xp, yp, zp), direct, angle1, angle2, angle3)
s = Solid.makeSphere(radius, offset, direct, angle1, angle2, angle3)

# We want a sphere for each point on the workplane
spheres = self.eachpoint(lambda loc: s.moved(loc), True)
Expand All @@ -3490,7 +3510,7 @@ def wedge(
zmax: float,
pnt: VectorLike = Vector(0, 0, 0),
dir: VectorLike = Vector(0, 0, 1),
centered: Tuple[bool, bool, bool] = (True, True, True),
centered: Union[bool, Tuple[bool, bool, bool]] = True,
combine: bool = True,
clean: bool = True,
) -> "Workplane":
Expand All @@ -3499,24 +3519,28 @@ def wedge(
:param dy: Distance along the Y axis
:param dz: Distance along the Z axis
:param xmin: The minimum X location
:param zmin:The minimum Z location
:param xmax:The maximum X location
:param zmin: The minimum Z location
:param xmax: The maximum X location
:param zmax: The maximum Z location
:param pnt: A vector (or tuple) for the origin of the direction for the wedge
:param dir: The direction vector (or tuple) for the major axis of the wedge
:param centered: If True, the wedge will be centered around the reference point.
If False, the corner of the wedge 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)
:param clean: true to attempt to have the kernel clean up the geometry, false otherwise
(and each other)
:param clean: True to attempt to have the kernel clean up the geometry, False otherwise
:return: A wedge object for each point on the stack
One wedge is created for each item on the current stack. If no items are on the stack, one
wedge using the current workplane center is created.
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 wedges produced
fused onto it otherwise, the result is the combination of all the produced wedges
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 wedges produced fused onto it otherwise,
the result is the combination of all the produced wedges.
If combine is false, the result will be a list of the wedges produced
If combine is False, the result will be a list of the wedges produced.
"""

# Convert the point tuple to a vector, if needed
Expand All @@ -3527,18 +3551,18 @@ def wedge(
if isinstance(dir, tuple):
dir = Vector(dir)

(xp, yp, zp) = (0.0, 0.0, 0.0)
if isinstance(centered, bool):
centered = (centered, centered, centered)

offset = Vector()
if centered[0]:
xp -= dx / 2.0

offset += Vector(-dx / 2, 0, 0)
if centered[1]:
yp -= dy / 2.0

offset += Vector(0, -dy / 2, 0)
if centered[2]:
zp -= dz / 2.0
offset += Vector(0, 0, -dz / 2)

w = Solid.makeWedge(dx, dy, dz, xmin, zmin, xmax, zmax, Vector(xp, yp, zp), dir)
w = Solid.makeWedge(dx, dy, dz, xmin, zmin, xmax, zmax, offset, dir)

# We want a wedge for each point on the workplane
wedges = self.eachpoint(lambda loc: w.moved(loc), True)
Expand Down
4 changes: 2 additions & 2 deletions doc/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1093,7 +1093,7 @@ Braille Example
base_height = get_plate_height(text_lines, cell_geometry)
base_thickness = get_base_plate_thickness(plate_thickness, cell_geometry)
base = cq.Workplane('XY').box(base_width, base_height, base_thickness,
centered=(False, False, False))
centered=False)
return base


Expand Down Expand Up @@ -1126,7 +1126,7 @@ Braille Example
base = (base.faces('>Z').edges()
.fillet(r - 0.001))
hidding_box = cq.Workplane('XY').box(
base_width, base_height, base_thickness, centered=(False, False, False))
base_width, base_height, base_thickness, centered=False)
result = hidding_box.union(base)
return result

Expand Down
Loading

0 comments on commit b6beba7

Please sign in to comment.