This package defines a wrapping functionality to create and manage mathematical node connections in Maya through Python scripting. The ease of access and method of using this will help to write readable and maintainable code for node based graphs that require a lot of mathematical processing.
The core concept of the Nodex is that each node in the graph can have multiple inputs, but has a single important output. This way the algorithm can be written as a single line of chained commands.
The process works by using an object called the Nodex
.
Upon initializing a Nodex you reference a value (its first argument). Since the Nodex has a magic initialization (factory within new) it will become the relevant datatype for the data you're referencing.
Then you can perform mathematical operations upon this Nodex with another Nodex (thus other attributes/numbers) and it will automatically return a Nodex that references the output attribute resulting from the created node graph.
Performable operations include mathematical operators that act directly on the Nodex instance, like:
- +, -, /, *, ^ for addition, subtraction, division, multiplication and power.
- ==, !=, >, >=, <, <= that will result in a 'condition' node.
Yet there's no boundary on what arguments one of the Nodex' inherited methods have so those could also include easing the workflow for much more complex operations than those already provided.
That's why you'll also see (among others) dot-product and cross-product implementations work when dealing with Vectors.
The Nodex will try to minimize the result/output based on the inputs. This means that when you add two integers it will give you the output attribute (as Nodex) for a single float attribute (output1D). But if you add two vectors together it will return the output3D from the used plusMinusAverage node.
Note: This is of course only a valid statement for where this conversion is possible with the given Maya graph nodes.
Since the output of each calculation with Nodex instances returns a new Nodex wrapping the calculation's output attribute you can write a chain of calculations. Of course you're free to stop take the resulting node midway, store it in a variable and use it's output for a multitude of node trees.
Using an input value or attribute it will try to guess how to connect it to the input attributes for a mathemical node based on the smaller input element (in dimensions) similar to above. This means adding together a float attribute with a vector attribute will actually result in a Vector where each component has the scalar added like this:
float + vector = vector + vector = vector output
0.5 + [0.3, -1, 10.1] = [0.5, 0.5, 0.5] + [0.3, -1, 10.1] = [0.8, -0.5, 10.6]
Multiply two attributes and connect the output value
mult = Nodex("pSphere1.translateX") * Nodex("pSphere1.translateY")
mult.connectTo( Nodex("pSphere1.translateZ") )
Add together multiple attributes (sum more than two values) and connect the result
result = Math.sum("pSphere1.translateX", "pSphere1.translateY", "pSphere1.translateZ", 1.0)
result.connectTo( Nodex("pSphere1.scaleX") )
Clamp an attribute and connect it
Nodex.clamp("pSphere1.translateX", min=0, max=1, output="pSphere1.translateY")
Complex operations become a bit more readable
(Nodex("pSphere.translateX") * 2) + 0.5
Using the Nodex's mixed array instantiation
nodex = Nodex([1, 0, "pSphere1.translateX"]) * 3
print nodex.value()
This will print [3, 0, 15]
if pSphere1.translateX is a value of 5.
Or perform a calculation through Maya nodes and clean up the Nodex tree afterwards
# The *MayaDeleteNewNodes* context manager is not included in the Nodex package but should be trivial to implement
with MayaDeleteNewNodes():
nodex = (Nodex(1) + Nodex("pSphere1.translateX")) / Nodex("pSphere2.translateY")
value = nodex.value()
No more choosing which pill to take; take both red and blue.
matrix = pymel.core.datatypes.Matrix()
nodex = Nodex(matrix)
# Convert the matrix into the local space of object `redPill`
redPill_matrix = Nodex("redPill.worldMatrix[0]")
local_mat = matrix * redPill_matrix.inverse()
# Decompose and directly connect to object `bluePill`
local_mat.decompose(translate="bluePill.translate",
rotate="bluePill.rotate",
scale="bluePill.scale")
Snap the src
object to the target
object.
This will also work if they are NOT under the same parent.
sel = pymel.core.ls(selection=True)
target = sel[0]
src = sel[1] # this will move towards the target
targetMat = Nodex(target.attr("worldMatrix[0]"))
srcParentInvMat = Nodex(src.attr("parentInverseMatrix[0]"))
localMat = targetMat * srcParentInvMat
localMat.decompose(translate=src.attr('translate'),
rotate=src.attr('rotate'),
scale=src.attr('scale'))
Have a blast normalizing your vectors!
vector = pymel.core.datatypes.Vector(10, 10, 0)
v = Nodex(vector)
# Let's get a point on the unit sphere.
v = v.normal()
# Let's put that point on a sphere of radius 5
v *= [5.0, 5.0, 5.0]
# Let's offset it by the translate of another node
v += Nodex('otherNode.translate')
# And use it to drive this node.
v.connect('thisNode.translate')
# cross product with another vector
vector.cross(otherVector)
# dot product with another vector
vector.dot(otherVector)
# normalize the vector
vector.normal()
# angle between two vectors
vector.angleTo(otherVector)
# distance between two vectors (as points)
vector.distanceTo(otherVector)
# length of the vector
vector.length()
# squared length of the vector (uses two nodes)
vector.squareLength()