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 TreeBuilder("ArithmeticDemo" ) as tree:
with tree.outputs:
out = s.SocketGeometry()
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("RandomValue<br/><small>(-1,-1,-1)</small>"):::converter-node
N5("InputPosition"):::input-node
N6("Math<br/><small>(FLOOR)</small>"):::converter-node
N7("Points"):::geometry-node
N8("VectorMath<br/><small>(MULTIPLY)</small>"):::vector-node
N9("SetPosition"):::geometry-node
N10("NodeGroupOutput"):::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->Vector"| N8
N8 -->|"Vector->Offset"| N9
N7 -->|"Points->Geometry"| N9
N9 -->|"Geometry->Geometry"| N10
classDef geometry-node fill:#e8f5f1,stroke:#3a7c49,stroke-width:2px
classDef converter-node fill:#e6f1f7,stroke:#246283,stroke-width:2px
classDef vector-node fill:#e9e9f5,stroke:#3C3C83,stroke-width:2px
classDef texture-node fill:#fef3e6,stroke:#E66800,stroke-width:2px
classDef shader-node fill:#fef0eb,stroke:#e67c52,stroke-width:2px
classDef input-node fill:#f1f8ed,stroke:#7fb069,stroke-width:2px
classDef output-node fill:#faf0ed,stroke:#c97659,stroke-width:2px
classDef default-node fill:#f0f0f0,stroke:#5a5a5a,stroke-width:2px
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 TreeBuilder("TypeDispatch" ) as tree:
with tree.outputs:
out = s.SocketGeometry()
# 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("InputPosition"):::input-node
N1("MeshGrid"):::geometry-node
N2("VectorMath<br/><small>(POWER)</small><br/><small>(2,2,2)</small>"):::vector-node
N3("VectorMath<br/><small>(MODULO)</small>"):::vector-node
N4("SetPosition"):::geometry-node
N5("InputIndex"):::input-node
N6("IntegerMath<br/><small>(DIVIDE_FLOOR)</small>"):::converter-node
N7("IntegerMath<br/><small>(MODULO)</small>"):::converter-node
N8("NodeGroupOutput"):::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
classDef geometry-node fill:#e8f5f1,stroke:#3a7c49,stroke-width:2px
classDef converter-node fill:#e6f1f7,stroke:#246283,stroke-width:2px
classDef vector-node fill:#e9e9f5,stroke:#3C3C83,stroke-width:2px
classDef texture-node fill:#fef3e6,stroke:#E66800,stroke-width:2px
classDef shader-node fill:#fef0eb,stroke:#e67c52,stroke-width:2px
classDef input-node fill:#f1f8ed,stroke:#7fb069,stroke-width:2px
classDef output-node fill:#faf0ed,stroke:#c97659,stroke-width:2px
classDef default-node fill:#f0f0f0,stroke:#5a5a5a,stroke-width:2px
Negation and Absolute Value
The unary - and abs() operators work with all numeric types:
with TreeBuilder("UnaryOps" ) as tree:
with tree.outputs:
out = s.SocketGeometry()
pos = g.Position()
mirrored = - pos # VectorMath.scale(pos, -1)
magnitude = abs (mirrored) # VectorMath.absolute
_ = g.Cube() >> g.SetPosition(offset= magnitude) >> out
tree
graph LR
N0("InputPosition"):::input-node
N1("VectorMath<br/><small>(SCALE)</small><br/><small>×-1</small>"):::vector-node
N2("MeshCube"):::geometry-node
N3("VectorMath<br/><small>(ABSOLUTE)</small>"):::vector-node
N4("SetPosition"):::geometry-node
N5("NodeGroupOutput"):::default-node
N0 -->|"Position->Vector"| N1
N1 -->|"Vector->Vector"| N3
N3 -->|"Vector->Offset"| N4
N2 -->|"Mesh->Geometry"| N4
N4 -->|"Geometry->Geometry"| N5
classDef geometry-node fill:#e8f5f1,stroke:#3a7c49,stroke-width:2px
classDef converter-node fill:#e6f1f7,stroke:#246283,stroke-width:2px
classDef vector-node fill:#e9e9f5,stroke:#3C3C83,stroke-width:2px
classDef texture-node fill:#fef3e6,stroke:#E66800,stroke-width:2px
classDef shader-node fill:#fef0eb,stroke:#e67c52,stroke-width:2px
classDef input-node fill:#f1f8ed,stroke:#7fb069,stroke-width:2px
classDef output-node fill:#faf0ed,stroke:#c97659,stroke-width:2px
classDef default-node fill:#f0f0f0,stroke:#5a5a5a,stroke-width:2px
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 TreeBuilder("CompareDemo" ) as tree:
with tree.outputs:
out = s.SocketGeometry()
pos = g.Position()
z = g.SeparateXYZ(pos).o_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("InputPosition"):::input-node
N1("SeparateXYZ"):::converter-node
N2("MeshCube<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("SetPosition<br/><small>+(0,0,1)</small>"):::geometry-node
N6("NodeGroupOutput"):::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
classDef geometry-node fill:#e8f5f1,stroke:#3a7c49,stroke-width:2px
classDef converter-node fill:#e6f1f7,stroke:#246283,stroke-width:2px
classDef vector-node fill:#e9e9f5,stroke:#3C3C83,stroke-width:2px
classDef texture-node fill:#fef3e6,stroke:#E66800,stroke-width:2px
classDef shader-node fill:#fef0eb,stroke:#e67c52,stroke-width:2px
classDef input-node fill:#f1f8ed,stroke:#7fb069,stroke-width:2px
classDef output-node fill:#faf0ed,stroke:#c97659,stroke-width:2px
classDef default-node fill:#f0f0f0,stroke:#5a5a5a,stroke-width:2px
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 TreeBuilder("BooleanDemo" ) as tree:
with tree.outputs:
out = s.SocketGeometry()
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("InputPosition"):::input-node
N1("SeparateXYZ"):::converter-node
N2("MeshCube<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("MeshToPoints"):::geometry-node
N6("BooleanMath<br/><small>(AND)</small>"):::converter-node
N7("SetPosition<br/><small>+(1,0,0)</small>"):::geometry-node
N8("NodeGroupOutput"):::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
classDef geometry-node fill:#e8f5f1,stroke:#3a7c49,stroke-width:2px
classDef converter-node fill:#e6f1f7,stroke:#246283,stroke-width:2px
classDef vector-node fill:#e9e9f5,stroke:#3C3C83,stroke-width:2px
classDef texture-node fill:#fef3e6,stroke:#E66800,stroke-width:2px
classDef shader-node fill:#fef0eb,stroke:#e67c52,stroke-width:2px
classDef input-node fill:#f1f8ed,stroke:#7fb069,stroke-width:2px
classDef output-node fill:#faf0ed,stroke:#c97659,stroke-width:2px
classDef default-node fill:#f0f0f0,stroke:#5a5a5a,stroke-width:2px
The ~ operator inverts a boolean:
with TreeBuilder("InvertDemo" ) as tree:
with tree.outputs:
out = s.SocketGeometry()
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 ))
>> out
)
tree
graph LR
N0("InputIndex"):::input-node
N1("IntegerMath<br/><small>(MODULO)</small>"):::converter-node
N2("MeshLine<br/><small>+(0,0,1)</small>"):::geometry-node
N3("Compare<br/><small>(GREATER_THAN)</small>"):::converter-node
N4("MeshToPoints"):::geometry-node
N5("BooleanMath<br/><small>(NOT)</small>"):::converter-node
N6("SetPosition<br/><small>+(0,0,0.5)</small>"):::geometry-node
N7("NodeGroupOutput"):::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
classDef geometry-node fill:#e8f5f1,stroke:#3a7c49,stroke-width:2px
classDef converter-node fill:#e6f1f7,stroke:#246283,stroke-width:2px
classDef vector-node fill:#e9e9f5,stroke:#3C3C83,stroke-width:2px
classDef texture-node fill:#fef3e6,stroke:#E66800,stroke-width:2px
classDef shader-node fill:#fef0eb,stroke:#e67c52,stroke-width:2px
classDef input-node fill:#f1f8ed,stroke:#7fb069,stroke-width:2px
classDef output-node fill:#faf0ed,stroke:#c97659,stroke-width:2px
classDef default-node fill:#f0f0f0,stroke:#5a5a5a,stroke-width:2px
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 TreeBuilder("MatmulDemo" ) as tree:
with tree.outputs:
out = s.SocketGeometry()
rotate = g.CombineTransform(rotation= (0 , 45 , 0 ))
translate = g.CombineTransform(translation= (2 , 0 , 0 ))
_ = (
g.Cube()
>> g.SetPosition(position= rotate @ translate @ g.Position())
>> out
)
tree
graph LR
N0("CombineTransform<br/><small>(0,4e+01,0)</small>"):::converter-node
N1("CombineTransform<br/><small>(2,0,0)</small>"):::converter-node
N2("InputPosition"):::input-node
N3("MatrixMultiply"):::converter-node
N4("MeshCube"):::geometry-node
N5("TransformPoint"):::converter-node
N6("SetPosition"):::geometry-node
N7("NodeGroupOutput"):::default-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
N6 -->|"Geometry->Geometry"| N7
classDef geometry-node fill:#e8f5f1,stroke:#3a7c49,stroke-width:2px
classDef converter-node fill:#e6f1f7,stroke:#246283,stroke-width:2px
classDef vector-node fill:#e9e9f5,stroke:#3C3C83,stroke-width:2px
classDef texture-node fill:#fef3e6,stroke:#E66800,stroke-width:2px
classDef shader-node fill:#fef0eb,stroke:#e67c52,stroke-width:2px
classDef input-node fill:#f1f8ed,stroke:#7fb069,stroke-width:2px
classDef output-node fill:#faf0ed,stroke:#c97659,stroke-width:2px
classDef default-node fill:#f0f0f0,stroke:#5a5a5a,stroke-width:2px
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 TreeBuilder("Checkerboard" ) as tree:
with tree.outputs:
out = s.SocketGeometry()
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 ))
>> out
)
tree
graph LR
N0("InputIndex"):::input-node
N1("IntegerMath<br/><small>(DIVIDE_FLOOR)</small>"):::converter-node
N2("IntegerMath<br/><small>(MODULO)</small>"):::converter-node
N3("Math<br/><small>(ADD)</small>"):::converter-node
N4("Math<br/><small>(FLOORED_MODULO)</small>"):::converter-node
N5("MeshGrid"):::geometry-node
N6("Compare<br/><small>(GREATER_THAN)</small>"):::converter-node
N7("SetPosition<br/><small>+(0,0,0.5)</small>"):::geometry-node
N8("NodeGroupOutput"):::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
classDef geometry-node fill:#e8f5f1,stroke:#3a7c49,stroke-width:2px
classDef converter-node fill:#e6f1f7,stroke:#246283,stroke-width:2px
classDef vector-node fill:#e9e9f5,stroke:#3C3C83,stroke-width:2px
classDef texture-node fill:#fef3e6,stroke:#E66800,stroke-width:2px
classDef shader-node fill:#fef0eb,stroke:#e67c52,stroke-width:2px
classDef input-node fill:#f1f8ed,stroke:#7fb069,stroke-width:2px
classDef output-node fill:#faf0ed,stroke:#c97659,stroke-width:2px
classDef default-node fill:#f0f0f0,stroke:#5a5a5a,stroke-width:2px
Layered Selection
Divide space into layers using floor division, then select specific layers:
with TreeBuilder("Layers" ) as tree:
with tree.outputs:
out = s.SocketGeometry()
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 ))
>> out
)
tree
graph LR
N0("InputPosition"):::input-node
N1("VectorMath<br/><small>(DIVIDE)</small>"):::vector-node
N2("VectorMath<br/><small>(FLOOR)</small>"):::vector-node
N3("SeparateXYZ"):::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("MeshCube<br/><small>(5,5,5)</small>"):::geometry-node
N8("BooleanMath<br/><small>(AND)</small>"):::converter-node
N9("BooleanMath<br/><small>(NOT)</small>"):::converter-node
N10("MeshToPoints"):::geometry-node
N11("BooleanMath<br/><small>(AND)</small>"):::converter-node
N12("InputPosition"):::input-node
N13("SetPosition<br/><small>+(1,0,0)</small>"):::geometry-node
N14("SeparateXYZ"):::converter-node
N15("NodeGroupOutput"):::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
classDef geometry-node fill:#e8f5f1,stroke:#3a7c49,stroke-width:2px
classDef converter-node fill:#e6f1f7,stroke:#246283,stroke-width:2px
classDef vector-node fill:#e9e9f5,stroke:#3C3C83,stroke-width:2px
classDef texture-node fill:#fef3e6,stroke:#E66800,stroke-width:2px
classDef shader-node fill:#fef0eb,stroke:#e67c52,stroke-width:2px
classDef input-node fill:#f1f8ed,stroke:#7fb069,stroke-width:2px
classDef output-node fill:#faf0ed,stroke:#c97659,stroke-width:2px
classDef default-node fill:#f0f0f0,stroke:#5a5a5a,stroke-width:2px
Spiral Point Distribution
Use power and modulo to create a spiral-like displacement pattern:
with TreeBuilder("Spiral" ) as tree:
with tree.outputs:
out = s.SocketGeometry()
pos = g.Position()
angle = g.SeparateXYZ(pos).o_x * 3.14
radius = abs (g.SeparateXYZ(pos).o_y) ** 0.5
spiral_offset = g.CombineXYZ(
x= g.Math.cosine(angle) * radius,
y= g.Math.sine(angle) * radius,
z= g.SeparateXYZ(pos).o_z,
)
_ = (
g.Points(500 , position= g.RandomValue.vector(min =- 1 ))
>> g.SetPosition(position= spiral_offset)
>> out
)
tree
graph LR
N0("InputPosition"):::input-node
N1("NodeReroute"):::default-node
N2("SeparateXYZ"):::converter-node
N3("SeparateXYZ"):::converter-node
N4("Math<br/><small>(MULTIPLY)</small>"):::converter-node
N5("Math<br/><small>(ABSOLUTE)</small>"):::converter-node
N6("Math<br/><small>(COSINE)</small>"):::converter-node
N7("Math<br/><small>(SINE)</small>"):::converter-node
N8("Math<br/><small>(POWER)</small>"):::converter-node
N9("RandomValue<br/><small>(-1,-1,-1)</small>"):::converter-node
N10("Math<br/><small>(MULTIPLY)</small>"):::converter-node
N11("Math<br/><small>(MULTIPLY)</small>"):::converter-node
N12("SeparateXYZ"):::converter-node
N13("Points"):::geometry-node
N14("CombineXYZ"):::converter-node
N15("SetPosition"):::geometry-node
N16("NodeGroupOutput"):::default-node
N0 -->|"Position->Vector"| N2
N2 -->|"X->Value"| N4
N0 -->|"Position->Vector"| N3
N3 -->|"Y->Value"| N5
N5 -->|"Value->Value"| N8
N4 -->|"Value->Value"| N6
N6 -->|"Value->Value"| N10
N8 -->|"Value->Value"| N10
N4 -->|"Value->Value"| N7
N7 -->|"Value->Value"| N11
N8 -->|"Value->Value"| N11
N10 -->|"Value->X"| N14
N11 -->|"Value->Y"| N14
N12 -->|"Z->Z"| N14
N9 -->|"Value->Position"| N13
N14 -->|"Vector->Position"| N15
N13 -->|"Points->Geometry"| N15
N15 -->|"Geometry->Geometry"| N16
N0 -->|"Position->Input"| N1
N1 -->|"Output->Vector"| N12
classDef geometry-node fill:#e8f5f1,stroke:#3a7c49,stroke-width:2px
classDef converter-node fill:#e6f1f7,stroke:#246283,stroke-width:2px
classDef vector-node fill:#e9e9f5,stroke:#3C3C83,stroke-width:2px
classDef texture-node fill:#fef3e6,stroke:#E66800,stroke-width:2px
classDef shader-node fill:#fef0eb,stroke:#e67c52,stroke-width:2px
classDef input-node fill:#f1f8ed,stroke:#7fb069,stroke-width:2px
classDef output-node fill:#faf0ed,stroke:#c97659,stroke-width:2px
classDef default-node fill:#f0f0f0,stroke:#5a5a5a,stroke-width:2px
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