Created
November 2, 2022 23:43
-
-
Save tbttfox/db629e3b594c30d5b1abcbe975315bed to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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