Home Website Youtube GitHub

MirrorControls doesn't work depends on ctrl name

There may already be someone who has asked a similar question, but sometimes MirrorControls does not work correctly.
It seems that if the guide Name contains an uppercase L, it causes problems.

After reading mirror_controls.py, it appears that when searching for the symmetrical controller, the script replaces L and R across the entire name string.

The diagram is simplified, but in the case of a controller named ‘sleeveLong’, which contains an uppercase L, the right-side controller corresponding to ‘sleeveLong_L0_fk0_ctl’ should be ‘sleeveLong_R0_fk0_ctl’.

However, due to the global replacement, the generated name becomes ‘sleeveRong_R0_fk0_ctl’, which results in the symmetrical controller not being found.

You didn’t ask a question. :grin:

But yes, this is what the code does, and has for many years. I quickly learned to avoid uppercase R and L in my guide names.

Alternatively, you could modify the code yourself to only replace the correct portion of the string.

Thank you and sorry not to ask a question
I was not sure I can ask for changing mirror_controls.py

I modified a part of mirror_controls.py like below
It is really brute force but it worked in my situation

def get_opposite_control(node):
    side_l = node.attr("L_custom_side_label").get()
    side_r = node.attr("R_custom_side_label").get()
    side = node.attr("side_label").get()

    target_name = None
    # ======================================================================
    guide_name = node.name().split('_')[0]
    tail_name = '_'.join(node.name().split('_')[1:])
    # ======================================================================
    if side == "L":
        # ==================================================================
        target_name = '_'.join([guide_name, tail_name.replace(side_l, side_r)])
        # ==================================================================
        # target_name = node.name().replace(side_l, side_r)
    elif side == "R":
        # ==================================================================
        target_name = '_'.join([guide_name, tail_name.replace(side_r, side_l)])
        # ==================================================================
        # target_name = node.name().replace(side_r, side_l)

    if target_name and pm.objExists(target_name):
        return pm.PyNode(target_name)
    return None

1 Like

I need to update this tool :sweat_smile:

3 Likes

yeah, I brought this up. heres a custom swap script I made that validates the name.

#rename a controller with validation
def valid_rename(control, shapes):
    for i, shape in enumerate(shapes):
        if i == 0:
            new_name = f"{control}Shape"
        else:
            new_name = f"{control}_{i-1}crvShape"

        if not cmds.objExists(new_name):
            cmds.rename(shape, new_name)
        else:
            cmds.warning(f"[valid_rename] {new_name} is in the scene. skipping")

#make sure the controller is correctly named                    
def validate_shape_names(control):
    shapes = cmds.listRelatives(control, s=True) or []
    valid   = []
    invalid = []

    for shape in shapes:
        single       = shape == f"{control}Shape"
        mgear_prefix = shape.startswith(f"{control}_") and shape.endswith("crvShape")
        if single or mgear_prefix:
            valid.append(shape)
        else:
            invalid.append(shape)

    for shape in invalid:
        cmds.warning(f"[validate_control] shape name invalid: {shape}. auto renaming")
    if not invalid:
        print(f"[valid_rename] {control} shapes are valid")

    valid_rename(control, invalid)

    return valid

#validate names and replace. deletes old shape. select new shape, then controller. 
def replace_shapes(new_transform, target_transform):
    rig_name = return_rig_name(target_transform)
    valid = validate_shape_names(target_transform)
    
    if valid:
        valid_name = valid[0]
        print(valid)
    else:
        cmds.warning("[replace_shapes] target control shapes invalid")
        return

    new_shape  = cmds.listRelatives(new_transform, c = True, s = True)
    old_shapes = cmds.listRelatives(target_transform, c = True, s = True)

    for shape in new_shape:
        cmds.parent(shape, target_transform, r = True, s = True)
        cmds.connectAttr(f"{rig_name}.ctl_x_ray", f"{shape}.alwaysDrawOnTop")
    for shape in old_shapes:
        cmds.delete(shape)

    renamed = cmds.rename(new_shape[0], valid_name)
    cmds.delete(new_transform)
1 Like