def cutObject(object, voronoiPoints): #Create group so we can easily access shards later shards = cmds.group(em=True, name='shards') # Cut according to Voronoi cells for from_point in voronoiPoints: # https://openmaya.quora.com/How-to-implement-Voronoi-with-Python-for-Maya # print(from_point) working_geom = cmds.duplicate(object[0]) cmds.select(working_geom[0]) cmds.parent(working_geom, shards) for to_point in voronoiPoints: if from_point != to_point: locator = cmds.spaceLocator() cmds.move(from_point[0], from_point[1], from_point[2]) cmds.parent(locator, working_geom) center_point = [(e1 + e2) / 2 for (e1, e2) in zip(to_point, from_point)] n = [(e1 - e2) for (e1, e2) in zip(from_point, to_point)] es = cmds.angleBetween(euler=True, v1=[0, 0, 1], v2=n) cmds.polyCut(working_geom, deleteFaces=True, cutPlaneCenter=center_point, cutPlaneRotate=es) cmds.polyCloseBorder(working_geom) cmds.delete(object) return shards
def createArrow(name=None, thickness=0.1, length=2, vector=[1,0,0], point=[0,0,0]): ''' Creates an arrow in the direction of the vector Example: arrow = createArrow(vector=[0,1,0], length=4) ''' if not name: name = 'arrow_CTRL' #calc length for thickness ratio = length/thickness cyl = cmds.cylinder(radius=thickness, sections=4, heightRatio=ratio, pivot=[length/2, 0, 0], ch=0)[0] cone = cmds.cone(radius=thickness*2, sections=4, ch=0, pivot=(length*1.0005,0,0))[0] xform = cmds.createNode('transform', ss=1, name=name) shapes = [] transforms = [] for node in [cone,cyl]: shapes.append(cmds.listRelatives(node, fullPath=1, shapes=1)[0]) transforms.append(node) cmds.parent(shapes, xform, r=1, s=1) rotateBy = cmds.angleBetween(euler=True, v1=(1,0,0), v2=vector) cmds.rotate(rotateBy[0], rotateBy[1], rotateBy[2], xform) cmds.xform(xform, t=point) cmds.delete(transforms) return xform
def placeObjects(filename, duplicates=100): meshList = om.MGlobal.getActiveSelectionList() parentPath = meshList.getDagPath(0) meshPath, meshObject = meshList.getComponent(1) parentFn = om.MFnMesh(parentPath) meshFn = om.MFnMesh(meshPath) meshIter = om.MItMeshPolygon(parentPath) count = 0 while count < duplicates: u, v = [random.uniform(0.01, 1), random.uniform(0.01, 1)] # NOTE 获取随机 UV 的颜色 | OpenMaya2.0 不支持 rgb = getRGBatUV(filename, [u, v]) if rgb <= [0.4, 0.4, 0.4]: continue meshIter.reset() while not meshIter.isDone(): faceId = meshIter.index() try: outPoint = meshIter.getPointAtUV([u, v], om.MSpace.kWorld) except RuntimeError: meshIter.next(1) continue faceNormal = meshIter.getNormal() name = meshPath.fullPathName() dupName = "%s_dup%s" % (name, count + 1) dup = cmds.duplicate(name, n=dupName) # NOTE Maya cmds 计算角度 t = [outPoint.x, outPoint.y, outPoint.z] ro = cmds.angleBetween(euler=True, v1=(0.0, 1.0, 0.0), v2=t) cmds.xform(dup, ws=1, t=t) cmds.xform(dup, ws=1, ro=ro) # NOTE 旧方案的计算方法 # selectionList = om.MSelectionList() # selectionList.add(dupName) # dupMeshPath = selectionList.getDagPath(0) # transform = om.MFnTransform(dupMeshPath) # dupObjVector = om.MVector(0,1,0) # rotAxisOrig = dupObjVector ^ faceNormal # rotAxis = rotAxisOrig.normal() # angleOrig = dupObjVector * faceNormal # angle = math.acos(angleOrig) # quat = om.MQuaternion(angle,rotAxis) # transform.setTranslation(om.MVector(outPoint),om.MSpace.kWorld) # transform.setRotation(quat,om.MSpace.kWorld) count += 1 break
def calcMidPointMVector(posA, posB, aim, offset): midpoint = (posA + posB) /2 dir = om.MVector.normal(posA - posB) offset = dir * offset offMidPoint = midpoint + offset rotate = mc.angleBetween( euler=True, v1=(aim[0], aim[1], aim[2]), v2=(dir.x, dir.y, dir.z) ) cut = (offMidPoint, rotate) return cut
def createArrow(name=None, thickness=0.1, length=2, vector=[1, 0, 0], point=[0, 0, 0]): ''' Creates an arrow in the direction of the vector Example: arrow = createArrow(vector=[0,1,0], length=4) ''' if not name: name = 'arrow_CTRL' #calc length for thickness ratio = length / thickness cyl = cmds.cylinder(radius=thickness, sections=4, heightRatio=ratio, pivot=[length / 2, 0, 0], ch=0)[0] cone = cmds.cone(radius=thickness * 2, sections=4, ch=0, pivot=(length * 1.0005, 0, 0))[0] xform = cmds.createNode('transform', ss=1, name=name) shapes = [] transforms = [] for node in [cone, cyl]: shapes.append(cmds.listRelatives(node, fullPath=1, shapes=1)[0]) transforms.append(node) cmds.parent(shapes, xform, r=1, s=1) rotateBy = cmds.angleBetween(euler=True, v1=(1, 0, 0), v2=vector) cmds.rotate(rotateBy[0], rotateBy[1], rotateBy[2], xform) cmds.xform(xform, t=point) cmds.delete(transforms) return xform
def calcMidPointMVector(posA, posB, aim, offset): midpoint = (posA + posB) / 2 dir = om.MVector.normal(posA - posB) offset = dir * offset offMidPoint = midpoint + offset rotate = mc.angleBetween(euler=True, v1=(aim[0], aim[1], aim[2]), v2=(dir.x, dir.y, dir.z)) cut = (offMidPoint, rotate) return cut
def do_rotate( p1, p2, p3, p4, group): v1 = [] v2 = [] for k in range( 3 ): v1.append( cmds.xform( p2, worldSpace = True, translation = True, query = True )[k] - cmds.xform( p1, worldSpace = True, translation = True, query = True )[k]) v2.append( cmds.xform( p4, worldSpace = True, translation = True, query = True )[k] - cmds.xform( p3, worldSpace = True, translation = True, query = True )[k]) angle = cmds.angleBetween( v1 = (v1[0], v1[1], v1[2]), v2 = (v2[0], v2[1], v2[2]), euler = True ) rpX, rpY, rpZ = cmds.xform( p1, worldSpace = True, translation = True, query = True ) cmds.xform( group, rotatePivot = ( rpX, rpY, rpZ ), rotation = ( angle[0], angle[1], angle[2] ), relative = True )
def voronoiShatter(obj, n, pShowProgress,id): # random point placement for polycut operation vPoints = getVoronoiPoints(obj,n) # create new group for shards cmds.setAttr(obj+'.visibility',0) shardGroup = cmds.group( em=True, name = obj + '_chunks_'+str(id) ) cmds.undoInfo(state = False) cmds.setAttr(str(obj) + '.visibility',0) step = 0 if pShowProgress: cmds.progressWindow(title = "Voronoi Calculating", progress = 0, isInterruptable = True, maxValue = n) cmds.undoInfo(state = False) for vFrom in vPoints: if pShowProgress: if cmds.progressWindow(q = True, isCancelled=True): break if cmds.progressWindow(q = True, progress = True) >= n: break step = step + 1 cmds.progressWindow(edit=True, progress = step, status=("Shattering step %d of %d completed..." % (step, n))) cmds.refresh() tempObj = cmds.duplicate(obj) if tempObj: cmds.setAttr(str(tempObj[0]) + '.visibility',1) cmds.parent(tempObj,shardGroup) for vTo in vPoints: if vFrom != vTo: aim = [(v1-v2) for (v1,v2) in zip(vFrom,vTo)] vCenter = [(v1 + v2)/2 for (v1,v2) in zip(vTo,vFrom)] planeAngle = cmds.angleBetween( euler = True, v1=[0,0,1], v2=aim ) cmds.polyCut(tempObj[0], df = True, cutPlaneCenter = vCenter, cutPlaneRotate = planeAngle) cmds.polyCloseBorder(tempObj[0], ch = False) cmds.xform(tempObj, cp = True) cmds.xform(shardGroup) cmds.undoInfo(state = True) cmds.progressWindow(endProgress=1) ConnectDynamic.addNewRigidBodies(id)
def doDrag(self, *args, **kwargs): if not self.newJoint: return None intersectPoint, intersectNormal = self.getIntersectPointAndNormal() if not intersectPoint: return None cmds.move( intersectPoint.x, intersectPoint.y, intersectPoint.z, self.newJoint, ws=1 ) angle = cmds.angleBetween( v1=[0,0,1], v2=[intersectNormal.x,intersectNormal.y,intersectNormal.z], er=1 ) cmds.rotate( angle[0], angle[1], angle[2], self.newJoint, ws=1 ) cmds.refresh() self.movedPosition = intersectPoint
def move_job(self): x, y, z = self.get_coord_at_click(self.selected, self.ctx) dxn, dyn, dzn = self.get_closest_normal_uv(self.selected[0], x, y, z)[0] if (x, y, z) != (0.0, 0.0, 0.0): dx, dy, dz = self.get_coord_on_drag(self.selected, self.ctx) angle_btw = cmds.angleBetween(euler=True, v1=(1.0, 0.0, 0.0), v2=(dxn, dyn, dzn)) cmds.move(dx, dy, dz, self.curve, worldSpace=True) cmds.rotate(angle_btw[0], angle_btw[1], angle_btw[2], self.curve) cmds.move(dx, dy, dz, self.follicle_trans)
def __makeMesh(self,curva): scale_0 = self.ui.spin_radius.value() scale_1 = self.ui.spin_radius_1.value() scale = self.ui.spin_radius.value() if (scale_0 >= scale_1): tempMaior = scale_0 tempMenor = scale_1 else: tempMaior = scale_1 tempMenor = scale_0 scale_extrude = tempMenor/tempMaior position = cmds.pointOnCurve(curva, top=True, pr=0, position=True) tangent = cmds.pointOnCurve(curva, top=True, pr=0, normalizedTangent=True) angle = cmds.angleBetween(er=True, v1=(0.0, 1.0, 0.0), v2=tangent) circle = cmds.circle(nr=(0, 1, 0), c=(0, 0, 0), degree=3, sections=16, r = 0.5) cmds.scale(tempMaior, tempMaior, tempMaior, circle[0]) cmds.move(position[0], position[1], position[2], circle[0]) cmds.rotate(angle[0], angle[1], angle[2], circle[0]) extrude = cmds.extrude(circle[0], curva, constructionHistory = True, range = True, polygon = 0, extrudeType = 2, useComponentPivot = 0, fixedPath = 0, useProfileNormal = 1, rotation = 0, scale = scale_extrude, reverseSurfaceIfPathReversed = 1) poly = cmds.nurbsToPoly(extrude[0], matchNormalDir = True, constructionHistory = False, format = 2, polygonType = 1, uType = 3, uNumber = 1, vType = 3, vNumber = 3) cmds.delete(circle, extrude[0]) print poly return poly
def calculate_rotation(self, position): # This tuple is located on the opposite side of the planet. opposite_position = ((position[0] * -1), (position[1] * -1), (position[2] * -1)) # Each moon is given a unique rotation based on the angle between the moon and the on the opposite side of the planet. between_angle = angleBetween(euler=True, vector1=(position[0], position[1], position[2]), vector2=(opposite_position[0], opposite_position[1], opposite_position[2])) # Returns that unique rotation as a tuple. return (between_angle[0], between_angle[1], between_angle[2])
def doPress( self, *args, **kwargs ): self.newJoint = None intersectPoint, intersectNormal = self.getIntersectPointAndNormal() if not intersectPoint: return None self.newJoint = self.getJoint() cmds.undoInfo( swf=0 ) cmds.select( self.newJoint ) cmds.setAttr( self.newJoint+'.dla', 1 ) self.origPosition = OpenMaya.MPoint( *cmds.xform( self.newJoint, q=1, ws=1, t=1 ) ) cmds.move( intersectPoint.x, intersectPoint.y, intersectPoint.z, self.newJoint, ws=1 ) angle = cmds.angleBetween( v1=[0,0,1], v2=[intersectNormal.x,intersectNormal.y,intersectNormal.z], er=1 ) cmds.rotate( angle[0], angle[1], angle[2], self.newJoint, ws=1 ) cmds.refresh()
def createLRA(node=None, matrix=None, name=None, color=True, thickness=0.1, length=2, vector=[1,0,0], point=[0,0,0], arrowRadiusMult=2): ''' Creates an LRA at the origin, or at a node, or a mmx Example: createLRA(length=2, thickness=0.05, arrowRadiusMult=3) ''' if not name: name = 'arrow_CTRL' nodes = [] x,y,z = [],[],[] #calc length for thickness ratio = length/thickness xform = cmds.createNode('transform', ss=1, name=name) x.append(cmds.cylinder(radius=thickness, sections=4, heightRatio=ratio, pivot=[length/2, 0, 0], ch=0, axis=[1,0,0])[0]) x.append(cmds.cone(radius=thickness*arrowRadiusMult, sections=4, ch=0, pivot=(length*1.0005,0,0))[0]) y.append(cmds.cylinder(radius=thickness, sections=4, heightRatio=ratio, pivot=[0, length/2, 0], ch=0, axis=[0,1,0])[0]) y.append(cmds.cone(radius=thickness*arrowRadiusMult, sections=4, ch=0, pivot=(0,length*1.0005,0), axis=[0,1,0])[0]) z.append(cmds.cylinder(radius=thickness, sections=4, heightRatio=ratio, pivot=[0, 0, length/2], ch=0, axis=[0,0,1])[0]) z.append(cmds.cone(radius=thickness*arrowRadiusMult, sections=4, ch=0, pivot=(0,0,length*1.0005), axis=[0,0,1])[0]) nodes.extend(x) nodes.extend(y) nodes.extend(z) if color: for node in x: colorControl(node, name='red_m', color=(1,0,0)) for node in y: colorControl(node, name='green_m', color=(0,1,0)) for node in z: colorControl(node, name='blue_m', color=(0,0,1)) shapes = [] transforms = [] for node in nodes: shapes.append(cmds.listRelatives(node, fullPath=1, shapes=1)[0]) transforms.append(node) cmds.parent(shapes, xform, r=1, s=1) rotateBy = cmds.angleBetween(euler=True, v1=(1,0,0), v2=vector) cmds.rotate(rotateBy[0], rotateBy[1], rotateBy[2], xform) cmds.xform(xform, t=point) cmds.delete(transforms) return xform
def __createCon(self, fkJointCur, fkJointNext): # create controller controllerName = fkJointCur.split('|')[-1] + '_Ctl' fkCtl = self.importCtl('fk_Ctl') mc.rename(fkCtl, controllerName) posCur = mc.xform(fkJointCur, q=True, t=True, ws=True) self._move(controllerName, posCur, False) posNext = mc.xform(fkJointNext, q=True, t=True, ws=True) angle = mc.angleBetween(v1=(0, 1, 0), v2=(posNext[0]-posCur[0], posNext[1]-posCur[1], posNext[2]-posCur[2]), euler=True) mc.rotate(angle[0], angle[1], angle[2], [controllerName], os=True) # freeze mc.makeIdentity(controllerName, apply=True, t=True, r=True, s=True, normal=0, preserveNormals=True) mc.parentConstraint(controllerName, fkJointCur, maintainOffset=True, weight=1.0) return controllerName
def __next(self, porcentagem, circle): position = cmds.pointOnCurve(self.ui.ln_path.text(), top=True, pr=porcentagem, position=True) tangent = cmds.pointOnCurve(self.ui.ln_path.text(), top=True, pr=porcentagem, tangent=True) angle = cmds.angleBetween(er=True, v1=(0.0, 1.0, 0.0), v2=tangent) cmds.scale((self.ui.spin_radius.value() * random.uniform( (1 - self.ui.spin_random.value()), 1.0)), (self.ui.spin_radius.value() * random.uniform( (1 - self.ui.spin_random.value()), 1.0)), (self.ui.spin_radius.value() * random.uniform( (1 - self.ui.spin_random.value()), 1.0)), circle[0]) cmds.move(position[0], position[1], position[2], circle[0]) cmds.rotate(angle[0], angle[1], angle[2], circle[0])
def __next(self, porcentagem,eight,scale): #print porcentagem curva = self.ui.ln_path.text() position = cmds.pointOnCurve(curva, top=True, pr=porcentagem, position=True) tangent = cmds.pointOnCurve(curva, top=True, pr=porcentagem, tangent=True) angle = cmds.angleBetween(er=True, v1=(0.0, 1.0, 0.0), v2=tangent) cmds.scale((scale * random.uniform((1-self.ui.spin_random.value()), 1.0)), (scale * random.uniform((1-self.ui.spin_random.value()), 1.0)), (scale * random.uniform((1-self.ui.spin_random.value()), 1.0)), eight[0]) cmds.move(position[0], position[1], position[2], eight[0]) cmds.rotate(angle[0], angle[1], angle[2], eight[0])
def placeInstance(instanceName, position, size, face, randRotation, animatedParentGroup=None): ''' Instantiate an object and place, scale and rotate it at the desired position. - instanceName: name of the object to be instantiated - position: where to place the instance - size: how big should the instance be - face: polygon the instance is to be placed on - randRotation: if the instance should receive a random rotation on the local Y axis - animatedParentGroup: if the instance should be parented to something (required in the case of an animated base mesh) ''' faceNormal = getNormal(face) psi = mc.angleBetween(euler=True, v1=(0.0, 1.0, 0.0), v2=(faceNormal.x, faceNormal.y, faceNormal.z)) objectInstance = mc.instance(instanceName) mc.move(position[0], position[1], position[2], objectInstance) mc.rotate(psi[0], psi[1], psi[2], objectInstance) if randRotation: mc.rotate(0, rand.uniform(-180, 180), 0, objectInstance, r=True, os=True) mc.scale(size, size, size, objectInstance) if animatedParentGroup != None: mc.select(face, r=True) mc.pointOnPolyConstraint(face, animatedParentGroup, mo=False) mc.parent(objectInstance, animatedParentGroup) mc.parent(animatedParentGroup, groupName) else: mc.parent(objectInstance, groupName)
def iterToMatchSeq(self, sourceNode, centerPos, driverAttr, zeroVector, targetAngle, value, maxVal, minVal, altMode=False): # source node is JNT_IK_noFlip_l_lowerLeg # next we need the center position # after that, we need the driveAttr (kneeTwist) # the zeroVector is our default position # lastly, we need the targetAngle to know the match # value is the iteration # count is how many times we've done this # this will return a float mc.setAttr("{0}".format(driverAttr), value) print("-----") # we then need to calculate the vector form the noFlip shin's current position to the center point, so we can its value curr_pos = mc.xform(sourceNode, q=True, ws=True, translation=True) # subtract the zeroPosVector from it centerToCurr = [ curr_pos[0] - centerPos[0], curr_pos[1] - centerPos[1], curr_pos[2] - centerPos[2], ] currAngle = mc.angleBetween(v1=centerToCurr, v2=zeroVector) # now we have an angle to compare with and an angle to compare to # however, we are dealing with precision values # we'll have a tolerance value instead tolerance = 0.01 print("targetAngle: {0}".format(targetAngle)) print("currAngle: {0}".format(currAngle)) print("value: {0}".format(value)) print("maxVal: {0}".format(maxVal)) print("minVal: {0}".format(minVal)) # we return success but we don't want to keep goin if we keep failing as to avoid a system crash if (abs(currAngle[3] - targetAngle) < tolerance): return value, False, minVal, maxVal, else: # if we haven't found the angle, we need to compare the current value to the target value if currAngle[3] > targetAngle: # if the angle is greater, we need to decrement the knee value by half the value we previously tried # I am making some adjustments to make this less bouncy if altMode: value = value - value / 2 else: minVal = value # the min value is at least the current value value = ( maxVal + value ) / 2 # get the average of the max value and the current value else: # if we are coming in under our target angle, then we increase it by half the previous value if altMode: value = value + value / 2 else: # I am making some adjustments to make this less bouncy maxVal = value # the min value is at least the current value value = ( minVal + value ) / 2 # get the average of the max value and the current value # use recursion value = value % 360 return value, True, minVal, maxVal # adjust
def __init__(self,*args,**keywords): # default options self.name='limb' self.stretch=20 self.squash=0 self.twist=True # performs auto detect self.sway=True self.switch='ik' # initial ik/fk switch state self.handleOptions=[{'type':'doubleEllipse','spin':-180},{'type':'doubleEllipse','spin':-90},{'type':'doubleEllipse'},{'type':'locator'}] self.tol=1.0 # angle tolerance for preferred angles self.parent='' self.shortNames=\ { 'n':'name', 'p':'parent', 'sp':'softParent', 'co':'controlObjects', 'ho':'handleOptions' } # attributes self.controlObjects=['','','',''] self.bindPoses=[] self.joints=[] self.group='' self.orientAxis='' self.bendAxis='' self.poleAxis='' self.ctrlJoints=[] self.handles=[] self.endEffector='' self.ikHandle='' self.jointParent='' self.jointParent='' self.originalRotations={} self.bendDirection=0 self.poleVector=[] self.poleVectorWorld=[] self.upVector=[] self.aimVector=[] self.parentSpaces=[] for k in keywords: if k in self.__dict__: exec('self.'+k+'=keywords[k]') elif k in self.shortNames: exec('self.'+self.shortNames[k]+'=keywords[k]') uniqueNames(re=True) if len(args)==0: args=mc.ls(sl=True) sel=[] for a in args: sel.extend(iterable(a)) sel=hierarchyOrder(sel) # parse options defualtHandleOptions=[{'type':'doubleEllipse','spin':-180},{'type':'doubleEllipse','spin':-90},{'type':'doubleEllipse'},{'type':'locator'}] i=len(self.handleOptions) while len(self.handleOptions)<4: self.handleOption.append(defualtHandleOptions[i]) i+=1 if isinstance(self.handleOptions,dict): self.handleOptions=[self.handleOptions,self.handleOptions,self.handleOptions] elif isIterable(self.handleOptions): if len(self.handleOptions)==0: self.handleOptions.append({}) while len(self.handleOptions)<3: self.handleOptions.append(self.handleOptions[-1]) else: self.handleOptions=[{},{},{}] self.controlObjects=iterable(self.controlObjects) self.orientAxis=self.orientAxis.lower() self.baseTwist='' self.hierarchy=[] if len(sel)>2: for j in sel[:-1]: if len(hierarchyBetween(j,sel[-1]))>len(self.hierarchy): self.hierarchy=hierarchyBetween(j,sel[-1]) closest=9e999 for s in removeAll([self.hierarchy[0],self.hierarchy[-1]],sel): if\ ( len(iterable(mc.listRelatives(self.hierarchy[0],p=True)))==0 or s in mc.listRelatives(mc.listRelatives(self.hierarchy[0],p=True)[0],c=True,type='joint') ): dist=distanceBetween(s,self.hierarchy[0]) if dist<closest: closest=dist self.baseTwist=s else: self.hierarchy=hierarchyBetween(sel[0],sel[-1]) self.bindPoses=iterable(getBindPoses(self.hierarchy)) self.joints=['','',''] if len(self.hierarchy)<3: raise Exception('There are no joints between your start and end joint. No IK created.') self.joints[0]=self.hierarchy[0] self.joints[-1]=self.hierarchy[-1] # find the orientation axis self.orientAxis='x' axisLen={'x':0,'y':0,'z':0} for j in self.hierarchy[1:]: for a in ['x','y','z']: axisLen[a]+=abs(mc.getAttr(j+'.t'+a)) if axisLen[a]>axisLen[self.orientAxis]: self.orientAxis=a # find bend joint and pole vector self.originalRotations={} for j in self.hierarchy[1:-1]: # check to see if any have a non-zero preferred angle for a in removeAll(self.orientAxis,['x','y','z']): if abs(mc.getAttr(j+'.pa'+a))>=self.tol: self.originalRotations[j+'.r'+a]=mc.getAttr(j+'.r'+a) mc.setAttr(j+'.r'+a,mc.getAttr(j+'.pa'+a)) greatestAngle=0 for j in self.hierarchy[1:-1]: jPos=mc.xform(j,q=True,ws=True,rp=True) prevJPos=mc.xform(self.hierarchy[self.hierarchy.index(j)-1],q=True,ws=True,rp=True) nextJPos=mc.xform(self.hierarchy[self.hierarchy.index(j)+1],q=True,ws=True,rp=True) vAngle=mc.angleBetween(v1=normalize(jPos[0]-prevJPos[0],jPos[1]-prevJPos[1],jPos[2]-prevJPos[2]),v2=normalize(nextJPos[0]-jPos[0],nextJPos[1]-jPos[1],jPos[2]-jPos[2]))[-1] if abs(vAngle)>greatestAngle: greatestAngle=abs(vAngle) self.joints[1]=j mp=midPoint\ ( self.hierarchy[0],self.hierarchy[-1], bias=\ ( distanceBetween(self.hierarchy[0],self.joints[1])/ (distanceBetween(self.hierarchy[0],self.joints[1])+distanceBetween(self.joints[1],self.hierarchy[-1])) ) ) bendPoint=mc.xform(self.joints[1],q=True,ws=True,rp=True) self.poleVectorWorld=normalize\ ( bendPoint[0]-mp[0], bendPoint[1]-mp[1], bendPoint[2]-mp[2] ) pmm=mc.createNode('pointMatrixMult') mc.setAttr(pmm+'.vm',True) mc.connectAttr(self.joints[1]+'.worldInverseMatrix',pmm+'.im') mc.setAttr(pmm+'.ip',*self.poleVectorWorld) self.poleVector=mc.getAttr(pmm+'.o')[0] disconnectNodes(pmm) mc.delete(pmm) greatestLength=0.0 for i in [0,1,2]: if abs(self.poleVector[i])>greatestLength and ['x','y','z'][i]!=self.orientAxis: self.poleAxis=['x','y','z'][i] greatestLength=abs(self.poleVector[i]) self.bendDirection=-abs(self.poleVector[i])/self.poleVector[i] for r in self.originalRotations: mc.setAttr(r,self.originalRotations[r]) preferredAngleWarning=False if not mc.objExists(self.joints[1]): preferredAngleWarning=True mp=midPoint(self.hierarchy[0],self.hierarchy[-1]) cd=9e999 dist=0 for j in self.hierarchy[1:-1]: dist=distanceBetween(j,mp) if dist<cd: cd=dist self.joints[1]=j self.bendAxis=removeAll(self.orientAxis,['z','y','x'])[0] if self.poleAxis=='': self.poleAxis=removeAll([self.orientAxis,self.bendAxis],['x','y','z'])[0] if self.bendAxis=='': self.bendAxis=removeAll([self.orientAxis,self.poleAxis],['x','y','z'])[0] if self.orientAxis=='': self.orientAxis=removeAll([self.bendAxis,self.poleAxis],['x','y','z'])[0] if self.poleAxis=='x': self.poleVector=[-self.bendDirection,0.0,0.0] if self.poleAxis=='y': self.poleVector=[0.0,-self.bendDirection,0.0] if self.poleAxis=='z': self.poleVector=[0.0,0.0,-self.bendDirection] if self.bendAxis=='x': self.upVector=[-self.bendDirection,0.0,0.0] if self.bendAxis=='y': self.upVector=[0.0,-self.bendDirection,0.0] if self.bendAxis=='z': self.upVector=[0.0,0.0,-self.bendDirection] if self.orientAxis=='x': self.aimVector=[self.bendDirection,0.0,0.0] if self.orientAxis=='y': self.aimVector=[0.0,self.bendDirection,0.0] if self.orientAxis=='z': self.aimVector=[0.0,0.0,self.bendDirection] if mc.objExists(self.baseTwist): conn=False for a in ['.r','.rx','.ry','.rz']: if mc.connectionInfo(self.baseTwist+a,id=True): conn=True if not conn: mc.orientConstraint(self.joints[0],self.baseTwist,sk=self.orientAxis) # load ik2Bsolver - ikRPSolver does not work well with this setup mel.eval('ik2Bsolver') self.create() if preferredAngleWarning: raise Warning('Warning: Joints are co-linear and no preferred angles were set. Results may be unpredictable.')
def do_align_rot_axis(p1, p2, trackGrp): v1 = cmds.xform( p1, worldSpace = True, translation = True, query = True ) v2 = cmds.xform( p2, worldSpace = True, translation = True, query = True ) axis = ( v2[0]-v1[0], v2[1]-v1[1], v2[2]-v1[2] ) angle = cmds.angleBetween( v1= axis, v2 = (1, 0, 0), euler = True ) cmds.rotate( angle[0], angle[1], angle[2], trackGrp+'.rotateAxis')
####################################################################################################################### import maya.cmds as cmds sl = cmds.ls(selection=True, flatten=True) toVerts = cmds.polyListComponentConversion(sl, tv=True) verts = cmds.ls(toVerts, flatten=True) flatnormals = cmds.polyNormalPerVertex(verts, q=True, xyz=True) normals = [] normals = zip(*[iter(flatnormals)] * 3) xNorm = [x[0] for x in normals] xVector = sum(xNorm) / len(xNorm) yNorm = [x[1] for x in normals] yVector = sum(yNorm) / len(yNorm) zNorm = [x[2] for x in normals] zVector = sum(zNorm) / len(zNorm) finalAngle = cmds.angleBetween(euler=True, v2=[xVector, yVector, zVector], v1=[0, 1, 0]) ## TEST OBJECT ALIGNMENT cmds.xform('pCone1', ws=1, ro=[finalAngle[0], finalAngle[1], finalAngle[2]]) ####################################################################################################################### ### USE THIS METHOD FOR FACE SELECTION. STILL AN ISSUES IF OBJECT IS ROTATED, NEED TO CALCUATE THAT IN AS WELL SOMEHOW. ####################################################################################################################### import maya.cmds as cmds import decimal ## Poluate Vars sl = cmds.ls(selection=True, flatten=True) faceList = [] xValue = [] yValue = [] zValue = []
def initLife(mesh, name, id, graph): print 'Generating normals...' # Initialize neighbors array neighbors = [] cmds.select(name) # Extract vertices numVerts = cmds.polyEvaluate(v=True) for i in range(0, numVerts): neighbors.append([]) for i in range(0, numVerts): print '... pass {0} out of {1}'.format(i, numVerts - 1) # Find 1st level neighbors for j in graph[i]: cmds.select('{0}.e[{1}]'.format(name, j)) found = cmds.polyInfo(ev=True) tokens = found[0].split(' ') # Skip over non-alphanumeric strings for t in tokens: tmp = '{0}'.format(t) if (tmp.isalnum() == True) and (tmp != 'EDGE') and (int(tmp) != i): neighbors[i].append(int(tmp)) print 'Initializing objects...' group = cmds.group(em=True, n=id) cmds.select(name) # Extract vertices numVerts = cmds.polyEvaluate(v=True) map = [] for i in range(0, numVerts): row = '' map.append(row) for i in range(0, numVerts): # Find the coordinates of each vertex trans = cmds.pointPosition('{0}.vtx[{1}]'.format(name, i)) #currVector = om.MVector( trans[0], trans[1], trans[2] ) currVector = om.MVector(0.0, 1.0, 0.0) currVector.normalize() neighborVectors = [] # Find the vectors from each vertex to each neighbor for n in neighbors[i]: vector = om.MVector() coord = cmds.pointPosition('{0}.vtx[{1}]'.format(name, n)) vector.x = coord[0] - trans[0] vector.y = coord[1] - trans[1] vector.z = coord[2] - trans[2] vector.normalize() neighborVectors.append(vector) # Find the normal normVector = om.MVector(0, 0, 0) for vector in neighborVectors: normVector = normVector + vector normVector.normalize() normVector.x = -normVector.x normVector.y = -normVector.y normVector.z = -normVector.z # Create joint in order to test the calculated normal #newJoint = cmds.joint() #cmds.parent( newJoint, jointGroup ) #cmds.xform( newJoint, t=(trans[0] + normVector.x, trans[1] + normVector.y, trans[2] + normVector.z) ) # Create an object and add to the group obj = cmds.polyCube(sx=1, sy=1) map = createObjMap(map, i, obj) cmds.parent(obj[0], group) # First rotate X-axis to face the orientation angle by projecting normal onto the XZ-plane projVector = om.MVector(normVector.x, 0.0, normVector.z) orientX = cmds.angleBetween(euler=True, v1=(1.0, 0.0, 0.0), v2=(projVector.x, projVector.y, projVector.z)) # Perform rotation cmds.xform(obj, r=True, ro=(orientX[0], orientX[1], orientX[2])) eulerRot = om.MEulerRotation(orientX[0], orientX[1], orientX[2], 0) currVector.rotateBy(eulerRot) orientY = cmds.angleBetween(euler=True, v1=(currVector.x, currVector.y, currVector.z), v2=(normVector.x, normVector.y, normVector.z)) # Perform rotation cmds.xform(obj, r=True, ro=(orientY[0], orientY[1], orientY[2])) # Translate object to vertex cmds.xform(obj, t=(trans[0], trans[1], trans[2])) cmds.select(mesh) return map
def scatter_map(self, args): # Scatter gravels on map self.selectedObjs = cmds.ls(selection=True) # Loop all maps for n in range(0, len(self.selectedObjs)): self.obj = self.selectedObjs[n] shapeNode = cmds.listRelatives(self.obj, shapes=True) nodeType = cmds.nodeType(shapeNode) # Initialize vertex variables self.randVerts = [] self.numVerts = cmds.polyEvaluate(self.obj, vertex=True) self.vertex_str_list = [] self.amount_idx = 0 # Select vertex randomly as many as amount and Save it to ranVerts list for i in range(0, self.amount): self.randVerts.append(random.randint(0, self.numVerts)) # Combine the vertex of selected object to string for k in self.randVerts: self.vertex_str_list.append("{0}.vtx[{1}]".format(self.obj, k)) print self.vertex_str_list # Make group for gravels and Move group position to selected map gravelGrp = cmds.group(empty=True, name="Gravel_Grp#") obj_pos = cmds.xform(self.obj, q=True, ws=True, t=True) cmds.move(obj_pos[0], obj_pos[1], obj_pos[2], gravelGrp) # Save position of the vertex selected randomly vertPos = [0, 0, 0] rot = [0.0, 0.0, 0.0] for r in self.vertex_str_list: self.randVerts_pos = cmds.xform(r, q=True, ws=True, t=True) for j in range(0, 3): vertPos[j] = self.randVerts_pos[j] # Get the normal vector on selected vertex and Get rotationXYZ self.normal = cmds.polyNormalPerVertex(r, query=True, normalXYZ=True)[:3] rot[0] = cmds.angleBetween(v1=self.normal, v2=(1.0, 0.0, 0.0))[3] - 90 rot[1] = cmds.angleBetween(v1=self.normal, v2=(0.0, 1.0, 0.0))[3] - 90 rot[2] = cmds.angleBetween(v1=self.normal, v2=(0.0, 0.0, 1.0))[3] - 90 # Create and Arrange the gravels. self.randomsize = random.uniform( self.min_size, self.max_size) * self.global_size gravel = self.get_gravel(self) temp = random.uniform(0.2, 0.4) cmds.scale( random.uniform(0.7, 0.9) * self.randomsize, temp * self.randomsize, self.randomsize, gravel) cmds.move(vertPos[0], vertPos[1] + temp * self.randomsize / 2, vertPos[2], gravel) cmds.move(vertPos[0], vertPos[1], vertPos[2], ".scalePivot", ".rotatePivot", absolute=True) cmds.rotate(rot[0], rot[1], rot[2], gravel) cmds.parent(gravel, gravelGrp) self.amount_idx += 1 cmds.select(gravelGrp, replace=True)
def Voronoi(uvs, selection): try: originalName = selection mcSelection = getSelectionSurface(selection)[0] surface = getSelectionSurface(selection)[1] except (IndexError): return centers = [ WS_from_UV(uv, surface) for uv in uvs if WS_from_UV(uv, surface)[0] ] newFaces = [] #create progress bar progressBarWindow = utils.pyside.SimpleProgressBar( "Voronoi Cells", "Dividing 0/{0}".format(len(centers))) progressBarWindow.show() #setting cursor to wait cursor utils.pyside.qw.QApplication.setOverrideCursor( utils.pyside.qc.Qt.WaitCursor) for i, from_point in enumerate(centers): working_geom = mc.duplicate(mcSelection) for to_point in centers: if from_point != to_point: print "**** Cut " locator = mc.spaceLocator() mc.move(from_point[0], from_point[1], from_point[2]) mc.parent(locator, working_geom) center_point = [(e1 + e2) / 2 for (e1, e2) in zip(to_point, from_point)] n = [(e1 - e2) for (e1, e2) in zip(from_point, to_point)] es = mc.angleBetween(euler=True, v1=[0, 0, 1], v2=n) mc.polyCut(working_geom, deleteFaces=True, cutPlaneCenter=center_point, cutPlaneRotate=es) # RandomColors mc.setAttr(working_geom[0] + '.displayColors', 1) mc.polyOptions(working_geom[0], cm='diffuse') mc.polyColorPerVertex(working_geom[0], rgb=utils.geo.random_color(i)) newFaces.append(working_geom[0]) #update progressbar progressBarWindow.updateLabel("Dividing {0}/{1}".format( i, len(centers))) progressBarWindow.updateProgress( len(centers) / (i + 1.0) * 100.0) #removing progressbar utils.pyside.qw.QApplication.restoreOverrideCursor() progressBarWindow.hide() progressBarWindow.deleteLater() mc.delete(mcSelection) scalp = mc.polyUnite(newFaces, n=originalName) mc.polyMergeVertex(scalp, d=0.25)
cmds.refresh() # Duplicate object to create splinters workingGeom = cmds.duplicate(obj) cmds.select(workingGeom[0]) #cmds.rigidBody(active=True, mass=5, bounciness=0.08, collisions=False) #RigidBody.CreateRigidBody(True).executeCommandCB() cmds.setAttr(str(workingGeom[0])+'.visibility', 1) cmds.parent(workingGeom, allShards) for endPoint in voroPoints: if startPoint != endPoint: # Construct line segments and calculate the mid point and its normal dirVec = [(pt2-pt1) for (pt1, pt2) in zip(startPoint, endPoint)] centerPoint = [(pt1 + pt2)/2 for (pt1, pt2) in zip(startPoint, endPoint)] planeAngle = cmds.angleBetween( euler=True, v1=[0,0,1], v2=dirVec ) # Cut Geometry (Bullet shatter) cmds.polyCut(workingGeom[0], deleteFaces=True, cutPlaneCenter = centerPoint, cutPlaneRotate = planeAngle) # Applying the material to the cut faces originalFaces = cmds.polyEvaluate(workingGeom[0], face=True) cmds.polyCloseBorder(workingGeom[0], ch=False) resultingFaces = cmds.polyEvaluate(workingGeom[0], face=True) newFaces = resultingFaces - originalFaces cutFaces = ( '%s.f[ %d ]' % (workingGeom[0], (resultingFaces + originalFaces - 1))) cmds.sets(cutFaces, forceElement = (surfaceMat + 'SG'), e=True) cmds.xform(workingGeom, cp=True)
####################################################################################################################### ## YOU CAN USE THIS METHOD FOR LINES AND VERTS ## ####################################################################################################################### import maya.cmds as cmds sl=cmds.ls(selection=True, flatten=True) toVerts = cmds.polyListComponentConversion(sl, tv=True) verts = cmds.ls(toVerts, flatten = True) flatnormals = cmds.polyNormalPerVertex(verts, q=True, xyz=True) normals = [] normals = zip(*[iter(flatnormals)]*3) xNorm = [x[0] for x in normals]; xVector = sum(xNorm)/len(xNorm) yNorm = [x[1] for x in normals]; yVector = sum(yNorm)/len(yNorm) zNorm = [x[2] for x in normals]; zVector = sum(zNorm)/len(zNorm) finalAngle = cmds.angleBetween( euler=True, v2= [xVector, yVector, zVector], v1=[0, 1, 0] ) ## TEST OBJECT ALIGNMENT cmds.xform('pCone1', ws = 1, ro = [ finalAngle[0], finalAngle[1] , finalAngle[2] ] ) ####################################################################################################################### ### USE THIS METHOD FOR FACE SELECTION. STILL AN ISSUES IF OBJECT IS ROTATED, NEED TO CALCUATE THAT IN AS WELL SOMEHOW. ####################################################################################################################### import maya.cmds as cmds import decimal ## Poluate Vars sl = cmds.ls(selection=True, flatten=True) faceList = [] xValue = [] yValue = [] zValue = []
def createLRA(node=None, matrix=None, name=None, color=True, thickness=0.1, length=2, vector=[1, 0, 0], point=[0, 0, 0], arrowRadiusMult=2): ''' Creates an LRA at the origin, or at a node, or a mmx Example: createLRA(length=2, thickness=0.05, arrowRadiusMult=3) ''' if not name: name = 'arrow_CTRL' nodes = [] x, y, z = [], [], [] #calc length for thickness ratio = length / thickness xform = cmds.createNode('transform', ss=1, name=name) x.append( cmds.cylinder(radius=thickness, sections=4, heightRatio=ratio, pivot=[length / 2, 0, 0], ch=0, axis=[1, 0, 0])[0]) x.append( cmds.cone(radius=thickness * arrowRadiusMult, sections=4, ch=0, pivot=(length * 1.0005, 0, 0))[0]) y.append( cmds.cylinder(radius=thickness, sections=4, heightRatio=ratio, pivot=[0, length / 2, 0], ch=0, axis=[0, 1, 0])[0]) y.append( cmds.cone(radius=thickness * arrowRadiusMult, sections=4, ch=0, pivot=(0, length * 1.0005, 0), axis=[0, 1, 0])[0]) z.append( cmds.cylinder(radius=thickness, sections=4, heightRatio=ratio, pivot=[0, 0, length / 2], ch=0, axis=[0, 0, 1])[0]) z.append( cmds.cone(radius=thickness * arrowRadiusMult, sections=4, ch=0, pivot=(0, 0, length * 1.0005), axis=[0, 0, 1])[0]) nodes.extend(x) nodes.extend(y) nodes.extend(z) if color: for node in x: colorControl(node, name='red_m', color=(1, 0, 0)) for node in y: colorControl(node, name='green_m', color=(0, 1, 0)) for node in z: colorControl(node, name='blue_m', color=(0, 0, 1)) shapes = [] transforms = [] for node in nodes: shapes.append(cmds.listRelatives(node, fullPath=1, shapes=1)[0]) transforms.append(node) cmds.parent(shapes, xform, r=1, s=1) rotateBy = cmds.angleBetween(euler=True, v1=(1, 0, 0), v2=vector) cmds.rotate(rotateBy[0], rotateBy[1], rotateBy[2], xform) cmds.xform(xform, t=point) cmds.delete(transforms) return xform
def angleBetween(*args, **kwargs): res = cmds.angleBetween(*args, **kwargs) if not kwargs.get('query', kwargs.get('q', False)): res = _factories.maybeConvert(res, _general.PyNode) return res
def leg_IKtoResult(self, leftRight, lowerLegMode, *args): # IK to FK try: default_upperLeg_length = mc.getAttr( "{0}lowerLeg_noFlip_stretch_COND.colorIfFalseR".format( leftRight)) # default_upperLeg_length = 31.855 except: default_upperLeg_length = 31.855 try: default_lowerLeg_length = mc.getAttr( "{0}foot_noFlip_stretch_COND.colorIfFalseR".format( leftRight)) # default_lowerLeg_length = 44.037 except: default_lowerLeg_length = 44.037 # current values curr_upperLeg_length = mc.getAttr( "JNT_BND_{0}lowerLeg.translateX".format(leftRight)) curr_lowerLeg_length = mc.getAttr( "JNT_BND_{0}legEnd.translateX".format(leftRight)) tolerance = 0.01 if ((abs(curr_upperLeg_length - default_upperLeg_length) > tolerance) or (abs(curr_lowerLeg_length - default_lowerLeg_length) > tolerance)): mc.setAttr("CTRL_{0}knee.kneeSnap".format(leftRight), 1) mc.setAttr("CTRL_settings_{0}leg.IK_stretch".format(leftRight), 0) mc.matchTransform("CTRL_{0}foot".format(leftRight), "GRP_IKsnap_{0}foot".format(leftRight), rotation=True) mc.matchTransform("CTRL_{0}foot".format(leftRight), "GRP_IKsnap_{0}foot".format(leftRight), position=True) # match the toe toeVal = mc.getAttr("JNT_BND_{0}ball.rotateY".format(leftRight)) mc.setAttr("CTRL_{0}foot.toeWiggle".format(leftRight), toeVal) if lowerLegMode == 2: mc.matchTransform("CTRL_{0}knee".format(leftRight), "JNT_BND_{0}lowerLeg".format(leftRight), position=True) mc.setAttr("CTRL_{0}foot.autoManualKneeBlend".format(leftRight), 1) # set the knee to manual else: leg_length_factor = mc.getAttr( "JNT_IK_noFlip_{0}lowerLeg_translateX.output".format( leftRight)) / default_upperLeg_length upperLeg_length_factor = curr_upperLeg_length / ( default_upperLeg_length * leg_length_factor ) # get the current thigh length. This will get us a normal sized leg lowerLeg_length_factor = curr_lowerLeg_length / ( default_lowerLeg_length * leg_length_factor ) # get the current shin length. This will get us a normal sized leg # We have set the leg length mc.setAttr("CTRL_{0}foot.autoKneeUpperLegLength".format(leftRight), upperLeg_length_factor) mc.setAttr("CTRL_{0}foot.autoKneeLowerLegLength".format(leftRight), lowerLeg_length_factor) mc.setAttr("CTRL_{0}foot.kneeTwist".format(leftRight), 0) noFlipLowerLegPos = mc.xform( "JNT_IK_noFlip_{0}lowerLeg".format(leftRight), q=True, ws=True, t=True) bndLowerLegPos = mc.xform("JNT_BND_{0}lowerLeg".format(leftRight), q=True, ws=True, t=True) # we are creating a common origin around which to measure our angles center_shin_pos = mc.xform( "JNT_IK_noFlip_{0}legEnd".format(leftRight), q=True, ws=True, t=True) # once we have an origin and two points in space, we need to find actual vectors that move from that origin to each point # we can do this by assigning a pair of variables between the noFlip shin position in position and center point shin position centerToNoFlip = [] centerToBND = [] for i in range(len(noFlipLowerLegPos)): # getting the difference addVal = noFlipLowerLegPos[i] - center_shin_pos[i] centerToNoFlip.append(addVal) addVal2 = bndLowerLegPos[i] - center_shin_pos[i] centerToBND.append(addVal2) # now we can use the angleBetween command to find the angle between them. # We'll use the -v1 and -v2 flags to assign our centerTo result and centerToNoFlip vectors deltaAngle = mc.angleBetween(v1=centerToNoFlip, v2=centerToBND) # the angleBetween command stores 4 values, the first three represent the axis on which the angle is # measured, while the last represents the angle itself # we don't need to be concerned too much on the axis, because the other angles we'll compare it to # will be on the same axis, we just want the angle itself # now we have an axis on which to compare this to sourceNode = "JNT_IK_noFlip_{0}lowerLeg".format(leftRight) driverAttr = "CTRL_{0}foot.kneeTwist".format(leftRight) # start at 90 degrees at 0ths try # print("deltaAngle: \t{0}".format(deltaAngle)) # self.iterateToMatchRecursive(sourceNode, center_shin_pos, driverAttr, centerToNoFlip, deltaAngle[3], 90, 0) maxCount = 5000 kneeTwistValue = 180.0 # we need this to be a float maxVal = 360.0 # we need this to be a float minVal = 0 # we need this to be a float repeat = True for i in range(maxCount): print("attempt #: {0}".format(i)) # python doesn't really like recursion if repeat: kneeTwistValue, repeat, minVal, maxVal = self.iterToMatchSeq( sourceNode, center_shin_pos, driverAttr, centerToNoFlip, deltaAngle[3], kneeTwistValue, maxVal, minVal) else: print("value found") break if repeat: print("Sequence failed") shinIK = "JNT_IK_noFlip_{0}lowerLeg".format(leftRight) curr_shin_pos = mc.xform(shinIK, q=True, ws=True, t=True) tolerance = 0.1 print("kneeTwist: {0}".format(kneeTwistValue)) if ((abs(curr_shin_pos[0] - bndLowerLegPos[0]) > tolerance) or (abs(curr_shin_pos[1] - bndLowerLegPos[1]) > tolerance) or (abs(curr_shin_pos[2] - bndLowerLegPos[2]) > tolerance)): kneeTwistValue = kneeTwistValue * -1 mc.setAttr(driverAttr, kneeTwistValue) print("new kneeTwist: {0}".format(kneeTwistValue)) print("centerToNoFlip: {0}".format(centerToNoFlip)) mc.setAttr("CTRL_{0}foot.autoManualKneeBlend".format(leftRight), 0) # set the knee to manual mc.setAttr( "CTRL_settings_{0}leg.fkik_blend".format(leftRight), 1) # now that we have the FIK in place, we want to switch to it
for chain_child in cmds.listRelatives(child, ad=True, type='joint'): c_length += cmds.getAttr( chain_child+'.tx' ) length_dict[child] = c_length if (c_length < length_dict[shortest]): shortest = child # now we have the shortest chain (thumb), we can start finding other fingers # I will use two vectors, v1 will be the shortest chain (thumb), and v2 every other finger # then compare the angle between the two for each of them v1 = cmds.getAttr(shortest+'.translate')[0] angle_dict = dict() for child in im_children: if child != shortest: v2 = cmds.getAttr(child+'.translate')[0] ab = cmds.angleBetween(v1=v1,v2=v2)[-1] angle_dict[child] = ab # sort angles in a list angle_list = angle_dict.values() angle_list.sort() # match angles to names based on their distance from the thumb if (hand_ctrl.lower()).find('right') >= 0: side = 'right' else: side = 'left' for child, angle in angle_dict.iteritems():