Last active
December 19, 2025 08:10
-
-
Save revoconner/fe38d31ff756b167a7d27742f5eaa030 to your computer and use it in GitHub Desktop.
Maya script to align pivot orientation to the best-fit (average) plane of multiple selected components. Aligns pivot rotation perfectly to arbitrary selected vertices, faces, or edges!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| """ | |
| Maya's pivot orientation can be manually adjusted in Edit Pivot mode, but aligning it to match an arbitrary | |
| selection of components is tedious and imprecise when: | |
| - The target plane has no obvious orientation to reference | |
| - Selected components are scattered with no clear alignment | |
| - Finding the correct axis would require manual trial and error | |
| - Manual aligning with component doesn't give the best result (you can only ever | |
| select one component as far as I understand) | |
| This script calculates the best-fit plane for any selection using SVD and | |
| orients the pivot so Y is perpendicular to it, with X and Z lying on the plane. | |
| ######################################################################### | |
| Instruction before running for the first time (one time setup): | |
| 1. This script requires numpy, which is a python module not installed by default in Maya. | |
| 2. To install, run the command below in cmd (enter your Maya version when prompted). | |
| Make sure to run the cmd (or windows terminal as administrator): | |
| set /p v=Maya version: && "C:\Program Files\Autodesk\Maya%v%\bin\mayapy.exe" -m pip install numpy | |
| 3. Enter your maya version and press enter. Only enter the yearly version. For example if your | |
| Maya is 2025.1, you should only enter 2025. | |
| 4. Once it completes, restart maya (if open) else open maya and then run this either from the shelf or script window. | |
| ########################################################################### | |
| How to use: | |
| 1. Select faces, vertices or edges that define the plane you want the pivot to align to. | |
| 2. Enter pivot edit mode (press D or Insert). | |
| 3. Run the script. | |
| 4. The pivot's Y axis will be perpendicular to the best-fit plane of your selection. | |
| """ | |
| import maya.cmds as cmds | |
| import numpy as np | |
| import maya.api.OpenMaya as om | |
| def is_in_pivot_edit_mode(): | |
| if cmds.manipMoveContext('Move', q=True, exists=True): | |
| if cmds.manipMoveContext('Move', q=True, editPivotMode=True): | |
| return True | |
| if cmds.manipRotateContext('Rotate', q=True, exists=True): | |
| if cmds.manipRotateContext('Rotate', q=True, editPivotMode=True): | |
| return True | |
| if cmds.manipScaleContext('Scale', q=True, exists=True): | |
| if cmds.manipScaleContext('Scale', q=True, editPivotMode=True): | |
| return True | |
| return False | |
| def align_pivot_to_best_fit_plane(): | |
| if not is_in_pivot_edit_mode(): | |
| cmds.error("Not in pivot edit mode. Press 'D' or 'Insert' before running the script.") | |
| return | |
| sel = cmds.ls(selection=True, flatten=True) | |
| if not sel: | |
| cmds.error("Nothing selected. Please select vertices, edges, or faces.") | |
| return | |
| verts = cmds.polyListComponentConversion(sel, toVertex=True) | |
| vert_items = cmds.filterExpand(verts, selectionMask=31) | |
| if not vert_items or len(vert_items) < 3: | |
| cmds.error("At least 3 vertices are required to define a plane.") | |
| return | |
| points = [] | |
| for v in vert_items: | |
| pos = cmds.xform(v, query=True, worldSpace=True, translation=True) | |
| points.append(pos) | |
| points = np.array(points) | |
| centroid = np.mean(points, axis=0) | |
| centered_points = points - centroid | |
| U, S, Vt = np.linalg.svd(centered_points) | |
| x_axis = Vt[0] | |
| z_axis = Vt[1] | |
| y_axis = Vt[2] | |
| if np.dot(np.cross(x_axis, y_axis), z_axis) < 0: | |
| z_axis = -z_axis | |
| matrix_list = [ | |
| x_axis[0], x_axis[1], x_axis[2], 0.0, | |
| y_axis[0], y_axis[1], y_axis[2], 0.0, | |
| z_axis[0], z_axis[1], z_axis[2], 0.0, | |
| 0.0, 0.0, 0.0, 1.0 | |
| ] | |
| m_matrix = om.MMatrix(matrix_list) | |
| m_transformation_matrix = om.MTransformationMatrix(m_matrix) | |
| euler_rot = m_transformation_matrix.rotation(asQuaternion=False) | |
| rot_degrees = [np.degrees(euler_rot.x), np.degrees(euler_rot.y), np.degrees(euler_rot.z)] | |
| cmds.manipPivot(orientation=rot_degrees) | |
| cmds.manipPivot(bakeOri=True) | |
| print(f"Pivot aligned. Y axis perpendicular to best-fit plane.") | |
| align_pivot_to_best_fit_plane() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment