Quickstart ========== This guide gets you working with tik.maya in five minutes. Installation ------------ tik.maya is part of the TikWorks repository. Add the ``src`` directory to your Maya Python path: .. code-block:: python import sys sys.path.append("/path/to/tikworks/src") import tik.maya as tm Dynamic cmds Wrapping --------------------- **tik.maya dynamically wraps the entire maya.cmds module**, allowing you to use it as a drop-in replacement for ``maya.cmds`` with minimal changes to existing scripts: .. code-block:: python # Traditional approach import maya.cmds as cmds # Simply change the import! import tik.maya as tm # Now all your cmds calls work through tik.maya tm.polyCube(name="myCube") tm.xform("myCube", translation=(1, 2, 3)) tm.setAttr("myCube.translateX", 5.0) Under the hood, tik.maya uses `PEP 562 `_ (module-level ``__getattr__``) to intercept attribute access. When you call ``tm.polyCube()``, it dynamically proxies to ``maya.cmds.polyCube()`` while intelligently wrapping inputs and outputs. **Key benefits:** - **Seamless migration:** Existing scripts can switch namespaces with minimal changes - **Automatic type resolution:** Commands that return nodes automatically return typed tik.maya wrappers - **Selective overrides:** Critical functions like ``createNode`` use optimized OpenMaya API implementations for better performance - **Input cleaning:** tik.maya objects are automatically converted to strings for Maya commands - **Output wrapping:** Maya node names are automatically wrapped as tik.maya objects when appropriate .. code-block:: python # Example: This returns a tik.maya.Transform object, not a string cube = tm.polyCube(name="myCube")[0] print(type(cube)) # # You can immediately use object-oriented methods cube.translate = (1, 2, 3) cube["visibility"].value = False .. tip:: **Want to see more?** Check out the ``snippets/comparisons/`` folder in the repository for many side-by-side examples comparing ``maya.cmds`` and ``tik.maya`` approaches. Example ``08_cylinder_rig`` demonstrates how an entire rigging script can work by simply changing ``import maya.cmds as cmds`` to ``import tik.maya as tm``! Wrapping Existing Nodes ----------------------- Use :func:`tik.maya.resolve` to wrap any existing Maya node: .. code-block:: python import tik.maya as tm # Wrap a node by name cube = tm.resolve("pCube1") # The returned object is typed — Transform, Mesh, Joint, etc. print(type(cube)) # tik.maya automatically returns the correct wrapper class based on the Maya node type. Working with Attributes ----------------------- Access attributes using bracket notation: .. code-block:: python # Get a Plug object for the attribute plug = cube["translateX"] # Read and write values plug.value = 5.0 print(plug.value) # 5.0 # Or use the property shortcut on transforms cube.translate_x = 10.0 Attribute plugs have useful properties: .. code-block:: python # Lock/unlock cube["translateX"].locked = True cube["translateX"].lock() # equivalent # Visibility in channel box cube["translateX"].visible = False # Keyable state cube["translateX"].keyable = False Plugs also support mathematical operators to create dependency graph nodes: .. code-block:: python # Arithmetic operations create Maya nodes automatically driver = tm.Transform.create(name="driver") follower = tm.Transform.create(name="follower") driver["tx"].value = 10.0 # Create addDL and multDL nodes, then connect to follower (driver["tx"] * 2.0 + 5) >> follower["ty"] print(follower["ty"].value) # 25.0 .. tip:: For comprehensive coverage of plug operations including all mathematical operators, connections, and advanced patterns, see :doc:`guides/working_with_plugs`. Connecting Attributes --------------------- Use the ``>>`` operator for connections: .. code-block:: python locator = tm.resolve("locator1") cube = tm.resolve("pCube1") # Connect translate locator["translate"] >> cube["translate"] # Chain connections a["output"] >> b["input"] >> c["input"] Or use the explicit method: .. code-block:: python locator["translateX"].connect(cube["translateX"]) Creating Nodes -------------- Use the ``create()`` class method on type classes: .. code-block:: python # Create a transform grp = tm.Transform.create(name="myGroup") # Create a joint jnt = tm.Joint.create(name="arm_jnt") # Create a locator (returns the shape node) loc = tm.Locator.create(name="myLocator") loc.transform.translate = (1, 2, 3) # Access parent transform # Create geometry (pass the Maya command as first argument) sphere = tm.Mesh.create("polySphere", name="mySphere") plane = tm.Nurbs.create("nurbsPlane", name="myPlane") .. note:: Shape types like ``Locator``, ``Mesh``, and ``Curve`` return shape node wrappers. Access the parent transform via the ``.transform`` property. DAG Hierarchy ------------- Navigate the scene hierarchy: .. code-block:: python transform = tm.resolve("pCube1") # Get parent parent = transform.parent # Set parent transform.parent = tm.resolve("group1") # Get children for child in transform.children: print(child.name) # Get shapes for shape in transform.shapes: print(shape.name, type(shape)) Transform Operations -------------------- Common transform operations are built in: .. code-block:: python # Read transforms print(cube.translate) # MVector print(cube.rotate) # MVector print(cube.scale) # MVector # Write transforms cube.translate = (1, 2, 3) cube.rotate = (0, 45, 0) cube.scale = (2, 2, 2) # Snap to another transform cube.snap_to(target, position=True, rotation=True) # Freeze transformations cube.freeze(translate=True, rotate=True, scale=True) # Get matrices local_matrix = cube.matrix world_matrix = cube.world_matrix Node Lifecycle -------------- .. code-block:: python # Check existence if cube.exists(): print("Node exists") # Rename (reference stays valid!) cube.rename("newCubeName") print(cube.name) # "newCubeName" # Delete cube.delete() Adding Custom Attributes ------------------------ .. code-block:: python # Add an attribute cube.add_attr("customFloat", attributeType="float", defaultValue=0.0) # Access it cube["customFloat"].value = 1.5 # Delete it cube.delete_attr("customFloat") Working with Shapes ------------------- tik.maya provides shape-specific functionality: .. code-block:: python # Mesh operations mesh = tm.resolve("pCubeShape1") vertices = mesh.vertices(space="world") nearby = mesh.vertices_in_radius((0, 0, 0), radius=1.0) mesh.unlock_normals(soften=True) # Curve operations curve = tm.resolve("curveShape1") cvs = curve.cvs(space="world") Advanced Example: Controllers and Panels ---------------------------------------- This example combines a controller role, the control shape library, hierarchy utilities, and a panel construct to build a small rig preview setup. The shape library returns a list of available shape names that you can pass into ``Controller.create``. The panel accepts a camera name, camera shape, or wrapper (``"persp"`` refers to Maya's default perspective camera) along with a window resolution in pixels. .. code-block:: python import tik.maya as tm from tik.maya.roles.controller import Controller from tik.maya.utils.control_shapes import ControlShapeLibrary from tik.maya.constructs import Panel # Build a simple joint chain root_joint = tm.Joint.create(name="spine_root") mid_joint = tm.Joint.create(name="spine_mid", parent=root_joint) end_joint = tm.Joint.create(name="spine_end", parent=mid_joint) # Browse available shapes in the library shape_library = ControlShapeLibrary.get_instance() available_shapes = shape_library.list_shapes() print(available_shapes) # ["Circle", "Square", "CubePin", ...] # Create a controller with a library shape if not available_shapes: raise RuntimeError("No control shapes found in the library.") shape_name = available_shapes[0] ctrl = Controller.create(name="spine_ctrl", shape=shape_name, size=2.0) ctrl.color = (0.9, 0.2, 0.2) # Align and connect to the joint chain ctrl.transform.snap_to(root_joint, position=True, rotation=True) ctrl.transform["translate"] >> root_joint["translate"] ctrl.transform["rotate"] >> root_joint["rotate"] # Lock scale attributes to prevent unintended joint scaling # collect_hierarchy returns wrapped DAG nodes under the root. for joint in root_joint.collect_hierarchy(node_types=["joint"], include_self=True): for axis in ["scaleX", "scaleY", "scaleZ"]: joint[axis].locked = True joint[axis].visible = False # Spawn a dedicated preview panel panel = Panel(camera="persp", resolution=(1280, 720), title="Rig Preview") panel.display_textures = True panel.grid = False Next Steps ---------- - Read :doc:`why_tik_maya` to understand the design philosophy - Explore :doc:`/architecture/core_concepts` for the architecture - Browse the :doc:`/autoapi/index` for complete API details