"""Module that defines a set of constants used throughout the project.
Include common values for rigging-related properties to ensure consistency.
"""
from enum import Enum, IntEnum
[docs]
class Tokens(str, Enum):
"""Defines the core components for the procedural naming system.
These tokens represent the fundamental parts of a name
assembled by the naming manager. The order and usage of these tokens
will define the final naming convention across the project.
"""
DESCRIPTOR = "descriptor"
SIDE = "side"
USAGE = "usage"
[docs]
class Side(str, Enum):
"""Common side tokens for naming conventions."""
LEFT = "l"
RIGHT = "r"
CENTER = "c"
MIDDLE = "m"
LEFT_LONG = "left"
RIGHT_LONG = "right"
CENTER_LONG = "center"
MIDDLE_LONG = "middle"
[docs]
def mirror(self) -> "Side":
"""Gets the mirrored side token.
Returns:
The mirrored side token (e.g., LEFT becomes RIGHT).
"""
mapping = {
Side.LEFT: Side.RIGHT,
Side.RIGHT: Side.LEFT,
Side.LEFT_LONG: Side.RIGHT_LONG,
Side.RIGHT_LONG: Side.LEFT_LONG,
}
# For center/middle or any other unmapped side, return itself.
return mapping.get(self, self)
[docs]
def is_left(self) -> bool:
"""Checks if the side is 'left'.
Returns:
True if the side is LEFT or LEFT_LONG, False otherwise.
"""
return self in (Side.LEFT, Side.LEFT_LONG)
[docs]
def is_right(self) -> bool:
"""Checks if the side is 'right'.
Returns:
True if the side is RIGHT or RIGHT_LONG, False otherwise.
"""
return self in (Side.RIGHT, Side.RIGHT_LONG)
[docs]
def is_center(self) -> bool:
"""Checks if the side is 'center'.
Returns:
True if the side is CENTER or CENTER_LONG, False otherwise.
"""
return self in (Side.CENTER, Side.CENTER_LONG)
[docs]
class Position(str, Enum):
"""Common position tokens for naming conventions."""
FRONT = "front"
BACK = "back"
UP = "up"
DOWN = "down"
MIDDLE = "middle"
INTERNAL = "internal"
EXTERNAL = "external"
[docs]
def mirror(self) -> "Position":
"""Gets the mirrored position token.
Returns:
The mirrored position token (e.g., FRONT becomes BACK).
"""
mapping = {
Position.FRONT: Position.BACK,
Position.BACK: Position.FRONT,
Position.UP: Position.DOWN,
Position.DOWN: Position.UP,
Position.INTERNAL: Position.EXTERNAL,
Position.EXTERNAL: Position.INTERNAL,
}
# For 'middle' or any other unmapped position, return itself.
return mapping.get(self, self)
[docs]
class Usage(str, Enum):
"""Defines core rigging usage tokens (Structure, Hierarchy, Mechanics)."""
# --- Core ---
CONTROL = "ctr"
OFFSET = "offset"
GROUP = "grp"
PIVOT = "pivot"
ROOT = "root"
SETTINGS = "settings"
SNAP = "snap"
TRIGGER = "trg"
AUTO = "auto"
ZERO = "zero"
SPLINE = "spl"
RIG_TYPE = "rigType"
# --- Organizers ---
COMPONENT = "cmp"
MODULE = "module"
INPUTS = "inputs"
OUTPUTS = "outputs"
CONTROLS = "controls"
JOINTS = "joints"
SKELETON = "skeleton"
LOGIC = "logic"
RIG = "rig"
LOCAL = "local"
LOCALS = "locals"
IN = "in"
OUT = "out"
END = "end"
# --- Geometry ---
GEOMETRY = "geo"
MESH = "mesh"
CURVE = "crv"
NURBS = "nurbs"
LATTICE = "lat"
LATTICE_BASE = "latBase"
VERTEX = "vtx"
CV = "cv"
# --- Joint ---
JOINT = "jnt"
GEO_SKIN_JOINT = "skn"
CURVE_SKIN_JOINT = "cskn"
NURBS_SKIN_JOINT = "nskn"
LATTICE_SKIN_JOINT = "lskn"
PREBIND = "prebind"
SKIN_SET = "skinSet"
# --- Mechanics ---
IK_HANDLE = "ikh"
EFFECTOR = "effector"
IK_SPLINE = "iks"
POLE_VECTOR = "pv"
DRIVER = "drv"
DRIVERS = "drivers"
GUIDE = "guide"
GUIDES = "guides"
REFERENCE = "ref"
LOCATOR = "loc"
ANCHOR = "anchor"
# --- Miscellaneous ---
ANIMATION_CURVE = "acrv"
SET_DRIVEN_KEY = "sdk"
TEST = "test"
TEMP = "temp"
RBF = "rbf"
ZIP = "zip"
PUSH = "push"
PULL = "pull"
MACRO = "macro"
MICRO = "micro"
[docs]
class UsageComponent(str, Enum):
"""Defines semantic component types."""
BODY = "boy"
FACE = "face"
EXTRA = "extra"
CUSTOM = "custom"
GENERIC = "generic"
HAIR = "hair"
CLOTH = "cloth"
UTILS = "utils"
PROP = "prop"
ELEMENT = "elem"
[docs]
class UsageConstraint(str, Enum):
"""Defines constraint types."""
PARENT_CONSTRAINT = "pacns"
POINT_CONSTRAINT = "pocns"
ORIENT_CONSTRAINT = "orcns"
SCALE_CONSTRAINT = "sccns"
AIM_CONSTRAINT = "aimcns"
POLE_VECTOR_CONSTRAINT = "pvcns"
MATRIX_CONSTRAINT = "matcns"
COMPONENT_MATCH = "componentMatch"
[docs]
class UsageUtility(str, Enum):
"""Defines utility node types (Math, Matrix, Vector, etc)."""
# --- Math & Logic ---
ADD = "add"
SUBTRACT = "sub"
PRODUCT = "prod"
MULTIPLY = "mult"
DIVIDE = "div"
POWER = "pow"
REVERSE = "rev"
CONDITION = "cond"
CLAMP = "clamp"
REMAP = "remap"
PLUS_MINUS_AVERAGE = "pma"
BLEND = "blend"
DISTANCE = "dist"
MAX = "max"
MIN = "min"
NORMALIZE = "norm"
ANGLE_BETWEEN = "ab"
SQUARE_ROOT = "sqrt"
ABSOLUTE = "abs"
# --- Matrix ---
COMPOSE_MATRIX = "cmat"
DECOMPOSE_MATRIX = "dmat"
MULTIPLY_MATRIX = "multmat"
INVERSE_MATRIX = "invmat"
BLEND_MATRIX = "blendmat"
PICK_MATRIX = "pickmat"
FOUR_BY_FOUR_MATRIX = "fbfmat"
AIM_MATRIX = "aimmat"
POINT_MATRIX_MULT = "pmatmult"
PROXIMITY_PIN = "pin"
UV_PIN = "uvPin"
# --- Vector & Quaternion ---
VECTOR_PRODUCT = "vecp"
EULER_TO_QUAT = "e2q"
QUAT_TO_EULER = "q2e"
QUAT_SLERP = "qslerp"
QUAT_INVERT = "qinv"
QUAT_PRODUCT = "qprod"
# --- Curve & Surface ---
CURVE_INFO = "cinfo"
POINT_ON_CURVE_INFO = "pocinfo"
NEAREST_POINT_ON_CURVE = "npoc"
SURFACE_INFO = "sinfo"
CLOSEST_POINT_ON_SURFACE = "cpos"
CLOSEST_POINT_ON_MESH = "cpom"
[docs]
class Extension(str, Enum):
"""Common file extensions for export and import operations."""
# Scene files
MAYA_ASCII = "ma"
MAYA_BINARY = "mb"
# Geometry/Cache files
OBJ = "obj"
FBX = "fbx"
ABC = "abc"
# Data files
JSON = "json"
YAML = "yaml"
XML = "xml"
# Script files
PYTHON = "py"
MEL = "mel"
# Image files
PNG = "png"
JPG = "jpg"
EXR = "exr"
TIFF = "tif"
[docs]
class AttributeType(str, Enum):
"""Common attribute types for Maya nodes."""
# --- Single Value Numeric Types ---
BOOL = "bool"
LONG = "long" # Default integer type in Maya's addAttr
DOUBLE = "double" # Default float type in Maya's addAttr
FLOAT = "float"
TIME = "time"
ANGLE = "angle"
# --- Compound Numeric Types ---
DOUBLE2 = "double2"
DOUBLE3 = "double3"
FLOAT2 = "float2"
FLOAT3 = "float3"
# --- Alias Types ---
VECTOR = "vector" # Alias for double3
COLOR = "color" # Alias for float3
# --- Other Data Types ---
ENUM = "enum"
STRING = "string"
MESSAGE = "message"
MATRIX = "matrix"
COMPOUND = "compound" # Generic compound for custom structures
[docs]
@classmethod
def get_numeric_types(cls) -> tuple["AttributeType", ...]:
"""Gets a tuple of all single-value numeric attribute types."""
return (cls.BOOL, cls.LONG, cls.DOUBLE, cls.FLOAT, cls.TIME, cls.ANGLE)
[docs]
@classmethod
def get_compound_numeric_types(cls) -> tuple["AttributeType", ...]:
"""Gets a tuple of all compound numeric attribute types, including aliases."""
return (
cls.DOUBLE2,
cls.DOUBLE3,
cls.FLOAT2,
cls.FLOAT3,
cls.VECTOR,
cls.COLOR,
)
[docs]
class RotateOrder(Enum):
"""Represents a Maya rotate order, holding both its string and index value.
This provides a single source of truth for rotate orders, preventing
discrepancies between string and integer representations.
"""
XYZ = ("xyz", 0)
YZX = ("yzx", 1)
ZXY = ("zxy", 2)
XZY = ("xzy", 3)
YXZ = ("yxz", 4)
ZYX = ("zyx", 5)
[docs]
def __init__(self, string_value: str, index_value: int):
"""Initializes the RotateOrder enum member.
Args:
string_value (str): The string representation of the rotate order.
index_value (int): The integer index of the rotate order.
"""
self._string_value = string_value
self._index_value = index_value
@property
def as_string(self) -> str:
"""The string representation of the rotate order (e.g., 'xyz')."""
return self._string_value
@property
def as_index(self) -> int:
"""The integer index of the rotate order (e.g., 0)."""
return self._index_value
[docs]
class VectorIndex(IntEnum):
"""Low-level integer indices for accessing components of vector attributes.
This enum is intentionally a simple `IntEnum` to be used directly for
indexing lists or tuples returned by Maya commands (e.g., `vector[VectorIndex.Y]`).
For a higher-level, more descriptive representation of axes, see the `Axis` class.
"""
X = 0
Y = 1
Z = 2
W = 3
[docs]
class ColorIndex(IntEnum):
"""Maya Index Colors for Drawing Overrides."""
GREY = 0
BLACK = 1
DARK_GREY = 2
LIGHT_GREY = 3
BURGUNDY = 4
NAVY_BLUE = 5
BLUE = 6
DARK_GREEN = 7
DARK_PURPLE = 8
MAGENTA = 9
BROWN = 10
DARK_BROWN = 11
DULL_RED = 12
RED = 13
GREEN = 14
BLUE_SKY = 15
WHITE = 16
YELLOW = 17
CYAN = 18
LIGHT_GREEN = 19
PINK = 20
PEACH = 21
LIGHT_YELLOW = 22
SEA_GREEN = 23
LIGHT_BROWN = 24
OLIVE = 25
LIME_GREEN = 26
TEAL_GREEN = 27
LIGHT_BLUE = 28
DARK_BLUE = 29
PURPLE = 30
DARK_MAGENTA = 31
[docs]
class ColorRGB(tuple[float, float, float], Enum):
"""Standard RGB Float Colors."""
WHITE = (1.0, 1.0, 1.0)
BLACK = (0.0, 0.0, 0.0)
RED = (1.0, 0.0, 0.0)
GREEN = (0.0, 1.0, 0.0)
BLUE = (0.0, 0.0, 1.0)
YELLOW = (1.0, 1.0, 0.0)
CYAN = (0.0, 1.0, 1.0)
MAGENTA = (1.0, 0.0, 1.0)
ORANGE = (1.0, 0.5, 0.0)
PURPLE = (0.5, 0.0, 0.5)
BROWN = (0.5, 0.25, 0.0)
PINK = (1.0, 0.75, 0.8)
GREY = (0.5, 0.5, 0.5)
DARK_GREY = (0.25, 0.25, 0.25)
LIGHT_GREY = (0.75, 0.75, 0.75)
LIGHT_RED = (1.0, 0.5, 0.5)
LIGHT_GREEN = (0.5, 1.0, 0.5)
LIGHT_BLUE = (0.5, 0.5, 1.0)
LIGHT_YELLOW = (1.0, 1.0, 0.5)
DARK_RED = (0.5, 0.0, 0.0)
DARK_GREEN = (0.0, 0.5, 0.0)
DARK_BLUE = (0.0, 0.0, 0.5)
DARK_YELLOW = (0.5, 0.5, 0.0)
DARK_CYAN = (0.0, 0.5, 0.5)
DARK_MAGENTA = (0.5, 0.0, 0.5)
DARK_ORANGE = (0.5, 0.25, 0.0)
DARK_PURPLE = (0.25, 0.0, 0.25)
DARK_BROWN = (0.25, 0.125, 0.0)
DARK_PINK = (0.5, 0.25, 0.5)
CHANNELS = ("R", "G", "B")
[docs]
class Axis(str, Enum):
"""Common axis tokens for naming conventions and vector operations."""
X = "X"
Y = "Y"
Z = "Z"
X_NEGATIVE = "-X"
Y_NEGATIVE = "-Y"
Z_NEGATIVE = "-Z"
@property
def is_negative(self) -> bool:
"""Checks if the axis is negative (e.g., '-X')."""
return self.value.startswith("-")
@property
def is_positive(self) -> bool:
"""Checks if the axis is positive (e.g., 'X')."""
return not self.is_negative
@property
def as_index(self) -> int:
"""Gets the corresponding integer index for axis (0 for X, 1 for Y, 2 for Z).
Note:
This method ignores the sign of the axis (e.g., both 'X' and '-X' return 0).
Returns:
The integer index (0, 1, or 2).
Raises:
ValueError: If the axis does not have a corresponding index.
"""
positive_self = Axis(self.value.strip("-"))
mapping = {
Axis.X: VectorIndex.X,
Axis.Y: VectorIndex.Y,
Axis.Z: VectorIndex.Z,
}
try:
return mapping[positive_self]
except KeyError:
raise ValueError(
f"Axis '{self.value}' does not have a corresponding vector index."
) from None
[docs]
def mirror(self) -> "Axis":
"""Gets the mirrored axis.
For example, 'X' becomes '-X' and '-Y' becomes 'Y'.
Returns:
The mirrored Axis member.
"""
if self.is_negative:
return Axis(self.value[1:])
return Axis(f"-{self.value}")
[docs]
@classmethod
def get_axis_orders(cls) -> dict[str, tuple["Axis", ...]]:
"""Gets a dictionary of all supported axis orders.
Returns:
A dictionary mapping rotate order strings to tuples of Axis members.
"""
return {
"xyz": (cls.X, cls.Y, cls.Z),
"yzx": (cls.Y, cls.Z, cls.X),
"zxy": (cls.Z, cls.X, cls.Y),
"xzy": (cls.X, cls.Z, cls.Y),
"yxz": (cls.Y, cls.X, cls.Z),
"zyx": (cls.Z, cls.Y, cls.X),
}
[docs]
class Vector(tuple[int, int, int], Enum):
"""Constants related to vector definitions. Inherits from tuple."""
X = (1, 0, 0)
Y = (0, 1, 0)
Z = (0, 0, 1)
[docs]
class Attribute(str, Enum):
"""Common attribute names for rigging elements."""
# --- General Node Attributes ---
MESSAGE = "message"
NODE_STATE = "nodeState"
# --- Transform Attributes ---
TRANSLATE = "translate"
ROTATE = "rotate"
SCALE = "scale"
SHEAR = "shear"
ROTATE_ORDER = "rotateOrder"
ROTATE_PIVOT = "rotatePivot"
SCALE_PIVOT = "scalePivot"
# --- Joint Specific Attributes ---
JOINT_ORIENT = "jointOrient"
ROTATE_AXIS = "rotateAxis"
# --- Matrix Attributes ---
MATRIX = "matrix"
WORLD_MATRIX = "worldMatrix"
WORLD_INVERSE_MATRIX = "worldInverseMatrix"
PARENT_MATRIX = "parentMatrix"
PARENT_INVERSE_MATRIX = "parentInverseMatrix"
OFFSET_PARENT_MATRIX = "offsetParentMatrix"
# --- Dag Node Attributes ---
VISIBILITY = "visibility"
INTERMEDIATE_OBJECT = "intermediateObject"
TEMPLATE = "template"
LOD_VISIBILITY = "lodVisibility"
# --- Drawing Overrides ---
OVERRIDE_ENABLED = "overrideEnabled"
OVERRIDE_DISPLAY_TYPE = "overrideDisplayType"
OVERRIDE_LOD = "overrideLevelOfDetail"
OVERRIDE_COLOR = "overrideColor"
OVERRIDE_COLOR_RGB = "overrideColorRGB"
[docs]
class Matrix:
"""Constants related to matrix definitions."""
IDENTITY: tuple[float, ...] = (
1.0,
0.0,
0.0,
0.0,
0.0,
1.0,
0.0,
0.0,
0.0,
0.0,
1.0,
0.0,
0.0,
0.0,
0.0,
1.0,
)
ZERO: tuple[float, ...] = (
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
)
[docs]
class ShapeType(str, Enum):
"""Common shape types for control curves."""
LOCATOR = "locator"
CIRCLE = "circle"
SQUARE = "square"
TRIANGLE = "triangle"
SPHERE = "sphere"
CUBE = "cube"
LINE = "line"
LOLLIPOP = "lollipop"
INFO = "info"
UMBRELLA = "umbrella"
DUMBBELL = "dumbbell"
ARROW = "arrow"
DOUBLEARROW = "doubleArrow"
TUBE = "tube"
FK = "fk"
IK = "ik"
OCTAGON = "octagon"
MAIN = "main"
ROOT = "root"