import pymel.core as pmimport timeimport 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.0hard_edges = True
#Get the initial selectionselection = pm.ls(sl=True, fl=True)#Filter the selection to face componentsselected_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_m...@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.
As for the speed at which this script and the similar built-in selection constraints operate, they are woefully slow in comparison to the cinema 4d tool I'm trying to emulate. I think there must be some data built behind the scenes to accelerate this sort of selection.
Can you provide me with any further hints? Is your 8 line script able to select upwards of 100,000 faces within an arbitrary angle tolerance in under a second or should I adjust my expectations?
normal = face.getNormal()
connected = face.connectedFaces()
for f in connected:
# If f is already in my list, continue
# Test normal.dot(f.getNormal()) and add to the end of the list if it passes
import math
import maya.api.OpenMaya as om
def getCoplanarFaceIds(fnMesh, ids, angle):
''' Accepts, and returns, a list of face IDs. '''
dot = math.cos(math.radians(angle))
itPoly = om.MItMeshPolygon(fnMesh.getPath())
# Keep track of ids both as a list and a set.
# Checking if an element is in a set is much faster than a list.
idSet = set(ids)
i = 0
while i < len(ids):
itPoly.setIndex(ids[i])
normal = itPoly.getNormal()
connectedIds = itPoly.getConnectedFaces()
for c in connectedIds:
if c not in idSet:
itPoly.setIndex(c)
if normal * itPoly.getNormal() > dot:
ids.append(c)
idSet.add(c)
i += 1
return ids
# Here's a rough script to drive the function.
selList = om.MGlobal.getActiveSelectionList()
for i in range(selList.length()):
try:
dagPath, comp = selList.getComponent(i)
except TypeError:
continue # Probably a DG node, like a material.
if comp == om.MObject.kNullObj:
continue # Not a component
else:
if dagPath.hasFn(om.MFn.kMesh):
# Just as a quick test, use the first component we find.
# TODO: At least check it's meant to be a face!
fnMesh = om.MFnMesh(dagPath)
fnComp = om.MFnSingleIndexedComponent(comp)
id = fnComp.element(0)
print 'Using {}.f[{}]'.format(fnMesh.partialPathName(), id)
connectedFaceIds = getCoplanarFaceIds(fnMesh, [id], 15)
break
print len(connectedFaceIds)