Even simple math expressions often require relatively large node networks, which are tedious to create by hand. While this process can be scripted, the code is likewise tedious to write and makes it difficult to see the logic at a glance.
To help alleviate these issues, Maya Math Nodes plugin provide a simple expression language that can be used to describe a series of mathematical operations inline, which can then be interpreted to generate a math node network for you. For example:
# project vector to plane eval_expression('node.t - (vec(0, 1, 0) * dot(node.t, vec(0, 1, 0)))', 'projectToPlane')
The language supports the following data types:
- float and int types are supported, ex:
-1, 0, 1.0
- string literals are used to reference Maya attributes, ex:
node.attribute, note that there are no quatation marks around the string literals!
- complex types such as vector, matrix, rotation, and quaternion are specified by using cast functions, ex:
vec(0, 1, 0)
The language supports a limited set of arithmetic operators:
+, -, *, /, %,
The language supports the following relational operators:
==, !=, >, <, >=, <=
These are used in combination with ternary conditional expression:
a == b ? true : false
The language supports calling functions with arguments. These functions map directly to the node operators available in the plugin.
Absolute node is made available through the
abs() function call.
Please see the Node Reference for the mapping between node type and function name.
The function arguments correspond with node attributes. For example the
Clamp node has two input
attributes, therefore the
clamp(arg1, arg2) function will take two arguments.
Likewise, array arguments are also supported with the following syntax:
minelement([1, 2, 3]).
Output array arguments can also be index using the
Several functions that output complex data types can take constant values as input.
mat(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)can be used to specify constat matrix value
rot(0, 1, 0)can be used to specify constant rotation value,
rot()also maps to several math nodes and can take other arguments, ex:
quat(0, 0, 0, 1)can be used to specify constant quaternion value,
quat()also maps to several math nodes and can take other arguments, ex:
vec(1, 0, 0)can be used to specify a constant vector value
Default and keyword arguments are currently not supported!
Expressions are evaluated left to right with the following operator precedence, listed from lowest to highest:
|+, -||Addition and subtraction|
|*, /, %||Multiplication, division, remainder|
|<, <=, >, >=, !=, ==||Comparisons|
The operators and functions are mapped to specific Maya nodes shipped with the plugin, and because the node library is strongly typed the parser needs to make a determination about types using the following rules:
- for operators, the left operand is used to determine primary type
- for conditional expressions, the true value is used to determine primary selector type
- for functions, the first argument is used to determine primary type
- if operand or argument is literal numeric type then casting to another numeric type is allowed
The expression evaluator will create Maya nodes procedurally and therefore needs a mechanism to generate unique names consistently.
This is achieved with the
NameGenerator class. To customize this behavior you can create your own implementation, with the
only requirement that it implements
get_name(str: node_type) -> str method.
eval_expression(str: expression, str: base_node_name='', NameGenerator: name_generator=None) -> str
The return value is the path to the output attribute of the last node in the generated node network that will have the result value computed for the expression. This value can then be passed to subsequent expressions to chain them together.
from maya_math_nodes import eval_expression # get twist value for roll joint eval_expression('twist(ctrl.worldMatrix, 0, 0) * 0.5', 'roll') # get toe pivot value for foot roll eval_expression('ctrl.roll > ctrl.break ? ctrl.roll - ctrl.break : 0', 'toeroll') # compute some pole vector with offset eval_expression('cross(axis(ctrl.matrix, 0), vec(0, 1, 0)) * 2', 'pole')