Source code for bgdev.tools.symmetry_api

"""Utility methods used to sort nodes and graphs.

:created: 20/11/2020
:author: Benoit GIELLY <benoit.gielly@gmail.com>
"""
from __future__ import absolute_import

import logging

from maya import cmds, mel
from maya.api import OpenMaya

LOG = logging.getLogger(__name__)


[docs]class Symmetry(object): """Generates a symmetry table for selected mesh."""
[docs] def __init__(self): self.table = {} self.edge = None self.mesh = None self.update()
@property def center(self): """Get center vertices.""" return self.get_side_vertices("center") @property def left(self): """Get left side vertices.""" return self.get_side_vertices("left") @property def right(self): """Get right side vertices.""" return self.get_side_vertices("right")
[docs] @staticmethod def get_vertex_id(vertex): """Get vertex index from name. Args: vertex (str): Full vertex name (eg. "mesh.vtx[15]") Return: int: Index of the vertex as integer. """ return int(vertex.rsplit("[")[-1].rsplit("]")[0])
[docs] def get_vertex_name(self, index): """Get vertex name from index. Args: index (int): Index of the vertex. Return: str: Full vertex name (eg. "mesh.vtx[15]") """ return "{}.vtx[{}]".format(self.mesh, index)
[docs] def get_side_vertices(self, side): """Get all components of given side. Args: side (str): Side of the table to query. Either "left", "center" or "right". Returns: list: List of side-vertices. """ return [self.get_vertex_name(i) for i in self.table.get(side)]
[docs] def mirror_selection(self, add=False): """Mirror selected vertices. Args: add (bool): Add to existing selection when True. """ selection = cmds.ls(selection=True, flatten=True) vertices = self.mirror(selection) cmds.select(vertices, add=add)
[docs] def mirror(self, vertices): """Mirror vertices. Args: vertices (list): List of vertices to mirror. Yields: str: The next vertex full name in the given list. """ for each in vertices: index = self.get_vertex_id(each) yield self.get_vertex_name(self.table[index])
[docs] def update(self): """Generate a symmetry table.""" # get selected edge, mesh name and all vertices self.edge = (cmds.ls(selection=True) or [None])[0] self.mesh = self.edge.rpartition(".")[0] # populate the table using maya topology symmetry (ergh...) try: cmds.symmetricModelling(self.edge, topoSymmetry=True) self.populate_table() except Exception: raise else: mel.eval("reflectionSetMode none;")
[docs] def populate_table(self): """Populate the symmetry table with vertices. Notes: Maya topology symmetry must be activated. """ self.table.clear() cmds.select(self.mesh + ".vtx[*]", symmetry=True) rich_selection = OpenMaya.MGlobal.getRichSelection() cmds.select(clear=True) clean_list = rich_selection.getSymmetry() clean_sel_iter = OpenMaya.MItSelectionList(clean_list) while not clean_sel_iter.isDone(): clean_dag, clean_obj = clean_sel_iter.getComponent() clean_vtx_iter = OpenMaya.MItMeshVertex(clean_dag, clean_obj) while not clean_vtx_iter.isDone(): clean_index = int(clean_vtx_iter.index()) clean_vtx = "%s.vtx[%s]" % (self.mesh, clean_index) # TODO: Figure something to speed this up... cmds.select(clean_vtx, symmetry=True) sym_rich_sel = OpenMaya.MGlobal.getRichSelection() sym_list = sym_rich_sel.getSelection() sym_sel_iter = OpenMaya.MItSelectionList(sym_list) sym_dag, sym_obj = sym_sel_iter.getComponent() sym_vtx_iter = OpenMaya.MItMeshVertex(sym_dag, sym_obj) sym_index = int(sym_vtx_iter.index()) self.table[clean_index] = sym_index self.table.setdefault("left", set()).add(clean_index) self.table[sym_index] = clean_index self.table.setdefault("right", set()).add(sym_index) clean_vtx_iter.next() clean_sel_iter.next() # add center vertices clean_sel_iter = OpenMaya.MItSelectionList(clean_list) mesh_dag, _ = clean_sel_iter.getComponent() points = OpenMaya.MFnMesh(mesh_dag).getPoints() for i in range(len(points)): if i not in self.table.keys() + self.table.values(): self.table[i] = i self.table.setdefault("center", set()).add(i)
[docs] @staticmethod def get_sides_from_rich(vertices): """Get resulting sides of a RichSelection in symmetry. Args: vertices (list): Vertices to select for symmetry. Returns: tuple: Two `OpenMaya.MSelectionList`, being the result of the MRichSelection.getSelection and MRichSelection.getSymmetry methods. """ cmds.select(vertices, symmetry=True) rich_selection = OpenMaya.MGlobal.getRichSelection() selection = rich_selection.getSelection() symmetry = rich_selection.getSymmetry() cmds.select(clear=True) return selection, symmetry