Implementing Swizzling operation to vectors.
Goals
Implement basic per-component swizzling style access and assignment to minimal vector, maya MVector and pymel's Vector (Extended MVector) type.
What's Swizzling?
Accessing/reordering vector component in arbitrary order by chaining attribute accessors.
The following demonstration code should be self-explanatory, run it in maya to see it in action!
Demo code and working sample
# -*- coding: utf-8 -*-
from __future__ import print_function, absolute_import, division
import inspect
import abc
import pymel.core as pm
from maya.OpenMaya import MVector
class DemoVector(object):
def __init__(self, initial_vec):
self.x, self.y, self.z = initial_vec
def __repr__(self):
return "{}".format((id(self), self.x, self.y, self.z))
class SingleCharComponentSwizzlingMixin(object):
# REGISTERED_COMPONENT_CHAR = []
def _component_read(self, access_name):
# return self.__dict__[access_name]
return getattr(self, access_name)
def _component_write(self, access_name, value):
# self.__dict__[access_name] = value
setattr(self, access_name, value)
def __getattr__(self, access_name):
if len(access_name) == 1:
self._component_read(access_name)
else:
retval = tuple(
self._component_read(char) for char in access_name
)
return retval
def __setattr__(self, access_name, value):
try:
if len(access_name) == 1:
self._component_write(access_name, value)
else:
if not len(value) == len(value):
raise AttributeError
for i, char in enumerate(access_name):
self._component_write(char, value)
except:
super(SingleCharComponentSwizzlingMixin, self).__setattr__(access_name, value)
class MinimalSwizzlingVector(SingleCharComponentSwizzlingMixin, DemoVector):
pass
class SwizzlingMVector(SingleCharComponentSwizzlingMixin, MVector):
pass
PYMEL_VECTOR_ALLOWED_COMPONENTS = ['x', 'y', 'z']
class PMVUseDefaultOps(Exception):
pass
class SwizzlingPmVector(pm.dt.Vector):
def __getattribute__(self, access_name):
try:
# is iterable and all key allowed
if all(len(access_name) > 1 and key in
PYMEL_VECTOR_ALLOWED_COMPONENTS for key in access_name
):
retval = tuple(
getattr(self, char) for char in access_name
)
return retval
else:
raise PMVUseDefaultOps
except PMVUseDefaultOps, TypeError:
return super(SwizzlingPmVector, self).__getattribute__(access_name)
def __setattr__(self, access_name, value):
try:
if not hasattr(value, "__len__"):
raise PMVUseDefaultOps
if all(len(access_name) > 1 and value > 1 and len(access_name) == len(
value) and key in PYMEL_VECTOR_ALLOWED_COMPONENTS for key in access_name
):
for i, char in enumerate(access_name):
setattr(self, char, value[i])
else:
raise PMVUseDefaultOps
except PMVUseDefaultOps, TypeError:
super(SwizzlingPmVector, self).__setattr__(access_name, value)
class A(object):
b = 1234
def __getattr__(self, item):
print("a get attr")
modified_item = item[0]
return getattr(type(self), modified_item)
class B(object):
def __getattr__(self, item):
print("b get attr")
modified_item = item[0]
return getattr(type(self), modified_item)
class C(B, A):
pass
def demo_mixin():
t = C()
print(t.bbb)
# 1234
def demo_minimal():
mv_a = MinimalSwizzlingVector([1, 2, 3])
mv_b = MinimalSwizzlingVector([0, 0, 0])
print(mv_a)
# (2507560141824L, 1, 2, 3)
print(mv_b)
# (2507560142608L, 0, 0, 0)
# swizzling read
print(mv_a.xxx, mv_a.yyy, mv_a.zzz, mv_a.zyx, mv_a.xyz)
# (1, 1, 1) (2, 2, 2) (3, 3, 3) (3, 2, 1) (1, 2, 3)
# swizzling read & assign
mv_b.xyz = mv_a.zzz
print(mv_b)
# (2507560142608L, (3, 3, 3), (3, 3, 3), (3, 3, 3))
def demo_mvector():
mayav_a = SwizzlingMVector(1, 2, 3)
mayav_b = SwizzlingMVector(0, 0, 0)
print(mayav_a)
# <__main__.SwizzlingMVector; proxy of <Swig Object of type 'MVector *' at 0x00000247D63C7A80> >
print(mayav_b)
# <__main__.SwizzlingMVector; proxy of <Swig Object of type 'MVector *' at 0x00000247D63C79C0> >
# swizzling reads
print(mayav_a.zyx)
# (3.0, 2.0, 1.0)
print(mayav_a.yzx)
# (2.0, 3.0, 1.0)
print(mayav_a.xxx)
# (1.0, 1.0, 1.0)
print(mayav_a.xyzyyzyx)
# (1.0, 2.0, 3.0, 2.0, 2.0, 3.0, 2.0, 1.0)
# swizzling read & assign
mayav_b.xyz = mayav_a.zyx
print(mayav_b.xyz)
# (3.0, 2.0, 1.0)
def demo_pymel_vector():
pmv_a = SwizzlingPmVector([1, 2, 3])
pmv_b = SwizzlingPmVector([0, 0, 0])
print(pmv_a.x)
# 1.0
print(pmv_a)
# [1.0, 2.0, 3.0]
print(pmv_b)
# [0.0, 0.0, 0.0]
# swizzling read
print(pmv_a.zyx)
# (3.0, 2.0, 1.0)
# swizzling read & assign
pmv_b.xyz = pmv_a.zyx
print(pmv_b)
# [3.0, 2.0, 1.0]
def main():
print("Mixin demo:")
demo_mixin()
print("Minimal:")
demo_minimal()
print("MVector:")
demo_mvector()
print("Pymel Vector:")
demo_pymel_vector()
main()