OK I ran out of time but I do have something. The interface is very rough -
it only uses the first selected component, and I don't have it selecting
the result at the end. However, it does process 90,000 faces in under 1
second on my PC. So that's something :)
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)
Incidentally here's some profile info. I was wrong about appending to the
list taking significant time. The missing time here would be mostly used by
the "if c not in idSet" line, I expect.
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.003 0.003 0.663 0.663 <maya console>:2(<module>)
1 0.208 0.208 0.659 0.659 <maya
console>:2(getCoplanarFaceIds)
90000 0.243 0.000 0.243 0.000 {method 'getConnectedFaces'
of 'OpenMaya.MItMeshPolygon' objects}
179999 0.175 0.000 0.175 0.000 {method 'getNormal' of
'OpenMaya.MItMeshPolygon' objects}
179999 0.012 0.000 0.012 0.000 {method 'setIndex' of
'OpenMaya.MItMeshPolygon' objects}
89999 0.009 0.000 0.009 0.000 {method 'add' of 'set'
objects}
89999 0.006 0.000 0.006 0.000 {method 'append' of 'list'
objects}
90004 0.006 0.000 0.006 0.000 {len}
On Thursday, 5 April 2018 17:06:33 UTC+10, Michael Boon wrote:
>
> 100,000 faces would take minutes. Growing a list one element at a time in
> Python gets very slow as your list gets big.
>
> I start with a flattened list of faces, then I'm just looping on my list
> until I get to the end. Inside the loop looks a bit like this:
> 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
>
> That's pretty much it. I haven't needed to flatten any components (though
> if you find a case where you think you do, I'd advise you try to use them
> as they are, because a mesh component iterable is an API data structure,
> and flattening it into individual mesh components involves a lot of Python
> behind the scenes.
>
> If you want to use it on 100,000 faces and you don't want to learn how to
> do it in C++ (which is fair enough), maybe try the Python API. One huge
> advantage of the API is the array types, which manage their memory very
> well and can be arbitrarily grown to 100,000 or even millions of elements
> without slowing everything horribly like Python lists would*. The logic
> would be very similar (using MItMeshPolygon instead of MeshFace) and I
> don't think it would be much faster for smaller selections, but it could
> handle larger ones better.
>
> (*Alternatively you can pre-assign the size of your Python lists, and keep
> track of the number of used elements yourself. Sometimes that's easier.)
>
> Honestly I'm really intrigued now. Hopefully I'll have time tonight to try
> to make a version that can handle big numbers.
>
> On Thursday, 5 April 2018 16:29:06 UTC+10, Darby Edelen wrote:
>>
>> There are definitely some intricacies of pymel that elude me. I don't
>> know, for example, how to get a flattened list of connected faces without
>> using ls. Or perhaps I don't need a flattened list? I assume that I need to
>> getNormal on each face and compare against its connected faces in order to
>> decide which faces to extend to on the next step. Can I do that if
>> connectedFaces isn't giving me a flattened list?
>>
>> 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?
>>
>>
--
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 [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/python_inside_maya/21acc224-1f2f-4ebb-a442-0a6885e2701e%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.