def setUp(self): super(TestOperators, self).setUp() # Create standard nodes on which to perform the operations on. # Transform test nodes self.tf_out_a = noca.create_node("transform", name="transform_out_A") self.tf_out_b = noca.create_node("transform", name="transform_out_B") self.tf_in_a = noca.create_node("transform", name="transform_in_A") self.tf_in_b = noca.create_node("transform", name="transform_in_B") # Matrix test nodes self.mat_out_a = noca.create_node("holdMatrix", name="matrix_out_A") self.mat_out_b = noca.create_node("holdMatrix", name="matrix_out_B") self.mat_in_a = noca.create_node("holdMatrix", name="matrix_in_A") # Quaternion test nodes self.quat_out_a = noca.create_node("eulerToQuat", name="quaternion_out_A") self.quat_out_b = noca.create_node("eulerToQuat", name="quaternion_out_B") self.quat_in_a = noca.create_node("quatNormalize", name="quaternion_in_A") # Mesh, curve and nurbsSurface test nodes self.mesh_out_a = noca.create_node("mesh", name="mesh_out_A") self.mesh_shape_out_a = noca.Node("mesh_out_AShape") self.curve_out_a = noca.create_node("nurbsCurve", name="curve_out_A") self.curve_shape_out_a = noca.Node("curve_out_AShape") self.surface_out_a = noca.create_node("nurbsSurface", name="surface_out_A") self.surface_shape_out_a = noca.Node("surface_out_AShape")
def test_standard_trace(self): """ Test regular tracing """ expected_trace = [ "cmds.setAttr('A.translateY', 1.1)", "val1 = cmds.getAttr('A.ty')", "var1 = cmds.createNode('multiplyDivide', name='nc_MUL_translateX_1f_multiplyDivide')", "cmds.setAttr(var1 + '.operation', 1)", "cmds.connectAttr('A.translateX', var1 + '.input1X', force=True)", "cmds.setAttr(var1 + '.input2X', (val1 + 2) / 2)", "var2 = cmds.createNode('plusMinusAverage', name='nc_SUB_list_plusMinusAverage')", "cmds.setAttr(var2 + '.operation', 2)", "cmds.connectAttr('B.scale', var2 + '.input3D[0]', force=True)", "cmds.connectAttr(var1 + '.outputX', var2 + '.input3D[1].input3Dx', force=True)", "cmds.connectAttr(var1 + '.outputX', var2 + '.input3D[1].input3Dy', force=True)", "cmds.connectAttr(var1 + '.outputX', var2 + '.input3D[1].input3Dz', force=True)", "cmds.connectAttr(var2 + '.output3D', 'C.translate', force=True)" ] with noca.Tracer() as trace: a = noca.Node("A") b = noca.Node("B") c = noca.Node("C") a.ty = 1.1 num = a.ty.get() c.t = b.s - a.tx * ((num + 2) / 2) self.assertEqual(trace, expected_trace)
def test_non_unique_node_names(self): """This test requires a specific set of nodes, different from the generic setUp.""" node_x = cmds.createNode("transform", name="X") group = cmds.createNode("transform", name="grp") node_x_grouped = cmds.createNode("transform") cmds.parent(node_x_grouped, group) cmds.rename(node_x_grouped, "X") node_x = "|X" node_x_grouped = "grp|X" nc_x = noca.Node(node_x) nc_x_grouped = noca.Node(node_x_grouped) nc_x.tx = self.node_a.tx nc_x.ty = 1 nc_x_grouped.tx = self.node_a.ty nc_x_grouped.ty = 2 node_x_connection = cmds.listConnections(node_x + ".tx", plugs=True) self.assertEqual(node_x_connection, [self.node_a.node + '.translateX']) self.assertEqual(cmds.getAttr(node_x + ".ty"), 1) node_x_grouped_connection = cmds.listConnections(node_x_grouped + ".tx", plugs=True) self.assertEqual(node_x_grouped_connection, [self.node_a.node + '.translateY']) self.assertEqual(cmds.getAttr(node_x_grouped + ".ty"), 2)
def setUp(self): super(TestNcListClass, self).setUp() self.a = noca.Node(cmds.createNode("transform", name=TEST_NODES[0]), TEST_ATTR) self.b = noca.Node(cmds.createNode("transform", name=TEST_NODES[1]), TEST_ATTR) self.c = noca.Node(cmds.createNode("transform", name=TEST_NODES[2]), TEST_ATTR) self.val = noca.Node(TEST_VALUE) self.desired_list = [self.a, self.val, self.b] self.nc_list = noca.NcList(self.desired_list)
def test_noca_set_method(self): """Test NoCa-specific set methods.""" # Test set method to set value on all NcList items at the same time self.node_only_nc_list.set(3) self.assertEqual(noca.Node(self.a, TEST_ATTRS[0]).get(), 3) self.assertEqual(noca.Node(self.b, TEST_ATTRS[1]).get(), 3) self.assertEqual(noca.Node(self.c, TEST_ATTRS[2]).get(), 3) self.node_only_nc_list.tz.set(7) self.assertEqual(self.a.tz.get(), 7) self.assertEqual(self.b.tz.get(), 7) self.assertEqual(self.c.tz.get(), 7)
def test_auto_unravel(self): """ Test automatic attribute unravelling: .t -> .tx, .ty, .tz """ # Test that by default unravelling takes place self.node_a.t = 1 # Test individual auto unravel setting # Setattr that requires unravelling should NOT work node_a_no_auto_unravel = noca.Node(self.node_a, auto_unravel=False) # Setattr that doesn't require unravelling should work node_a_no_auto_unravel.tx = 1 with self.assertRaises(RuntimeError): node_a_no_auto_unravel.t = [1, 2, 3] with self.assertRaises(RuntimeError): node_a_no_auto_unravel.t = 1 # Test global auto unravel setting noca.set_global_auto_unravel(False) # Setattr that doesn't require unravelling should work self.node_a.tx = 1 with self.assertRaises(RuntimeError): self.node_a.t = [1, 2, 3] # Setattr that requires unravelling should NOT work with self.assertRaises(RuntimeError): self.node_a.t = 1
def test_shape_attribute_access(self): """Test whether attrs of a transforms shape are directly accessible""" mesh_a = cmds.polyCube(name="testMeshA", constructionHistory=False)[0] mesh_b = cmds.polyCube(name="testMeshB", constructionHistory=False)[0] nc_mesh_a = noca.Node(mesh_a) nc_mesh_b = noca.Node(mesh_b) # Make sure the NodeCalculator nodes directly refer to the transforms, not the shapes! self.assertEqual(cmds.objectType(nc_mesh_a.node), "transform") self.assertEqual(cmds.objectType(nc_mesh_b.node), "transform") # Check whether the shapes get connected correctly, without accessing them explicitly. nc_mesh_a.inMesh = nc_mesh_b.outMesh mesh_a_connections = cmds.listConnections(mesh_a + ".inMesh", plugs=True) self.assertEqual(mesh_a_connections, [mesh_b + 'Shape.outMesh'])
def test_nc_value_creation(self): """ Test metadata variable creation via noca-convenience function """ value = 1.1 node = noca.Node(value) # Make sure instance of correct type was created self.assertIsInstance(node, noca.nc_value.NcFloatValue) # Make sure MetadataValue holds correct value self.assertEqual(node, value)
def test_node_without_attrs_creation(self): """ Test node creation via noca-convenience function """ transform = cmds.group(empty=True, name="testGroup") node = noca.Node(transform) # Make sure instance of correct type was created self.assertIsInstance(node, noca.NcNode) # Make sure no attributes were added to this node instance self.assertEqual(len(node.attrs), 0)
def test_nc_list_creation(self): """ Test collection creation via noca-convenience function """ transform_a = cmds.group(empty=True, name="testGroupA") attributes = ["tx", "tz"] transform_b = cmds.group(empty=True, name="testGroupB") value_a = 1.1 value_b = 5 node = noca.Node([ noca.Node(transform_a, attributes), transform_b, noca.Node(value_a), value_b ]) # Make sure instance of correct type was created self.assertIsInstance(node, noca.NcList) # Make sure Collection with right amount of elements was created self.assertEqual(len(node), 4) # Make sure elements of Collection are of correct type self.assertIsInstance(node[0], noca.NcNode) self.assertIsInstance(node[1], noca.NcNode) self.assertIsInstance(node[2], noca.nc_value.NcFloatValue) self.assertIsInstance(node[3], int)
def setUp(self): self.node_a = noca.Node( cmds.createNode("transform", name=TEST_NODES[0])) self.node_b = noca.Node( cmds.createNode("transform", name=TEST_NODES[1])) self.node_c = noca.Node( cmds.createNode("transform", name=TEST_NODES[2])) self.desired_node_list = [self.node_a, self.node_b, self.node_c] self.attr_a = noca.Node(self.node_a, TEST_ATTR) self.attr_b = noca.Node(self.node_a, TEST_ATTR) self.attr_c = noca.Node(self.node_a, TEST_ATTR) self.desired_attr_list = [self.attr_a, self.attr_b, self.attr_c] self.value_a = noca.Node(TEST_VALUES[0]) self.value_b = noca.Node(TEST_VALUES[1]) self.value_c = noca.Node(TEST_VALUES[2]) self.desired_value_list = [self.value_a, self.value_b, self.value_c] self.node_list = noca.NcList(self.desired_node_list) self.attr_list = noca.NcList(self.desired_attr_list) self.value_list = noca.NcList(self.desired_value_list)
def test_attributes(self): """Test child and indexed attribute handling""" blendshape_name = "test_blendshape" blendshape_attr = "inputTarget[0].inputTargetGroup[0].targetWeights[0]" blendshape_node = noca.Node( cmds.createNode("blendShape", name=blendshape_name), blendshape_attr) blendshape_node.attrs = self.attr_a blendshape_connections = cmds.listConnections( "{}.{}".format(blendshape_name, blendshape_attr), connections=True, plugs=True, ) desired_connections = [ "{}.{}".format(blendshape_name, blendshape_attr), "{}.{}".format(TEST_NODES[0], TEST_ATTR), ] self.assertEqual(blendshape_connections, desired_connections)
# Tab1 # BASICS import node_calculator.core as noca # Valid Node instantiations a = noca.Node("A_geo") a = noca.Node("A_geo.tx") a = noca.Node("A_geo", ["ty", "tz", "tx"]) a = noca.Node("A_geo", attrs=["ty", "tz", "tx"]) a = noca.Node(["A_geo.ty", "B_geo.tz", "A_geo.tx"]) # Numbers and lists work as well a = noca.Node(7) a = noca.Node([1, 2, 3]) # Created NcNode has a node-& attrs-part a = noca.Node("A_geo.tx") print(a) # Tab2 # BASICS import node_calculator.core as noca # Valid Node instantiations a = noca.Node("A_geo") # Attribute setting a.tx = 7 a.translateX = 6
# INTRODUCTION import node_calculator.core as noca # Initiate Node-objects for all test-geos a_geo = noca.Node("A_geo") b_geo = noca.Node("B_geo") c_geo = noca.Node("C_geo") # Average all directions of the A-translation translate_a_average = noca.Op.average(a_geo.tx, a_geo.ty, a_geo.tz) # Create a "floor collider" based on the height of B b_height_condition = noca.Op.condition(b_geo.ty > 0, b_geo.ty, 0) # Drive C-translation by different example-formula for each direction c_geo.translate = [ b_geo.tx / 2 - 2, b_height_condition * 2, translate_a_average ] # ~~~ VS ~~~ from maya import cmds # Average all directions of the A-translation var1 = cmds.createNode("plusMinusAverage", name="A_translate_average") cmds.setAttr(var1 + ".operation", 3) cmds.connectAttr("A_geo.tx", var1 + ".input3D[0].input3Dx", force=True) cmds.connectAttr("A_geo.ty", var1 + ".input3D[1].input3Dx", force=True) cmds.connectAttr("A_geo.tz", var1 + ".input3D[2].input3Dx", force=True) # Create a "floor collider" based on the height of B
# Tab1 # CONVENIENT EXTRAS import node_calculator.core as noca a = noca.Node("A_geo") b = noca.Node("B_geo") c = noca.Node("C_geo") # Keywords (see cheatSheet!) add_result = a.t + [1, 2, 3] print(add_result.node) print(add_result.attrs) print(add_result.attrs_list) print(add_result.plugs) noca_list = noca.Node([a.tx, c.tx, b.tz]) print(noca_list.nodes) # Tab2 # CONVENIENT EXTRAS import node_calculator.core as noca # Create nodes as NcNode instances my_transform = noca.transform("myTransform") my_locator = noca.locator("myLocator") my_xy = noca.create_node("nurbsCurve", "myCurve") # Add attributes my_transform.add_separator()
import node_calculator.core as noca # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # INTRODUCTION import node_calculator.core as noca # Initiate Node-objects for all test-geos a_geo = noca.Node("A_geo") b_geo = noca.Node("B_geo") c_geo = noca.Node("C_geo") # Average all directions of the A-translation translate_a_average = noca.Op.average(a_geo.tx, a_geo.ty, a_geo.tz) # Create a "floor collider" based on the height of B b_height_condition = noca.Op.condition(b_geo.ty > 0, b_geo.ty, 0) # Drive C-translation by different example-formula for each direction c_geo.translate = [ b_geo.tx / 2 - 2, b_height_condition * 2, translate_a_average ] # ~~~ VS ~~~ from maya import cmds # Average all directions of the A-translation var1 = cmds.createNode("plusMinusAverage", name="A_translate_average") cmds.setAttr(var1 + ".operation", 3)
def setUp(self): self.a = noca.Node(cmds.createNode("transform", name=TEST_NODES[0])) self.b = noca.Node(cmds.createNode("transform", name=TEST_NODES[1])) self.c = noca.Node(cmds.createNode("transform", name=TEST_NODES[2])) self.m = noca.Node(cmds.createNode("holdMatrix", name=TEST_NODES[3]))
# Tab1 # UNDER THE HOOD import node_calculator.core as noca _node = noca.Node("A_geo") print('Type of noca.Node("A_geo"):', type(_node)) _attr = noca.Node("A_geo").tx print('Type of noca.Node("A_geo").tx:', type(_attr)) _node_list = noca.Node(["A_geo", "B_geo"]) print('Type of noca.Node(["A_geo", "B_geo"]):', type(_node_list)) _int = noca.Node(7) print('Type of noca.Node(7):', type(_int)) _float = noca.Node(1.2) print('Type of noca.Node(1.2):', type(_float)) _list = noca.Node([1, 2, 3]) print('Type of noca.Node([1, 2, 3]):', type(_list)) # Tab2 # UNDER THE HOOD # NcNode vs NcAttrs nc_node = noca.Node("A_geo.translateX")
# EXAMPLE: Dynamic colors import node_calculator.core as noca """Task: Drive color based on translate x, y, z values: The further into the x/y/z plane: The more r/g/b. Values below zero/threshold should default to black. """ # Easy, but incomplete, due to: minus * minus = positive b = noca.Node("geo") b_mat = noca.Node("mat") multiplier = b.add_float("multiplier", value=0.25, max=0.5) r_value = b.ty * b.tz * multiplier g_value = b.tx * b.tz * multiplier b_value = b.tx * b.ty * multiplier b_mat.color = [r_value, g_value, b_value] # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Prototype red first. b = noca.Node("geo") b_mat = noca.Node("mat") multiplier = b.add_float("multiplier", value=0.25, max=0.5) r_value = b.ty * b.tz * multiplier b_mat.colorR = noca.Op.condition(b.ty > 0, noca.Op.condition(b.tz > 0, r_value, 0), 0) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# EXAMPLE: cogs_stepping import node_calculator.core as noca """Task: Drive all cogs by an attribute on the ctrl. All teeth but one were removed from one of the cogs(!) Uses maya_math_nodes extension! """ # Initialize nodes. main_rot = noca.Node("ctrl.cogRot") * 100 cog_5 = noca.Node("gear_5_geo") cog_7 = noca.Node("gear_7_geo") cog_8 = noca.Node("gear_8_geo") cog_22 = noca.Node("gear_22_geo") cog_17 = noca.Node("gear_17_geo") # Basic cog-rotation of small and big cog. cog_5.ry = main_rot / 5.0 cog_7.ry = main_rot / -7.0 cog_22.ry = main_rot / -22.0 # Make rotation positive (cog_22 increases in negative direction). single_tooth_rot = -cog_22.ry # Dividing single_tooth_rot by 360deg and ceiling gives number of steps. step_count = noca.Op.floor(noca.Op.divide(single_tooth_rot, 360)) single_tooth_degrees_per_step = 360 / 8.0 receiving_degrees_per_step = single_tooth_degrees_per_step / 17.0 * 8
if (in_value > (target_value - fade_in_range)): if (fade_in_range > 0): exponent = -(in_value - (target_value - fade_in_range)) / fade_in_range result = target_value - fade_in_range * exp(exponent) else: result = target_value else: result = in_value driven.attr = result """ import math driver = noca.Node("driver") in_value = driver.tx driven = noca.Node("driven.tx") fade_in_range = driver.add_float("transitionRange", value=1) target_value = driver.add_float("targetValue", value=5) # Note: Factoring the leading minus sign into the parenthesis requires one node # less. I didn't do so to maintain the similarity to Harry's example. # However; I'm using the optimized version in noca.Op.soft_approach() exponent = -(in_value - (target_value - fade_in_range)) / fade_in_range soft_approach_value = target_value - fade_in_range * math.e**exponent is_range_valid_condition = noca.Op.condition(fade_in_range > 0, soft_approach_value, target_value) is_in_range_condition = noca.Op.condition(
def test_auto_consolidate(self): """ Test automatic attribute consolidating: .tx, .ty, .tz -> .t """ # Test that by default consolidating takes place self.node_a.s = self.node_a.t child_connections = [ cmds.listConnections("{}.sx".format(TEST_NODES[0]), connections=True, plugs=True), cmds.listConnections("{}.sy".format(TEST_NODES[0]), connections=True, plugs=True), cmds.listConnections("{}.sz".format(TEST_NODES[0]), connections=True, plugs=True), ] parent_connections = cmds.listConnections("{}.s".format(TEST_NODES[0]), connections=True, plugs=True)[1] self.assertEqual(child_connections, [None, None, None]) self.assertEqual(parent_connections, "A.translate") # Test individual auto consolidate setting node_b_no_auto_consolidate = noca.Node(TEST_NODES[1], auto_consolidate=False) node_b_no_auto_consolidate.s = self.node_a.t child_connections = [ cmds.listConnections("{}.sx".format(TEST_NODES[1]), connections=True, plugs=True)[1], cmds.listConnections("{}.sy".format(TEST_NODES[1]), connections=True, plugs=True)[1], cmds.listConnections("{}.sz".format(TEST_NODES[1]), connections=True, plugs=True)[1], ] parent_connections = cmds.listConnections("{}.s".format(TEST_NODES[1]), connections=True, plugs=True) self.assertEqual(child_connections, ["A.translateX", "A.translateY", "A.translateZ"]) self.assertEqual(parent_connections, None) # Test global auto consolidate setting noca.set_global_auto_consolidate(False) self.node_c.s = self.node_a.t child_connections = [ cmds.listConnections("{}.sx".format(TEST_NODES[2]), connections=True, plugs=True)[1], cmds.listConnections("{}.sy".format(TEST_NODES[2]), connections=True, plugs=True)[1], cmds.listConnections("{}.sz".format(TEST_NODES[2]), connections=True, plugs=True)[1], ] parent_connections = cmds.listConnections("{}.s".format(TEST_NODES[2]), connections=True, plugs=True) self.assertEqual(child_connections, ["A.translateX", "A.translateY", "A.translateZ"]) self.assertEqual(parent_connections, None)
# EXAMPLE: cogs_simple import node_calculator.core as noca """Task: Drive all cogs by an attribute on the ctrl. """ # Solution 1: # Direct drive rotation ctrl = noca.Node("ctrl") gear_5 = noca.Node("gear_5_geo") gear_7 = noca.Node("gear_7_geo") gear_8 = noca.Node("gear_8_geo") gear_17 = noca.Node("gear_17_geo") gear_22 = noca.Node("gear_22_geo") driver = ctrl.add_float("cogRotation") * 10 gear_22.ry = driver / 22.0 gear_5.ry = driver / -5.0 gear_7.ry = driver / 7.0 gear_8.ry = driver / -8.0 gear_17.ry = driver / 17.0 # Solution 2: # Chained rotation: ctrl = noca.Node("ctrl") gear_5 = noca.Node("gear_5_geo") gear_7 = noca.Node("gear_7_geo")
# Tab1 # TRACER import node_calculator.core as noca a = noca.Node("A_geo") b = noca.Node("B_geo") c = noca.Node("C_geo") # This is our lovely formula, but it needs to be faster! c.ty = a.tx + b.ty / 2 # Tab2 # TRACER with noca.Tracer() as trace: c.ty = a.tx + b.ty / 2 print(trace) with noca.Tracer(pprint_trace=True): current_val = a.tx.get() offset_val = (current_val - 1) / 3 c.ty = a.tx + b.ty / offset_val