Nodes in nodebpy support Python’s arithmetic, comparison, and boolean operators. Instead of manually creating Math, Compare, or BooleanMath nodes and wiring them together, you can write expressions that read like regular Python code. The correct node type (Math, IntegerMath, VectorMath, Compare, BooleanMath, or MultiplyMatrices) is chosen automatically based on the socket types involved.
Arithmetic Operators
The standard arithmetic operators +, -, *, / are joined by ** (power), % (modulo), // (floor division), unary - (negate), and abs().
with g.tree("ArithmeticDemo" ) as tree:
out = tree.outputs.geometry()
val = g.Value(2.0 )
scaled = val** 2 # Math.power
wrapped = scaled % 5.0 # Math.floored_modulo
snapped = scaled // 3.0 # Math.divide -> Math.floor
_ = (
g.Points(100 , position= g.RandomValue.vector(min =- 1 ))
>> g.SetPosition(offset= g.Position() * snapped)
>> out
)
tree
graph LR
N0("Value"):::input-node
N1("Math<br/><small>(POWER)</small>"):::converter-node
N2("Math<br/><small>(DIVIDE)</small>"):::converter-node
N3("Math<br/><small>(FLOORED_MODULO)</small>"):::converter-node
N4("Random Value<br/><small>(-1,-1,-1)</small>"):::converter-node
N5("Position"):::input-node
N6("Math<br/><small>(FLOOR)</small>"):::converter-node
N7("Points"):::geometry-node
N8("Vector Math<br/><small>(SCALE)</small>"):::vector-node
N9("Set Position"):::geometry-node
N10("Group Output"):::default-node
N0 -->|"Value->Value"| N1
N1 -->|"Value->Value"| N3
N1 -->|"Value->Value"| N2
N2 -->|"Value->Value"| N6
N4 -->|"Value->Position"| N7
N5 -->|"Position->Vector"| N8
N6 -->|"Value->Scale"| N8
N8 -->|"Vector->Offset"| N9
N7 -->|"Points->Geometry"| N9
N9 -->|"Geometry->Geometry"| N10
All operators automatically select the right node type. With integers you get IntegerMath, with vectors you get VectorMath, and scalars are broadcast when mixed with vectors:
with g.tree("TypeDispatch" ) as tree:
out = tree.outputs.geometry()
# Integer operations use IntegerMath nodes
idx = g.Index()
row = idx // 10 # IntegerMath.divide_floor
col = idx % 10 # IntegerMath.modulo
# Vector operations use VectorMath nodes
pos = g.Position()
offset = pos** 2 # VectorMath.power (element-wise)
wrapped = pos % (1 , 1 , 1 ) # VectorMath.modulo
_ = g.Grid(10 , 10 , 100 , 100 ) >> g.SetPosition(offset= wrapped) >> out
tree
graph LR
N0("Position"):::input-node
N1("Grid"):::geometry-node
N2("Vector Math<br/><small>(POWER)</small><br/><small>(2,2,2)</small>"):::vector-node
N3("Vector Math<br/><small>(MODULO)</small>"):::vector-node
N4("Set Position"):::geometry-node
N5("Index"):::input-node
N6("Integer Math<br/><small>(DIVIDE_FLOOR)</small>"):::converter-node
N7("Integer Math<br/><small>(MODULO)</small>"):::converter-node
N8("Group Output"):::default-node
N5 -->|"Index->Value"| N6
N5 -->|"Index->Value"| N7
N0 -->|"Position->Vector"| N2
N0 -->|"Position->Vector"| N3
N3 -->|"Vector->Offset"| N4
N1 -->|"Mesh->Geometry"| N4
N4 -->|"Geometry->Geometry"| N8
Negation and Absolute Value
The unary - and abs() operators work with all numeric types:
with g.tree("UnaryOps" ) as tree:
_ = (
g.Cube()
>> g.SetPosition(offset=- abs (g.Position()))
>> tree.outputs.geometry()
)
tree
graph LR
N0("Position"):::input-node
N1("Vector Math<br/><small>(ABSOLUTE)</small>"):::vector-node
N2("Cube"):::geometry-node
N3("Vector Math<br/><small>(SCALE)</small><br/><small>×-1</small>"):::vector-node
N4("Set Position"):::geometry-node
N5("Group Output"):::default-node
N0 -->|"Position->Vector"| N1
N1 -->|"Vector->Vector"| N3
N3 -->|"Vector->Offset"| N4
N2 -->|"Mesh->Geometry"| N4
N4 -->|"Geometry->Geometry"| N5
Comparison Operators
The <, >, <=, >= operators create Compare nodes that output boolean sockets. The correct data type (float, integer, or vector) is inferred from the left-hand operand.
with g.tree("CompareDemo" ) as tree:
out = tree.outputs.geometry()
pos = g.Position()
z = pos.o.position.z
above_ground = z > 0.0 # Compare.float.greater_than
below_ceiling = z <= 5.0 # Compare.float.less_equal
_ = (
g.Cube(size= 10 )
>> g.SetPosition(selection= above_ground, offset= (0 , 0 , 1 ))
>> out
)
tree
graph LR
N0("Position"):::input-node
N1("Separate XYZ"):::converter-node
N2("Cube<br/><small>(1e+01,1e+01,1e+01)</small>"):::geometry-node
N3("Compare<br/><small>(LESS_EQUAL)</small>"):::converter-node
N4("Compare<br/><small>(GREATER_THAN)</small>"):::converter-node
N5("Set Position<br/><small>+(0,0,1)</small>"):::geometry-node
N6("Group Output"):::default-node
N0 -->|"Position->Vector"| N1
N1 -->|"Z->A"| N4
N1 -->|"Z->A"| N3
N4 -->|"Result->Selection"| N5
N2 -->|"Mesh->Geometry"| N5
N5 -->|"Geometry->Geometry"| N6
Comparison into a Switch
The result of a comparison is a Compare node, which can be used to directly chain into a Switch node when in a Geometry node tree. This saves us some time having to directly use g.Switch.float(pos.z > v, ...)
with g.tree("SwitchDemo" ) as tree:
v = g.Value(5.0 )
pos = g.Position().o.position
result = (pos.z > v).switch(g.RandomValue.float (), 5.0 ** g.Value(10.0 ))
tree
graph LR
N0("Position"):::input-node
N1("Separate XYZ"):::converter-node
N2("Value"):::input-node
N3("Value"):::input-node
N4("Compare<br/><small>(GREATER_THAN)</small>"):::converter-node
N5("Random Value"):::converter-node
N6("Math<br/><small>(POWER)</small>"):::converter-node
N7("Switch"):::converter-node
N0 -->|"Position->Vector"| N1
N1 -->|"Z->A"| N4
N2 -->|"Value->B"| N4
N3 -->|"Value->Value"| N6
N4 -->|"Result->Switch"| N7
N5 -->|"Value->False"| N7
N6 -->|"Value->True"| N7
Boolean Operators
Python’s bitwise operators & (and), | (or), ^ (xor), and ~ (not) map to BooleanMath nodes. These are especially useful for combining comparison results into complex selections.
with g.tree("BooleanDemo" ) as tree:
out = tree.outputs.geometry()
z = g.SeparateXYZ(g.Position()).o.z
# Combine conditions: select points in a vertical band
selection = (z > - 2.0 ) & (z < 2.0 )
_ = (
g.Cube(size= 6 )
>> g.MeshToPoints()
>> g.SetPosition(selection= selection, offset= (1 , 0 , 0 ))
>> out
)
tree
graph LR
N0("Position"):::input-node
N1("Separate XYZ"):::converter-node
N2("Cube<br/><small>(6,6,6)</small>"):::geometry-node
N3("Compare<br/><small>(GREATER_THAN)</small>"):::converter-node
N4("Compare<br/><small>(LESS_THAN)</small>"):::converter-node
N5("Mesh to Points"):::geometry-node
N6("Boolean Math<br/><small>(AND)</small>"):::converter-node
N7("Set Position<br/><small>+(1,0,0)</small>"):::geometry-node
N8("Group Output"):::default-node
N0 -->|"Position->Vector"| N1
N1 -->|"Z->A"| N3
N1 -->|"Z->A"| N4
N3 -->|"Result->Boolean"| N6
N4 -->|"Result->Boolean"| N6
N2 -->|"Mesh->Mesh"| N5
N6 -->|"Boolean->Selection"| N7
N5 -->|"Points->Geometry"| N7
N7 -->|"Geometry->Geometry"| N8
The ~ operator inverts a boolean:
with g.tree("InvertDemo" ) as tree:
is_even = (g.Index() % 2 ) > 0
is_odd = ~ is_even
_ = (
g.MeshLine(count= 20 )
>> g.MeshToPoints()
>> g.SetPosition(selection= is_odd, offset= (0 , 0 , 0.5 ))
>> tree.outputs.geometry()
)
tree
graph LR
N0("Index"):::input-node
N1("Integer Math<br/><small>(MODULO)</small>"):::converter-node
N2("Mesh Line<br/><small>+(0,0,1)</small>"):::geometry-node
N3("Compare<br/><small>(GREATER_THAN)</small>"):::converter-node
N4("Mesh to Points"):::geometry-node
N5("Boolean Math<br/><small>(NOT)</small>"):::converter-node
N6("Set Position<br/><small>+(0,0,0.5)</small>"):::geometry-node
N7("Group Output"):::default-node
N0 -->|"Index->Value"| N1
N1 -->|"Value->A"| N3
N3 -->|"Result->Boolean"| N5
N2 -->|"Mesh->Mesh"| N4
N5 -->|"Boolean->Selection"| N6
N4 -->|"Points->Geometry"| N6
N6 -->|"Geometry->Geometry"| N7
Matrix Multiplication
The @ operator maps to MultiplyMatrices, composing two 4x4 transformation matrices. You can also multiply a matrix by a vector using @ and a TransformPoint will automatically be added.
with g.tree("MatmulDemo" ) as tree:
rotate = g.CombineTransform(rotation= (0 , 45 , 0 ))
translate = g.CombineTransform(translation= (2 , 0 , 0 ))
_ = g.Cube() >> g.SetPosition(position= rotate @ translate @ g.Position())
tree
graph LR
N0("Combine Transform<br/><small>(0,4e+01,0)</small>"):::converter-node
N1("Combine Transform<br/><small>(2,0,0)</small>"):::converter-node
N2("Position"):::input-node
N3("Multiply Matrices"):::converter-node
N4("Cube"):::geometry-node
N5("Transform Point"):::converter-node
N6("Set Position"):::geometry-node
N0 -->|"Transform->Matrix"| N3
N1 -->|"Transform->Matrix"| N3
N2 -->|"Position->Vector"| N5
N3 -->|"Matrix->Transform"| N5
N5 -->|"Vector->Position"| N6
N4 -->|"Mesh->Geometry"| N6
Putting It All Together
Here are some examples of building small algorithms entirely with operators.
Checkerboard Selection
Select alternating faces on a grid using integer modulo and comparisons:
with g.tree("Checkerboard" ) as tree:
idx = g.Index()
row = idx // 10
col = idx % 10
is_checker = ((row + col) % 2 ) > 0
_ = (
g.Grid(10 , 10 , 10 , 10 )
>> g.SetPosition(selection= is_checker, offset= (0 , 0 , 0.5 ))
>> tree.outputs.geometry()
)
tree
graph LR
N0("Index"):::input-node
N1("Integer Math<br/><small>(DIVIDE_FLOOR)</small>"):::converter-node
N2("Integer Math<br/><small>(MODULO)</small>"):::converter-node
N3("Integer Math<br/><small>(ADD)</small>"):::converter-node
N4("Integer Math<br/><small>(MODULO)</small>"):::converter-node
N5("Grid"):::geometry-node
N6("Compare<br/><small>(GREATER_THAN)</small>"):::converter-node
N7("Set Position<br/><small>+(0,0,0.5)</small>"):::geometry-node
N8("Group Output"):::default-node
N0 -->|"Index->Value"| N1
N0 -->|"Index->Value"| N2
N1 -->|"Value->Value"| N3
N2 -->|"Value->Value"| N3
N3 -->|"Value->Value"| N4
N4 -->|"Value->A"| N6
N6 -->|"Result->Selection"| N7
N5 -->|"Mesh->Geometry"| N7
N7 -->|"Geometry->Geometry"| N8
Layered Selection
Divide space into layers using floor division, then select specific layers:
with g.tree("Layers" ) as tree:
z = g.SeparateXYZ(g.Position()).o.z
layer = g.SeparateXYZ(g.Position() // (1 , 1 , 1 )).o.z
selection = (layer > 0 ) & (layer < 3 ) & ~ (layer > 1 )
_ = (
g.Cube(size= 5 )
>> g.MeshToPoints()
>> g.SetPosition(selection= selection, offset= (1 , 0 , 0 ))
>> tree.outputs.geometry()
)
tree
graph LR
N0("Position"):::input-node
N1("Vector Math<br/><small>(DIVIDE)</small>"):::vector-node
N2("Vector Math<br/><small>(FLOOR)</small>"):::vector-node
N3("Separate XYZ"):::converter-node
N4("Compare<br/><small>(GREATER_THAN)</small>"):::converter-node
N5("Compare<br/><small>(LESS_THAN)</small>"):::converter-node
N6("Compare<br/><small>(GREATER_THAN)</small>"):::converter-node
N7("Cube<br/><small>(5,5,5)</small>"):::geometry-node
N8("Boolean Math<br/><small>(AND)</small>"):::converter-node
N9("Boolean Math<br/><small>(NOT)</small>"):::converter-node
N10("Mesh to Points"):::geometry-node
N11("Boolean Math<br/><small>(AND)</small>"):::converter-node
N12("Position"):::input-node
N13("Set Position<br/><small>+(1,0,0)</small>"):::geometry-node
N14("Separate XYZ"):::converter-node
N15("Group Output"):::default-node
N12 -->|"Position->Vector"| N14
N0 -->|"Position->Vector"| N1
N1 -->|"Vector->Vector"| N2
N2 -->|"Vector->Vector"| N3
N3 -->|"Z->A"| N4
N3 -->|"Z->A"| N5
N4 -->|"Result->Boolean"| N8
N5 -->|"Result->Boolean"| N8
N3 -->|"Z->A"| N6
N6 -->|"Result->Boolean"| N9
N8 -->|"Boolean->Boolean"| N11
N9 -->|"Boolean->Boolean"| N11
N7 -->|"Mesh->Mesh"| N10
N11 -->|"Boolean->Selection"| N13
N10 -->|"Points->Geometry"| N13
N13 -->|"Geometry->Geometry"| N15
Spiral Point Distribution
Use power and modulo to create a spiral-like displacement pattern:
with g.tree("Spiral" ) as tree:
pos = g.Position().o.position
angle = pos.x * 3.14
radius = abs (pos.y) ** 0.5
spiral_offset = g.CombineXYZ(
x= g.Math.cosine(angle) * radius,
y= g.Math.sine(angle) * radius,
z= pos.z,
)
_ = (
g.Points(500 , position= g.RandomValue.vector(min =- 1 ))
>> g.SetPosition(position= spiral_offset)
>> tree.outputs.geometry()
)
tree
graph LR
N0("Position"):::input-node
N1("Separate XYZ"):::converter-node
N2("Math<br/><small>(MULTIPLY)</small>"):::converter-node
N3("Math<br/><small>(ABSOLUTE)</small>"):::converter-node
N4("Math<br/><small>(COSINE)</small>"):::converter-node
N5("Math<br/><small>(SINE)</small>"):::converter-node
N6("Math<br/><small>(POWER)</small>"):::converter-node
N7("Random Value<br/><small>(-1,-1,-1)</small>"):::converter-node
N8("Math<br/><small>(MULTIPLY)</small>"):::converter-node
N9("Math<br/><small>(MULTIPLY)</small>"):::converter-node
N10("Points"):::geometry-node
N11("Combine XYZ"):::converter-node
N12("Set Position"):::geometry-node
N13("Group Output"):::default-node
N0 -->|"Position->Vector"| N1
N1 -->|"X->Value"| N2
N1 -->|"Y->Value"| N3
N3 -->|"Value->Value"| N6
N2 -->|"Value->Value"| N4
N4 -->|"Value->Value"| N8
N6 -->|"Value->Value"| N8
N2 -->|"Value->Value"| N5
N5 -->|"Value->Value"| N9
N6 -->|"Value->Value"| N9
N8 -->|"Value->X"| N11
N9 -->|"Value->Y"| N11
N7 -->|"Value->Position"| N10
N11 -->|"Vector->Position"| N12
N10 -->|"Points->Geometry"| N12
N12 -->|"Geometry->Geometry"| N13
N1 -->|"Z->Z"| N11
Operator Reference
Add
a + b
Math / VectorMath / IntegerMath
Subtract
a - b
Math / VectorMath / IntegerMath
Multiply
a * b
Math / VectorMath / IntegerMath
Divide
a / b
Math / VectorMath / IntegerMath
Power
a ** b
Math / VectorMath / IntegerMath
Modulo
a % b
Math / VectorMath / IntegerMath
Floor Divide
a // b
IntegerMath (int) or Divide+Floor (float/vector)
Negate
-a
IntegerMath.negate / Math.multiply(a, -1) / VectorMath.scale(a, -1)
Absolute
abs(a)
Math / VectorMath / IntegerMath .absolute
Less Than
a < b
Compare
Greater Than
a > b
Compare
Less Equal
a <= b
Compare
Greater Equal
a >= b
Compare
And
a & b
BooleanMath
Or
a \| b
BooleanMath
Xor
a ^ b
BooleanMath
Not
~a
BooleanMath
Matrix Multiply
a @ b
MultiplyMatrices
Chain
a >> b
Links output to input