nodebpy 0.10.2__tar.gz → 0.11.1__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 → nodebpy-0.11.1}/PKG-INFO +3 -3
- {nodebpy-0.10.2 → nodebpy-0.11.1}/pyproject.toml +3 -3
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/__init__.py +2 -2
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/builder/__init__.py +3 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/builder/accessor.py +26 -4
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/builder/mixins.py +29 -26
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/builder/node.py +30 -3
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/builder/socket.py +405 -51
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/builder/tree.py +48 -42
- nodebpy-0.11.1/src/nodebpy/diagram.py +139 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/compositor/__init__.py +18 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/compositor/converter.py +188 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/compositor/filter.py +67 -18
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/compositor/input.py +50 -116
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/compositor/manual.py +3 -1
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/geometry/__init__.py +26 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/geometry/converter.py +836 -14
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/geometry/geometry.py +201 -79
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/geometry/grid.py +864 -57
- nodebpy-0.11.1/src/nodebpy/nodes/geometry/groups.py +205 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/geometry/input.py +81 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/geometry/interface.py +11 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/geometry/manual.py +545 -419
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/geometry/vector.py +8 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/shader/__init__.py +6 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/shader/input.py +88 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/shader/manual.py +2 -1
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/shader/vector.py +20 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/types.py +4 -0
- nodebpy-0.10.2/src/nodebpy/nodes/geometry/groups.py +0 -104
- nodebpy-0.10.2/src/nodebpy/screenshot.py +0 -260
- {nodebpy-0.10.2 → nodebpy-0.11.1}/README.md +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/arrange.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/builder/_registry.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/builder/_utils.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/builder/interface.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/lib/nodearrange/__init__.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/lib/nodearrange/arrange/graph.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/lib/nodearrange/arrange/ordering.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/lib/nodearrange/arrange/ranking.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/lib/nodearrange/arrange/realize.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/lib/nodearrange/arrange/stacking.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/lib/nodearrange/arrange/structs.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/lib/nodearrange/arrange/sugiyama.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/lib/nodearrange/arrange/x_coords.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/lib/nodearrange/arrange/y_coords.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/lib/nodearrange/config.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/lib/nodearrange/utils.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/__init__.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/compositor/color.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/compositor/distort.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/compositor/group.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/compositor/interface.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/compositor/matte.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/compositor/output.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/compositor/vector.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/geometry/attribute.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/geometry/color.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/geometry/group.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/geometry/output.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/geometry/texture.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/geometry/zone.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/shader/color.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/shader/converter.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/shader/grid.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/shader/group.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/shader/interface.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/shader/output.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/shader/script.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/shader/shader.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/nodes/shader/texture.py +0 -0
- {nodebpy-0.10.2 → nodebpy-0.11.1}/src/nodebpy/sockets.py +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: nodebpy
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.11.1
|
|
4
4
|
Summary: Build nodes trees in Blender more elegantly with code
|
|
5
5
|
Author: Brady Johnston
|
|
6
6
|
Author-email: Brady Johnston <brady.johnston@me.com>
|
|
7
7
|
License: GPL-3.0-or-later
|
|
8
|
-
Requires-Dist: bpy>=5.0
|
|
8
|
+
Requires-Dist: bpy>=5.1.0 ; extra == 'bpy'
|
|
9
9
|
Requires-Dist: networkx>=3.6.1 ; extra == 'networkx'
|
|
10
|
-
Requires-Python: >=3.
|
|
10
|
+
Requires-Python: >=3.13
|
|
11
11
|
Provides-Extra: bpy
|
|
12
12
|
Provides-Extra: networkx
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "nodebpy"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.11.1"
|
|
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
|
-
requires-python = ">=3.
|
|
9
|
+
requires-python = ">=3.13"
|
|
10
10
|
license = { text = "GPL-3.0-or-later" }
|
|
11
11
|
|
|
12
12
|
[project.scripts]
|
|
@@ -17,7 +17,7 @@ networkx = [
|
|
|
17
17
|
"networkx>=3.6.1",
|
|
18
18
|
]
|
|
19
19
|
bpy = [
|
|
20
|
-
"bpy>=5.0
|
|
20
|
+
"bpy>=5.1.0",
|
|
21
21
|
]
|
|
22
22
|
|
|
23
23
|
[build-system]
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from . import nodes,
|
|
1
|
+
from . import nodes, diagram, sockets
|
|
2
2
|
from .builder import TreeBuilder, NodeGroupBuilder
|
|
3
3
|
from .nodes import compositor, geometry, shader
|
|
4
4
|
|
|
@@ -8,7 +8,7 @@ __all__ = [
|
|
|
8
8
|
"geometry",
|
|
9
9
|
"shader",
|
|
10
10
|
"sockets",
|
|
11
|
-
"
|
|
11
|
+
"diagram",
|
|
12
12
|
"TreeBuilder",
|
|
13
13
|
"NodeGroupBuilder",
|
|
14
14
|
]
|
|
@@ -35,6 +35,7 @@ from .socket import (
|
|
|
35
35
|
CollectionSocket,
|
|
36
36
|
ColorSocket,
|
|
37
37
|
FloatSocket,
|
|
38
|
+
FontSocket,
|
|
38
39
|
GeometrySocket,
|
|
39
40
|
ImageSocket,
|
|
40
41
|
IntegerSocket,
|
|
@@ -93,6 +94,7 @@ __all__ = [
|
|
|
93
94
|
"MenuSocket",
|
|
94
95
|
"GeometrySocket",
|
|
95
96
|
"ObjectSocket",
|
|
97
|
+
"FontSocket",
|
|
96
98
|
"MaterialSocket",
|
|
97
99
|
"ImageSocket",
|
|
98
100
|
"CollectionSocket",
|
|
@@ -120,6 +122,7 @@ __all__ = [
|
|
|
120
122
|
"SocketMenu",
|
|
121
123
|
"SocketObject",
|
|
122
124
|
"SocketGeometry",
|
|
125
|
+
"SocketFont",
|
|
123
126
|
"SocketCollection",
|
|
124
127
|
"SocketImage",
|
|
125
128
|
"SocketMaterial",
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import TYPE_CHECKING, Literal
|
|
3
|
+
from typing import TYPE_CHECKING, Literal, overload
|
|
4
4
|
|
|
5
5
|
import bpy
|
|
6
6
|
from bpy.types import NodeSocket
|
|
7
7
|
|
|
8
8
|
from ._registry import _get_socket_linker
|
|
9
|
-
from ._utils import
|
|
9
|
+
from ._utils import (
|
|
10
|
+
SocketError,
|
|
11
|
+
_allow_innactive_sockets,
|
|
12
|
+
denormalize_name,
|
|
13
|
+
normalize_name,
|
|
14
|
+
)
|
|
10
15
|
|
|
11
16
|
if TYPE_CHECKING:
|
|
12
17
|
from .socket import Socket
|
|
@@ -43,6 +48,10 @@ class SocketAccessor:
|
|
|
43
48
|
for candidate in (key, denorm):
|
|
44
49
|
if candidate in ids:
|
|
45
50
|
return ids.index(candidate)
|
|
51
|
+
# Normalized identifier match: 'value_001' matches identifier 'Value_001'
|
|
52
|
+
normalized_ids = [normalize_name(id) for id in ids]
|
|
53
|
+
if key in normalized_ids:
|
|
54
|
+
return normalized_ids.index(key)
|
|
46
55
|
names = [s.name for s in self._collection]
|
|
47
56
|
for key in (key, denorm):
|
|
48
57
|
if key in names:
|
|
@@ -58,11 +67,24 @@ class SocketAccessor:
|
|
|
58
67
|
f"{self._node.bl_idname}. Available sockets (id: name): {list(zip(ids, names))}"
|
|
59
68
|
)
|
|
60
69
|
|
|
61
|
-
|
|
70
|
+
@overload
|
|
71
|
+
def _get(self, key: slice) -> "list[Socket]": ...
|
|
72
|
+
@overload
|
|
73
|
+
def _get(self, key: str | int) -> "Socket": ...
|
|
74
|
+
def _get(self, key: str | int | slice) -> "Socket | list[Socket]":
|
|
62
75
|
"""Get a Socket for a socket by identifier, name, or index."""
|
|
76
|
+
if isinstance(key, slice):
|
|
77
|
+
return [
|
|
78
|
+
_get_socket_linker(self._collection[i])
|
|
79
|
+
for i in range(*key.indices(len(self._collection)))
|
|
80
|
+
]
|
|
63
81
|
return _get_socket_linker(self._collection[self._index(key)])
|
|
64
82
|
|
|
65
|
-
|
|
83
|
+
@overload
|
|
84
|
+
def __getitem__(self, key: slice) -> "list[Socket]": ...
|
|
85
|
+
@overload
|
|
86
|
+
def __getitem__(self, key: str | int) -> "Socket": ...
|
|
87
|
+
def __getitem__(self, key: str | int | slice) -> "Socket | list[Socket]":
|
|
66
88
|
"""Access by identifier, name, or integer index."""
|
|
67
89
|
return self._get(key)
|
|
68
90
|
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from types import EllipsisType
|
|
4
|
-
from typing import TYPE_CHECKING, Any
|
|
4
|
+
from typing import TYPE_CHECKING, Any, TypeVar
|
|
5
5
|
|
|
6
6
|
from bpy.types import NodeLink, NodeSocket
|
|
7
7
|
|
|
8
8
|
from ._registry import _get_socket_linker
|
|
9
9
|
from ._utils import SocketError, _resolve_promotion, _SocketLike
|
|
10
10
|
|
|
11
|
+
_RShiftT = TypeVar("_RShiftT")
|
|
12
|
+
|
|
11
13
|
if TYPE_CHECKING:
|
|
14
|
+
from ..nodes.geometry import Compare, Math
|
|
12
15
|
from ..types import InputLinkable
|
|
13
16
|
from .node import BaseNode
|
|
14
17
|
from .socket import Socket
|
|
@@ -26,7 +29,7 @@ class OperatorMixin:
|
|
|
26
29
|
|
|
27
30
|
def _apply_math_operation(
|
|
28
31
|
self, other: Any, operation: str, reverse: bool = False
|
|
29
|
-
) -> "
|
|
32
|
+
) -> "Math":
|
|
30
33
|
socket, other, reverse = _resolve_promotion(
|
|
31
34
|
self._default_output_socket,
|
|
32
35
|
other,
|
|
@@ -34,43 +37,43 @@ class OperatorMixin:
|
|
|
34
37
|
)
|
|
35
38
|
return _get_socket_linker(socket)._dispatch_math(other, operation, reverse)
|
|
36
39
|
|
|
37
|
-
def __mul__(self, other: Any) -> "
|
|
40
|
+
def __mul__(self, other: Any) -> "Math":
|
|
38
41
|
return self._apply_math_operation(other, "multiply")
|
|
39
42
|
|
|
40
|
-
def __rmul__(self, other: Any) -> "
|
|
43
|
+
def __rmul__(self, other: Any) -> "Math":
|
|
41
44
|
return self._apply_math_operation(other, "multiply", reverse=True)
|
|
42
45
|
|
|
43
|
-
def __truediv__(self, other: Any) -> "
|
|
46
|
+
def __truediv__(self, other: Any) -> "Math":
|
|
44
47
|
return self._apply_math_operation(other, "divide")
|
|
45
48
|
|
|
46
|
-
def __rtruediv__(self, other: Any) -> "
|
|
49
|
+
def __rtruediv__(self, other: Any) -> "Math":
|
|
47
50
|
return self._apply_math_operation(other, "divide", reverse=True)
|
|
48
51
|
|
|
49
|
-
def __add__(self, other: Any) -> "
|
|
52
|
+
def __add__(self, other: Any) -> "Math":
|
|
50
53
|
return self._apply_math_operation(other, "add")
|
|
51
54
|
|
|
52
|
-
def __radd__(self, other: Any) -> "
|
|
55
|
+
def __radd__(self, other: Any) -> "Math":
|
|
53
56
|
return self._apply_math_operation(other, "add", reverse=True)
|
|
54
57
|
|
|
55
|
-
def __sub__(self, other: Any) -> "
|
|
58
|
+
def __sub__(self, other: Any) -> "Math":
|
|
56
59
|
return self._apply_math_operation(other, "subtract")
|
|
57
60
|
|
|
58
|
-
def __rsub__(self, other: Any) -> "
|
|
61
|
+
def __rsub__(self, other: Any) -> "Math":
|
|
59
62
|
return self._apply_math_operation(other, "subtract", reverse=True)
|
|
60
63
|
|
|
61
|
-
def __pow__(self, other: Any) -> "
|
|
64
|
+
def __pow__(self, other: Any) -> "Math":
|
|
62
65
|
return self._apply_math_operation(other, "power")
|
|
63
66
|
|
|
64
|
-
def __rpow__(self, other: Any) -> "
|
|
67
|
+
def __rpow__(self, other: Any) -> "Math":
|
|
65
68
|
return self._apply_math_operation(other, "power", reverse=True)
|
|
66
69
|
|
|
67
|
-
def __mod__(self, other: Any) -> "
|
|
70
|
+
def __mod__(self, other: Any) -> "Math":
|
|
68
71
|
return self._apply_math_operation(other, "modulo")
|
|
69
72
|
|
|
70
|
-
def __rmod__(self, other: Any) -> "
|
|
73
|
+
def __rmod__(self, other: Any) -> "Math":
|
|
71
74
|
return self._apply_math_operation(other, "modulo", reverse=True)
|
|
72
75
|
|
|
73
|
-
def __floordiv__(self, other: Any) -> "
|
|
76
|
+
def __floordiv__(self, other: Any) -> "Math":
|
|
74
77
|
socket, other, reverse = _resolve_promotion(
|
|
75
78
|
self._default_output_socket,
|
|
76
79
|
other,
|
|
@@ -78,7 +81,7 @@ class OperatorMixin:
|
|
|
78
81
|
)
|
|
79
82
|
return _get_socket_linker(socket)._dispatch_floordiv(other, reverse)
|
|
80
83
|
|
|
81
|
-
def __rfloordiv__(self, other: Any) -> "
|
|
84
|
+
def __rfloordiv__(self, other: Any) -> "Math":
|
|
82
85
|
socket, other, reverse = _resolve_promotion(
|
|
83
86
|
self._default_output_socket,
|
|
84
87
|
other,
|
|
@@ -86,37 +89,37 @@ class OperatorMixin:
|
|
|
86
89
|
)
|
|
87
90
|
return _get_socket_linker(socket)._dispatch_floordiv(other, reverse)
|
|
88
91
|
|
|
89
|
-
def __neg__(self) -> "
|
|
92
|
+
def __neg__(self) -> "Math":
|
|
90
93
|
return _get_socket_linker(self._default_output_socket)._dispatch_unary( # type: ignore[attr-defined]
|
|
91
94
|
"negate"
|
|
92
95
|
)
|
|
93
96
|
|
|
94
|
-
def __abs__(self) -> "
|
|
97
|
+
def __abs__(self) -> "Math":
|
|
95
98
|
return _get_socket_linker(self._default_output_socket)._dispatch_unary( # type: ignore[attr-defined]
|
|
96
99
|
"absolute"
|
|
97
100
|
)
|
|
98
101
|
|
|
99
|
-
def _apply_compare_operation(self, other: Any, operation: str) -> "
|
|
102
|
+
def _apply_compare_operation(self, other: Any, operation: str) -> "Math":
|
|
100
103
|
return _get_socket_linker(self._default_output_socket)._dispatch_compare( # type: ignore[attr-defined]
|
|
101
104
|
other, operation
|
|
102
105
|
)
|
|
103
106
|
|
|
104
|
-
def __lt__(self, other: Any) -> "
|
|
107
|
+
def __lt__(self, other: Any) -> "Compare":
|
|
105
108
|
return self._apply_compare_operation(other, "less_than")
|
|
106
109
|
|
|
107
|
-
def __gt__(self, other: Any) -> "
|
|
110
|
+
def __gt__(self, other: Any) -> "Compare":
|
|
108
111
|
return self._apply_compare_operation(other, "greater_than")
|
|
109
112
|
|
|
110
|
-
def __le__(self, other: Any) -> "
|
|
113
|
+
def __le__(self, other: Any) -> "Compare":
|
|
111
114
|
return self._apply_compare_operation(other, "less_equal")
|
|
112
115
|
|
|
113
|
-
def __ge__(self, other: Any) -> "
|
|
116
|
+
def __ge__(self, other: Any) -> "Compare":
|
|
114
117
|
return self._apply_compare_operation(other, "greater_equal")
|
|
115
118
|
|
|
116
|
-
def __eq__(self, other: Any) -> "
|
|
119
|
+
def __eq__(self, other: Any) -> "Compare": # type: ignore[override]
|
|
117
120
|
return self._apply_compare_operation(other, "equal")
|
|
118
121
|
|
|
119
|
-
def __ne__(self, other: Any) -> "
|
|
122
|
+
def __ne__(self, other: Any) -> "Compare": # type: ignore[override]
|
|
120
123
|
return self._apply_compare_operation(other, "not_equal")
|
|
121
124
|
|
|
122
125
|
def _apply_boolean_operation(self, other: Any, operation: str):
|
|
@@ -273,7 +276,7 @@ class LinkingMixin:
|
|
|
273
276
|
else:
|
|
274
277
|
self._link(source, input)
|
|
275
278
|
|
|
276
|
-
def __rshift__(self, other:
|
|
279
|
+
def __rshift__(self, other: _RShiftT) -> _RShiftT:
|
|
277
280
|
"""Chain nodes using >> operator. Links output to input.
|
|
278
281
|
|
|
279
282
|
Usage:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from abc import ABC, abstractmethod
|
|
4
|
-
from typing import TYPE_CHECKING, Iterable, Literal
|
|
4
|
+
from typing import TYPE_CHECKING, Iterable, Literal, Self
|
|
5
5
|
|
|
6
6
|
import bpy
|
|
7
7
|
from bpy.types import Node, NodeSocket
|
|
@@ -25,7 +25,7 @@ class BaseNode(_NodeLike, OperatorMixin, LinkingMixin):
|
|
|
25
25
|
_default_output_id: str | None = None
|
|
26
26
|
_placeholder_inputs: list[str]
|
|
27
27
|
|
|
28
|
-
def __init__(self):
|
|
28
|
+
def __init__(self, node: bpy.types.Node | None = None):
|
|
29
29
|
tree = (
|
|
30
30
|
TreeBuilder._tree_contexts[-1] if len(TreeBuilder._tree_contexts) else None
|
|
31
31
|
)
|
|
@@ -39,7 +39,7 @@ class BaseNode(_NodeLike, OperatorMixin, LinkingMixin):
|
|
|
39
39
|
|
|
40
40
|
self._tree = tree
|
|
41
41
|
self._placeholder_inputs = []
|
|
42
|
-
self.node
|
|
42
|
+
self.node = node if node else self._tree.add(self.__class__._bl_idname)
|
|
43
43
|
|
|
44
44
|
@property
|
|
45
45
|
def tree(self) -> TreeBuilder:
|
|
@@ -71,6 +71,33 @@ class BaseNode(_NodeLike, OperatorMixin, LinkingMixin):
|
|
|
71
71
|
socket = self.node.outputs[counter]
|
|
72
72
|
return socket
|
|
73
73
|
|
|
74
|
+
@classmethod
|
|
75
|
+
def _from_node(cls, node: bpy.types.Node) -> Self:
|
|
76
|
+
builder = cls()
|
|
77
|
+
builder.tree.nodes.remove(builder.node)
|
|
78
|
+
builder.node = node
|
|
79
|
+
return builder
|
|
80
|
+
|
|
81
|
+
@classmethod
|
|
82
|
+
def _find_or_create_linked(cls, socket: NodeSocket) -> Self:
|
|
83
|
+
if socket.is_output:
|
|
84
|
+
if socket.links:
|
|
85
|
+
for link in socket.links:
|
|
86
|
+
assert link.to_node
|
|
87
|
+
if link.to_node.bl_idname == cls._bl_idname:
|
|
88
|
+
return cls._from_node(link.to_node)
|
|
89
|
+
return cls(socket)
|
|
90
|
+
else:
|
|
91
|
+
if socket.links:
|
|
92
|
+
for link in socket.links:
|
|
93
|
+
assert link.from_node
|
|
94
|
+
if link.from_node.bl_idname == cls._bl_idname:
|
|
95
|
+
return cls._from_node(link.from_node)
|
|
96
|
+
|
|
97
|
+
node = cls()
|
|
98
|
+
node >> socket
|
|
99
|
+
return node
|
|
100
|
+
|
|
74
101
|
def _set_input_default_value(self, input, value):
|
|
75
102
|
"""Set the default value for an input socket, handling type conversions."""
|
|
76
103
|
if (
|