import maya.cmds as cmds

 

def update_target_list():

    target = cmds.ls(selection=True, type="transform")

    cmds.textScrollList("targetList", edit=True, removeAll=True)

    if len(target) == 1:

        cmds.textScrollList("targetList", edit=True, append=target)

    elif len(target) > 1:

        cmds.confirmDialog(title="エラー", message="ターゲットオブジェクトは1つだけ選択してください。", button=["OK"])

        cmds.textScrollList("targetList", edit=True, append=["ターゲットオブジェクトは選択されていません"])

    else:

        cmds.textScrollList("targetList", edit=True, append=["ターゲットオブジェクトは選択されていません"])

 

def update_selection_list():

    selection = cmds.ls(selection=True, type="transform")

    cmds.textScrollList("selectionList", edit=True, removeAll=True)

    if selection:

        cmds.textScrollList("selectionList", edit=True, append=selection)

    else:

        cmds.textScrollList("selectionList", edit=True, append=["選択中のオブジェクトはありません"])

 

def get_text_scroll_list_items(list_name):

    items = cmds.textScrollList(list_name, query=True, allItems=True)

    return items if items else []

 

def delete_set_and_contents(set_name):

    if cmds.objExists(set_name):

        contents = cmds.sets(set_name, query=True) or []

        if contents:

            cmds.delete(contents)

        cmds.delete(set_name)

        print(f"セット {set_name} とその内容を削除しました。")

 

def get_all_child_joints(joint):

    children = cmds.listRelatives(joint, ad=True, type='joint') or []

    ordered = cmds.ls(children, long=True)

    return [joint] + ordered[::-1]

 

def get_controlled_joints_in_order(ctrl_list):

    result = []

    seen = set()

    for ctrl in ctrl_list:

        joints = set()

        for attr in ['rotateX', 'rotateY', 'rotateZ']:

            conns = cmds.listConnections(f"{ctrl}.{attr}", s=False, d=True) or []

            for c in conns:

                if cmds.nodeType(c) == 'joint':

                    joints.add(c)

        for pnt in cmds.listConnections(ctrl, type='pointConstraint') or []:

            targets = cmds.listConnections(pnt + ".constraintTranslateX", s=False, d=True) or []

            for t in targets:

                if cmds.nodeType(t) == 'joint':

                    joints.add(t)

        for pcn in cmds.listConnections(ctrl, type='parentConstraint') or []:

            targets = cmds.listConnections(pcn + ".constraintRotateX", s=False, d=True) or []

            for t in targets:

                if cmds.nodeType(t) == 'joint':

                    joints.add(t)

        for jnt in joints:

            all_joints = get_all_child_joints(jnt)

            for aj in all_joints:

                if aj not in seen:

                    result.append(aj)

                    seen.add(aj)

    return result

 

def duplicate_joint_chain(joint_list, prefix="IK_"):

    duplicated = {}

    for jnt in joint_list:

        short = jnt.split('|')[-1]

        dup = cmds.duplicate(jnt, name=prefix + short, parentOnly=True)[0]

        duplicated[short] = dup

 

    for jnt in joint_list:

        short = jnt.split('|')[-1]

        parent = cmds.listRelatives(jnt, parent=True, type="joint")

        if parent:

            p_short = parent[0].split('|')[-1]

            if p_short in duplicated:

                current_parent = cmds.listRelatives(duplicated[short], parent=True)

                if not current_parent or current_parent[0] != duplicated[p_short]:

                    try:

                        cmds.parent(duplicated[short], duplicated[p_short])

                    except RuntimeError as e:

                        cmds.warning(f"{duplicated[short]} の親設定に失敗: {str(e)}")

 

    return list(duplicated.values()), duplicated

 

def create_segmented_ik_chain_with_constraints():

    target_items = get_text_scroll_list_items("targetList")

    if not target_items or "選択されていません" in target_items[0]:

        cmds.confirmDialog(title="エラー", message="ターゲットオブジェクトを指定してください。", button=["OK"])

        return

 

    target = target_items[0]

    ctrl_list = get_text_scroll_list_items("selectionList")

    if not ctrl_list or "選択中のオブジェクトはありません" in ctrl_list:

        cmds.confirmDialog(title="エラー", message="コントローラーを選択してください。", button=["OK"])

        return

 

    original_joints = get_controlled_joints_in_order(ctrl_list)

    if len(original_joints) < 2:

        cmds.confirmDialog(title="エラー", message="対象ジョイントが2つ未満です。", button=["OK"])

        return

 

    duplicated_joints, dup_map = duplicate_joint_chain(original_joints, prefix="IK_")

 

    sorted_dup = sorted(duplicated_joints, key=lambda j: j.count('|') if '|' in j else 0)

 

    if not cmds.objExists("IK_Joints_GRP"):

        cmds.group(em=True, name="IK_Joints_GRP")

    if not cmds.objExists("IK_Handles_GRP"):

        cmds.group(em=True, name="IK_Handles_GRP")

    cmds.parent(sorted_dup[0], "IK_Joints_GRP")

 

    ik_handles = []

    for i in range(len(sorted_dup) - 1):

        j1, j2 = sorted_dup[i], sorted_dup[i + 1]

        try:

            ik_name = f"ikHandle_{j1.split('|')[-1]}_to_{j2.split('|')[-1]}"

            ik, _ = cmds.ikHandle(name=ik_name, startJoint=j1, endEffector=j2, solver="ikRPsolver")

            cmds.parent(ik, "IK_Handles_GRP")

            ik_handles.append(ik)

        except RuntimeError as e:

            cmds.warning(f"IK作成スキップ: {j1} → {j2} ({str(e)})")

 

    # ターゲットに親付け

    try:

        cmds.parent("IK_Joints_GRP", target)

        cmds.parent("IK_Handles_GRP", target)

        print(f"IK構成をターゲット {target} にペアレントしました。")

    except Exception as e:

        cmds.warning(f"ターゲットへのペアレントに失敗: {str(e)}")

 

    # 元コントローラーをIKジョイントで制御

    for orig_jnt, ctrl in zip(original_joints, ctrl_list):

        orig_short = orig_jnt.split('|')[-1]

        if orig_short in dup_map:

            ik_jnt = dup_map[orig_short]

            constraint_attrs = []

            for attr in ["translateX", "translateY", "translateZ", "rotateX", "rotateY", "rotateZ"]:

                if not cmds.getAttr(f"{ctrl}.{attr}", lock=True):

                    constraint_attrs.append(attr)

            if constraint_attrs:

                try:

                    cmds.parentConstraint(

                        ik_jnt, ctrl,

                        maintainOffset=True,

                        skipTranslate=[a[-1].lower() for a in ["translateX", "translateY", "translateZ"] if a not in constraint_attrs],

                        skipRotate=[a[-1].lower() for a in ["rotateX", "rotateY", "rotateZ"] if a not in constraint_attrs]

                    )

                    print(f"{ik_jnt} → {ctrl} にコンストレイントを設定しました。")

                except Exception as e:

                    cmds.warning(f"{ik_jnt} → {ctrl} のコンストレイント失敗: {str(e)}")

            else:

                print(f"{ctrl} のすべてのトランスフォーム属性がロックされています。")

 

    if not cmds.objExists("joint_and_ik_set"):

        cmds.sets(name="joint_and_ik_set", empty=True)

    cmds.sets(sorted_dup + ik_handles, add="joint_and_ik_set")

    cmds.confirmDialog(title="完了", message="分節IKチェーンを作成し、ターゲットへの親付け・追従設定を行いました。", button=["OK"])

 

def create_selection_and_joint_ui():

    if cmds.window("selectionJointUI", exists=True):

        cmds.deleteUI("selectionJointUI")

 

    window = cmds.window("selectionJointUI", title="分節IKチェーン作成ツール", widthHeight=(300, 450))

    cmds.columnLayout(adjustableColumn=True)

 

    cmds.text(label="ターゲットオブジェクト:", align="center")

    cmds.textScrollList("targetList", height=50)

    cmds.button(label="ターゲットを更新", command=lambda x: update_target_list())

 

    cmds.text(label="親から子の順にコントローラーを選択:", align="center")

    cmds.textScrollList("selectionList", height=120)

    cmds.button(label="現在選択を更新", command=lambda x: update_selection_list())

 

    cmds.separator(height=10, style='in')

    cmds.button(label="分節IKチェーンを作成", height=40, command=lambda x: create_segmented_ik_chain_with_constraints())

    cmds.button(label="セットとその中身を削除", command=lambda x: delete_set_and_contents("joint_and_ik_set"))

 

    cmds.showWindow(window)

    update_target_list()

    update_selection_list()

 

# 実行

create_selection_and_joint_ui()