Skip to content

Instantly share code, notes, and snippets.

@tbttfox
Created November 2, 2022 23:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tbttfox/db629e3b594c30d5b1abcbe975315bed to your computer and use it in GitHub Desktop.
Save tbttfox/db629e3b594c30d5b1abcbe975315bed to your computer and use it in GitHub Desktop.
from maya import cmds
from itertools import groupby
def mayaSelRange(vals):
"""Convert maya cmds.ls() component selection list into indices
Arguments:
vals (list): A list of components like what you get out of cmds.ls(sl=True)
Returns:
list: A list of integer indices
"""
out = []
for val in vals:
nn = val.split('[')[1][:-1].split(':')
nn = list(map(int, nn))
out.extend(range(nn[0], nn[-1] + 1))
return out
def list_to_ranges(iterable):
"""Convert a list of integers into inclusive ranges
so [1, 2, 3, 4, 17, 18, 19] would get turned into [(1, 4), (17, 19)]
Arguments:
iterable (iterable): The list of integers
Returns:
list: A list of (start, stop) integer tuples
"""
iterable = sorted(set(iterable))
for key, group in groupby(enumerate(iterable), lambda x: x[1] - x[0]):
group = list(group)
yield (group[0][1], group[-1][1])
def generate_components(obj, comp, indices, do_ranges=True):
"""Build a component selection list for an object with the given indices
Used like so:
generate_components("pCylinder", "v", indices)
>>> ['pCylinder.v[20:39]', 'pCylinder.v[41]']
Arguments:
obj (str): The name of the object
comp (str): The component type
indices (list): The list of indices to select
Returns:
list: The selection list of components
"""
if do_ranges:
for pair in list_to_ranges(indices):
if pair[1] == pair[0]:
yield '{0}.{1}[{2}]'.format(obj, comp, pair[0])
else:
yield '{0}.{1}[{2}:{3}]'.format(obj, comp, pair[0], pair[1])
else:
for index in indices:
yield '{0}.{1}[{2}]'.format(obj, comp, index)
def buildNeighborDict(vertColl):
"""Parse vertices into a dictionary of neighbors, limited to the original vertex set
Arguments:
vertColl (list): A list of verts like what you get out of cmds.ls(sl=True)
Returns:
dict: A dictionary formatted like {vertIndex: [neighborVertIdx, ...], ...}
"""
# Get the object name
objName = vertColl[0].split('.')[0]
verts = set(mayaSelRange(vertColl))
neighborDict = {}
for v in verts:
vname = '{0}.vtx[{1}]'.format(objName, v)
edges = cmds.polyListComponentConversion(vname, fromVertex=True, toEdge=True)
neighbors = cmds.polyListComponentConversion(
edges, fromEdge=True, toVertex=True
)
neighbors = set(mayaSelRange(neighbors))
neighbors.remove(v)
neighborDict[v] = list(neighbors & verts)
return neighborDict
def sortLoops(neighborDict):
"""Sort vertex loop neighbors into individual loops
Arguments:
neighborDict (dict): A dictionary formatted like {vertIndex: [neighborVertIdx, ...], ...}
Returns:
list of lists: A list of lists containing ordered vertex loops.
Only if the loop is closed, the first and last element will be the same.
"""
# Make a copy of the neighborDict that only contains neighbors in the current dict
vk = neighborDict.viewkeys()
neighborDict = {k: list(set(v) & vk) for k, v in neighborDict.items()}
loops = []
# If it makes it more than 1000 times through this code, something is probably wrong
# This way I don't get stuck in an infinite loop like I could with while(neighborDict)
for _ in range(1000):
if not neighborDict:
break
vertLoop = [neighborDict.keys()[0]]
vertLoop.append(neighborDict[vertLoop[-1]][0])
# Loop over this twice: Once forward, and once backward
# This handles loops that don't connect back to themselves
for i in range(2):
vertLoop = vertLoop[::-1]
while vertLoop[0] != vertLoop[-1]:
nextNeighbors = neighborDict[vertLoop[-1]]
if len(nextNeighbors) == 1:
break
elif nextNeighbors[0] == vertLoop[-2]:
vertLoop.append(nextNeighbors[1])
else:
vertLoop.append(nextNeighbors[0])
# Remove vertices I've already seen from the dict
# Don't remove the same vert twice if the first and last items are the same
start = 0
if vertLoop[0] == vertLoop[-1]:
start = 1
for v in vertLoop[start:]:
del neighborDict[v]
loops.append(vertLoop)
else:
raise RuntimeError(
"You made it through 1000 loops, and you still aren't done? Something must be wrong"
)
return loops
# 4 divisions will result in 4 x 4 = 16 separate pieces
divisions = 4
# get the selected object
sel = cmds.ls(selection=True, long=True)
obj = sel[0]
# get the verts along the border of the plane
faces = cmds.polyListComponentConversion(obj, toFace=True)
border_edges = cmds.polyListComponentConversion(faces, toEdge=True, bo=True)
border_edges = cmds.ls(border_edges, flatten=True, long=True)
border_edge_set = set(border_edges)
border_verts = cmds.polyListComponentConversion(border_edges, toVertex=True)
border_verts = cmds.ls(border_verts, flatten=True, long=True)
# get one of the corner verts
for v in border_verts:
adj_edges = cmds.polyListComponentConversion(v, toEdge=True)
adj_edges = cmds.ls(adj_edges, flatten=True, long=True)
if len(adj_edges) <= 2:
corner_vert = v
break
# get the corner edges
corner_edges = cmds.polyListComponentConversion(corner_vert, toEdge=True)
corner_edges = cmds.ls(corner_edges, flatten=True)
flatLoopList = []
for k in range(2):
# get the edges of one dimension
cmds.select(corner_edges[k])
cmds.polySelectConstraint(t=0x8000, pp=4)
edges_u = cmds.ls(selection=True)
# Get the vertices in order along that dimension
verts_u = cmds.polyListComponentConversion(edges_u, toVertex=True)
neighbors_u = buildNeighborDict(verts_u)
neighbors_u = sortLoops(neighbors_u)[0] # should only ever have 1 loop
neighbors_u = list(generate_components(obj, 'vtx', neighbors_u, do_ranges=False))
# Get the edge pointing inward that's adjacent to each vertex along the dimension edge
# And get the loop along that edge
for neigh in neighbors_u[:: len(neighbors_u) // divisions]:
adj_edges = cmds.polyListComponentConversion(neigh, toEdge=True)
adj_edges = cmds.ls(adj_edges, flatten=True, long=True)
inner_edge = set(adj_edges) - border_edge_set
if not inner_edge:
# could be one of the corners
continue
if not len(inner_edge) == 1:
# Not sure that this could happen
raise ValueError("Something is wrong here")
innerEdgeIdx = mayaSelRange(inner_edge)[0]
edge_loop = cmds.polySelect(
obj, edgeLoop=innerEdgeIdx, noSelection=True, asSelectString=True
)
flatLoopList.extend(edge_loop)
# split edges
cmds.polySplitEdge(flatLoopList, op=1)
# separate
cmds.polySeparate(obj, ch=0)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment