Skip to content

Commit

Permalink
Add shape and workplane support to the Workplane.eachpoint() function…
Browse files Browse the repository at this point in the history
…. 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 <adam-urbanczyk@users.noreply.github.com>
Co-authored-by: Jeremy Wright <wrightjmf@gmail.com>
  • Loading branch information
3 people committed May 13, 2024
1 parent c6a827f commit f29f2d6
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 7 deletions.
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

0 comments on commit f29f2d6

Please sign in to comment.