Hello,

I'm new to the list and relatively new to Maya.

I've been missing a tool in Cinema 4D that allows the user to select 
contiguous faces within boundaries defined by differences in polygon normal 
angles (boundaries = hard edges or an arbitrary angle), so I rolled my own 
Maya script.

It seems to be working well, but it gets quite slow as the number of faces 
to select increases.  For example, on my home PC it takes about 6 seconds 
to select 320 faces along the inside of a ring.

If you have any ideas on how I could implement this more efficiently I 
would love your input.  I've heard that PyMEL tends to be the slowest of 
options for scripting, but it's super convenient to work in a more 
'pythonic' mode.  I started playing with the OpenMaya API but found that it 
doesn't seem to have a polyListComponentConversion equivalent and my brain 
is fried enough that I can't think about implementing my own version using 
the API.  I have a suspicion that the polyListComponentConversion function 
is one of those adding the most time to execution;  I'm using it to find 
the boundary edges of the faces currently marked for selection.

I've attached a .py file and pasted my code here as well:

import pymel.core as pm
import time
import itertools

def compare_normals(n1, n2):
    #Take dot product of normals and convert to degrees difference
    return 90.0 * (1.0 - n1*n2)

def check_angle(edge, angle, hard_edges=True):
    if edge.isOnBoundary():
        #Boundary reached; no need to continue!
        return False
    
    soft = True
    if hard_edges:
        #Check for hard or soft edge
        soft = edge.isSmooth()
    
    #Get all faces connected to this edge
    faces = pm.ls(edge.connectedFaces(), fl=True)
    #Compare the face normals between edges to determine if the face should 
be selected
    face_compare = {c for c in itertools.combinations(faces, 2) if 
compare_normals(c[0].getNormal(), c[1].getNormal()) > angle}
    
    #Returns True if the face should be selected
    return len(face_compare) == 0 and soft
    

def get_connected_faces(faces, angle=0.0, hard_edges=True):
    #Get edge boundary of current face selection
    boundary = pm.ls(pm.polyListComponentConversion(faces, bo=True, 
te=True), fl=True)
    #Combine currently selected faces with neighboring faces that pass the 
face normal test
    new_faces = faces | set(pm.ls([edge.connectedFaces() for edge in 
boundary if \
                                   check_angle(edge, angle, hard_edges)], 
fl=True))

    if new_faces != faces:
        #Yield the new faces to select for as long as we haven't exhausted 
our supply
        yield new_faces
    #Stop when there are no more faces to select
    raise StopIteration

t1 = time.clock()

angle_tolerance = 30.0
hard_edges = True

#Get the initial selection
selection = pm.ls(sl=True, fl=True)
#Filter the selection to face components
selected_faces = {face for face in selection if isinstance(face, 
pm.general.MeshFace)}

if selected_faces:
    #Since there are faces selected...
    try:
        while True:
            #Find new neighboring faces until no more meet the criteria
            selected_faces = get_connected_faces(selected_faces, 
angle_tolerance).next()
    except StopIteration:
        #Select all faces found
        pm.select(selected_faces)
        t2 = time.clock()
        #Report our performance
        print("Selected {0} faces in {1:.2f} 
seconds.".format(len(selected_faces), t2-t1))



-- 
You received this message because you are subscribed to the Google Groups 
"Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to python_inside_maya+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/python_inside_maya/cbccc4b3-d73d-41f6-820f-beeefc07fb5c%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
import pymel.core as pm
import time
import itertools

def compare_normals(n1, n2):
    #Take dot product of normals and convert to degrees difference
    return 90.0 * (1.0 - n1*n2)

def check_angle(edge, angle, hard_edges=True):
    if edge.isOnBoundary():
        #Boundary reached; no need to continue!
        return False
    
    soft = True
    if hard_edges:
        #Check for hard or soft edge
        soft = edge.isSmooth()
    
    #Get all faces connected to this edge
    faces = pm.ls(edge.connectedFaces(), fl=True)
    #Compare the face normals between edges to determine if the face should be selected
    face_compare = {c for c in itertools.combinations(faces, 2) if compare_normals(c[0].getNormal(), c[1].getNormal()) > angle}
    
    #Returns True if the face should be selected
    return len(face_compare) == 0 and soft
    

def get_connected_faces(faces, angle=0.0, hard_edges=True):
    #Get edge boundary of current face selection
    boundary = pm.ls(pm.polyListComponentConversion(faces, bo=True, te=True), fl=True)
    #Combine currently selected faces with neighboring faces that pass the face normal test
    new_faces = faces | set(pm.ls([edge.connectedFaces() for edge in boundary if \
                                   check_angle(edge, angle, hard_edges)], fl=True))

    if new_faces != faces:
        #Yield the new faces to select for as long as we haven't exhausted our supply
        yield new_faces
    #Stop when there are no more faces to select
    raise StopIteration

t1 = time.clock()

angle_tolerance = 30.0
hard_edges = True

#Get the initial selection
selection = pm.ls(sl=True, fl=True)
#Filter the selection to face components
selected_faces = {face for face in selection if isinstance(face, pm.general.MeshFace)}

if selected_faces:
    #Since there are faces selected...
    try:
        while True:
            #Find new neighboring faces until no more meet the criteria
            selected_faces = get_connected_faces(selected_faces, angle_tolerance).next()
    except StopIteration:
        #Select all faces found
        pm.select(selected_faces)
        t2 = time.clock()
        #Report our performance
        print("Selected {0} faces in {1:.2f} seconds.".format(len(selected_faces), t2-t1))

Reply via email to