cadquery icon indicating copy to clipboard operation
cadquery copied to clipboard

Selected faces must be co-planar

Open jniclas opened this issue 2 years ago • 6 comments
trafficstars

Hi, I have the following code snippet:

def perFace(face: cq.Workplane):
    return (
        face.box(2, 2, 2)
    )
part = (
    cq.Workplane("XY")
    .polygon(6, (radius + wall + shift)*2, circumscribed=True)
    .extrude(-partHeight)
)
part = (
    part.faces("#Z")
    .workplane()
    .each(perFace)
)

I am trying to apply the box to each side face of the polygon, but I get the error ValueError: Selected faces must be co-planar How can I repeat the operation in the function perFace for each selected face? faces("#Z").workplane().each(perFace) seems not to work 🤔

jniclas avatar Jun 15 '23 05:06 jniclas

I got it running now, but very ugly. Is there a better way?

faceCenters: list[cq.Vector] = []
def perFace(face: cq.Face):
    faceCenters.append(face.CenterOfBoundBox())
    return face
part = (
    cq.Workplane("XY")
    .polygon(6, (radius + wall + shift)*2, circumscribed=True)
    .extrude(-partHeight)
)
part = (
    part.faces("#Z")
    .each(perFace, combine=False)
)
for faceCenter in faceCenters:
    part = (
        part
        .faces(cq.NearestToPointSelector((faceCenter.x, faceCenter.y, faceCenter.z)))
        .workplane(centerOption="CenterOfBoundBox")
        .box(2, 2, 2)
    )

And why .faces(cq.NearestToPointSelector(faceCenter)) throws the error TypeError: cadquery.occ_impl.geom.Vector() argument after * must be an iterable, not Vector?

jniclas avatar Jun 15 '23 06:06 jniclas

There's a plugin for this. Even if you don't use it, maybe it's got some code you could reuse. https://github.com/CadQuery/cadquery-plugins/tree/main/plugins/apply_to_each_face

jmwright avatar Jun 15 '23 11:06 jmwright

Ah cool, thank you 👍🏻

jniclas avatar Jun 15 '23 12:06 jniclas

@jniclas note that each requires callables with the following signature:

Callable[[Union[cadquery.occ_impl.geom.Vector, cadquery.occ_impl.geom.Location, cadquery.occ_impl.shapes.Shape, cadquery.sketch.Sketch]], cadquery.occ_impl.shapes.Shape]

Yours was returning a Workplane and not a Shape.

adam-urbanczyk avatar Jun 15 '23 18:06 adam-urbanczyk

@adam-urbanczyk I can not figure out how to add shapes with the each method, when the argument is a Face, and without using the plugin. Could you give me a hint on that sinppet?:

def perFace(face: cq.Face):
    return (
        ...?
        .box(2, 2, 2)
    )

part = (
    cq.Workplane("XY")
    .polygon(6, 10, circumscribed=True)
    .extrude(50)
    .faces("#Z")
    .each(perFace)
)

jniclas avatar Jun 17 '23 18:06 jniclas

The point was that you need to return a Shape and not a Workplane. So if you insist on using cq.Workplane, it could be:

def perFace(face: cq.Face):
    return cq.Workplane(origin=face.Center()).box(1,1,1).val()

But I don't know if this is what you actually want.

adam-urbanczyk avatar Jun 18 '23 16:06 adam-urbanczyk