Selecting edges in a script

Need help, or want to share a macro? Post here!
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
User avatar
instance
Posts: 20
Joined: Tue Jul 14, 2015 1:28 am
Location: Ontario, Canada
Contact:

Selecting edges in a script

Post by instance »

This is a general question about selecting edges (or faces, or anything really) in a repeatable way from a script. Here's a specific case:

I have script that creates a box-like shape into which a number of channels have been cut:
fc1.jpg
fc1.jpg (42.45 KiB) Viewed 1362 times
I want the script to chamfer the inside edges of the top face, the vertical edges of the tabs, and the top front edge.

Clever me, I figure I'll do this manually, use ObjectsToPython to export the shape, extract a list of edges, and then code those into the script! This works... except the edges aren't assigned in the same order. I get a lovely mess. With 260 or so edges, trying to get this right by iterating through would be crazy.

Is there a way to either compute what the edge numbers would be in a case like this or to do something like "select all edges in this bounding box"?

Or is there another approach I'm just not seeing?
User avatar
onekk
Veteran
Posts: 6144
Joined: Sat Jan 17, 2015 7:48 am
Contact:

Re: Selecting edges in a script

Post by onekk »

Hello it depends in how you have created the shapes.

If you think of scripting a solid like you "think in the gui" you will complicate things.

as the edges seems created hollowing (subtracting a shape) multiple time, you could reduce the problem creating an hollowing shape that will subtract a portion of toroid on top and bottom so a slightly complicate "hollowing shape" will reduce many further operation.

or you could selet the wires that are on the upper face and offset them creating paths to make a sweep or a pipeshell that will subtract things.

or you could create the base, position the hokes as wires and reuse these wires directly to obtain the paths to mske the sweeps.

or ....

But if the shape is derived say from a step file you have to obtain wires, so you could obtain the upper face, use his wires and find a geometric way to select the proper edges and...

So how did you made the solid?

Plus, put the info required in the post atarting with "IMPORTANT:" in the top part of the forum so we have an idea of what version of FC you are using.


Some code is very useful if you have build the shape from scratch so maybe someone that want help you should do less guesses and be more "centered" on your problem.

Regards

Carlo D.
GitHub page: https://github.com/onekk/freecad-doc.
- In deep articles on FreeCAD.
- Learning how to model with scripting.
- Various other stuffs.

Blog: https://okkmkblog.wordpress.com/
User avatar
dprojects
Posts: 721
Joined: Mon Mar 06, 2017 6:02 pm
Location: Poland
Contact:

Re: Selecting edges in a script

Post by dprojects »

I use chamfer to create picture frame from cubes. I select faces and search for edges instead. But I select cubes and cut pads ;-) I guess it is TNP because the faces order are different at pad and the cube no longer exists ;-)

If you want to make chamfer from selected edge it would be even simpler to do. But this is the same problem as I asking here get face number from face object. If you will select the edges you get list of objects but chamfer expect to get list of edge labels (they are strings). As I remember there is no such thing like Edge.Label

Image

Thanks
Darek
github.com/dprojects

workbench for woodworking is available at: github.com/dprojects/Woodworking
User avatar
instance
Posts: 20
Joined: Tue Jul 14, 2015 1:28 am
Location: Ontario, Canada
Contact:

Re: Selecting edges in a script

Post by instance »

onekk wrote: Sun Jul 31, 2022 2:57 pm
So how did you made the solid?

Plus, put the info required in the post atarting with "IMPORTANT:" in the top part of the forum so we have an idea of what version of FC you are using.


Some code is very useful if you have build the shape from scratch so maybe someone that want help you should do less guesses and be more "centered" on your problem.
Hi Carlo,

Good points, although what I'm really hoping for is a more generalized approach so I can solve the same kind of problem in the future.
Running FC 0.20 here's the code that creates the shape. A couple of notes:

box.insert returns a set of points that contains the XY profile of the overall shape.
h.xyz() returns a Vector.

Code: Select all

    def make(self, size, width, depth, height):
        box = StorageBox.StorageBox()
        points = box.insert(width, depth)
        margin_x = 9
        count_x = int((box.size_x - 2 * margin_x + 2) // (size + 2))
        extent_x = count_x * (size + 2) - 2
        offset_x = (box.size_x - extent_x) / 2
        margin_y = 3 + 5
        range_z = height * box.unit_height - box.floor_thickness - 3
        shift = range_z * math.sin(math.radians(self.tilt))
        size_y = size + shift
        count_y = int((box.size_y - shift - 2 *margin_y + 2) // (size + 2))
        extent_y = count_y * (size + 2) - 2 + shift
        offset_y = 3

        # Create a solid insert
        insert = h.poly_to_face(points).extrude(h.xyz(z=range_z))

        # Cut the holes out of the insert
        # Make a quadrilateral face in the YZ plane to represent the tilted profile,
        # Extrude in X to get the tilted prism.
        hole_points = [
            h.xyz(), h.xyz(y=size), h.xyz(0, size_y, range_z), h.xyz(0, shift, range_z), h.xyz()
        ]
        hole = h.poly_to_face(hole_points).extrude(h.xyz(size))

        for x in range(count_x):
            for y in range(count_y):
                hole.Placement = Placement(h.xyz(offset_x + x * (size + 2), offset_y + y * (size + 2)), Rotation())
                insert = insert.cut(hole)

        # Cut Y channels out
        channel_points = [
            h.xyz(), h.xyz(y=extent_y - shift), h.xyz(0, extent_y, range_z), h.xyz(0, 0, range_z), h.xyz()
        ]
        tab = size / 3
        channel = h.poly_to_face(channel_points).extrude(h.xyz(size - 2 * tab))
        for x in range(count_x):
            channel.Placement = Placement(h.xyz(offset_x + tab + x * (size + 2)), Rotation())
            insert = insert.cut(channel)
edwilliams16
Veteran
Posts: 3106
Joined: Thu Sep 24, 2020 10:31 pm
Location: Hawaii
Contact:

Re: Selecting edges in a script

Post by edwilliams16 »

You could select edges inside a selection cube of your choice in your script this way:

Code: Select all

def edgesInBoundBox(shp, xmin, ymin, zmin, xmax, ymax, zmax, all = True, show = True):
    ''' return indices of edges of shp in the cube BB
        all = True: completely enclosed
        all = False: at least one vertex enclosed
        show = True: display wireframe bounds'''
    bb = App.BoundBox(xmin, ymin, zmin, xmax, ymax, zmax)
    if show:
        bbBox = Part.makeBox(xmax - xmin, ymax - ymin, zmax - zmin, App.Vector(xmin, ymin, zmin))
        part = Part.show(bbBox)
        part.ViewObject.DisplayMode = 'Wireframe'
    indList = []
    for i, edge in enumerate(shp.Edges):
        if all:
            if bb.isInside(edge.BoundBox):
               indList.append(i)
        else:
            if bb.isInside(edge.Vertexes[0].Point) or bb.isInside(edge.Vertexes[1].Point):
               indList.append(i)
    return indList
User avatar
instance
Posts: 20
Joined: Tue Jul 14, 2015 1:28 am
Location: Ontario, Canada
Contact:

Re: Selecting edges in a script

Post by instance »

edwilliams16 wrote: Sun Jul 31, 2022 6:21 pm You could select edges inside a selection cube of your choice in your script this way:
That's exactly what I'm looking for. Thanks!!
User avatar
dprojects
Posts: 721
Joined: Mon Mar 06, 2017 6:02 pm
Location: Poland
Contact:

Re: Selecting edges in a script

Post by dprojects »

edwilliams16 wrote: Sun Jul 31, 2022 6:21 pm

Code: Select all

bbBox = Part.makeBox(xmax - xmin, ymax - ymin, zmax - zmin, App.Vector(xmin, ymin, zmin))
You don't have to switchApproxiamtion if you cross the axis? Also the BoundBox can be something like 1.0000004, but is normal at FreeCAD, rubbish you said right?, so I had to round it ! and convert it back to compare ! getEdgeIndexByKey(iObj, iBoundBox)

Thanks
Darek
github.com/dprojects

workbench for woodworking is available at: github.com/dprojects/Woodworking
edwilliams16
Veteran
Posts: 3106
Joined: Thu Sep 24, 2020 10:31 pm
Location: Hawaii
Contact:

Re: Selecting edges in a script

Post by edwilliams16 »

dprojects wrote: Sun Jul 31, 2022 6:48 pm
edwilliams16 wrote: Sun Jul 31, 2022 6:21 pm

Code: Select all

bbBox = Part.makeBox(xmax - xmin, ymax - ymin, zmax - zmin, App.Vector(xmin, ymin, zmin))
The script here assumes you construct your bbBox to properly enclose the edges/vertices you want to select. Its behavior will be undefined if you nominally run your box face through the vertex, because of potential numerical error.

I expect it to be more robust to use the provided isEqual() methods to compare faces/edges etc. than to cook up your own using Bounding Boxes and your own numerical error criteria. e. g.

Code: Select all

def getFaceIndex(iShp, iFace):
    for i, face in enumerate(iShp.Faces):
        if iFace.isEqual(face):
            return i
    return -1
EDIT: That was the example I already gave, for edges:

Code: Select all

def getEdgeIndex(iShp, iEdge):
    for i, edge in enumerate(iShp.Edges):
        if iEdge.isEqual(edge):
            return i
    return -1
User avatar
dprojects
Posts: 721
Joined: Mon Mar 06, 2017 6:02 pm
Location: Poland
Contact:

Re: Selecting edges in a script

Post by dprojects »

edwilliams16 wrote: Sun Jul 31, 2022 7:17 pm than to cook up your own using Bounding Boxes and your own numerical error criteria. e. g.

Code: Select all

def getFaceIndex(iShp, iFace):
    for i, face in enumerate(iShp.Faces):
        if iFace.isEqual(face):
            return i
    return -1
EDIT: That was the example I already gave, for edges:

Code: Select all

def getEdgeIndex(iShp, iEdge):
    for i, edge in enumerate(iShp.Edges):
        if iEdge.isEqual(edge):
            return i
    return -1
But how do developers not have to cook if there are no basic functions in the API? This is why my question was there, why is there no such function in the API? I don't want to believe that no one has been able to figure out how to do it for 20 years? and now they invented? sorry, you invented ;-) ok so if you invented maybe you finally will add it, and all developers will be happy?

Thanks
Darek
github.com/dprojects

workbench for woodworking is available at: github.com/dprojects/Woodworking
User avatar
instance
Posts: 20
Joined: Tue Jul 14, 2015 1:28 am
Location: Ontario, Canada
Contact:

Re: Selecting edges in a script

Post by instance »

dprojects wrote: Sun Jul 31, 2022 7:39 pm But how do developers not have to cook if there are no basic functions in the API? This is why my question was there, why is there no such function in the API? I don't want to believe that no one has been able to figure out how to do it for 20 years? and now they invented? sorry, you invented ;-) ok so if you invented maybe you finally will add it, and all developers will be happy?
With respect, I don't think many developers are trying to access faces (or edges) in the same way that you are. There are methods for identifying these things that are more intuitive for most... the bounding box and indexing an array method works perfectly for me, and while there is probably a "get all the faces in this bounding box" method in the Open Cascade API and the core of FreeCAD probably makes great use of it, there might not be a good argument for promoting that to the Python API.

Rather than say "no one has been able to figure it out for 20 years" when it's not clear that anyone else has ever even wanted to "figure it out", it's more helpful to say "any reason why we can't have this functionality in the Python API, I'd add it but don't have the C programming skills" and see if a positive discussion comes from it. Open source is only what people contribute to it. It's much more helpful to talk about what it could become, rather than what is isn't.
Post Reply