# -*- 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 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 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))