[API] Determining all the top faces of the design

[API] Determining all the top faces of the design

Anonymous
Not applicable
1,857 Views
5 Replies
Message 1 of 6

[API] Determining all the top faces of the design

Anonymous
Not applicable

Hi, thank you a lot for the wonderful community here! 

I am trying to write a script that would make different configurations of holes in the upper body and then see which one is best after running heat simulations.  Hence, I want my program to be able to: (1) give a user a choice to select the faces where holes should be placed (and I achieve this easily with selectEntity); (2) if the user chooses *non* to select any faces, then I want my script to select *all* the top faces for themselves.

 

Why the *top* faces?: because later on, I want to specify convection coefficient which is usually thermally loaded only on top faces

Language: Python 

Ideas I thought of:

(1) iterating over all the faces that the body has does *not* seem to work as I don't know which one of them is the top one? 
(2) having a bunch of vectors (or only four if the design is a box) going in many different directions from the center and then check all the faces that intersect the vectors.  From there, I could determine which one is 'the top one' looking at x-y-z values? <-- this seems to be very tedious. 

(3) at least try selecting the upper body (my script attempts to do that but heavily relies on temporary indexing) and then make holes on all faces. Hence, if I had a case as an upper-body, I would make holes from two sides of the cases and I would *need to hope* that they match and don't create weird holes.

 

Any ideas on what I could do here?

 

Attached find the code I have right now.

 

 

def run(context):
    '''Main program that is executed when  the script is run from Fusion360 env.'''
    ui = None
    try:
        ### -- SETTING UP: 
        app = adsk.core.Application.get()
        ui  = app.userInterface
        design = app.activeProduct
        # ensuring that there is model built & active:
        if not design:
            ui.messageBox('No active Fusion design', 'No Design')
            return
        # Get the root component of the active design:
        rootComp = design.rootComponent
        holes = rootComp.features.holeFeatures
        ### --

        ### --
        # [[face_ref, [which_side, length_x, width_y, height_z], [face.centroid x y z]], ...] 
        # storage for reference to objects
        unsafe_faces = [] 
        ### -- 

        ### -- SELECTION OF THE FACES. TWO WAYS: (1) by user; (2) by our script
        user_answer = ui.inputBox("Do you want to specify faces where holes will be placed? Type 'YES', if so \
            \n Otherwise, if you want the program to determine faces for you, type 'NO'. ")
        
        if user_answer[1] == False and str(user_answer[0]).strip().upper() == 'YES': 
            selected_face = ui.selectEntity("Please, choose faces which need better heat dissipation.\
             \n If needed, choose all the faces under the upper case.", "Faces")
            unsafe_faces.append([selected_face.entity])
  
            while True:
                user_answer = ui.inputBox("Great. Do you want to have holes on another face? Type 'YES' or 'NO'.")
                if user_answer[1]: 
                    break
                if str(user_answer[0]).strip().upper() != 'YES': 
                    break
                else: 
                    selected_face = ui.selectEntity("Great. Then, please, choose face № {} \n upon which holes need to be added".format(len(unsafe_faces) + 1), 'Faces')
                    unsafe_faces.append([selected_face.entity])
                
        else: ###PROBLEM??????
            pass
            body_with_faces = rootComp.bRepBodies[-1]
            for face_idx in range(0, body_with_faces.faces.count): 
                unsafe_faces.append([body_with_faces.faces.item(face_idx)])

 

 

0 Likes
Accepted solutions (1)
1,858 Views
5 Replies
Replies (5)
Message 2 of 6

kandennti
Mentor
Mentor

Hi @Anonymous .

 

Is the upward direction always fixed with Z plus?
If it's fixed, I feel like you can determine this by using the BoundingBox3D and Vector3D.dotProduct of the surface.
 
 
How about using the messageBox instead of the inputBox for the "user_answer" part?  By changing the button type, you can give the user a choice.
        res = ui.messageBox(
            'hoge',
            'piyo',
            adsk.core.MessageBoxButtonTypes.YesNoCancelButtonType,
            adsk.core.MessageBoxIconTypes.QuestionIconType)

        if res == adsk.core.DialogResults.DialogYes:
            ui.messageBox('yes')
        elif res == adsk.core.DialogResults.DialogNo:
            ui.messageBox('no')
        else:
            ui.messageBox('cancel')
 
0 Likes
Message 3 of 6

Anonymous
Not applicable

Hi @kandennti , thanks a lot for replying here. 

 

Regarding the direction of the model -- no, it is not. The idea is actually that these holes could be added to any type of model, depending on which one user has used. I am slightly confused about what exactly you suggest in having a bounding box and dot product. Do you suggest looking the dot product of the e.g. normal vector from the surface of the model (let it be vec_a) and the normal vector of the bounding box (let it be vec_b)? And then, checking is *dot product between vec_a & vec_b is 1 *, hence pointing in the same direction?

 

Also, THANK YOU for the message box suggestion. I was looking exactly for this for some time now. Amazing trick! 

0 Likes
Message 4 of 6

kandennti
Mentor
Mentor
Accepted solution
Please forgive me for being out of tune with the forum and just text. I haven't actually made it, so it's just an idea. I don't know if I can handle it the way I envision it. I found a description of boundingBox that says it is not accurate. https://forums.autodesk.com/t5/fusion-360-api-and-scripts/component-boundingbox/m-p/6635908#M2306 I accept that the only surface needed is a planar surface. So you can use "BRepFace.geometry.surfaceType" to select a planar surface only. Here is a "Measure Sample". https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-4b13c86c-7aa0-4555-b3e3-02f7d1b01d10 Minimum distances and angles between geometries can be measured. https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-DF2F5C84-57C7-4F61-BA05-D2978FFF2724 It uses an upward vector to create a plane at a very distant location. I think the minimum distance and angle to this plane will give you the surface you need. If you can't determine the angle, I would use Vector3D.dotProduct because you can use BRepFace.pointOnFace to get the point and BRepFace.evaluator to get the normal vector. I don't speak English, so I hope my words will be enough to get the message across...
0 Likes
Message 5 of 6

BrianEkins
Mentor
Mentor

This is certainly possible and I've written many programs that do something similar.  Here are some of the key things you'll want to become familiar with.  The BRepFace object supports the geometry, evaluator, and pointOnFace properties.  I assume you're only looking for planar faces?  That's not a hard requirement but it complicates determining upper faces and placing holes if you support other shapes.  You can use the geometry property to determine if a face is represented by a plane or not.  The geometry property will return a geometry object of the type the face represents.  For example if the face is planar, you'll get back a Plane object or if it's cylindrical you'll get back a Cylinder object.  You can check the type of the returned object to know if it's a plane.

 

Once you know the face is a plane you can use the evaluator property of the BRepFace object to get the associated SurfaceEvaluator object.  This object support many different types of evaluations you can perform on any face.  The one that's the most interesting here is the getNormalAtPoint method.  You pass in a point on the surface and it will return a vector that represents the normal of the surface.  For a solid, normals always point out of the solid.  You can compare this normal with a vector you've created that defines the "up" direction. 

 

The pointOnFace property of the BRepFace object is a convenient way to get a point to pass into the getNormalAtPoint.  For a plane, any point on the face will return the same normal, but that property will guarantee to return a point somewhere on the face.

---------------------------------------------------------------
Brian Ekins
Inventor and Fusion 360 API Expert
Website/Blog: https://EkinsSolutions.com
0 Likes
Message 6 of 6

Anonymous
Not applicable

Hi Brian, Thank you a lot for your thoughtful response. I will answer below to some points that you mentioned.
(1) For now, I do assume that I only have planar faces, however, this is simplification *, for now,* and in the nearest plans, I hope my script will also be able to generate holes for any type of surface. 
(2) As for suggestion regarding Surface evaluator, sounds very reasonable (thanks!). Although, I also just want to clarify that by "top" I mean *all the faces around 360 degrees that are on the surface* (sorry, English is not my first language). Hence, in the example below, where I have a small box covered with the case, I would like my script to find *all 6 facets of the lighter gray object*. Hence, if following your suggestion, I will have probably all 6+6*2=18 faces found as *plane* ones, and then I could use normal vector from each to find which facets are "upper" ones, rather those that belong to e.g. dark gray box? I could find coordinates of each face's normal and see which one will have the biggest absolute value. Is my understanding correct?

svitlana96MQ7_0-1596448324027.pngsvitlana96MQ7_1-1596448332976.pngsvitlana96MQ7_2-1596448348069.png
Thanks again for your help. You mentioned that you have written a plenty of programs that do something similar -- any chance you could share the links for those (if the are publicly available)? I looked everywhere (including Github repo) for similar examples, but I could not find anything. 

 

0 Likes