Vuo  0.7.0
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
Modules

Description

A node class is like a function or method. It takes some input, processes it, and produces some output.

When you write a node class using Vuo's API, the node class consists of:

Vuo currently provides an API for the C programming language. (Node classes can also reference code in C++, Objective-C, and any other language that exports C symbols; see Developing a Library Module and Managing Dependencies.) Support for other programming languages is planned.

Quick start

The easiest way to start developing a node class is with one of the example Qt projects for a node class, which are provided with the Vuo SDK.

  1. Install Qt and Qt Creator.
  2. Make a copy of the example Qt project for a stateless node class (example/node/stateless).
  3. Open the project in Qt Creator.
  4. In stateless.pro, change test.vuoize.c to your node class file name.
  5. In the example project folder, rename test.vuoize.c to your node class file name.
  6. In test.vuoize.c, in the Module Metadata call, change "Vuoize Text" to the title that you want to appear on the node.
  7. Build your Qt project (Build > Build All).
  8. Start the Vuo Editor (or restart it if it's already running).

Your node class should now be listed in your Node Library. You can now add it to a composition and see it perform the task defined in its nodeEvent() function.

The next step is to modify the nodeEvent() function so that your node actually does something interesting. To learn how, read on.

Writing a node class

To implement a node class, you need to:

The file name

When you implement a node class, the first thing you have to decide is the class name. Every node class has two names: a class name (like vuo.math.add) and a title (like Add). In the Vuo Editor, you can see the difference between titles and class names by switching between the "Titles" view and the "Class names" view of the Node Library. After a node has been added to a composition, its title can be changed, but its class name always stays the same. When you create a node class, the file name is the node class name. For example, the node class with name vuo.math.add is implemented in a file called vuo.math.add.c.

The metadata

Here's an example of metadata for a node class:

"title" : "Add",
"description" : "Adds the terms and outputs their sum.",
"keywords" : [ "sum", "+" ],
"version" : "1.0.0",
"node": {
"isInterface" : false
}
});

The Module Metadata macro takes a JSON-formatted argument. The argument includes the default title for nodes of this node class, a description to help users understand how to use the node class, an array of keywords used when searching the Node Library, and a version number to help users upgrade and manage their node classes.

Node classes are not the only files in Vuo that can define module metadata. You'll see this in Developing a Port Type and Developing a Library Module. The title, description, keywords, and version can be defined in other types of files as well.

But some module metadata are specific to node classes. The module metadata above include whether the node class is an interface, which affects how the node is rendered in a composition.

When deciding which keywords to put in the module metadata, be aware that some are added automatically, so you don't have to add them. Each word in the node's title and class name is automatically used in searches of the Node Library. If your node class has trigger ports, then the keywords "events", "trigger", and "fire" are automatically added.

For more information, see the documentation for Module Metadata.

The functions

When you implement a node class, you have to implement certain functions that define what happens when a node receives an event. You implement different functions depending on whether the node class is stateless or stateful.

A stateless node class (like Add) is simpler. It just has input ports and output ports; given the same input, a stateless node always produces the same output. A stateful node class (like Count) additionally keeps track of its state. It can remember its progress, and it can fire events when something happens in the background.

A stateless node class

For a stateless node class, you just need to implement one function: nodeEvent. The nodeEvent function is called each time a node of your node class receives an event through any input port. (If the same event is received through multiple input ports, the nodeEvent function is called just one time.) Its arguments represent input and output ports of the node.

As a simple example, here's the nodeEvent function for the vuo.logic.negate node class:

(
VuoInputData(VuoBoolean, {"default":false}) value,
)
{
*notValue = !value;
}

The above nodeEvent function has a VuoInputData parameter for each input port and a VuoOutputData parameter for each output port. (The refresh port and done port are added to each node automatically.) Both VuoInputData and VuoOutputData define data-and-event ports. The parameter name (value or notValue) is the port name that appears on the node.

The first argument of VuoInputData or VuoOutputData is the port type. Vuo comes with many built-in port types (see Built-in Types), and you can also define your own (see Developing a Port Type).

The second argument of VuoInputData defines details about the input port in JSON format. In this case, it specifies the default value for the input port when a node is first added to a composition.

The body of nodeEvent defines what happens when a node receives an event. In this case, it negates the input port value and sets the output port value. Notice that notValue is a pointer to the VuoOutputData type (VuoBoolean).

A stateful node class

For a stateful node class, there are several functions to implement.

Instead of nodeEvent, you need to implement an equivalent function called nodeInstanceEvent. Like nodeEvent, the nodeInstanceEvent function is called whenever the node receives an event.

You also need to implement the nodeInstanceInit function and the nodeInstanceFini function to set up and clean up the node's state. The nodeInstanceInit function is called when the composition starts or when the node is added to a running composition. The nodeInstanceFini function is called when the composition stops or when the node is removed from a running composition.

If your node class has trigger ports, you may also need to implement the nodeInstanceTriggerStart function, the nodeInstanceTriggerUpdate function, and the nodeInstanceTriggerStop function.

As an example, let's look at a simplified version of the vuo.math.count node class. Its only input port is increment. Here are the functions for that node class:

{
VuoReal *countState = (VuoReal *) malloc(sizeof(VuoReal));
VuoRegister(countState, free);
*countState = 0;
return countState;
}
(
VuoInstanceData(VuoReal *) countState,
VuoInputData(VuoReal, {"default":1}) increment,
VuoInputEvent(VuoPortEventBlocking_None,increment) incrementEvent,
)
{
if (incrementEvent)
**countState += increment;
*count = **countState;
}
(
VuoInstanceData(VuoReal *) countState
)
{
}

The state of the node is called instance data. It's created by nodeInstanceInit and passed, via VuoInstanceData parameters, to nodeInstanceEvent and nodeInstanceFini. Notice that the return type of nodeInstanceInit and the type passed to each VuoInstanceData call must match. Currently, it needs to be a pointer type; support for non-pointer types is planned. Unlike the type passed to VuoInputData or VuoOutputData, the type passed to VuoInstanceData doesn't have to be a port type. For example, it can be a struct type defined in your node class. The name of the VuoInstanceData parameter (countState) isn't important; you can choose any name. Notice that countState is a pointer to the VuoInstanceData type (VuoReal*).

The call to VuoRegister in nodeInstanceInit is necessary to make sure that the memory allocated for countState is freed at the right time. For more information, see Managing Memory.

Besides the instance data, another difference between this example and the stateless example above is the VuoInputEvent parameter of nodeInstanceEvent. The VuoInputEvent macro can represent either an event-only port or the event part of a data-and-event port. In this case, it's the latter. We can see this because the second argument of VuoInputEvent is the name of the corresponding VuoInputData parameter (and the port name that appears on the node). The first argument of VuoInputEvent says whether the port has an event wall, an event door, or (as in this case) neither. The value of incrementEvent is true if the node has just received an event through its increment port.

The body of nodeInstanceEvent defines what happens when the node receives an event. In this case, it increments the count if the event came in through the increment port, then outputs the count.

Nodes should treat port data as immutable

The Vuo Compiler assumes that data passed between nodes is immutable.

If a node function receives a heap type (pointer) as input, it should not modify that heap data. For example, this is incorrect:

(
VuoOutputData(VuoText) textWithFirstLetterCapitalized
)
{
// Don't do this!
text[0] = toupper(text[0]);
*textWithFirstLetterCapitalized = text;
}

Instead, node functions should make a copy of the heap data, then modify that copy. For example:

(
VuoOutputData(VuoText) textWithFirstLetterCapitalized
)
{
VuoText t = VuoText_make(text); // makes a copy of text
t[0] = toupper(t[0]);
*textWithFirstLetterCapitalized = t;
}

Also, if a stateful node function sends heap data via an output port, it should not keep a reference to that heap data and modify it after the node function returns.

See Managing Memory for more information.

Additional functions and variables

In addition to the Vuo API functions, your node class can define additional functions. Be sure to add a unique prefix to these function names to avoid naming conflicts with other node classes. Although the Vuo compiler renames the nodeEvent function and other API functions to avoid naming conflicts, it doesn't rename your custom functions.

If you want to share functions between multiple node classes, or use C++ or Objective-C functions in a node class, see Developing a Library Module.

A node class should not define any global variables.

Generic port types

If a node class can work with multiple different types of data, then rather than implementing a version of the node class for each data type, you can use generic types as placeholders for the actual data types. When a node with generic types is added to a composition, each of its generic types has the potential to be specialized (replaced) with an actual data type. Some examples of node classes that use generic types are Hold Value, Select Latest, and Add.

To use a generic type in a node class, use VuoGenericType1 in place of the data type. For example, you can define a generic input port as VuoInputData(VuoGenericType1) value. You can define a list-of-generics input port as VuoInputData(VuoList_VuoGenericType1) list. You can append a value of generic type to a list by calling VuoListAppend_VuoGenericType1(list, value).

To use more than one generic type in a node class, you can use VuoGenericType2, VuoGenericType3, and so on.

To specify the default value for a generic input port, you can use the "defaults" key (as opposed to the the singular "default" key for non-generic input ports) in the JSON-formatted port details. For example, a port that can be specialized to either a VuoReal defaulting to 1 or a VuoPoint2d defaulting to (1,1) would be declared with this parameter: VuoInputData(VuoGenericType1, {"defaults":{"VuoReal":0.0, "VuoPoint2d":{"x":1,"y":1}}}) portName.

For a node class to use generic types, it must be part of a node set. The source code for the node class and its included header files must be packaged into the node set. See Packaging a Node Set.

For more information about using generic port types in a node class — including how to restrict the types that a generic type can be specialized with — see Module Metadata.

Advanced topics

For more information about the parameters that can be passed to the nodeEvent, nodeInstanceEvent, and other node class functions — including how to create a trigger port — see Node Parameters.

For more information about the node class functions, see Node Methods: Stateless and Node Methods: Stateful.

Also see the source code for Vuo's built-in node classes, which can serve as examples to help you write your own node classes.

Compiling a node class

Before you can install your node class, you need to use the Vuo Compiler to compile it to a .vuonode file.

If you're using the example Qt project for creating a node class, then you can just build the Qt project (Build > Build All).

Otherwise, you need to use the vuo-compile command-line tool that comes with the Vuo SDK. To learn how to use vuo-compile, see the Vuo Manual, run vuo-compile --help, or look at the vuo-compile command in the example Qt project.

Installing a node class

The final step is to place your compiled node class in the correct folder, so that it will show up in the Vuo Editor's Node Library and be detected by the Vuo framework and the Vuo command-line tools. You can place it in either ~/Library/Application Support/Vuo/Modules/ or /Library/Application Support/Vuo/Modules/. For more information about these folders, see the Vuo Manual.

If you're using the example Qt project for creating a node class, then when you build the project, the compiled node class is automatically placed in ~/Library/Application Support/Vuo/Modules/.

Otherwise, you need to manually move the compiled node class (.vuonode) file to one of these folders.

After that, the next time you start the Vuo Editor, the node class should show up in the Vuo Editor's Node Library. You can also see a list of all installed node classes by running vuo-compile --list-node-classes.

Troubleshooting

If you're having trouble compiling or installing a node class, try running vuo-compile --verbose. This lists the paths that Vuo is using to compile a node class and find installed node classes.

If your node class is not showing up in the Vuo Editor's Node Library:

Naming node classes and ports

Please do not begin your node class's name with "vuo". This is reserved for node classes distributed by Team Vuo / Kosada. Please use your own company or personal name for your node classes so that Vuo users can appreciate your work (and not be confused).

Built-in Vuo nodes follow a set of naming conventions. If you develop node classes to share with other Vuo users, we encourage you to follow these conventions, too, to make your node classes easier to use.

In addition to these general rules, there are some special kinds of node classes that all have similar names:

Type-converter nodes

In the Vuo Editor, when the user attempts to connect a cable between ports of different types (for example, VuoInteger to VuoReal), the connection is bridged by a type-converter node (for example, Convert Integer to Real) if one is available. This type-converter node is rendered in a special collapsed form and is attached to the input port to which it's connected. Currently, only certain built-in nodes may be used as type converters. Support is planned to allow any node with a single data-and-event input port and a single data-and-event output port of a different type to be used as a type converter. The Vuo Renderer decides when a node should be rendered as a collapsed, attached type converter, based on its connections to other nodes.

Modules

 
 Module Debugging
 Macros to help with debugging.
 
 Node Parameters
 Parameter decorations to be used by node classes.
 
 Node Methods: Stateless
 Event handler method to be implemented by node classes.
 
 Node Methods: Stateful
 Setup, event handler, and teardown methods to be implemented by stateful node classes.