# -*- coding: utf-8 -*- """ :author: Jeremy Ernst :description: The base_component provides the base class, ART_Component, from which all components are derived. """ import inspect import os import re from abc import abstractmethod, ABCMeta import pymel.core as pm import artv2.utilities.error_utilities as errors import artv2.utilities.general_utilities as utils import artv2.utilities.component_utilities as component_utils from artv2.components.base_components.joint_mover import JointMover import artv2.tools.system.logger.output_logger as logger # noinspection PyPep8Naming class ART_Component(object): """ Base class for defining a component (arm, leg, torso, single joint, etc) in ARTv2. All components should inherit from this class. When creating a new component, here is a list of common attributes and functions you will want or need to override. +------------+------------------------+----------------------------------------------------------------------------+ | Type | Name | Description | +============+========================+============================================================================+ | attribute | nice_name | (string) The nice name of the component (as it will appear in UIs) | +------------+------------------------+----------------------------------------------------------------------------+ | attribute | category | (string) The name of the category where this component will show up in the | | | | user interface. For example: "Limbs", "Primitives", etc. | +------------+------------------------+----------------------------------------------------------------------------+ | attribute | base_name | The string base name of the component. This is a simple string used to | | | | identify the component type and append onto nodes created by the component.| | | | For example, on a leg component, the base_name is simply: "leg" | +------------+------------------------+----------------------------------------------------------------------------+ | attribute | has_sides | Bool for whether or not the component supports different sides,like a leg | | | | or arm do. | +------------+------------------------+----------------------------------------------------------------------------+ | attribute | can_overwrite_names | Bool for whether or not the user can overwrite the names of the joints of | | | | this component. | +------------+------------------------+----------------------------------------------------------------------------+ | attribute | joint_mover_file | The relative path from the ARTv2 directory to the joint mover file of the | | | | component. For example, self.joint_mover_file = | | | | "resources/rigging_guides/root.ma" | +------------+------------------------+----------------------------------------------------------------------------+ | attribute | mirror_table | A dictionary of translate and rotate attributes with their multiplier for | | | | mirroring values to mirrored controls. | +------------+------------------------+----------------------------------------------------------------------------+ | function | _add_metadata | This method adds attributes you want to keep track of that are unique to | | | | your component. Make sure that you still call on the base class's | | | | _add_metadata method to setup the required attributes. | +------------+------------------------+----------------------------------------------------------------------------+ """ __metaclass__ = ABCMeta nice_name = "" category = None base_name = None has_sides = False can_overwrite_names = True joint_mover_file = "replace_me_with_valid_path.ma" mirror_table = {"translateX": -1, "translateY": -1, "translateZ": -1, "rotateX": 1, "rotateY": 1, "rotateZ": 1} def __init__(self, prefix="", suffix="", network_node=None, side="Left"): self.logger = logger.OutputLogger(type(self).__name__) if network_node is not None: network_node = pm.PyNode(network_node) if prefix != "": self.logger.info("{0}.__init__: Validating prefix: {1}".format(str(type(self).__name__), prefix)) prefix = component_utils.validate_prefix(network_node, prefix) if suffix != "": self.logger.info("{0}.__init__: Validating suffix: {1}".format(str(type(self).__name__), suffix)) suffix = component_utils.validate_suffix(network_node, suffix) if prefix is not False and suffix is not False: if network_node is None: all_joints = component_utils.get_all_created_joints() self._unique_name = prefix + self.base_name + suffix self._create_component(prefix, suffix) self.joint_mover = JointMover(self.joint_mover_file, self.network_node) self.joint_mover.add_joint_mover() conflict = component_utils.detect_joint_conflicts(self.joint_mover.get_created_joints(), all_joints, self) if conflict: self.delete() return self.prefix = prefix self.suffix = suffix if side == "right": self.set_side("right") else: if pm.objExists(network_node): network_node = pm.PyNode(network_node) prefix = network_node.prefix.get() suffix = network_node.suffix.get() self._unique_name = prefix + self.base_name + suffix self.joint_mover = JointMover(self.joint_mover_file, network_node) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # def _create_component(self, prefix, suffix): """ Method for adding the component to the Maya scene. Calls on various functions to accomplish. """ character_node = component_utils.get_rig_asset_node() self.logger.info("{0}._create_component: Creating network node for: {1}".format(str(type(self).__name__), self._unique_name)) network_node = self._create_network_node() self.logger.info("{0}._create_component: Adding metadata to {1}.".format(str(type(self).__name__), network_node)) self._add_metadata(network_node, prefix, suffix) character_node.components.connect(network_node.asset) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # @abstractmethod def _add_metadata(self, network_node, prefix, suffix): """ Method for adding metadata attributes to the network node that are important for the component. The ones created by the base class are necessary for all components. :param network_node: component's network node name to add attributes to. :param str prefix: the component's (optional) prefix. :param str suffix: the component's (optional) suffix. :type network_node: PyNode """ network_node.unlock() network_node.addAttr("asset", at="message") network_node.addAttr("modulePath", dt="string", keyable=False) module_path = os.path.splitext(inspect.getfile(self.__class__).split("scripts")[1])[0] module_path = module_path.replace("\\", ".") module_path = module_path.partition(".")[2] network_node.modulePath.set(module_path, type="string", lock=True) network_node.addAttr("className", dt="string", keyable=False) network_node.className.set(self.__class__.__name__, type="string", lock=True) network_node.addAttr("componentName", dt="string", keyable=False) network_node.componentName.set(self._unique_name, type="string", lock=True) network_node.addAttr("baseName", dt="string", keyable=False) network_node.baseName.set(self.base_name, type="string", lock=True) network_node.addAttr("prefix", dt="string", keyable=False) network_node.prefix.set(prefix, type="string", lock=True) network_node.addAttr("suffix", dt="string", keyable=False) network_node.suffix.set(suffix, type="string", lock=True) network_node.addAttr("parentComponentBone", dt="string", keyable=False) network_node.addAttr("pinned", at="bool", keyable=False) network_node.pinned.set(False, lock=True) network_node.addAttr("isAiming", at="bool", keyable=False) network_node.isAiming.set(False, lock=True) network_node.addAttr("aimModeConstraints", at="message") network_node.addAttr("has_mirror", at="bool", keyable=False) network_node.has_mirror.set(False, lock=True) network_node.addAttr("mirror_component", at="message") network_node.addAttr("global_movers", at="message") network_node.addAttr("offset_movers", at="message") network_node.addAttr("mover_grp", at="message") network_node.addAttr("movers_visible", at="bool", dv=True, keyable=False) if self.has_sides: network_node.addAttr("side", min=0, at="enum", enumName="Left:Right", dv=0, keyable=False) network_node.side.set(lock=True) network_node.lock() # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # def _create_network_node(self): """ Creates the metanode that will store metadata for the component. :return: Returns the metanode. :rtype: PyNode """ existing_nodes = component_utils.return_all_components() for node in existing_nodes: if node.componentName.get() == self._unique_name: self.logger.error("{0}._create_network_node: A component with this name already " "exists: {1}".format(str(type(self).__name__), self._unique_name)) errors.raise_error("This component instance has already been created! Aborting.") return None network_node = pm.shadingNode("network", name=self._unique_name + "_metadata", asUtility=True) network_node.lock() return network_node # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # def copy_settings(self): """ Copies the values of the class properties (things like number of twist joints, etc) and writes them to a temp file. """ self.logger.info("{0}.copy_settings: Copying settings of {1}".format(str(type(self).__name__), self._unique_name)) properties = utils.get_class_properties(self) utils.create_clipboard("artv2Settings", properties) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # def paste_settings(self): """ Retrieves values from the temp file (if it exists) and sets any properties of the class matching those stored in the temp file, to those values. """ self.logger.info("{0}.paste_settings: Pasting settings onto {1}".format(str(type(self).__name__), self._unique_name)) clipboard = utils.retrieve_clipboard("artv2Settings") if clipboard is not None: data = utils.read_clipboard(clipboard) properties = utils.get_class_properties(self) pm.undoInfo(openChunk=True) for key in data: if key in properties: setattr(self, key, data.get(key)) pm.undoInfo(closeChunk=True) else: self.logger.warning("{0}.paste_settings: No clipboard exists.".format(str(type(self).__name__))) errors.raise_error("No clipboard exists!") # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # def reset_settings(self): """ Resets all values of the class properties to the default they had at creation. """ self.logger.info("{0}.reset_settings: Resetting settings for {1}".format(str(type(self).__name__), self._unique_name)) properties = utils.get_class_properties(self) pm.undoInfo(openChunk=True) for each in properties: default = pm.attributeQuery(each, node=self.network_node, ld=True) attr_type = pm.attributeQuery(each, node=self.network_node, at=True) if attr_type == "bool": if default[0] > 0: setattr(self, each, True) else: setattr(self, each, False) else: setattr(self, each, default[0]) pm.undoInfo(closeChunk=True) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # def change_num_joints(self, joints, offset_movers, state): """ Sets the build state (whether or not they will be created) of the given joints to the given state. :param joints: List of PyNode joints to toggle build state on :param offset_movers: List of PyNode offset movers to toggle visibility on. :param state: The build state to set. """ self.logger.info("{0}.change_num_joints: Setting {1} to build={2}".format(self.__class__.__name__, joints, state)) info = [] for i, _ in enumerate(joints): dependencies = self.joint_mover.toggle_build_joint(joints[i], offset_movers[i], state) if dependencies is not None: info.append(dependencies) if info: errors.report_issues(info) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # def set_twist_joints(self, attr, new_number, attr_min, attr_max, keyword): """ Base logic for setting number of twist joints via properties in components like arm, leg. :param attr: the property name :param new_number: The new value to set the property to :param attr_min: The minimum value of allowable values :param attr_max: The maximum value of allowable values :param keyword: The keyword to search mover nodes for """ self.logger.info("{0}.{1}: Setting property to {2}".format(self.__class__.__name__, attr, new_number)) if isinstance(new_number, (int, float)): if float(new_number).is_integer(): new_number = int(new_number) if utils.validate_integer(new_number, attr_min, attr_max): utils.set_attribute(self.network_node, attr, new_number) mover_data = component_utils.find_mover_data_from_name(self, keyword) self.change_num_joints(mover_data[2], mover_data[1], False) for i in range(attr_min, new_number): mover_data = component_utils.find_mover_data_from_name(self, keyword + str(i + 1)) if mover_data[2] and mover_data[1]: self.change_num_joints(mover_data[2], mover_data[1], True) else: self.logger.warning("{0}.{1}: Invalid number for property: {2}. " "Integer must be between {3} and {4}" "".format(self.__class__.__name__, attr, attr, attr_min, attr_max)) errors.raise_error("Invalid number for property: {0}. Integer must be between {1} and {2}" "".format(attr, attr_min, attr_max)) else: self.logger.warning("{0}.{1}: Whole numbers only.".format(self.__class__.__name__, attr)) errors.raise_error("{0}.{1}: Whole numbers only.".format(self.__class__.__name__, attr)) else: self.logger.warning("{0}.{1}: invalid type for value. Int or floats only." "".format(self.__class__.__name__, attr)) errors.raise_error("{0}.{1}: Whole ints or floats numbers only.".format(self.__class__.__name__, attr)) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # def delete(self): """ Deletes the component from the scene. example usage: .. code-block:: python asset = rig_asset.ART_RigAsset() leg = leg.BipedLeg(prefix="l") leg.delete() """ self.logger.info("{0}.delete: Deleting {1}".format(str(type(self).__name__), self._unique_name)) # unparent the component current_state = self.joint_mover.metanode.pinned.get() if not current_state: self.joint_mover.toggle_pin_component() joints = [] movers = self.joint_mover.get_movers() for each in movers.get("offset"): if each.hasAttr("connected_joint"): joints.append(each.connected_joint.connections()[0]) details = "" for joint in self.joint_mover.get_created_joints(): children = component_utils.check_for_children(joint) details += (component_utils.fix_dependencies(children)) if self.network_node.has_mirror.get() is True: mirror = self.network_node.mirror_component.connections()[0] mirror_inst = component_utils.get_component_instance(mirror) utils.set_attribute(mirror_inst.network_node, "has_mirror", False) mirror_inst.network_node.mirror_component.disconnect() self.joint_mover.remove_movers() for joint in joints: if pm.objExists(joint): utils.delete_node(joint) utils.delete_node(self.network_node) if details: self.logger.info("{0}.change_num_joints: Some components have had their parent set to the root joint " "due to dependencies".format(str(type(self).__name__))) errors.report_issues(details) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # def duplicate(self): """ Creates a duplicate instance of self, with the same settings, parent, and transforms. A unique trailing suffix is appended to prevent naming issues. example usage: .. code-block:: python asset = rig_asset.ART_RigAsset() leg = leg.BipedLeg(prefix="l") leg.duplicate() """ pm.undoInfo(openChunk=True) self.logger.info("{0}.duplicate: Creating duplicate of {1}".format(str(type(self).__name__), self._unique_name)) # create a new instance of the class with an appended suffix existing_components = component_utils.get_all_component_names() new_suffix = utils.generate_unique_suffix(existing_components, self) self.logger.debug("{0}.duplicate: Creating duplicate".format(self.__class__.__name__)) duplicate_inst = component_utils.create_duplicate_instance(self.network_node, self.prefix, new_suffix) # copy/paste settings if self.network_node.hasAttr("side"): if self.network_node.side.get() != duplicate_inst.network_node.side.get(): self.logger.debug("{0}.duplicate: Setting side".format(self.__class__.__name__)) duplicate_inst.set_side(self.network_node.side.get()) self.logger.debug("{0}.duplicate: Copying settings".format(self.__class__.__name__)) self.copy_settings() self.logger.debug("{0}.duplicate: Pasting settings".format(self.__class__.__name__)) duplicate_inst.paste_settings() # copy/paste transforms self.logger.debug("{0}.duplicate: Copying transforms".format(self.__class__.__name__)) self.joint_mover.copy_transforms() self.logger.debug("{0}.duplicate: Pasting transforms".format(self.__class__.__name__)) duplicate_inst.joint_mover.paste_transforms() # set parent if self.parent is not None: duplicate_inst.parent = self.parent # select the top mover pynode = pm.PyNode(duplicate_inst.network_node.mover_grp.connections()[0]) mover = component_utils.get_top_level_mover(pynode) pm.select(mover.fullPath()) pm.setToolTo("moveSuperContext") pm.undoInfo(closeChunk=True) return duplicate_inst # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # def create_mirror(self, prefix, suffix, parent): """ Creates a mirror of the component, then mirrors its transformations. :param str prefix: prefix to give the created mirrored component. :param str suffix: suffix to give the created mirrored component. :param str parent: parent joint for the created mirrored component. :return: Instance of the mirrored component example usage: .. code-block:: python asset = rig_asset.ART_RigAsset() leg = leg.BipedLeg(prefix="l") mirror = leg.create_mirror(prefix="r", suffix="", parent="root") """ self.logger.info("{0}.create_mirror: Creating a mirror of {1}".format(str(type(self).__name__), self._unique_name)) pm.undoInfo(openChunk=True) # create the mirrored instance mirrored_inst = component_utils.create_duplicate_instance(self.network_node, prefix, suffix) # copy/paste settings self.copy_settings() mirrored_inst.paste_settings() # change the side to the proper side if self.network_node.hasAttr("side"): side = self.network_node.side.get() if side == 0: mirrored_inst.set_side("right") else: mirrored_inst.set_side("left") # mirror transformations self.joint_mover.copy_transforms() mirrored_inst.joint_mover.paste_transforms(mirror=True) # fix top group component_utils.mirror_top_mover_grp(self, mirrored_inst) # set parent if parent is not None: mirrored_inst.parent = parent # set mirror component attr utils.set_attribute(self.network_node, "has_mirror", True) utils.set_attribute(mirrored_inst.network_node, "has_mirror", True) self.network_node.mirror_component.connect(mirrored_inst.network_node.mirror_component) pm.undoInfo(closeChunk=True) return mirrored_inst # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # def set_mirror(self, mirror_metanode): """ Sets the mirror component if the passed in mirror component is of the same class type. :param mirror_metanode: The network node for the mirror component to be set. """ self.logger.info("{0}.set_mirror: Setting the mirror component to {1}".format(str(type(self).__name__), mirror_metanode)) if mirror_metanode is not None: if mirror_metanode != self.network_node: mirror_inst = component_utils.get_component_instance(mirror_metanode) mirror_class = mirror_inst.network_node.className.get() class_name = self.network_node.className.get() if mirror_class == class_name: if self.network_node.has_mirror.get() is False: if mirror_inst.network_node.has_mirror.get() is False: utils.set_attribute(self.network_node, "has_mirror", True) utils.set_attribute(mirror_inst.network_node, "has_mirror", True) self.network_node.mirror_component.connect(mirror_inst.network_node.mirror_component) else: self.logger.warning("{0}.set_mirror: The chosen mirror component's class is not of the same" "type.".format(str(type(self).__name__))) errors.raise_error("The chosen mirror component's class is not of the same type.") else: self.logger.warning("{0}.set_mirror: Cannot set self as a mirror.".format(str(type(self).__name__))) errors.raise_error("Cannot set self as a mirror.") else: utils.set_attribute(self.network_node, "has_mirror", False) current_mirror = self.network_node.mirror_component.connections() if current_mirror: mirror_inst = component_utils.get_component_instance(current_mirror[0]) utils.set_attribute(mirror_inst.network_node, "has_mirror", False) del mirror_inst self.network_node.mirror_component.disconnect() # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # def rename_joint(self, new_name, old_name, offset_mover): """ Renames a joint that the component creates. The component's prefix and suffix will be maintained. For example, if the component has a prefix of "_l", and it will create a joint named foot, the user can rename foot to ankle, and the new name will be l_ankle. :param str new_name: The new name of the joint :param str old_name: The old name of the joint :param offset_mover: The offset mover (PyNode) for the joint example usage: .. code-block:: python asset = rig_asset.ART_RigAsset() leg = leg.BipedLeg(prefix="l") data = component_utils.get_joint_labels(leg.network_node) leg.rename_joint("ankle", "foot", data.get("foot")[2]) """ self.logger.info("{0}.rename_joint: Renaming {1} to {2}.".format(str(type(self).__name__), old_name, new_name)) if re.search(r'^\d', new_name): self.logger.warning("{0}.rename_joint: Name cannot start with digits.".format(str(type(self).__name__))) errors.raise_error("Name cannot start with digits!") return False if re.search(r'\W', new_name): self.logger.warning("{0}.rename_joint: " "Name has non-alphanumeric characters.".format(str(type(self).__name__))) errors.raise_error("Name has non-alphanumeric characters!") return False # check for created joints with new name all_joints = component_utils.get_all_created_joints() if new_name in all_joints: self.logger.warning("{0}.rename_joint: A joint already exists with that " "name.".format(str(type(self).__name__))) errors.raise_error("A joint already exists with that name!") else: # if there are no conflicts, rename the joint utils.set_attribute(offset_mover, "created_joint", new_name, "string") children = component_utils.check_for_children(old_name) if children: for each in children: inst = component_utils.get_component_instance(each) inst.parent = new_name return True # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # def set_side(self, new_side): """ Sets the side of a component if that components supports setting sides. Setting a side from left to right, for example, will change the local rotation axis to be mirrored on the joints. :param new_side: This can be any of the following: "left", "Left", "right", "Right", 0, 1 """ # valid options for new side are: 0 or left or Left, and 1 or right or Right. sides = {"left": 0, "Left": 0, "right": 1, "Right": 1, 0: 0, 1: 1} if new_side in sides: side_value = sides.get(new_side) elif new_side in [0, 1]: side_value = new_side else: self.logger.warning("{0}.set_side: Invalid side given. Valid arguments are {1}".format( self.__class__.__name__, sides.keys())) errors.raise_error("Invalid side given! Valid arguments are left, Left, 0, right, Right, 1") if self.network_node is not None: current_side = self.network_node.side.get() if current_side != sides.get(new_side): self.logger.info("{0}.set_side: Setting side on {1} to {2}.".format(self.__class__.__name__, self._unique_name, new_side)) utils.set_attribute(self.network_node, "side", side_value) # get aim mode aim_mode = self.network_node.isAiming.get() if aim_mode: self.joint_mover.aim_helper.toggle_aim_mode() # rebuild mover joints = utils.convert_to_pynodes(self.joint_mover.get_driven_joints()) self.joint_mover.remove_movers() for joint in joints: joint.translateX.set(joint.initial_translateX.get()) joint.translateY.set(joint.initial_translateY.get()) joint.translateZ.set(joint.initial_translateZ.get()) joint.rotateX.set(joint.initial_rotateX.get()) joint.rotateY.set(joint.initial_rotateY.get()) joint.rotateZ.set(joint.initial_rotateZ.get()) root_joint = utils.get_root_joint_of_component(joints) mirrored_joints = utils.convert_to_pynodes( pm.mirrorJoint(root_joint, mirrorYZ=True, mirrorBehavior=True)) for joint in joints: joint.unlock() pm.delete(root_joint) for joint in mirrored_joints: original_name = joint.attr("otherType").get() utils.rename_object(joint, original_name) invert = joint.invertAimAxis.get() joint.invertAimAxis.set(not invert) mirrored_root_joint = utils.get_root_joint_of_component(mirrored_joints) self.joint_mover.create_joint_mover_controls(mirrored_root_joint) joints = utils.convert_to_pynodes(self.joint_mover.get_driven_joints()) for joint in joints: joint.initial_translateX.set(joint.translateX.get()) joint.initial_translateY.set(joint.translateY.get()) joint.initial_translateZ.set(joint.translateZ.get()) joint.initial_rotateX.set(joint.rotateX.get()) joint.initial_rotateY.set(joint.rotateY.get()) joint.initial_rotateZ.set(joint.rotateZ.get()) if aim_mode: self.joint_mover.aim_helper.toggle_aim_mode() else: self.logger.error("{0}.set_side: No network node found.".format(self.__class__.__name__)) errors.raise_error("ART_Leg_Biped.side.setter: No network node found.") # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # @property def parent(self): """ This property holds the joint name of the parent of this component. """ return self.network_node.parentComponentBone.get() @parent.setter def parent(self, new_parent): self.logger.info("{0}.parent: Setting parent to {1}".format(str(type(self).__name__), new_parent)) all_joints = component_utils.get_all_created_joints() created_joints = self.joint_mover.get_created_joints() if new_parent in all_joints: if new_parent not in created_joints: utils.set_attribute(self.network_node, "parentComponentBone", new_parent, "string") self.joint_mover.set_mover_parent(new_parent) else: self.logger.warning("{0}.parent: Cannot set the parent to a joint this component creates." "".format(str(type(self).__name__))) errors.raise_error("Cannot set the parent to a joint this component creates!") else: self.logger.warning("{0}.parent: Invalid parent: {1}. No component creates such a joint." "".format(str(type(self).__name__), new_parent)) errors.raise_error("Invalid parent: {0}! No component creates such a joint.".format(new_parent)) return # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # @property def prefix(self): """ This property holds the component's prefix (optional) :return: Returns the component's prefix. :rtype: str """ return self.network_node.prefix.get() @prefix.setter def prefix(self, new_prefix): current_prefix = self.network_node.prefix.get() if new_prefix != current_prefix: self.logger.info("{0}.prefix: Setting prefix to {1}".format(str(type(self).__name__), new_prefix)) valid_prefix = component_utils.validate_prefix(self.network_node, new_prefix) if valid_prefix is not False: old_prefix = self.prefix suffix = self.suffix utils.set_attribute(self.network_node, "prefix", valid_prefix, "string") utils.set_attribute(self.network_node, "componentName", valid_prefix + self.base_name + suffix, "string") self._unique_name = valid_prefix + self.base_name + suffix pynode = pm.PyNode(self.network_node) utils.rename_object(pynode, self._unique_name + "_metadata") containers = self.joint_mover.get_containers() utils.rename_object(containers[0], self._unique_name + "_component") utils.rename_object(containers[1], self._unique_name + "_mover_guides") utils.rename_object(containers[2], self._unique_name + "_mover_utilities") self.joint_mover.metanode = pm.PyNode(self._unique_name + "_metadata") self.joint_mover.rename_movers(old_prefix, self.suffix) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # @property def suffix(self): """ This property holds the component's suffix (optional) :return: Returns the component's suffix. :rtype: str """ return self.network_node.suffix.get() @suffix.setter def suffix(self, new_suffix): current_suffix = self.network_node.suffix.get() if new_suffix != current_suffix: self.logger.info("{0}.suffix: Setting suffix to {1}".format(str(type(self).__name__), new_suffix)) valid_suffix = component_utils.validate_suffix(self.network_node, new_suffix) if valid_suffix is not False: old_suffix = self.suffix prefix = self.prefix utils.set_attribute(self.network_node, "suffix", valid_suffix, "string") utils.set_attribute(self.network_node, "componentName", prefix + self.base_name + valid_suffix, "string") self._unique_name = prefix + self.base_name + valid_suffix pynode = pm.PyNode(self.network_node) utils.rename_object(pynode, self._unique_name + "_metadata") containers = self.joint_mover.get_containers() utils.rename_object(containers[0], self._unique_name + "_component") utils.rename_object(containers[1], self._unique_name + "_mover_guides") utils.rename_object(containers[2], self._unique_name + "_mover_utilities") self.joint_mover.metanode = pm.PyNode(self._unique_name + "_metadata") self.joint_mover.rename_movers(self.prefix, old_suffix) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # @property def network_node(self): """ This property holds the network node that contains this component's metadata. """ return_node = None components = component_utils.return_all_components() for component in components: if component.componentName.get() == self._unique_name: return_node = component break if return_node is None: errors.raise_error("ART_Component.network_node: No network node found.") return pm.PyNode(return_node)