cadquery icon indicating copy to clipboard operation
cadquery copied to clipboard

Proper way to extrude a planar face along its normal?

Open raquo opened this issue 2 years ago • 2 comments

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.

raquo avatar Apr 05 '22 22:04 raquo

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

fedorkotov avatar Apr 06 '22 04:04 fedorkotov

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.

lorenzncode avatar Apr 08 '22 01:04 lorenzncode