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

Add shape and workplane support to the Workplane.eachpoint() function. Issue #1395 #1578

Merged
merged 9 commits into from
May 13, 2024
Merged
36 changes: 29 additions & 7 deletions cadquery/cq.py
Original file line number Diff line number Diff line change
Expand Up @@ -2494,15 +2494,13 @@ def each(

def eachpoint(
self: T,
callback: Callable[[Location], Shape],
arg: Union[Shape, "Workplane", Callable[[Location], Shape]],
useLocalCoordinates: bool = False,
combine: CombineMode = False,
clean: bool = True,
) -> T:
"""
Same as each(), except each item on the stack is converted into a point before it
is passed into the callback function.

Same as each(), except arg is translated by the positions on the stack. If arg is a callback function, then the function is called for each point on the stack, and the resulting shape is used.
:return: CadQuery object which contains a list of vectors (points ) on its stack.

:param useLocalCoordinates: should points be in local or global coordinates
Expand All @@ -2519,6 +2517,7 @@ def eachpoint(
If the stack has zero length, a single point is returned, which is the center of the current
workplane/coordinate system
"""

# convert stack to a list of points
pnts = []
plane = self.plane
Expand All @@ -2537,10 +2536,33 @@ def eachpoint(
else:
pnts.append(o)

if useLocalCoordinates:
res = [callback(p).move(loc) for p in pnts]
if isinstance(arg, Workplane):
if useLocalCoordinates:
res = [
v.moved(p).move(loc)
for v in arg.vals()
for p in pnts
if isinstance(v, Shape)
]
else:
res = [
v.moved(p * loc)
for v in arg.vals()
for p in pnts
if isinstance(v, Shape)
]
elif isinstance(arg, Shape):
if useLocalCoordinates:
res = [arg.moved(p).move(loc) for p in pnts]
else:
res = [arg.moved(p * loc) for p in pnts]
elif callable(arg):
if useLocalCoordinates:
res = [arg(p).move(loc) for p in pnts]
else:
res = [arg(p * loc) for p in pnts]
else:
res = [callback(p * loc) for p in pnts]
raise ValueError(f"{arg} is not supported")

for r in res:
if isinstance(r, Wire) and not r.forConstruction:
Expand Down
52 changes: 52 additions & 0 deletions tests/test_cadquery.py
Original file line number Diff line number Diff line change
Expand Up @@ -5331,6 +5331,58 @@ def testEachpoint(self):
r = ref.vertices().eachpoint(lambda loc: box.moved(loc), combine="cut")
self.assertGreater(ref.val().Volume(), r.val().Volume())

# test eachpoint with a wire cutThru()
holeRadius = 1
wire = Wire.makeCircle(holeRadius, (0, 0, 0), (0, 0, 1))
boxHeight = 1
ref = Workplane("XY").box(10, 10, boxHeight)
r = (
ref.faces(">Z")
.rect(7, 7, forConstruction=True)
.vertices()
.eachpoint(wire)
.cutThruAll()
)
holeVolume = math.pi * holeRadius ** 2 * boxHeight
self.assertAlmostEqual(r.val().Volume(), ref.val().Volume() - holeVolume * 4)

# same with useLocalCoordinates, which should give the same result
holeRadius = 1
wire = Wire.makeCircle(holeRadius, (0, 0, 0), (0, 0, 1))
boxHeight = 1
ref = Workplane("XY").box(10, 10, boxHeight)
r = (
ref.faces(">Z")
.rect(7, 7, forConstruction=True)
.vertices()
.eachpoint(wire, useLocalCoordinates=True)
.cutThruAll()
)
holeVolume = math.pi * holeRadius ** 2 * boxHeight
self.assertAlmostEqual(r.val().Volume(), ref.val().Volume() - holeVolume * 4)

# test eachpoint with a workplane
box = Workplane().box(2, 2, 2)
sph = Workplane().sphere(1.0)
# Place the sphere in the center of each box face
r = box.faces().eachpoint(sph, combine=True)
self.assertAlmostEqual(
r.val().Volume(), box.val().Volume() + 3 * sph.val().Volume()
)

# same with useLocalCoordinates which should give the same result
box = Workplane().box(2, 2, 2)
sph = Workplane().sphere(1.0)
# Place the sphere in the center of each box face
r = box.faces().eachpoint(sph, combine=True, useLocalCoordinates=True)
self.assertAlmostEqual(
r.val().Volume(), box.val().Volume() + 3 * sph.val().Volume()
)

# Test that unknown types throw an exception
with self.assertRaises(ValueError) as cm:
box.faces().eachpoint(42) # Integers not allowed

def testSketch(self):

r1 = (
Expand Down