component islands

186 views
Skip to first unread message

kevco...@gmail.com

unread,
Sep 18, 2014, 1:14:37 PM9/18/14
to python_in...@googlegroups.com
Hey everybody,

I'm starting to work on some tools that will operate on selected component islands. But once again, im banging my head on the keyboard trying to understand how maya wants me to think. This stuff is so easy in XSI ;)

anyways, I'm passing in a component selection and returning the connected verts for each selected island. I'm using Pymel (i know what your gonna say), but I can't really figure out a good way to get the selected islands without having to do a ton of looping.

so far I'm just passing in the selection list, which kinda represents the selected islands, but not really. I guess it depends on how you selected, and in what order.

so errr, uhh what's the best way to do this? and I definitely wouldn't mind some speed suggestions on my code so far, by someone who's been through this. Aside from not using pymel ;)

==============

import pymel.core as pm
from itertools import chain

# list of component selections
selection = pm.selected()

#loop through each item of the list (selection islands)
for component in selection:
print "========================================"
print "Component ID/'s is {0}".format(component)

#make a list of connected verts
connectedVerts = [ island.connectedVertices() for island in component ]

#print it pretty
for item in set( chain(*connectedVerts) ):
print item

=============

Thanks!
Kev

Marcus Ottosson

unread,
Sep 18, 2014, 1:41:31 PM9/18/14
to python_in...@googlegroups.com

This stuff is so easy in XSI ;)

That isn’t the way to introduce a question in a Maya group. :)

kevco...@gmail.com

unread,
Sep 18, 2014, 1:56:43 PM9/18/14
to python_in...@googlegroups.com
yes true, but I don't play those games ;)

a little help would be much appreciated!

Marcus Ottosson

unread,
Sep 18, 2014, 2:57:53 PM9/18/14
to python_in...@googlegroups.com
No sure what games you refer to. 

What I meant was that by introducing yourself and your question by saying the equivalent to "Softimage is better" you put people in a defensive position. We'll have little choice but to try and prove to you that Maya isn't so bad and some people, including myself, may not have the energy to do that for you.

On 18 September 2014 19:56, <kevco...@gmail.com> wrote:
yes true, but I don't play those games ;)

a little help would be much appreciated!

--
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/3b03878f-ad4a-4927-9780-497750138c76%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Marcus Ottosson
konstr...@gmail.com

kevco...@gmail.com

unread,
Sep 18, 2014, 3:03:00 PM9/18/14
to python_in...@googlegroups.com, kevco...@gmail.com
a little progress, but still stuck getting the islands. Plus, I don't think connectedVertices() is giving me what I want, so I'm starting to have to get test for the component type. it's still in progress.

============================

import pymel.core as pm
from itertools import chain


def getComponentIslands( components ):
#placeholder, till I figure this out
componentIslands = components
return componentIslands


def getComponentIslandVerts( islands ):
for component in islands:
print "========================================"
print "Component Island ID/'s are {0}".format(component)
node = component.node()
if type(component) == pm.MeshFace:
verts = [ island.connectedVertices() for island in component ]

elif type(component) == pm.MeshEdge:
verts = [ island.connectedVertices() for island in component ]

elif type(component) == pm.MeshVertex:
verts = [ island.connectedVertices() for island in component ]

for item in set( chain(*verts) ):
print item


getComponentIslandVerts( getComponentIslands( pm.selected() ) )


==========================

Thanks
Kev

Message has been deleted

kevco...@gmail.com

unread,
Sep 18, 2014, 7:20:25 PM9/18/14
to python_in...@googlegroups.com, kevco...@gmail.com
I suppose I've been put on ignore for mentioning xsi (i'm sorry, i'm sorry ;) )

but if anyone is listening, I've taken a step back, and decided to work on the simpler bits for now. avoiding the islands for now, and just focusing on what I want from the components passed in.

Things would be much easier for me I think if my selection list remained objects, and not strings after converting the list to verts using pm.polyListComponentConversion(). is there a better way?

sorry for a silly question, but how could I get the objects instead or after? so that I can get the position, and normal, etc, etc.

===========

def getComponentIslands( components ):
'''
do fancy stuff here to return component islands
'''
return componentIslands


def convertToVerts( components ):
return pm.polyListComponentConversion( components, toVertex=True )


def getAvgVertTfm( components ):

avgTfm = pm.datatypes.TransformationMatrix()
verts = convertToVerts(components)
for vert in verts:
'''
do all the fancy math stuff here
'''
pass

return avgTfm

=============

Thanks
Kev

Justin Israel

unread,
Sep 18, 2014, 7:28:03 PM9/18/14
to python_in...@googlegroups.com, kevco...@gmail.com


On 19/09/2014 11:20 AM, <kevco...@gmail.com> wrote:
>
> I suppose I've been put on ignore for mentioning xsi (i'm sorry, i'm sorry ;) )
>

You haven't been put on ignore for any reason. I just think it's a matter of the the right person not yet having responded with an answer.

> --
> 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/6507d05d-8aba-4085-8637-6faf20a92f56%40googlegroups.com.

Geordie Martinez

unread,
Sep 18, 2014, 7:38:40 PM9/18/14
to python_inside_maya
I can help you.
when you say component islands you mean you have some polygon that has a bunch of separate poly islands. 
i.e., if you selected the geometry and did a separate command you'd have a bunch of individual pieces?

you want to discover those pieces without using the separate command. You want to make a set of data sets like a list of verts converted to faces based on a vert selection?

is this correct?


kevco...@gmail.com

unread,
Sep 18, 2014, 7:52:56 PM9/18/14
to python_in...@googlegroups.com
Hi, Thanks for replying.

by "component islands", I mean to say - 'Groups' of connected components (edges, faces, verts) in the current selection or passed in component id's.

I'd love some help with that. I will just be creating locators at the average tfm of those component 'Groups' for now.

I've figured out my previous posts dilemma, by just instancing a pm.MeshVertex() with the string(s) that the conversion cmd was giving me. that's probably not that best way though i figure.

even so, now I have access to their positions and normals, just need to do some err, math.

Geordie Martinez

unread,
Sep 18, 2014, 8:03:14 PM9/18/14
to python_inside_maya
here is some code I use for doing that. It could be written better, but I was young. I needed the money. It has some code smell, but I dont' have time to improve it.
import pymel.core as pm
def tfmAtCentroid(vertSel=None, tfmSel=None, locator=True, name=None):
    """ select verts and joints. snaps a joint to the centroid position of selected verts. 
    """
    # SELECTED VERTS AND JOINTS
    
    verts = vertSel or []
    tfms = tfmSel or []
    sel = pm.selected()
    
    returnList=[]
    
    # NOTHING PASSED IN OR SELECTED
    if not vertSel and not tfmSel and not sel:
        return
    
    # IF NO ARGS PASSED IN GET SELECTED
    if sel:
        for item in sel:
           # SORT THE SELECTION
           if item.__class__.__name__ == "MeshVertex":
               verts.append(item)
               
           else:
               tfms.append(item)
               
    # JUST SELECTED WHOLE PIECE(s) OF GEOM
    if tfms != [] and verts == []:
        
        for tfm in tfms:
            try:
                pm.select("%s.vtx[:]"%tfm,r=True)
                newTfm = tfmAtCentroid(locator=locator,name=name)[0]
                returnList.append(newTfm)
            except:
                # NOT A MESH
                if locator:
                    name = name or "%sNUL"%tfm
                    loc = pm.spaceLocator(n=name)
                    pm.delete(pm.pointConstraint(tfm,loc,mo=False))
                    returnList.append(loc)
                else:
                    name = name or "%sJNT"%tfm
                    pm.select(d=True)
                    j = pm.joint(n=name)
                    pm.select(d=True)
                    pm.delete(pm.pointConstraint(tfm,j,mo=False))
                    returnList.append(j)
                    
        # HAVE TO RETURN HERE OR IT WILL CONTINUE ON AND MOVE YOUR TFM TO THE ORIGIN   
        return returnList
               
    # SELECTED SOME VERTS AND THATS IT   
    if tfms == [] and verts != []:
        if locator:
            name = name or "%sNUL"% verts[0].node()
            loc = pm.spaceLocator(n=name)
            tfms.append(loc)
            returnList.append(loc)
        else:
            name = name or "%sJNT"% verts[0].node()
            j = pm.joint(n=name)
            pm.select(d=True)
            tfms.append(j)
            returnList.append(j)
            
        pm.select(verts,r=True)
        # CREATE A CLUSTER TEMPORARILY
        clsNode, clsHandle = pm.cluster()   
        for tfm in tfms:
            # SNAP JOINTS TO THE CLUSTER HANDLE
            pm.select(d=True)
            pm.delete(pm.pointConstraint(clsHandle,tfm,mo=False))
    
    #DELETE THE CLUSTER HANDLE    
    pm.select(d=True)
    try:
        pm.delete(clsNode,clsHandle)
    except: pass

    return returnList


--
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.

damon shelton

unread,
Sep 18, 2014, 8:25:53 PM9/18/14
to python_in...@googlegroups.com
are you just trying to select the shells(islands) based on selected components or check if selected components are in the same shell?

kevco...@gmail.com

unread,
Sep 18, 2014, 8:30:35 PM9/18/14
to python_in...@googlegroups.com
Thank you, I'll have to stare at that for a while ;)

I think I've been staring at this too long now, but how do I loop on something like pCube1.vtx[14:16]? or better yet, flatten that?

my vertex list looks like this:

[u'pCube1.vtx[14:16]', u'pCube1.vtx[20:22]', u'pCube1.vtx[26:27]']

so when I loops over the vertex list to get pos, I only get 3 vertex positions, instead of 23.
pCube1.vtx[14:16]
[-0.809902191162, -0.809902191162, 4.04951095581]
pCube1.vtx[20:22]
[-0.809902191162, 0.809902191162, 4.04951095581]
pCube1.vtx[26:27]
[-0.809902191162, 2.42970657349, 4.04951095581]

Thanks,
Kev

kevco...@gmail.com

unread,
Sep 18, 2014, 8:32:22 PM9/18/14
to python_in...@googlegroups.com, kevco...@gmail.com
sorry, not 23. lol, 9

damon shelton

unread,
Sep 18, 2014, 8:34:42 PM9/18/14
to python_in...@googlegroups.com
when you get selection you need to pass the flatten=True flag to flatten components out

selection = pm.selected(flatten=True)

--
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.

kevco...@gmail.com

unread,
Sep 18, 2014, 9:24:07 PM9/18/14
to python_in...@googlegroups.com
oh, thank you. That worked great.

This is getting much faster. hoorayy...

Kev

Marcus Ottosson

unread,
Sep 19, 2014, 4:05:39 AM9/19/14
to python_in...@googlegroups.com

I suppose I’ve been put on ignore for mentioning xsi (i’m sorry, i’m sorry ;) )

No one is bring put on ignore, it just so happens that nightfall struck Europe since my last reply. However I don’t have much to add.



Kev

--
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.

For more options, visit https://groups.google.com/d/optout.



--
Marcus Ottosson
konstr...@gmail.com

kevco...@gmail.com

unread,
Sep 19, 2014, 2:39:42 PM9/19/14
to python_in...@googlegroups.com, kevco...@gmail.com
okay, so I've got something kinda working. But my math is wrong somewhere in figuring out the average normals. I'm having a hard time visualizing it. Here's my code in case anybody has experience averaging normals. Maybe I'm doing this all wrong - I dunno. I'd really appreciate any help. Thx!

#========================================


import pymel.core as pm
from itertools import chain

import time


def convertToVerts( components ):
return pm.ls( pm.polyListComponentConversion( components, toVertex=True ),
flatten=True)

def getAvgVertTfm( components ):

verts = convertToVerts(components)

positions = []
normals = []
for vert in verts:
positions.append( vert.getPosition() )
normals.append( vert.getNormals() )

avgPos = [ sum(pos)/len(pos) for pos in zip(*positions) ]
avgNormal = [ sum(normal)/len(normal) for normal in zip(*normals) ]

avgNormalRotation = [ sum(rotation)/len(rotation) for rotation
in zip(*pm.datatypes.degrees(avgNormal)) ]

avgTfm = pm.datatypes.TransformationMatrix()
avgTfm.setTranslation(avgPos, space="world")
avgTfm.setRotation(avgNormalRotation)

null = pm.spaceLocator()
null.setTransformation( avgTfm )

'''
still need to add object matrix multiplications
'''


startTime = time.time()
getAvgVertTfm( pm.ls(sl=True) )
print time.time()-startTime
#=======================================

Andres Weber

unread,
Sep 19, 2014, 3:31:09 PM9/19/14
to python_in...@googlegroups.com, kevco...@gmail.com
This is the code I wrote a little while ago for getting component islands/shells...it's not the most efficient but it works.  Oh well...  Aside from that you could just cheat and create a cluster based on those verts and use that. or replace it with a locator after.

def getShells(objs):
	'''Gets the shells for a selected list objects and returns a dict
	Args:
		objs (list of pm.PyNode): all objects to be checked
	Returns (dict): dictionary of mesh shells
	Usage:
		detectSeparateMeshes(pm.ls(sl=True))
	'''
	shells_group={}
	for obj in objs:		
		verts = [pm.PyNode(obj.getShape().name() + '.vtx[%s]'%vert )for vert in range(0,obj.getShape().numVertices())]
		vertsLeft = verts[:]
		shells=[]
		for vert in verts:
			if vert in vertsLeft:
				shell = getShell(vert)
				vertsLeft = [vertex for vertex in vertsLeft if vertex not in shell]
				shells.append(shell)
		shells_group[obj.name()]=shells
	return shells_group

def getShell(vertex, shell=[]):
	'''Returns the vert list of the shell connected to given vertex
	Args:
		vertex (pm.nt.MeshVertex): vertex to get shell from
		shell (list): internal array, not for external use
	Returns:
		(list): pm.nt.MeshVertex list of shell verts
	'''
	if shell == []:
		shell=[]
		obj = vertex.node()
		shell.append(vertex)
	#get connected verts to current vert
	connected = pm.ls(vertex.connectedVertices(), fl=True)
	for connect in connected:
		#if the connected vert is not in the shell add it then run on it
		if connect not in shell:
			shell.append(connect)
			getShell(connect, shell=shell)
	return shell

kevco...@gmail.com

unread,
Sep 19, 2014, 3:53:24 PM9/19/14
to python_in...@googlegroups.com, kevco...@gmail.com
Hi, thanks for this. It's not quite what I'm after though. For some reason, i'm having a tough time explaining what I want.

from the list of selected components or components ID's, create a list of 'groups' that contain all the components that are connected to eachother.

kevco...@gmail.com

unread,
Sep 19, 2014, 3:59:09 PM9/19/14
to python_in...@googlegroups.com, kevco...@gmail.com
so if I have a white cow made of verts ;)

and I paint the cow to have black spots,

return a list of vertices for black spot, in all the black spots

kevco...@gmail.com

unread,
Sep 22, 2014, 1:09:37 PM9/22/14
to python_in...@googlegroups.com, kevco...@gmail.com
okay, I feel dumb now looking at my code again.

I wasn't doing things right at all. I ended up using the current vertex normal as my X axis, getting the next vertex position and subtracting the current vertex position, normalizing it, and using it for my Y axis, then Z was the cross product of X & Y, then set Y to be the cross of Z & X.

kevco...@gmail.com

unread,
Sep 22, 2014, 5:37:00 PM9/22/14
to python_in...@googlegroups.com, kevco...@gmail.com
So this is almost working. Although I'm not really getting the orientation I'd like for verts on non-flat meshes. In some areas, it works great, in others - I'm not sure how to get the proper orientation. Any ideas on how to make this better? if you run it with a really lowpoly sphere selected you can see what I mean towards the poles.

#=======================
import pymel.core as pm


def getVtxTfm(vtx):
pos = vtx.getPosition(space='world')
xVec = vtx.getNormal()

connectedVertNames = [ vert.name() for vert in vtx.connectedVertices() ]

nextVertName = "{0}.vtx[{1}]".format( vtx.node().name(), ( vtx.index() + 1) )
nextVert = pm.PyNode( nextVertName )
prevVertName = "{0}.vtx[{1}]".format( vtx.node().name(), ( vtx.index() - 1) )
prevVert = pm.PyNode( prevVertName )

if nextVertName in connectedVertNames:
yVec = ( nextVert.getPosition() - pos ).normal()
else:
yVec = -( prevVert.getPosition() - pos ).normal()

zVec = xVec.cross(yVec).normal()
yVec = zVec.cross(xVec)

mat = pm.datatypes.Matrix()
mat.a00 = xVec[0]
mat.a01 = xVec[1]
mat.a02 = xVec[2]

mat.a10 = yVec[0]
mat.a11 = yVec[1]
mat.a12 = yVec[2]

mat.a20 = zVec[0]
mat.a21 = zVec[1]
mat.a22 = zVec[2]

mat.a30 = pos[0]
mat.a31 = pos[1]
mat.a32 = pos[2]

tfm = pm.datatypes.TransformationMatrix( mat )

null = pm.spaceLocator()
null.setTransformation( tfm )

sel = pm.selected()
nodeName = sel[0].name()
for vtxID in sel[0].getVertices()[1]:
vertex = "{0}.vtx[{1}]".format( nodeName, vtxID )
vert = pm.PyNode(vertex)
getVtxTfm(vert)

#=======================

Thanks!
Kev

Reply all
Reply to author
Forward
0 new messages