Code Snippet

Maya - Circular Topology Fix


NOTE: Run the script a few times to get the perfect alignment!


# Paul Ambrosiussen - Technical Artist
# Free Code Snippet

import maya.cmds as cmds
from math import atan2, pi, sin, cos, sqrt, degrees


def ConvertFaceToVertex(a_Face):
	t_vertices = cmds.polyListComponentConversion(a_Face, tv=1)
	cmds.select(t_vertices)
	return cmds.filterExpand(sm=31)

def GetVertexPositions(a_Vertices):
	return [cmds.pointPosition(x, w=1) for x in a_Vertices]

def GetAverageV3(a_list):
	t_vec3 = ["","",""]
	t_vec3[0] = sum([x[0] for x in a_list]) / len(a_list)
	t_vec3[1] = sum([x[1] for x in a_list]) / len(a_list)
	t_vec3[2] = sum([x[2] for x in a_list]) / len(a_list)
	return t_vec3

def GetPositionOnCircle(a_Radius, a_Angle, a_Center):
	t_pos = ['', '', '']
	t_pos[0] = a_Center[0] + a_Radius * cos(a_Angle) 
	t_pos[1] = a_Center[1]
	t_pos[2] = a_Center[2] + a_Radius * sin(a_Angle) 
	return t_pos

def GetAverageRadius(a_list, a_Center):
	return sum([sqrt(pow(a_Center[0] - x[0], 2) + pow(a_Center[2] - x[2], 2)) for x in a_list]) / len(a_list)

def GetAngleBetweenPoints(a_Pos1, a_Pos2, a_Origin):
	angle = degrees(math.atan2(a[1]-origin[1], a[0]-origin[0]) - math.atan2(b[1]-origin[1], b[0]-origin[0]))
	return angle+360 if angle < 0 else angle

def RotateFaceBasedOnVertices(a_Face, a_Center, a_Vtx):
	t_vtx = cmds.polyListComponentConversion( a_Vtx[0], te=True, fv=True)
	t_connector = [x for x in cmds.polyListComponentConversion(t_vtx, tv=1) if x not in a_Vtx][0]
	t_positions = GetVertexPositions([t_connector, a_Vtx[0]])
	t_extraRotation = GetAngleBetweenPoints(t_positions[0], t_positions[1], a_Center)
	cmds.select(a_Face)
	cmds.xform(r=True, ro=(0, -t_extraRotation-360, 0), piv=a_Center )

def MainFunction():

	#Get Face Selection, clamp it to 1
	face = cmds.filterExpand(sm=34)[:1]
	
	#Get Vertex Selection
	vertexIdentifiers = ConvertFaceToVertex(face)
	vertexPositions = GetVertexPositions(vertexIdentifiers)
	
	#Vertex identifier and Position
	vertexData = zip(vertexIdentifiers,vertexPositions)

	#CalculateAverageVertexPosition
	avgPos = GetAverageV3(vertexPositions)

	#Sorted All points Clockwise
	vertexData.sort(key=lambda x: atan2(x[1][0]-avgPos[0], x[1][2]-avgPos[2]), reverse=True)
	cmds.select(vertexData[0][0])
	
	radius = GetAverageRadius(vertexPositions, avgPos)
	angle = pi*2 / len(vertexPositions)
	angleOffset = pi*2 - (atan2(vertexData[0][1][0]-avgPos[0], vertexData[0][1][2]))

	#Set Vertex Position On Circle
	for i in range(0,len(vertexData)):
		cmds.xform( vertexData[i][0], a=1, ws=1, t=GetPositionOnCircle(radius, angle*i+angleOffset, avgPos) )

	RotateFaceBasedOnVertices(face, avgPos, vertexIdentifiers)

MainFunction()