nodebpy 0.1.0__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.1.0/PKG-INFO ADDED
@@ -0,0 +1,160 @@
1
+ Metadata-Version: 2.3
2
+ Name: nodebpy
3
+ Version: 0.1.0
4
+ Summary: Build nodes in Blender with code
5
+ Author: Brady Johnston
6
+ Author-email: Brady Johnston <brady.johnston@me.com>
7
+ Requires-Dist: arrangebpy>=0.1.0
8
+ Requires-Dist: jsondiff>=2.2.1
9
+ Requires-Dist: syrupy>=5.0.0
10
+ Requires-Dist: tree-clipper>=0.1.1
11
+ Requires-Dist: bpy>=5.0.0 ; extra == 'bpy'
12
+ Requires-Dist: fake-bpy-module>=20260113 ; extra == 'dev'
13
+ Requires-Dist: pytest>=9.0.2 ; extra == 'dev'
14
+ Requires-Dist: pytest-cov>=7.0.0 ; extra == 'dev'
15
+ Requires-Dist: quarto-cli>=1.8.26 ; extra == 'dev'
16
+ Requires-Dist: quartodoc>=0.11.1 ; extra == 'dev'
17
+ Requires-Dist: ruff>=0.14.11 ; extra == 'dev'
18
+ Requires-Dist: ipython>=8.0.0 ; extra == 'jupyter'
19
+ Requires-Python: >=3.11
20
+ Provides-Extra: bpy
21
+ Provides-Extra: dev
22
+ Provides-Extra: jupyter
23
+ Description-Content-Type: text/markdown
24
+
25
+
26
+
27
+ # nodebpy
28
+
29
+ [![Run
30
+ Tests](https://github.com/BradyAJohnston/nodebpy/actions/workflows/tests.yml/badge.svg)](https://github.com/BradyAJohnston/nodebpy/actions/workflows/tests.yml)
31
+ [![](https://codecov.io/gh/BradyAJohnston/nodebpy/graph/badge.svg?token=buThDQZUED)](https://codecov.io/gh/BradyAJohnston/nodebpy)
32
+
33
+ A package to help build node trees in blender more elegantly with python
34
+ code.
35
+
36
+ ## The Design Idea
37
+
38
+ Other projects have attempted similar but none quite handled the API how
39
+ I felt it should be done. Notable existing projects are
40
+ [geometry-script](https://github.com/carson-katri/geometry-script),
41
+ [geonodes](https://github.com/al1brn/geonodes),
42
+ [NodeToPython](https://github.com/BrendanParmer/NodeToPython).
43
+
44
+ Other projects implement chaining of nodes mostly as dot methos of nodes
45
+ to chain them (`InstanceOnPoints().set_position()`). This has the
46
+ potential to crowd the API for individual nodes and easy chaining is
47
+ instead approached via overriding the `>>` operator.
48
+
49
+ ### Chain Nodes with `>>`
50
+
51
+ By default the operator attempts to link the first output of the
52
+ previous node with the first input of the next. You can override this
53
+ behaviour by being explicit with the socket you are passing out
54
+ (`AccumulateField().o_total`) or using the `...` for the inputs into the
55
+ next node. The dots can appear at multiple locations and each input will
56
+ be linked to the previous node via the inferred or specified socket.
57
+
58
+ # Example Node Tree
59
+
60
+ ``` python
61
+ from nodebpy import TreeBuilder, nodes as n, sockets as s
62
+
63
+ with TreeBuilder("AnotherTree") as tree:
64
+ with tree.inputs:
65
+ count = s.SocketInt("Count", 10)
66
+ with tree.outputs:
67
+ instances = s.SocketGeometry("Instances")
68
+
69
+ rotation = (
70
+ n.RandomValue.vector(min=(-1, -1, -1), seed=2)
71
+ >> n.AlignRotationToVector()
72
+ >> n.RotateRotation(
73
+ rotate_by=n.AxisAngleToRotation(angle=0.3), rotation_space="LOCAL"
74
+ )
75
+ )
76
+
77
+ _ = (
78
+ count
79
+ >> n.Points(position=n.RandomValue.vector(min=(-1, -1, -1)))
80
+ >> n.InstanceOnPoints(instance=n.Cube(), rotation=rotation)
81
+ >> n.SetPosition(
82
+ position=n.Position() * 2.0 + (0, 0.2, 0.3),
83
+ offset=(0, 0, 0.1),
84
+ )
85
+ >> n.RealizeInstances()
86
+ >> n.InstanceOnPoints(n.Cube(), instance=...)
87
+ >> instances
88
+ )
89
+
90
+ tree
91
+ ```
92
+
93
+ ``` mermaid
94
+ graph LR
95
+ N0("NodeGroupInput"):::default-node
96
+ N1("RandomValue<br/><small>(-1,-1,-1) seed:2</small>"):::converter-node
97
+ N2("RandomValue<br/><small>(-1,-1,-1) seed:1</small>"):::converter-node
98
+ N3("AlignRotationToVector<br/><small>(0,0,1)</small>"):::converter-node
99
+ N4("AxisAngleToRotation<br/><small>(0,0,1)</small>"):::converter-node
100
+ N5("InputPosition"):::input-node
101
+ N6("Points"):::geometry-node
102
+ N7("MeshCube"):::geometry-node
103
+ N8("RotateRotation"):::converter-node
104
+ N9("VectorMath<br/><small>×2</small>"):::vector-node
105
+ N10("InstanceOnPoints"):::geometry-node
106
+ N11("VectorMath<br/><small>(0,0.2,0.3)</small>"):::vector-node
107
+ N12("SetPosition<br/><small>+(0,0,0.1)</small>"):::geometry-node
108
+ N13("MeshCube"):::geometry-node
109
+ N14("RealizeInstances"):::geometry-node
110
+ N15("InstanceOnPoints"):::geometry-node
111
+ N16("NodeGroupOutput"):::default-node
112
+ N1 -->|"Value>>Rotation"| N3
113
+ N4 -->|"Rotation>>Rotate By"| N8
114
+ N3 -->|"Rotation>>Rotation"| N8
115
+ N2 -->|"Value>>Position"| N6
116
+ N0 -->|"Count>>Count"| N6
117
+ N7 -->|"Mesh>>Instance"| N10
118
+ N8 -->|"Rotation>>Rotation"| N10
119
+ N6 -->|"Points>>Points"| N10
120
+ N5 -->|"Position>>Vector"| N9
121
+ N9 -->|"Vector>>Vector"| N11
122
+ N11 -->|"Vector>>Position"| N12
123
+ N10 -->|"Instances>>Geometry"| N12
124
+ N12 -->|"Geometry>>Geometry"| N14
125
+ N13 -->|"Mesh>>Points"| N15
126
+ N14 -->|"Geometry>>Instance"| N15
127
+ N15 -->|"Instances>>Instances"| N16
128
+
129
+ classDef geometry-node fill:#e8f5f1,stroke:#3a7c49,stroke-width:2px
130
+ classDef converter-node fill:#e6f1f7,stroke:#246283,stroke-width:2px
131
+ classDef vector-node fill:#e9e9f5,stroke:#3C3C83,stroke-width:2px
132
+ classDef texture-node fill:#fef3e6,stroke:#E66800,stroke-width:2px
133
+ classDef shader-node fill:#fef0eb,stroke:#e67c52,stroke-width:2px
134
+ classDef input-node fill:#f1f8ed,stroke:#7fb069,stroke-width:2px
135
+ classDef output-node fill:#faf0ed,stroke:#c97659,stroke-width:2px
136
+ classDef default-node fill:#f0f0f0,stroke:#5a5a5a,stroke-width:2px
137
+ ```
138
+
139
+ ![](docs/images/paste-2.png)
140
+
141
+ # Design Considerations
142
+
143
+ Whenever possible, support IDE auto-complete and have useful types. We
144
+ should know as much ahead of time as possible if our network will
145
+ actually build.
146
+
147
+ - Stick as closely to Geometry Nodes naming as possible
148
+ - `RandomValue` creates a random value node
149
+ - `RandomValue.vector()` creates it set to `"VECTOR"` data type and
150
+ provides arguments for IDE auto-complete
151
+ - Inputs and outputs from a node are prefixed with `i_*` and `o_`:
152
+ - `AccumulateField().o_total` returns the output `Total` socket
153
+ - `AccumulateField().i_value` returns the input `Value` socket
154
+ - If inputs are subject to change depending on enums, provide separate
155
+ constructor methods that provide related inputs as arguments. There
156
+ should be no guessing involved and IDEs should provide documentation
157
+ for what is required:
158
+ - `TransformGeometry.matrix(CombineTrasnsform(translation=(0, 0, 1))`
159
+ - `TransformGeoemtry.components(translation=(0, 0, 1))`
160
+ - `TransformGeometry(translation=(0, 0, 1))`
@@ -0,0 +1,136 @@
1
+
2
+
3
+ # nodebpy
4
+
5
+ [![Run
6
+ Tests](https://github.com/BradyAJohnston/nodebpy/actions/workflows/tests.yml/badge.svg)](https://github.com/BradyAJohnston/nodebpy/actions/workflows/tests.yml)
7
+ [![](https://codecov.io/gh/BradyAJohnston/nodebpy/graph/badge.svg?token=buThDQZUED)](https://codecov.io/gh/BradyAJohnston/nodebpy)
8
+
9
+ A package to help build node trees in blender more elegantly with python
10
+ code.
11
+
12
+ ## The Design Idea
13
+
14
+ Other projects have attempted similar but none quite handled the API how
15
+ I felt it should be done. Notable existing projects are
16
+ [geometry-script](https://github.com/carson-katri/geometry-script),
17
+ [geonodes](https://github.com/al1brn/geonodes),
18
+ [NodeToPython](https://github.com/BrendanParmer/NodeToPython).
19
+
20
+ Other projects implement chaining of nodes mostly as dot methos of nodes
21
+ to chain them (`InstanceOnPoints().set_position()`). This has the
22
+ potential to crowd the API for individual nodes and easy chaining is
23
+ instead approached via overriding the `>>` operator.
24
+
25
+ ### Chain Nodes with `>>`
26
+
27
+ By default the operator attempts to link the first output of the
28
+ previous node with the first input of the next. You can override this
29
+ behaviour by being explicit with the socket you are passing out
30
+ (`AccumulateField().o_total`) or using the `...` for the inputs into the
31
+ next node. The dots can appear at multiple locations and each input will
32
+ be linked to the previous node via the inferred or specified socket.
33
+
34
+ # Example Node Tree
35
+
36
+ ``` python
37
+ from nodebpy import TreeBuilder, nodes as n, sockets as s
38
+
39
+ with TreeBuilder("AnotherTree") as tree:
40
+ with tree.inputs:
41
+ count = s.SocketInt("Count", 10)
42
+ with tree.outputs:
43
+ instances = s.SocketGeometry("Instances")
44
+
45
+ rotation = (
46
+ n.RandomValue.vector(min=(-1, -1, -1), seed=2)
47
+ >> n.AlignRotationToVector()
48
+ >> n.RotateRotation(
49
+ rotate_by=n.AxisAngleToRotation(angle=0.3), rotation_space="LOCAL"
50
+ )
51
+ )
52
+
53
+ _ = (
54
+ count
55
+ >> n.Points(position=n.RandomValue.vector(min=(-1, -1, -1)))
56
+ >> n.InstanceOnPoints(instance=n.Cube(), rotation=rotation)
57
+ >> n.SetPosition(
58
+ position=n.Position() * 2.0 + (0, 0.2, 0.3),
59
+ offset=(0, 0, 0.1),
60
+ )
61
+ >> n.RealizeInstances()
62
+ >> n.InstanceOnPoints(n.Cube(), instance=...)
63
+ >> instances
64
+ )
65
+
66
+ tree
67
+ ```
68
+
69
+ ``` mermaid
70
+ graph LR
71
+ N0("NodeGroupInput"):::default-node
72
+ N1("RandomValue<br/><small>(-1,-1,-1) seed:2</small>"):::converter-node
73
+ N2("RandomValue<br/><small>(-1,-1,-1) seed:1</small>"):::converter-node
74
+ N3("AlignRotationToVector<br/><small>(0,0,1)</small>"):::converter-node
75
+ N4("AxisAngleToRotation<br/><small>(0,0,1)</small>"):::converter-node
76
+ N5("InputPosition"):::input-node
77
+ N6("Points"):::geometry-node
78
+ N7("MeshCube"):::geometry-node
79
+ N8("RotateRotation"):::converter-node
80
+ N9("VectorMath<br/><small>×2</small>"):::vector-node
81
+ N10("InstanceOnPoints"):::geometry-node
82
+ N11("VectorMath<br/><small>(0,0.2,0.3)</small>"):::vector-node
83
+ N12("SetPosition<br/><small>+(0,0,0.1)</small>"):::geometry-node
84
+ N13("MeshCube"):::geometry-node
85
+ N14("RealizeInstances"):::geometry-node
86
+ N15("InstanceOnPoints"):::geometry-node
87
+ N16("NodeGroupOutput"):::default-node
88
+ N1 -->|"Value>>Rotation"| N3
89
+ N4 -->|"Rotation>>Rotate By"| N8
90
+ N3 -->|"Rotation>>Rotation"| N8
91
+ N2 -->|"Value>>Position"| N6
92
+ N0 -->|"Count>>Count"| N6
93
+ N7 -->|"Mesh>>Instance"| N10
94
+ N8 -->|"Rotation>>Rotation"| N10
95
+ N6 -->|"Points>>Points"| N10
96
+ N5 -->|"Position>>Vector"| N9
97
+ N9 -->|"Vector>>Vector"| N11
98
+ N11 -->|"Vector>>Position"| N12
99
+ N10 -->|"Instances>>Geometry"| N12
100
+ N12 -->|"Geometry>>Geometry"| N14
101
+ N13 -->|"Mesh>>Points"| N15
102
+ N14 -->|"Geometry>>Instance"| N15
103
+ N15 -->|"Instances>>Instances"| N16
104
+
105
+ classDef geometry-node fill:#e8f5f1,stroke:#3a7c49,stroke-width:2px
106
+ classDef converter-node fill:#e6f1f7,stroke:#246283,stroke-width:2px
107
+ classDef vector-node fill:#e9e9f5,stroke:#3C3C83,stroke-width:2px
108
+ classDef texture-node fill:#fef3e6,stroke:#E66800,stroke-width:2px
109
+ classDef shader-node fill:#fef0eb,stroke:#e67c52,stroke-width:2px
110
+ classDef input-node fill:#f1f8ed,stroke:#7fb069,stroke-width:2px
111
+ classDef output-node fill:#faf0ed,stroke:#c97659,stroke-width:2px
112
+ classDef default-node fill:#f0f0f0,stroke:#5a5a5a,stroke-width:2px
113
+ ```
114
+
115
+ ![](docs/images/paste-2.png)
116
+
117
+ # Design Considerations
118
+
119
+ Whenever possible, support IDE auto-complete and have useful types. We
120
+ should know as much ahead of time as possible if our network will
121
+ actually build.
122
+
123
+ - Stick as closely to Geometry Nodes naming as possible
124
+ - `RandomValue` creates a random value node
125
+ - `RandomValue.vector()` creates it set to `"VECTOR"` data type and
126
+ provides arguments for IDE auto-complete
127
+ - Inputs and outputs from a node are prefixed with `i_*` and `o_`:
128
+ - `AccumulateField().o_total` returns the output `Total` socket
129
+ - `AccumulateField().i_value` returns the input `Value` socket
130
+ - If inputs are subject to change depending on enums, provide separate
131
+ constructor methods that provide related inputs as arguments. There
132
+ should be no guessing involved and IDEs should provide documentation
133
+ for what is required:
134
+ - `TransformGeometry.matrix(CombineTrasnsform(translation=(0, 0, 1))`
135
+ - `TransformGeoemtry.components(translation=(0, 0, 1))`
136
+ - `TransformGeometry(translation=(0, 0, 1))`
@@ -0,0 +1,38 @@
1
+ [project]
2
+ name = "nodebpy"
3
+ version = "0.1.0"
4
+ description = "Build nodes in Blender with code"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "Brady Johnston", email = "brady.johnston@me.com" }
8
+ ]
9
+ requires-python = ">=3.11"
10
+ dependencies = [
11
+ "arrangebpy>=0.1.0",
12
+ "jsondiff>=2.2.1",
13
+ "syrupy>=5.0.0",
14
+ "tree-clipper>=0.1.1",
15
+ ]
16
+
17
+ [project.scripts]
18
+ nodebpy = "nodebpy:main"
19
+
20
+ [project.optional-dependencies]
21
+ bpy = [
22
+ "bpy>=5.0.0",
23
+ ]
24
+ jupyter = [
25
+ "ipython>=8.0.0",
26
+ ]
27
+ dev = [
28
+ "fake-bpy-module>=20260113",
29
+ "pytest>=9.0.2",
30
+ "pytest-cov>=7.0.0",
31
+ "quarto-cli>=1.8.26",
32
+ "quartodoc>=0.11.1",
33
+ "ruff>=0.14.11",
34
+ ]
35
+
36
+ [build-system]
37
+ requires = ["uv_build>=0.8.15,<0.9.0"]
38
+ build-backend = "uv_build"
@@ -0,0 +1,12 @@
1
+ from . import nodes, sockets, screenshot
2
+ from .builder import TreeBuilder
3
+ from .screenshot import generate_mermaid_diagram, save_mermaid_diagram
4
+
5
+ __all__ = [
6
+ "nodes",
7
+ "sockets",
8
+ "screenshot",
9
+ "TreeBuilder",
10
+ "generate_mermaid_diagram",
11
+ "save_mermaid_diagram",
12
+ ]