nodebpy 0.12.0__tar.gz → 0.14.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.12.0 → nodebpy-0.14.0}/PKG-INFO +1 -1
- {nodebpy-0.12.0 → nodebpy-0.14.0}/pyproject.toml +1 -1
- nodebpy-0.14.0/src/nodebpy/__init__.py +14 -0
- nodebpy-0.14.0/src/nodebpy/builder/__init__.py +86 -0
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/builder/_utils.py +4 -1
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/builder/accessor.py +2 -3
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/builder/mixins.py +55 -27
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/builder/node.py +38 -22
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/builder/socket.py +158 -92
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/builder/tree.py +164 -199
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/compositor/__init__.py +29 -13
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/compositor/color.py +13 -13
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/compositor/converter.py +16 -16
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/compositor/distort.py +14 -14
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/compositor/filter.py +18 -18
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/compositor/group.py +2 -2
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/compositor/input.py +207 -10
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/compositor/interface.py +2 -2
- nodebpy-0.14.0/src/nodebpy/nodes/compositor/manual.py +383 -0
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/compositor/matte.py +13 -13
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/compositor/output.py +3 -3
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/compositor/vector.py +2 -2
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/geometry/__init__.py +16 -2
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/geometry/attribute.py +5 -5
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/geometry/color.py +3 -3
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/geometry/converter.py +58 -58
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/geometry/geometry.py +93 -669
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/geometry/grid.py +37 -37
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/geometry/group.py +2 -2
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/geometry/input.py +77 -77
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/geometry/interface.py +8 -8
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/geometry/manual.py +1147 -230
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/geometry/output.py +2 -2
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/geometry/texture.py +11 -11
- nodebpy-0.14.0/src/nodebpy/nodes/geometry/utilities.py +69 -0
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/geometry/vector.py +5 -5
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/geometry/zone.py +145 -15
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/shader/__init__.py +18 -14
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/shader/color.py +5 -5
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/shader/converter.py +7 -81
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/shader/grid.py +6 -6
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/shader/group.py +2 -2
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/shader/input.py +20 -20
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/shader/manual.py +76 -14
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/shader/output.py +6 -6
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/shader/script.py +2 -2
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/shader/shader.py +21 -21
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/shader/texture.py +5 -5
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/shader/vector.py +8 -8
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/types.py +22 -4
- nodebpy-0.12.0/src/nodebpy/__init__.py +0 -23
- nodebpy-0.12.0/src/nodebpy/builder/__init__.py +0 -155
- nodebpy-0.12.0/src/nodebpy/builder/interface.py +0 -571
- nodebpy-0.12.0/src/nodebpy/nodes/compositor/manual.py +0 -30
- nodebpy-0.12.0/src/nodebpy/nodes/shader/interface.py +0 -100
- nodebpy-0.12.0/src/nodebpy/sockets.py +0 -48
- {nodebpy-0.12.0 → nodebpy-0.14.0}/README.md +0 -0
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/arrange.py +0 -0
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/builder/_registry.py +0 -0
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/diagram.py +0 -0
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/lib/nodearrange/__init__.py +0 -0
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/lib/nodearrange/arrange/graph.py +0 -0
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/lib/nodearrange/arrange/ordering.py +0 -0
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/lib/nodearrange/arrange/ranking.py +0 -0
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/lib/nodearrange/arrange/realize.py +0 -0
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/lib/nodearrange/arrange/stacking.py +0 -0
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/lib/nodearrange/arrange/structs.py +0 -0
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/lib/nodearrange/arrange/sugiyama.py +0 -0
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/lib/nodearrange/arrange/x_coords.py +0 -0
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/lib/nodearrange/arrange/y_coords.py +0 -0
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/lib/nodearrange/config.py +0 -0
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/lib/nodearrange/utils.py +0 -0
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/__init__.py +0 -0
- {nodebpy-0.12.0 → nodebpy-0.14.0}/src/nodebpy/nodes/geometry/groups.py +0 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
from ._utils import SocketError, denormalize_name, normalize_name
|
|
2
|
+
from .accessor import SocketAccessor
|
|
3
|
+
from .mixins import LinkingMixin, OperatorMixin
|
|
4
|
+
from .node import (
|
|
5
|
+
BaseNode,
|
|
6
|
+
CustomCompositorGroup,
|
|
7
|
+
CustomGeometryGroup,
|
|
8
|
+
CustomShaderGroup,
|
|
9
|
+
DynamicInputsMixin,
|
|
10
|
+
NodeGroupBuilder,
|
|
11
|
+
)
|
|
12
|
+
from .socket import (
|
|
13
|
+
BooleanSocket,
|
|
14
|
+
BundleSocket,
|
|
15
|
+
ClosureSocket,
|
|
16
|
+
CollectionSocket,
|
|
17
|
+
ColorSocket,
|
|
18
|
+
FloatSocket,
|
|
19
|
+
FontSocket,
|
|
20
|
+
GeometrySocket,
|
|
21
|
+
ImageSocket,
|
|
22
|
+
IntegerSocket,
|
|
23
|
+
MaterialSocket,
|
|
24
|
+
MatrixSocket,
|
|
25
|
+
MenuSocket,
|
|
26
|
+
ObjectSocket,
|
|
27
|
+
RotationSocket,
|
|
28
|
+
ShaderSocket,
|
|
29
|
+
Socket,
|
|
30
|
+
StringSocket,
|
|
31
|
+
VectorSocket,
|
|
32
|
+
)
|
|
33
|
+
from .tree import (
|
|
34
|
+
InputInterfaceContext,
|
|
35
|
+
MaterialBuilder,
|
|
36
|
+
OutputInterfaceContext,
|
|
37
|
+
PanelContext,
|
|
38
|
+
SocketContext,
|
|
39
|
+
TreeBuilder,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
__all__ = [
|
|
43
|
+
# Core
|
|
44
|
+
"TreeBuilder",
|
|
45
|
+
"MaterialBuilder",
|
|
46
|
+
"BaseNode",
|
|
47
|
+
"Socket",
|
|
48
|
+
"SocketAccessor",
|
|
49
|
+
# Mixins
|
|
50
|
+
"OperatorMixin",
|
|
51
|
+
"LinkingMixin",
|
|
52
|
+
"DynamicInputsMixin",
|
|
53
|
+
# Node groups
|
|
54
|
+
"NodeGroupBuilder",
|
|
55
|
+
"CustomCompositorGroup",
|
|
56
|
+
"CustomGeometryGroup",
|
|
57
|
+
"CustomShaderGroup",
|
|
58
|
+
# Runtime socket types
|
|
59
|
+
"FloatSocket",
|
|
60
|
+
"VectorSocket",
|
|
61
|
+
"ColorSocket",
|
|
62
|
+
"IntegerSocket",
|
|
63
|
+
"BooleanSocket",
|
|
64
|
+
"RotationSocket",
|
|
65
|
+
"MatrixSocket",
|
|
66
|
+
"StringSocket",
|
|
67
|
+
"MenuSocket",
|
|
68
|
+
"GeometrySocket",
|
|
69
|
+
"ObjectSocket",
|
|
70
|
+
"FontSocket",
|
|
71
|
+
"MaterialSocket",
|
|
72
|
+
"ImageSocket",
|
|
73
|
+
"CollectionSocket",
|
|
74
|
+
"BundleSocket",
|
|
75
|
+
"ClosureSocket",
|
|
76
|
+
"ShaderSocket",
|
|
77
|
+
# Tree context
|
|
78
|
+
"SocketContext",
|
|
79
|
+
"PanelContext",
|
|
80
|
+
"InputInterfaceContext",
|
|
81
|
+
"OutputInterfaceContext",
|
|
82
|
+
# Utilities
|
|
83
|
+
"SocketError",
|
|
84
|
+
"normalize_name",
|
|
85
|
+
"denormalize_name",
|
|
86
|
+
]
|
|
@@ -67,7 +67,10 @@ def _resolve_promotion(
|
|
|
67
67
|
|
|
68
68
|
if other_prec > self_prec:
|
|
69
69
|
# Other side is dominant — swap so the linker wraps the vector/higher socket
|
|
70
|
-
|
|
70
|
+
if isinstance(other, NodeSocket):
|
|
71
|
+
other_socket = other
|
|
72
|
+
else:
|
|
73
|
+
other_socket = other._default_output_socket
|
|
71
74
|
return other_socket, self_socket, not reverse
|
|
72
75
|
|
|
73
76
|
return self_socket, other, reverse
|
|
@@ -28,7 +28,7 @@ class SocketAccessor:
|
|
|
28
28
|
|
|
29
29
|
def __init__(
|
|
30
30
|
self,
|
|
31
|
-
collection: bpy.types.NodeInputs | bpy.types.NodeOutputs,
|
|
31
|
+
collection: bpy.types.NodeInputs | bpy.types.NodeOutputs | list[NodeSocket],
|
|
32
32
|
direction: Literal["input", "output"],
|
|
33
33
|
):
|
|
34
34
|
self._direction = direction
|
|
@@ -190,8 +190,7 @@ class SocketAccessor:
|
|
|
190
190
|
return len(self._items())
|
|
191
191
|
|
|
192
192
|
def __iter__(self):
|
|
193
|
-
|
|
194
|
-
return iter(self._keys())
|
|
193
|
+
return iter(self._values())
|
|
195
194
|
|
|
196
195
|
def __getattr__(self, name: str) -> "Socket":
|
|
197
196
|
"""Dynamic socket access by normalised attribute name.
|
|
@@ -11,10 +11,11 @@ from ._utils import SocketError, _resolve_promotion, _SocketLike
|
|
|
11
11
|
_RShiftT = TypeVar("_RShiftT")
|
|
12
12
|
|
|
13
13
|
if TYPE_CHECKING:
|
|
14
|
-
from ..nodes.geometry import Compare, Math
|
|
14
|
+
from ..nodes.geometry import Compare, Math, MultiplyMatrices, TransformPoint
|
|
15
15
|
from ..types import InputLinkable
|
|
16
16
|
from .node import BaseNode
|
|
17
|
-
from .socket import Socket
|
|
17
|
+
from .socket import MatrixSocket, Socket
|
|
18
|
+
from .tree import TreeBuilder
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
class OperatorMixin:
|
|
@@ -100,9 +101,12 @@ class OperatorMixin:
|
|
|
100
101
|
)
|
|
101
102
|
|
|
102
103
|
def _apply_compare_operation(self, other: Any, operation: str) -> "Math":
|
|
103
|
-
|
|
104
|
-
|
|
104
|
+
socket, other, _ = _resolve_promotion(
|
|
105
|
+
self._default_output_socket, # type: ignore[attr-defined]
|
|
106
|
+
other,
|
|
107
|
+
False,
|
|
105
108
|
)
|
|
109
|
+
return _get_socket_linker(socket)._dispatch_compare(other, operation)
|
|
106
110
|
|
|
107
111
|
def __lt__(self, other: Any) -> "Compare":
|
|
108
112
|
return self._apply_compare_operation(other, "less_than")
|
|
@@ -157,46 +161,46 @@ class OperatorMixin:
|
|
|
157
161
|
return BooleanMath.l_not(self)
|
|
158
162
|
|
|
159
163
|
@staticmethod
|
|
160
|
-
def _cast_to_matrix(value):
|
|
164
|
+
def _cast_to_matrix(value) -> MatrixSocket:
|
|
161
165
|
from ..nodes.geometry.converter import CombineMatrix
|
|
162
166
|
|
|
163
167
|
if hasattr(value, "shape") and value.shape == (4, 4):
|
|
164
|
-
return CombineMatrix(*value.ravel())
|
|
168
|
+
return CombineMatrix(*value.ravel()).o.matrix
|
|
165
169
|
else:
|
|
166
170
|
return value
|
|
167
171
|
|
|
168
|
-
def __matmul__(self, other: Any):
|
|
172
|
+
def __matmul__(self, other: Any) -> "MultiplyMatrices | TransformPoint":
|
|
169
173
|
from ..nodes.geometry.converter import MultiplyMatrices, TransformPoint
|
|
170
174
|
|
|
171
175
|
other = self._cast_to_matrix(other)
|
|
172
|
-
socket = self._default_output_socket
|
|
173
|
-
other_type = getattr(other, "type", None)
|
|
176
|
+
socket = self._default_output_socket
|
|
174
177
|
|
|
175
|
-
if socket.type == "MATRIX" and
|
|
178
|
+
if socket.type == "MATRIX" and other.type == "VECTOR":
|
|
176
179
|
return TransformPoint(other, socket)
|
|
177
180
|
|
|
178
|
-
return MultiplyMatrices(
|
|
181
|
+
return MultiplyMatrices(socket, other)
|
|
179
182
|
|
|
180
|
-
def __rmatmul__(self, other: Any):
|
|
183
|
+
def __rmatmul__(self, other: Any) -> "MultiplyMatrices | TransformPoint":
|
|
181
184
|
from ..nodes.geometry.converter import MultiplyMatrices, TransformPoint
|
|
182
185
|
|
|
183
186
|
other = self._cast_to_matrix(other)
|
|
184
|
-
socket = self._default_output_socket
|
|
185
|
-
other_type = getattr(other, "type", None)
|
|
187
|
+
socket = self._default_output_socket
|
|
186
188
|
|
|
187
|
-
if socket.type == "VECTOR" and
|
|
189
|
+
if socket.type == "VECTOR" and getattr(other, "type", None) == "MATRIX":
|
|
188
190
|
return TransformPoint(socket, other)
|
|
189
191
|
|
|
190
|
-
return MultiplyMatrices(other,
|
|
192
|
+
return MultiplyMatrices(other, socket)
|
|
191
193
|
|
|
192
194
|
|
|
193
195
|
class LinkingMixin:
|
|
194
196
|
"""Node/socket linking logic: ``>>``, ``_link``, best-socket matching.
|
|
195
197
|
|
|
196
|
-
Requires ``tree``, ``
|
|
198
|
+
Requires ``tree``, ``i``, ``o``, ``_default_output_socket``,
|
|
197
199
|
and ``_default_input_socket`` on the concrete class.
|
|
198
200
|
"""
|
|
199
201
|
|
|
202
|
+
tree: "TreeBuilder"
|
|
203
|
+
|
|
200
204
|
def _source_socket(self, node: "InputLinkable | Socket | NodeSocket") -> NodeSocket:
|
|
201
205
|
assert node
|
|
202
206
|
if isinstance(node, NodeSocket):
|
|
@@ -221,21 +225,45 @@ class LinkingMixin:
|
|
|
221
225
|
target: "BaseNode | Socket | NodeSocket | EllipsisType | LinkingMixin",
|
|
222
226
|
) -> tuple[NodeSocket, NodeSocket]:
|
|
223
227
|
"""Find the best compatible pair of sockets between two nodes/sockets."""
|
|
224
|
-
from ..
|
|
228
|
+
from ..builder.node import BaseNode
|
|
229
|
+
from ..builder.socket import Socket
|
|
230
|
+
from ..types import PREFER_FIRST_SOCKET, SOCKET_COMPATIBILITY
|
|
225
231
|
|
|
226
232
|
possible_combos = []
|
|
227
|
-
if
|
|
228
|
-
outputs = source.
|
|
233
|
+
if isinstance(source, BaseNode):
|
|
234
|
+
outputs = source.o._available
|
|
229
235
|
elif isinstance(source, NodeSocket):
|
|
230
236
|
outputs = [source]
|
|
237
|
+
elif isinstance(source, Socket):
|
|
238
|
+
outputs = [source.socket]
|
|
231
239
|
else:
|
|
232
240
|
raise TypeError(f"Cannot get outputs from {type(source)}")
|
|
233
241
|
|
|
234
|
-
if
|
|
235
|
-
inputs = target.
|
|
242
|
+
if isinstance(target, BaseNode):
|
|
243
|
+
inputs = target.i._available
|
|
236
244
|
else:
|
|
237
245
|
inputs = [target]
|
|
238
246
|
|
|
247
|
+
# NodeReroute adapts its type to whatever is linked — skip type matching
|
|
248
|
+
if getattr(getattr(target, "node", None), "bl_idname", None) == "NodeReroute":
|
|
249
|
+
if outputs and inputs:
|
|
250
|
+
return inputs[0], outputs[0]
|
|
251
|
+
|
|
252
|
+
# Try first available input first — if the output type matches it exactly,
|
|
253
|
+
# or is a "preferred" implicit conversion (e.g. float→color, vector→color),
|
|
254
|
+
# use the first socket rather than searching for a better-typed later one.
|
|
255
|
+
# This keeps float→Image working in the compositor instead of drifting to
|
|
256
|
+
# a float Factor socket that scores higher on raw compatibility.
|
|
257
|
+
# Pairs not in PREFER_FIRST_SOCKET (e.g. VALUE→BOOLEAN, VECTOR→ROTATION)
|
|
258
|
+
# fall through to the ranked search below.
|
|
259
|
+
if inputs:
|
|
260
|
+
first_input = inputs[0]
|
|
261
|
+
for output in outputs:
|
|
262
|
+
if first_input.type == output.type:
|
|
263
|
+
return first_input, output
|
|
264
|
+
if (output.type, first_input.type) in PREFER_FIRST_SOCKET:
|
|
265
|
+
return first_input, output
|
|
266
|
+
|
|
239
267
|
for output in outputs:
|
|
240
268
|
compat_sockets = SOCKET_COMPATIBILITY.get(output.type, ())
|
|
241
269
|
for input in inputs:
|
|
@@ -261,7 +289,7 @@ class LinkingMixin:
|
|
|
261
289
|
) -> NodeLink:
|
|
262
290
|
source_socket = self._source_socket(source)
|
|
263
291
|
target_socket = self._target_socket(target)
|
|
264
|
-
return self.tree.link(source_socket, target_socket)
|
|
292
|
+
return self.tree.link(source_socket, target_socket)
|
|
265
293
|
|
|
266
294
|
def _link_from(
|
|
267
295
|
self,
|
|
@@ -270,9 +298,9 @@ class LinkingMixin:
|
|
|
270
298
|
):
|
|
271
299
|
if isinstance(input, str):
|
|
272
300
|
try:
|
|
273
|
-
self._link(source, self.node.inputs[input])
|
|
301
|
+
self._link(source, self.node.inputs[input])
|
|
274
302
|
except KeyError:
|
|
275
|
-
self._link(source, self.node.inputs[self.
|
|
303
|
+
self._link(source, self.node.inputs[self.i._index(input)])
|
|
276
304
|
else:
|
|
277
305
|
self._link(source, input)
|
|
278
306
|
|
|
@@ -296,8 +324,8 @@ class LinkingMixin:
|
|
|
296
324
|
try:
|
|
297
325
|
target = other.node.inputs[name]
|
|
298
326
|
except KeyError:
|
|
299
|
-
target = other.node.inputs[other.
|
|
300
|
-
source = self.
|
|
327
|
+
target = other.node.inputs[other.i._index(name)]
|
|
328
|
+
source = self.o._best_match(target.type) if hasattr(self, "o") else self
|
|
301
329
|
else:
|
|
302
330
|
try:
|
|
303
331
|
source, target = self._find_best_socket_pair(self, other)
|
|
@@ -15,10 +15,13 @@ from typing import (
|
|
|
15
15
|
|
|
16
16
|
import bpy
|
|
17
17
|
from bpy.types import (
|
|
18
|
+
CompositorNodeGroup,
|
|
18
19
|
CompositorNodeTree,
|
|
20
|
+
GeometryNodeGroup,
|
|
19
21
|
GeometryNodeTree,
|
|
20
22
|
Node,
|
|
21
23
|
NodeSocket,
|
|
24
|
+
ShaderNodeGroup,
|
|
22
25
|
ShaderNodeTree,
|
|
23
26
|
)
|
|
24
27
|
|
|
@@ -38,7 +41,7 @@ if TYPE_CHECKING:
|
|
|
38
41
|
def _add_inputs(self, *args: Any, **kwargs: Any) -> dict[str, NodeSocket]: ...
|
|
39
42
|
|
|
40
43
|
@property
|
|
41
|
-
def
|
|
44
|
+
def i(self) -> SocketAccessor: ...
|
|
42
45
|
|
|
43
46
|
|
|
44
47
|
class BaseNode(_NodeLike, OperatorMixin, LinkingMixin):
|
|
@@ -50,7 +53,7 @@ class BaseNode(_NodeLike, OperatorMixin, LinkingMixin):
|
|
|
50
53
|
_default_output_id: str | None = None
|
|
51
54
|
_placeholder_inputs: list[str]
|
|
52
55
|
|
|
53
|
-
def __init__(self, node:
|
|
56
|
+
def __init__(self, node: Node | None = None):
|
|
54
57
|
tree = (
|
|
55
58
|
TreeBuilder._tree_contexts[-1] if len(TreeBuilder._tree_contexts) else None
|
|
56
59
|
)
|
|
@@ -81,13 +84,13 @@ class BaseNode(_NodeLike, OperatorMixin, LinkingMixin):
|
|
|
81
84
|
@property
|
|
82
85
|
def _default_input_socket(self) -> NodeSocket:
|
|
83
86
|
if self._default_input_id is not None:
|
|
84
|
-
return self.node.inputs[self.
|
|
87
|
+
return self.node.inputs[self.i._index(self._default_input_id)]
|
|
85
88
|
return self.node.inputs[0]
|
|
86
89
|
|
|
87
90
|
@property
|
|
88
91
|
def _default_output_socket(self) -> NodeSocket:
|
|
89
92
|
if self._default_output_id is not None:
|
|
90
|
-
return self.node.outputs[self.
|
|
93
|
+
return self.node.outputs[self.o._index(self._default_output_id)]
|
|
91
94
|
|
|
92
95
|
counter = 0
|
|
93
96
|
socket = self.node.outputs[counter]
|
|
@@ -97,7 +100,7 @@ class BaseNode(_NodeLike, OperatorMixin, LinkingMixin):
|
|
|
97
100
|
return socket
|
|
98
101
|
|
|
99
102
|
@classmethod
|
|
100
|
-
def _from_node(cls, node:
|
|
103
|
+
def _from_node(cls, node: Node) -> Self:
|
|
101
104
|
builder = cls()
|
|
102
105
|
builder.tree.nodes.remove(builder.node)
|
|
103
106
|
builder.node = node
|
|
@@ -112,7 +115,7 @@ class BaseNode(_NodeLike, OperatorMixin, LinkingMixin):
|
|
|
112
115
|
if link.to_node.bl_idname == cls._bl_idname:
|
|
113
116
|
return cls._from_node(link.to_node)
|
|
114
117
|
node = cls()
|
|
115
|
-
node.tree.link(socket, node.
|
|
118
|
+
node.tree.link(socket, node.i._best_match(socket.type))
|
|
116
119
|
return node
|
|
117
120
|
else:
|
|
118
121
|
if socket.links:
|
|
@@ -160,9 +163,7 @@ class BaseNode(_NodeLike, OperatorMixin, LinkingMixin):
|
|
|
160
163
|
elif isinstance(value, NodeSocket):
|
|
161
164
|
self._link_from(value, name)
|
|
162
165
|
elif isinstance(value, _NodeLike):
|
|
163
|
-
self._link_from(
|
|
164
|
-
value.outputs._best_match(self.inputs._get(name).type), name
|
|
165
|
-
)
|
|
166
|
+
self._link_from(value.o._best_match(self.i._get(name).type), name)
|
|
166
167
|
else:
|
|
167
168
|
if name in input_ids:
|
|
168
169
|
input = self.node.inputs[input_ids.index(name)]
|
|
@@ -174,14 +175,6 @@ class BaseNode(_NodeLike, OperatorMixin, LinkingMixin):
|
|
|
174
175
|
input = self.node.inputs[name.replace("_", " ").title()]
|
|
175
176
|
self._set_input_default_value(input, value)
|
|
176
177
|
|
|
177
|
-
@property
|
|
178
|
-
def outputs(self) -> SocketAccessor:
|
|
179
|
-
return SocketAccessor(self.node.outputs, "output")
|
|
180
|
-
|
|
181
|
-
@property
|
|
182
|
-
def inputs(self) -> SocketAccessor:
|
|
183
|
-
return SocketAccessor(self.node.inputs, "input")
|
|
184
|
-
|
|
185
178
|
@property
|
|
186
179
|
def o(self) -> SocketAccessor:
|
|
187
180
|
"""Output socket accessor. Subclasses narrow the return type via TYPE_CHECKING."""
|
|
@@ -222,7 +215,7 @@ class DynamicInputsMixin(ABC):
|
|
|
222
215
|
except SocketError:
|
|
223
216
|
dyn = cast("_DynamicTarget", target)
|
|
224
217
|
target_name, source_socket = list(dyn._add_inputs(source).items())[0]
|
|
225
|
-
return (source_socket, dyn.
|
|
218
|
+
return (source_socket, dyn.i[target_name].socket)
|
|
226
219
|
|
|
227
220
|
@abstractmethod
|
|
228
221
|
def _add_socket(self, name: str, *args: Any, **kwargs: Any) -> NodeSocket: ...
|
|
@@ -235,7 +228,9 @@ class DynamicInputsMixin(ABC):
|
|
|
235
228
|
items[arg._default_output_socket.name] = arg
|
|
236
229
|
items.update(kwargs)
|
|
237
230
|
for key, source in items.items():
|
|
238
|
-
socket_source, type = self._match_compatible_data(
|
|
231
|
+
socket_source, type = self._match_compatible_data(
|
|
232
|
+
source.o._available if hasattr(source, "o") else [source]
|
|
233
|
+
)
|
|
239
234
|
if type in self._type_map:
|
|
240
235
|
type = self._type_map[type]
|
|
241
236
|
socket = self._add_socket(name=key, type=type)
|
|
@@ -274,6 +269,12 @@ class NodeGroupBuilder(BaseNode, ABC, Generic[_T]):
|
|
|
274
269
|
self.node.show_options = False
|
|
275
270
|
self._establish_links(**kwargs)
|
|
276
271
|
|
|
272
|
+
@property
|
|
273
|
+
@abstractmethod
|
|
274
|
+
def node_tree(self) -> _T:
|
|
275
|
+
"""The internal node tree for this group node."""
|
|
276
|
+
...
|
|
277
|
+
|
|
277
278
|
@abstractmethod
|
|
278
279
|
def _setup_node_group(self) -> None:
|
|
279
280
|
"""Set ``self.node.node_tree`` and any node-type-specific properties.
|
|
@@ -304,7 +305,12 @@ class CustomGeometryGroup(NodeGroupBuilder[GeometryNodeTree]):
|
|
|
304
305
|
"""Node group in a Geometry Nodes tree."""
|
|
305
306
|
|
|
306
307
|
_bl_idname = "GeometryNodeGroup"
|
|
307
|
-
node:
|
|
308
|
+
node: GeometryNodeGroup
|
|
309
|
+
|
|
310
|
+
@property
|
|
311
|
+
def node_tree(self) -> GeometryNodeTree:
|
|
312
|
+
assert self.node.node_tree is not None
|
|
313
|
+
return self.node.node_tree
|
|
308
314
|
|
|
309
315
|
def _setup_node_group(self) -> None:
|
|
310
316
|
self.node.node_tree = self._get_or_create_group()
|
|
@@ -324,7 +330,12 @@ class CustomShaderGroup(NodeGroupBuilder[ShaderNodeTree]):
|
|
|
324
330
|
"""Node group in a Shader (Material) node tree."""
|
|
325
331
|
|
|
326
332
|
_bl_idname = "ShaderNodeGroup"
|
|
327
|
-
node:
|
|
333
|
+
node: ShaderNodeGroup
|
|
334
|
+
|
|
335
|
+
@property
|
|
336
|
+
def node_tree(self) -> ShaderNodeTree:
|
|
337
|
+
assert self.node.node_tree is not None
|
|
338
|
+
return self.node.node_tree
|
|
328
339
|
|
|
329
340
|
def _setup_node_group(self) -> None:
|
|
330
341
|
self.node.node_tree = self._get_or_create_group()
|
|
@@ -343,7 +354,12 @@ class CustomCompositorGroup(NodeGroupBuilder[CompositorNodeTree]):
|
|
|
343
354
|
"""Node group in a Compositor node tree."""
|
|
344
355
|
|
|
345
356
|
_bl_idname = "CompositorNodeGroup"
|
|
346
|
-
node:
|
|
357
|
+
node: CompositorNodeGroup
|
|
358
|
+
|
|
359
|
+
@property
|
|
360
|
+
def node_tree(self) -> CompositorNodeTree:
|
|
361
|
+
assert self.node.node_tree is not None
|
|
362
|
+
return self.node.node_tree
|
|
347
363
|
|
|
348
364
|
def _setup_node_group(self) -> None:
|
|
349
365
|
self.node.node_tree = self._get_or_create_group()
|