nodebpy 0.1.1__py3-none-any.whl → 0.2.0__py3-none-any.whl
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/builder.py +770 -306
- nodebpy/nodes/__init__.py +623 -9
- nodebpy/nodes/attribute.py +174 -273
- nodebpy/nodes/color.py +76 -0
- nodebpy/nodes/converter.py +4518 -0
- nodebpy/nodes/experimental.py +314 -0
- nodebpy/nodes/geometry.py +3665 -5250
- nodebpy/nodes/grid.py +1228 -0
- nodebpy/nodes/group.py +20 -0
- nodebpy/nodes/input.py +1571 -254
- nodebpy/nodes/interface.py +400 -0
- nodebpy/nodes/mesh.py +0 -1391
- nodebpy/nodes/texture.py +70 -0
- nodebpy/nodes/types.py +319 -6
- nodebpy/nodes/vector.py +0 -0
- nodebpy/nodes/zone.py +442 -0
- nodebpy/screenshot.py +2 -1
- nodebpy/sockets.py +12 -12
- {nodebpy-0.1.1.dist-info → nodebpy-0.2.0.dist-info}/METADATA +4 -4
- nodebpy-0.2.0.dist-info/RECORD +25 -0
- nodebpy/nodes/curve.py +0 -2006
- nodebpy/nodes/manually_specified.py +0 -1382
- nodebpy/nodes/utilities.py +0 -2344
- nodebpy-0.1.1.dist-info/RECORD +0 -19
- {nodebpy-0.1.1.dist-info → nodebpy-0.2.0.dist-info}/WHEEL +0 -0
- {nodebpy-0.1.1.dist-info → nodebpy-0.2.0.dist-info}/entry_points.txt +0 -0
nodebpy/builder.py
CHANGED
|
@@ -2,6 +2,9 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import TYPE_CHECKING, Any, ClassVar, Literal
|
|
4
4
|
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from .nodes.converter import Math, VectorMath
|
|
7
|
+
|
|
5
8
|
import arrangebpy
|
|
6
9
|
import bpy
|
|
7
10
|
from bpy.types import (
|
|
@@ -12,12 +15,18 @@ from bpy.types import (
|
|
|
12
15
|
)
|
|
13
16
|
|
|
14
17
|
from .nodes.types import (
|
|
18
|
+
LINKABLE,
|
|
19
|
+
SOCKET_COMPATIBILITY,
|
|
20
|
+
SOCKET_TYPES,
|
|
21
|
+
TYPE_INPUT_ALL,
|
|
15
22
|
FloatInterfaceSubtypes,
|
|
16
23
|
IntegerInterfaceSubtypes,
|
|
17
24
|
StringInterfaceSubtypes,
|
|
18
25
|
VectorInterfaceSubtypes,
|
|
19
26
|
_AttributeDomains,
|
|
27
|
+
_SocketShapeStructureType,
|
|
20
28
|
)
|
|
29
|
+
|
|
21
30
|
# from .arrange import arrange_tree
|
|
22
31
|
|
|
23
32
|
GEO_NODE_NAMES = (
|
|
@@ -33,13 +42,6 @@ GEO_NODE_NAMES = (
|
|
|
33
42
|
)
|
|
34
43
|
|
|
35
44
|
|
|
36
|
-
# POSSIBLE_NODE_NAMES = "GeometryNode"
|
|
37
|
-
LINKABLE = "Node | NodeSocket | NodeBuilder"
|
|
38
|
-
TYPE_INPUT_VECTOR = "NodeSocketVector | Vector | NodeBuilder | list[float] | tuple[float, float, float] | None"
|
|
39
|
-
TYPE_INPUT_ROTATION = "NodeSocketRotation | Quaternion | NodeBuilder | list[float] | tuple[float, float, float, float] | None"
|
|
40
|
-
TYPE_INPUT_BOOLEAN = "NodeSocketBool | bool | NodeBuilder | None"
|
|
41
|
-
|
|
42
|
-
|
|
43
45
|
def normalize_name(name: str) -> str:
|
|
44
46
|
"""Convert 'Geometry' or 'My Socket' to 'geometry' or 'my_socket'."""
|
|
45
47
|
return name.lower().replace(" ", "_")
|
|
@@ -50,28 +52,61 @@ def denormalize_name(attr_name: str) -> str:
|
|
|
50
52
|
return attr_name.replace("_", " ").title()
|
|
51
53
|
|
|
52
54
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
55
|
+
class SocketContext:
|
|
56
|
+
_direction: Literal["INPUT", "OUTPUT"] | None
|
|
57
|
+
_active_context: SocketContext | None = None
|
|
58
|
+
|
|
59
|
+
def __init__(self, tree_builder: TreeBuilder):
|
|
60
|
+
self.builder = tree_builder
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def tree(self) -> GeometryNodeTree:
|
|
64
|
+
tree = self.builder.tree
|
|
65
|
+
assert tree is not None and isinstance(tree, GeometryNodeTree)
|
|
66
|
+
return tree
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def interface(self) -> bpy.types.NodeTreeInterface:
|
|
70
|
+
interface = self.tree.interface
|
|
71
|
+
assert interface is not None
|
|
72
|
+
return interface
|
|
73
|
+
|
|
74
|
+
def _create_socket(
|
|
75
|
+
self, socket_def: SocketBase, name: str
|
|
76
|
+
) -> bpy.types.NodeTreeInterfaceSocket:
|
|
77
|
+
"""Create a socket from a socket definition."""
|
|
78
|
+
socket = self.interface.new_socket(
|
|
79
|
+
name=name,
|
|
80
|
+
in_out=self._direction,
|
|
81
|
+
socket_type=socket_def._bl_socket_type,
|
|
82
|
+
)
|
|
83
|
+
socket.description = socket_def.description
|
|
84
|
+
return socket
|
|
85
|
+
|
|
86
|
+
def __enter__(self):
|
|
87
|
+
SocketContext._direction = self._direction
|
|
88
|
+
SocketContext._active_context = self
|
|
89
|
+
return self
|
|
90
|
+
|
|
91
|
+
def __exit__(self, *args):
|
|
92
|
+
SocketContext._direction = None
|
|
93
|
+
SocketContext._active_context = None
|
|
94
|
+
pass
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class DirectionalContext(SocketContext):
|
|
98
|
+
"""Base class for directional socket contexts"""
|
|
99
|
+
|
|
100
|
+
_direction: Literal["INPUT", "OUTPUT"] = "INPUT"
|
|
101
|
+
_active_context = None
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class InputInterfaceContext(DirectionalContext):
|
|
105
|
+
_direction = "INPUT"
|
|
63
106
|
|
|
64
107
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
return node
|
|
68
|
-
elif isinstance(node, Node):
|
|
69
|
-
return node.inputs[0]
|
|
70
|
-
elif hasattr(node, "_default_input_socket"):
|
|
71
|
-
# NodeBuilder or SocketNodeBuilder
|
|
72
|
-
return node._default_input_socket
|
|
73
|
-
else:
|
|
74
|
-
raise TypeError(f"Unsupported type: {type(node)}")
|
|
108
|
+
class OutputInterfaceContext(DirectionalContext):
|
|
109
|
+
_direction = "OUTPUT"
|
|
75
110
|
|
|
76
111
|
|
|
77
112
|
class TreeBuilder:
|
|
@@ -112,6 +147,9 @@ class TreeBuilder:
|
|
|
112
147
|
def nodes(self) -> Nodes:
|
|
113
148
|
return self.tree.nodes
|
|
114
149
|
|
|
150
|
+
def __len__(self) -> int:
|
|
151
|
+
return len(self.nodes)
|
|
152
|
+
|
|
115
153
|
def arrange(self):
|
|
116
154
|
settings = arrangebpy.LayoutSettings(
|
|
117
155
|
horizontal_spacing=200, vertical_spacing=200, align_top_layer=True
|
|
@@ -148,92 +186,46 @@ class TreeBuilder:
|
|
|
148
186
|
except KeyError:
|
|
149
187
|
return self.tree.nodes.new("NodeGroupOutput") # type: ignore
|
|
150
188
|
|
|
151
|
-
def link(self, socket1: NodeSocket, socket2: NodeSocket):
|
|
189
|
+
def link(self, socket1: NodeSocket, socket2: NodeSocket) -> bpy.types.NodeLink:
|
|
152
190
|
if isinstance(socket1, SocketLinker):
|
|
153
191
|
socket1 = socket1.socket
|
|
154
192
|
if isinstance(socket2, SocketLinker):
|
|
155
193
|
socket2 = socket2.socket
|
|
156
194
|
|
|
157
|
-
self.tree.links.new(socket1, socket2)
|
|
195
|
+
link = self.tree.links.new(socket1, socket2, handle_dynamic_sockets=True)
|
|
158
196
|
|
|
159
197
|
if any(socket.is_inactive for socket in [socket1, socket2]):
|
|
160
198
|
# the warning message should report which sockets from which nodes were linked and which were innactive
|
|
161
199
|
for socket in [socket1, socket2]:
|
|
162
|
-
if socket
|
|
200
|
+
# we want to be loud about it if we end up linking an inactive socket to a node that is not a switch
|
|
201
|
+
if socket.is_inactive and socket.node.bl_idname not in (
|
|
202
|
+
"GeometryNodeIndexSwitch",
|
|
203
|
+
"GeometryNodeMenuSwitch",
|
|
204
|
+
):
|
|
163
205
|
message = f"Socket {socket.name} from node {socket.node.name} is inactive."
|
|
164
206
|
message += f" It is linked to socket {socket2.name} from node {socket2.node.name}."
|
|
165
207
|
message += " This link will be created by Blender but ignored when evaluated."
|
|
166
208
|
message += f"Socket type: {socket.bl_idname}"
|
|
167
209
|
raise RuntimeError(message)
|
|
168
210
|
|
|
211
|
+
return link
|
|
212
|
+
|
|
169
213
|
def add(self, name: str) -> Node:
|
|
170
214
|
self.just_added = self.tree.nodes.new(name) # type: ignore
|
|
171
215
|
assert self.just_added is not None
|
|
172
216
|
return self.just_added
|
|
173
217
|
|
|
174
218
|
|
|
175
|
-
class SocketContext:
|
|
176
|
-
_direction: Literal["INPUT", "OUTPUT"] | None
|
|
177
|
-
_active_context: SocketContext | None = None
|
|
178
|
-
|
|
179
|
-
def __init__(self, tree_builder: TreeBuilder):
|
|
180
|
-
self.builder = tree_builder
|
|
181
|
-
|
|
182
|
-
@property
|
|
183
|
-
def tree(self) -> GeometryNodeTree:
|
|
184
|
-
tree = self.builder.tree
|
|
185
|
-
assert tree is not None and isinstance(tree, GeometryNodeTree)
|
|
186
|
-
return tree
|
|
187
|
-
|
|
188
|
-
@property
|
|
189
|
-
def interface(self) -> bpy.types.NodeTreeInterface:
|
|
190
|
-
interface = self.tree.interface
|
|
191
|
-
assert interface is not None
|
|
192
|
-
return interface
|
|
193
|
-
|
|
194
|
-
def _create_socket(
|
|
195
|
-
self, socket_def: SocketBase
|
|
196
|
-
) -> bpy.types.NodeTreeInterfaceSocket:
|
|
197
|
-
"""Create a socket from a socket definition."""
|
|
198
|
-
socket = self.interface.new_socket(
|
|
199
|
-
name=socket_def.name,
|
|
200
|
-
in_out=self._direction,
|
|
201
|
-
socket_type=socket_def._bl_socket_type,
|
|
202
|
-
)
|
|
203
|
-
socket.description = socket_def.description
|
|
204
|
-
return socket
|
|
205
|
-
|
|
206
|
-
def __enter__(self):
|
|
207
|
-
SocketContext._direction = self._direction
|
|
208
|
-
SocketContext._active_context = self
|
|
209
|
-
return self
|
|
210
|
-
|
|
211
|
-
def __exit__(self, *args):
|
|
212
|
-
SocketContext._direction = None
|
|
213
|
-
SocketContext._active_context = None
|
|
214
|
-
pass
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
class InputInterfaceContext(SocketContext):
|
|
218
|
-
_direction = "INPUT"
|
|
219
|
-
_active_context = None
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
class OutputInterfaceContext(SocketContext):
|
|
223
|
-
_direction = "OUTPUT"
|
|
224
|
-
_active_context = None
|
|
225
|
-
|
|
226
|
-
|
|
227
219
|
class NodeBuilder:
|
|
228
220
|
"""Base class for all geometry node wrappers."""
|
|
229
221
|
|
|
230
|
-
node:
|
|
231
|
-
name: str
|
|
222
|
+
node: Any
|
|
232
223
|
_tree: "TreeBuilder"
|
|
233
|
-
_link_target: str | None = None
|
|
224
|
+
_link_target: str | None = None
|
|
234
225
|
_from_socket: NodeSocket | None = None
|
|
235
226
|
_default_input_id: str | None = None
|
|
236
227
|
_default_output_id: str | None = None
|
|
228
|
+
_socket_data_types = tuple(SOCKET_COMPATIBILITY.keys())
|
|
237
229
|
|
|
238
230
|
def __init__(self):
|
|
239
231
|
# Get active tree from context manager
|
|
@@ -246,9 +238,6 @@ class NodeBuilder:
|
|
|
246
238
|
f" node = {self.__class__.__name__}()\n"
|
|
247
239
|
)
|
|
248
240
|
|
|
249
|
-
self.inputs = InputInterfaceContext(tree)
|
|
250
|
-
self.outputs = OutputInterfaceContext(tree)
|
|
251
|
-
|
|
252
241
|
self._tree = tree
|
|
253
242
|
self._link_target = None
|
|
254
243
|
if self.__class__.name is not None:
|
|
@@ -266,6 +255,14 @@ class NodeBuilder:
|
|
|
266
255
|
def tree(self, value: "TreeBuilder"):
|
|
267
256
|
self._tree = value
|
|
268
257
|
|
|
258
|
+
@property
|
|
259
|
+
def type(self) -> SOCKET_TYPES:
|
|
260
|
+
return self._default_output_socket.type # type: ignore
|
|
261
|
+
|
|
262
|
+
@property
|
|
263
|
+
def name(self) -> str:
|
|
264
|
+
return str(self.node.name)
|
|
265
|
+
|
|
269
266
|
@property
|
|
270
267
|
def _default_input_socket(self) -> NodeSocket:
|
|
271
268
|
if self._default_input_id is not None:
|
|
@@ -278,6 +275,309 @@ class NodeBuilder:
|
|
|
278
275
|
return self.node.outputs[self._output_idx(self._default_output_id)]
|
|
279
276
|
return self.node.outputs[0]
|
|
280
277
|
|
|
278
|
+
def _source_socket(self, node: LINKABLE | SocketLinker | NodeSocket) -> NodeSocket:
|
|
279
|
+
assert node
|
|
280
|
+
if isinstance(node, NodeSocket):
|
|
281
|
+
return node
|
|
282
|
+
elif isinstance(node, Node):
|
|
283
|
+
return node.outputs[0]
|
|
284
|
+
elif hasattr(node, "_default_output_socket"):
|
|
285
|
+
return node._default_output_socket
|
|
286
|
+
else:
|
|
287
|
+
raise TypeError(f"Unsupported type: {type(node)}")
|
|
288
|
+
|
|
289
|
+
def _target_socket(self, node: LINKABLE | SocketLinker | NodeSocket) -> NodeSocket:
|
|
290
|
+
assert node
|
|
291
|
+
if isinstance(node, NodeSocket):
|
|
292
|
+
return node
|
|
293
|
+
elif isinstance(node, Node):
|
|
294
|
+
return node.inputs[0]
|
|
295
|
+
elif hasattr(node, "_default_input_socket"):
|
|
296
|
+
return node._default_input_socket
|
|
297
|
+
else:
|
|
298
|
+
raise TypeError(f"Unsupported type: {type(node)}")
|
|
299
|
+
|
|
300
|
+
def _find_compatible_output_socket(self, linkable: "NodeBuilder") -> NodeSocket:
|
|
301
|
+
"""Find a compatible output socket from the linkable node that matches our accepted socket types."""
|
|
302
|
+
# First try the default output socket
|
|
303
|
+
default_output = linkable._default_output_socket
|
|
304
|
+
for comp in SOCKET_COMPATIBILITY[default_output.type]:
|
|
305
|
+
if comp in self._socket_data_types:
|
|
306
|
+
return default_output
|
|
307
|
+
|
|
308
|
+
# If default doesn't work, try all other output sockets
|
|
309
|
+
for output_socket in linkable.node.outputs:
|
|
310
|
+
for comp in SOCKET_COMPATIBILITY[output_socket.type]:
|
|
311
|
+
if comp in self._socket_data_types:
|
|
312
|
+
return output_socket
|
|
313
|
+
|
|
314
|
+
# No compatible socket found
|
|
315
|
+
raise ValueError(
|
|
316
|
+
f"No compatible output socket found on {linkable.node.name} for {self.__class__.__name__}. "
|
|
317
|
+
f"Available output types: {[s.type for s in linkable.node.outputs]}, "
|
|
318
|
+
f"Accepted input types: {self._socket_data_types}"
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
def _find_compatible_source_socket(
|
|
322
|
+
self, source_node: "NodeBuilder", target_socket: NodeSocket
|
|
323
|
+
) -> NodeSocket:
|
|
324
|
+
"""Find a compatible output socket from source node that can link to the target input socket."""
|
|
325
|
+
target_type = target_socket.type
|
|
326
|
+
compatible_types = SOCKET_COMPATIBILITY.get(target_type, ())
|
|
327
|
+
|
|
328
|
+
# Collect all compatible sockets with their compatibility priority
|
|
329
|
+
compatible_sockets = []
|
|
330
|
+
for output_socket in source_node.node.outputs:
|
|
331
|
+
if output_socket.type in compatible_types:
|
|
332
|
+
# Priority is the index in the compatibility list (0 = highest priority)
|
|
333
|
+
priority = compatible_types.index(output_socket.type)
|
|
334
|
+
compatible_sockets.append((priority, output_socket))
|
|
335
|
+
|
|
336
|
+
if not compatible_sockets:
|
|
337
|
+
# No compatible socket found
|
|
338
|
+
raise ValueError(
|
|
339
|
+
f"No compatible output socket found on {source_node.node.name} for target socket {target_socket.name} of type {target_type}. "
|
|
340
|
+
f"Available output types: {[s.type for s in source_node.node.outputs]}, "
|
|
341
|
+
f"Compatible types: {compatible_types}"
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
# Sort by priority (lowest number = highest priority) and return the best match
|
|
345
|
+
compatible_sockets.sort(key=lambda x: x[0])
|
|
346
|
+
return compatible_sockets[0][1]
|
|
347
|
+
|
|
348
|
+
def _find_best_socket_pair(
|
|
349
|
+
self, target_node: "NodeBuilder"
|
|
350
|
+
) -> tuple[NodeSocket, NodeSocket]:
|
|
351
|
+
"""Find the best compatible socket pair between this node (source) and target node."""
|
|
352
|
+
# First try to connect default output to default input
|
|
353
|
+
default_output = self._default_output_socket
|
|
354
|
+
default_input = target_node._default_input_socket
|
|
355
|
+
|
|
356
|
+
# Handle zone outputs that don't have inputs yet
|
|
357
|
+
if default_input is None and hasattr(target_node, "_add_socket"):
|
|
358
|
+
# Target is a zone without inputs - create compatible socket
|
|
359
|
+
source_type = default_output.type
|
|
360
|
+
compatible_types = SOCKET_COMPATIBILITY.get(source_type, [source_type])
|
|
361
|
+
best_type = compatible_types[0] if compatible_types else source_type
|
|
362
|
+
|
|
363
|
+
# Create socket on target zone
|
|
364
|
+
default_input = target_node._add_socket(
|
|
365
|
+
name=best_type.title(), type=best_type
|
|
366
|
+
)
|
|
367
|
+
return default_output, default_input
|
|
368
|
+
|
|
369
|
+
# Check if default sockets are compatible
|
|
370
|
+
if default_input is not None:
|
|
371
|
+
output_compatibles = SOCKET_COMPATIBILITY.get(default_output.type, ())
|
|
372
|
+
if default_input.type in output_compatibles and (
|
|
373
|
+
not default_input.links or default_input.is_multi_input
|
|
374
|
+
):
|
|
375
|
+
return default_output, default_input
|
|
376
|
+
|
|
377
|
+
# If defaults don't work, try all combinations with priority-based matching
|
|
378
|
+
best_match = None
|
|
379
|
+
best_priority = float("inf")
|
|
380
|
+
|
|
381
|
+
for output_socket in self.node.outputs:
|
|
382
|
+
output_compatibles = SOCKET_COMPATIBILITY.get(output_socket.type, ())
|
|
383
|
+
for input_socket in target_node.node.inputs:
|
|
384
|
+
# Skip if socket already has a link and isn't multi-input
|
|
385
|
+
if input_socket.links and not input_socket.is_multi_input:
|
|
386
|
+
continue
|
|
387
|
+
|
|
388
|
+
if input_socket.type in output_compatibles:
|
|
389
|
+
# Calculate priority as the index in the compatibility list
|
|
390
|
+
priority = output_compatibles.index(input_socket.type)
|
|
391
|
+
if priority < best_priority:
|
|
392
|
+
best_priority = priority
|
|
393
|
+
best_match = (output_socket, input_socket)
|
|
394
|
+
|
|
395
|
+
if best_match:
|
|
396
|
+
return best_match
|
|
397
|
+
|
|
398
|
+
# No compatible pair found
|
|
399
|
+
available_outputs = [s.type for s in self.node.outputs]
|
|
400
|
+
available_inputs = [
|
|
401
|
+
s.type for s in target_node.node.inputs if not s.links or s.is_multi_input
|
|
402
|
+
]
|
|
403
|
+
raise RuntimeError(
|
|
404
|
+
f"Cannot link any output from {self.node.name} to any input of {target_node.node.name}. "
|
|
405
|
+
f"Available output types: {available_outputs}, "
|
|
406
|
+
f"Available input types: {available_inputs}"
|
|
407
|
+
)
|
|
408
|
+
|
|
409
|
+
def _socket_type_from_linkable(self, linkable: LINKABLE):
|
|
410
|
+
assert linkable, "Linkable cannot be None"
|
|
411
|
+
# If it's a NodeBuilder, try to find a compatible output socket
|
|
412
|
+
if hasattr(linkable, "node") and hasattr(linkable, "_default_output_socket"):
|
|
413
|
+
compatible_socket = self._find_compatible_output_socket(linkable)
|
|
414
|
+
socket_type = compatible_socket.type
|
|
415
|
+
for comp in SOCKET_COMPATIBILITY[socket_type]:
|
|
416
|
+
if comp in self._socket_data_types:
|
|
417
|
+
return comp if comp != "VALUE" else "FLOAT"
|
|
418
|
+
|
|
419
|
+
# Fallback to original logic for other types
|
|
420
|
+
for comp in SOCKET_COMPATIBILITY[linkable.type]:
|
|
421
|
+
if comp in self._socket_data_types:
|
|
422
|
+
return comp if comp != "VALUE" else "FLOAT"
|
|
423
|
+
raise ValueError(
|
|
424
|
+
f"Unsupported socket type for linking: {linkable}, type: {linkable.type=}"
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
def _add_inputs(self, *args, **kwargs) -> dict[str, LINKABLE]:
|
|
428
|
+
"""Dictionary with {new_socket.name: from_linkable} for link creation"""
|
|
429
|
+
new_sockets = {}
|
|
430
|
+
items = {}
|
|
431
|
+
for arg in args:
|
|
432
|
+
if isinstance(arg, bpy.types.NodeSocket):
|
|
433
|
+
name = arg.name
|
|
434
|
+
items[name] = arg
|
|
435
|
+
else:
|
|
436
|
+
items[arg._default_output_socket.name] = arg
|
|
437
|
+
items.update(kwargs)
|
|
438
|
+
for key, value in items.items():
|
|
439
|
+
# For NodeBuilder objects, find the best compatible output socket
|
|
440
|
+
if hasattr(value, "node") and hasattr(value, "_default_output_socket"):
|
|
441
|
+
compatible_socket = self._find_compatible_output_socket(value)
|
|
442
|
+
# Create a SocketLinker to represent the specific socket we want to link from
|
|
443
|
+
# from . import SocketLinker
|
|
444
|
+
socket_linker = SocketLinker(compatible_socket)
|
|
445
|
+
type = self._socket_type_from_linkable(value)
|
|
446
|
+
socket = self._add_socket(name=key, type=type)
|
|
447
|
+
new_sockets[socket.name] = socket_linker
|
|
448
|
+
else:
|
|
449
|
+
type = self._socket_type_from_linkable(value)
|
|
450
|
+
socket = self._add_socket(name=key, type=type)
|
|
451
|
+
new_sockets[socket.name] = value
|
|
452
|
+
|
|
453
|
+
return new_sockets
|
|
454
|
+
|
|
455
|
+
def _add_socket(
|
|
456
|
+
self, name: str, type: str, default_value: Any | None = None
|
|
457
|
+
) -> NodeSocket:
|
|
458
|
+
raise NotImplementedError
|
|
459
|
+
|
|
460
|
+
def _find_or_create_compatible_output_socket(
|
|
461
|
+
self, target_type: str
|
|
462
|
+
) -> NodeSocket | None:
|
|
463
|
+
"""Find an existing compatible output socket or create a new one if this node supports it.
|
|
464
|
+
|
|
465
|
+
Args:
|
|
466
|
+
target_type: The socket type needed for compatibility
|
|
467
|
+
|
|
468
|
+
Returns:
|
|
469
|
+
Compatible output socket if found/created, None if not possible
|
|
470
|
+
"""
|
|
471
|
+
if not hasattr(self, "_add_socket"):
|
|
472
|
+
return None
|
|
473
|
+
|
|
474
|
+
# Check if we already have a compatible output socket
|
|
475
|
+
if hasattr(self, "outputs"):
|
|
476
|
+
for name, socket_linker in self.outputs.items():
|
|
477
|
+
socket_compatibles = SOCKET_COMPATIBILITY.get(
|
|
478
|
+
socket_linker.socket.type, []
|
|
479
|
+
)
|
|
480
|
+
if target_type in socket_compatibles:
|
|
481
|
+
return socket_linker.socket
|
|
482
|
+
|
|
483
|
+
# No existing compatible socket found, try to create one
|
|
484
|
+
try:
|
|
485
|
+
# Check if this node type supports the target socket type
|
|
486
|
+
# by examining the type signature of _add_socket
|
|
487
|
+
import inspect
|
|
488
|
+
|
|
489
|
+
sig = inspect.signature(self._add_socket)
|
|
490
|
+
type_param = sig.parameters.get("type")
|
|
491
|
+
|
|
492
|
+
# If there's a type annotation that limits the allowed types, check it
|
|
493
|
+
if type_param and hasattr(type_param.annotation, "__args__"):
|
|
494
|
+
# This is a Literal type with specific allowed values
|
|
495
|
+
allowed_types = list(type_param.annotation.__args__)
|
|
496
|
+
if target_type not in allowed_types:
|
|
497
|
+
# Try to find a compatible type that this node can create
|
|
498
|
+
for allowed_type in allowed_types:
|
|
499
|
+
if target_type in SOCKET_COMPATIBILITY.get(allowed_type, []):
|
|
500
|
+
# Create the allowed type instead
|
|
501
|
+
self._add_socket(
|
|
502
|
+
name=allowed_type.title(), type=allowed_type
|
|
503
|
+
)
|
|
504
|
+
break
|
|
505
|
+
else:
|
|
506
|
+
# No compatible type found
|
|
507
|
+
return None
|
|
508
|
+
else:
|
|
509
|
+
# Target type is directly supported
|
|
510
|
+
self._add_socket(name=target_type.title(), type=target_type)
|
|
511
|
+
else:
|
|
512
|
+
# No type restrictions, try to create the target type
|
|
513
|
+
self._add_socket(name=target_type.title(), type=target_type)
|
|
514
|
+
|
|
515
|
+
# Find the newly created output socket
|
|
516
|
+
if hasattr(self, "outputs"):
|
|
517
|
+
for name, socket_linker in self.outputs.items():
|
|
518
|
+
socket_compatibles = SOCKET_COMPATIBILITY.get(
|
|
519
|
+
socket_linker.socket.type, []
|
|
520
|
+
)
|
|
521
|
+
if target_type in socket_compatibles:
|
|
522
|
+
return socket_linker.socket
|
|
523
|
+
|
|
524
|
+
# Fallback: try to get the socket directly from the node
|
|
525
|
+
if hasattr(self.node, "outputs"):
|
|
526
|
+
for output_socket in self.node.outputs:
|
|
527
|
+
socket_compatibles = SOCKET_COMPATIBILITY.get(
|
|
528
|
+
output_socket.type, []
|
|
529
|
+
)
|
|
530
|
+
if target_type in socket_compatibles:
|
|
531
|
+
return output_socket
|
|
532
|
+
except (NotImplementedError, AttributeError, RuntimeError):
|
|
533
|
+
# Node doesn't support dynamic socket creation or the type is not supported
|
|
534
|
+
pass
|
|
535
|
+
|
|
536
|
+
return None
|
|
537
|
+
|
|
538
|
+
def _smart_link_to(self, target_node: "NodeBuilder") -> "NodeBuilder":
|
|
539
|
+
"""Smart linking that creates compatible sockets when needed.
|
|
540
|
+
|
|
541
|
+
This method checks if we have a compatible output socket for the target node's input,
|
|
542
|
+
and creates one if this node supports dynamic socket creation.
|
|
543
|
+
|
|
544
|
+
Args:
|
|
545
|
+
target_node: The node to link to
|
|
546
|
+
|
|
547
|
+
Returns:
|
|
548
|
+
The target node (for chaining)
|
|
549
|
+
"""
|
|
550
|
+
if not hasattr(target_node, "_default_input_socket"):
|
|
551
|
+
# Fall back to regular linking
|
|
552
|
+
return target_node
|
|
553
|
+
|
|
554
|
+
target_socket = target_node._default_input_socket
|
|
555
|
+
if not target_socket:
|
|
556
|
+
# Target has no input socket - can't link
|
|
557
|
+
return target_node
|
|
558
|
+
|
|
559
|
+
# Check if our default output is compatible
|
|
560
|
+
source_socket = self._default_output_socket
|
|
561
|
+
if source_socket:
|
|
562
|
+
source_compatibles = SOCKET_COMPATIBILITY.get(source_socket.type, [])
|
|
563
|
+
if target_socket.type in source_compatibles:
|
|
564
|
+
# Compatible - use normal linking
|
|
565
|
+
self.tree.link(source_socket, target_socket)
|
|
566
|
+
return target_node
|
|
567
|
+
|
|
568
|
+
# Not compatible - try to find/create a compatible output socket
|
|
569
|
+
compatible_socket = self._find_or_create_compatible_output_socket(
|
|
570
|
+
target_socket.type
|
|
571
|
+
)
|
|
572
|
+
if compatible_socket:
|
|
573
|
+
self.tree.link(compatible_socket, target_socket)
|
|
574
|
+
return target_node
|
|
575
|
+
|
|
576
|
+
# Fall back to regular linking (may create reroute nodes)
|
|
577
|
+
if source_socket:
|
|
578
|
+
self.tree.link(source_socket, target_socket)
|
|
579
|
+
return target_node
|
|
580
|
+
|
|
281
581
|
def _input_idx(self, identifier: str) -> int:
|
|
282
582
|
# currently there is a Blender bug that is preventing the lookup of sockets from identifiers on some
|
|
283
583
|
# nodes but not others
|
|
@@ -312,22 +612,120 @@ class NodeBuilder:
|
|
|
312
612
|
"""Output socket: Vector"""
|
|
313
613
|
return SocketLinker(self.node.outputs[self._output_idx(identifier)])
|
|
314
614
|
|
|
315
|
-
def
|
|
316
|
-
self
|
|
615
|
+
def _link(
|
|
616
|
+
self, source: LINKABLE | SocketLinker | NodeSocket, target: LINKABLE
|
|
617
|
+
) -> bpy.types.NodeLink:
|
|
618
|
+
return self.tree.link(self._source_socket(source), self._target_socket(target))
|
|
317
619
|
|
|
318
|
-
def
|
|
319
|
-
self.tree.link(self._default_output_socket,
|
|
620
|
+
def _link_to(self, target: LINKABLE) -> bpy.types.NodeLink:
|
|
621
|
+
return self.tree.link(self._default_output_socket, self._target_socket(target))
|
|
622
|
+
|
|
623
|
+
def _link_from(
|
|
624
|
+
self,
|
|
625
|
+
source: LINKABLE,
|
|
626
|
+
input: LINKABLE | str,
|
|
627
|
+
):
|
|
628
|
+
# Special handling for dynamic socket nodes (zones, bake, capture attribute, etc.)
|
|
629
|
+
# These nodes have an 'outputs' property that returns a dict based on their items
|
|
630
|
+
if (
|
|
631
|
+
hasattr(source, "_add_socket")
|
|
632
|
+
and hasattr(source, "_smart_link_to")
|
|
633
|
+
and hasattr(source.__class__, "outputs")
|
|
634
|
+
and isinstance(getattr(source.__class__, "outputs"), property)
|
|
635
|
+
and not isinstance(input, str)
|
|
636
|
+
):
|
|
637
|
+
# Use smart linking that can create compatible sockets
|
|
638
|
+
return source._smart_link_to(input)
|
|
320
639
|
|
|
321
|
-
def link_from(self, source: LINKABLE, input: "LINKABLE | str"):
|
|
322
640
|
if isinstance(input, str):
|
|
323
641
|
try:
|
|
324
|
-
self.
|
|
642
|
+
self._link(source, self.node.inputs[input])
|
|
325
643
|
except KeyError:
|
|
326
|
-
self.
|
|
644
|
+
self._link(source, self.node.inputs[self._input_idx(input)])
|
|
645
|
+
else:
|
|
646
|
+
self._link(source, input)
|
|
647
|
+
|
|
648
|
+
def _smart_link_from(
|
|
649
|
+
self,
|
|
650
|
+
source: LINKABLE,
|
|
651
|
+
input_name: str,
|
|
652
|
+
):
|
|
653
|
+
"""Smart linking that finds compatible sockets when default fails."""
|
|
654
|
+
# Get the target input socket
|
|
655
|
+
try:
|
|
656
|
+
target_socket = self.node.inputs[input_name]
|
|
657
|
+
except KeyError:
|
|
658
|
+
target_socket = self.node.inputs[self._input_idx(input_name)]
|
|
659
|
+
|
|
660
|
+
# If source is a NodeBuilder, find the best compatible output socket
|
|
661
|
+
if isinstance(source, NodeBuilder):
|
|
662
|
+
# Search for compatible output sockets - don't try default first as it might be wrong type
|
|
663
|
+
try:
|
|
664
|
+
compatible_output = self._find_compatible_source_socket(
|
|
665
|
+
source, target_socket
|
|
666
|
+
)
|
|
667
|
+
self._link(compatible_output, target_socket)
|
|
668
|
+
return
|
|
669
|
+
except ValueError:
|
|
670
|
+
# No compatible socket found - this is an error, don't fall back
|
|
671
|
+
raise ValueError(
|
|
672
|
+
f"Cannot link {source.node.name} to {self.node.name}.{input_name}: "
|
|
673
|
+
f"No compatible sockets. Available output types: {[s.type for s in source.node.outputs]}, "
|
|
674
|
+
f"Target socket type: {target_socket.type}, "
|
|
675
|
+
f"Compatible types: {SOCKET_COMPATIBILITY.get(target_socket.type, ())}"
|
|
676
|
+
)
|
|
677
|
+
else:
|
|
678
|
+
# For other types, use original link_from behavior
|
|
679
|
+
self._link_from(source, input_name)
|
|
680
|
+
|
|
681
|
+
def _set_input_default_value(self, input, value):
|
|
682
|
+
"""Set the default value for an input socket, handling type conversions."""
|
|
683
|
+
if (
|
|
684
|
+
hasattr(input, "type")
|
|
685
|
+
and input.type == "VECTOR"
|
|
686
|
+
and isinstance(value, (int, float))
|
|
687
|
+
):
|
|
688
|
+
input.default_value = [value] * len(input.default_value)
|
|
327
689
|
else:
|
|
328
|
-
|
|
690
|
+
input.default_value = value
|
|
691
|
+
|
|
692
|
+
def _find_best_compatible_socket(
|
|
693
|
+
self, target_node: "NodeBuilder", output_socket: NodeSocket
|
|
694
|
+
) -> NodeSocket:
|
|
695
|
+
"""Find the best compatible input socket on target node for the given output socket."""
|
|
696
|
+
output_type = output_socket.type
|
|
697
|
+
compatible_types = SOCKET_COMPATIBILITY.get(output_type, ())
|
|
698
|
+
|
|
699
|
+
# Collect all compatible input sockets with their priority
|
|
700
|
+
compatible_inputs = []
|
|
701
|
+
for input_socket in target_node.node.inputs:
|
|
702
|
+
# Skip if socket already has a link and isn't multi-input
|
|
703
|
+
if input_socket.links and not input_socket.is_multi_input:
|
|
704
|
+
continue
|
|
329
705
|
|
|
330
|
-
|
|
706
|
+
if input_socket.type in compatible_types:
|
|
707
|
+
# Priority is the index in the compatibility list (0 = highest priority)
|
|
708
|
+
priority = compatible_types.index(input_socket.type)
|
|
709
|
+
compatible_inputs.append((priority, input_socket))
|
|
710
|
+
|
|
711
|
+
if not compatible_inputs:
|
|
712
|
+
# No compatible socket found
|
|
713
|
+
available_types = [
|
|
714
|
+
socket.type
|
|
715
|
+
for socket in target_node.node.inputs
|
|
716
|
+
if not socket.links or socket.is_multi_input
|
|
717
|
+
]
|
|
718
|
+
raise RuntimeError(
|
|
719
|
+
f"Cannot link {output_type} output to {target_node.node.name}. "
|
|
720
|
+
f"Compatible types: {compatible_types}, "
|
|
721
|
+
f"Available input types: {available_types}"
|
|
722
|
+
)
|
|
723
|
+
|
|
724
|
+
# Sort by priority (lowest number = highest priority) and return the best match
|
|
725
|
+
compatible_inputs.sort(key=lambda x: x[0])
|
|
726
|
+
return compatible_inputs[0][1]
|
|
727
|
+
|
|
728
|
+
def _establish_links(self, **kwargs: TYPE_INPUT_ALL):
|
|
331
729
|
input_ids = [input.identifier for input in self.node.inputs]
|
|
332
730
|
for name, value in kwargs.items():
|
|
333
731
|
if value is None:
|
|
@@ -337,21 +735,23 @@ class NodeBuilder:
|
|
|
337
735
|
# Ellipsis indicates this input should receive links from >> operator
|
|
338
736
|
# which can potentially target multiple inputs on the new node
|
|
339
737
|
if self._from_socket is not None:
|
|
340
|
-
self.
|
|
738
|
+
self._link(
|
|
341
739
|
self._from_socket, self.node.inputs[self._input_idx(name)]
|
|
342
740
|
)
|
|
343
741
|
|
|
742
|
+
elif isinstance(value, SocketLinker):
|
|
743
|
+
self._link(value, self.node.inputs[self._input_idx(name)])
|
|
344
744
|
# we can also provide just a default value for the socket to take if we aren't
|
|
345
745
|
# providing a socket to link with
|
|
346
|
-
elif isinstance(value, (NodeBuilder,
|
|
347
|
-
self.
|
|
746
|
+
elif isinstance(value, (NodeBuilder, NodeSocket, Node)):
|
|
747
|
+
self._smart_link_from(value, name)
|
|
348
748
|
else:
|
|
349
749
|
if name in input_ids:
|
|
350
750
|
input = self.node.inputs[input_ids.index(name)]
|
|
351
|
-
input
|
|
751
|
+
self._set_input_default_value(input, value)
|
|
352
752
|
else:
|
|
353
753
|
input = self.node.inputs[name.replace("_", "").capitalize()]
|
|
354
|
-
input
|
|
754
|
+
self._set_input_default_value(input, value)
|
|
355
755
|
|
|
356
756
|
def __rshift__(self, other: "NodeBuilder | SocketLinker") -> "NodeBuilder":
|
|
357
757
|
"""Chain nodes using >> operator. Links output to input.
|
|
@@ -361,32 +761,34 @@ class NodeBuilder:
|
|
|
361
761
|
tree.inputs.value >> Math.add(..., 0.1) >> tree.outputs.result
|
|
362
762
|
|
|
363
763
|
If the target node has an ellipsis placeholder (...), links to that specific input.
|
|
364
|
-
Otherwise,
|
|
764
|
+
Otherwise, finds the best compatible socket pair based on type compatibility.
|
|
365
765
|
|
|
366
766
|
Returns the right-hand node to enable continued chaining.
|
|
367
767
|
"""
|
|
368
|
-
# Get source socket - prefer Geometry, fall back to default
|
|
369
|
-
socket_out = self.node.outputs.get("Geometry") or self._default_output_socket
|
|
370
|
-
other._from_socket = socket_out
|
|
371
|
-
|
|
372
768
|
if isinstance(other, SocketLinker):
|
|
769
|
+
# Direct socket linking - use default output
|
|
770
|
+
socket_out = self._default_output_socket
|
|
373
771
|
socket_in = other.socket
|
|
772
|
+
other._from_socket = socket_out
|
|
374
773
|
else:
|
|
375
|
-
#
|
|
774
|
+
# Standard NodeBuilder linking - need to find compatible sockets
|
|
376
775
|
if other._link_target is not None:
|
|
377
|
-
#
|
|
776
|
+
# Target socket is specified
|
|
378
777
|
socket_in = self._get_input_socket_by_name(other, other._link_target)
|
|
379
|
-
|
|
380
|
-
# Default behavior - prefer Geometry, fall back to default
|
|
381
|
-
socket_in = (
|
|
382
|
-
other.node.inputs.get("Geometry") or other._default_input_socket
|
|
383
|
-
)
|
|
778
|
+
socket_out = self._default_output_socket
|
|
384
779
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
780
|
+
# Try to find a better source socket if default doesn't work
|
|
781
|
+
try:
|
|
782
|
+
socket_out = self._find_compatible_source_socket(self, socket_in)
|
|
783
|
+
except ValueError:
|
|
784
|
+
# If no compatible socket found, use default and let the link fail with a clear error
|
|
785
|
+
pass
|
|
786
|
+
|
|
787
|
+
other._from_socket = socket_out
|
|
788
|
+
else:
|
|
789
|
+
# No target specified - find best compatible socket pair
|
|
790
|
+
socket_out, socket_in = self._find_best_socket_pair(other)
|
|
791
|
+
other._from_socket = socket_out
|
|
390
792
|
|
|
391
793
|
self.tree.link(socket_out, socket_in)
|
|
392
794
|
return other
|
|
@@ -396,116 +798,92 @@ class NodeBuilder:
|
|
|
396
798
|
try:
|
|
397
799
|
return node.node.inputs[name]
|
|
398
800
|
except KeyError:
|
|
399
|
-
# Try with title case if direct access fails
|
|
400
801
|
title_name = name.replace("_", " ").title()
|
|
401
802
|
return node.node.inputs[title_name]
|
|
402
803
|
|
|
403
|
-
def
|
|
404
|
-
self,
|
|
405
|
-
) ->
|
|
406
|
-
"""
|
|
407
|
-
|
|
408
|
-
inputs = socket.node.inputs
|
|
409
|
-
current_idx = inputs.find(socket.identifier)
|
|
410
|
-
if current_idx >= 0 and current_idx + 1 < len(inputs):
|
|
411
|
-
if socket_out.type == "GEOMETRY":
|
|
412
|
-
# Prefer Geometry sockets
|
|
413
|
-
for idx in range(current_idx + 1, len(inputs)):
|
|
414
|
-
if inputs[idx].type == "GEOMETRY" and not inputs[idx].links:
|
|
415
|
-
return inputs[idx]
|
|
416
|
-
raise RuntimeError("No available Geometry input sockets found.")
|
|
417
|
-
return inputs[current_idx + 1]
|
|
418
|
-
except (KeyError, IndexError, AttributeError):
|
|
419
|
-
pass
|
|
420
|
-
return None
|
|
804
|
+
def _apply_math_operation(
|
|
805
|
+
self, other: Any, operation: str, reverse: bool = False
|
|
806
|
+
) -> "VectorMath | Math":
|
|
807
|
+
"""Apply a math operation with appropriate Math/VectorMath node."""
|
|
808
|
+
from .nodes.converter import VectorMath
|
|
421
809
|
|
|
422
|
-
|
|
423
|
-
|
|
810
|
+
values = (
|
|
811
|
+
(self._default_output_socket, other)
|
|
812
|
+
if not reverse
|
|
813
|
+
else (other, self._default_output_socket)
|
|
814
|
+
)
|
|
424
815
|
|
|
425
|
-
|
|
426
|
-
|
|
816
|
+
# Determine if either operand is a vector type
|
|
817
|
+
self_is_vector = self._default_output_socket.type == "VECTOR"
|
|
818
|
+
other_is_vector = False
|
|
819
|
+
if isinstance(other, NodeBuilder):
|
|
820
|
+
other_is_vector = other._default_output_socket.type == "VECTOR"
|
|
821
|
+
|
|
822
|
+
# Use VectorMath if either operand is a vector
|
|
823
|
+
if self_is_vector or other_is_vector:
|
|
824
|
+
if operation == "multiply":
|
|
825
|
+
# Handle special cases for vector multiplication where we might scale instead
|
|
826
|
+
# of using the multiply method
|
|
427
827
|
if isinstance(other, (int, float)):
|
|
428
828
|
return VectorMath.scale(self._default_output_socket, other)
|
|
429
829
|
elif isinstance(other, (list, tuple)) and len(other) == 3:
|
|
430
|
-
return VectorMath.multiply(
|
|
830
|
+
return VectorMath.multiply(*values)
|
|
831
|
+
elif isinstance(other, NodeBuilder):
|
|
832
|
+
return VectorMath.multiply(*values)
|
|
431
833
|
else:
|
|
432
834
|
raise TypeError(
|
|
433
|
-
f"Unsupported type for
|
|
835
|
+
f"Unsupported type for {operation} with VECTOR socket: {type(other)}"
|
|
434
836
|
)
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
case _:
|
|
438
|
-
raise TypeError(
|
|
439
|
-
f"Unsupported socket type for multiplication: {self._default_output_socket.type}"
|
|
440
|
-
)
|
|
441
|
-
|
|
442
|
-
def __rmul__(self, other: Any) -> "VectorMath | Math":
|
|
443
|
-
from .nodes import Math, VectorMath
|
|
444
|
-
|
|
445
|
-
match self._default_output_socket.type:
|
|
446
|
-
case "VECTOR":
|
|
837
|
+
else:
|
|
838
|
+
vector_method = getattr(VectorMath, operation)
|
|
447
839
|
if isinstance(other, (int, float)):
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
840
|
+
scalar_vector = (other, other, other)
|
|
841
|
+
return (
|
|
842
|
+
vector_method(scalar_vector, self._default_output_socket)
|
|
843
|
+
if not reverse
|
|
844
|
+
else vector_method(self._default_output_socket, scalar_vector)
|
|
845
|
+
)
|
|
846
|
+
elif (
|
|
847
|
+
isinstance(other, (list, tuple)) and len(other) == 3
|
|
848
|
+
) or isinstance(other, NodeBuilder):
|
|
849
|
+
return vector_method(*values)
|
|
850
|
+
|
|
451
851
|
else:
|
|
452
852
|
raise TypeError(
|
|
453
|
-
f"Unsupported type for
|
|
853
|
+
f"Unsupported type for {operation} with VECTOR operand: {type(other)}"
|
|
454
854
|
)
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
raise TypeError(
|
|
459
|
-
f"Unsupported socket type for multiplication: {self._default_output_socket.type}"
|
|
460
|
-
)
|
|
855
|
+
else:
|
|
856
|
+
# Both operands are scalar types, use regular Math
|
|
857
|
+
from .nodes.converter import IntegerMath, Math
|
|
461
858
|
|
|
462
|
-
|
|
463
|
-
|
|
859
|
+
if isinstance(other, int):
|
|
860
|
+
return getattr(IntegerMath, operation)(*values)
|
|
861
|
+
else:
|
|
862
|
+
return getattr(Math, operation)(*values)
|
|
464
863
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
return VectorMath.divide(self._default_output_socket, other)
|
|
468
|
-
case _:
|
|
469
|
-
raise TypeError(
|
|
470
|
-
f"Unsupported socket type for division: {self._default_output_socket.type}"
|
|
471
|
-
)
|
|
864
|
+
def __mul__(self, other: Any) -> "VectorMath | Math":
|
|
865
|
+
return self._apply_math_operation(other, "multiply")
|
|
472
866
|
|
|
473
|
-
def
|
|
474
|
-
|
|
867
|
+
def __rmul__(self, other: Any) -> "VectorMath | Math":
|
|
868
|
+
return self._apply_math_operation(other, "multiply", reverse=True)
|
|
475
869
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
f"Unsupported socket type for division: {self._default_output_socket.type}"
|
|
482
|
-
)
|
|
870
|
+
def __truediv__(self, other: Any) -> "VectorMath | Math":
|
|
871
|
+
return self._apply_math_operation(other, "divide")
|
|
872
|
+
|
|
873
|
+
def __rtruediv__(self, other: Any) -> "VectorMath | Math":
|
|
874
|
+
return self._apply_math_operation(other, "divide", reverse=True)
|
|
483
875
|
|
|
484
876
|
def __add__(self, other: Any) -> "VectorMath | Math":
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
match self._default_output_socket.type:
|
|
488
|
-
case "VECTOR":
|
|
489
|
-
return VectorMath.add(self._default_output_socket, other)
|
|
490
|
-
case "VALUE":
|
|
491
|
-
return Math.add(self._default_output_socket, other)
|
|
492
|
-
case _:
|
|
493
|
-
raise TypeError(
|
|
494
|
-
f"Unsupported socket type for addition: {self._default_output_socket.type}"
|
|
495
|
-
)
|
|
877
|
+
return self._apply_math_operation(other, "add")
|
|
496
878
|
|
|
497
879
|
def __radd__(self, other: Any) -> "VectorMath | Math":
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
case _:
|
|
506
|
-
raise TypeError(
|
|
507
|
-
f"Unsupported socket type for addition: {self._default_output_socket.type}"
|
|
508
|
-
)
|
|
880
|
+
return self._apply_math_operation(other, "add", reverse=True)
|
|
881
|
+
|
|
882
|
+
def __sub__(self, other: Any) -> "VectorMath | Math":
|
|
883
|
+
return self._apply_math_operation(other, "subtract")
|
|
884
|
+
|
|
885
|
+
def __rsub__(self, other: Any) -> "VectorMath | Math":
|
|
886
|
+
return self._apply_math_operation(other, "subtract", reverse=True)
|
|
509
887
|
|
|
510
888
|
|
|
511
889
|
class SocketLinker(NodeBuilder):
|
|
@@ -517,39 +895,16 @@ class SocketLinker(NodeBuilder):
|
|
|
517
895
|
self._tree = TreeBuilder(socket.node.id_data) # type: ignore
|
|
518
896
|
|
|
519
897
|
@property
|
|
520
|
-
def type(self) ->
|
|
521
|
-
return self.socket.type
|
|
898
|
+
def type(self) -> SOCKET_TYPES:
|
|
899
|
+
return self.socket.type # type: ignore
|
|
522
900
|
|
|
523
901
|
@property
|
|
524
902
|
def socket_name(self) -> str:
|
|
525
903
|
return self.socket.name
|
|
526
904
|
|
|
527
|
-
|
|
528
|
-
class SocketNodeBuilder(NodeBuilder):
|
|
529
|
-
"""Special NodeBuilder for accessing specific sockets on input/output nodes."""
|
|
530
|
-
|
|
531
|
-
def __init__(self, node: Node, socket_name: str, direction: str):
|
|
532
|
-
# Don't call super().__init__ - we already have a node
|
|
533
|
-
self.node = node
|
|
534
|
-
self._tree = TreeBuilder(node.id_data) # type: ignore
|
|
535
|
-
self._socket_name = socket_name
|
|
536
|
-
self._direction = direction
|
|
537
|
-
|
|
538
905
|
@property
|
|
539
|
-
def
|
|
540
|
-
|
|
541
|
-
if self._direction == "INPUT":
|
|
542
|
-
return self.node.outputs[self._socket_name]
|
|
543
|
-
else:
|
|
544
|
-
raise ValueError("Output nodes don't have outputs")
|
|
545
|
-
|
|
546
|
-
@property
|
|
547
|
-
def _default_input_socket(self) -> NodeSocket:
|
|
548
|
-
"""Return the specific named input socket."""
|
|
549
|
-
if self._direction == "OUTPUT":
|
|
550
|
-
return self.node.inputs[self._socket_name]
|
|
551
|
-
else:
|
|
552
|
-
raise ValueError("Input nodes don't have inputs")
|
|
906
|
+
def name(self) -> str:
|
|
907
|
+
return str(self.socket.name)
|
|
553
908
|
|
|
554
909
|
|
|
555
910
|
class SocketBase(SocketLinker):
|
|
@@ -558,11 +913,10 @@ class SocketBase(SocketLinker):
|
|
|
558
913
|
_bl_socket_type: str = ""
|
|
559
914
|
|
|
560
915
|
def __init__(self, name: str, description: str = ""):
|
|
561
|
-
self.name = name
|
|
562
916
|
self.description = description
|
|
563
917
|
|
|
564
918
|
self._socket_context: SocketContext = SocketContext._active_context
|
|
565
|
-
self.interface_socket = self._socket_context._create_socket(self)
|
|
919
|
+
self.interface_socket = self._socket_context._create_socket(self, name)
|
|
566
920
|
self._tree = self._socket_context.builder
|
|
567
921
|
if self._socket_context._direction == "INPUT":
|
|
568
922
|
socket = self.tree._input_node().outputs[self.interface_socket.identifier]
|
|
@@ -576,58 +930,78 @@ class SocketBase(SocketLinker):
|
|
|
576
930
|
continue
|
|
577
931
|
setattr(self.interface_socket, key, value)
|
|
578
932
|
|
|
933
|
+
@property
|
|
934
|
+
def default_value(self):
|
|
935
|
+
if not hasattr(self.interface_socket, "default_value"):
|
|
936
|
+
raise AttributeError(
|
|
937
|
+
f"'{self.__class__.__name__}' object has no attribute 'default_value'"
|
|
938
|
+
)
|
|
939
|
+
return self.interface_socket.default_value
|
|
579
940
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
super().__init__(name, description)
|
|
941
|
+
@default_value.setter
|
|
942
|
+
def default_value(self, value):
|
|
943
|
+
if not hasattr(self.interface_socket, "default_value"):
|
|
944
|
+
raise AttributeError(
|
|
945
|
+
f"'{self.__class__.__name__}' object has no attribute 'default_value'"
|
|
946
|
+
)
|
|
947
|
+
self.interface_socket.default_value = value
|
|
588
948
|
|
|
589
949
|
|
|
590
|
-
class
|
|
591
|
-
"""
|
|
950
|
+
class SocketFloat(SocketBase):
|
|
951
|
+
"""Float socket"""
|
|
592
952
|
|
|
593
|
-
_bl_socket_type: str = "
|
|
594
|
-
socket: bpy.types.
|
|
953
|
+
_bl_socket_type: str = "NodeSocketFloat"
|
|
954
|
+
socket: bpy.types.NodeTreeInterfaceSocketFloat
|
|
595
955
|
|
|
596
956
|
def __init__(
|
|
597
957
|
self,
|
|
598
|
-
name: str = "
|
|
599
|
-
default_value:
|
|
600
|
-
*,
|
|
958
|
+
name: str = "Value",
|
|
959
|
+
default_value: float = 0.0,
|
|
601
960
|
description: str = "",
|
|
961
|
+
*,
|
|
962
|
+
min_value: float | None = None,
|
|
963
|
+
max_value: float | None = None,
|
|
964
|
+
optional_label: bool = False,
|
|
602
965
|
hide_value: bool = False,
|
|
966
|
+
hide_in_modifier: bool = False,
|
|
967
|
+
structure_type: _SocketShapeStructureType = "AUTO",
|
|
968
|
+
subtype: FloatInterfaceSubtypes = "NONE",
|
|
603
969
|
attribute_domain: _AttributeDomains = "POINT",
|
|
604
970
|
default_attribute: str | None = None,
|
|
605
971
|
):
|
|
606
972
|
super().__init__(name, description)
|
|
607
973
|
self._set_values(
|
|
608
974
|
default_value=default_value,
|
|
975
|
+
min_value=min_value,
|
|
976
|
+
max_value=max_value,
|
|
977
|
+
optional_label=optional_label,
|
|
609
978
|
hide_value=hide_value,
|
|
979
|
+
hide_in_modifier=hide_in_modifier,
|
|
980
|
+
structure_type=structure_type,
|
|
981
|
+
subtype=subtype,
|
|
610
982
|
attribute_domain=attribute_domain,
|
|
611
983
|
default_attribute=default_attribute,
|
|
612
984
|
)
|
|
613
985
|
|
|
614
986
|
|
|
615
|
-
class
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
_bl_socket_type: str = "NodeSocketFloat"
|
|
619
|
-
socket: bpy.types.NodeTreeInterfaceSocketFloat
|
|
987
|
+
class SocketInt(SocketBase):
|
|
988
|
+
_bl_socket_type: str = "NodeSocketInt"
|
|
989
|
+
socket: bpy.types.NodeTreeInterfaceSocketInt
|
|
620
990
|
|
|
621
991
|
def __init__(
|
|
622
992
|
self,
|
|
623
|
-
name: str = "
|
|
624
|
-
default_value:
|
|
625
|
-
*,
|
|
993
|
+
name: str = "Integer",
|
|
994
|
+
default_value: int = 0,
|
|
626
995
|
description: str = "",
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
996
|
+
*,
|
|
997
|
+
min_value: int = -2147483648,
|
|
998
|
+
max_value: int = 2147483647,
|
|
999
|
+
optional_label: bool = False,
|
|
630
1000
|
hide_value: bool = False,
|
|
1001
|
+
hide_in_modifier: bool = False,
|
|
1002
|
+
structure_type: _SocketShapeStructureType = "AUTO",
|
|
1003
|
+
default_input: Literal["INDEX", "VALUE", "ID_OR_INDEX"] = "VALUE",
|
|
1004
|
+
subtype: IntegerInterfaceSubtypes = "NONE",
|
|
631
1005
|
attribute_domain: _AttributeDomains = "POINT",
|
|
632
1006
|
default_attribute: str | None = None,
|
|
633
1007
|
):
|
|
@@ -636,73 +1010,87 @@ class SocketFloat(SocketBase):
|
|
|
636
1010
|
default_value=default_value,
|
|
637
1011
|
min_value=min_value,
|
|
638
1012
|
max_value=max_value,
|
|
639
|
-
|
|
1013
|
+
optional_label=optional_label,
|
|
640
1014
|
hide_value=hide_value,
|
|
1015
|
+
hide_in_modifier=hide_in_modifier,
|
|
1016
|
+
structure_type=structure_type,
|
|
1017
|
+
default_input=default_input,
|
|
1018
|
+
subtype=subtype,
|
|
641
1019
|
attribute_domain=attribute_domain,
|
|
642
1020
|
default_attribute=default_attribute,
|
|
643
1021
|
)
|
|
644
1022
|
|
|
645
1023
|
|
|
646
|
-
class
|
|
647
|
-
|
|
648
|
-
|
|
1024
|
+
class SocketBoolean(SocketBase):
|
|
1025
|
+
"""Boolean socket - true/false value."""
|
|
1026
|
+
|
|
1027
|
+
_bl_socket_type: str = "NodeSocketBool"
|
|
1028
|
+
socket: bpy.types.NodeTreeInterfaceSocketBool
|
|
649
1029
|
|
|
650
1030
|
def __init__(
|
|
651
1031
|
self,
|
|
652
|
-
name: str = "
|
|
653
|
-
default_value:
|
|
654
|
-
*,
|
|
1032
|
+
name: str = "Boolean",
|
|
1033
|
+
default_value: bool = False,
|
|
655
1034
|
description: str = "",
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
max_value: float | None = None,
|
|
1035
|
+
*,
|
|
1036
|
+
optional_label: bool = False,
|
|
659
1037
|
hide_value: bool = False,
|
|
660
|
-
|
|
661
|
-
|
|
1038
|
+
hide_in_modifier: bool = False,
|
|
1039
|
+
structure_type: _SocketShapeStructureType = "AUTO",
|
|
1040
|
+
layer_selection_field: bool = False,
|
|
662
1041
|
attribute_domain: _AttributeDomains = "POINT",
|
|
1042
|
+
default_attribute: str | None = None,
|
|
663
1043
|
):
|
|
664
1044
|
super().__init__(name, description)
|
|
665
|
-
assert len(default_value) == dimensions, (
|
|
666
|
-
"Default value length must match dimensions"
|
|
667
|
-
)
|
|
668
1045
|
self._set_values(
|
|
669
|
-
dimensions=dimensions,
|
|
670
1046
|
default_value=default_value,
|
|
671
|
-
|
|
672
|
-
max_value=max_value,
|
|
1047
|
+
optional_label=optional_label,
|
|
673
1048
|
hide_value=hide_value,
|
|
674
|
-
|
|
675
|
-
|
|
1049
|
+
layer_selection_field=layer_selection_field,
|
|
1050
|
+
hide_in_modifier=hide_in_modifier,
|
|
1051
|
+
structure_type=structure_type,
|
|
676
1052
|
attribute_domain=attribute_domain,
|
|
1053
|
+
default_attribute=default_attribute,
|
|
677
1054
|
)
|
|
678
1055
|
|
|
679
1056
|
|
|
680
|
-
class
|
|
681
|
-
_bl_socket_type: str = "
|
|
682
|
-
socket: bpy.types.
|
|
1057
|
+
class SocketVector(SocketBase):
|
|
1058
|
+
_bl_socket_type: str = "NodeSocketVector"
|
|
1059
|
+
socket: bpy.types.NodeTreeInterfaceSocketVector
|
|
683
1060
|
|
|
684
1061
|
def __init__(
|
|
685
1062
|
self,
|
|
686
|
-
name: str = "
|
|
687
|
-
default_value:
|
|
688
|
-
*,
|
|
1063
|
+
name: str = "Vector",
|
|
1064
|
+
default_value: tuple[float, float, float] = (0.0, 0.0, 0.0),
|
|
689
1065
|
description: str = "",
|
|
690
|
-
|
|
691
|
-
|
|
1066
|
+
*,
|
|
1067
|
+
dimensions: int = 3,
|
|
1068
|
+
min_value: float | None = None,
|
|
1069
|
+
max_value: float | None = None,
|
|
1070
|
+
optional_label: bool = False,
|
|
692
1071
|
hide_value: bool = False,
|
|
693
|
-
|
|
694
|
-
|
|
1072
|
+
hide_in_modifier: bool = False,
|
|
1073
|
+
structure_type: _SocketShapeStructureType = "AUTO",
|
|
1074
|
+
subtype: VectorInterfaceSubtypes = "NONE",
|
|
695
1075
|
default_attribute: str | None = None,
|
|
1076
|
+
attribute_domain: _AttributeDomains = "POINT",
|
|
696
1077
|
):
|
|
1078
|
+
assert len(default_value) == dimensions, (
|
|
1079
|
+
"Default value length must match dimensions"
|
|
1080
|
+
)
|
|
697
1081
|
super().__init__(name, description)
|
|
698
1082
|
self._set_values(
|
|
1083
|
+
dimensions=dimensions,
|
|
699
1084
|
default_value=default_value,
|
|
700
1085
|
min_value=min_value,
|
|
701
1086
|
max_value=max_value,
|
|
1087
|
+
optional_label=optional_label,
|
|
702
1088
|
hide_value=hide_value,
|
|
1089
|
+
hide_in_modifier=hide_in_modifier,
|
|
1090
|
+
structure_type=structure_type,
|
|
703
1091
|
subtype=subtype,
|
|
704
|
-
attribute_domain=attribute_domain,
|
|
705
1092
|
default_attribute=default_attribute,
|
|
1093
|
+
attribute_domain=attribute_domain,
|
|
706
1094
|
)
|
|
707
1095
|
|
|
708
1096
|
|
|
@@ -716,17 +1104,23 @@ class SocketColor(SocketBase):
|
|
|
716
1104
|
self,
|
|
717
1105
|
name: str = "Color",
|
|
718
1106
|
default_value: tuple[float, float, float, float] = (1.0, 1.0, 1.0, 1.0),
|
|
719
|
-
*,
|
|
720
1107
|
description: str = "",
|
|
1108
|
+
*,
|
|
1109
|
+
optional_label: bool = False,
|
|
721
1110
|
hide_value: bool = False,
|
|
1111
|
+
hide_in_modifier: bool = False,
|
|
1112
|
+
structure_type: _SocketShapeStructureType = "AUTO",
|
|
722
1113
|
attribute_domain: _AttributeDomains = "POINT",
|
|
723
1114
|
default_attribute: str | None = None,
|
|
724
1115
|
):
|
|
725
|
-
super().__init__(name, description)
|
|
726
1116
|
assert len(default_value) == 4, "Default color must be RGBA tuple"
|
|
1117
|
+
super().__init__(name, description)
|
|
727
1118
|
self._set_values(
|
|
728
1119
|
default_value=default_value,
|
|
1120
|
+
optional_label=optional_label,
|
|
729
1121
|
hide_value=hide_value,
|
|
1122
|
+
hide_in_modifier=hide_in_modifier,
|
|
1123
|
+
structure_type=structure_type,
|
|
730
1124
|
attribute_domain=attribute_domain,
|
|
731
1125
|
default_attribute=default_attribute,
|
|
732
1126
|
)
|
|
@@ -742,17 +1136,22 @@ class SocketRotation(SocketBase):
|
|
|
742
1136
|
self,
|
|
743
1137
|
name: str = "Rotation",
|
|
744
1138
|
default_value: tuple[float, float, float] = (1.0, 0.0, 0.0),
|
|
745
|
-
*,
|
|
746
1139
|
description: str = "",
|
|
1140
|
+
*,
|
|
1141
|
+
optional_label: bool = False,
|
|
747
1142
|
hide_value: bool = False,
|
|
1143
|
+
hide_in_modifier: bool = False,
|
|
1144
|
+
structure_type: _SocketShapeStructureType = "AUTO",
|
|
748
1145
|
attribute_domain: _AttributeDomains = "POINT",
|
|
749
1146
|
default_attribute: str | None = None,
|
|
750
1147
|
):
|
|
751
1148
|
super().__init__(name, description)
|
|
752
|
-
assert len(default_value) == 4, "Default rotation must be quaternion tuple"
|
|
753
1149
|
self._set_values(
|
|
754
1150
|
default_value=default_value,
|
|
1151
|
+
optional_label=optional_label,
|
|
755
1152
|
hide_value=hide_value,
|
|
1153
|
+
hide_in_modifier=hide_in_modifier,
|
|
1154
|
+
structure_type=structure_type,
|
|
756
1155
|
attribute_domain=attribute_domain,
|
|
757
1156
|
default_attribute=default_attribute,
|
|
758
1157
|
)
|
|
@@ -767,15 +1166,23 @@ class SocketMatrix(SocketBase):
|
|
|
767
1166
|
def __init__(
|
|
768
1167
|
self,
|
|
769
1168
|
name: str = "Matrix",
|
|
770
|
-
*,
|
|
771
1169
|
description: str = "",
|
|
1170
|
+
*,
|
|
1171
|
+
optional_label: bool = False,
|
|
772
1172
|
hide_value: bool = False,
|
|
1173
|
+
hide_in_modifier: bool = False,
|
|
1174
|
+
structure_type: _SocketShapeStructureType = "AUTO",
|
|
1175
|
+
default_input: Literal["VALUE", "INSTANCE_TRANSFORM"] = "VALUE",
|
|
773
1176
|
attribute_domain: _AttributeDomains = "POINT",
|
|
774
1177
|
default_attribute: str | None = None,
|
|
775
1178
|
):
|
|
776
1179
|
super().__init__(name, description)
|
|
777
1180
|
self._set_values(
|
|
1181
|
+
optional_label=optional_label,
|
|
778
1182
|
hide_value=hide_value,
|
|
1183
|
+
hide_in_modifier=hide_in_modifier,
|
|
1184
|
+
structure_type=structure_type,
|
|
1185
|
+
default_input=default_input,
|
|
779
1186
|
attribute_domain=attribute_domain,
|
|
780
1187
|
default_attribute=default_attribute,
|
|
781
1188
|
)
|
|
@@ -789,20 +1196,24 @@ class SocketString(SocketBase):
|
|
|
789
1196
|
self,
|
|
790
1197
|
name: str = "String",
|
|
791
1198
|
default_value: str = "",
|
|
792
|
-
*,
|
|
793
1199
|
description: str = "",
|
|
1200
|
+
*,
|
|
1201
|
+
optional_label: bool = False,
|
|
794
1202
|
hide_value: bool = False,
|
|
1203
|
+
hide_in_modifier: bool = False,
|
|
795
1204
|
subtype: StringInterfaceSubtypes = "NONE",
|
|
796
1205
|
):
|
|
797
1206
|
super().__init__(name, description)
|
|
798
1207
|
self._set_values(
|
|
799
1208
|
default_value=default_value,
|
|
1209
|
+
optional_label=optional_label,
|
|
800
1210
|
hide_value=hide_value,
|
|
1211
|
+
hide_in_modifier=hide_in_modifier,
|
|
801
1212
|
subtype=subtype,
|
|
802
1213
|
)
|
|
803
1214
|
|
|
804
1215
|
|
|
805
|
-
class
|
|
1216
|
+
class SocketMenu(SocketBase):
|
|
806
1217
|
"""Menu socket - holds a selection from predefined items."""
|
|
807
1218
|
|
|
808
1219
|
_bl_socket_type: str = "NodeSocketMenu"
|
|
@@ -812,16 +1223,22 @@ class MenuSocket(SocketBase):
|
|
|
812
1223
|
self,
|
|
813
1224
|
name: str = "Menu",
|
|
814
1225
|
default_value: str | None = None,
|
|
815
|
-
*,
|
|
816
1226
|
description: str = "",
|
|
1227
|
+
*,
|
|
817
1228
|
expanded: bool = False,
|
|
1229
|
+
optional_label: bool = False,
|
|
818
1230
|
hide_value: bool = False,
|
|
1231
|
+
hide_in_modifier: bool = False,
|
|
1232
|
+
structure_type: _SocketShapeStructureType = "AUTO",
|
|
819
1233
|
):
|
|
820
1234
|
super().__init__(name, description)
|
|
821
1235
|
self._set_values(
|
|
822
1236
|
default_value=default_value,
|
|
823
1237
|
menu_expanded=expanded,
|
|
1238
|
+
optional_label=optional_label,
|
|
824
1239
|
hide_value=hide_value,
|
|
1240
|
+
hide_in_modifier=hide_in_modifier,
|
|
1241
|
+
structure_type=structure_type,
|
|
825
1242
|
)
|
|
826
1243
|
|
|
827
1244
|
|
|
@@ -835,14 +1252,41 @@ class SocketObject(SocketBase):
|
|
|
835
1252
|
self,
|
|
836
1253
|
name: str = "Object",
|
|
837
1254
|
default_value: bpy.types.Object | None = None,
|
|
838
|
-
*,
|
|
839
1255
|
description: str = "",
|
|
1256
|
+
*,
|
|
1257
|
+
optional_label: bool = False,
|
|
840
1258
|
hide_value: bool = False,
|
|
1259
|
+
hide_in_modifier: bool = False,
|
|
841
1260
|
):
|
|
842
1261
|
super().__init__(name, description)
|
|
843
1262
|
self._set_values(
|
|
844
1263
|
default_value=default_value,
|
|
1264
|
+
optional_label=optional_label,
|
|
1265
|
+
hide_value=hide_value,
|
|
1266
|
+
hide_in_modifier=hide_in_modifier,
|
|
1267
|
+
)
|
|
1268
|
+
|
|
1269
|
+
|
|
1270
|
+
class SocketGeometry(SocketBase):
|
|
1271
|
+
"""Geometry socket - holds mesh, curve, point cloud, or volume data."""
|
|
1272
|
+
|
|
1273
|
+
_bl_socket_type: str = "NodeSocketGeometry"
|
|
1274
|
+
socket: bpy.types.NodeTreeInterfaceSocketGeometry
|
|
1275
|
+
|
|
1276
|
+
def __init__(
|
|
1277
|
+
self,
|
|
1278
|
+
name: str = "Geometry",
|
|
1279
|
+
description: str = "",
|
|
1280
|
+
*,
|
|
1281
|
+
optional_label: bool = False,
|
|
1282
|
+
hide_value: bool = False,
|
|
1283
|
+
hide_in_modifier: bool = False,
|
|
1284
|
+
):
|
|
1285
|
+
super().__init__(name, description)
|
|
1286
|
+
self._set_values(
|
|
1287
|
+
optional_label=optional_label,
|
|
845
1288
|
hide_value=hide_value,
|
|
1289
|
+
hide_in_modifier=hide_in_modifier,
|
|
846
1290
|
)
|
|
847
1291
|
|
|
848
1292
|
|
|
@@ -856,14 +1300,18 @@ class SocketCollection(SocketBase):
|
|
|
856
1300
|
self,
|
|
857
1301
|
name: str = "Collection",
|
|
858
1302
|
default_value: bpy.types.Collection | None = None,
|
|
859
|
-
*,
|
|
860
1303
|
description: str = "",
|
|
1304
|
+
*,
|
|
1305
|
+
optional_label: bool = False,
|
|
861
1306
|
hide_value: bool = False,
|
|
1307
|
+
hide_in_modifier: bool = False,
|
|
862
1308
|
):
|
|
863
1309
|
super().__init__(name, description)
|
|
864
1310
|
self._set_values(
|
|
865
1311
|
default_value=default_value,
|
|
1312
|
+
optional_label=optional_label,
|
|
866
1313
|
hide_value=hide_value,
|
|
1314
|
+
hide_in_modifier=hide_in_modifier,
|
|
867
1315
|
)
|
|
868
1316
|
|
|
869
1317
|
|
|
@@ -877,14 +1325,18 @@ class SocketImage(SocketBase):
|
|
|
877
1325
|
self,
|
|
878
1326
|
name: str = "Image",
|
|
879
1327
|
default_value: bpy.types.Image | None = None,
|
|
880
|
-
*,
|
|
881
1328
|
description: str = "",
|
|
1329
|
+
*,
|
|
1330
|
+
optional_label: bool = False,
|
|
882
1331
|
hide_value: bool = False,
|
|
1332
|
+
hide_in_modifier: bool = False,
|
|
883
1333
|
):
|
|
884
1334
|
super().__init__(name, description)
|
|
885
1335
|
self._set_values(
|
|
886
1336
|
default_value=default_value,
|
|
1337
|
+
optional_label=optional_label,
|
|
887
1338
|
hide_value=hide_value,
|
|
1339
|
+
hide_in_modifier=hide_in_modifier,
|
|
888
1340
|
)
|
|
889
1341
|
|
|
890
1342
|
|
|
@@ -898,14 +1350,18 @@ class SocketMaterial(SocketBase):
|
|
|
898
1350
|
self,
|
|
899
1351
|
name: str = "Material",
|
|
900
1352
|
default_value: bpy.types.Material | None = None,
|
|
901
|
-
*,
|
|
902
1353
|
description: str = "",
|
|
1354
|
+
*,
|
|
1355
|
+
optional_label: bool = False,
|
|
903
1356
|
hide_value: bool = False,
|
|
1357
|
+
hide_in_modifier: bool = False,
|
|
904
1358
|
):
|
|
905
1359
|
super().__init__(name, description)
|
|
906
1360
|
self._set_values(
|
|
907
1361
|
default_value=default_value,
|
|
1362
|
+
optional_label=optional_label,
|
|
908
1363
|
hide_value=hide_value,
|
|
1364
|
+
hide_in_modifier=hide_in_modifier,
|
|
909
1365
|
)
|
|
910
1366
|
|
|
911
1367
|
|
|
@@ -918,13 +1374,17 @@ class SocketBundle(SocketBase):
|
|
|
918
1374
|
def __init__(
|
|
919
1375
|
self,
|
|
920
1376
|
name: str = "Bundle",
|
|
921
|
-
*,
|
|
922
1377
|
description: str = "",
|
|
1378
|
+
*,
|
|
1379
|
+
optional_label: bool = False,
|
|
923
1380
|
hide_value: bool = False,
|
|
1381
|
+
hide_in_modifier: bool = False,
|
|
924
1382
|
):
|
|
925
1383
|
super().__init__(name, description)
|
|
926
1384
|
self._set_values(
|
|
1385
|
+
optional_label=optional_label,
|
|
927
1386
|
hide_value=hide_value,
|
|
1387
|
+
hide_in_modifier=hide_in_modifier,
|
|
928
1388
|
)
|
|
929
1389
|
|
|
930
1390
|
|
|
@@ -937,11 +1397,15 @@ class SocketClosure(SocketBase):
|
|
|
937
1397
|
def __init__(
|
|
938
1398
|
self,
|
|
939
1399
|
name: str = "Closure",
|
|
940
|
-
*,
|
|
941
1400
|
description: str = "",
|
|
1401
|
+
*,
|
|
1402
|
+
optional_label: bool = False,
|
|
942
1403
|
hide_value: bool = False,
|
|
1404
|
+
hide_in_modifier: bool = False,
|
|
943
1405
|
):
|
|
944
1406
|
super().__init__(name, description)
|
|
945
1407
|
self._set_values(
|
|
1408
|
+
optional_label=optional_label,
|
|
946
1409
|
hide_value=hide_value,
|
|
1410
|
+
hide_in_modifier=hide_in_modifier,
|
|
947
1411
|
)
|