nodebpy 0.18.0__tar.gz → 520.0.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.
Files changed (71) hide show
  1. {nodebpy-0.18.0 → nodebpy-520.0.0}/PKG-INFO +2 -2
  2. {nodebpy-0.18.0 → nodebpy-520.0.0}/pyproject.toml +2 -3
  3. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/builder/__init__.py +51 -0
  4. nodebpy-520.0.0/src/nodebpy/builder/_registry.py +26 -0
  5. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/builder/_utils.py +1 -0
  6. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/builder/accessor.py +5 -5
  7. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/builder/mixins.py +67 -50
  8. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/builder/node.py +27 -10
  9. nodebpy-520.0.0/src/nodebpy/builder/socket.py +2457 -0
  10. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/builder/tree.py +13 -4
  11. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/compositor/__init__.py +91 -1
  12. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/compositor/color.py +9 -9
  13. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/compositor/converter.py +352 -311
  14. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/compositor/distort.py +19 -11
  15. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/compositor/filter.py +13 -13
  16. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/compositor/group.py +1 -1
  17. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/compositor/input.py +176 -16
  18. nodebpy-520.0.0/src/nodebpy/nodes/compositor/interface.py +242 -0
  19. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/compositor/matte.py +11 -11
  20. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/compositor/output.py +15 -1
  21. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/compositor/vector.py +5 -1
  22. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/geometry/__init__.py +61 -1
  23. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/geometry/attribute.py +135 -23
  24. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/geometry/color.py +6 -6
  25. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/geometry/converter.py +2828 -812
  26. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/geometry/geometry.py +926 -74
  27. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/geometry/grid.py +207 -190
  28. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/geometry/group.py +1 -1
  29. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/geometry/groups.py +51 -14
  30. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/geometry/input.py +184 -35
  31. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/geometry/interface.py +77 -52
  32. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/geometry/manual.py +663 -84
  33. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/geometry/output.py +1 -1
  34. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/geometry/texture.py +10 -10
  35. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/geometry/utilities.py +7 -16
  36. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/geometry/vector.py +6 -6
  37. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/geometry/zone.py +8 -11
  38. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/shader/__init__.py +22 -8
  39. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/shader/color.py +6 -6
  40. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/shader/converter.py +178 -29
  41. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/shader/grid.py +10 -10
  42. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/shader/group.py +1 -1
  43. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/shader/input.py +7 -7
  44. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/shader/output.py +9 -9
  45. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/shader/script.py +1 -1
  46. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/shader/shader.py +41 -17
  47. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/shader/texture.py +7 -7
  48. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/shader/vector.py +7 -7
  49. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/types.py +114 -13
  50. nodebpy-0.18.0/src/nodebpy/builder/_registry.py +0 -19
  51. nodebpy-0.18.0/src/nodebpy/builder/socket.py +0 -1866
  52. nodebpy-0.18.0/src/nodebpy/nodes/compositor/interface.py +0 -149
  53. {nodebpy-0.18.0 → nodebpy-520.0.0}/README.md +0 -0
  54. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/__init__.py +0 -0
  55. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/arrange.py +0 -0
  56. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/diagram.py +0 -0
  57. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/lib/nodearrange/__init__.py +0 -0
  58. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/lib/nodearrange/arrange/graph.py +0 -0
  59. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/lib/nodearrange/arrange/ordering.py +0 -0
  60. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/lib/nodearrange/arrange/ranking.py +0 -0
  61. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/lib/nodearrange/arrange/realize.py +0 -0
  62. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/lib/nodearrange/arrange/stacking.py +0 -0
  63. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/lib/nodearrange/arrange/structs.py +0 -0
  64. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/lib/nodearrange/arrange/sugiyama.py +0 -0
  65. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/lib/nodearrange/arrange/x_coords.py +0 -0
  66. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/lib/nodearrange/arrange/y_coords.py +0 -0
  67. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/lib/nodearrange/config.py +0 -0
  68. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/lib/nodearrange/utils.py +0 -0
  69. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/__init__.py +0 -0
  70. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/compositor/manual.py +0 -0
  71. {nodebpy-0.18.0 → nodebpy-520.0.0}/src/nodebpy/nodes/shader/manual.py +0 -0
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: nodebpy
3
- Version: 0.18.0
3
+ Version: 520.0.0
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.1.0 ; extra == 'bpy'
8
+ Requires-Dist: bpy==5.2.* ; extra == 'bpy'
9
9
  Requires-Dist: networkx>=3.6.1 ; extra == 'networkx'
10
10
  Requires-Python: >=3.13
11
11
  Provides-Extra: bpy
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nodebpy"
3
- version = "0.18.0"
3
+ version = "520.0.0"
4
4
  description = "Build nodes trees in Blender more elegantly with code"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -17,7 +17,7 @@ networkx = [
17
17
  "networkx>=3.6.1",
18
18
  ]
19
19
  bpy = [
20
- "bpy>=5.1.0",
20
+ "bpy==5.2.*",
21
21
  ]
22
22
 
23
23
  [build-system]
@@ -36,7 +36,6 @@ dev = [
36
36
  "quartodoc>=0.11.1",
37
37
  "ruff>=0.14.11",
38
38
  "syrupy>=5.0.0",
39
- "tree-clipper>=0.1.1",
40
39
  "pytest-xdist>=3.8.0",
41
40
  "griffe<2.0.0",
42
41
  "ty>=0.0.35",
@@ -11,24 +11,49 @@ from .node import (
11
11
  )
12
12
  from .socket import (
13
13
  BooleanSocket,
14
+ BooleanSocketGrid,
15
+ BooleanSocketList,
14
16
  BundleSocket,
17
+ BundleSocketList,
15
18
  ClosureSocket,
19
+ ClosureSocketList,
16
20
  CollectionSocket,
21
+ CollectionSocketList,
17
22
  ColorSocket,
23
+ ColorSocketList,
18
24
  FloatSocket,
25
+ FloatSocketGrid,
26
+ FloatSocketList,
19
27
  FontSocket,
28
+ FontSocketList,
20
29
  GeometrySocket,
30
+ GeometrySocketList,
21
31
  ImageSocket,
32
+ ImageSocketList,
22
33
  IntegerSocket,
34
+ IntegerSocketGrid,
35
+ IntegerSocketList,
36
+ IntegerVectorSocket,
23
37
  MaterialSocket,
38
+ MaterialSocketList,
24
39
  MatrixSocket,
40
+ MatrixSocketList,
25
41
  MenuSocket,
42
+ MenuSocketList,
26
43
  ObjectSocket,
44
+ ObjectSocketList,
27
45
  RotationSocket,
46
+ RotationSocketList,
28
47
  ShaderSocket,
48
+ ShaderSocketList,
29
49
  Socket,
50
+ SoundSocket,
51
+ SoundSocketList,
30
52
  StringSocket,
53
+ StringSocketList,
31
54
  VectorSocket,
55
+ VectorSocketGrid,
56
+ VectorSocketList,
32
57
  )
33
58
  from .tree import (
34
59
  InputInterfaceContext,
@@ -55,25 +80,51 @@ __all__ = [
55
80
  "CustomCompositorGroup",
56
81
  "CustomGeometryGroup",
57
82
  "CustomShaderGroup",
83
+ "CustomShaderGroup",
58
84
  # Runtime socket types
59
85
  "FloatSocket",
86
+ "FloatSocketGrid",
87
+ "FloatSocketList",
60
88
  "VectorSocket",
89
+ "VectorSocketGrid",
90
+ "VectorSocketList",
61
91
  "ColorSocket",
92
+ "ColorSocketList",
62
93
  "IntegerSocket",
94
+ "IntegerVectorSocket",
95
+ "IntegerSocketGrid",
96
+ "IntegerSocketList",
63
97
  "BooleanSocket",
98
+ "BooleanSocketGrid",
99
+ "BooleanSocketList",
64
100
  "RotationSocket",
101
+ "RotationSocketList",
65
102
  "MatrixSocket",
103
+ "MatrixSocketList",
66
104
  "StringSocket",
105
+ "StringSocketList",
67
106
  "MenuSocket",
107
+ "MenuSocketList",
68
108
  "GeometrySocket",
109
+ "GeometrySocketList",
69
110
  "ObjectSocket",
111
+ "ObjectSocketList",
70
112
  "FontSocket",
113
+ "FontSocketList",
71
114
  "MaterialSocket",
115
+ "MaterialSocketList",
72
116
  "ImageSocket",
117
+ "ImageSocketList",
73
118
  "CollectionSocket",
119
+ "CollectionSocketList",
74
120
  "BundleSocket",
121
+ "BundleSocketList",
75
122
  "ClosureSocket",
123
+ "ClosureSocketList",
76
124
  "ShaderSocket",
125
+ "ShaderSocketList",
126
+ "SoundSocket",
127
+ "SoundSocketList",
77
128
  # Tree context
78
129
  "SocketContext",
79
130
  "PanelContext",
@@ -0,0 +1,26 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from bpy.types import NodeSocket
6
+
7
+ if TYPE_CHECKING:
8
+ from .socket import Socket
9
+
10
+ _SOCKET_REGISTRY: dict[str, "type[Socket]"] = {}
11
+ _SOCKET_LIST_REGISTRY: dict[str, "type[Socket]"] = {}
12
+ _SOCKET_GRID_REGISTRY: dict[str, "type[Socket]"] = {}
13
+
14
+
15
+ def _wrap_socket(socket: NodeSocket) -> "Socket":
16
+ for key, cls in _SOCKET_REGISTRY.items():
17
+ if key in socket.bl_idname:
18
+ structure = getattr(socket, "inferred_structure_type", "SINGLE")
19
+ if structure == "LIST":
20
+ return _SOCKET_LIST_REGISTRY.get(key, cls)(socket)
21
+ elif structure == "GRID":
22
+ return _SOCKET_GRID_REGISTRY.get(key, cls)(socket)
23
+ return cls(socket)
24
+ from .socket import Socket
25
+
26
+ return Socket(socket)
@@ -47,6 +47,7 @@ def _allow_innactive_sockets(node: bpy.types.Node) -> bool:
47
47
  "GeometryNodeIndexSwitch",
48
48
  "GeometryNodeMenuSwitch",
49
49
  "ShaderNodeMixShader",
50
+ # "ShaderNodeMix",
50
51
  "GeometryNodeSwitch",
51
52
  )
52
53
 
@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, Iterator, Literal, overload
5
5
  import bpy
6
6
  from bpy.types import NodeSocket
7
7
 
8
- from ._registry import _get_socket_linker
8
+ from ._registry import _wrap_socket
9
9
  from ._utils import (
10
10
  SocketError,
11
11
  _allow_innactive_sockets,
@@ -79,14 +79,14 @@ class SocketAccessor:
79
79
  """Get a Socket for a socket by identifier, name, or index."""
80
80
  if isinstance(key, slice):
81
81
  sockets = [
82
- _get_socket_linker(self._collection[i])
82
+ _wrap_socket(self._collection[i])
83
83
  for i in range(*key.indices(len(self._collection)))
84
84
  ]
85
85
  if self._builder is not None:
86
86
  for s in sockets:
87
87
  s._builder_node = self._builder
88
88
  return sockets
89
- socket = _get_socket_linker(self._collection[self._index(key)])
89
+ socket = _wrap_socket(self._collection[self._index(key)])
90
90
  if self._builder is not None:
91
91
  socket._builder_node = self._builder
92
92
  return socket
@@ -185,7 +185,7 @@ class SocketAccessor:
185
185
  Uses node-level visibility rules regardless of ``ignore_visibility`` —
186
186
  see ``_visible_sockets`` for rationale.
187
187
  """
188
- return [_get_socket_linker(s) for s in self._visible_sockets()]
188
+ return [_wrap_socket(s) for s in self._visible_sockets()]
189
189
 
190
190
  def _items(self) -> "list[tuple[str, Socket]]":
191
191
  """All visible sockets as (name, Socket) pairs.
@@ -193,7 +193,7 @@ class SocketAccessor:
193
193
  Uses node-level visibility rules regardless of ``ignore_visibility`` —
194
194
  see ``_visible_sockets`` for rationale.
195
195
  """
196
- return [(s.name, _get_socket_linker(s)) for s in self._visible_sockets()]
196
+ return [(s.name, _wrap_socket(s)) for s in self._visible_sockets()]
197
197
 
198
198
  def _keys(self) -> list[str]:
199
199
  """All visible socket names."""
@@ -1,16 +1,17 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from types import EllipsisType
4
- from typing import TYPE_CHECKING, Any, TypeVar, cast, overload
4
+ from typing import TYPE_CHECKING, Any, Self, TypeVar, cast, overload
5
5
 
6
6
  from bpy.types import NodeLink, NodeSocket
7
7
 
8
- from ._registry import _get_socket_linker
8
+ from ._registry import _wrap_socket
9
9
  from ._utils import SocketError, _resolve_promotion, _SocketLike
10
10
 
11
11
  _RShiftT = TypeVar("_RShiftT")
12
12
 
13
13
  if TYPE_CHECKING:
14
+ from ..nodes.geometry import CombineTransform
14
15
  from ..types import InputLinkable
15
16
  from .accessor import SocketAccessor
16
17
  from .node import BaseNode
@@ -19,6 +20,7 @@ if TYPE_CHECKING:
19
20
  FloatSocket,
20
21
  IntegerSocket,
21
22
  MatrixSocket,
23
+ Position,
22
24
  Socket,
23
25
  VectorSocket,
24
26
  )
@@ -30,7 +32,7 @@ class OperatorMixin:
30
32
 
31
33
  Requires ``_default_output_socket`` on the concrete class.
32
34
  Delegates all dispatch to type-specific ``_dispatch_*`` methods on Socket
33
- subclasses, looked up via ``_get_socket_linker``.
35
+ subclasses, looked up via ``_wrap_socket``.
34
36
  """
35
37
 
36
38
  __array_ufunc__ = None
@@ -48,7 +50,7 @@ class OperatorMixin:
48
50
  other,
49
51
  reverse,
50
52
  )
51
- return _get_socket_linker(socket)._dispatch_math(other, operation, reverse)
53
+ return _wrap_socket(socket)._dispatch_math(other, operation, reverse)
52
54
 
53
55
  def __mul__(self, other: Any) -> "FloatSocket | VectorSocket | IntegerSocket":
54
56
  return self._apply_math_operation(other, "multiply")
@@ -92,7 +94,7 @@ class OperatorMixin:
92
94
  other,
93
95
  False,
94
96
  )
95
- return _get_socket_linker(socket)._dispatch_floordiv(other, reverse)
97
+ return _wrap_socket(socket)._dispatch_floordiv(other, reverse)
96
98
 
97
99
  def __rfloordiv__(self, other: Any) -> "FloatSocket | VectorSocket | IntegerSocket":
98
100
  socket, other, reverse = _resolve_promotion(
@@ -100,15 +102,66 @@ class OperatorMixin:
100
102
  other,
101
103
  True,
102
104
  )
103
- return _get_socket_linker(socket)._dispatch_floordiv(other, reverse)
105
+ return _wrap_socket(socket)._dispatch_floordiv(other, reverse)
106
+
107
+ @overload
108
+ def __matmul__(self, other: "Position") -> "VectorSocket": ...
109
+ @overload
110
+ def __matmul__(self, other: "CombineTransform") -> "VectorSocket": ...
111
+ def __matmul__(
112
+ self, other: "Position | CombineTransform | VectorSocket | MatrixSocket"
113
+ ) -> "VectorSocket | MatrixSocket":
114
+ from ..builder.socket import VectorSocket
115
+ from ..nodes.geometry import (
116
+ MultiplyMatrices,
117
+ Position,
118
+ TransformPoint,
119
+ )
120
+
121
+ if isinstance(other, (Position, VectorSocket)):
122
+ return TransformPoint(other, self).o.vector # ty: ignore[invalid-argument-type]
123
+
124
+ return MultiplyMatrices(self, other).o.matrix # ty: ignore[invalid-argument-type]
125
+
126
+ def __rmatmul__(self, other: Any) -> "MatrixSocket | VectorSocket":
127
+ from ..builder.socket import VectorSocket
128
+ from ..nodes.geometry import MultiplyMatrices, Position, TransformPoint
129
+
130
+ if isinstance(
131
+ self,
132
+ (
133
+ VectorSocket,
134
+ Position,
135
+ ),
136
+ ):
137
+ return TransformPoint(self, other).o.vector
138
+
139
+ return MultiplyMatrices(other, self).o.matrix # ty: ignore[invalid-argument-type]
104
140
 
105
141
  def __neg__(self) -> "FloatSocket | VectorSocket | IntegerSocket":
106
- return _get_socket_linker(self._default_output_socket)._dispatch_unary("negate")
142
+ return _wrap_socket(self._default_output_socket)._dispatch_unary("negate")
107
143
 
108
144
  def __abs__(self) -> "FloatSocket | VectorSocket | IntegerSocket":
109
- return _get_socket_linker(self._default_output_socket)._dispatch_unary(
110
- "absolute"
111
- )
145
+ return _wrap_socket(self._default_output_socket)._dispatch_unary("absolute")
146
+
147
+ if TYPE_CHECKING:
148
+
149
+ def __mul__(self, other: Any) -> Self: ...
150
+ def __rmul__(self, other: Any) -> Self: ...
151
+ def __truediv__(self, other: Any) -> Self: ...
152
+ def __rtruediv__(self, other: Any) -> Self: ...
153
+ def __add__(self, other: Any) -> Self: ...
154
+ def __radd__(self, other: Any) -> Self: ...
155
+ def __sub__(self, other: Any) -> Self: ...
156
+ def __rsub__(self, other: Any) -> Self: ...
157
+ def __pow__(self, other: Any) -> Self: ...
158
+ def __rpow__(self, other: Any) -> Self: ...
159
+ def __mod__(self, other: Any) -> Self: ...
160
+ def __rmod__(self, other: Any) -> Self: ...
161
+ def __floordiv__(self, other: Any) -> Self: ...
162
+ def __rfloordiv__(self, other: Any) -> Self: ...
163
+ def __neg__(self) -> Self: ...
164
+ def __abs__(self) -> Self: ...
112
165
 
113
166
  def _apply_compare_operation(
114
167
  self, other: Any, operation: str
@@ -118,7 +171,7 @@ class OperatorMixin:
118
171
  other,
119
172
  False,
120
173
  )
121
- return _get_socket_linker(socket)._dispatch_compare(other, operation)
174
+ return _wrap_socket(socket)._dispatch_compare(other, operation)
122
175
 
123
176
  def __lt__(self, other: Any) -> "FloatSocket | BooleanSocket":
124
177
  return self._apply_compare_operation(other, "less_than")
@@ -172,41 +225,6 @@ class OperatorMixin:
172
225
 
173
226
  return BooleanMath.l_not(cast(Any, self))
174
227
 
175
- @staticmethod
176
- def _cast_to_matrix(value) -> MatrixSocket:
177
- from ..nodes.geometry.converter import CombineMatrix
178
-
179
- if hasattr(value, "shape") and value.shape == (4, 4):
180
- return CombineMatrix(*value.ravel()).o.matrix
181
- else:
182
- return value
183
-
184
- def __matmul__(self, other: Any) -> "MatrixSocket | VectorSocket":
185
- from ..nodes.geometry.converter import MultiplyMatrices, TransformPoint
186
-
187
- other = self._cast_to_matrix(other)
188
- socket = self._default_output_socket
189
-
190
- if socket.type == "MATRIX" and other.type == "VECTOR":
191
- return TransformPoint(other, socket).o.vector
192
-
193
- return MultiplyMatrices(socket, other).o.matrix
194
-
195
- @overload
196
- def __rmatmul__(self, other: MatrixSocket) -> "MatrixSocket": ...
197
- @overload
198
- def __rmatmul__(self, other: VectorSocket) -> "VectorSocket": ...
199
- def __rmatmul__(self, other: Any) -> "MatrixSocket | VectorSocket":
200
- from ..nodes.geometry.converter import MultiplyMatrices, TransformPoint
201
-
202
- other = self._cast_to_matrix(other)
203
- socket = self._default_output_socket
204
-
205
- if socket.type == "VECTOR" and getattr(other, "type", None) == "MATRIX":
206
- return TransformPoint(socket, other).o.vector
207
-
208
- return MultiplyMatrices(other, socket).o.matrix
209
-
210
228
 
211
229
  class LinkingMixin:
212
230
  """Node/socket linking logic: ``>>``, ``_link``, best-socket matching.
@@ -332,11 +350,10 @@ class LinkingMixin:
332
350
  source: "InputLinkable",
333
351
  input: "InputLinkable | str",
334
352
  ):
353
+ from .node import _find_socket_from_name
354
+
335
355
  if isinstance(input, str):
336
- try:
337
- self._link(source, self.node.inputs[input])
338
- except KeyError:
339
- self._link(source, self.node.inputs[self.i._index(input)])
356
+ self._link(source, _find_socket_from_name(self.node.inputs, input))
340
357
  else:
341
358
  self._link(source, input)
342
359
 
@@ -45,6 +45,25 @@ if TYPE_CHECKING:
45
45
  def i(self) -> SocketAccessor: ...
46
46
 
47
47
 
48
+ def _find_socket_from_name(
49
+ collection: bpy.types.NodeInputs | bpy.types.NodeOutputs | list[NodeSocket],
50
+ name: str,
51
+ ) -> NodeSocket:
52
+ ids = [socket.identifier for socket in collection]
53
+ names = [socket.name for socket in collection]
54
+ for format in [name, name.title(), name.replace("_", " ").title()]:
55
+ try:
56
+ return collection[names.index(format)]
57
+ except ValueError:
58
+ try:
59
+ return collection[ids.index(format)]
60
+ except ValueError:
61
+ continue
62
+ raise ValueError(
63
+ f"Socket '{name}' not found in collection names or ids, available names: {names}, available ids: {ids}"
64
+ )
65
+
66
+
48
67
  class BaseNode(_NodeLike, OperatorMixin, LinkingMixin):
49
68
  """Base class for all node wrappers."""
50
69
 
@@ -143,8 +162,8 @@ class BaseNode(_NodeLike, OperatorMixin, LinkingMixin):
143
162
  input.default_value = value # type: ignore
144
163
 
145
164
  def _establish_links(self, **kwargs: InputAny):
146
- input_ids = [input.identifier for input in self.node.inputs]
147
165
  for name, value in kwargs.items():
166
+ # TODO: don't like these manual overrides for particular nodes, but best I can do for now
148
167
  if value is None or (
149
168
  "GridPrune" in self._bl_idname
150
169
  and name == "Threshold"
@@ -167,15 +186,13 @@ class BaseNode(_NodeLike, OperatorMixin, LinkingMixin):
167
186
  elif isinstance(value, _NodeLike):
168
187
  self._link_from(value.o._best_match(self.i._get(name).type), name) # type: ignore
169
188
  else:
170
- if name in input_ids:
171
- input = self.node.inputs[input_ids.index(name)]
172
- self._set_input_default_value(input, value)
173
- else:
174
- if name in self.node.inputs:
175
- input = self.node.inputs[name]
176
- else:
177
- input = self.node.inputs[name.replace("_", " ").title()]
178
- self._set_input_default_value(input, value)
189
+ # TODO: explicitly skipping the sockets for BooleanMath as they are default false,
190
+ # but this needs to be a more generic solution for sockets which aren't available
191
+ # https://github.com/BradyAJohnston/nodebpy/issues/90
192
+ if "BooleanMath" in self._bl_idname and value is False:
193
+ continue
194
+ socket = _find_socket_from_name(self.node.inputs, name)
195
+ self._set_input_default_value(socket, value)
179
196
 
180
197
  @property
181
198
  def o(self) -> SocketAccessor: