Code Monkey home page Code Monkey logo

node_calculator's Introduction

NodeCalculator

This OpenSource Python module allows you to create node networks in Autodesk Maya by writing math formulas.

Why use it?

Turn those long lists of cmds.createNode, cmds.setAttr & cmds.connectAttr into readable math formulas.

All you need: Tutorials, CheatSheet, etc.

Documentation on ReadTheDocs

Install & Import

Download or clone this git-repo. Save the inner node_calculator folder (the one that contains core.py, etc.) to your \Documents\maya\scripts folder.

Also you can git clone the repo to a location of your choice then while in the top folder run mayapy -m pip install . This will make sure it is installed in maya's site-packages directory. Optionally you can install it in your userScripts: mayapy -m pip install . -t /path/to/your/userScriptsDirectory/ Note: Make sure to run in a terminal/console that has administrator privileges if Maya is installed in a write-protected folder.

Usage

Import it in Maya via

import node_calculator.core as noca

CI tested with:

  • Maya 2020
  • Maya 2019
  • Maya 2018
  • Maya 2017

(Thank you Marcus Ottosson for your crazy helpful Docker images)

Run Tests

If you are developing the NodeCalculator further, you can run the test suite to check whether the basic functionality is still intact. To do so, navigate to the bin-folder in your Maya directory. For example: C:\Program Files\Autodesk\Maya2018\bin And run this command in a terminal: .\mayapy.exe -m unittest discover -s >path\to\node_calculator\tests< -v

Think this is useful? I won't say no, if you insist on buying me a hot chocolate ;P

Donate

node_calculator's People

Contributors

andresmweber avatar kleinheinz avatar mischakolbe avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

node_calculator's Issues

Add .attr method

Currently certain attribute names would be impossible to work with:
.node, .attrs, .attrs_list

Add .attr() method that takes 1 String to specifically request ANY attribute (bypass any .node, .attrs, ... logic and return that Attrs-instance directly).

Avoid unnecessary setAttr-commands in Tracer

Currently

with noca.Tracer(pprint_trace=True):
    b.tx = noca.Op.set_range(a.tx)

results in

var1 = cmds.createNode('setRange', name='nc_SET_RANGE_translateX_0_1_0_1_setRange')
cmds.connectAttr('A.translateX', var1 + '.valueX', force=True)
cmds.setAttr(var1 + '.minX', 0)
cmds.setAttr(var1 + '.maxX', 1)
cmds.setAttr(var1 + '.oldMinX', 0)
cmds.setAttr(var1 + '.oldMaxX', 1)
cmds.connectAttr(var1 + '.outValueX', 'B.translateX', force=True)

If min/max/oldMin/oldMax values are not different to default values they should not be traced:

var1 = cmds.createNode('setRange', name='nc_SET_RANGE_translateX_0_1_0_1_setRange')
cmds.connectAttr('A.translateX', var1 + '.valueX', force=True)
cmds.connectAttr(var1 + '.outValueX', 'B.translateX', force=True)

Create default_noca_op function

A lot of times all that is required to add a node is basically the OPERATIONS-dictionary entry.

The Op-method is often times a simple

def example_operation(attr_a, attr_b=(0, 1, 2), attr_c=False):
    created_node = _create_and_connect_node('example_operation', attr_a, attr_b, attr_c)
    return created_node

This should could be simplified to use the OPERATIONS-dictionary directly to create the Op-method on the fly.
Maybe a "defaults" dictionary entry in the OPERATIONS-dict would be necessary:

defaults = OPERATIONS["example_op"].get("defaults", None)
if defaults:
    kwargs_defaults = {}
    for func_arg, default_value in zip(func_args, defaults):
        if default_value:
             kwargs_defaults[func_kwarg] = default_value
             func_args.remove(func_arg)

Needs:
func_name
func_args
func_kwargs
kwargs_defaults

func_name(func_args, func_kwargs):
    created_node = _create_and_connect_node('func_name', func_args + func_kwargs.keys())
    return created_node

Node names sometimes give warning and are nonsensical

For example this code creates 2 nodes that show this problem.

import node_calculator.core as noca
reload(noca)

a = noca.link("A", auto_unravel=True, auto_consolidate=True)
b = noca.link("B", auto_unravel=True, auto_consolidate=True)
c = noca.link("C", auto_unravel=True, auto_consolidate=True)

inter = (c.ty * 2)
b.t = noca.Op.condition(inter > inter.get(), c.tz, a.s + [1, 2, 2])

More versatile way of unravelling attributes

Attributes currently always unravel. That is not always desirable!
For example choice-nodes should pass through EXACTLY what is given/asked!
But these different cases are hard to detect right now (what is connected to what)
Currently connecting a.t to choice-node connects a.tz and b.t = choice.output
connects choice.output to all 3 directions (b.tx, b.ty, b.tz).
Should connect this way: a.t -> choice.input[0] and choice.output -> b.t

Given conn A to B: unravelling A doesn't make sense if B doesn't get unravelled.
A type-query with excluded node-types for unravelling might help? Not perfect either...

Revisit docStrings

Some docStrings are formulated a bit oddly. Reword to make as clear as possible.

Allow for attributes deeper than 2 levels

Currently only
node.firstLevelAttr.secondLevelAttr
is supported.

Use blendshape node as an example where a thirdLevel exists. Support setting/getting such attributes!

NodeCalculator Nodes should return a standard string

Currently a variable like
a = noca.Node("A_geo")
can not be passed on to a Maya command like this:
cmds.hide(a)
because it's a special entity.

Therefore it makes it necessary to keep 2 variables for the same thing:
a = "A_geo"
a_nc = noca.Node("A_geo")

Which is very unnecessary, redundant and not elegant.

Add getAttr to Tracer (with variables for queried values)

Add getAttr to Tracer. Maybe would need ID for each Node-object to keep track of values?
Otherwise a getAttr-result becomes a normal int/float! Not distinguishable anymore and
therefore variables within Tracer become impossible. With IDs there could be a check
for which Node-object is evaluated and that object could be identified

Use the variables.py approach: An overloaded Var-class that allows floats, ints, ... to store metadata (MObject?)

Add flags to get()?

Could include flag like "worldSpace" and apply kwargs to the getAttr command.

Use NcList ror multi-dimensional outputs

Operators like pair_blend or decompose_matrix currently return an awkward NcNode with a huge amount of attributes (3D+).

Within _create_and_connect_node there could be a check for the dimension of the outputs-list and if it's above 1 an NcList made up of NcNodes would be returned.

That would mean that all Operators output would have to be reformatted! All would need to be a list of lists!

Allow accessing noca.Node-attrs via index

It's currently impossible to access indexed attributes:
choice.input[0] = a.tx # This won't do anything, because the index bombs
Add setting of values by using index: a.attrs[0] = 3.5
Might need an Attr-Class that allows to identify attrs as such! Otherwise it's a
normal list indexing!

Add add_separator() method

There should be an easy and direct way to add attribute separators:

Using an enum or string attribute that is locked and has a unique attr-name but the same niceName and value for all separators.

Add the default separator strings (attr name and attr value) as GLOBALS at the top of core.py

Changelog NoCa v2

  • Uses MObjects and MPlugs to reference to Maya nodes and attributes; Renaming of objects, attributes with index, etc. are no longer an issue.
  • Indexed attributes now possible (still a bit awkward, but hey..)
  • Any child attribute will be consolidated (array, normal, ..)
  • Tracer now stores values as variables (from get() or so)
  • Cleaner code; Clear separation of classes and their functionality (NcList, NcNode, NcAttrs, NcValue)
  • Documentation; NoCa v2 cheat sheet!
  • Tests (based on Chad Vernon's test suite)
  • Convenience functions for transforms, locators & create_node.
  • auto_consolidate & auto_unravel can be turned off (globally & individually)
  • add any kind of attribute like add_float, add_short, ...
  • additional operators
  • om_util
  • Many other small improvements.

Add multiple multi-dimensional input support

Currently it works to have 1 multi-dimensional input:
[inputX[{multi_input}, inputY[{multi_input}, inputZ[{multi_input}]

but it would be better to somehow designate specific attributes that are multi dimensional!
Maybe instead of a generic "is_multi_input" it would be better to tag the input-index as multi-dimensional:
"multi_input_attrs": [1, 2]

Or filter through the various inputs and check if there is a multi_input-tag:

new_node_inputs = [
    [
        "input[{multi_input}].inputX",
        "input[{multi_input}].inputY",
        "input[{multi_input}].inputZ",
    ],
    ["simpleBool"],
]

args = [
    [1, 2, 3],
    True
]

# REMOVE * from passed on args!!!!
# Check len args == len new_node_inputs

cleaned_node_inputs = []

for arg, new_node_input in zip(args, new_node_inputs):
    print new_node_input
    is_multi_input = False
    for axis in new_node_input:
        if "{multi_input}" in axis:
            is_multi_input = True
            break

    if is_multi_input:
        multi_new_node_input = []
        multiplied_inputs = len(arg) * [new_node_input]
        print multiplied_inputs
        for index, node_input in enumerate(multiplied_inputs):
            multi_axis_inputs = []
            for axis in node_input:
                formatted = axis.format(multi_input=index)
                multi_axis_inputs.append(formatted)
            multi_new_node_input.append(multi_axis_inputs)

        new_node_input = multi_new_node_input

    cleaned_node_inputs.append(new_node_input)

This would allow for:
[inputX1[{multi_input}, inputY1[{multi_input}, inputZ1[{multi_input}]
[inputX2[{multi_input}, inputY2[{multi_input}, inputZ2[{multi_input}]

The same functionality should be implemented for outputs.

Replace "link" with "Node"

Similar to PyNode:
Use "Node" as the main class that redirects to the appropriate Class via new.

Rename what is currently named "Node" to "MayaNode", "NcNode", "MNode" or so.

Rename Collection and MetadataValue

Collection -> NcList
MetadataValue -> NcValue

Both names will be more descriptive of what the actual thing is.

NcList will need proper iterator and lookup methods implemented(!)

Use most efficient node available

Add a resolution setup that picks the most efficient node available to do the job.

Right now a simple 1D-multiplication uses the overloaded multiplyDivide node. That could be done in a more efficient multDoubleLinear. Same goes for plusMinusAverage & addDoubleLinear.

Add .get_shape keyword/property

noca.Nodes could provide a convenience method to access the shape nodes of a transform as a list. Returning None (or empty list?) if there isn't any shape.

Properly deal with multi index attributes

Manually dealing with multiIndex attributes is impossible or hacky at best. For example:

a = noca.Node("plusMinusAverage")
a.input3D[0].inputX = 5 # This fails! The first .input3D[0] returns a noca.Node-instance. The inputX is then run on that noca.Node and the result is a.inputX

Hopefully the Node & Attrs class will be able to take care of this.

Add multi-dim output option.

Currently only the
"is_multi_index" flag exists.
This should be changed to
is_multi_input
is_multi_output

To allow for individual setting of this.

Split out core functionality and additional config

Implementing NoCa into a pipeline is currently restricting:

Adding propietary nodes to the NoCa requries changing core files. Better would be to allow giving NoCa a path where to source additional configs (lookup_table AND Op-class!). That way the NoCa core (ie; what is in this repo) can be updated independently from what a studio might want to add to it.

Add flag to turn off attr-consolidation

Reducing child-plugs to their parent-plug can cause update-issues. Add a flag that allows the user to turn this feature off.

Maybe add core-settings:
noca.ATTR_CONSOLIDATION = False
etc.

Write unit tests!

test EVERYTHING!

  • initialization
  • attribute setting
  • attribute connecting (correct input/output connection(s))
  • attribute getting
  • adding attributes (different types!)
  • all Ops (correct node-type, correct mode, correct connections)
  • metadataValues
  • Tracer
  • ...

Add warning for .node on NcList

I still believe [.node for NcNode & NcAttrs] <> [.nodes for NcList] makes sense. They return different things and they should be different.

However: As a user one might not realize an element is an NcList and try to use .node on it. A reasonable warning, suggesting to use .nodes and returning None might be a good idea.

Attrs repr should include node

Currently the Attrs repr method only shows attributes. Since Node & Attrs class must be used quite interchangeably their repr methods should be identical or at least similar.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.