Skip to content

Commit f29f2d6

Browse files
dovadam-urbanczykjmwright
authored
Add shape and workplane support to the Workplane.eachpoint() function. Issue #1395 (#1578)
* Add shape and workplane support to the Workplane.eachpoint() function. #1395 * Added test coverage * Added tests for the useLocalCoordinates options * Union instead of | * Fix mypy and change error --------- Co-authored-by: AU <[email protected]> Co-authored-by: Jeremy Wright <[email protected]>
1 parent c6a827f commit f29f2d6

File tree

2 files changed

+81
-7
lines changed

2 files changed

+81
-7
lines changed

cadquery/cq.py

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2494,15 +2494,13 @@ def each(
24942494

24952495
def eachpoint(
24962496
self: T,
2497-
callback: Callable[[Location], Shape],
2497+
arg: Union[Shape, "Workplane", Callable[[Location], Shape]],
24982498
useLocalCoordinates: bool = False,
24992499
combine: CombineMode = False,
25002500
clean: bool = True,
25012501
) -> T:
25022502
"""
2503-
Same as each(), except each item on the stack is converted into a point before it
2504-
is passed into the callback function.
2505-
2503+
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.
25062504
:return: CadQuery object which contains a list of vectors (points ) on its stack.
25072505
25082506
:param useLocalCoordinates: should points be in local or global coordinates
@@ -2519,6 +2517,7 @@ def eachpoint(
25192517
If the stack has zero length, a single point is returned, which is the center of the current
25202518
workplane/coordinate system
25212519
"""
2520+
25222521
# convert stack to a list of points
25232522
pnts = []
25242523
plane = self.plane
@@ -2537,10 +2536,33 @@ def eachpoint(
25372536
else:
25382537
pnts.append(o)
25392538

2540-
if useLocalCoordinates:
2541-
res = [callback(p).move(loc) for p in pnts]
2539+
if isinstance(arg, Workplane):
2540+
if useLocalCoordinates:
2541+
res = [
2542+
v.moved(p).move(loc)
2543+
for v in arg.vals()
2544+
for p in pnts
2545+
if isinstance(v, Shape)
2546+
]
2547+
else:
2548+
res = [
2549+
v.moved(p * loc)
2550+
for v in arg.vals()
2551+
for p in pnts
2552+
if isinstance(v, Shape)
2553+
]
2554+
elif isinstance(arg, Shape):
2555+
if useLocalCoordinates:
2556+
res = [arg.moved(p).move(loc) for p in pnts]
2557+
else:
2558+
res = [arg.moved(p * loc) for p in pnts]
2559+
elif callable(arg):
2560+
if useLocalCoordinates:
2561+
res = [arg(p).move(loc) for p in pnts]
2562+
else:
2563+
res = [arg(p * loc) for p in pnts]
25422564
else:
2543-
res = [callback(p * loc) for p in pnts]
2565+
raise ValueError(f"{arg} is not supported")
25442566

25452567
for r in res:
25462568
if isinstance(r, Wire) and not r.forConstruction:

tests/test_cadquery.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5331,6 +5331,58 @@ def testEachpoint(self):
53315331
r = ref.vertices().eachpoint(lambda loc: box.moved(loc), combine="cut")
53325332
self.assertGreater(ref.val().Volume(), r.val().Volume())
53335333

5334+
# test eachpoint with a wire cutThru()
5335+
holeRadius = 1
5336+
wire = Wire.makeCircle(holeRadius, (0, 0, 0), (0, 0, 1))
5337+
boxHeight = 1
5338+
ref = Workplane("XY").box(10, 10, boxHeight)
5339+
r = (
5340+
ref.faces(">Z")
5341+
.rect(7, 7, forConstruction=True)
5342+
.vertices()
5343+
.eachpoint(wire)
5344+
.cutThruAll()
5345+
)
5346+
holeVolume = math.pi * holeRadius ** 2 * boxHeight
5347+
self.assertAlmostEqual(r.val().Volume(), ref.val().Volume() - holeVolume * 4)
5348+
5349+
# same with useLocalCoordinates, which should give the same result
5350+
holeRadius = 1
5351+
wire = Wire.makeCircle(holeRadius, (0, 0, 0), (0, 0, 1))
5352+
boxHeight = 1
5353+
ref = Workplane("XY").box(10, 10, boxHeight)
5354+
r = (
5355+
ref.faces(">Z")
5356+
.rect(7, 7, forConstruction=True)
5357+
.vertices()
5358+
.eachpoint(wire, useLocalCoordinates=True)
5359+
.cutThruAll()
5360+
)
5361+
holeVolume = math.pi * holeRadius ** 2 * boxHeight
5362+
self.assertAlmostEqual(r.val().Volume(), ref.val().Volume() - holeVolume * 4)
5363+
5364+
# test eachpoint with a workplane
5365+
box = Workplane().box(2, 2, 2)
5366+
sph = Workplane().sphere(1.0)
5367+
# Place the sphere in the center of each box face
5368+
r = box.faces().eachpoint(sph, combine=True)
5369+
self.assertAlmostEqual(
5370+
r.val().Volume(), box.val().Volume() + 3 * sph.val().Volume()
5371+
)
5372+
5373+
# same with useLocalCoordinates which should give the same result
5374+
box = Workplane().box(2, 2, 2)
5375+
sph = Workplane().sphere(1.0)
5376+
# Place the sphere in the center of each box face
5377+
r = box.faces().eachpoint(sph, combine=True, useLocalCoordinates=True)
5378+
self.assertAlmostEqual(
5379+
r.val().Volume(), box.val().Volume() + 3 * sph.val().Volume()
5380+
)
5381+
5382+
# Test that unknown types throw an exception
5383+
with self.assertRaises(ValueError) as cm:
5384+
box.faces().eachpoint(42) # Integers not allowed
5385+
53345386
def testSketch(self):
53355387

53365388
r1 = (

0 commit comments

Comments
 (0)