nodebpy 0.10.1__tar.gz → 0.10.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nodebpy-0.10.2/PKG-INFO +157 -0
- nodebpy-0.10.2/README.md +143 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/pyproject.toml +3 -4
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/builder/tree.py +9 -0
- nodebpy-0.10.1/PKG-INFO +0 -154
- nodebpy-0.10.1/README.md +0 -141
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/__init__.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/arrange.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/builder/__init__.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/builder/_registry.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/builder/_utils.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/builder/accessor.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/builder/interface.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/builder/mixins.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/builder/node.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/builder/socket.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/lib/nodearrange/__init__.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/lib/nodearrange/arrange/graph.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/lib/nodearrange/arrange/ordering.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/lib/nodearrange/arrange/ranking.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/lib/nodearrange/arrange/realize.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/lib/nodearrange/arrange/stacking.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/lib/nodearrange/arrange/structs.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/lib/nodearrange/arrange/sugiyama.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/lib/nodearrange/arrange/x_coords.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/lib/nodearrange/arrange/y_coords.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/lib/nodearrange/config.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/lib/nodearrange/utils.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/__init__.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/compositor/__init__.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/compositor/color.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/compositor/converter.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/compositor/distort.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/compositor/filter.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/compositor/group.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/compositor/input.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/compositor/interface.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/compositor/manual.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/compositor/matte.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/compositor/output.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/compositor/vector.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/geometry/__init__.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/geometry/attribute.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/geometry/color.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/geometry/converter.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/geometry/geometry.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/geometry/grid.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/geometry/group.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/geometry/groups.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/geometry/input.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/geometry/interface.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/geometry/manual.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/geometry/output.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/geometry/texture.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/geometry/vector.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/geometry/zone.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/shader/__init__.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/shader/color.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/shader/converter.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/shader/grid.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/shader/group.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/shader/input.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/shader/interface.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/shader/manual.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/shader/output.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/shader/script.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/shader/shader.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/shader/texture.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/nodes/shader/vector.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/screenshot.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/sockets.py +0 -0
- {nodebpy-0.10.1 → nodebpy-0.10.2}/src/nodebpy/types.py +0 -0
nodebpy-0.10.2/PKG-INFO
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: nodebpy
|
|
3
|
+
Version: 0.10.2
|
|
4
|
+
Summary: Build nodes trees in Blender more elegantly with code
|
|
5
|
+
Author: Brady Johnston
|
|
6
|
+
Author-email: Brady Johnston <brady.johnston@me.com>
|
|
7
|
+
License: GPL-3.0-or-later
|
|
8
|
+
Requires-Dist: bpy>=5.0.1 ; extra == 'bpy'
|
|
9
|
+
Requires-Dist: networkx>=3.6.1 ; extra == 'networkx'
|
|
10
|
+
Requires-Python: >=3.11
|
|
11
|
+
Provides-Extra: bpy
|
|
12
|
+
Provides-Extra: networkx
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# nodebpy
|
|
16
|
+
|
|
17
|
+
[](https://github.com/BradyAJohnston/nodebpy/actions/workflows/tests.yml)
|
|
19
|
+
[](https://codecov.io/gh/BradyAJohnston/nodebpy)
|
|
20
|
+
|
|
21
|
+
A package to build node trees in blender more elegantly with python code. Geometry Nodes, Shader Nodes and Compositor nodes are all fully supported. Look at the [documentation](https://bradyajohnston.github.io/nodebpy) for more examples.
|
|
22
|
+
|
|
23
|
+
## Creating Nodes With Code
|
|
24
|
+
|
|
25
|
+
> A text-based version of nodes should bring the convenience of writing code with IDE auto-completion, type hinting, with overall compactness and readability, while staying as close as possible to what building a node tree via the GUI feels like.
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
from nodebpy import geometry as g
|
|
29
|
+
|
|
30
|
+
with g.tree("AnotherTree", collapse=True) as tree:
|
|
31
|
+
rotation = (
|
|
32
|
+
g.RandomValue.vector(min=-1, seed=2)
|
|
33
|
+
>> g.AlignRotationToVector()
|
|
34
|
+
>> g.RotateRotation(rotate_by=g.AxisAngleToRotation(angle=0.3))
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
_ = (
|
|
38
|
+
tree.inputs.integer("Count", 10)
|
|
39
|
+
>> g.Points(position=g.RandomValue.vector(min=-1))
|
|
40
|
+
>> g.InstanceOnPoints(instance=g.Cube(), rotation=rotation)
|
|
41
|
+
>> g.SetPosition(
|
|
42
|
+
position=g.Position() * 2.0 + (0, 0.2, 0.3),
|
|
43
|
+
offset=(0, 0, 0.1),
|
|
44
|
+
)
|
|
45
|
+
>> g.RealizeInstances()
|
|
46
|
+
>> g.InstanceOnPoints(g.Cube(), instance=...)
|
|
47
|
+
>> tree.outputs.geometry("Instances")
|
|
48
|
+
)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+

|
|
52
|
+
|
|
53
|
+
Nodes are created by instantiating their classes. The node tree they are added to is determined by the context the code is executed in (while inside `with g.tree():`). The interface for the node tree is created with `tree.inputs` and `tree.inputs`, adding the sockets and returning the input or output socket for linking with other nodes.
|
|
54
|
+
|
|
55
|
+
Nodes are linked by overloading the `>>` operator, to link from the previous node on the left to the input on the right. Suitable socket pairs are automatically selected or explicitly supplied.
|
|
56
|
+
|
|
57
|
+
The layout / arrangement of the node tree is not important to Blender's evaluation of it - but an automatic layout algorithm is potentially applied upon exiting the node tree context.
|
|
58
|
+
|
|
59
|
+
### `nodebpy` and the `>>` operator
|
|
60
|
+
|
|
61
|
+
In `nodebpy` we use the `>>` operator to link from one node or socket into another.
|
|
62
|
+
This should feel and behave much like the <kbd>Alt</kbd> + <kbd>Right Click</kbd> drag between nodes in [Node Wrangler](https://docs.blender.org/manual/en/latest/addons/node/node_wrangler.html). It will use some smart logic to match the most compatible sockets between the nodes, but if you ever want to be explicit you do so. The input and output sockets of a node are accessible as properties via the `i.*` and `o.*` prefixes, or you can use the `...` placeholder to specify the particular input to be user, or pass in the previous node as a named argument.
|
|
63
|
+
|
|
64
|
+
```py
|
|
65
|
+
# vector output will be linked into the first vector input (position)
|
|
66
|
+
g.Vector() >> g.SetPosition()
|
|
67
|
+
# vector output will be linked into the offset input
|
|
68
|
+
g.Vector() >> g.SetPosition(offset=...)
|
|
69
|
+
g.SetPosition(offset=g.Vector())
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The `>>` operator will always look for the _most_ compatible sockets first (matching data types) before looking for other compatible but not identical socket data types to link.
|
|
73
|
+
If a compatible match can't be found an error _will_ be thrown.
|
|
74
|
+
|
|
75
|
+
### Contexts
|
|
76
|
+
|
|
77
|
+
What node tree or node tree interface we are currently editing is determined based on contexts.
|
|
78
|
+
Instantiating a node class outside of a tree context will throw an error. The easiest way to enter and exit a tree context is to use the `with` statement.
|
|
79
|
+
|
|
80
|
+
Each time you instantiate a node class, a new node will be created and added to the current tree.
|
|
81
|
+
If these nodes are given as arguments to other nodes or used with the `>>` operator, they will be automatically linked to the appropriate sockets.
|
|
82
|
+
|
|
83
|
+
## Nodes
|
|
84
|
+
|
|
85
|
+
Documentation for all of the nodes can be found in the [API Reference](https://bradyajohnston.github.io/nodebpy/reference/). This is mostly built automatically from the existing Blender node classes.
|
|
86
|
+
|
|
87
|
+
Every node has all of it's input sockets and enum options exposed as arguments to the class constructor. Input sockets are prefixed with `.i.*` and output sockets are prefixed with `.o.*`. Properties that aren't exposed as sockets are available as class properties. Many properties are also available as class methods for convenience when constructing.
|
|
88
|
+
|
|
89
|
+
The basic math operators also automatically add relevant nodes with their operations and values set.
|
|
90
|
+
|
|
91
|
+
```py
|
|
92
|
+
# operation is exposed as a property
|
|
93
|
+
math = g.Math(1.0, 2.0, operation='ADD')
|
|
94
|
+
math.operation = "SUBTRACT"
|
|
95
|
+
|
|
96
|
+
# operation can be chose as a class method
|
|
97
|
+
math = g.Math.subtract(1.0, 2.0)
|
|
98
|
+
math = g.Value(1.0) - 2.0
|
|
99
|
+
math = g.Math.add(1.0, 2.0)
|
|
100
|
+
math = g.Value(1.0) + 2.0
|
|
101
|
+
# the 3.0 + 2.0 is evaluated as regular python code first,
|
|
102
|
+
# so the result with be a Math.add(g.Value(1.0), 5.0)
|
|
103
|
+
math = g.Value(1.0) 3.0 + 2.0
|
|
104
|
+
|
|
105
|
+
# these are equivalent, the g.Math.multiply is automatically added
|
|
106
|
+
g.Value(1.0) * 2
|
|
107
|
+
g.Math.multiply(g.Value(1.0), 2.0)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
# Design Considerations
|
|
111
|
+
|
|
112
|
+
The top priority of `nodebpy` has been type hinting and IDE auto-complete.
|
|
113
|
+
Typical tooling that supports autoring regular python code should also support the authoring of node trees.
|
|
114
|
+
Much like [`databpy`](https://github.com/BradyAJohnston/databpy), this started as an internal tool used inside of [`molecularnodes`](https://github.com/BradyAJohnston/molecularnodes) but has since been broken out into it's own separate project.
|
|
115
|
+
This projects is robustly typed and tested, with the intent that it can be used internally for multiple other add-ons and projects.
|
|
116
|
+
|
|
117
|
+
- Node classes are named after nodes 'Random Value' -> `RandomValue()`
|
|
118
|
+
- Node 'subtypes' and methods should be accessible via dot (`.`) for easier IDE auto-complete and authoring:
|
|
119
|
+
- `RandomValue(data_type="FLOAT_VECTOR")` -> `RandomValue.vector()`
|
|
120
|
+
- Node properties are available on the top level, with inputs and outputs available behind `.i.*` and `.o.*` accessors
|
|
121
|
+
- Inputs and outputs from a node are prefixed with `i.*` and `o.*`:
|
|
122
|
+
- `AccumulateField().o.total` returns the output `Total` socket
|
|
123
|
+
- `AccumulateField().i.value` returns the input `Value` socket
|
|
124
|
+
|
|
125
|
+
## Building
|
|
126
|
+
|
|
127
|
+
Most of the code for classes are generated automatically with the `generate.py` script.
|
|
128
|
+
Some nodes are manually specified in the `src/nodebpy/nodes/geometry/manual.py` if they require special handling.
|
|
129
|
+
|
|
130
|
+
Run the build & format script as such:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
uv run generate.py && uvx ruff format && uvx ruff check --fix
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Other Projects
|
|
137
|
+
|
|
138
|
+
There are several other notable projects which have also attempted interfacing with node trees via code. They mostly fit into two categories of either storing & retrieving node trees via code (`.json` or the `bpy` API), or authoring of node trees with custom API and syntax. This project mostly fits in to the latter category.
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
### Storing node trees as code:
|
|
143
|
+
Converting node trees to the python API calls or `.json` is great to have a robust storage method, but this approach falls down in human authorability / readability. These projects are great for storage but less useful when wanting to write / generate node trees froms scratch.
|
|
144
|
+
|
|
145
|
+
- [NodeToPython](https://github.com/BrendanParmer/NodeToPython)
|
|
146
|
+
- [TreeClipper](https://github.com/Algebraic-UG/tree_clipper) (used by this project for running tests & snapshots)
|
|
147
|
+
|
|
148
|
+
### Authoring node trees with code:
|
|
149
|
+
Two previous projects have made similar approaches to authoring node trees. `geometry-script` also auto-generated most of it's type hinting, code and docs. It uses the approach of method chaining with the `.` operator, but obfuscates some of the non-linear way of building node trees.
|
|
150
|
+
|
|
151
|
+
The other project `geonodes` uses a similar context system for creating and authoring node trees, but doesn't use the same method of exposing each individual node as it's own class that `nodebpy` does.
|
|
152
|
+
|
|
153
|
+
I personally found both of their APIs to _not quite_ fit how I wanted to work, leading to the creation of `nodebpy`.
|
|
154
|
+
In comparison, this project is also the only one that is also distributed on `PyPi` and insallable via `pip` for easier use in other projects.
|
|
155
|
+
|
|
156
|
+
- [geometry-script](https://github.com/carson-katri/geometry-script),
|
|
157
|
+
- [geonodes](https://github.com/al1brn/geonodes)
|
nodebpy-0.10.2/README.md
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# nodebpy
|
|
2
|
+
|
|
3
|
+
[](https://github.com/BradyAJohnston/nodebpy/actions/workflows/tests.yml)
|
|
5
|
+
[](https://codecov.io/gh/BradyAJohnston/nodebpy)
|
|
6
|
+
|
|
7
|
+
A package to build node trees in blender more elegantly with python code. Geometry Nodes, Shader Nodes and Compositor nodes are all fully supported. Look at the [documentation](https://bradyajohnston.github.io/nodebpy) for more examples.
|
|
8
|
+
|
|
9
|
+
## Creating Nodes With Code
|
|
10
|
+
|
|
11
|
+
> A text-based version of nodes should bring the convenience of writing code with IDE auto-completion, type hinting, with overall compactness and readability, while staying as close as possible to what building a node tree via the GUI feels like.
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from nodebpy import geometry as g
|
|
15
|
+
|
|
16
|
+
with g.tree("AnotherTree", collapse=True) as tree:
|
|
17
|
+
rotation = (
|
|
18
|
+
g.RandomValue.vector(min=-1, seed=2)
|
|
19
|
+
>> g.AlignRotationToVector()
|
|
20
|
+
>> g.RotateRotation(rotate_by=g.AxisAngleToRotation(angle=0.3))
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
_ = (
|
|
24
|
+
tree.inputs.integer("Count", 10)
|
|
25
|
+
>> g.Points(position=g.RandomValue.vector(min=-1))
|
|
26
|
+
>> g.InstanceOnPoints(instance=g.Cube(), rotation=rotation)
|
|
27
|
+
>> g.SetPosition(
|
|
28
|
+
position=g.Position() * 2.0 + (0, 0.2, 0.3),
|
|
29
|
+
offset=(0, 0, 0.1),
|
|
30
|
+
)
|
|
31
|
+
>> g.RealizeInstances()
|
|
32
|
+
>> g.InstanceOnPoints(g.Cube(), instance=...)
|
|
33
|
+
>> tree.outputs.geometry("Instances")
|
|
34
|
+
)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+

|
|
38
|
+
|
|
39
|
+
Nodes are created by instantiating their classes. The node tree they are added to is determined by the context the code is executed in (while inside `with g.tree():`). The interface for the node tree is created with `tree.inputs` and `tree.inputs`, adding the sockets and returning the input or output socket for linking with other nodes.
|
|
40
|
+
|
|
41
|
+
Nodes are linked by overloading the `>>` operator, to link from the previous node on the left to the input on the right. Suitable socket pairs are automatically selected or explicitly supplied.
|
|
42
|
+
|
|
43
|
+
The layout / arrangement of the node tree is not important to Blender's evaluation of it - but an automatic layout algorithm is potentially applied upon exiting the node tree context.
|
|
44
|
+
|
|
45
|
+
### `nodebpy` and the `>>` operator
|
|
46
|
+
|
|
47
|
+
In `nodebpy` we use the `>>` operator to link from one node or socket into another.
|
|
48
|
+
This should feel and behave much like the <kbd>Alt</kbd> + <kbd>Right Click</kbd> drag between nodes in [Node Wrangler](https://docs.blender.org/manual/en/latest/addons/node/node_wrangler.html). It will use some smart logic to match the most compatible sockets between the nodes, but if you ever want to be explicit you do so. The input and output sockets of a node are accessible as properties via the `i.*` and `o.*` prefixes, or you can use the `...` placeholder to specify the particular input to be user, or pass in the previous node as a named argument.
|
|
49
|
+
|
|
50
|
+
```py
|
|
51
|
+
# vector output will be linked into the first vector input (position)
|
|
52
|
+
g.Vector() >> g.SetPosition()
|
|
53
|
+
# vector output will be linked into the offset input
|
|
54
|
+
g.Vector() >> g.SetPosition(offset=...)
|
|
55
|
+
g.SetPosition(offset=g.Vector())
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The `>>` operator will always look for the _most_ compatible sockets first (matching data types) before looking for other compatible but not identical socket data types to link.
|
|
59
|
+
If a compatible match can't be found an error _will_ be thrown.
|
|
60
|
+
|
|
61
|
+
### Contexts
|
|
62
|
+
|
|
63
|
+
What node tree or node tree interface we are currently editing is determined based on contexts.
|
|
64
|
+
Instantiating a node class outside of a tree context will throw an error. The easiest way to enter and exit a tree context is to use the `with` statement.
|
|
65
|
+
|
|
66
|
+
Each time you instantiate a node class, a new node will be created and added to the current tree.
|
|
67
|
+
If these nodes are given as arguments to other nodes or used with the `>>` operator, they will be automatically linked to the appropriate sockets.
|
|
68
|
+
|
|
69
|
+
## Nodes
|
|
70
|
+
|
|
71
|
+
Documentation for all of the nodes can be found in the [API Reference](https://bradyajohnston.github.io/nodebpy/reference/). This is mostly built automatically from the existing Blender node classes.
|
|
72
|
+
|
|
73
|
+
Every node has all of it's input sockets and enum options exposed as arguments to the class constructor. Input sockets are prefixed with `.i.*` and output sockets are prefixed with `.o.*`. Properties that aren't exposed as sockets are available as class properties. Many properties are also available as class methods for convenience when constructing.
|
|
74
|
+
|
|
75
|
+
The basic math operators also automatically add relevant nodes with their operations and values set.
|
|
76
|
+
|
|
77
|
+
```py
|
|
78
|
+
# operation is exposed as a property
|
|
79
|
+
math = g.Math(1.0, 2.0, operation='ADD')
|
|
80
|
+
math.operation = "SUBTRACT"
|
|
81
|
+
|
|
82
|
+
# operation can be chose as a class method
|
|
83
|
+
math = g.Math.subtract(1.0, 2.0)
|
|
84
|
+
math = g.Value(1.0) - 2.0
|
|
85
|
+
math = g.Math.add(1.0, 2.0)
|
|
86
|
+
math = g.Value(1.0) + 2.0
|
|
87
|
+
# the 3.0 + 2.0 is evaluated as regular python code first,
|
|
88
|
+
# so the result with be a Math.add(g.Value(1.0), 5.0)
|
|
89
|
+
math = g.Value(1.0) 3.0 + 2.0
|
|
90
|
+
|
|
91
|
+
# these are equivalent, the g.Math.multiply is automatically added
|
|
92
|
+
g.Value(1.0) * 2
|
|
93
|
+
g.Math.multiply(g.Value(1.0), 2.0)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
# Design Considerations
|
|
97
|
+
|
|
98
|
+
The top priority of `nodebpy` has been type hinting and IDE auto-complete.
|
|
99
|
+
Typical tooling that supports autoring regular python code should also support the authoring of node trees.
|
|
100
|
+
Much like [`databpy`](https://github.com/BradyAJohnston/databpy), this started as an internal tool used inside of [`molecularnodes`](https://github.com/BradyAJohnston/molecularnodes) but has since been broken out into it's own separate project.
|
|
101
|
+
This projects is robustly typed and tested, with the intent that it can be used internally for multiple other add-ons and projects.
|
|
102
|
+
|
|
103
|
+
- Node classes are named after nodes 'Random Value' -> `RandomValue()`
|
|
104
|
+
- Node 'subtypes' and methods should be accessible via dot (`.`) for easier IDE auto-complete and authoring:
|
|
105
|
+
- `RandomValue(data_type="FLOAT_VECTOR")` -> `RandomValue.vector()`
|
|
106
|
+
- Node properties are available on the top level, with inputs and outputs available behind `.i.*` and `.o.*` accessors
|
|
107
|
+
- Inputs and outputs from a node are prefixed with `i.*` and `o.*`:
|
|
108
|
+
- `AccumulateField().o.total` returns the output `Total` socket
|
|
109
|
+
- `AccumulateField().i.value` returns the input `Value` socket
|
|
110
|
+
|
|
111
|
+
## Building
|
|
112
|
+
|
|
113
|
+
Most of the code for classes are generated automatically with the `generate.py` script.
|
|
114
|
+
Some nodes are manually specified in the `src/nodebpy/nodes/geometry/manual.py` if they require special handling.
|
|
115
|
+
|
|
116
|
+
Run the build & format script as such:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
uv run generate.py && uvx ruff format && uvx ruff check --fix
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Other Projects
|
|
123
|
+
|
|
124
|
+
There are several other notable projects which have also attempted interfacing with node trees via code. They mostly fit into two categories of either storing & retrieving node trees via code (`.json` or the `bpy` API), or authoring of node trees with custom API and syntax. This project mostly fits in to the latter category.
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
### Storing node trees as code:
|
|
129
|
+
Converting node trees to the python API calls or `.json` is great to have a robust storage method, but this approach falls down in human authorability / readability. These projects are great for storage but less useful when wanting to write / generate node trees froms scratch.
|
|
130
|
+
|
|
131
|
+
- [NodeToPython](https://github.com/BrendanParmer/NodeToPython)
|
|
132
|
+
- [TreeClipper](https://github.com/Algebraic-UG/tree_clipper) (used by this project for running tests & snapshots)
|
|
133
|
+
|
|
134
|
+
### Authoring node trees with code:
|
|
135
|
+
Two previous projects have made similar approaches to authoring node trees. `geometry-script` also auto-generated most of it's type hinting, code and docs. It uses the approach of method chaining with the `.` operator, but obfuscates some of the non-linear way of building node trees.
|
|
136
|
+
|
|
137
|
+
The other project `geonodes` uses a similar context system for creating and authoring node trees, but doesn't use the same method of exposing each individual node as it's own class that `nodebpy` does.
|
|
138
|
+
|
|
139
|
+
I personally found both of their APIs to _not quite_ fit how I wanted to work, leading to the creation of `nodebpy`.
|
|
140
|
+
In comparison, this project is also the only one that is also distributed on `PyPi` and insallable via `pip` for easier use in other projects.
|
|
141
|
+
|
|
142
|
+
- [geometry-script](https://github.com/carson-katri/geometry-script),
|
|
143
|
+
- [geonodes](https://github.com/al1brn/geonodes)
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "nodebpy"
|
|
3
|
-
version = "0.10.
|
|
3
|
+
version = "0.10.2"
|
|
4
4
|
description = "Build nodes trees in Blender more elegantly with code"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [
|
|
7
7
|
{ name = "Brady Johnston", email = "brady.johnston@me.com" }
|
|
8
8
|
]
|
|
9
9
|
requires-python = ">=3.11"
|
|
10
|
-
|
|
10
|
+
license = { text = "GPL-3.0-or-later" }
|
|
11
11
|
|
|
12
12
|
[project.scripts]
|
|
13
13
|
nodebpy = "nodebpy:main"
|
|
@@ -28,12 +28,11 @@ build-backend = "uv_build"
|
|
|
28
28
|
dev = [
|
|
29
29
|
"ipython>=8.0.0",
|
|
30
30
|
"networkx>=3.6.1",
|
|
31
|
-
"numpy<2.0",
|
|
32
31
|
"fake-bpy-module>=20260113",
|
|
33
32
|
"jsondiff>=2.2.1",
|
|
34
33
|
"pytest>=9.0.2",
|
|
35
34
|
"pytest-cov>=7.0.0",
|
|
36
|
-
"quarto-cli>=1.
|
|
35
|
+
"quarto-cli>=1.9",
|
|
37
36
|
"quartodoc>=0.11.1",
|
|
38
37
|
"ruff>=0.14.11",
|
|
39
38
|
"syrupy>=5.0.0",
|
|
@@ -595,6 +595,15 @@ class SocketContext:
|
|
|
595
595
|
SocketContext._direction = None
|
|
596
596
|
SocketContext._active_context = None
|
|
597
597
|
|
|
598
|
+
def __len__(self) -> int:
|
|
599
|
+
return len(
|
|
600
|
+
list(
|
|
601
|
+
item
|
|
602
|
+
for item in self.tree.interface.items_tree
|
|
603
|
+
if item.in_out == self._direction
|
|
604
|
+
)
|
|
605
|
+
)
|
|
606
|
+
|
|
598
607
|
|
|
599
608
|
class DirectionalContext(SocketContext):
|
|
600
609
|
"""Base class for directional socket contexts"""
|
nodebpy-0.10.1/PKG-INFO
DELETED
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.3
|
|
2
|
-
Name: nodebpy
|
|
3
|
-
Version: 0.10.1
|
|
4
|
-
Summary: Build nodes trees in Blender more elegantly with code
|
|
5
|
-
Author: Brady Johnston
|
|
6
|
-
Author-email: Brady Johnston <brady.johnston@me.com>
|
|
7
|
-
Requires-Dist: bpy>=5.0.1 ; extra == 'bpy'
|
|
8
|
-
Requires-Dist: networkx>=3.6.1 ; extra == 'networkx'
|
|
9
|
-
Requires-Python: >=3.11
|
|
10
|
-
Provides-Extra: bpy
|
|
11
|
-
Provides-Extra: networkx
|
|
12
|
-
Description-Content-Type: text/markdown
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
# nodebpy
|
|
17
|
-
|
|
18
|
-
[](https://github.com/BradyAJohnston/nodebpy/actions/workflows/tests.yml)
|
|
20
|
-
[](https://codecov.io/gh/BradyAJohnston/nodebpy)
|
|
21
|
-
|
|
22
|
-
A package to help build node trees in blender more elegantly with python code.
|
|
23
|
-
|
|
24
|
-
## The Design Idea
|
|
25
|
-
|
|
26
|
-
> A text-based version of nodes should bring the convenience of writing code with IDE auto-completion, type hinting, with overall compactness and readability, while staying as close as possible to what building a node tree via the GUI feels like.
|
|
27
|
-
|
|
28
|
-
Other projects have attempted similar but none quite handled the API how
|
|
29
|
-
I felt it should be done. Notable existing projects are:
|
|
30
|
-
|
|
31
|
-
- Authoring node trees with code:
|
|
32
|
-
- [geometry-script](https://github.com/carson-katri/geometry-script),
|
|
33
|
-
- [geonodes](https://github.com/al1brn/geonodes)
|
|
34
|
-
- Storing node trees as code:
|
|
35
|
-
- [NodeToPython](https://github.com/BrendanParmer/NodeToPython).
|
|
36
|
-
- [TreeClipper](https://github.com/Algebraic-UG/tree_clipper) (used by this project for running tests & snapshots)
|
|
37
|
-
|
|
38
|
-
Previous projects mostly implement the chaining of nodes together via class methods and chaining the `.` operator (`instance_on_points().set_position()`).
|
|
39
|
-
|
|
40
|
-
This approach is limiting - not being able to explicitly specify output sockets and input sockets while chaining.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
### `nodebpy` and the `>>` operator
|
|
44
|
-
|
|
45
|
-
In `nodebpy` we use the `>>` operator to link from one node or socket into another.
|
|
46
|
-
This should feel and behave much like the <kbd>Alt</kbd> + <kbd>Right Click</kbd> drag between nodes in [Node Wrangler](https://docs.blender.org/manual/en/latest/addons/node/node_wrangler.html). It will use some smart logic to match the most compatible sockets between the nodes, but if you ever want to be explicit you do so. The input and output sockets of a node are accessible as properties via the `i_*` and `o_*` prefixes, or you can use the `...` placeholder to specify the particular input to be user, or pass in the previous node as a named argument.
|
|
47
|
-
|
|
48
|
-
```py
|
|
49
|
-
g.Vector() >> g.SetPosition(offset=...)
|
|
50
|
-
g.SetPosition(offset=g.Vector())
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
The `>>` operator will always look for the _most_ compatible sockets first (matching data types) before looking for other compatible but not identical socket data types to link.
|
|
54
|
-
If a compatible match can't be found an error _will_ be thrown.
|
|
55
|
-
|
|
56
|
-
### Contexts
|
|
57
|
-
What node tree or node tree interface we are currently editing is determined based on contexts.
|
|
58
|
-
Instantiating a node class outside of a tree context will throw an error. The easiest way to enter and exit a tree context is to use the `with` statement.
|
|
59
|
-
|
|
60
|
-
Each time you instantiate a node class, a new node will be created and added to the current tree.
|
|
61
|
-
If these nodes are given as arguments to other nodes or used with the `>>` operator, they will be automatically linked to the appropriate sockets.
|
|
62
|
-
|
|
63
|
-
Entering the `tree.inputs` and `tree.outputs` contexts will let you add new interface sockets to the node tree for usage as a modifier or as a node group in another node tree. These also return an object that can be used as arguments to other nodes or with the `>>` operator for linking.
|
|
64
|
-
|
|
65
|
-
```py
|
|
66
|
-
with TreeBuilder("MyTree") as tree:
|
|
67
|
-
points = g.Points(position=g.RandomValue.vector(min=-1))
|
|
68
|
-
with tree.outputs:
|
|
69
|
-
points >> s.SocketGeometry("New Points")
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
# Example Node Tree
|
|
73
|
-
|
|
74
|
-
The node tree below creates a integer input and geometry output to the node group. We create a `rotation` variable that can be used later on as an argument, then construct a longer chain of nodes being created and linked together. The nodes are added and linked as each node is instantiated. After we exit the tree context, the nodes are automatically arranged.
|
|
75
|
-
|
|
76
|
-
``` python
|
|
77
|
-
from nodebpy import geometry as g
|
|
78
|
-
|
|
79
|
-
with g.tree("AnotherTree", collapse=True) as tree:
|
|
80
|
-
rotation = (
|
|
81
|
-
g.RandomValue.vector(min=-1, seed=2)
|
|
82
|
-
>> g.AlignRotationToVector()
|
|
83
|
-
>> g.RotateRotation(rotate_by=g.AxisAngleToRotation(angle=0.3))
|
|
84
|
-
)
|
|
85
|
-
|
|
86
|
-
_ = (
|
|
87
|
-
tree.inputs.integer("Count", 10)
|
|
88
|
-
>> g.Points(position=g.RandomValue.vector(min=-1))
|
|
89
|
-
>> g.InstanceOnPoints(instance=g.Cube(), rotation=rotation)
|
|
90
|
-
>> g.SetPosition(
|
|
91
|
-
position=g.Position() * 2.0 + (0, 0.2, 0.3),
|
|
92
|
-
offset=(0, 0, 0.1),
|
|
93
|
-
)
|
|
94
|
-
>> g.RealizeInstances()
|
|
95
|
-
>> g.InstanceOnPoints(g.Cube(), instance=...)
|
|
96
|
-
>> tree.outputs.geometry("Instances")
|
|
97
|
-
)
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-

|
|
101
|
-
|
|
102
|
-
## Nodes
|
|
103
|
-
|
|
104
|
-
Documentation for all of the nodes can be found in the [API Reference](https://bradyajohnston.github.io/nodebpy/reference/). This is mostly built automatically from the existing Blender node classes.
|
|
105
|
-
|
|
106
|
-
Every node has all of it's input sockets and enum options exposed as arguments to the class constructor. Input sockets are prefixed with `i_` and output sockets are prefixed with `o_`. Properties that aren't exposed as sockets are available as class properties. Many properties are also available as class methods for convenience when constructing.
|
|
107
|
-
|
|
108
|
-
The basic math operators also automatically add relevant nodes with their operations and values set.
|
|
109
|
-
|
|
110
|
-
```py
|
|
111
|
-
# operation is exposed as a property
|
|
112
|
-
math = g.Math(1.0, 2.0, operation='ADD')
|
|
113
|
-
math.operation = "SUBTRACT"
|
|
114
|
-
|
|
115
|
-
# operation can be chose as a class method
|
|
116
|
-
math = g.Math.subtract(1.0, 2.0)
|
|
117
|
-
math = g.Value(1.0) - 2.0
|
|
118
|
-
math = g.Math.add(1.0, 2.0)
|
|
119
|
-
math = g.Value(1.0) + 2.0
|
|
120
|
-
|
|
121
|
-
# these are equivalent, the g.Math.multiply is automatically added
|
|
122
|
-
g.Value(1.0) * 2
|
|
123
|
-
g.Math.multiply(g.Value(1.0), 2.0)
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
# Design Considerations
|
|
127
|
-
|
|
128
|
-
Whenever possible, support IDE auto-complete and have useful types.
|
|
129
|
-
We should know as much ahead of time as possible if our network will actually build.
|
|
130
|
-
|
|
131
|
-
- Stick as closely to Geometry Nodes naming as possible
|
|
132
|
-
- `RandomValue` creates a random value node
|
|
133
|
-
- `RandomValue.vector()` creates it set to `"VECTOR"` data type and
|
|
134
|
-
provides arguments for IDE auto-complete
|
|
135
|
-
- Inputs and outputs from a node are prefixed with `i_*` and `o_`:
|
|
136
|
-
- `AccumulateField().o_total` returns the output `Total` socket
|
|
137
|
-
- `AccumulateField().i_value` returns the input `Value` socket
|
|
138
|
-
- If inputs are subject to change depending on enums, provide separate
|
|
139
|
-
constructor methods that provide related inputs as arguments. There
|
|
140
|
-
should be no guessing involved and IDEs should provide documentation
|
|
141
|
-
for what is required:
|
|
142
|
-
- `TransformGeometry.matrix(CombineTrasnsform(translation=(0, 0, 1))`
|
|
143
|
-
- `TransformGeoemtry.components(translation=(0, 0, 1))`
|
|
144
|
-
- `TransformGeometry(translation=(0, 0, 1))`
|
|
145
|
-
|
|
146
|
-
## Building
|
|
147
|
-
|
|
148
|
-
Most node classes are generated automatically with this. The nodes in
|
|
149
|
-
`nodes/manual.py` are currently manually specified due to varying
|
|
150
|
-
complexities of particular nodes (usually lergacy).
|
|
151
|
-
|
|
152
|
-
``` bash
|
|
153
|
-
uv run generate.py && ruff format && ruff check --fix
|
|
154
|
-
```
|
nodebpy-0.10.1/README.md
DELETED
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
# nodebpy
|
|
4
|
-
|
|
5
|
-
[](https://github.com/BradyAJohnston/nodebpy/actions/workflows/tests.yml)
|
|
7
|
-
[](https://codecov.io/gh/BradyAJohnston/nodebpy)
|
|
8
|
-
|
|
9
|
-
A package to help build node trees in blender more elegantly with python code.
|
|
10
|
-
|
|
11
|
-
## The Design Idea
|
|
12
|
-
|
|
13
|
-
> A text-based version of nodes should bring the convenience of writing code with IDE auto-completion, type hinting, with overall compactness and readability, while staying as close as possible to what building a node tree via the GUI feels like.
|
|
14
|
-
|
|
15
|
-
Other projects have attempted similar but none quite handled the API how
|
|
16
|
-
I felt it should be done. Notable existing projects are:
|
|
17
|
-
|
|
18
|
-
- Authoring node trees with code:
|
|
19
|
-
- [geometry-script](https://github.com/carson-katri/geometry-script),
|
|
20
|
-
- [geonodes](https://github.com/al1brn/geonodes)
|
|
21
|
-
- Storing node trees as code:
|
|
22
|
-
- [NodeToPython](https://github.com/BrendanParmer/NodeToPython).
|
|
23
|
-
- [TreeClipper](https://github.com/Algebraic-UG/tree_clipper) (used by this project for running tests & snapshots)
|
|
24
|
-
|
|
25
|
-
Previous projects mostly implement the chaining of nodes together via class methods and chaining the `.` operator (`instance_on_points().set_position()`).
|
|
26
|
-
|
|
27
|
-
This approach is limiting - not being able to explicitly specify output sockets and input sockets while chaining.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
### `nodebpy` and the `>>` operator
|
|
31
|
-
|
|
32
|
-
In `nodebpy` we use the `>>` operator to link from one node or socket into another.
|
|
33
|
-
This should feel and behave much like the <kbd>Alt</kbd> + <kbd>Right Click</kbd> drag between nodes in [Node Wrangler](https://docs.blender.org/manual/en/latest/addons/node/node_wrangler.html). It will use some smart logic to match the most compatible sockets between the nodes, but if you ever want to be explicit you do so. The input and output sockets of a node are accessible as properties via the `i_*` and `o_*` prefixes, or you can use the `...` placeholder to specify the particular input to be user, or pass in the previous node as a named argument.
|
|
34
|
-
|
|
35
|
-
```py
|
|
36
|
-
g.Vector() >> g.SetPosition(offset=...)
|
|
37
|
-
g.SetPosition(offset=g.Vector())
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
The `>>` operator will always look for the _most_ compatible sockets first (matching data types) before looking for other compatible but not identical socket data types to link.
|
|
41
|
-
If a compatible match can't be found an error _will_ be thrown.
|
|
42
|
-
|
|
43
|
-
### Contexts
|
|
44
|
-
What node tree or node tree interface we are currently editing is determined based on contexts.
|
|
45
|
-
Instantiating a node class outside of a tree context will throw an error. The easiest way to enter and exit a tree context is to use the `with` statement.
|
|
46
|
-
|
|
47
|
-
Each time you instantiate a node class, a new node will be created and added to the current tree.
|
|
48
|
-
If these nodes are given as arguments to other nodes or used with the `>>` operator, they will be automatically linked to the appropriate sockets.
|
|
49
|
-
|
|
50
|
-
Entering the `tree.inputs` and `tree.outputs` contexts will let you add new interface sockets to the node tree for usage as a modifier or as a node group in another node tree. These also return an object that can be used as arguments to other nodes or with the `>>` operator for linking.
|
|
51
|
-
|
|
52
|
-
```py
|
|
53
|
-
with TreeBuilder("MyTree") as tree:
|
|
54
|
-
points = g.Points(position=g.RandomValue.vector(min=-1))
|
|
55
|
-
with tree.outputs:
|
|
56
|
-
points >> s.SocketGeometry("New Points")
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
# Example Node Tree
|
|
60
|
-
|
|
61
|
-
The node tree below creates a integer input and geometry output to the node group. We create a `rotation` variable that can be used later on as an argument, then construct a longer chain of nodes being created and linked together. The nodes are added and linked as each node is instantiated. After we exit the tree context, the nodes are automatically arranged.
|
|
62
|
-
|
|
63
|
-
``` python
|
|
64
|
-
from nodebpy import geometry as g
|
|
65
|
-
|
|
66
|
-
with g.tree("AnotherTree", collapse=True) as tree:
|
|
67
|
-
rotation = (
|
|
68
|
-
g.RandomValue.vector(min=-1, seed=2)
|
|
69
|
-
>> g.AlignRotationToVector()
|
|
70
|
-
>> g.RotateRotation(rotate_by=g.AxisAngleToRotation(angle=0.3))
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
_ = (
|
|
74
|
-
tree.inputs.integer("Count", 10)
|
|
75
|
-
>> g.Points(position=g.RandomValue.vector(min=-1))
|
|
76
|
-
>> g.InstanceOnPoints(instance=g.Cube(), rotation=rotation)
|
|
77
|
-
>> g.SetPosition(
|
|
78
|
-
position=g.Position() * 2.0 + (0, 0.2, 0.3),
|
|
79
|
-
offset=(0, 0, 0.1),
|
|
80
|
-
)
|
|
81
|
-
>> g.RealizeInstances()
|
|
82
|
-
>> g.InstanceOnPoints(g.Cube(), instance=...)
|
|
83
|
-
>> tree.outputs.geometry("Instances")
|
|
84
|
-
)
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-

|
|
88
|
-
|
|
89
|
-
## Nodes
|
|
90
|
-
|
|
91
|
-
Documentation for all of the nodes can be found in the [API Reference](https://bradyajohnston.github.io/nodebpy/reference/). This is mostly built automatically from the existing Blender node classes.
|
|
92
|
-
|
|
93
|
-
Every node has all of it's input sockets and enum options exposed as arguments to the class constructor. Input sockets are prefixed with `i_` and output sockets are prefixed with `o_`. Properties that aren't exposed as sockets are available as class properties. Many properties are also available as class methods for convenience when constructing.
|
|
94
|
-
|
|
95
|
-
The basic math operators also automatically add relevant nodes with their operations and values set.
|
|
96
|
-
|
|
97
|
-
```py
|
|
98
|
-
# operation is exposed as a property
|
|
99
|
-
math = g.Math(1.0, 2.0, operation='ADD')
|
|
100
|
-
math.operation = "SUBTRACT"
|
|
101
|
-
|
|
102
|
-
# operation can be chose as a class method
|
|
103
|
-
math = g.Math.subtract(1.0, 2.0)
|
|
104
|
-
math = g.Value(1.0) - 2.0
|
|
105
|
-
math = g.Math.add(1.0, 2.0)
|
|
106
|
-
math = g.Value(1.0) + 2.0
|
|
107
|
-
|
|
108
|
-
# these are equivalent, the g.Math.multiply is automatically added
|
|
109
|
-
g.Value(1.0) * 2
|
|
110
|
-
g.Math.multiply(g.Value(1.0), 2.0)
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
# Design Considerations
|
|
114
|
-
|
|
115
|
-
Whenever possible, support IDE auto-complete and have useful types.
|
|
116
|
-
We should know as much ahead of time as possible if our network will actually build.
|
|
117
|
-
|
|
118
|
-
- Stick as closely to Geometry Nodes naming as possible
|
|
119
|
-
- `RandomValue` creates a random value node
|
|
120
|
-
- `RandomValue.vector()` creates it set to `"VECTOR"` data type and
|
|
121
|
-
provides arguments for IDE auto-complete
|
|
122
|
-
- Inputs and outputs from a node are prefixed with `i_*` and `o_`:
|
|
123
|
-
- `AccumulateField().o_total` returns the output `Total` socket
|
|
124
|
-
- `AccumulateField().i_value` returns the input `Value` socket
|
|
125
|
-
- If inputs are subject to change depending on enums, provide separate
|
|
126
|
-
constructor methods that provide related inputs as arguments. There
|
|
127
|
-
should be no guessing involved and IDEs should provide documentation
|
|
128
|
-
for what is required:
|
|
129
|
-
- `TransformGeometry.matrix(CombineTrasnsform(translation=(0, 0, 1))`
|
|
130
|
-
- `TransformGeoemtry.components(translation=(0, 0, 1))`
|
|
131
|
-
- `TransformGeometry(translation=(0, 0, 1))`
|
|
132
|
-
|
|
133
|
-
## Building
|
|
134
|
-
|
|
135
|
-
Most node classes are generated automatically with this. The nodes in
|
|
136
|
-
`nodes/manual.py` are currently manually specified due to varying
|
|
137
|
-
complexities of particular nodes (usually lergacy).
|
|
138
|
-
|
|
139
|
-
``` bash
|
|
140
|
-
uv run generate.py && ruff format && ruff check --fix
|
|
141
|
-
```
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|