cadquery
cadquery copied to clipboard
Proper way to extrude a planar face along its normal?
Hello, I've seen this snippet given as the way to extrude a planar face:
cq.Workplane().box(10, 20, 30).faces('>Z').wires().toPending().extrude(10, taper=15)
However, it does not work for all planar faces. For example, changing >Z
to >Y
causes a "ValueError: Null TopoDS_Shape object" error, even though fundamentally there should be no reason for it, I think.
The reason seems to be that Workplane's extrude
method hardcodes the direction of extrusion as self.plane.zDir
(source), and I guess selecting .faces()
does not affect self.plane
.
When extruding a face, I would expect extrude direction to default to the selected face's normal, not self.plane
normal. I guess the same would apply to extruding any supported wire, although perhaps there isn't enough information in the wire to know which of the two sides the normal is pointing to (if that makes sense).
Perhaps CadQuery could let users provide a custom extrusion direction to extrude
, and also default to the selected face's normal when using this (currently unsupported) syntax: .faces('>Z').extrude(10, taper=15)
? That would be backwards-compatible, although I don't know if that's actually possible.
I found an old suggestion to just force-override self.plane.zDir
along with a warning not to do that. So, building on that idea, the following appears to work:
def extrudeFaces(wp: Workplane, selector: str, until: float, taper: float = 0):
originalNormal = wp.plane.zDir
faceWp = wp.faces(selector)
faceNormal = faceWp.val().normalAt()
faceWp.plane.zDir = faceNormal
result = faceWp.wires().toPending().extrude(until, taper = taper)
faceWp.plane.zDir = originalNormal
return result
extrudeFaces(cq.Workplane("XY").box(10, 20, 30), '>Y', until = 5, taper = 15)
I'm not sure what are the risks of messing with plane.zDir
this way. Is the above a good / safe approach to the problem? If yes, maybe CadQuery could have something like that built-in?
It's entirely possible that I've missed some obvious feature / mechanic of CadQuery, but the above is the best I got at the moment.
You can do this with Workplane.each(..)
. Either manually or using my plugin https://github.com/CadQuery/cadquery-plugins/tree/main/plugins/apply_to_each_face
See also #713
However, it does not work for all planar faces.
Try calling workplane
.
r = (
cq.Workplane()
.box(10, 20, 30)
.faces(">Y")
.tag("yface")
.workplane()
.faces(tag="yface")
.wires()
.toPending()
.extrude(10, taper=15)
)
It could also be done without tags.