On this page |
A function is a block of code that performs a specific operation. In APEX Script, users can create their own functions or use the built-in APEX functions.
User-defined functions ¶
User-defined functions in APEX Script are represented as subnet nodes in the APEX network view. The contents of the subnet node contains the function logic.
APEX Script code:
# Function definition def test(a: Int, b: Int): c: Int = a + b return c # Function call x = test(1, 2)

Users can define their own functions in APEX Script similar to Python:
-
Functions are defined using the
def
keyword followed by the function name. -
Information, called arguments, can be passed into a function through a comma-separated list of values within a set of parentheses
()
. These arguments follow the function name. -
The block of code that is executed when the function is called must be indented.
-
Information can be returned by a function, and these return values are specified using the
return
statement. A function can return multiple values separated by commas. -
Functions must be defined before they are called.
Function with no arguments:
# Function definition def test(): a = 1.5 return a # Function call; result is x = 1.5 x = test()
The number of arguments in the function call must match the number of arguments in the function definition. It is good practice to annotate the arguments in the function defintion:
# Function definition def test(a: Int, b: Int): c: Int = a + b return c # Function call x = test(1, 2)
You can call a function with keyword arguments, which are arguments with the syntax <key>=<value>
, where <key>
is the name of the argument. When calling a function using all keyword arguments, the order of the arguments does not matter.
Note
If you use both keyword and positional (non-keyword) arguments in a function call, keyword arguments must be placed after positional arguments.
# Function definition def test(a: Int, b: Int): c: Int = a + b return c # Function call with keyword arguments x = test(b=1, a=2)
Keyword arguments that contain __dot__
are replaced with '.
'. This allows APEX Script to handle rigs with geometry names like Base.skel
and Base.shp
:
# Function takes in Base.skel and returns Base.shp def test(Base__dot__skel: Geometry): Base__dot__shp = Geometry() return Base__dot__shp # Graph input is Base.skel geo = BindInput(Base__dot__skel=Geometry()) x: Geometry = test(geo)

A function can return multiple values, and you can specify the return types following the ->
after the argument list. Function return types can be specified with parentheses or square brackets:
def test(a: Vector3, b: Float) -> tuple[Vector3, Float]: a = a * b b = b + 1.0 return a, b x, y = test(a=(3.0, 3.0, 3.0), b=5.5)
You can specify a name for an output port of a subgraph or subnet in the function return type. Use square brackets to do this. In the example below, the subnet output port b is renamed to outputy:
def test(a: Vector3, b: Float) -> tuple[Vector3, Float[outputy]]: a = a * b b = b + 1.0 return a, b x, y = test(a=(3.0, 3.0, 3.0), b=5.5) BindOutput(x, y)


If you want to ignore certain return values, assign those values to an underscore (_
):
def test(a: Int): a1: Int = a + 1 a2: Int = a + 2 a3: Int = a + 3 a4: Int = a + 4 return a1, a2, a3, a4 _, _, x, _ = test(1)
You can specify default values for the function arguments. If the function is called without arguments, the default values are used:
def foo(a: String = 'test', b: Int = Int(Float(1.5))): return a, b # Result: x = 'test', y = 1 x, y = foo()
Expressions in function calls:
def foo(a: String, b: Int): return a, b m = 3 n = 4 p = 'test' # Result: x = 'test3', y = 7 x, y = foo(p+String(m), m+n)
Functions can be nested inside other functions:
def foo(a: Int): return a+2 # Call foo within this function def bar(a: Int) -> Int: return foo(a)+4 # Result: x1 = 3, x2 = 7 x1 = foo(1) x2 = bar(1)
Functions can be daisy-chained:
geo.computeTransform().transform(xform=Matrix4())
Saving functions as subgraphs ¶
Functions can be saved to disk as subgraphs using the APEX Script SOP. These subgraphs can then be used in APEX Script code as global functions. Any subgraphs you have, including ones built by other users, could be used as functions in APEX Script.
To save a function as a subgraph:
-
In an APEX Script SOP, turn on the Subgraphs parameter.
-
In the Subgraphs section, define the function in the Subgraph snippet parameter, and put the
@subgraph
decorator above the function definition.For example:
@subgraph def test(a: Int, b: Int): c: Int = a + b return c
-
The subgraph must be saved as a
.bgeo
file in@/apexgraph
, where@
expands to the directories in the HOUDINI_PATH. If you don’t specify the directory, the subgraph will be saved to$HOME/apexgraph
. You can set the subgraph location and filename in the Geometry File parameter or the subgraph decorator argument,@subgraph(<file_path>)
. -
Click the Save Subgraphs button.
In the same APEX Script SOP, or in a new APEX Script SOP, you can now use the saved subgraph as a function when writing APEX Script code.
Decorators ¶
A decorator performs an operation on another function and is specified with the @
symbol. For example:
@subgraph @namespace('my_namespace') @safeguard_inputs(True) def test(a: Int, b: Int): c: Int = a + b return c
The available decorators are:
Subgraph ¶
The @subgraph
decorator allows the function that directly follows it to be saved as a subgraph. The subgraph is saved as a packed subgraph - it is stored as a primitive in a packed folder.
The @subgraph
decorator takes in an optional argument, <file_path>
, and also supports the use of keyword-only arguments:
@subgraph([<file_path>], name=<name>, hidden=[True|False], ...)
Argument |
Type |
Description |
---|---|---|
name |
String |
The name assigned to the subgraph. This is the |
hidden |
Bool |
If set to True, hides the subgraph from the tab menu in the APEX network view. This value is added to the |
You can also specify custom keyword-only arguments, which are added to the properties
attribute of the graph geometry.
Example:
The @subgraph
decorator below names the subgraph testnode::3.0
. Without the name
keyword argument, the subgraph would be named test
:
@subgraph('$HOME/houdini21.0/apexgraph/subgraph_test.bgeo', name='testnode::3.0', hidden=False, extrainfo=123) def test(a: Int, b: Int): c: Int = a + b return c
After saving the subgraph, you can use the subgraph in the following ways:
-
Search for the subgraph
testnode
in the APEX network view: -
Use the subgraph as a function in an APEX Script snippet:
s = testnode(1,2)
To see the properties of the subgraph, you’ll need to unpack the graph:

-
Use the
File SOP to bring in the subgraph:
-
Set Geometry File to the subgraph
.bgeo
file. In our example,$HOME/houdini21.0/apexgraph/subgraph_test.bgeo
. -
In the geometry spreadsheet, click
Primitives on the top toolbar. The subgraph name
testnode::3.0
is listed as the name of the packed subgraph.
-
-
The subgraph is in a packed folder, so to see its properties, unpack it using an
Unpack Folder SOP:
-
Set Extract Pattern to the name of the subgraph,
/testnode::3.0
. -
In the geometry spreadsheet, click
Detail on the top toolbar. The following values are listed in the
properties
attribute of the subgraph:{ "extrainfo": 123, "hidden": 0 }
-
Namespace ¶
You can specify a namespace for a function with the @namespace
decorator. After saving a function as a subgraph, you can call the function using <namespace>.<function>
. The syntax for the @namespace
decorator is:
@namespace('<namespace>')
For example, you can define the following subgraph in the APEX Script SOP, Subgraphs section:
@subgraph @namespace('my_namespace') def test(a: Int, b: Int): c: Int = a + b return c
After saving the function as a subgraph, you can call the subgraph function with:
x = my_namespace.test(1,2)
Safeguard inputs ¶
The @safeguard_inputs
decorator helps prevent graph sorting issues by making copies of function input arguments where necessary. The syntax for the @safeguard_inputs
decorator is:
@safeguard_inputs(True|False)
When @safeguard_inputs
is set to True, APEX Script makes a copy of a function input argument (adds a Value node to the graph) if:
-
The input argument feeds into an in-place port.
and
-
The name of that input does not match any output names on the function.
If @safeguard_inputs
is set to False, APEX Script does not create copies of any function arguments. This could introduce graph sorting issues, so use caution when setting @safeguard_inputs
to False.
If @safeguard_inputs
is not included before a function, it is set to True by default.
Built-in functions ¶
A library of built-in functions is available within APEX Script. These functions can be called using either the APEX namespace, or an object-oriented syntax where the function operates on one of the inputs. The list of available APEX functions can be found in the APEX nodes index, the APEX network view TAB menu, or in the autocomplete feature when typing in the Snippet parameter of the APEX Script SOP or
APEX Autorig Component SOP.
Different syntax for calling functions:
geo = BindInput(Geometry()) # Call function using the APEX and SOP namespaces a = apex.sop.copytopoints(geo) # Object-oriented syntax for calling a function b = geo.copytopoints()
The example below calls the append() function using the APEX namespace. The first input of append() is an in-place port, so the input array,
x
, is updated:
x = ['aaa'] x = apex.array.append_String(x, 'bbb') # Result: x = ['aaa', 'bbb']
If the result of append() is assigned to a different variable, the original array is not updated:
x = ['aaa'] y = apex.array.append_String(x, 'bbb') # Result: x = ['aaa'], y = ['aaa', 'bbb']
With the object-oriented syntax, the first input of a function is the “object” that the function operates on, and is placed before the function call. This object is then passed to the function as the first input. If this first input is an in-place port, it is updated in-place:
x = ['aaa'] x.append_String('bbb') # Result: x = ['aaa', 'bbb']
If the object-oriented append() is assigned to a variable, the variable will be set to the 2nd return value of append():
x = ['aaa'] idx = x.append_String('bbb') # Result: x = ['aaa', 'bbb'], idx = 1
If the first input of a function is not an in-place port, that input is not updated:
x = ['aaa','bbb','ccc'] y = x.get_String(1) # Result: x = ['aaa', 'bbb', 'ccc'], y = 'bbb'
Call a function with arguments:
box = apex.sop.box(t=Vector3(1,1,1), scale=Float(2))
Functions without a namespace, for example Sine(), do not need to be prefixed with the APEX namespace:
# With APEX namespace a = apex.sine(1.0) # Without APEX namespace b = sine(1.0)
By default, APEX Script uses the exact version of the APEX function that is called, and not the latest version. For example, IntBitMaskToBool()
calls the first version of the function. If you want to use the second version of IntBitMaskToBool, call
IntBitMaskToBool.v2_0()
. See the special function HoudiniVersion() for information on how to use a specific version of APEX functions.
Templated functions ¶
Templated functions are functions that operate on different types of data. For example, geo::SetPrimAttribValue<T> is a templated function, where <T> could be one of several different types. Templated functions can be specified in the following ways:
Use the specific function name that contains the type:
geo.setPrimAttribValue_String(prim, 'name', 'test')
Use the valuetype
argument to specify the type, valuetype=<type>
:
geo.setPrimAttribValue(prim, 'name', 'test', valuetype=String)
The valuetype
argument can be a list or tuple:
b = Convert(a, valuetype=(Float, Int)) c = Convert(b, valuetype=[Int, Bool])
Special functions ¶
APEX Script provides special functions that allow you to perform various graph operations, including the ability to add information to graph nodes, use a specific version of APEX nodes, and invoke graphs from within a graph.
Function |
Description |
||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
BeginMetadata, EndMetadata |
Sets metadata on the block begin and end nodes for loops and if blocks (for example, y = 0 for x in range(3): # Set metadata on ForBegin node BeginMetadata(__name='test_begin', __color=(1,0,1), __tags=['tag1', 'tag2']) y = y + 1 # Set metadata on ForEnd node EndMetadata(__name='test_end', __pos=(1,1,1), __tags=['out_tag']) |
||||||||||||||
BindInput, BindOutput |
Defines the inputs and outputs of a graph. For more information, see this example for how to use BindInput() and BindOutput(), and specifying graph inputs for example uses of BindInput(). |
||||||||||||||
HoudiniVersion |
Sets the version of the APEX functions to use. For example, You could also specify the Houdini version in the header template:
Two other arguments are available for use with the HoudiniVersion() function -
If HoudiniVersion() is not present, this is the same as calling |
||||||||||||||
InputMetadata, OutputMetadata |
Sets metadata on the graph input (parms) and output nodes inside a subnet. The available metadata are listed in the special function arguments. def test(a,b): # Set metadata on subnet input node InputMetadata(__name='abc', __pos=(1,0,0), __properties={'test': 1.5}) c: Float = a + b # Set metadata on subnet output node OutputMetadata(__color=(1,0,1), __tags=['out_tag1','out_tag2']) return c x = test(1,2) |
||||||||||||||
InvokeOutputs |
See invoke graph. |
||||||||||||||
SetGraph |
|||||||||||||||
StickyNote |
Creates sticky notes in the graph. The arguments listed below, in addition to the special function arguments, can be used to set certain properties on the sticky note:
Example:
|
||||||||||||||
|
Creates another identifier for a variable without making a copy. This could introduce graph sorting issues, so use with caution. Create an alias of a variable: x = 1.5 y = _portalias(x) # y = 1.5 BindOutput(y) Create an alias of a variable that is assigned to a function: def test(): x = 1 return x m = test() m_alias = _portalias(m) # m_alias = 1, n = 3 n = m_alias + 2 Use an alias in a function: def test(a: Int, b: Int): c: Int = a + b d = _portalias(c) d += 2 return d # m = 5 m = test(1, 2) |
Special function arguments ¶
The special function arguments listed below can be used to specify certain information on a node:
Argument |
Type |
Description |
---|---|---|
|
|
The name of the node. |
|
|
The color of the node. |
|
|
The position of the node in the graph. |
|
|
The properties stored on the node. |
|
|
The tags stored on the node. |
Example:
def abc(a: Vector3, b: Float): a = a * b b = b + 1.0 return a, b # Subnet node name is set to the function name subnet_a = abc(a=(3.0, 3.0, 3.0), b=5.5) # Special function arguments used to set the name and color of the subnet node subnet_b = abc(a=(3.0, 3.0, 3.0), b=5.5, __name='test_b', __color=(1,0,0))

Function mappings ¶
APEX Script provides convenient name mappings for some of the built-in APEX functions.
Graph functions ¶
The examples in this section use the following definitions:
graph = ApexGraphHandle() # Add two TransformObject nodes to the graph a = graph.addNode('test_a', 'TransformObject') b = graph.addNode('test_b', 'TransformObject') # Dictionary d = {'xord': 3}
Function |
Maps to |
Example |
---|---|---|
|
graph::AddNode |
|
|
graph::ConnectInput |
|
|
graph::FindAndConnectInput |
|
|
graph::GetConnectedPorts |
|
|
graph::ConnectInput |
|
|
graph::FindAndConnectInput |
|
|
graph::Compile |
If
|
|
graph::FindOrAddPort |
|
|
Value<ApexGraphHandle> |
“Saves” the graph in a Value node.
|
|
graph::NodeInputs |
|
|
graph::NodeOutputs |
|
|
graph::FindPort |
|
|
graph::FindNodes |
|
|
graph::FindPorts |
|
|
graph::FindPorts |
|
|
graph::PromoteInput |
|
|
graph::PromoteOutput |
|
|
graph::UpdateNodeParms |
|
Node functions ¶
The examples in this section use the following definitions:
graph = ApexGraphHandle() # TransformObject node 'test_a' a = graph.addNode('test_a', 'TransformObject') # Dictionaries d = {'abc': 3}
Function |
Maps to |
Example |
---|---|---|
|
graphutils::NodeAncestors |
# Pack node into a subnet node_arr: ApexNodeIDArray = [a] graph.packSubnet('my_test', node_arr) # Returns 'my_test' anc = a.ancestors() |
|
graphutils::NodeCallbackName |
|
|
graph::NodeData |
|
|
graph::NodeInputs |
|
|
graph::NodeName |
|
|
graph::NodeOutputs |
|
|
graphutils::NodeParent |
|
|
graphutils::NodeParms |
|
|
graphutils::NodePath |
|
|
graph::FindPort |
|
|
graphutils::NodeProperties |
|
|
graph::UpdateNode |
|
|
graph::UpdateNode |
|
|
graph::UpdateNodeParms |
|
|
graph::UpdateNode |
|
|
graph::UpdateNodeProperties |
|
|
graph::UpdateNodeTags |
|
|
graphutils::NodeTags |
|
Port functions ¶
The examples in this section use the following definitions:
graph = ApexGraphHandle() # Add two TransformObject nodes to graph a = graph.addNode('test_a', 'TransformObject') b = graph.addNode('test_b', 'TransformObject') # Ports on node 'test_a' a_in = a.t_in a_out = a.xform_out # Ports on node 'test_b' b_in = b.parent_in b_out = b.t_out
Function |
Maps to |
Example |
---|---|---|
|
graph::ConnectInput |
Connect ports on the same nesting level in a graph.
|
|
graph::GetConnectedPorts |
|
|
graph::AddWirePath |
Connects ports on different nesting levels in a graph, for example, connecting to a node in a subnet. The name of the connection port that is created on the subnet node is set to # Add a node to the graph c = graph.addNode('test_x', 'TransformObject') # Pack the node into a subnet node_arr: ApexNodeIDArray = [c] graph.packSubnet('test_subnet', node_arr) # Connect to node in subnet a_out.connectPath(c.xform_in, 'abc') |
|
graph::PortData |
|
|
graph::DisconnectPort |
|
|
graph::PortNode |
|
|
graph::PromotePort |
|
|
graph::GetPromotedPort |
|
|
graph::PromoteInput |
|
|
graph::PromoteOutput |
|
|
graph::GetSubPort |
If you use subport() to create a subport on a node, you won’t see the new subport in the graph until you connect something to it. # Add nodes to graph c = graph.addNode('test_c', 'Add<Float>') d = graph.addNode('test_d', 'Add<Float>') # Create subport 'dd' d_in = d.b_in.subport('dd') # Connect to subport 'dd' c.result.connect(d_in) |
Data functions ¶
The examples in this section use the following definitions:
# Dictionaries d = {'abc': 3} # Vectors v1 = (1,2,3) v2 = (2,4,5) # Matrix m = Matrix4(2)
Function |
Maps to |
Example |
---|---|---|
|
sop::kinefx::computetransform |
geo: Geometry = BindInput() geo.addJoint('joint_1', Matrix4(2)) geo.addJoint('joint_2', Matrix4(3)) x = geo.computetransform(mode=2) |
|
CrossProduct |
|
|
Distance |
|
|
Distance |
|
|
DotProduct |
|
|
Value<Dict>, Value<Geometry> |
“Saves” the dictionary or geometry in a Value node.
|
|
Invert<Matrix4> |
This function is performed in-place - the input matrix is updated, so there is no need to assign to an output variable.
|
|
transform::Dihedral<Matrix4> |
|
|
Normalize |
This function is performed in-place - the input vector is updated:
If normalize() is assigned to a variable, the variable is set to the length of the vector:
|
|
Normalize |
|
|
transform::SmoothRotation |
|