CadQuery: Selecting an edge by index (Filleting specific edges)

1k views Asked by At

I come from the engineering CAD world and I'm creating some designs in CadQuery. What I want to do is this (pseudocode):

edges = part.edges()
edges[n].fillet(r)

Or ideally have the ability to do something like this (though I can't find any methods for edge properties). Pseudocode:

edges = part.edges()
for edge in edges:
    if edge.length() > x:
        edge.fillet(a)
    else:
        edge.fillet(b)

This would be very useful when a design contains non-orthogonal faces. I understand that I can select edges with selectors, but I find them unnecessarily complicated and work best with orthogonal faces. FreeCAD lets you treat edges as a list.

I believe there might be a method to select the closest edge to a point, but I can't seem to track it down.

If someone can provide guidance that would be great -- thank you!

Bonus question: Is there a way to return coordinates of geometry as a list or vector? e.g.:

origin = cq.workplane.center().val
>> [x,y,z]

(or something like the above)

4

There are 4 answers

0
NANDHA KUMAR On

Take a look at this code, i hope this will be helpful.

import cadquery as cq

plane1 = cq.Workplane()

block = plane1.rect(10,12).extrude(10)

edges = block.edges("|Z")

filleted_block = edges.all()[0].fillet(0.5)

show(filleted_block)
0
ItsNotFunny On

For the posterity. To select multiple edges eg. for chamfering you can use newObject() on Workplane. The argument is a list of edges (they have to be cq.occ_impl.shapes.Edge instances, NOT cq.Workplane instances).

import cadquery as cq

model = cq.Workplane().box(10, 10, 5)
edges = model.edges()

# edges.all() returns worplanes, we have to get underlying geometry
selected = list(map(lambda x: x.objects[0], edges.all()))

model_with_chamfer = model.newObject(selected).chamfer(1)

To get edge length you can do something like this:

edge = model.edges().all()[0] # This select one 'random' edge
length = edge.objects[0].Length()

edge.Length() doesn't work since edge is Workplane instance, not geometry instance.

To get edges of certain length you can just create dict with edge geometry and length and filter it using builtin python's filter(). Here is a snippet of my implementation for chamfering short edges on topmost face:

top_edges = model.edges(">Z and #Z")

def get_length(edge):
    try:
        return edge.vals()[0].Length()
    except Exception:
        return 0.0

# Inside edges are shorter - filter only those
edge_len_list = list(map(
    lambda x: (x.objects[0], get_length(x)),
    top_edges.all()))
avg = mean([a for _, a in edge_len_list])
selected = filter(lambda x: x[1] < avg, edge_len_list)
selected = [e for e, _ in selected]

vertical_edges = model.edges("|Z").all()
selected.extend(vertical_edges)

model = model.newObject(selected)
model = model.chamfer(chamfer_size)
1
Mario Cuéllar On
base = (
    cq.Workplane("XY").box(50,60,15)
    .faces(">Z")
    .rect(30,30,forConstruction=True)
    .vertices().rect(10,5).cutBlind(until=-10)
)

edges = base.edges("|Z").all()
edges.sort( key=(lambda x: x.val().Length() ))

for ed in edges[:-4]:
    base = base.union( ed.fillet(2))
0
Pa Dalton On

A different approach would be to work with solids (and therefore cq.occ_impl.shapes directly) instead of the workplane, and then turn back to the workplane once the object has been champfered.
With this approach you can shorten @ItsNotFunny's answer, since you will avoid the use of custom edge length but use instead directly the built-in ones.

A short example:

import cadquery as cq

box = cq.Solid.makeBox(3,4,5)

# here you can code the condition to accept an edge or not
# you can use .Length()->float or .Center()->cq.Vector
# selector = lambda edge: edge.Length() == 3
selector = lambda edge: edge.Center().Length > 5.5

# create a list containing all edges to fillet (or chamfer)
edge_selection = [edge for edge in box.Edges() if selector(edge)]

# apply fillet (or chamfer)
chamfered_box = box.chamfer(0.6, None, edge_selection)
filleted_box  = box.fillet(0.6, edge_selection)

# check
reset_show()
show_object(chamfered_box)
show_object(filleted_box.translate((0,0,6)))

# you can then go back to a workplane using
cq.workplane().add(filleted_box)