# -*- coding: utf-8 -*-
"""
:author:
Jeremy Ernst
:description:
This module contains the chain component class for constructing a chain with 3 to 99 joints.
"""
import artv2.components.base_components.base_component as base
import artv2.utilities.rigging_utilities as rigging_utils
import artv2.utilities.general_utilities as utils
import artv2.utilities.component_utilities as component_utils
import artv2.utilities.error_utilities as errors
import artv2.utilities.joint_mover_utilities as jm_utils
import pymel.core as pm
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[docs]class Chain(base.ART_Component):
"""
This class defines and creates the chain component, for creating a chain with 3 to 99 joints.
"""
nice_name = "Chain"
category = "Primitives"
base_name = "chain"
has_sides = True
can_overwrite_names = True
joint_mover_file = "resources\\rigging_guides\\chain.ma"
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
def _add_metadata(self, network_node, prefix, suffix):
super(Chain, self)._add_metadata(network_node, prefix, suffix)
network_node.unlock()
network_node.addAttr("num_joints", min=3, max=99, dv=3, keyable=False)
network_node.num_joints.set(lock=True)
network_node.lock()
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
def _change_number_of_joints(self, name, aim_mode, new_number):
if aim_mode:
self.joint_mover.aim_helper.toggle_aim_mode()
# check for dependencies and remove movers
dependencies = []
for each in self.joint_mover.get_created_joints():
issues = component_utils.check_for_children(each)
if issues:
dependencies.append(component_utils.fix_dependencies(issues))
loc = pm.spaceLocator()
pm.delete(pm.parentConstraint(self._unique_name + "_" + name + "_mover", loc))
pm.delete(pm.scaleConstraint(self._unique_name + "_" + name + "_mover", loc))
self.joint_mover.copy_transforms()
self.joint_mover.remove_movers()
# reset guide joints
pm.select(name, hi=True)
guide_joints = pm.ls(sl=True)
for each in guide_joints:
each.unlock()
for joint in guide_joints:
rigging_utils.reset_joint_to_initial(joint)
# remove down to 2, then add back up to the new number so divisions are clean
end_joint = pm.PyNode(name).listRelatives(allDescendents=True)[0]
self.logger.debug("end joint: {0}".format(end_joint))
self.logger.debug("removing {0} joints".format(self.num_joints - 2))
rigging_utils.remove_joints(int(self.num_joints - 2), end_joint)
new_end_joint = pm.PyNode(name).listRelatives(allDescendents=True)[0]
self.logger.debug("new end joint: {0}".format(new_end_joint))
self.logger.debug("dividing {0} into {1}".format(new_end_joint, new_number - 2))
rigging_utils.divide_joint(new_number - 2, new_end_joint, self.base_name)
return [loc, dependencies]
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
def _redo_markups(self, name, base_joint, root_joint):
"""
Takes a set of joints and removes the markup data on them, then re-adds the markup data and sets it. The joint
mover controls will then be rebuilt with that new markup data.
"""
# remove markups
joint_mover_utils = jm_utils.JointMoverUtils()
pm.select(base_joint, hi=True)
guide_joints = pm.ls(sl=True)
joint_mover_utils.remove_markups()
del joint_mover_utils
# add markups
jm_utils.markup_joints(guide_joints, root_joint)
# set markup values
num_spine_joints = len(guide_joints)
for i in range(num_spine_joints):
jnt = guide_joints[i]
if i + 1 < num_spine_joints:
jnt.canAim.set(True)
self.logger.debug("Setting {0} to aim at {1}_{2}".format(jnt, name, utils.create_trailing_number(i+2)))
jnt.aimJoint.set("{0}_{1}".format(name, utils.create_trailing_number(i + 2)))
jnt.controlType.set("circle")
# rebuild movers
for each in guide_joints:
each.lock()
self.joint_mover.create_joint_mover_controls(root_joint)
self.joint_mover.toggle_global_visibility(False)
self.joint_mover.toggle_offset_visibility(False)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
def _toggle_data_cleanup(self, name, aim_mode, loc):
for each in self.joint_mover.get_driven_joints():
offset = each.message.connections(type="transform")[0]
self.joint_mover.toggle_build_joint(each, offset, True)
if aim_mode:
self.joint_mover.aim_helper.toggle_aim_mode()
pm.delete(pm.parentConstraint(loc, self._unique_name + "_" + name + "_mover"))
pm.delete(pm.scaleConstraint(loc, self._unique_name + "_" + name + "_mover"))
pm.delete(loc)
self.joint_mover.select_top_mover()
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
@property
def num_joints(self):
"""
Property for defining/returning the number of chain joints
example usage:
.. code-block:: python
chain_inst = chain.Chain()
chain_inst.num_joints = 10
:return: (int) number of chain joints.
"""
return self.network_node.num_joints.get()
@num_joints.setter
def num_joints(self, new_number):
if utils.validate_integer(new_number, 3, 99):
if new_number != self.num_joints:
new_number = int(new_number)
self.logger.info("{0}.num_joints: Setting property to {1}".format(self.__class__.__name__, new_number))
# change number of joints
aim_mode = self.network_node.isAiming.get()
loc, dependencies = self._change_number_of_joints("chain_01", aim_mode, new_number)
# redo markups
self._redo_markups("chain", "chain_01", "chain_01")
utils.set_attribute(self.network_node, "num_joints", new_number)
# cleanup
self._toggle_data_cleanup("chain_01", aim_mode, loc)
# report
if dependencies:
errors.report_issues(dependencies)
else:
self.logger.warning("{0}.num_joints: {1} is not a valid value.".format(self.__class__.__name__,
new_number))
errors.raise_error("{0} is not a valid value!".format(new_number))