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.
@@ -0,0 +1,4518 @@
1
+ from typing import Literal
2
+
3
+ import bpy
4
+ from bpy.types import NodeSocket
5
+
6
+ from ..builder import NodeBuilder, SocketLinker
7
+ from . import types
8
+ from .types import (
9
+ LINKABLE,
10
+ SOCKET_TYPES,
11
+ TYPE_INPUT_ALL,
12
+ TYPE_INPUT_BOOLEAN,
13
+ TYPE_INPUT_COLOR,
14
+ TYPE_INPUT_INT,
15
+ TYPE_INPUT_MATRIX,
16
+ TYPE_INPUT_MENU,
17
+ TYPE_INPUT_ROTATION,
18
+ TYPE_INPUT_STRING,
19
+ TYPE_INPUT_VALUE,
20
+ TYPE_INPUT_VECTOR,
21
+ _AccumulateFieldDataTypes,
22
+ _AttributeDomains,
23
+ _EvaluateAtIndexDataTypes,
24
+ _IntegerMathOperations,
25
+ _is_default_value,
26
+ _MixColorBlendTypes,
27
+ _MixDataTypes,
28
+ _RandomValueDataTypes,
29
+ _VectorMathOperations,
30
+ )
31
+
32
+
33
+ class AlignRotationToVector(NodeBuilder):
34
+ """Orient a rotation along the given direction"""
35
+
36
+ name = "FunctionNodeAlignRotationToVector"
37
+ node: bpy.types.FunctionNodeAlignRotationToVector
38
+
39
+ def __init__(
40
+ self,
41
+ rotation: TYPE_INPUT_ROTATION = (0.0, 0.0, 0.0),
42
+ factor: TYPE_INPUT_VALUE = 1.0,
43
+ vector: TYPE_INPUT_VECTOR = (0.0, 0.0, 1.0),
44
+ axis: Literal["X", "Y", "Z"] = "Z",
45
+ pivot_axis: Literal["AUTO", "X", "Y", "Z"] = "AUTO",
46
+ **kwargs,
47
+ ):
48
+ super().__init__()
49
+ key_args = {"Rotation": rotation, "Factor": factor, "Vector": vector}
50
+ key_args.update(kwargs)
51
+ self.axis = axis
52
+ self.pivot_axis = pivot_axis
53
+ self._establish_links(**key_args)
54
+
55
+ @property
56
+ def i_rotation(self) -> SocketLinker:
57
+ """Input socket: Rotation"""
58
+ return self._input("Rotation")
59
+
60
+ @property
61
+ def i_factor(self) -> SocketLinker:
62
+ """Input socket: Factor"""
63
+ return self._input("Factor")
64
+
65
+ @property
66
+ def i_vector(self) -> SocketLinker:
67
+ """Input socket: Vector"""
68
+ return self._input("Vector")
69
+
70
+ @property
71
+ def o_rotation(self) -> SocketLinker:
72
+ """Output socket: Rotation"""
73
+ return self._output("Rotation")
74
+
75
+ @property
76
+ def axis(self) -> Literal["X", "Y", "Z"]:
77
+ return self.node.axis
78
+
79
+ @axis.setter
80
+ def axis(self, value: Literal["X", "Y", "Z"]):
81
+ self.node.axis = value
82
+
83
+ @property
84
+ def pivot_axis(self) -> Literal["AUTO", "X", "Y", "Z"]:
85
+ return self.node.pivot_axis
86
+
87
+ @pivot_axis.setter
88
+ def pivot_axis(self, value: Literal["AUTO", "X", "Y", "Z"]):
89
+ self.node.pivot_axis = value
90
+
91
+
92
+ class AxesToRotation(NodeBuilder):
93
+ """Create a rotation from a primary and (ideally orthogonal) secondary axis"""
94
+
95
+ name = "FunctionNodeAxesToRotation"
96
+ node: bpy.types.FunctionNodeAxesToRotation
97
+
98
+ def __init__(
99
+ self,
100
+ primary_axis_vector: TYPE_INPUT_VECTOR = (0.0, 0.0, 1.0),
101
+ secondary_axis_vector: TYPE_INPUT_VECTOR = (1.0, 0.0, 0.0),
102
+ primary_axis: Literal["X", "Y", "Z"] = "Z",
103
+ secondary_axis: Literal["X", "Y", "Z"] = "X",
104
+ **kwargs,
105
+ ):
106
+ super().__init__()
107
+ key_args = {
108
+ "Primary Axis": primary_axis_vector,
109
+ "Secondary Axis": secondary_axis_vector,
110
+ }
111
+ key_args.update(kwargs)
112
+ self.primary_axis = primary_axis
113
+ self.secondary_axis = secondary_axis
114
+ self._establish_links(**key_args)
115
+
116
+ @property
117
+ def i_primary_axis(self) -> SocketLinker:
118
+ """Input socket: Primary Axis"""
119
+ return self._input("Primary Axis")
120
+
121
+ @property
122
+ def i_secondary_axis(self) -> SocketLinker:
123
+ """Input socket: Secondary Axis"""
124
+ return self._input("Secondary Axis")
125
+
126
+ @property
127
+ def o_rotation(self) -> SocketLinker:
128
+ """Output socket: Rotation"""
129
+ return self._output("Rotation")
130
+
131
+ @property
132
+ def primary_axis(self) -> Literal["X", "Y", "Z"]:
133
+ return self.node.primary_axis
134
+
135
+ @primary_axis.setter
136
+ def primary_axis(self, value: Literal["X", "Y", "Z"]):
137
+ self.node.primary_axis = value
138
+
139
+ @property
140
+ def secondary_axis(self) -> Literal["X", "Y", "Z"]:
141
+ return self.node.secondary_axis
142
+
143
+ @secondary_axis.setter
144
+ def secondary_axis(self, value: Literal["X", "Y", "Z"]):
145
+ self.node.secondary_axis = value
146
+
147
+
148
+ class AxisAngleToRotation(NodeBuilder):
149
+ """Build a rotation from an axis and a rotation around that axis"""
150
+
151
+ name = "FunctionNodeAxisAngleToRotation"
152
+ node: bpy.types.FunctionNodeAxisAngleToRotation
153
+
154
+ def __init__(
155
+ self,
156
+ axis: TYPE_INPUT_VECTOR = (0.0, 0.0, 1.0),
157
+ angle: TYPE_INPUT_VALUE = 0.0,
158
+ **kwargs,
159
+ ):
160
+ super().__init__()
161
+ key_args = {"Axis": axis, "Angle": angle}
162
+ key_args.update(kwargs)
163
+
164
+ self._establish_links(**key_args)
165
+
166
+ @property
167
+ def i_axis(self) -> SocketLinker:
168
+ """Input socket: Axis"""
169
+ return self._input("Axis")
170
+
171
+ @property
172
+ def i_angle(self) -> SocketLinker:
173
+ """Input socket: Angle"""
174
+ return self._input("Angle")
175
+
176
+ @property
177
+ def o_rotation(self) -> SocketLinker:
178
+ """Output socket: Rotation"""
179
+ return self._output("Rotation")
180
+
181
+
182
+ class BitMath(NodeBuilder):
183
+ """Perform bitwise operations on 32-bit integers"""
184
+
185
+ name = "FunctionNodeBitMath"
186
+ node: bpy.types.FunctionNodeBitMath
187
+
188
+ def __init__(
189
+ self,
190
+ a: TYPE_INPUT_INT = 0,
191
+ b: TYPE_INPUT_INT = 0,
192
+ operation: Literal["AND", "OR", "XOR", "NOT", "SHIFT", "ROTATE"] = "AND",
193
+ **kwargs,
194
+ ):
195
+ super().__init__()
196
+ key_args = {"A": a, "B": b}
197
+ key_args.update(kwargs)
198
+ self.operation = operation
199
+ self._establish_links(**key_args)
200
+
201
+ @classmethod
202
+ def l_and(cls, a: TYPE_INPUT_INT = 0, b: TYPE_INPUT_INT = 0) -> "BitMath":
203
+ """Create Bit Math with operation 'And'."""
204
+ return cls(operation="AND", A=a, B=b)
205
+
206
+ @classmethod
207
+ def l_or(cls, a: TYPE_INPUT_INT = 0, b: TYPE_INPUT_INT = 0) -> "BitMath":
208
+ """Create Bit Math with operation 'Or'."""
209
+ return cls(operation="OR", A=a, B=b)
210
+
211
+ @classmethod
212
+ def l_exclusive_or(cls, a: TYPE_INPUT_INT = 0, b: TYPE_INPUT_INT = 0) -> "BitMath":
213
+ """Create Bit Math with operation 'XOR'."""
214
+ return cls(operation="XOR", A=a, B=b)
215
+
216
+ @classmethod
217
+ def l_not(cls, a: TYPE_INPUT_INT = 0) -> "BitMath":
218
+ """Create Bit Math with operation 'Not'."""
219
+ return cls(operation="NOT", A=a)
220
+
221
+ @classmethod
222
+ def shift(cls, a: TYPE_INPUT_INT = 0, shift: TYPE_INPUT_INT = 0) -> "BitMath":
223
+ """Create Bit Math with operation 'Shift'."""
224
+ return cls(operation="SHIFT", A=a, B=shift)
225
+
226
+ @classmethod
227
+ def rotate(cls, a: TYPE_INPUT_INT = 0, shift: TYPE_INPUT_INT = 0) -> "BitMath":
228
+ """Create Bit Math with operation 'Rotate'."""
229
+ return cls(operation="ROTATE", A=a, B=shift)
230
+
231
+ @property
232
+ def i_a(self) -> SocketLinker:
233
+ """Input socket: A"""
234
+ return self._input("A")
235
+
236
+ @property
237
+ def i_b(self) -> SocketLinker:
238
+ """Input socket: B"""
239
+ return self._input("B")
240
+
241
+ @property
242
+ def o_value(self) -> SocketLinker:
243
+ """Output socket: Value"""
244
+ return self._output("Value")
245
+
246
+ @property
247
+ def operation(self) -> Literal["AND", "OR", "XOR", "NOT", "SHIFT", "ROTATE"]:
248
+ return self.node.operation
249
+
250
+ @operation.setter
251
+ def operation(self, value: Literal["AND", "OR", "XOR", "NOT", "SHIFT", "ROTATE"]):
252
+ self.node.operation = value
253
+
254
+
255
+ class CombineColor(NodeBuilder):
256
+ """Combine four channels into a single color, based on a particular color model"""
257
+
258
+ name = "FunctionNodeCombineColor"
259
+ node: bpy.types.FunctionNodeCombineColor
260
+
261
+ def __init__(
262
+ self,
263
+ red: TYPE_INPUT_VALUE = 0.0,
264
+ green: TYPE_INPUT_VALUE = 0.0,
265
+ blue: TYPE_INPUT_VALUE = 0.0,
266
+ alpha: TYPE_INPUT_VALUE = 1.0,
267
+ mode: Literal["RGB", "HSV", "HSL"] = "RGB",
268
+ ):
269
+ super().__init__()
270
+ key_args = {"Red": red, "Green": green, "Blue": blue, "Alpha": alpha}
271
+ self.mode = mode
272
+ self._establish_links(**key_args)
273
+
274
+ @classmethod
275
+ def rgb(
276
+ cls,
277
+ red: TYPE_INPUT_VALUE = 0.0,
278
+ green: TYPE_INPUT_VALUE = 0.0,
279
+ blue: TYPE_INPUT_VALUE = 0.0,
280
+ alpha: TYPE_INPUT_VALUE = 1.0,
281
+ ):
282
+ return cls(red=red, green=green, blue=blue, alpha=alpha, mode="RGB")
283
+
284
+ @classmethod
285
+ def hsv(
286
+ cls,
287
+ hue: TYPE_INPUT_VALUE = 0.0,
288
+ saturation: TYPE_INPUT_VALUE = 0.0,
289
+ value: TYPE_INPUT_VALUE = 0.0,
290
+ alpha: TYPE_INPUT_VALUE = 1.0,
291
+ ):
292
+ return cls(red=hue, green=saturation, blue=value, alpha=alpha, mode="HSV")
293
+
294
+ @classmethod
295
+ def hsl(
296
+ cls,
297
+ hue: TYPE_INPUT_VALUE = 0.0,
298
+ saturation: TYPE_INPUT_VALUE = 0.0,
299
+ lightness: TYPE_INPUT_VALUE = 0.0,
300
+ alpha: TYPE_INPUT_VALUE = 1.0,
301
+ ):
302
+ return cls(red=hue, green=saturation, blue=lightness, alpha=alpha, mode="HSL")
303
+
304
+ @property
305
+ def i_red(self) -> SocketLinker:
306
+ """Input socket: Red"""
307
+ return self._input("Red")
308
+
309
+ @property
310
+ def i_green(self) -> SocketLinker:
311
+ """Input socket: Green"""
312
+ return self._input("Green")
313
+
314
+ @property
315
+ def i_blue(self) -> SocketLinker:
316
+ """Input socket: Blue"""
317
+ return self._input("Blue")
318
+
319
+ @property
320
+ def i_alpha(self) -> SocketLinker:
321
+ """Input socket: Alpha"""
322
+ return self._input("Alpha")
323
+
324
+ @property
325
+ def o_color(self) -> SocketLinker:
326
+ """Output socket: Color"""
327
+ return self._output("Color")
328
+
329
+ @property
330
+ def mode(self) -> Literal["RGB", "HSV", "HSL"]:
331
+ return self.node.mode
332
+
333
+ @mode.setter
334
+ def mode(self, value: Literal["RGB", "HSV", "HSL"]):
335
+ self.node.mode = value
336
+
337
+
338
+ class CombineMatrix(NodeBuilder):
339
+ """Construct a 4x4 matrix from its individual values"""
340
+
341
+ name = "FunctionNodeCombineMatrix"
342
+ node: bpy.types.FunctionNodeCombineMatrix
343
+
344
+ def __init__(
345
+ self,
346
+ column_1_row_1: TYPE_INPUT_VALUE = 1.0,
347
+ column_1_row_2: TYPE_INPUT_VALUE = 0.0,
348
+ column_1_row_3: TYPE_INPUT_VALUE = 0.0,
349
+ column_1_row_4: TYPE_INPUT_VALUE = 0.0,
350
+ column_2_row_1: TYPE_INPUT_VALUE = 0.0,
351
+ column_2_row_2: TYPE_INPUT_VALUE = 1.0,
352
+ column_2_row_3: TYPE_INPUT_VALUE = 0.0,
353
+ column_2_row_4: TYPE_INPUT_VALUE = 0.0,
354
+ column_3_row_1: TYPE_INPUT_VALUE = 0.0,
355
+ column_3_row_2: TYPE_INPUT_VALUE = 0.0,
356
+ column_3_row_3: TYPE_INPUT_VALUE = 1.0,
357
+ column_3_row_4: TYPE_INPUT_VALUE = 0.0,
358
+ column_4_row_1: TYPE_INPUT_VALUE = 0.0,
359
+ column_4_row_2: TYPE_INPUT_VALUE = 0.0,
360
+ column_4_row_3: TYPE_INPUT_VALUE = 0.0,
361
+ column_4_row_4: TYPE_INPUT_VALUE = 1.0,
362
+ **kwargs,
363
+ ):
364
+ super().__init__()
365
+ key_args = {
366
+ "Column 1 Row 1": column_1_row_1,
367
+ "Column 1 Row 2": column_1_row_2,
368
+ "Column 1 Row 3": column_1_row_3,
369
+ "Column 1 Row 4": column_1_row_4,
370
+ "Column 2 Row 1": column_2_row_1,
371
+ "Column 2 Row 2": column_2_row_2,
372
+ "Column 2 Row 3": column_2_row_3,
373
+ "Column 2 Row 4": column_2_row_4,
374
+ "Column 3 Row 1": column_3_row_1,
375
+ "Column 3 Row 2": column_3_row_2,
376
+ "Column 3 Row 3": column_3_row_3,
377
+ "Column 3 Row 4": column_3_row_4,
378
+ "Column 4 Row 1": column_4_row_1,
379
+ "Column 4 Row 2": column_4_row_2,
380
+ "Column 4 Row 3": column_4_row_3,
381
+ "Column 4 Row 4": column_4_row_4,
382
+ }
383
+ key_args.update(kwargs)
384
+
385
+ self._establish_links(**key_args)
386
+
387
+ @property
388
+ def i_column_1_row_1(self) -> SocketLinker:
389
+ """Input socket: Column 1 Row 1"""
390
+ return self._input("Column 1 Row 1")
391
+
392
+ @property
393
+ def i_column_1_row_2(self) -> SocketLinker:
394
+ """Input socket: Column 1 Row 2"""
395
+ return self._input("Column 1 Row 2")
396
+
397
+ @property
398
+ def i_column_1_row_3(self) -> SocketLinker:
399
+ """Input socket: Column 1 Row 3"""
400
+ return self._input("Column 1 Row 3")
401
+
402
+ @property
403
+ def i_column_1_row_4(self) -> SocketLinker:
404
+ """Input socket: Column 1 Row 4"""
405
+ return self._input("Column 1 Row 4")
406
+
407
+ @property
408
+ def i_column_2_row_1(self) -> SocketLinker:
409
+ """Input socket: Column 2 Row 1"""
410
+ return self._input("Column 2 Row 1")
411
+
412
+ @property
413
+ def i_column_2_row_2(self) -> SocketLinker:
414
+ """Input socket: Column 2 Row 2"""
415
+ return self._input("Column 2 Row 2")
416
+
417
+ @property
418
+ def i_column_2_row_3(self) -> SocketLinker:
419
+ """Input socket: Column 2 Row 3"""
420
+ return self._input("Column 2 Row 3")
421
+
422
+ @property
423
+ def i_column_2_row_4(self) -> SocketLinker:
424
+ """Input socket: Column 2 Row 4"""
425
+ return self._input("Column 2 Row 4")
426
+
427
+ @property
428
+ def i_column_3_row_1(self) -> SocketLinker:
429
+ """Input socket: Column 3 Row 1"""
430
+ return self._input("Column 3 Row 1")
431
+
432
+ @property
433
+ def i_column_3_row_2(self) -> SocketLinker:
434
+ """Input socket: Column 3 Row 2"""
435
+ return self._input("Column 3 Row 2")
436
+
437
+ @property
438
+ def i_column_3_row_3(self) -> SocketLinker:
439
+ """Input socket: Column 3 Row 3"""
440
+ return self._input("Column 3 Row 3")
441
+
442
+ @property
443
+ def i_column_3_row_4(self) -> SocketLinker:
444
+ """Input socket: Column 3 Row 4"""
445
+ return self._input("Column 3 Row 4")
446
+
447
+ @property
448
+ def i_column_4_row_1(self) -> SocketLinker:
449
+ """Input socket: Column 4 Row 1"""
450
+ return self._input("Column 4 Row 1")
451
+
452
+ @property
453
+ def i_column_4_row_2(self) -> SocketLinker:
454
+ """Input socket: Column 4 Row 2"""
455
+ return self._input("Column 4 Row 2")
456
+
457
+ @property
458
+ def i_column_4_row_3(self) -> SocketLinker:
459
+ """Input socket: Column 4 Row 3"""
460
+ return self._input("Column 4 Row 3")
461
+
462
+ @property
463
+ def i_column_4_row_4(self) -> SocketLinker:
464
+ """Input socket: Column 4 Row 4"""
465
+ return self._input("Column 4 Row 4")
466
+
467
+ @property
468
+ def o_matrix(self) -> SocketLinker:
469
+ """Output socket: Matrix"""
470
+ return self._output("Matrix")
471
+
472
+
473
+ class CombineTransform(NodeBuilder):
474
+ """Combine a translation vector, a rotation, and a scale vector into a transformation matrix"""
475
+
476
+ name = "FunctionNodeCombineTransform"
477
+ node: bpy.types.FunctionNodeCombineTransform
478
+
479
+ def __init__(
480
+ self,
481
+ translation: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
482
+ rotation: TYPE_INPUT_ROTATION = (0.0, 0.0, 0.0),
483
+ scale: TYPE_INPUT_VECTOR = (1.0, 1.0, 1.0),
484
+ **kwargs,
485
+ ):
486
+ super().__init__()
487
+ key_args = {"Translation": translation, "Rotation": rotation, "Scale": scale}
488
+ key_args.update(kwargs)
489
+
490
+ self._establish_links(**key_args)
491
+
492
+ @property
493
+ def i_translation(self) -> SocketLinker:
494
+ """Input socket: Translation"""
495
+ return self._input("Translation")
496
+
497
+ @property
498
+ def i_rotation(self) -> SocketLinker:
499
+ """Input socket: Rotation"""
500
+ return self._input("Rotation")
501
+
502
+ @property
503
+ def i_scale(self) -> SocketLinker:
504
+ """Input socket: Scale"""
505
+ return self._input("Scale")
506
+
507
+ @property
508
+ def o_transform(self) -> SocketLinker:
509
+ """Output socket: Transform"""
510
+ return self._output("Transform")
511
+
512
+
513
+ _CompareOperations = Literal[
514
+ "LESS_THAN",
515
+ "LESS_EQUAL",
516
+ "GREATER_THAN",
517
+ "GREATER_EQUAL",
518
+ "EQUAL",
519
+ "NOT_EQUAL",
520
+ "BRIGHTER",
521
+ "DARKER",
522
+ ]
523
+
524
+ _CompareDataTypes = Literal[
525
+ "FLOAT",
526
+ "INT",
527
+ "VECTOR",
528
+ "RGBA",
529
+ "ROTATION",
530
+ "STRING",
531
+ ]
532
+
533
+ _CompareVectorModes = Literal[
534
+ "ELEMENT", "LENGTH", "AVERAGE", "DOT_PRODUCT", "DIRECTION"
535
+ ]
536
+
537
+
538
+ def _compare_operation_method(operation: _CompareOperations):
539
+ """Create a factory for Compare with a specific operation"""
540
+
541
+ class CompareOperationFactory:
542
+ @staticmethod
543
+ def float(
544
+ a: TYPE_INPUT_VALUE = 0.0,
545
+ b: TYPE_INPUT_VALUE = 0.0,
546
+ *,
547
+ epsilon: TYPE_INPUT_VALUE = 0.0001,
548
+ ) -> "Compare":
549
+ return Compare.float(operation=operation, a=a, b=b, epsilon=epsilon)
550
+
551
+ @staticmethod
552
+ def integer(a: TYPE_INPUT_INT = 0, b: TYPE_INPUT_INT = 0) -> "Compare":
553
+ return Compare.integer(operation=operation, a=a, b=b)
554
+
555
+ @staticmethod
556
+ def vector(
557
+ a: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
558
+ b: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
559
+ *,
560
+ mode: _CompareVectorModes = "ELEMENT",
561
+ c: TYPE_INPUT_VALUE = None,
562
+ angle: TYPE_INPUT_VALUE = None,
563
+ epsilon: TYPE_INPUT_VALUE = None,
564
+ ) -> "Compare":
565
+ return Compare.vector(
566
+ operation=operation,
567
+ a=a,
568
+ b=b,
569
+ mode=mode,
570
+ c=c,
571
+ angle=angle,
572
+ epsilon=epsilon,
573
+ )
574
+
575
+ @staticmethod
576
+ def color(
577
+ a: TYPE_INPUT_COLOR = None,
578
+ b: TYPE_INPUT_COLOR = None,
579
+ epsilon: TYPE_INPUT_VALUE = None,
580
+ ) -> "Compare":
581
+ return Compare.color(operation=operation, a=a, b=b, epsilon=epsilon)
582
+
583
+ @staticmethod
584
+ def string(a: TYPE_INPUT_STRING = "", b: TYPE_INPUT_STRING = "") -> "Compare":
585
+ return Compare.string(operation=operation, a=a, b=b)
586
+
587
+ return CompareOperationFactory()
588
+
589
+
590
+ class Compare(NodeBuilder):
591
+ """Perform a comparison operation on the two given inputs"""
592
+
593
+ name = "FunctionNodeCompare"
594
+ node: bpy.types.FunctionNodeCompare
595
+
596
+ less_than = _compare_operation_method("LESS_THAN")
597
+ less_equal = _compare_operation_method("LESS_EQUAL")
598
+ greater_than = _compare_operation_method("GREATER_THAN")
599
+ greater_equal = _compare_operation_method("GREATER_EQUAL")
600
+ equal = _compare_operation_method("EQUAL")
601
+ not_equal = _compare_operation_method("NOT_EQUAL")
602
+ brighter = _compare_operation_method("BRIGHTER")
603
+ darker = _compare_operation_method("DARKER")
604
+
605
+ def __init__(
606
+ self,
607
+ operation: _CompareOperations = "GREATER_THAN",
608
+ data_type: _CompareDataTypes = "FLOAT",
609
+ **kwargs,
610
+ ):
611
+ super().__init__()
612
+ self.operation = operation
613
+ self.data_type = data_type
614
+ if self.data_type == "VECTOR":
615
+ self.mode = kwargs["mode"]
616
+ self._establish_links(**kwargs)
617
+
618
+ @classmethod
619
+ def float(
620
+ cls,
621
+ a: TYPE_INPUT_VALUE = 0.0,
622
+ b: TYPE_INPUT_VALUE = 0.0,
623
+ operation: _CompareOperations = "LESS_THAN",
624
+ *,
625
+ epsilon: TYPE_INPUT_VALUE = 0.0001,
626
+ ):
627
+ kwargs = {"operation": operation, "data_type": "FLOAT", "A": a, "B": b}
628
+ if operation in ("EQUAL", "NOT_EQUAL"):
629
+ kwargs["Epsilon"] = epsilon
630
+ return cls(**kwargs)
631
+
632
+ @classmethod
633
+ def integer(
634
+ cls,
635
+ a: TYPE_INPUT_INT = 0,
636
+ b: TYPE_INPUT_INT = 0,
637
+ operation: _CompareOperations = "LESS_THAN",
638
+ ) -> "Compare":
639
+ return cls(operation=operation, data_type="INT", A_INT=a, B_INT=b)
640
+
641
+ @classmethod
642
+ def vector(
643
+ cls,
644
+ a: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
645
+ b: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
646
+ operation: _CompareOperations = "LESS_THAN",
647
+ *,
648
+ mode: _CompareVectorModes = "ELEMENT",
649
+ c: TYPE_INPUT_VALUE = None,
650
+ angle: TYPE_INPUT_VALUE = None,
651
+ epsilon: TYPE_INPUT_VALUE = None,
652
+ ) -> "Compare":
653
+ kwargs = {
654
+ "operation": operation,
655
+ "data_type": "VECTOR",
656
+ "mode": mode,
657
+ "A_VEC3": a,
658
+ "B_VEC3": b,
659
+ }
660
+ if operation in ("EQUAL", "NOT_EQUAL"):
661
+ kwargs["Epsilon"] = epsilon
662
+
663
+ match mode:
664
+ case "DIRECTION":
665
+ kwargs["Angle"] = angle
666
+ case "DOT_PRODUCT":
667
+ kwargs["C"] = c
668
+ case _:
669
+ pass
670
+
671
+ return cls(**kwargs)
672
+
673
+ @classmethod
674
+ def color(
675
+ cls,
676
+ a: TYPE_INPUT_COLOR = None,
677
+ b: TYPE_INPUT_COLOR = None,
678
+ operation: _CompareOperations = "EQUAL",
679
+ *,
680
+ epsilon: TYPE_INPUT_VALUE = None,
681
+ ) -> "Compare":
682
+ """Create Compare with operation 'Color'."""
683
+ kwargs = {
684
+ "operation": operation,
685
+ "data_type": "RGBA",
686
+ "A_COL": a,
687
+ "B_COL": b,
688
+ }
689
+ if operation in ("EQUAL", "NOT_EQUAL"):
690
+ kwargs["Epsilon"] = epsilon
691
+ return cls(**kwargs)
692
+
693
+ @classmethod
694
+ def string(
695
+ cls,
696
+ a,
697
+ b,
698
+ ) -> "Compare":
699
+ """Create Compare with operation 'String'."""
700
+ return cls(mode="STRING", A_STR=a, B_STR=b)
701
+
702
+ def _suffix(self) -> str:
703
+ suffix_lookup = {
704
+ "FLOAT": "",
705
+ "INT": "_INT",
706
+ "VECTOR": "_VEC",
707
+ "RGBA": "_COL",
708
+ "STRING": "_STR",
709
+ }
710
+ return suffix_lookup[self.data_type]
711
+
712
+ @property
713
+ def i_a(self) -> SocketLinker:
714
+ """Input socket: A"""
715
+ return self._input(f"A{self._suffix()}")
716
+
717
+ @property
718
+ def i_b(self) -> SocketLinker:
719
+ """Input socket: B"""
720
+ return self._input(f"B{self._suffix()}")
721
+
722
+ @property
723
+ def o_result(self) -> SocketLinker:
724
+ """Output socket: Result"""
725
+ return self._output("Result")
726
+
727
+ @property
728
+ def operation(
729
+ self,
730
+ ) -> _CompareOperations:
731
+ return self.node.operation
732
+
733
+ @operation.setter
734
+ def operation(
735
+ self,
736
+ value: _CompareOperations,
737
+ ):
738
+ self.node.operation = value
739
+
740
+ @property
741
+ def data_type(
742
+ self,
743
+ ) -> _CompareDataTypes:
744
+ return self.node.data_type # type: ignore
745
+
746
+ @data_type.setter
747
+ def data_type(
748
+ self,
749
+ value: _CompareDataTypes,
750
+ ):
751
+ self.node.data_type = value
752
+
753
+ @property
754
+ def mode(
755
+ self,
756
+ ) -> _CompareVectorModes:
757
+ return self.node.mode
758
+
759
+ @mode.setter
760
+ def mode(
761
+ self,
762
+ value: _CompareVectorModes,
763
+ ):
764
+ self.node.mode = value
765
+
766
+
767
+ class EulerToRotation(NodeBuilder):
768
+ """Build a rotation from separate angles around each axis"""
769
+
770
+ name = "FunctionNodeEulerToRotation"
771
+ node: bpy.types.FunctionNodeEulerToRotation
772
+
773
+ def __init__(self, euler: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0)):
774
+ super().__init__()
775
+ key_args = {"Euler": euler}
776
+ self._establish_links(**key_args)
777
+
778
+ @property
779
+ def i_euler(self) -> SocketLinker:
780
+ """Input socket: Euler"""
781
+ return self._input("Euler")
782
+
783
+ @property
784
+ def o_rotation(self) -> SocketLinker:
785
+ """Output socket: Rotation"""
786
+ return self._output("Rotation")
787
+
788
+
789
+ class FindInString(NodeBuilder):
790
+ """Find the number of times a given string occurs in another string and the position of the first match"""
791
+
792
+ name = "FunctionNodeFindInString"
793
+ node: bpy.types.FunctionNodeFindInString
794
+
795
+ def __init__(
796
+ self,
797
+ string: TYPE_INPUT_STRING = "",
798
+ search: TYPE_INPUT_STRING = "",
799
+ ):
800
+ super().__init__()
801
+ key_args = {"String": string, "Search": search}
802
+
803
+ self._establish_links(**key_args)
804
+
805
+ @property
806
+ def i_string(self) -> SocketLinker:
807
+ """Input socket: String"""
808
+ return self._input("String")
809
+
810
+ @property
811
+ def i_search(self) -> SocketLinker:
812
+ """Input socket: Search"""
813
+ return self._input("Search")
814
+
815
+ @property
816
+ def o_first_found(self) -> SocketLinker:
817
+ """Output socket: First Found"""
818
+ return self._output("First Found")
819
+
820
+ @property
821
+ def o_count(self) -> SocketLinker:
822
+ """Output socket: Count"""
823
+ return self._output("Count")
824
+
825
+
826
+ class FloatToInteger(NodeBuilder):
827
+ """Convert the given floating-point number to an integer, with a choice of methods"""
828
+
829
+ name = "FunctionNodeFloatToInt"
830
+ node: bpy.types.FunctionNodeFloatToInt
831
+
832
+ def __init__(
833
+ self,
834
+ float: TYPE_INPUT_VALUE = 0.0,
835
+ rounding_mode: Literal["ROUND", "FLOOR", "CEILING", "TRUNCATE"] = "ROUND",
836
+ ):
837
+ super().__init__()
838
+ key_args = {"Float": float}
839
+ self.rounding_mode = rounding_mode
840
+ self._establish_links(**key_args)
841
+
842
+ @property
843
+ def i_float(self) -> SocketLinker:
844
+ """Input socket: Float"""
845
+ return self._input("Float")
846
+
847
+ @property
848
+ def o_integer(self) -> SocketLinker:
849
+ """Output socket: Integer"""
850
+ return self._output("Integer")
851
+
852
+ @property
853
+ def rounding_mode(self) -> Literal["ROUND", "FLOOR", "CEILING", "TRUNCATE"]:
854
+ return self.node.rounding_mode
855
+
856
+ @rounding_mode.setter
857
+ def rounding_mode(self, value: Literal["ROUND", "FLOOR", "CEILING", "TRUNCATE"]):
858
+ self.node.rounding_mode = value
859
+
860
+
861
+ class FormatString(NodeBuilder):
862
+ """Insert values into a string using a Python and path template compatible formatting syntax"""
863
+
864
+ name = "FunctionNodeFormatString"
865
+ node: bpy.types.FunctionNodeFormatString
866
+ _socket_data_types = ("VALUE", "INT", "STRING")
867
+
868
+ def __init__(
869
+ self,
870
+ *args,
871
+ format: TYPE_INPUT_STRING = "",
872
+ **kwargs,
873
+ ):
874
+ super().__init__()
875
+ key_args = {"Format": format}
876
+ key_args.update(self._add_inputs(*args, **kwargs)) # type: ignore
877
+ self._establish_links(**key_args)
878
+
879
+ def _add_socket( # type: ignore
880
+ self,
881
+ name: str,
882
+ type: Literal["FLOAT", "INT", "STRING"] = "FLOAT",
883
+ default_value: float | int | str | None = None,
884
+ ):
885
+ item = self.node.format_items.new(socket_type=type, name=name)
886
+ if default_value is not None:
887
+ try:
888
+ self.node.inputs[item.name].default_value = default_value # type: ignore
889
+ except TypeError as e:
890
+ raise ValueError(
891
+ f"Invalid default value for {type}: {default_value}"
892
+ ) from e
893
+ return self.node.inputs[item.name]
894
+
895
+ @property
896
+ def i_format(self) -> SocketLinker:
897
+ """Input socket: Format"""
898
+ return self._input("Format")
899
+
900
+ @property
901
+ def i_input_socket(self) -> SocketLinker:
902
+ """Input socket:"""
903
+ return self._input("__extend__")
904
+
905
+ @property
906
+ def items(self) -> dict[str, SocketLinker]:
907
+ """Input sockets:"""
908
+ return {socket.name: self._input(socket.name) for socket in self.node.inputs}
909
+
910
+ @property
911
+ def o_string(self) -> SocketLinker:
912
+ """Output socket: String"""
913
+ return self._output("String")
914
+
915
+
916
+ _HashValueDataTypes = Literal[
917
+ "FLOAT",
918
+ "INT",
919
+ "VECTOR",
920
+ "RGBA",
921
+ "ROTATION",
922
+ "MATRIX",
923
+ "STRING",
924
+ ]
925
+
926
+
927
+ class HashValue(NodeBuilder):
928
+ """Generate a randomized integer using the given input value as a seed"""
929
+
930
+ name = "FunctionNodeHashValue"
931
+ node: bpy.types.FunctionNodeHashValue
932
+
933
+ def __init__(
934
+ self,
935
+ value: TYPE_INPUT_VALUE
936
+ | TYPE_INPUT_INT
937
+ | TYPE_INPUT_VECTOR
938
+ | TYPE_INPUT_STRING
939
+ | TYPE_INPUT_COLOR
940
+ | TYPE_INPUT_ROTATION
941
+ | TYPE_INPUT_MATRIX
942
+ | TYPE_INPUT_STRING = None,
943
+ seed: TYPE_INPUT_INT = 0,
944
+ *,
945
+ data_type: _HashValueDataTypes = "INT",
946
+ ):
947
+ super().__init__()
948
+ key_args = {"Value": value, "Seed": seed}
949
+ self.data_type = data_type
950
+ self._establish_links(**key_args)
951
+
952
+ @classmethod
953
+ def float(cls, value: TYPE_INPUT_VALUE = None, seed: TYPE_INPUT_INT = 0):
954
+ return cls(value, seed, "FLOAT")
955
+
956
+ @classmethod
957
+ def integer(cls, value: TYPE_INPUT_INT = None, seed: TYPE_INPUT_INT = 0):
958
+ return cls(value, seed, "INT")
959
+
960
+ @classmethod
961
+ def vector(cls, value: TYPE_INPUT_VECTOR = None, seed: TYPE_INPUT_INT = 0):
962
+ return cls(value, seed, "VECTOR")
963
+
964
+ @classmethod
965
+ def string(cls, value: TYPE_INPUT_STRING = None, seed: TYPE_INPUT_INT = 0):
966
+ return cls(value, seed, "STRING")
967
+
968
+ @classmethod
969
+ def color(cls, value: TYPE_INPUT_COLOR = None, seed: TYPE_INPUT_INT = 0):
970
+ return cls(value, seed, "RGBA")
971
+
972
+ @property
973
+ def i_value(self) -> SocketLinker:
974
+ """Input socket: Value"""
975
+ return self._input("Value")
976
+
977
+ @property
978
+ def i_seed(self) -> SocketLinker:
979
+ """Input socket: Seed"""
980
+ return self._input("Seed")
981
+
982
+ @property
983
+ def o_hash(self) -> SocketLinker:
984
+ """Output socket: Hash"""
985
+ return self._output("Hash")
986
+
987
+ @property
988
+ def data_type(
989
+ self,
990
+ ) -> _HashValueDataTypes:
991
+ return self.node.data_type # type: ignore
992
+
993
+ @data_type.setter
994
+ def data_type(
995
+ self,
996
+ value: _HashValueDataTypes,
997
+ ):
998
+ self.node.data_type = value
999
+
1000
+
1001
+ class Math(NodeBuilder):
1002
+ """Perform math operations"""
1003
+
1004
+ name = "ShaderNodeMath"
1005
+ node: bpy.types.ShaderNodeMath # type: ignore
1006
+
1007
+ def __init__(
1008
+ self,
1009
+ operation: types.NodeMathItems = "ADD",
1010
+ use_clamp: bool = False,
1011
+ **kwargs,
1012
+ ):
1013
+ super().__init__()
1014
+ self.operation = operation
1015
+ self.use_clamp = use_clamp
1016
+ self._establish_links(**kwargs)
1017
+
1018
+ @property
1019
+ def operation(self) -> types.NodeMathItems:
1020
+ return self.node.operation
1021
+
1022
+ @operation.setter
1023
+ def operation(self, value: types.NodeMathItems):
1024
+ self.node.operation = value
1025
+
1026
+ @property
1027
+ def use_clamp(self) -> bool:
1028
+ return self.node.use_clamp
1029
+
1030
+ @use_clamp.setter
1031
+ def use_clamp(self, value: bool):
1032
+ self.node.use_clamp = value
1033
+
1034
+ @property
1035
+ def o_value(self) -> SocketLinker:
1036
+ return self._output("Value") # type: ignore
1037
+
1038
+ @property
1039
+ def i_value(self) -> SocketLinker:
1040
+ return self._input("Value")
1041
+
1042
+ @property
1043
+ def i_value_001(self) -> SocketLinker:
1044
+ return self._input("Value_001")
1045
+
1046
+ @property
1047
+ def i_value_002(self) -> SocketLinker:
1048
+ return self._input("Value_002")
1049
+
1050
+ @classmethod
1051
+ def add(
1052
+ cls,
1053
+ a: float | LINKABLE = 0.5,
1054
+ b: float | LINKABLE = 0.5,
1055
+ ) -> "Math":
1056
+ """Create Math with operation of `a + b`."""
1057
+ return cls(operation="ADD", Value=a, Value_001=b)
1058
+
1059
+ @classmethod
1060
+ def subtract(
1061
+ cls,
1062
+ a: float | LINKABLE = 0.5,
1063
+ b: float | LINKABLE = 0.5,
1064
+ ) -> "Math":
1065
+ """Create Math with operation of `a - b`."""
1066
+ return cls(operation="SUBTRACT", Value=a, Value_001=b)
1067
+
1068
+ @classmethod
1069
+ def multiply(
1070
+ cls,
1071
+ a: float | LINKABLE = 0.5,
1072
+ b: float | LINKABLE = 0.5,
1073
+ ) -> "Math":
1074
+ """Create Math with operation of `a * b`."""
1075
+ return cls(operation="MULTIPLY", Value=a, Value_001=b)
1076
+
1077
+ @classmethod
1078
+ def divide(
1079
+ cls,
1080
+ a: float | LINKABLE = 0.5,
1081
+ b: float | LINKABLE = 0.5,
1082
+ ) -> "Math":
1083
+ """Create Math with operation of `a / b`."""
1084
+ return cls(operation="DIVIDE", Value=a, Value_001=b)
1085
+
1086
+ @classmethod
1087
+ def multiply_add(
1088
+ cls,
1089
+ a: float | LINKABLE = 0.5,
1090
+ b: float | LINKABLE = 0.5,
1091
+ c: float | LINKABLE = 0.5,
1092
+ ) -> "Math":
1093
+ """Create Math with operation `a * b + c`."""
1094
+ return cls(operation="MULTIPLY_ADD", Value=a, Value_001=b, Value_002=c)
1095
+
1096
+ @classmethod
1097
+ def power(
1098
+ cls,
1099
+ base: float | LINKABLE = 0.5,
1100
+ exponent: float | LINKABLE = 0.5,
1101
+ ) -> "Math":
1102
+ """Create Math with operation `base ** exponent`."""
1103
+ return cls(operation="POWER", Value=base, Value_001=exponent)
1104
+
1105
+ @classmethod
1106
+ def logarithm(
1107
+ cls,
1108
+ value: float | LINKABLE = 0.5,
1109
+ base: float | LINKABLE = 0.5,
1110
+ ) -> "Math":
1111
+ """Create Math with operation `log(value, base)`."""
1112
+ return cls(operation="LOGARITHM", Value=value, Value_001=base)
1113
+
1114
+ @classmethod
1115
+ def sqrt(
1116
+ cls,
1117
+ value: float | LINKABLE = 0.5,
1118
+ ) -> "Math":
1119
+ """Create Math with operation `sqrt(value)`."""
1120
+ return cls(operation="SQRT", Value=value)
1121
+
1122
+ @classmethod
1123
+ def inverse_sqrt(
1124
+ cls,
1125
+ value: float | LINKABLE = 0.5,
1126
+ ) -> "Math":
1127
+ """Create Math with operation `inverse_sqrt(value)`."""
1128
+ return cls(operation="INVERSE_SQRT", Value=value)
1129
+
1130
+ @classmethod
1131
+ def absolute(
1132
+ cls,
1133
+ value: float | LINKABLE = 0.5,
1134
+ ) -> "Math":
1135
+ """Create Math with operation `abs(value)`."""
1136
+ return cls(operation="ABSOLUTE", Value=value)
1137
+
1138
+ @classmethod
1139
+ def exponent(
1140
+ cls,
1141
+ value: float | LINKABLE = 0.5,
1142
+ ) -> "Math":
1143
+ """Create Math with operation `exp(value)`."""
1144
+ return cls(operation="EXPONENT", Value=value)
1145
+
1146
+ @classmethod
1147
+ def minimum(
1148
+ cls,
1149
+ a: float | LINKABLE = 0.5,
1150
+ b: float | LINKABLE = 0.5,
1151
+ ) -> "Math":
1152
+ """Create Math with operation `min(a, b)`."""
1153
+ return cls(operation="MINIMUM", Value=a, Value_001=b)
1154
+
1155
+ @classmethod
1156
+ def maximum(
1157
+ cls,
1158
+ a: float | LINKABLE = 0.5,
1159
+ b: float | LINKABLE = 0.5,
1160
+ ) -> "Math":
1161
+ """Create Math with operation `max(a, b)`."""
1162
+ return cls(operation="MAXIMUM", Value=a, Value_001=b)
1163
+
1164
+ @classmethod
1165
+ def less_than(
1166
+ cls,
1167
+ value: float | LINKABLE = 0.5,
1168
+ threshold: float | LINKABLE = 0.5,
1169
+ ) -> "Math":
1170
+ """Create Math with operation `value < threshold` returning 1 or 0."""
1171
+ return cls(operation="LESS_THAN", Value=value, Value_001=threshold)
1172
+
1173
+ @classmethod
1174
+ def greater_than(
1175
+ cls,
1176
+ value: float | LINKABLE = 0.5,
1177
+ threshold: float | LINKABLE = 0.5,
1178
+ ) -> "Math":
1179
+ """Create Math with operation `value > threshold` returning 1 or 0."""
1180
+ return cls(operation="GREATER_THAN", Value=value, Value_001=threshold)
1181
+
1182
+ @classmethod
1183
+ def sign(
1184
+ cls,
1185
+ value: float | LINKABLE = 0.5,
1186
+ ) -> "Math":
1187
+ """Create Math with operation `sign(value)` returning -1, 0, or 1."""
1188
+ return cls(operation="SIGN", Value=value)
1189
+
1190
+ @classmethod
1191
+ def compare(
1192
+ cls,
1193
+ a: float | LINKABLE = 0.5,
1194
+ b: float | LINKABLE = 0.5,
1195
+ epsilon: float | LINKABLE = 0.5,
1196
+ ) -> "Math":
1197
+ """Create Math with operation `compare(a, b, epsilon)` returning -1, 0, or 1."""
1198
+ return cls(operation="COMPARE", Value=a, Value_001=b, Value_002=epsilon)
1199
+
1200
+ @classmethod
1201
+ def smooth_min(
1202
+ cls,
1203
+ a: float | LINKABLE = 0.5,
1204
+ b: float | LINKABLE = 0.5,
1205
+ distance: float | LINKABLE = 0.5,
1206
+ ) -> "Math":
1207
+ """Create Math with operation `smooth_min(a, b, distance)`."""
1208
+ return cls(operation="SMOOTH_MIN", Value=a, Value_001=b, Value_002=distance)
1209
+
1210
+ @classmethod
1211
+ def smooth_max_(
1212
+ cls,
1213
+ a: float | LINKABLE = 0.5,
1214
+ b: float | LINKABLE = 0.5,
1215
+ distance: float | LINKABLE = 0.5,
1216
+ ) -> "Math":
1217
+ """Create Math with operation `smooth_max(a, b, distance)`."""
1218
+ return cls(operation="SMOOTH_MAX", Value=a, Value_001=b, Value_002=distance)
1219
+
1220
+ @classmethod
1221
+ def round(
1222
+ cls,
1223
+ value: float | LINKABLE = 0.5,
1224
+ ) -> "Math":
1225
+ """Round A to the nearest integer. Round up if 0.5 or greater."""
1226
+ return cls(operation="ROUND", Value=value)
1227
+
1228
+ @classmethod
1229
+ def floor(
1230
+ cls,
1231
+ value: float | LINKABLE = 0.5,
1232
+ ) -> "Math":
1233
+ """The largest integer smaller than or equal to `value`"""
1234
+ return cls(operation="FLOOR", Value=value)
1235
+
1236
+ @classmethod
1237
+ def ceil(
1238
+ cls,
1239
+ value: float | LINKABLE = 0.5,
1240
+ ) -> "Math":
1241
+ """The smallest integer greater than or equal to `value`"""
1242
+ return cls(operation="CEIL", Value=value)
1243
+
1244
+ @classmethod
1245
+ def truncate(
1246
+ cls,
1247
+ value: float | LINKABLE = 0.5,
1248
+ ) -> "Math":
1249
+ """The integer part of `value` removing the fractional part"""
1250
+ return cls(operation="TRUNC", Value=value)
1251
+
1252
+ @classmethod
1253
+ def fraction(
1254
+ cls,
1255
+ value: float | LINKABLE = 0.5,
1256
+ ) -> "Math":
1257
+ """The fractional part of `value`"""
1258
+ return cls(operation="FRACT", Value=value)
1259
+
1260
+ @classmethod
1261
+ def truncated_modulo(
1262
+ cls,
1263
+ a: float | LINKABLE = 0.5,
1264
+ b: float | LINKABLE = 0.5,
1265
+ ) -> "Math":
1266
+ """The remained of truncated division using fmod(a, b)"""
1267
+ return cls(operation="MODULO", Value=a, Value_001=b)
1268
+
1269
+ @classmethod
1270
+ def floored_modulo(
1271
+ cls,
1272
+ a: float | LINKABLE = 0.5,
1273
+ b: float | LINKABLE = 0.5,
1274
+ ) -> "Math":
1275
+ """The remained of floored division"""
1276
+ return cls(operation="FLOORED_MODULO", Value=a, Value_001=b)
1277
+
1278
+ @classmethod
1279
+ def wrap(
1280
+ cls,
1281
+ value: float | LINKABLE = 0.5,
1282
+ max: float | LINKABLE = 0.5,
1283
+ min: float | LINKABLE = 0.5,
1284
+ ) -> "Math":
1285
+ """Wrap value to range, wrap(value, max, min)"""
1286
+ return cls(operation="WRAP", Value=value, Value_001=max, Value_002=min)
1287
+
1288
+ @classmethod
1289
+ def snap(
1290
+ cls,
1291
+ value: float | LINKABLE = 0.5,
1292
+ increment: float | LINKABLE = 0.5,
1293
+ ) -> "Math":
1294
+ """Snap to increment of `snap(value, increment)`"""
1295
+ return cls(operation="SNAP", Value=value, Value_001=increment)
1296
+
1297
+ @classmethod
1298
+ def ping_pong(
1299
+ cls,
1300
+ value: float | LINKABLE = 0.5,
1301
+ scale: float | LINKABLE = 0.5,
1302
+ ) -> "Math":
1303
+ """Wraps a value and reverses every other cycle"""
1304
+ return cls(operation="PINGPONG", Value=value, Value_001=scale)
1305
+
1306
+ @classmethod
1307
+ def sin(
1308
+ cls,
1309
+ value: float | LINKABLE = 0.5,
1310
+ ) -> "Math":
1311
+ """Create Math with operation 'sin(value)'."""
1312
+ return cls(operation="SINE", Value=value)
1313
+
1314
+ @classmethod
1315
+ def cos(
1316
+ cls,
1317
+ value: float | LINKABLE = 0.5,
1318
+ ) -> "Math":
1319
+ """Create Math with operation 'cos(value)'."""
1320
+ return cls(operation="COSINE", Value=value)
1321
+
1322
+ @classmethod
1323
+ def tan(
1324
+ cls,
1325
+ value: float | LINKABLE = 0.5,
1326
+ ) -> "Math":
1327
+ """Create Math with operation 'tan(value)'."""
1328
+ return cls(operation="TANGENT", Value=value)
1329
+
1330
+ @classmethod
1331
+ def arcsin(
1332
+ cls,
1333
+ value: float | LINKABLE = 0.5,
1334
+ ) -> "Math":
1335
+ """Create Math with operation `arcsin(value)`."""
1336
+ return cls(operation="ARCSINE", Value=value)
1337
+
1338
+ @classmethod
1339
+ def arccos(
1340
+ cls,
1341
+ value: float | LINKABLE = 0.5,
1342
+ ) -> "Math":
1343
+ """Create Math with operation 'arccos(value)'."""
1344
+ return cls(operation="ARCCOSINE", Value=value)
1345
+
1346
+ @classmethod
1347
+ def arctan(
1348
+ cls,
1349
+ value: float | LINKABLE = 0.5,
1350
+ ) -> "Math":
1351
+ """Create Math with operation 'arctan(value)'."""
1352
+ return cls(operation="ARCTANGENT", Value=value)
1353
+
1354
+ @classmethod
1355
+ def arctan2(
1356
+ cls,
1357
+ a: float | LINKABLE = 0.5,
1358
+ b: float | LINKABLE = 0.5,
1359
+ ) -> "Math":
1360
+ """Create Math with operation 'arctan(a / b)'."""
1361
+ return cls(operation="ARCTAN2", Value=a, Value_001=b)
1362
+
1363
+ @classmethod
1364
+ def sinh(
1365
+ cls,
1366
+ value: float | LINKABLE = 0.5,
1367
+ ) -> "Math":
1368
+ """Create Math with operation `sinh(value)`."""
1369
+ return cls(operation="SINH", Value=value)
1370
+
1371
+ @classmethod
1372
+ def cosh(
1373
+ cls,
1374
+ value: float | LINKABLE = 0.5,
1375
+ ) -> "Math":
1376
+ """Create Math with operation `cosh(value)`."""
1377
+ return cls(operation="COSH", Value=value)
1378
+
1379
+ @classmethod
1380
+ def tanh(
1381
+ cls,
1382
+ value: float | LINKABLE = 0.5,
1383
+ ) -> "Math":
1384
+ """Create Math with operation `tanh(value)`."""
1385
+ return cls(operation="TANH", Value=value)
1386
+
1387
+ @classmethod
1388
+ def radians(
1389
+ cls,
1390
+ degrees: float | LINKABLE = 0.5,
1391
+ ) -> "Math":
1392
+ """Create Math with operation `radians(degrees)`."""
1393
+ return cls(operation="RADIANS", Value=degrees)
1394
+
1395
+ @classmethod
1396
+ def degrees(
1397
+ cls,
1398
+ radians: float | LINKABLE = 0.5,
1399
+ ) -> "Math":
1400
+ """Create Math with operation 'To Degrees'."""
1401
+ return cls(operation="DEGREES", Value=radians)
1402
+
1403
+
1404
+ class BooleanMath(NodeBuilder):
1405
+ """Boolean Math node"""
1406
+
1407
+ name = "FunctionNodeBooleanMath"
1408
+ node: bpy.types.FunctionNodeBooleanMath
1409
+
1410
+ def __init__(self, operation: types.NodeBooleanMathItems = "AND", **kwargs):
1411
+ super().__init__()
1412
+ self.operator = operation
1413
+ self._establish_links(**kwargs)
1414
+
1415
+ @property
1416
+ def operation(self) -> types.NodeBooleanMathItems:
1417
+ return self.node.operation
1418
+
1419
+ @operation.setter
1420
+ def operation(self, value: types.NodeBooleanMathItems):
1421
+ self.node.operation = value
1422
+
1423
+ @property
1424
+ def i_boolean(self) -> SocketLinker:
1425
+ return self._input("Boolean") # type: ignore
1426
+
1427
+ @property
1428
+ def i_boolean_001(self) -> SocketLinker:
1429
+ return self._input("Boolean_001") # type: ignore
1430
+
1431
+ @property
1432
+ def o_boolean(self) -> SocketLinker:
1433
+ return self._output("Boolean") # type: ignore
1434
+
1435
+ @classmethod
1436
+ def l_and(
1437
+ cls,
1438
+ boolean: TYPE_INPUT_BOOLEAN = False,
1439
+ boolean_001: TYPE_INPUT_BOOLEAN = False,
1440
+ ) -> "BooleanMath":
1441
+ """Create Boolean Math with operation 'AND'."""
1442
+ return cls(operation="AND", Boolean=boolean, Boolean_001=boolean_001)
1443
+
1444
+ @classmethod
1445
+ def l_or(
1446
+ cls,
1447
+ boolean: TYPE_INPUT_BOOLEAN = False,
1448
+ boolean_001: TYPE_INPUT_BOOLEAN = False,
1449
+ ) -> "BooleanMath":
1450
+ """Create Boolean Math with operation 'OR'."""
1451
+ return cls(operation="OR", Boolean=boolean, Boolean_001=boolean_001)
1452
+
1453
+ @classmethod
1454
+ def l_not(cls, boolean: TYPE_INPUT_BOOLEAN = False) -> "BooleanMath":
1455
+ """Create Boolean Math with operation 'NOT'."""
1456
+ return cls(operation="NOT", Boolean=boolean)
1457
+
1458
+ @classmethod
1459
+ def l_not_and(
1460
+ cls,
1461
+ boolean: TYPE_INPUT_BOOLEAN = False,
1462
+ boolean_001: TYPE_INPUT_BOOLEAN = False,
1463
+ ) -> "BooleanMath":
1464
+ """Create Boolean Math with operation 'NAND'."""
1465
+ return cls(operation="NAND", Boolean=boolean, Boolean_001=boolean_001)
1466
+
1467
+ @classmethod
1468
+ def l_nor(
1469
+ cls,
1470
+ boolean: TYPE_INPUT_BOOLEAN = False,
1471
+ boolean_001: TYPE_INPUT_BOOLEAN = False,
1472
+ ) -> "BooleanMath":
1473
+ """Create Boolean Math with operation 'NOR'."""
1474
+ return cls(operation="NOR", Boolean=boolean, Boolean_001=boolean_001)
1475
+
1476
+ @classmethod
1477
+ def l_equal(
1478
+ cls,
1479
+ boolean: TYPE_INPUT_BOOLEAN = False,
1480
+ boolean_001: TYPE_INPUT_BOOLEAN = False,
1481
+ ) -> "BooleanMath":
1482
+ """Create Boolean Math with operation 'XNOR'."""
1483
+ return cls(operation="XNOR", Boolean=boolean, Boolean_001=boolean_001)
1484
+
1485
+ @classmethod
1486
+ def l_not_equal(
1487
+ cls,
1488
+ boolean: TYPE_INPUT_BOOLEAN = False,
1489
+ boolean_001: TYPE_INPUT_BOOLEAN = False,
1490
+ ) -> "BooleanMath":
1491
+ """Create Boolean Math with operation 'XOR'."""
1492
+ return cls(operation="XOR", Boolean=boolean, Boolean_001=boolean_001)
1493
+
1494
+ @classmethod
1495
+ def l_imply(
1496
+ cls,
1497
+ boolean: TYPE_INPUT_BOOLEAN = False,
1498
+ boolean_001: TYPE_INPUT_BOOLEAN = False,
1499
+ ) -> "BooleanMath":
1500
+ """Create Boolean Math with operation 'IMPLY'."""
1501
+ return cls(operation="IMPLY", Boolean=boolean, Boolean_001=boolean_001)
1502
+
1503
+ @classmethod
1504
+ def l_subtract(
1505
+ cls,
1506
+ boolean: TYPE_INPUT_BOOLEAN = False,
1507
+ boolean_001: TYPE_INPUT_BOOLEAN = False,
1508
+ ) -> "BooleanMath":
1509
+ """Create Boolean Math with operation 'NIMPLY'."""
1510
+ return cls(operation="NIMPLY", Boolean=boolean, Boolean_001=boolean_001)
1511
+
1512
+
1513
+ class VectorMath(NodeBuilder):
1514
+ """Perform vector math operation"""
1515
+
1516
+ name = "ShaderNodeVectorMath"
1517
+ node: bpy.types.ShaderNodeVectorMath
1518
+
1519
+ def __init__(
1520
+ self,
1521
+ operation: _VectorMathOperations = "ADD",
1522
+ **kwargs,
1523
+ ):
1524
+ super().__init__()
1525
+ self.operation = operation
1526
+ self._establish_links(**kwargs)
1527
+
1528
+ @property
1529
+ def i_vector(self) -> SocketLinker:
1530
+ """Input socket: Vector"""
1531
+ return self._input("Vector")
1532
+
1533
+ @property
1534
+ def i_vector_001(self) -> SocketLinker:
1535
+ """Input socket: Vector"""
1536
+ return self._input("Vector_001")
1537
+
1538
+ @property
1539
+ def o_vector(self) -> SocketLinker:
1540
+ """Output socket: Vector"""
1541
+ if self.operation in {"DOT_PRODUCT", "DISTANCE", "LENGTH"}:
1542
+ raise RuntimeError(
1543
+ f"Output 'Vector' is not available for operation '{self.operation}'"
1544
+ )
1545
+ return self._output("Vector")
1546
+
1547
+ @property
1548
+ def o_value(self) -> SocketLinker:
1549
+ """Output socket: Value"""
1550
+ assert self.operation in {"DOT_PRODUCT", "DISTANCE", "LENGTH"}
1551
+ return self._output("Value")
1552
+
1553
+ @property
1554
+ def _default_output_socket(self) -> bpy.types.NodeSocket:
1555
+ match self.operation:
1556
+ case "DOT_PRODUCT" | "DISTANCE" | "LENGTH":
1557
+ return self.o_value.socket
1558
+ case _:
1559
+ return self.o_vector.socket
1560
+
1561
+ @property
1562
+ def operation(
1563
+ self,
1564
+ ) -> _VectorMathOperations:
1565
+ return self.node.operation
1566
+
1567
+ @operation.setter
1568
+ def operation(
1569
+ self,
1570
+ value: _VectorMathOperations,
1571
+ ):
1572
+ self.node.operation = value
1573
+
1574
+ @classmethod
1575
+ def add(
1576
+ cls,
1577
+ a: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1578
+ b: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1579
+ ) -> "VectorMath":
1580
+ """Create Vector Math with operation `a + b`."""
1581
+ return cls(operation="ADD", Vector=a, Vector_001=b)
1582
+
1583
+ @classmethod
1584
+ def subtract(
1585
+ cls,
1586
+ a: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1587
+ b: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1588
+ ) -> "VectorMath":
1589
+ """Create Vector Math with operation `a - b`."""
1590
+ return cls(operation="SUBTRACT", Vector=a, Vector_001=b)
1591
+
1592
+ @classmethod
1593
+ def multiply(
1594
+ cls,
1595
+ a: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1596
+ b: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1597
+ ) -> "VectorMath":
1598
+ """Create Vector Math with operation `a * b` element-wise."""
1599
+ return cls(operation="MULTIPLY", Vector=a, Vector_001=b)
1600
+
1601
+ @classmethod
1602
+ def divide(
1603
+ cls,
1604
+ a: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1605
+ b: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1606
+ ) -> "VectorMath":
1607
+ """Create Vector Math with operation 'Divide'."""
1608
+ return cls(operation="DIVIDE", Vector=a, Vector_001=b)
1609
+
1610
+ @classmethod
1611
+ def multiply_add(
1612
+ cls,
1613
+ vector: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1614
+ multiplier: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1615
+ addend: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1616
+ ) -> "VectorMath":
1617
+ """Create Vector Math with operation 'Multiply Add'."""
1618
+ return cls(
1619
+ operation="MULTIPLY_ADD",
1620
+ Vector=vector,
1621
+ Vector_001=multiplier,
1622
+ Vector_002=addend,
1623
+ )
1624
+
1625
+ @classmethod
1626
+ def cross_product(
1627
+ cls,
1628
+ a: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1629
+ b: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1630
+ ) -> "VectorMath":
1631
+ """Create Vector Math with operation 'Cross Product'."""
1632
+ return cls(operation="CROSS_PRODUCT", Vector=a, Vector_001=b)
1633
+
1634
+ @classmethod
1635
+ def project(
1636
+ cls,
1637
+ a: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1638
+ b: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1639
+ ) -> "VectorMath":
1640
+ """Project A onto B."""
1641
+ return cls(operation="PROJECT", Vector=a, Vector_001=b)
1642
+
1643
+ @classmethod
1644
+ def reflect(
1645
+ cls,
1646
+ a: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1647
+ b: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1648
+ ) -> "VectorMath":
1649
+ """Reflect A around the normal B. B does not need to be normalized."""
1650
+ return cls(operation="REFLECT", Vector=a, Vector_001=b)
1651
+
1652
+ @classmethod
1653
+ def refract(
1654
+ cls,
1655
+ a: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1656
+ b: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1657
+ ior: LINKABLE | float = 1.0,
1658
+ ) -> "VectorMath":
1659
+ """For a given incident vector and surface normal (b) with an index of refraction (ior), return the refraction vector"""
1660
+ return cls(operation="REFRACT", Vector=a, Vector_001=b, Scale=ior)
1661
+
1662
+ @classmethod
1663
+ def face_forward(
1664
+ cls,
1665
+ vector: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1666
+ incidence: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1667
+ reference: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1668
+ ) -> "VectorMath":
1669
+ """Orients a vector to face away from a surface (incidence) defined by it's normal (reference)"""
1670
+ return cls(
1671
+ operation="FACEFORWARD",
1672
+ Vector=vector,
1673
+ Vector_001=incidence,
1674
+ Vector_002=reference,
1675
+ )
1676
+
1677
+ @classmethod
1678
+ def dot_product(
1679
+ cls,
1680
+ a: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1681
+ b: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1682
+ ) -> "VectorMath":
1683
+ """Create Vector Math with operation 'Dot Product'."""
1684
+ return cls(operation="DOT_PRODUCT", Vector=a, Vector_001=b)
1685
+
1686
+ @classmethod
1687
+ def distance(
1688
+ cls,
1689
+ a: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1690
+ b: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1691
+ ) -> "VectorMath":
1692
+ """Create Vector Math with operation 'Distance'."""
1693
+ return cls(operation="DISTANCE", Vector=a, Vector_001=b)
1694
+
1695
+ @classmethod
1696
+ def length(
1697
+ cls,
1698
+ vector: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1699
+ ) -> "VectorMath":
1700
+ """Create Vector Math with operation 'Length'."""
1701
+ return cls(operation="LENGTH", Vector=vector)
1702
+
1703
+ @classmethod
1704
+ def scale(
1705
+ cls,
1706
+ vector: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1707
+ scale: LINKABLE | float = 1.0,
1708
+ ) -> "VectorMath":
1709
+ """Create Vector Math with operation 'Scale'."""
1710
+ return cls(operation="SCALE", Vector=vector, Scale=scale)
1711
+
1712
+ @classmethod
1713
+ def normalize(
1714
+ cls,
1715
+ vector: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1716
+ ) -> "VectorMath":
1717
+ """Create Vector Math with operation 'Normalize'."""
1718
+ return cls(operation="NORMALIZE", Vector=vector)
1719
+
1720
+ @classmethod
1721
+ def absolute(
1722
+ cls,
1723
+ vector: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1724
+ ) -> "VectorMath":
1725
+ """Create Vector Math with operation 'Absolute'."""
1726
+ return cls(operation="ABSOLUTE", Vector=vector)
1727
+
1728
+ @classmethod
1729
+ def power(
1730
+ cls,
1731
+ base: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1732
+ exponent: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1733
+ ) -> "VectorMath":
1734
+ """Create Vector Math with operation 'Power'."""
1735
+ return cls(operation="POWER", Vector=base, Vector_001=exponent)
1736
+
1737
+ @classmethod
1738
+ def sign(
1739
+ cls,
1740
+ vector: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1741
+ ) -> "VectorMath":
1742
+ """Create Vector Math with operation 'Sign'."""
1743
+ return cls(operation="SIGN", Vector=vector)
1744
+
1745
+ @classmethod
1746
+ def minimum(
1747
+ cls,
1748
+ a: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1749
+ b: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1750
+ ) -> "VectorMath":
1751
+ """Create Vector Math with operation 'Minimum'."""
1752
+ return cls(operation="MINIMUM", Vector=a, Vector_001=b)
1753
+
1754
+ @classmethod
1755
+ def maximum(
1756
+ cls,
1757
+ a: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1758
+ b: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1759
+ ) -> "VectorMath":
1760
+ """Create Vector Math with operation 'Maximum'."""
1761
+ return cls(operation="MAXIMUM", Vector=a, Vector_001=b)
1762
+
1763
+ @classmethod
1764
+ def floor(
1765
+ cls,
1766
+ vector: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1767
+ ) -> "VectorMath":
1768
+ """Create Vector Math with operation 'Floor'."""
1769
+ return cls(operation="FLOOR", Vector=vector)
1770
+
1771
+ @classmethod
1772
+ def ceil(
1773
+ cls,
1774
+ vector: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1775
+ ) -> "VectorMath":
1776
+ """Create Vector Math with operation 'Ceil'."""
1777
+ return cls(operation="CEIL", Vector=vector)
1778
+
1779
+ @classmethod
1780
+ def fraction(
1781
+ cls,
1782
+ vector: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1783
+ ) -> "VectorMath":
1784
+ """Create Vector Math with operation 'Fraction'."""
1785
+ return cls(operation="FRACTION", Vector=vector)
1786
+
1787
+ @classmethod
1788
+ def modulo(
1789
+ cls,
1790
+ vector: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1791
+ ) -> "VectorMath":
1792
+ """Create Vector Math with operation 'Modulo'."""
1793
+ return cls(operation="MODULO", Vector=vector)
1794
+
1795
+ @classmethod
1796
+ def wrap(
1797
+ cls,
1798
+ vector: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1799
+ min: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1800
+ max: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1801
+ ) -> "VectorMath":
1802
+ """Create Vector Math with operation 'Wrap'."""
1803
+ return cls(operation="WRAP", Vector=vector, Vector_001=min, Vector_002=max)
1804
+
1805
+ @classmethod
1806
+ def snap(
1807
+ cls,
1808
+ vector: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1809
+ increment: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1810
+ ) -> "VectorMath":
1811
+ """Create Vector Math with operation 'Snap'."""
1812
+ return cls(operation="SNAP", Vector=vector, Vector_001=increment)
1813
+
1814
+ @classmethod
1815
+ def sin(
1816
+ cls,
1817
+ vector: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1818
+ ) -> "VectorMath":
1819
+ """Create Vector Math with operation 'Sine'."""
1820
+ return cls(operation="SINE", Vector=vector)
1821
+
1822
+ @classmethod
1823
+ def cos(
1824
+ cls,
1825
+ vector: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1826
+ ) -> "VectorMath":
1827
+ """Create Vector Math with operation 'Cosine'."""
1828
+ return cls(operation="COSINE", Vector=vector)
1829
+
1830
+ @classmethod
1831
+ def tan(
1832
+ cls,
1833
+ vector: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1834
+ ) -> "VectorMath":
1835
+ """Create Vector Math with operation 'Tangent'."""
1836
+ return cls(operation="TANGENT", Vector=vector)
1837
+
1838
+
1839
+ class RandomValue(NodeBuilder):
1840
+ """Random Value node"""
1841
+
1842
+ name = "FunctionNodeRandomValue"
1843
+ node: bpy.types.FunctionNodeRandomValue
1844
+ _default_input_id = "ID"
1845
+
1846
+ def __init__(
1847
+ self,
1848
+ id: TYPE_INPUT_INT = None,
1849
+ seed: TYPE_INPUT_INT = 0,
1850
+ *,
1851
+ data_type: _RandomValueDataTypes = "FLOAT",
1852
+ **kwargs,
1853
+ ):
1854
+ super().__init__()
1855
+ self.node.data_type = data_type
1856
+ key_args = {
1857
+ "ID": id,
1858
+ "Seed": seed,
1859
+ }
1860
+ key_args.update(kwargs)
1861
+ self._establish_links(**key_args)
1862
+
1863
+ @property
1864
+ def data_type(self) -> _RandomValueDataTypes:
1865
+ return self.node.data_type # type: ignore
1866
+
1867
+ @data_type.setter
1868
+ def data_type(self, value: _RandomValueDataTypes):
1869
+ self.node.data_type = value
1870
+
1871
+ @property
1872
+ def o_value(self) -> SocketLinker:
1873
+ """Output socket: Value"""
1874
+ match self.data_type:
1875
+ case "FLOAT":
1876
+ return self._output("Value_001")
1877
+ case "INT":
1878
+ return self._output("Value_002")
1879
+ case "BOOLEAN":
1880
+ return self._output("Value_003")
1881
+ case "FLOAT_VECTOR":
1882
+ return self._output("Value")
1883
+
1884
+ def i_min(self) -> SocketLinker:
1885
+ """Input socket: Minimum"""
1886
+ match self.data_type:
1887
+ case "FLOAT":
1888
+ return self._input("Min_001")
1889
+ case "INT":
1890
+ return self._input("Min_002")
1891
+ case "BOOLEAN":
1892
+ raise ValueError(
1893
+ "Boolean data type does not support minimum value, use 'Probability'"
1894
+ )
1895
+ case "FLOAT_VECTOR":
1896
+ return self._input("Min")
1897
+
1898
+ def i_max(self) -> SocketLinker:
1899
+ """Input socket: Maximum"""
1900
+ match self.data_type:
1901
+ case "FLOAT":
1902
+ return self._input("Max_001")
1903
+ case "INT":
1904
+ return self._input("Max_002")
1905
+ case "BOOLEAN":
1906
+ raise ValueError(
1907
+ "Boolean data type does not support maximum value, use 'Probability'"
1908
+ )
1909
+ case "FLOAT_VECTOR":
1910
+ return self._input("Max")
1911
+
1912
+ def i_probability(self) -> SocketLinker:
1913
+ """Input socket: Probability"""
1914
+ if self.data_type != "BOOLEAN":
1915
+ raise ValueError(
1916
+ f"Probability socket is only supported for boolean data types, not for data type: {self.data_type}"
1917
+ )
1918
+
1919
+ return self._input("Probability")
1920
+
1921
+ @classmethod
1922
+ def float(
1923
+ cls,
1924
+ min: TYPE_INPUT_VALUE = 0.0,
1925
+ max: TYPE_INPUT_VALUE = 1.0,
1926
+ id: TYPE_INPUT_INT = None,
1927
+ seed: int | LINKABLE = 1,
1928
+ ) -> NodeBuilder:
1929
+ buidler = cls(Min_001=min, Max_001=max, id=id, seed=seed, data_type="FLOAT")
1930
+ buidler._default_output_id = "Value_001"
1931
+ return buidler
1932
+
1933
+ @classmethod
1934
+ def integer(
1935
+ cls,
1936
+ min: TYPE_INPUT_INT = 0,
1937
+ max: TYPE_INPUT_INT = 1,
1938
+ id: TYPE_INPUT_INT = None,
1939
+ seed: TYPE_INPUT_INT = 1,
1940
+ ) -> NodeBuilder:
1941
+ buidler = cls(Min_002=min, Max_002=max, id=id, seed=seed, data_type="INT")
1942
+ buidler._default_output_id = "Value_002"
1943
+ return buidler
1944
+
1945
+ @classmethod
1946
+ def boolean(
1947
+ cls,
1948
+ probability: TYPE_INPUT_VALUE = 0.5,
1949
+ id: TYPE_INPUT_INT = None,
1950
+ seed: TYPE_INPUT_INT = 1,
1951
+ ) -> NodeBuilder:
1952
+ builder = cls(Probability=probability, id=id, seed=seed, data_type="BOOLEAN")
1953
+ builder._default_output_id = "Value_003"
1954
+ return builder
1955
+
1956
+ @classmethod
1957
+ def vector(
1958
+ cls,
1959
+ min: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
1960
+ max: TYPE_INPUT_VECTOR = (1.0, 1.0, 1.0),
1961
+ id: TYPE_INPUT_INT = None,
1962
+ seed: TYPE_INPUT_INT = 1,
1963
+ ) -> NodeBuilder:
1964
+ builder = cls(Min=min, Max=max, id=id, seed=seed, data_type="FLOAT_VECTOR")
1965
+ builder._default_output_id = "Value"
1966
+ return builder
1967
+
1968
+
1969
+ class SeparateXYZ(NodeBuilder):
1970
+ """Split a vector into its X, Y, and Z components"""
1971
+
1972
+ name = "ShaderNodeSeparateXYZ"
1973
+ node: bpy.types.ShaderNodeSeparateXYZ # type: ignore
1974
+
1975
+ def __init__(self, vector: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0)):
1976
+ super().__init__()
1977
+ self._establish_links(**{"Vector": vector})
1978
+
1979
+ @property
1980
+ def i_vector(self) -> SocketLinker:
1981
+ """Input socket: Vector"""
1982
+ return self._input("Vector")
1983
+
1984
+ @property
1985
+ def o_x(self) -> SocketLinker:
1986
+ """Output socket: X"""
1987
+ return self._output("X")
1988
+
1989
+ @property
1990
+ def o_y(self) -> SocketLinker:
1991
+ """Output socket: Y"""
1992
+ return self._output("Y")
1993
+
1994
+ @property
1995
+ def o_z(self) -> SocketLinker:
1996
+ """Output socket: Z"""
1997
+ return self._output("Z")
1998
+
1999
+
2000
+ class CombineXYZ(NodeBuilder):
2001
+ """Create a vector from X, Y, and Z components"""
2002
+
2003
+ name = "ShaderNodeCombineXYZ"
2004
+ node: bpy.types.ShaderNodeCombineXYZ # type: ignore
2005
+
2006
+ def __init__(
2007
+ self,
2008
+ x: TYPE_INPUT_VALUE = 0.0,
2009
+ y: TYPE_INPUT_VALUE = 0.0,
2010
+ z: TYPE_INPUT_VALUE = 0.0,
2011
+ ):
2012
+ super().__init__()
2013
+ self._establish_links(**{"X": x, "Y": y, "Z": z})
2014
+
2015
+ @property
2016
+ def o_vector(self) -> SocketLinker:
2017
+ """Output socket: Vector"""
2018
+ return self._output("Vector")
2019
+
2020
+ @property
2021
+ def i_x(self) -> SocketLinker:
2022
+ """Input socket: X"""
2023
+ return self._input("X")
2024
+
2025
+ @property
2026
+ def i_y(self) -> SocketLinker:
2027
+ """Input socket: Y"""
2028
+ return self._input("Y")
2029
+
2030
+ @property
2031
+ def i_z(self) -> SocketLinker:
2032
+ """Input socket: Z"""
2033
+ return self._input("Z")
2034
+
2035
+
2036
+ class Mix(NodeBuilder):
2037
+ """Mix values by a factor"""
2038
+
2039
+ name = "ShaderNodeMix"
2040
+ node: bpy.types.ShaderNodeMix # type: ignore
2041
+
2042
+ def __init__(
2043
+ self,
2044
+ data_type: _MixDataTypes = "FLOAT",
2045
+ **kwargs,
2046
+ ):
2047
+ super().__init__()
2048
+ self._default_input_id = f"A_{data_type.title()}"
2049
+ self._default_output_id = f"Result_{data_type.title()}"
2050
+ self.node.data_type = "RGBA" if data_type == "COLOR" else data_type
2051
+ key_args = {}
2052
+ key_args.update(kwargs)
2053
+ self._establish_links(**key_args)
2054
+
2055
+ @property
2056
+ def data_type(self) -> str:
2057
+ return self.node.data_type
2058
+
2059
+ @data_type.setter
2060
+ def data_type(self, value: _MixDataTypes):
2061
+ self.node.data_type = value # type: ignore
2062
+
2063
+ @property
2064
+ def factor_mode(self) -> Literal["UNIFORM", "NON_UNIFORM"]:
2065
+ return self.node.factor_mode
2066
+
2067
+ @factor_mode.setter
2068
+ def factor_mode(self, value: Literal["NON_UNIFORM", "UNIFORM"]):
2069
+ self.node.factor_mode = value
2070
+
2071
+ @property
2072
+ def o_result(self) -> SocketLinker:
2073
+ """Output socket: Result"""
2074
+ return SocketLinker(self._default_output_socket)
2075
+
2076
+ @property
2077
+ def i_factor(self) -> SocketLinker:
2078
+ """Input socket: Factor"""
2079
+ match self.data_type:
2080
+ case "FLOAT":
2081
+ name = "Factor_Float"
2082
+ case "VECTOR":
2083
+ name = (
2084
+ "Factor_Float" if self.factor_mode == "UNIFORM" else "Factor_Vector"
2085
+ )
2086
+ case "RGBA":
2087
+ name = "Factor_Color"
2088
+ case "ROTATION":
2089
+ name = "Factor_Rotation"
2090
+ case _:
2091
+ raise ValueError(f"Unsupported data type: {self.data_type}")
2092
+
2093
+ idx = self._input_idx(name)
2094
+ return SocketLinker(self.node.inputs[idx])
2095
+
2096
+ @property
2097
+ def i_value_a(self) -> SocketLinker:
2098
+ """Input socket: Value A"""
2099
+ type_name = "Color" if self.data_type == "RGBA" else self.data_type
2100
+ name = f"A_{type_name}"
2101
+ idx = self._input_idx(name)
2102
+ return SocketLinker(self.node.inputs[idx])
2103
+
2104
+ @property
2105
+ def i_value_b(self) -> SocketLinker:
2106
+ """Input socket: Value B"""
2107
+ type_name = "Color" if self.data_type == "RGBA" else self.data_type
2108
+ name = f"B_{type_name}"
2109
+ idx = self._input_idx(name)
2110
+ return SocketLinker(self.node.inputs[idx])
2111
+
2112
+ @classmethod
2113
+ def float(
2114
+ cls,
2115
+ factor: TYPE_INPUT_VALUE = 0.5,
2116
+ a: TYPE_INPUT_VALUE = 0.0,
2117
+ b: TYPE_INPUT_VALUE = 0.0,
2118
+ clamp_factor: bool = True,
2119
+ ) -> "Mix":
2120
+ builder = cls(
2121
+ Factor_Float=factor,
2122
+ A_Float=a,
2123
+ B_Float=b,
2124
+ data_type="COLOR",
2125
+ )
2126
+ builder.node.clamp_factor = clamp_factor
2127
+ return builder
2128
+
2129
+ @classmethod
2130
+ def vector(
2131
+ cls,
2132
+ factor: TYPE_INPUT_VALUE = 0.5,
2133
+ a: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
2134
+ b: TYPE_INPUT_VECTOR = (1.0, 1.0, 1.0),
2135
+ clamp_factor: bool = True,
2136
+ factor_mode: Literal["UNIFORM", "NON_UNIFORM"] = "UNIFORM",
2137
+ ) -> "Mix":
2138
+ match factor_mode:
2139
+ case "UNIFORM":
2140
+ builder = cls(
2141
+ Factor_Float=factor,
2142
+ A_Vector=a,
2143
+ B_Vector=b,
2144
+ data_type="VECTOR",
2145
+ )
2146
+ case "NON_UNIFORM":
2147
+ builder = cls(
2148
+ Factor_Vector=factor,
2149
+ A_Vector=a,
2150
+ B_Vector=b,
2151
+ data_type="VECTOR",
2152
+ )
2153
+
2154
+ builder.node.clamp_factor = clamp_factor
2155
+ return builder
2156
+
2157
+ @classmethod
2158
+ def color(
2159
+ cls,
2160
+ factor: TYPE_INPUT_VALUE = 0.5,
2161
+ a: TYPE_INPUT_COLOR = (0.0, 0.0, 0.0, 0.0),
2162
+ b: TYPE_INPUT_COLOR = (1.0, 1.0, 1.0, 1.0),
2163
+ blend_type: _MixColorBlendTypes = "ADD",
2164
+ clamp_factor: bool = True,
2165
+ clamp_result: bool = True,
2166
+ ) -> "Mix":
2167
+ builder = cls(
2168
+ Factor_Float=factor,
2169
+ A_Color=a,
2170
+ B_Color=b,
2171
+ data_type="COLOR",
2172
+ )
2173
+ builder.node.blend_type = blend_type
2174
+ builder.node.clamp_factor = clamp_factor
2175
+ builder.node.clamp_result = clamp_result
2176
+ return builder
2177
+
2178
+ @classmethod
2179
+ def rotation(
2180
+ cls,
2181
+ a: TYPE_INPUT_ROTATION = (0.0, 0.0, 0.0),
2182
+ b: TYPE_INPUT_ROTATION = (0.0, 0.0, 0.0),
2183
+ factor: TYPE_INPUT_VALUE = 0.5,
2184
+ clamp_factor: bool = True,
2185
+ ) -> "Mix":
2186
+ builder = cls(
2187
+ Factor_Float=factor,
2188
+ A_Rotation=a,
2189
+ B_Rotation=b,
2190
+ data_type="ROTATION",
2191
+ )
2192
+ builder.node.clamp_factor = clamp_factor
2193
+ return builder
2194
+
2195
+
2196
+ def _accumlate_field_factory(domain: _AttributeDomains):
2197
+ """Create a factory for AccumulateField with a specific data type"""
2198
+
2199
+ class EvaluateAtIndexDomainFactory:
2200
+ @staticmethod
2201
+ def float(
2202
+ value: TYPE_INPUT_VALUE = None, index: TYPE_INPUT_INT = 0
2203
+ ) -> "AccumulateField":
2204
+ return AccumulateField(value, index, domain=domain, data_type="FLOAT")
2205
+
2206
+ @staticmethod
2207
+ def integer(
2208
+ value: TYPE_INPUT_INT = None, index: TYPE_INPUT_INT = 0
2209
+ ) -> "AccumulateField":
2210
+ return AccumulateField(value, index, domain=domain, data_type="INT")
2211
+
2212
+ @staticmethod
2213
+ def vector(
2214
+ value: TYPE_INPUT_VECTOR = None, index: TYPE_INPUT_INT = 0
2215
+ ) -> "AccumulateField":
2216
+ return AccumulateField(
2217
+ value, index, domain=domain, data_type="FLOAT_VECTOR"
2218
+ )
2219
+
2220
+ @staticmethod
2221
+ def transform(
2222
+ value: TYPE_INPUT_MATRIX = None, index: TYPE_INPUT_INT = 0
2223
+ ) -> "AccumulateField":
2224
+ return AccumulateField(value, index, domain=domain, data_type="TRANSFORM")
2225
+
2226
+ return EvaluateAtIndexDomainFactory()
2227
+
2228
+
2229
+ class AccumulateField(NodeBuilder):
2230
+ """Add the values of an evaluated field together and output the running total for each element"""
2231
+
2232
+ name = "GeometryNodeAccumulateField"
2233
+ node: bpy.types.GeometryNodeAccumulateField
2234
+
2235
+ point = _accumlate_field_factory("POINT")
2236
+ edge = _accumlate_field_factory("EDGE")
2237
+ face = _accumlate_field_factory("FACE")
2238
+ corner = _accumlate_field_factory("CORNER")
2239
+ spline = _accumlate_field_factory("SPLINE")
2240
+ instance = _accumlate_field_factory("INSTANCE")
2241
+ layer = _accumlate_field_factory("LAYER")
2242
+
2243
+ def __init__(
2244
+ self,
2245
+ value: TYPE_INPUT_VALUE
2246
+ | TYPE_INPUT_INT
2247
+ | TYPE_INPUT_VECTOR
2248
+ | TYPE_INPUT_MATRIX = 1.0,
2249
+ group_index: TYPE_INPUT_INT = 0,
2250
+ *,
2251
+ data_type: _AccumulateFieldDataTypes = "FLOAT",
2252
+ domain: _AttributeDomains = "POINT",
2253
+ **kwargs,
2254
+ ):
2255
+ super().__init__()
2256
+ key_args = {"Value": value, "Group Index": group_index}
2257
+ key_args.update(kwargs)
2258
+ self.data_type = data_type
2259
+ self.domain = domain
2260
+ self._establish_links(**key_args)
2261
+
2262
+ @property
2263
+ def i_value(self) -> SocketLinker:
2264
+ """Input socket: Value"""
2265
+ return self._input("Value")
2266
+
2267
+ @property
2268
+ def i_group_id(self) -> SocketLinker:
2269
+ """Input socket: Group ID"""
2270
+ return self._input("Group Index")
2271
+
2272
+ @property
2273
+ def o_leading(self) -> SocketLinker:
2274
+ """Output socket: Leading"""
2275
+ return self._output("Leading")
2276
+
2277
+ @property
2278
+ def o_trailing(self) -> SocketLinker:
2279
+ """Output socket: Trailing"""
2280
+ return self._output("Trailing")
2281
+
2282
+ @property
2283
+ def o_total(self) -> SocketLinker:
2284
+ """Output socket: Total"""
2285
+ return self._output("Total")
2286
+
2287
+ @property
2288
+ def data_type(self) -> _AccumulateFieldDataTypes:
2289
+ return self.node.data_type
2290
+
2291
+ @data_type.setter
2292
+ def data_type(self, value: _AccumulateFieldDataTypes):
2293
+ self.node.data_type = value
2294
+
2295
+ @property
2296
+ def domain(
2297
+ self,
2298
+ ) -> _AttributeDomains:
2299
+ return self.node.domain
2300
+
2301
+ @domain.setter
2302
+ def domain(
2303
+ self,
2304
+ value: _AttributeDomains,
2305
+ ):
2306
+ self.node.domain = value
2307
+
2308
+
2309
+ def _evaluate_at_index_factory(domain: _AttributeDomains):
2310
+ """Create a factory for AccumulateField with a specific data type"""
2311
+
2312
+ class EvaluateAtIndexDomainFactory:
2313
+ @staticmethod
2314
+ def float(value: TYPE_INPUT_VALUE = None, index: TYPE_INPUT_INT = 0):
2315
+ return EvaluateAtIndex(value, index, domain=domain, data_type="FLOAT")
2316
+
2317
+ @staticmethod
2318
+ def integer(value: TYPE_INPUT_INT = None, index: TYPE_INPUT_INT = 0):
2319
+ return EvaluateAtIndex(value, index, domain=domain, data_type="INT")
2320
+
2321
+ @staticmethod
2322
+ def boolean(value: TYPE_INPUT_BOOLEAN = None, index: TYPE_INPUT_INT = 0):
2323
+ return EvaluateAtIndex(value, index, domain=domain, data_type="BOOLEAN")
2324
+
2325
+ @staticmethod
2326
+ def vector(value: TYPE_INPUT_VECTOR = None, index: TYPE_INPUT_INT = 0):
2327
+ return EvaluateAtIndex(
2328
+ value, index, domain=domain, data_type="FLOAT_VECTOR"
2329
+ )
2330
+
2331
+ @staticmethod
2332
+ def rotation(value: TYPE_INPUT_ROTATION = None, index: TYPE_INPUT_INT = 0):
2333
+ return EvaluateAtIndex(value, index, domain=domain, data_type="QUATERNION")
2334
+
2335
+ @staticmethod
2336
+ def transform(value: TYPE_INPUT_MATRIX = None, index: TYPE_INPUT_INT = 0):
2337
+ return EvaluateAtIndex(value, index, domain=domain, data_type="TRANSFORM")
2338
+
2339
+ return EvaluateAtIndexDomainFactory()
2340
+
2341
+
2342
+ class EvaluateAtIndex(NodeBuilder):
2343
+ """Retrieve data of other elements in the context's geometry"""
2344
+
2345
+ name = "GeometryNodeFieldAtIndex"
2346
+ node: bpy.types.GeometryNodeFieldAtIndex
2347
+
2348
+ point = _evaluate_at_index_factory("POINT")
2349
+ edge = _evaluate_at_index_factory("EDGE")
2350
+ face = _evaluate_at_index_factory("FACE")
2351
+ corner = _evaluate_at_index_factory("CORNER")
2352
+ spline = _evaluate_at_index_factory("SPLINE")
2353
+ instance = _evaluate_at_index_factory("INSTANCE")
2354
+ layer = _evaluate_at_index_factory("LAYER")
2355
+
2356
+ def __init__(
2357
+ self,
2358
+ value: LINKABLE = None,
2359
+ index: TYPE_INPUT_INT = 0,
2360
+ *,
2361
+ domain: _AttributeDomains = "POINT",
2362
+ data_type: _EvaluateAtIndexDataTypes = "FLOAT",
2363
+ **kwargs,
2364
+ ):
2365
+ super().__init__()
2366
+ key_args = {"Value": value, "Index": index}
2367
+ key_args.update(kwargs)
2368
+ self.domain = domain
2369
+ self.data_type = data_type
2370
+ self._establish_links(**key_args)
2371
+
2372
+ @property
2373
+ def i_value(self) -> SocketLinker:
2374
+ """Input socket: Value"""
2375
+ return self._input("Value")
2376
+
2377
+ @property
2378
+ def i_index(self) -> SocketLinker:
2379
+ """Input socket: Index"""
2380
+ return self._input("Index")
2381
+
2382
+ @property
2383
+ def o_value(self) -> SocketLinker:
2384
+ """Output socket: Value"""
2385
+ return self._output("Value")
2386
+
2387
+ @property
2388
+ def domain(
2389
+ self,
2390
+ ) -> _AttributeDomains:
2391
+ return self.node.domain
2392
+
2393
+ @domain.setter
2394
+ def domain(
2395
+ self,
2396
+ value: _AttributeDomains,
2397
+ ):
2398
+ self.node.domain = value
2399
+
2400
+ @property
2401
+ def data_type(
2402
+ self,
2403
+ ) -> _EvaluateAtIndexDataTypes:
2404
+ return self.node.data_type
2405
+
2406
+ @data_type.setter
2407
+ def data_type(
2408
+ self,
2409
+ value: _EvaluateAtIndexDataTypes,
2410
+ ):
2411
+ self.node.data_type = value
2412
+
2413
+
2414
+ def _field_average_factory(domain: _AttributeDomains):
2415
+ """Create a factory for FieldVariance with a specific data type"""
2416
+
2417
+ class FieldAverageDomainFactory:
2418
+ @staticmethod
2419
+ def float(
2420
+ value: TYPE_INPUT_VALUE = 1.0,
2421
+ group_index: TYPE_INPUT_INT = 0,
2422
+ ) -> "FieldAverage":
2423
+ """Create FieldAverage for the "FLOAT" data type"""
2424
+ return FieldAverage(value, group_index, data_type="FLOAT", domain=domain)
2425
+
2426
+ @staticmethod
2427
+ def vector(
2428
+ value: TYPE_INPUT_VECTOR = (1.0, 1.0, 1.0),
2429
+ group_index: TYPE_INPUT_INT = 0,
2430
+ ) -> "FieldAverage":
2431
+ """Create FieldAverage on for the "FLOAT_VECTOR" data type"""
2432
+ return FieldAverage(
2433
+ value, group_index, data_type="FLOAT_VECTOR", domain=domain
2434
+ )
2435
+
2436
+ return FieldAverageDomainFactory()
2437
+
2438
+
2439
+ class FieldAverage(NodeBuilder):
2440
+ """Calculate the mean and median of a given field"""
2441
+
2442
+ name = "GeometryNodeFieldAverage"
2443
+ node: bpy.types.GeometryNodeFieldAverage
2444
+
2445
+ point = _field_average_factory("POINT")
2446
+ edge = _field_average_factory("EDGE")
2447
+ face = _field_average_factory("FACE")
2448
+ corner = _field_average_factory("CORNER")
2449
+ spline = _field_average_factory("SPLINE")
2450
+ instance = _field_average_factory("INSTANCE")
2451
+ layer = _field_average_factory("LAYER")
2452
+
2453
+ def __init__(
2454
+ self,
2455
+ value: LINKABLE = None,
2456
+ group_index: TYPE_INPUT_VALUE | TYPE_INPUT_VECTOR = 0,
2457
+ *,
2458
+ data_type: Literal["FLOAT", "FLOAT_VECTOR"] = "FLOAT",
2459
+ domain: _AttributeDomains = "POINT",
2460
+ ):
2461
+ super().__init__()
2462
+ key_args = {"Value": value, "Group Index": group_index}
2463
+ self.data_type = data_type
2464
+ self.domain = domain
2465
+ self._establish_links(**key_args)
2466
+
2467
+ @property
2468
+ def i_value(self) -> SocketLinker:
2469
+ """Input socket: Value"""
2470
+ return self._input("Value")
2471
+
2472
+ @property
2473
+ def i_group_id(self) -> SocketLinker:
2474
+ """Input socket: Group ID"""
2475
+ return self._input("Group Index")
2476
+
2477
+ @property
2478
+ def o_mean(self) -> SocketLinker:
2479
+ """Output socket: Mean"""
2480
+ return self._output("Mean")
2481
+
2482
+ @property
2483
+ def o_median(self) -> SocketLinker:
2484
+ """Output socket: Median"""
2485
+ return self._output("Median")
2486
+
2487
+ @property
2488
+ def data_type(self) -> Literal["FLOAT", "FLOAT_VECTOR"]:
2489
+ return self.node.data_type
2490
+
2491
+ @data_type.setter
2492
+ def data_type(self, value: Literal["FLOAT", "FLOAT_VECTOR"]):
2493
+ self.node.data_type = value
2494
+
2495
+ @property
2496
+ def domain(
2497
+ self,
2498
+ ) -> _AttributeDomains:
2499
+ return self.node.domain
2500
+
2501
+ @domain.setter
2502
+ def domain(
2503
+ self,
2504
+ value: _AttributeDomains,
2505
+ ):
2506
+ self.node.domain = value
2507
+
2508
+
2509
+ def _field_min_and_max_factory(domain: _AttributeDomains):
2510
+ """Create a factory for AccumulateField with a specific data type"""
2511
+
2512
+ class FieldMinMaxDataTypeFactory:
2513
+ @staticmethod
2514
+ def float(
2515
+ value: TYPE_INPUT_VALUE = 1.0,
2516
+ group_index: TYPE_INPUT_INT = 0,
2517
+ ) -> "FieldMinMax":
2518
+ """Create FieldMinMax for the "FLOAT" data type"""
2519
+ return FieldMinMax(value, group_index, data_type="FLOAT", domain=domain)
2520
+
2521
+ @staticmethod
2522
+ def integer(
2523
+ value: TYPE_INPUT_INT = 1,
2524
+ group_index: TYPE_INPUT_INT = 0,
2525
+ ) -> "FieldMinMax":
2526
+ """Create FieldMinMax for the "INT" data type"""
2527
+ return FieldMinMax(value, group_index, data_type="INT", domain=domain)
2528
+
2529
+ @staticmethod
2530
+ def vector(
2531
+ value: TYPE_INPUT_VECTOR = (1.0, 1.0, 1.0),
2532
+ group_index: TYPE_INPUT_INT = 0,
2533
+ ) -> "FieldMinMax":
2534
+ """Create FieldMinMax on for the "FLOAT_VECTOR" data type"""
2535
+ return FieldMinMax(
2536
+ value, group_index, data_type="FLOAT_VECTOR", domain=domain
2537
+ )
2538
+
2539
+ return FieldMinMaxDataTypeFactory()
2540
+
2541
+
2542
+ class FieldMinMax(NodeBuilder):
2543
+ """Calculate the minimum and maximum of a given field"""
2544
+
2545
+ name = "GeometryNodeFieldMinAndMax"
2546
+ node: bpy.types.GeometryNodeFieldMinAndMax
2547
+
2548
+ point = _field_min_and_max_factory("POINT")
2549
+ edge = _field_min_and_max_factory("EDGE")
2550
+ face = _field_min_and_max_factory("FACE")
2551
+ corner = _field_min_and_max_factory("CORNER")
2552
+ spline = _field_min_and_max_factory("SPLINE")
2553
+ instance = _field_min_and_max_factory("INSTANCE")
2554
+ layer = _field_min_and_max_factory("LAYER")
2555
+
2556
+ def __init__(
2557
+ self,
2558
+ value: TYPE_INPUT_VALUE | TYPE_INPUT_VECTOR | TYPE_INPUT_INT = 1.0,
2559
+ group_index: TYPE_INPUT_INT = 0,
2560
+ *,
2561
+ data_type: Literal["FLOAT", "INT", "FLOAT_VECTOR"] = "FLOAT",
2562
+ domain: _AttributeDomains = "POINT",
2563
+ ):
2564
+ super().__init__()
2565
+ key_args = {"Value": value, "Group Index": group_index}
2566
+ self.data_type = data_type
2567
+ self.domain = domain
2568
+ self._establish_links(**key_args)
2569
+
2570
+ @property
2571
+ def i_value(self) -> SocketLinker:
2572
+ """Input socket: Value"""
2573
+ return self._input("Value")
2574
+
2575
+ @property
2576
+ def i_group_id(self) -> SocketLinker:
2577
+ """Input socket: Group ID"""
2578
+ return self._input("Group Index")
2579
+
2580
+ @property
2581
+ def o_min(self) -> SocketLinker:
2582
+ """Output socket: Min"""
2583
+ return self._output("Min")
2584
+
2585
+ @property
2586
+ def o_max(self) -> SocketLinker:
2587
+ """Output socket: Max"""
2588
+ return self._output("Max")
2589
+
2590
+ @property
2591
+ def data_type(self) -> Literal["FLOAT", "INT", "FLOAT_VECTOR"]:
2592
+ return self.node.data_type
2593
+
2594
+ @data_type.setter
2595
+ def data_type(self, value: Literal["FLOAT", "INT", "FLOAT_VECTOR"]):
2596
+ self.node.data_type = value
2597
+
2598
+ @property
2599
+ def domain(
2600
+ self,
2601
+ ) -> _AttributeDomains:
2602
+ return self.node.domain
2603
+
2604
+ @domain.setter
2605
+ def domain(
2606
+ self,
2607
+ value: _AttributeDomains,
2608
+ ):
2609
+ self.node.domain = value
2610
+
2611
+
2612
+ def _evaluate_on_domain_factory(domain: _AttributeDomains):
2613
+ """Create a factory for AccumulateField with a specific data type"""
2614
+
2615
+ class EvaluateOnDomainDomainFactory:
2616
+ @staticmethod
2617
+ def float(value: TYPE_INPUT_VALUE = None):
2618
+ return EvaluateOnDomain(value, domain=domain, data_type="FLOAT")
2619
+
2620
+ @staticmethod
2621
+ def integer(value: TYPE_INPUT_INT = None):
2622
+ return EvaluateOnDomain(value, domain=domain, data_type="INT")
2623
+
2624
+ @staticmethod
2625
+ def boolean(value: TYPE_INPUT_BOOLEAN = None, index: TYPE_INPUT_INT = 0):
2626
+ return EvaluateOnDomain(value, index, domain=domain, data_type="BOOLEAN")
2627
+
2628
+ @staticmethod
2629
+ def vector(value: TYPE_INPUT_VECTOR = None):
2630
+ return EvaluateOnDomain(value, domain=domain, data_type="FLOAT_VECTOR")
2631
+
2632
+ @staticmethod
2633
+ def rotation(value: TYPE_INPUT_ROTATION = None):
2634
+ return EvaluateOnDomain(value, domain=domain, data_type="QUATERNION")
2635
+
2636
+ @staticmethod
2637
+ def transform(value: TYPE_INPUT_MATRIX = None):
2638
+ return EvaluateOnDomain(value, domain=domain, data_type="TRANSFORM")
2639
+
2640
+ return EvaluateOnDomainDomainFactory()
2641
+
2642
+
2643
+ class EvaluateOnDomain(NodeBuilder):
2644
+ """Retrieve values from a field on a different domain besides the domain from the context"""
2645
+
2646
+ name = "GeometryNodeFieldOnDomain"
2647
+ node: bpy.types.GeometryNodeFieldOnDomain
2648
+
2649
+ point = _field_min_and_max_factory("POINT")
2650
+ edge = _field_min_and_max_factory("EDGE")
2651
+ face = _field_min_and_max_factory("FACE")
2652
+ corner = _field_min_and_max_factory("CORNER")
2653
+ spline = _field_min_and_max_factory("SPLINE")
2654
+ instance = _field_min_and_max_factory("INSTANCE")
2655
+ layer = _field_min_and_max_factory("LAYER")
2656
+
2657
+ def __init__(
2658
+ self,
2659
+ value: LINKABLE = None,
2660
+ *,
2661
+ domain: _AttributeDomains = "POINT",
2662
+ data_type: _EvaluateAtIndexDataTypes = "FLOAT",
2663
+ ):
2664
+ super().__init__()
2665
+ key_args = {"Value": value}
2666
+ self.domain = domain
2667
+ self.data_type = data_type
2668
+ self._establish_links(**key_args)
2669
+
2670
+ @property
2671
+ def i_value(self) -> SocketLinker:
2672
+ """Input socket: Value"""
2673
+ return self._input("Value")
2674
+
2675
+ @property
2676
+ def o_value(self) -> SocketLinker:
2677
+ """Output socket: Value"""
2678
+ return self._output("Value")
2679
+
2680
+ @property
2681
+ def domain(
2682
+ self,
2683
+ ) -> _AttributeDomains:
2684
+ return self.node.domain
2685
+
2686
+ @domain.setter
2687
+ def domain(
2688
+ self,
2689
+ value: _AttributeDomains,
2690
+ ):
2691
+ self.node.domain = value
2692
+
2693
+ @property
2694
+ def data_type(
2695
+ self,
2696
+ ) -> _EvaluateAtIndexDataTypes:
2697
+ return self.node.data_type
2698
+
2699
+ @data_type.setter
2700
+ def data_type(
2701
+ self,
2702
+ value: _EvaluateAtIndexDataTypes,
2703
+ ):
2704
+ self.node.data_type = value
2705
+
2706
+
2707
+ def _field_variance_factory(domain: _AttributeDomains):
2708
+ """Create a factory for FieldVariance with a specific data type"""
2709
+
2710
+ class FieldVarianceDomainFactory:
2711
+ @staticmethod
2712
+ def float(
2713
+ value: TYPE_INPUT_VALUE = 1.0,
2714
+ group_index: TYPE_INPUT_INT = 0,
2715
+ ) -> "FieldVariance":
2716
+ """Create FieldVariance for the "FLOAT" data type"""
2717
+ return FieldVariance(value, group_index, data_type="FLOAT", domain=domain)
2718
+
2719
+ @staticmethod
2720
+ def vector(
2721
+ value: TYPE_INPUT_VECTOR = (1.0, 1.0, 1.0),
2722
+ group_index: TYPE_INPUT_INT = 0,
2723
+ ) -> "FieldVariance":
2724
+ """Create FieldVariance on for the "FLOAT_VECTOR" data type"""
2725
+ return FieldVariance(
2726
+ value, group_index, data_type="FLOAT_VECTOR", domain=domain
2727
+ )
2728
+
2729
+ return FieldVarianceDomainFactory()
2730
+
2731
+
2732
+ class FieldVariance(NodeBuilder):
2733
+ """Calculate the standard deviation and variance of a given field"""
2734
+
2735
+ name = "GeometryNodeFieldVariance"
2736
+ node: bpy.types.GeometryNodeFieldVariance
2737
+
2738
+ point = _field_variance_factory("POINT")
2739
+ edge = _field_variance_factory("EDGE")
2740
+ face = _field_variance_factory("FACE")
2741
+ corner = _field_variance_factory("CORNER")
2742
+ spline = _field_variance_factory("SPLINE")
2743
+ instance = _field_variance_factory("INSTANCE")
2744
+ layer = _field_variance_factory("LAYER")
2745
+
2746
+ def __init__(
2747
+ self,
2748
+ value: TYPE_INPUT_VALUE | TYPE_INPUT_VECTOR = None,
2749
+ group_index: TYPE_INPUT_INT = None,
2750
+ *,
2751
+ data_type: Literal["FLOAT", "FLOAT_VECTOR"] = "FLOAT",
2752
+ domain: _AttributeDomains = "POINT",
2753
+ ):
2754
+ super().__init__()
2755
+ key_args = {"Value": value, "Group Index": group_index}
2756
+ self.data_type = data_type
2757
+ self.domain = domain
2758
+ self._establish_links(**key_args)
2759
+
2760
+ @property
2761
+ def i_value(self) -> SocketLinker:
2762
+ """Input socket: Value"""
2763
+ return self._input("Value")
2764
+
2765
+ @property
2766
+ def i_group_id(self) -> SocketLinker:
2767
+ """Input socket: Group ID"""
2768
+ return self._input("Group Index")
2769
+
2770
+ @property
2771
+ def o_standard_deviation(self) -> SocketLinker:
2772
+ """Output socket: Standard Deviation"""
2773
+ return self._output("Standard Deviation")
2774
+
2775
+ @property
2776
+ def o_variance(self) -> SocketLinker:
2777
+ """Output socket: Variance"""
2778
+ return self._output("Variance")
2779
+
2780
+ @property
2781
+ def data_type(self) -> Literal["FLOAT", "FLOAT_VECTOR"]:
2782
+ return self.node.data_type
2783
+
2784
+ @data_type.setter
2785
+ def data_type(self, value: Literal["FLOAT", "FLOAT_VECTOR"]):
2786
+ self.node.data_type = value
2787
+
2788
+ @property
2789
+ def domain(
2790
+ self,
2791
+ ) -> _AttributeDomains:
2792
+ return self.node.domain
2793
+
2794
+ @domain.setter
2795
+ def domain(
2796
+ self,
2797
+ value: _AttributeDomains,
2798
+ ):
2799
+ self.node.domain = value
2800
+
2801
+
2802
+ class IndexOfNearest(NodeBuilder):
2803
+ """Find the nearest element in a group. Similar to the "Sample Nearest" node"""
2804
+
2805
+ name = "GeometryNodeIndexOfNearest"
2806
+ node: bpy.types.GeometryNodeIndexOfNearest
2807
+
2808
+ def __init__(
2809
+ self, position: TYPE_INPUT_VECTOR = None, group_id: TYPE_INPUT_INT = None
2810
+ ):
2811
+ super().__init__()
2812
+ key_args = {"Position": position, "Group ID": group_id}
2813
+ self._establish_links(**key_args)
2814
+
2815
+ @property
2816
+ def i_position(self) -> SocketLinker:
2817
+ """Input socket: Position"""
2818
+ return self._input("Position")
2819
+
2820
+ @property
2821
+ def i_group_id(self) -> SocketLinker:
2822
+ """Input socket: Group ID"""
2823
+ return self._input("Group ID")
2824
+
2825
+ @property
2826
+ def o_index(self) -> SocketLinker:
2827
+ """Output socket: Index"""
2828
+ return self._output("Index")
2829
+
2830
+ @property
2831
+ def o_has_neighbor(self) -> SocketLinker:
2832
+ """Output socket: Has Neighbor"""
2833
+ return self._output("Has Neighbor")
2834
+
2835
+
2836
+ class JoinStrings(NodeBuilder):
2837
+ """Combine any number of input strings"""
2838
+
2839
+ name = "GeometryNodeStringJoin"
2840
+ node: bpy.types.GeometryNodeStringJoin
2841
+
2842
+ def __init__(self, *args: LINKABLE, delimiter: TYPE_INPUT_STRING = ""):
2843
+ super().__init__()
2844
+
2845
+ self._establish_links(Delimiter=delimiter)
2846
+ for arg in args:
2847
+ self._link_from(arg, "Strings")
2848
+
2849
+ @property
2850
+ def i_delimiter(self) -> SocketLinker:
2851
+ """Input socket: Delimiter"""
2852
+ return self._input("Delimiter")
2853
+
2854
+ @property
2855
+ def i_strings(self) -> SocketLinker:
2856
+ """Input socket: Strings"""
2857
+ return self._input("Strings")
2858
+
2859
+ @property
2860
+ def o_string(self) -> SocketLinker:
2861
+ """Output socket: String"""
2862
+ return self._output("String")
2863
+
2864
+
2865
+ def _switch_data_type_method(input_type: SOCKET_TYPES):
2866
+ @classmethod
2867
+ def method(
2868
+ cls, switch: TYPE_INPUT_BOOLEAN, false: LINKABLE, true: LINKABLE
2869
+ ) -> "Switch":
2870
+ """Create a NamedAttribute with specific data type."""
2871
+ return cls(switch=switch, false=false, true=true, input_type=input_type)
2872
+
2873
+ return method
2874
+
2875
+
2876
+ class Switch(NodeBuilder):
2877
+ """Switch between two inputs"""
2878
+
2879
+ name = "GeometryNodeSwitch"
2880
+ node: bpy.types.GeometryNodeSwitch
2881
+
2882
+ float = _switch_data_type_method("FLOAT")
2883
+ integer = _switch_data_type_method("INT")
2884
+ boolean = _switch_data_type_method("BOOLEAN")
2885
+ vector = _switch_data_type_method("VECTOR")
2886
+ rgba = _switch_data_type_method("RGBA")
2887
+ rotation = _switch_data_type_method("ROTATION")
2888
+ matrix = _switch_data_type_method("MATRIX")
2889
+ string = _switch_data_type_method("STRING")
2890
+ menu = _switch_data_type_method("MENU")
2891
+ object = _switch_data_type_method("OBJECT")
2892
+ geometry = _switch_data_type_method("GEOMETRY")
2893
+ collection = _switch_data_type_method("COLLECTION")
2894
+ image = _switch_data_type_method("IMAGE")
2895
+ material = _switch_data_type_method("MATERIAL")
2896
+ bundle = _switch_data_type_method("BUNDLE")
2897
+ closure = _switch_data_type_method("CLOSURE")
2898
+
2899
+ def __init__(
2900
+ self,
2901
+ switch: TYPE_INPUT_BOOLEAN = False,
2902
+ false: LINKABLE = None,
2903
+ true: LINKABLE = None,
2904
+ *,
2905
+ input_type: SOCKET_TYPES = "GEOMETRY",
2906
+ ):
2907
+ super().__init__()
2908
+ self.input_type = input_type
2909
+ key_args = {"Switch": switch, "False": false, "True": true}
2910
+ self._establish_links(**key_args)
2911
+
2912
+ @property
2913
+ def i_switch(self) -> SocketLinker:
2914
+ """Input socket: Switch"""
2915
+ return self._input("Switch")
2916
+
2917
+ @property
2918
+ def i_false(self) -> SocketLinker:
2919
+ """Input socket: False"""
2920
+ return self._input("False")
2921
+
2922
+ @property
2923
+ def i_true(self) -> SocketLinker:
2924
+ """Input socket: True"""
2925
+ return self._input("True")
2926
+
2927
+ @property
2928
+ def o_output(self) -> SocketLinker:
2929
+ """Output socket: Output"""
2930
+ return self._output("Output")
2931
+
2932
+ @property
2933
+ def input_type(
2934
+ self,
2935
+ ) -> SOCKET_TYPES:
2936
+ return self.node.input_type # type: ignore
2937
+
2938
+ @input_type.setter
2939
+ def input_type(
2940
+ self,
2941
+ value: SOCKET_TYPES,
2942
+ ):
2943
+ self.node.input_type = value
2944
+
2945
+
2946
+ class PackUVIslands(NodeBuilder):
2947
+ """Scale islands of a UV map and move them so they fill the UV space as much as possible"""
2948
+
2949
+ name = "GeometryNodeUVPackIslands"
2950
+ node: bpy.types.GeometryNodeUVPackIslands
2951
+
2952
+ def __init__(
2953
+ self,
2954
+ uv: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
2955
+ selection: TYPE_INPUT_BOOLEAN = True,
2956
+ margin: TYPE_INPUT_VALUE = 0.001,
2957
+ rotate: TYPE_INPUT_BOOLEAN = True,
2958
+ method: Literal["Bounding Box", "Convex Hull", "Exact Shape"] = "Bounding Box",
2959
+ ):
2960
+ super().__init__()
2961
+ key_args = {
2962
+ "UV": uv,
2963
+ "Selection": selection,
2964
+ "Margin": margin,
2965
+ "Rotate": rotate,
2966
+ "Method": method,
2967
+ }
2968
+ self._establish_links(**key_args)
2969
+
2970
+ @property
2971
+ def i_uv(self) -> SocketLinker:
2972
+ """Input socket: UV"""
2973
+ return self._input("UV")
2974
+
2975
+ @property
2976
+ def i_selection(self) -> SocketLinker:
2977
+ """Input socket: Selection"""
2978
+ return self._input("Selection")
2979
+
2980
+ @property
2981
+ def i_margin(self) -> SocketLinker:
2982
+ """Input socket: Margin"""
2983
+ return self._input("Margin")
2984
+
2985
+ @property
2986
+ def i_rotate(self) -> SocketLinker:
2987
+ """Input socket: Rotate"""
2988
+ return self._input("Rotate")
2989
+
2990
+ @property
2991
+ def i_method(self) -> SocketLinker:
2992
+ """Input socket: Method"""
2993
+ return self._input("Method")
2994
+
2995
+ @property
2996
+ def o_uv(self) -> SocketLinker:
2997
+ """Output socket: UV"""
2998
+ return self._output("UV")
2999
+
3000
+
3001
+ class UVUnwrap(NodeBuilder):
3002
+ """Generate a UV map based on seam edges"""
3003
+
3004
+ name = "GeometryNodeUVUnwrap"
3005
+ node: bpy.types.GeometryNodeUVUnwrap
3006
+
3007
+ def __init__(
3008
+ self,
3009
+ selection: TYPE_INPUT_BOOLEAN = True,
3010
+ seam: TYPE_INPUT_BOOLEAN = False,
3011
+ margin: TYPE_INPUT_VALUE = 0.001,
3012
+ fill_holes: TYPE_INPUT_BOOLEAN = True,
3013
+ method: Literal["Angle Based", "Conformal"] = "Angle Based",
3014
+ ):
3015
+ super().__init__()
3016
+ key_args = {
3017
+ "Selection": selection,
3018
+ "Seam": seam,
3019
+ "Margin": margin,
3020
+ "Fill Holes": fill_holes,
3021
+ "Method": method,
3022
+ }
3023
+ self._establish_links(**key_args)
3024
+
3025
+ @property
3026
+ def i_selection(self) -> SocketLinker:
3027
+ """Input socket: Selection"""
3028
+ return self._input("Selection")
3029
+
3030
+ @property
3031
+ def i_seam(self) -> SocketLinker:
3032
+ """Input socket: Seam"""
3033
+ return self._input("Seam")
3034
+
3035
+ @property
3036
+ def i_margin(self) -> SocketLinker:
3037
+ """Input socket: Margin"""
3038
+ return self._input("Margin")
3039
+
3040
+ @property
3041
+ def i_fill_holes(self) -> SocketLinker:
3042
+ """Input socket: Fill Holes"""
3043
+ return self._input("Fill Holes")
3044
+
3045
+ @property
3046
+ def i_method(self) -> SocketLinker:
3047
+ """Input socket: Method"""
3048
+ return self._input("Method")
3049
+
3050
+ @property
3051
+ def o_uv(self) -> SocketLinker:
3052
+ """Output socket: UV"""
3053
+ return self._output("UV")
3054
+
3055
+
3056
+ class FloatCurve(NodeBuilder):
3057
+ """Map an input float to a curve and outputs a float value"""
3058
+
3059
+ # TODO: add support for custom curves
3060
+
3061
+ name = "ShaderNodeFloatCurve"
3062
+ node: bpy.types.ShaderNodeFloatCurve
3063
+
3064
+ def __init__(self, factor: TYPE_INPUT_VALUE = 1.0, value: TYPE_INPUT_VALUE = 1.0):
3065
+ super().__init__()
3066
+ key_args = {"Factor": factor, "Value": value}
3067
+ self._establish_links(**key_args)
3068
+
3069
+ @property
3070
+ def i_factor(self) -> SocketLinker:
3071
+ """Input socket: Factor"""
3072
+ return self._input("Factor")
3073
+
3074
+ @property
3075
+ def i_value(self) -> SocketLinker:
3076
+ """Input socket: Value"""
3077
+ return self._input("Value")
3078
+
3079
+ @property
3080
+ def o_value(self) -> SocketLinker:
3081
+ """Output socket: Value"""
3082
+ return self._output("Value")
3083
+
3084
+
3085
+ class IntegerMath(NodeBuilder):
3086
+ """Perform various math operations on the given integer inputs"""
3087
+
3088
+ name = "FunctionNodeIntegerMath"
3089
+ node: bpy.types.FunctionNodeIntegerMath
3090
+
3091
+ def __init__(
3092
+ self,
3093
+ operation: _IntegerMathOperations = "ADD",
3094
+ **kwargs,
3095
+ ):
3096
+ super().__init__()
3097
+ key_args = {}
3098
+ key_args.update(kwargs)
3099
+ self.operation = operation
3100
+ self._establish_links(**key_args)
3101
+
3102
+ @classmethod
3103
+ def add(cls, a: TYPE_INPUT_INT = 0, b: TYPE_INPUT_INT = 0) -> "IntegerMath":
3104
+ """Create Integer Math with operation 'Add'."""
3105
+ return cls(operation="ADD", Value=a, Value_001=b)
3106
+
3107
+ @classmethod
3108
+ def subtract(cls, a: TYPE_INPUT_INT = 0, b: TYPE_INPUT_INT = 0) -> "IntegerMath":
3109
+ """Create Integer Math with operation 'Subtract'."""
3110
+ return cls(operation="SUBTRACT", Value=a, Value_001=b)
3111
+
3112
+ @classmethod
3113
+ def multiply(
3114
+ cls, a: TYPE_INPUT_INT = 0, value_001: TYPE_INPUT_INT = 0
3115
+ ) -> "IntegerMath":
3116
+ """Create Integer Math with operation 'Multiply'."""
3117
+ return cls(operation="MULTIPLY", Value=a, Value_001=value_001)
3118
+
3119
+ @classmethod
3120
+ def divide(
3121
+ cls, a: TYPE_INPUT_INT = 0, value_001: TYPE_INPUT_INT = 0
3122
+ ) -> "IntegerMath":
3123
+ """Create Integer Math with operation 'Divide'."""
3124
+ return cls(operation="DIVIDE", Value=a, Value_001=value_001)
3125
+
3126
+ @classmethod
3127
+ def multiplyadd(
3128
+ cls,
3129
+ value: TYPE_INPUT_INT = 0,
3130
+ multiplier: TYPE_INPUT_INT = 0,
3131
+ addend: TYPE_INPUT_INT = 0,
3132
+ ) -> "IntegerMath":
3133
+ """Create Integer Math with operation 'Multiply Add'."""
3134
+ return cls(
3135
+ operation="MULTIPLY_ADD",
3136
+ Value=value,
3137
+ Value_001=multiplier,
3138
+ Value_002=addend,
3139
+ )
3140
+
3141
+ @classmethod
3142
+ def absolute(cls, value: TYPE_INPUT_INT = 0) -> "IntegerMath":
3143
+ """Create Integer Math with operation 'Absolute'."""
3144
+ return cls(operation="ABSOLUTE", Value=value)
3145
+
3146
+ @classmethod
3147
+ def negate(cls, value: TYPE_INPUT_INT = 0) -> "IntegerMath":
3148
+ """Create Integer Math with operation 'Negate'."""
3149
+ return cls(operation="NEGATE", Value=value)
3150
+
3151
+ @classmethod
3152
+ def power(
3153
+ cls, base: TYPE_INPUT_INT = 0, exponent: TYPE_INPUT_INT = 0
3154
+ ) -> "IntegerMath":
3155
+ """Create Integer Math with operation 'Power'."""
3156
+ return cls(operation="POWER", Value=base, Value_001=exponent)
3157
+
3158
+ @classmethod
3159
+ def minimum(cls, a: TYPE_INPUT_INT = 0, b: TYPE_INPUT_INT = 0) -> "IntegerMath":
3160
+ """Create Integer Math with operation 'Minimum'."""
3161
+ return cls(operation="MINIMUM", Value=a, Value_001=b)
3162
+
3163
+ @classmethod
3164
+ def maximum(cls, a: TYPE_INPUT_INT = 0, b: TYPE_INPUT_INT = 0) -> "IntegerMath":
3165
+ """Create Integer Math with operation 'Maximum'."""
3166
+ return cls(operation="MAXIMUM", Value=a, Value_001=b)
3167
+
3168
+ @classmethod
3169
+ def sign(cls, value: TYPE_INPUT_INT = 0) -> "IntegerMath":
3170
+ """Create Integer Math with operation 'Sign'."""
3171
+ return cls(operation="SIGN", Value=value)
3172
+
3173
+ @classmethod
3174
+ def divide_round(
3175
+ cls, a: TYPE_INPUT_INT = 0, b: TYPE_INPUT_INT = 0
3176
+ ) -> "IntegerMath":
3177
+ """Create Integer Math with operation 'Divide Round'."""
3178
+ return cls(operation="DIVIDE_ROUND", Value=a, Value_001=b)
3179
+
3180
+ @classmethod
3181
+ def divide_floor(
3182
+ cls, a: TYPE_INPUT_INT = 0, b: TYPE_INPUT_INT = 0
3183
+ ) -> "IntegerMath":
3184
+ """Create Integer Math with operation 'Divide Floor'."""
3185
+ return cls(operation="DIVIDE_FLOOR", Value=a, Value_001=b)
3186
+
3187
+ @classmethod
3188
+ def divide_ceil(cls, a: TYPE_INPUT_INT = 0, b: TYPE_INPUT_INT = 0) -> "IntegerMath":
3189
+ """Create Integer Math with operation 'Divide Ceiling'."""
3190
+ return cls(operation="DIVIDE_CEIL", Value=a, Value_001=b)
3191
+
3192
+ @classmethod
3193
+ def floored_modulo(
3194
+ cls, a: TYPE_INPUT_INT = 0, b: TYPE_INPUT_INT = 0
3195
+ ) -> "IntegerMath":
3196
+ """Create Integer Math with operation 'Floored Modulo'."""
3197
+ return cls(operation="FLOORED_MODULO", Value=a, Value_001=b)
3198
+
3199
+ @classmethod
3200
+ def modulo(cls, a: TYPE_INPUT_INT = 0, b: TYPE_INPUT_INT = 0) -> "IntegerMath":
3201
+ """Create Integer Math with operation 'Modulo'."""
3202
+ return cls(operation="MODULO", Value=a, Value_001=b)
3203
+
3204
+ @classmethod
3205
+ def greatest_common_divisor(
3206
+ cls, a: TYPE_INPUT_INT = 0, b: TYPE_INPUT_INT = 0
3207
+ ) -> "IntegerMath":
3208
+ """Create Integer Math with operation 'Greatest Common Divisor'."""
3209
+ return cls(operation="GCD", Value=a, Value_001=b)
3210
+
3211
+ @classmethod
3212
+ def least_common_multiple(
3213
+ cls, a: TYPE_INPUT_INT = 0, b: TYPE_INPUT_INT = 0
3214
+ ) -> "IntegerMath":
3215
+ """Create Integer Math with operation 'Least Common Multiple'."""
3216
+ return cls(operation="LCM", Value=a, Value_001=b)
3217
+
3218
+ @property
3219
+ def i_value(self) -> SocketLinker:
3220
+ """Input socket: Value"""
3221
+ return self._input("Value")
3222
+
3223
+ @property
3224
+ def i_value_001(self) -> SocketLinker:
3225
+ """Input socket: Value"""
3226
+ return self._input("Value_001")
3227
+
3228
+ @property
3229
+ def i_value_002(self) -> SocketLinker:
3230
+ """Input socket: Value"""
3231
+ return self._input("Value_002")
3232
+
3233
+ @property
3234
+ def o_value(self) -> SocketLinker:
3235
+ """Output socket: Value"""
3236
+ return self._output("Value")
3237
+
3238
+ @property
3239
+ def operation(
3240
+ self,
3241
+ ) -> _IntegerMathOperations:
3242
+ return self.node.operation
3243
+
3244
+ @operation.setter
3245
+ def operation(
3246
+ self,
3247
+ value: _IntegerMathOperations,
3248
+ ):
3249
+ self.node.operation = value
3250
+
3251
+
3252
+ class InvertMatrix(NodeBuilder):
3253
+ """Compute the inverse of the given matrix, if one exists"""
3254
+
3255
+ name = "FunctionNodeInvertMatrix"
3256
+ node: bpy.types.FunctionNodeInvertMatrix
3257
+
3258
+ def __init__(self, matrix: TYPE_INPUT_MATRIX = None):
3259
+ super().__init__()
3260
+ key_args = {"Matrix": matrix}
3261
+ self._establish_links(**key_args)
3262
+
3263
+ @property
3264
+ def i_matrix(self) -> SocketLinker:
3265
+ """Input socket: Matrix"""
3266
+ return self._input("Matrix")
3267
+
3268
+ @property
3269
+ def o_matrix(self) -> SocketLinker:
3270
+ """Output socket: Matrix"""
3271
+ return self._output("Matrix")
3272
+
3273
+ @property
3274
+ def o_invertible(self) -> SocketLinker:
3275
+ """Output socket: Invertible"""
3276
+ return self._output("Invertible")
3277
+
3278
+
3279
+ class InvertRotation(NodeBuilder):
3280
+ """Compute the inverse of the given rotation"""
3281
+
3282
+ name = "FunctionNodeInvertRotation"
3283
+ node: bpy.types.FunctionNodeInvertRotation
3284
+
3285
+ def __init__(self, rotation: TYPE_INPUT_ROTATION = (0.0, 0.0, 0.0)):
3286
+ super().__init__()
3287
+ key_args = {"Rotation": rotation}
3288
+ self._establish_links(**key_args)
3289
+
3290
+ @property
3291
+ def i_rotation(self) -> SocketLinker:
3292
+ """Input socket: Rotation"""
3293
+ return self._input("Rotation")
3294
+
3295
+ @property
3296
+ def o_rotation(self) -> SocketLinker:
3297
+ """Output socket: Rotation"""
3298
+ return self._output("Rotation")
3299
+
3300
+
3301
+ class MatchString(NodeBuilder):
3302
+ """Check if a given string exists within another string"""
3303
+
3304
+ name = "FunctionNodeMatchString"
3305
+ node: bpy.types.FunctionNodeMatchString
3306
+
3307
+ def __init__(
3308
+ self,
3309
+ string: TYPE_INPUT_STRING = "",
3310
+ operation: Literal["Starts With", "Ends With", "Contains"]
3311
+ | TYPE_INPUT_MENU = "Starts With",
3312
+ key: TYPE_INPUT_STRING = "",
3313
+ ):
3314
+ super().__init__()
3315
+ key_args = {"String": string, "Operation": operation, "Key": key}
3316
+ self._establish_links(**key_args)
3317
+
3318
+ @property
3319
+ def i_string(self) -> SocketLinker:
3320
+ """Input socket: String"""
3321
+ return self._input("String")
3322
+
3323
+ @property
3324
+ def i_operation(self) -> SocketLinker:
3325
+ """Input socket: Operation"""
3326
+ return self._input("Operation")
3327
+
3328
+ @property
3329
+ def i_key(self) -> SocketLinker:
3330
+ """Input socket: Key"""
3331
+ return self._input("Key")
3332
+
3333
+ @property
3334
+ def o_result(self) -> SocketLinker:
3335
+ """Output socket: Result"""
3336
+ return self._output("Result")
3337
+
3338
+
3339
+ class MatrixDeterminant(NodeBuilder):
3340
+ """Compute the determinant of the given matrix"""
3341
+
3342
+ name = "FunctionNodeMatrixDeterminant"
3343
+ node: bpy.types.FunctionNodeMatrixDeterminant
3344
+
3345
+ def __init__(self, matrix: TYPE_INPUT_MATRIX = None):
3346
+ super().__init__()
3347
+ key_args = {"Matrix": matrix}
3348
+ self._establish_links(**key_args)
3349
+
3350
+ @property
3351
+ def i_matrix(self) -> SocketLinker:
3352
+ """Input socket: Matrix"""
3353
+ return self._input("Matrix")
3354
+
3355
+ @property
3356
+ def o_determinant(self) -> SocketLinker:
3357
+ """Output socket: Determinant"""
3358
+ return self._output("Determinant")
3359
+
3360
+
3361
+ class MultiplyMatrices(NodeBuilder):
3362
+ """Perform a matrix multiplication on two input matrices"""
3363
+
3364
+ name = "FunctionNodeMatrixMultiply"
3365
+ node: bpy.types.FunctionNodeMatrixMultiply
3366
+
3367
+ def __init__(
3368
+ self, matrix: TYPE_INPUT_MATRIX = None, matrix_001: TYPE_INPUT_MATRIX = None
3369
+ ):
3370
+ super().__init__()
3371
+ key_args = {"Matrix": matrix, "Matrix_001": matrix_001}
3372
+ self._establish_links(**key_args)
3373
+
3374
+ @property
3375
+ def i_matrix(self) -> SocketLinker:
3376
+ """Input socket: Matrix"""
3377
+ return self._input("Matrix")
3378
+
3379
+ @property
3380
+ def i_matrix_001(self) -> SocketLinker:
3381
+ """Input socket: Matrix"""
3382
+ return self._input("Matrix_001")
3383
+
3384
+ @property
3385
+ def o_matrix(self) -> SocketLinker:
3386
+ """Output socket: Matrix"""
3387
+ return self._output("Matrix")
3388
+
3389
+
3390
+ class ProjectPoint(NodeBuilder):
3391
+ """Project a point using a matrix, using location, rotation, scale, and perspective divide"""
3392
+
3393
+ name = "FunctionNodeProjectPoint"
3394
+ node: bpy.types.FunctionNodeProjectPoint
3395
+
3396
+ def __init__(
3397
+ self,
3398
+ vector: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
3399
+ transform: TYPE_INPUT_MATRIX = None,
3400
+ ):
3401
+ super().__init__()
3402
+ key_args = {"Vector": vector, "Transform": transform}
3403
+ self._establish_links(**key_args)
3404
+
3405
+ @property
3406
+ def i_vector(self) -> SocketLinker:
3407
+ """Input socket: Vector"""
3408
+ return self._input("Vector")
3409
+
3410
+ @property
3411
+ def i_transform(self) -> SocketLinker:
3412
+ """Input socket: Transform"""
3413
+ return self._input("Transform")
3414
+
3415
+ @property
3416
+ def o_vector(self) -> SocketLinker:
3417
+ """Output socket: Vector"""
3418
+ return self._output("Vector")
3419
+
3420
+
3421
+ class QuaternionToRotation(NodeBuilder):
3422
+ """Build a rotation from quaternion components"""
3423
+
3424
+ name = "FunctionNodeQuaternionToRotation"
3425
+ node: bpy.types.FunctionNodeQuaternionToRotation
3426
+
3427
+ def __init__(
3428
+ self,
3429
+ w: TYPE_INPUT_VALUE = 1.0,
3430
+ x: TYPE_INPUT_VALUE = 0.0,
3431
+ y: TYPE_INPUT_VALUE = 0.0,
3432
+ z: TYPE_INPUT_VALUE = 0.0,
3433
+ ):
3434
+ super().__init__()
3435
+ key_args = {"W": w, "X": x, "Y": y, "Z": z}
3436
+ self._establish_links(**key_args)
3437
+
3438
+ @property
3439
+ def i_w(self) -> SocketLinker:
3440
+ """Input socket: W"""
3441
+ return self._input("W")
3442
+
3443
+ @property
3444
+ def i_x(self) -> SocketLinker:
3445
+ """Input socket: X"""
3446
+ return self._input("X")
3447
+
3448
+ @property
3449
+ def i_y(self) -> SocketLinker:
3450
+ """Input socket: Y"""
3451
+ return self._input("Y")
3452
+
3453
+ @property
3454
+ def i_z(self) -> SocketLinker:
3455
+ """Input socket: Z"""
3456
+ return self._input("Z")
3457
+
3458
+ @property
3459
+ def o_rotation(self) -> SocketLinker:
3460
+ """Output socket: Rotation"""
3461
+ return self._output("Rotation")
3462
+
3463
+
3464
+ class ReplaceString(NodeBuilder):
3465
+ """Replace a given string segment with another"""
3466
+
3467
+ name = "FunctionNodeReplaceString"
3468
+ node: bpy.types.FunctionNodeReplaceString
3469
+
3470
+ def __init__(
3471
+ self,
3472
+ string: TYPE_INPUT_STRING = "",
3473
+ find: TYPE_INPUT_STRING = "",
3474
+ replace: TYPE_INPUT_STRING = "",
3475
+ ):
3476
+ super().__init__()
3477
+ key_args = {"String": string, "Find": find, "Replace": replace}
3478
+ self._establish_links(**key_args)
3479
+
3480
+ @property
3481
+ def i_string(self) -> SocketLinker:
3482
+ """Input socket: String"""
3483
+ return self._input("String")
3484
+
3485
+ @property
3486
+ def i_find(self) -> SocketLinker:
3487
+ """Input socket: Find"""
3488
+ return self._input("Find")
3489
+
3490
+ @property
3491
+ def i_replace(self) -> SocketLinker:
3492
+ """Input socket: Replace"""
3493
+ return self._input("Replace")
3494
+
3495
+ @property
3496
+ def o_string(self) -> SocketLinker:
3497
+ """Output socket: String"""
3498
+ return self._output("String")
3499
+
3500
+
3501
+ class RotateEuler(NodeBuilder):
3502
+ """Apply a secondary Euler rotation to a given Euler rotation"""
3503
+
3504
+ name = "FunctionNodeRotateEuler"
3505
+ node: bpy.types.FunctionNodeRotateEuler
3506
+
3507
+ def __init__(
3508
+ self,
3509
+ rotation: TYPE_INPUT_ROTATION = (0.0, 0.0, 0.0),
3510
+ rotate_by: TYPE_INPUT_ROTATION = (0.0, 0.0, 0.0),
3511
+ *,
3512
+ rotation_type: Literal["AXIS_ANGLE", "EULER"] = "EULER",
3513
+ space: Literal["OBJECT", "LOCAL"] = "OBJECT",
3514
+ ):
3515
+ super().__init__()
3516
+ key_args = {"Rotation": rotation, "Rotate By": rotate_by}
3517
+ self.rotation_type = rotation_type
3518
+ self.space = space
3519
+ self._establish_links(**key_args)
3520
+
3521
+ @property
3522
+ def i_rotation(self) -> SocketLinker:
3523
+ """Input socket: Rotation"""
3524
+ return self._input("Rotation")
3525
+
3526
+ @property
3527
+ def i_rotate_by(self) -> SocketLinker:
3528
+ """Input socket: Rotate By"""
3529
+ return self._input("Rotate By")
3530
+
3531
+ @property
3532
+ def o_rotation(self) -> SocketLinker:
3533
+ """Output socket: Rotation"""
3534
+ return self._output("Rotation")
3535
+
3536
+ @property
3537
+ def rotation_type(self) -> Literal["AXIS_ANGLE", "EULER"]:
3538
+ return self.node.rotation_type
3539
+
3540
+ @rotation_type.setter
3541
+ def rotation_type(self, value: Literal["AXIS_ANGLE", "EULER"]):
3542
+ self.node.rotation_type = value
3543
+
3544
+ @property
3545
+ def space(self) -> Literal["OBJECT", "LOCAL"]:
3546
+ return self.node.space
3547
+
3548
+ @space.setter
3549
+ def space(self, value: Literal["OBJECT", "LOCAL"]):
3550
+ self.node.space = value
3551
+
3552
+
3553
+ class RotateRotation(NodeBuilder):
3554
+ """Apply a secondary rotation to a given rotation value"""
3555
+
3556
+ name = "FunctionNodeRotateRotation"
3557
+ node: bpy.types.FunctionNodeRotateRotation
3558
+
3559
+ def __init__(
3560
+ self,
3561
+ rotation: TYPE_INPUT_ROTATION = (0.0, 0.0, 0.0),
3562
+ rotate_by: TYPE_INPUT_ROTATION = (0.0, 0.0, 0.0),
3563
+ *,
3564
+ rotation_space: Literal["GLOBAL", "LOCAL"] = "GLOBAL",
3565
+ ):
3566
+ super().__init__()
3567
+ key_args = {"Rotation": rotation, "Rotate By": rotate_by}
3568
+ self.rotation_space = rotation_space
3569
+ self._establish_links(**key_args)
3570
+
3571
+ @property
3572
+ def i_rotation(self) -> SocketLinker:
3573
+ """Input socket: Rotation"""
3574
+ return self._input("Rotation")
3575
+
3576
+ @property
3577
+ def i_rotate_by(self) -> SocketLinker:
3578
+ """Input socket: Rotate By"""
3579
+ return self._input("Rotate By")
3580
+
3581
+ @property
3582
+ def o_rotation(self) -> SocketLinker:
3583
+ """Output socket: Rotation"""
3584
+ return self._output("Rotation")
3585
+
3586
+ @property
3587
+ def rotation_space(self) -> Literal["GLOBAL", "LOCAL"]:
3588
+ return self.node.rotation_space
3589
+
3590
+ @rotation_space.setter
3591
+ def rotation_space(self, value: Literal["GLOBAL", "LOCAL"]):
3592
+ self.node.rotation_space = value
3593
+
3594
+
3595
+ class RotateVector(NodeBuilder):
3596
+ """Apply a rotation to a given vector"""
3597
+
3598
+ name = "FunctionNodeRotateVector"
3599
+ node: bpy.types.FunctionNodeRotateVector
3600
+
3601
+ def __init__(
3602
+ self,
3603
+ vector: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
3604
+ rotation: TYPE_INPUT_ROTATION = (0.0, 0.0, 0.0),
3605
+ ):
3606
+ super().__init__()
3607
+ key_args = {"Vector": vector, "Rotation": rotation}
3608
+ self._establish_links(**key_args)
3609
+
3610
+ @property
3611
+ def i_vector(self) -> SocketLinker:
3612
+ """Input socket: Vector"""
3613
+ return self._input("Vector")
3614
+
3615
+ @property
3616
+ def i_rotation(self) -> SocketLinker:
3617
+ """Input socket: Rotation"""
3618
+ return self._input("Rotation")
3619
+
3620
+ @property
3621
+ def o_vector(self) -> SocketLinker:
3622
+ """Output socket: Vector"""
3623
+ return self._output("Vector")
3624
+
3625
+
3626
+ class RotationToAxisAngle(NodeBuilder):
3627
+ """Convert a rotation to axis angle components"""
3628
+
3629
+ name = "FunctionNodeRotationToAxisAngle"
3630
+ node: bpy.types.FunctionNodeRotationToAxisAngle
3631
+
3632
+ def __init__(self, rotation: TYPE_INPUT_ROTATION = (0.0, 0.0, 0.0)):
3633
+ super().__init__()
3634
+ key_args = {"Rotation": rotation}
3635
+ self._establish_links(**key_args)
3636
+
3637
+ @property
3638
+ def i_rotation(self) -> SocketLinker:
3639
+ """Input socket: Rotation"""
3640
+ return self._input("Rotation")
3641
+
3642
+ @property
3643
+ def o_axis(self) -> SocketLinker:
3644
+ """Output socket: Axis"""
3645
+ return self._output("Axis")
3646
+
3647
+ @property
3648
+ def o_angle(self) -> SocketLinker:
3649
+ """Output socket: Angle"""
3650
+ return self._output("Angle")
3651
+
3652
+
3653
+ class RotationToEuler(NodeBuilder):
3654
+ """Convert a standard rotation value to an Euler rotation"""
3655
+
3656
+ name = "FunctionNodeRotationToEuler"
3657
+ node: bpy.types.FunctionNodeRotationToEuler
3658
+
3659
+ def __init__(self, rotation: TYPE_INPUT_ROTATION = (0.0, 0.0, 0.0)):
3660
+ super().__init__()
3661
+ key_args = {"Rotation": rotation}
3662
+ self._establish_links(**key_args)
3663
+
3664
+ @property
3665
+ def i_rotation(self) -> SocketLinker:
3666
+ """Input socket: Rotation"""
3667
+ return self._input("Rotation")
3668
+
3669
+ @property
3670
+ def o_euler(self) -> SocketLinker:
3671
+ """Output socket: Euler"""
3672
+ return self._output("Euler")
3673
+
3674
+
3675
+ class RotationToQuaternion(NodeBuilder):
3676
+ """Retrieve the quaternion components representing a rotation"""
3677
+
3678
+ name = "FunctionNodeRotationToQuaternion"
3679
+ node: bpy.types.FunctionNodeRotationToQuaternion
3680
+
3681
+ def __init__(self, rotation: TYPE_INPUT_ROTATION = (0.0, 0.0, 0.0)):
3682
+ super().__init__()
3683
+ key_args = {"Rotation": rotation}
3684
+ self._establish_links(**key_args)
3685
+
3686
+ @property
3687
+ def i_rotation(self) -> SocketLinker:
3688
+ """Input socket: Rotation"""
3689
+ return self._input("Rotation")
3690
+
3691
+ @property
3692
+ def o_w(self) -> SocketLinker:
3693
+ """Output socket: W"""
3694
+ return self._output("W")
3695
+
3696
+ @property
3697
+ def o_x(self) -> SocketLinker:
3698
+ """Output socket: X"""
3699
+ return self._output("X")
3700
+
3701
+ @property
3702
+ def o_y(self) -> SocketLinker:
3703
+ """Output socket: Y"""
3704
+ return self._output("Y")
3705
+
3706
+ @property
3707
+ def o_z(self) -> SocketLinker:
3708
+ """Output socket: Z"""
3709
+ return self._output("Z")
3710
+
3711
+
3712
+ class SeparateColor(NodeBuilder):
3713
+ """Split a color into separate channels, based on a particular color model"""
3714
+
3715
+ name = "FunctionNodeSeparateColor"
3716
+ node: bpy.types.FunctionNodeSeparateColor
3717
+
3718
+ def __init__(
3719
+ self,
3720
+ color: TYPE_INPUT_COLOR = (
3721
+ 1.0,
3722
+ 1.0,
3723
+ 1.0,
3724
+ 1.0,
3725
+ ),
3726
+ *,
3727
+ mode: Literal["RGB", "HSV", "HSL"] = "RGB",
3728
+ ):
3729
+ super().__init__()
3730
+ key_args = {"Color": color}
3731
+ self.mode = mode
3732
+ self._establish_links(**key_args)
3733
+
3734
+ @property
3735
+ def i_color(self) -> SocketLinker:
3736
+ """Input socket: Color"""
3737
+ return self._input("Color")
3738
+
3739
+ @property
3740
+ def o_red(self) -> SocketLinker:
3741
+ """Output socket: Red"""
3742
+ return self._output("Red")
3743
+
3744
+ @property
3745
+ def o_green(self) -> SocketLinker:
3746
+ """Output socket: Green"""
3747
+ return self._output("Green")
3748
+
3749
+ @property
3750
+ def o_blue(self) -> SocketLinker:
3751
+ """Output socket: Blue"""
3752
+ return self._output("Blue")
3753
+
3754
+ @property
3755
+ def o_alpha(self) -> SocketLinker:
3756
+ """Output socket: Alpha"""
3757
+ return self._output("Alpha")
3758
+
3759
+ @property
3760
+ def mode(self) -> Literal["RGB", "HSV", "HSL"]:
3761
+ return self.node.mode
3762
+
3763
+ @mode.setter
3764
+ def mode(self, value: Literal["RGB", "HSV", "HSL"]):
3765
+ self.node.mode = value
3766
+
3767
+
3768
+ class SeparateMatrix(NodeBuilder):
3769
+ """Split a 4x4 matrix into its individual values"""
3770
+
3771
+ name = "FunctionNodeSeparateMatrix"
3772
+ node: bpy.types.FunctionNodeSeparateMatrix
3773
+
3774
+ def __init__(self, matrix: TYPE_INPUT_MATRIX = None):
3775
+ super().__init__()
3776
+ key_args = {"Matrix": matrix}
3777
+ self._establish_links(**key_args)
3778
+
3779
+ @property
3780
+ def i_matrix(self) -> SocketLinker:
3781
+ """Input socket: Matrix"""
3782
+ return self._input("Matrix")
3783
+
3784
+ @property
3785
+ def o_column_1_row_1(self) -> SocketLinker:
3786
+ """Output socket: Column 1 Row 1"""
3787
+ return self._output("Column 1 Row 1")
3788
+
3789
+ @property
3790
+ def o_column_1_row_2(self) -> SocketLinker:
3791
+ """Output socket: Column 1 Row 2"""
3792
+ return self._output("Column 1 Row 2")
3793
+
3794
+ @property
3795
+ def o_column_1_row_3(self) -> SocketLinker:
3796
+ """Output socket: Column 1 Row 3"""
3797
+ return self._output("Column 1 Row 3")
3798
+
3799
+ @property
3800
+ def o_column_1_row_4(self) -> SocketLinker:
3801
+ """Output socket: Column 1 Row 4"""
3802
+ return self._output("Column 1 Row 4")
3803
+
3804
+ @property
3805
+ def o_column_2_row_1(self) -> SocketLinker:
3806
+ """Output socket: Column 2 Row 1"""
3807
+ return self._output("Column 2 Row 1")
3808
+
3809
+ @property
3810
+ def o_column_2_row_2(self) -> SocketLinker:
3811
+ """Output socket: Column 2 Row 2"""
3812
+ return self._output("Column 2 Row 2")
3813
+
3814
+ @property
3815
+ def o_column_2_row_3(self) -> SocketLinker:
3816
+ """Output socket: Column 2 Row 3"""
3817
+ return self._output("Column 2 Row 3")
3818
+
3819
+ @property
3820
+ def o_column_2_row_4(self) -> SocketLinker:
3821
+ """Output socket: Column 2 Row 4"""
3822
+ return self._output("Column 2 Row 4")
3823
+
3824
+ @property
3825
+ def o_column_3_row_1(self) -> SocketLinker:
3826
+ """Output socket: Column 3 Row 1"""
3827
+ return self._output("Column 3 Row 1")
3828
+
3829
+ @property
3830
+ def o_column_3_row_2(self) -> SocketLinker:
3831
+ """Output socket: Column 3 Row 2"""
3832
+ return self._output("Column 3 Row 2")
3833
+
3834
+ @property
3835
+ def o_column_3_row_3(self) -> SocketLinker:
3836
+ """Output socket: Column 3 Row 3"""
3837
+ return self._output("Column 3 Row 3")
3838
+
3839
+ @property
3840
+ def o_column_3_row_4(self) -> SocketLinker:
3841
+ """Output socket: Column 3 Row 4"""
3842
+ return self._output("Column 3 Row 4")
3843
+
3844
+ @property
3845
+ def o_column_4_row_1(self) -> SocketLinker:
3846
+ """Output socket: Column 4 Row 1"""
3847
+ return self._output("Column 4 Row 1")
3848
+
3849
+ @property
3850
+ def o_column_4_row_2(self) -> SocketLinker:
3851
+ """Output socket: Column 4 Row 2"""
3852
+ return self._output("Column 4 Row 2")
3853
+
3854
+ @property
3855
+ def o_column_4_row_3(self) -> SocketLinker:
3856
+ """Output socket: Column 4 Row 3"""
3857
+ return self._output("Column 4 Row 3")
3858
+
3859
+ @property
3860
+ def o_column_4_row_4(self) -> SocketLinker:
3861
+ """Output socket: Column 4 Row 4"""
3862
+ return self._output("Column 4 Row 4")
3863
+
3864
+
3865
+ class SeparateTransform(NodeBuilder):
3866
+ """Split a transformation matrix into a translation vector, a rotation, and a scale vector"""
3867
+
3868
+ name = "FunctionNodeSeparateTransform"
3869
+ node: bpy.types.FunctionNodeSeparateTransform
3870
+
3871
+ def __init__(self, transform: TYPE_INPUT_MATRIX = None):
3872
+ super().__init__()
3873
+ key_args = {"Transform": transform}
3874
+ self._establish_links(**key_args)
3875
+
3876
+ @property
3877
+ def i_transform(self) -> SocketLinker:
3878
+ """Input socket: Transform"""
3879
+ return self._input("Transform")
3880
+
3881
+ @property
3882
+ def o_translation(self) -> SocketLinker:
3883
+ """Output socket: Translation"""
3884
+ return self._output("Translation")
3885
+
3886
+ @property
3887
+ def o_rotation(self) -> SocketLinker:
3888
+ """Output socket: Rotation"""
3889
+ return self._output("Rotation")
3890
+
3891
+ @property
3892
+ def o_scale(self) -> SocketLinker:
3893
+ """Output socket: Scale"""
3894
+ return self._output("Scale")
3895
+
3896
+
3897
+ class SliceString(NodeBuilder):
3898
+ """Extract a string segment from a larger string"""
3899
+
3900
+ name = "FunctionNodeSliceString"
3901
+ node: bpy.types.FunctionNodeSliceString
3902
+
3903
+ def __init__(
3904
+ self,
3905
+ string: TYPE_INPUT_STRING = "",
3906
+ position: TYPE_INPUT_INT = 0,
3907
+ length: TYPE_INPUT_INT = 10,
3908
+ ):
3909
+ super().__init__()
3910
+ key_args = {"String": string, "Position": position, "Length": length}
3911
+ self._establish_links(**key_args)
3912
+
3913
+ @property
3914
+ def i_string(self) -> SocketLinker:
3915
+ """Input socket: String"""
3916
+ return self._input("String")
3917
+
3918
+ @property
3919
+ def i_position(self) -> SocketLinker:
3920
+ """Input socket: Position"""
3921
+ return self._input("Position")
3922
+
3923
+ @property
3924
+ def i_length(self) -> SocketLinker:
3925
+ """Input socket: Length"""
3926
+ return self._input("Length")
3927
+
3928
+ @property
3929
+ def o_string(self) -> SocketLinker:
3930
+ """Output socket: String"""
3931
+ return self._output("String")
3932
+
3933
+
3934
+ class StringLength(NodeBuilder):
3935
+ """Output the number of characters in the given string"""
3936
+
3937
+ name = "FunctionNodeStringLength"
3938
+ node: bpy.types.FunctionNodeStringLength
3939
+
3940
+ def __init__(self, string: TYPE_INPUT_STRING = ""):
3941
+ super().__init__()
3942
+ key_args = {"String": string}
3943
+ self._establish_links(**key_args)
3944
+
3945
+ @property
3946
+ def i_string(self) -> SocketLinker:
3947
+ """Input socket: String"""
3948
+ return self._input("String")
3949
+
3950
+ @property
3951
+ def o_length(self) -> SocketLinker:
3952
+ """Output socket: Length"""
3953
+ return self._output("Length")
3954
+
3955
+
3956
+ class StringToValue(NodeBuilder):
3957
+ """Derive a numeric value from a given string representation"""
3958
+
3959
+ name = "FunctionNodeStringToValue"
3960
+ node: bpy.types.FunctionNodeStringToValue
3961
+
3962
+ def __init__(
3963
+ self,
3964
+ string: TYPE_INPUT_STRING = "",
3965
+ *,
3966
+ data_type: Literal["FLOAT", "INT"] = "FLOAT",
3967
+ ):
3968
+ super().__init__()
3969
+ key_args = {"String": string}
3970
+ self.data_type = data_type
3971
+ self._establish_links(**key_args)
3972
+
3973
+ @property
3974
+ def i_string(self) -> SocketLinker:
3975
+ """Input socket: String"""
3976
+ return self._input("String")
3977
+
3978
+ @property
3979
+ def o_value(self) -> SocketLinker:
3980
+ """Output socket: Value"""
3981
+ return self._output("Value")
3982
+
3983
+ @property
3984
+ def o_length(self) -> SocketLinker:
3985
+ """Output socket: Length"""
3986
+ return self._output("Length")
3987
+
3988
+ @property
3989
+ def data_type(self) -> Literal["FLOAT", "INT"]:
3990
+ return self.node.data_type
3991
+
3992
+ @data_type.setter
3993
+ def data_type(self, value: Literal["FLOAT", "INT"]):
3994
+ self.node.data_type = value
3995
+
3996
+
3997
+ class TransformDirection(NodeBuilder):
3998
+ """Apply a transformation matrix (excluding translation) to the given vector"""
3999
+
4000
+ name = "FunctionNodeTransformDirection"
4001
+ node: bpy.types.FunctionNodeTransformDirection
4002
+
4003
+ def __init__(
4004
+ self,
4005
+ direction: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
4006
+ transform: TYPE_INPUT_MATRIX = None,
4007
+ ):
4008
+ super().__init__()
4009
+ key_args = {"Direction": direction, "Transform": transform}
4010
+ self._establish_links(**key_args)
4011
+
4012
+ @property
4013
+ def i_direction(self) -> SocketLinker:
4014
+ """Input socket: Direction"""
4015
+ return self._input("Direction")
4016
+
4017
+ @property
4018
+ def i_transform(self) -> SocketLinker:
4019
+ """Input socket: Transform"""
4020
+ return self._input("Transform")
4021
+
4022
+ @property
4023
+ def o_direction(self) -> SocketLinker:
4024
+ """Output socket: Direction"""
4025
+ return self._output("Direction")
4026
+
4027
+
4028
+ class TransformPoint(NodeBuilder):
4029
+ """Apply a transformation matrix to the given vector"""
4030
+
4031
+ name = "FunctionNodeTransformPoint"
4032
+ node: bpy.types.FunctionNodeTransformPoint
4033
+
4034
+ def __init__(
4035
+ self,
4036
+ vector: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
4037
+ transform: TYPE_INPUT_MATRIX = None,
4038
+ ):
4039
+ super().__init__()
4040
+ key_args = {"Vector": vector, "Transform": transform}
4041
+ self._establish_links(**key_args)
4042
+
4043
+ @property
4044
+ def i_vector(self) -> SocketLinker:
4045
+ """Input socket: Vector"""
4046
+ return self._input("Vector")
4047
+
4048
+ @property
4049
+ def i_transform(self) -> SocketLinker:
4050
+ """Input socket: Transform"""
4051
+ return self._input("Transform")
4052
+
4053
+ @property
4054
+ def o_vector(self) -> SocketLinker:
4055
+ """Output socket: Vector"""
4056
+ return self._output("Vector")
4057
+
4058
+
4059
+ class TransposeMatrix(NodeBuilder):
4060
+ """Flip a matrix over its diagonal, turning columns into rows and vice-versa"""
4061
+
4062
+ name = "FunctionNodeTransposeMatrix"
4063
+ node: bpy.types.FunctionNodeTransposeMatrix
4064
+
4065
+ def __init__(self, matrix: TYPE_INPUT_MATRIX = None):
4066
+ super().__init__()
4067
+ key_args = {"Matrix": matrix}
4068
+ self._establish_links(**key_args)
4069
+
4070
+ @property
4071
+ def i_matrix(self) -> SocketLinker:
4072
+ """Input socket: Matrix"""
4073
+ return self._input("Matrix")
4074
+
4075
+ @property
4076
+ def o_matrix(self) -> SocketLinker:
4077
+ """Output socket: Matrix"""
4078
+ return self._output("Matrix")
4079
+
4080
+
4081
+ class ValueToString(NodeBuilder):
4082
+ """Generate a string representation of the given input value"""
4083
+
4084
+ name = "FunctionNodeValueToString"
4085
+ node: bpy.types.FunctionNodeValueToString
4086
+
4087
+ def __init__(
4088
+ self,
4089
+ value: TYPE_INPUT_VALUE = 0.0,
4090
+ decimals: TYPE_INPUT_INT = 0,
4091
+ *,
4092
+ data_type: Literal["FLOAT", "INT"] = "FLOAT",
4093
+ ):
4094
+ super().__init__()
4095
+ key_args = {"Value": value, "Decimals": decimals}
4096
+ self.data_type = data_type
4097
+ self._establish_links(**key_args)
4098
+
4099
+ @property
4100
+ def i_value(self) -> SocketLinker:
4101
+ """Input socket: Value"""
4102
+ return self._input("Value")
4103
+
4104
+ @property
4105
+ def i_decimals(self) -> SocketLinker:
4106
+ """Input socket: Decimals"""
4107
+ return self._input("Decimals")
4108
+
4109
+ @property
4110
+ def o_string(self) -> SocketLinker:
4111
+ """Output socket: String"""
4112
+ return self._output("String")
4113
+
4114
+ @property
4115
+ def data_type(self) -> Literal["FLOAT", "INT"]:
4116
+ return self.node.data_type
4117
+
4118
+ @data_type.setter
4119
+ def data_type(self, value: Literal["FLOAT", "INT"]):
4120
+ self.node.data_type = value
4121
+
4122
+
4123
+ class Clamp(NodeBuilder):
4124
+ """Clamp a value between a minimum and a maximum"""
4125
+
4126
+ name = "ShaderNodeClamp"
4127
+ node: bpy.types.ShaderNodeClamp
4128
+
4129
+ def __init__(
4130
+ self,
4131
+ value: TYPE_INPUT_VALUE = 1.0,
4132
+ min: TYPE_INPUT_VALUE = 0.0,
4133
+ max: TYPE_INPUT_VALUE = 1.0,
4134
+ *,
4135
+ clamp_type: Literal["MINMAX", "RANGE"] = "MINMAX",
4136
+ ):
4137
+ super().__init__()
4138
+ key_args = {"Value": value, "Min": min, "Max": max}
4139
+ self.clamp_type = clamp_type
4140
+ self._establish_links(**key_args)
4141
+
4142
+ @property
4143
+ def i_value(self) -> SocketLinker:
4144
+ """Input socket: Value"""
4145
+ return self._input("Value")
4146
+
4147
+ @property
4148
+ def i_min(self) -> SocketLinker:
4149
+ """Input socket: Min"""
4150
+ return self._input("Min")
4151
+
4152
+ @property
4153
+ def i_max(self) -> SocketLinker:
4154
+ """Input socket: Max"""
4155
+ return self._input("Max")
4156
+
4157
+ @property
4158
+ def o_result(self) -> SocketLinker:
4159
+ """Output socket: Result"""
4160
+ return self._output("Result")
4161
+
4162
+ @property
4163
+ def clamp_type(self) -> Literal["MINMAX", "RANGE"]:
4164
+ return self.node.clamp_type
4165
+
4166
+ @clamp_type.setter
4167
+ def clamp_type(self, value: Literal["MINMAX", "RANGE"]):
4168
+ self.node.clamp_type = value
4169
+
4170
+
4171
+ class MapRange(NodeBuilder):
4172
+ """Remap a value from a range to a target range"""
4173
+
4174
+ name = "ShaderNodeMapRange"
4175
+ node: bpy.types.ShaderNodeMapRange
4176
+
4177
+ def __init__(
4178
+ self,
4179
+ *,
4180
+ interpolation_type: Literal[
4181
+ "LINEAR", "STEPPED", "SMOOTHSTEP", "SMOOTHERSTEP"
4182
+ ] = "LINEAR",
4183
+ data_type: Literal["FLOAT", "FLOAT_VECTOR"] = "FLOAT",
4184
+ clamp: bool = False,
4185
+ **kwargs,
4186
+ ):
4187
+ super().__init__()
4188
+ key_args = {}
4189
+ key_args.update(kwargs)
4190
+ self.clamp = clamp
4191
+ self.interpolation_type = interpolation_type
4192
+ self.data_type = data_type
4193
+ self._establish_links(**key_args)
4194
+
4195
+ @classmethod
4196
+ def float(
4197
+ cls,
4198
+ value: TYPE_INPUT_VALUE = 1.0,
4199
+ from_min: TYPE_INPUT_VALUE = 0.0,
4200
+ from_max: TYPE_INPUT_VALUE = 1.0,
4201
+ to_min: TYPE_INPUT_VALUE = 0.0,
4202
+ to_max: TYPE_INPUT_VALUE = 1.0,
4203
+ steps: TYPE_INPUT_VALUE = 1.0,
4204
+ *,
4205
+ interpolation_type: Literal[
4206
+ "LINEAR", "STEPPED", "SMOOTHSTEP", "SMOOTHERSTEP"
4207
+ ] = "LINEAR",
4208
+ clamp: bool = False,
4209
+ **kwargs,
4210
+ ):
4211
+ key_args = {
4212
+ "Value": value,
4213
+ "From Min": from_min,
4214
+ "From Max": from_max,
4215
+ "To Min": to_min,
4216
+ "To Max": to_max,
4217
+ "Steps": steps,
4218
+ }
4219
+ return cls(
4220
+ **key_args,
4221
+ interpolation_type=interpolation_type,
4222
+ data_type="FLOAT",
4223
+ clamp=clamp,
4224
+ )
4225
+
4226
+ @classmethod
4227
+ def vector(
4228
+ cls,
4229
+ value: TYPE_INPUT_VECTOR = (1.0, 1.0, 1.0),
4230
+ from_min: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
4231
+ from_max: TYPE_INPUT_VECTOR = (1.0, 1.0, 1.0),
4232
+ to_min: TYPE_INPUT_VECTOR = (0.0, 0.0, 0.0),
4233
+ to_max: TYPE_INPUT_VECTOR = (1.0, 1.0, 1.0),
4234
+ steps: TYPE_INPUT_VECTOR = (1.0, 1.0, 1.0),
4235
+ *,
4236
+ interpolation_type: Literal[
4237
+ "LINEAR", "STEPPED", "SMOOTHSTEP", "SMOOTHERSTEP"
4238
+ ] = "LINEAR",
4239
+ clamp: bool = False,
4240
+ ):
4241
+ key_args = {
4242
+ "Vector": value,
4243
+ "From_Min_FLOAT3": from_min,
4244
+ "From_Max_FLOAT3": from_max,
4245
+ "To_Min_FLOAT3": to_min,
4246
+ "To_Max_FLOAT3": to_max,
4247
+ "Steps_FLOAT3": steps,
4248
+ }
4249
+ return cls(
4250
+ **key_args,
4251
+ data_type="FLOAT_VECTOR",
4252
+ interpolation_type=interpolation_type,
4253
+ clamp=clamp,
4254
+ )
4255
+
4256
+ @property
4257
+ def i_value(self) -> SocketLinker:
4258
+ """Input socket: Value"""
4259
+ return self._input("Value" if self.data_type == "FLOAT" else "Vector")
4260
+
4261
+ @property
4262
+ def i_from_min(self) -> SocketLinker:
4263
+ """Input socket: From Min"""
4264
+ return self._input(
4265
+ "From Min" if self.data_type == "FLOAT" else "From_Min_FLOAT3"
4266
+ )
4267
+
4268
+ @property
4269
+ def i_from_max(self) -> SocketLinker:
4270
+ """Input socket: From Max"""
4271
+ return self._input(
4272
+ "From Max" if self.data_type == "FLOAT" else "From_Max_FLOAT3"
4273
+ )
4274
+
4275
+ @property
4276
+ def i_to_min(self) -> SocketLinker:
4277
+ """Input socket: To Min"""
4278
+ return self._input("To Min" if self.data_type == "FLOAT" else "To_Min_FLOAT3")
4279
+
4280
+ @property
4281
+ def i_to_max(self) -> SocketLinker:
4282
+ """Input socket: To Max"""
4283
+ return self._input("To Max" if self.data_type == "FLOAT" else "To_Max_FLOAT3")
4284
+
4285
+ @property
4286
+ def i_steps(self) -> SocketLinker:
4287
+ """Input socket: Steps"""
4288
+ return self._input("Steps" if self.data_type == "FLOAT" else "Steps_FLOAT3")
4289
+
4290
+ @property
4291
+ def o_result(self) -> SocketLinker:
4292
+ """Output socket: Result"""
4293
+ return self._output("Result" if self.data_type == "FLOAT" else "Vector")
4294
+
4295
+ @property
4296
+ def clamp(self) -> bool:
4297
+ return self.node.clamp
4298
+
4299
+ @clamp.setter
4300
+ def clamp(self, value: bool):
4301
+ self.node.clamp = value
4302
+
4303
+ @property
4304
+ def interpolation_type(
4305
+ self,
4306
+ ) -> Literal["LINEAR", "STEPPED", "SMOOTHSTEP", "SMOOTHERSTEP"]:
4307
+ return self.node.interpolation_type
4308
+
4309
+ @interpolation_type.setter
4310
+ def interpolation_type(
4311
+ self, value: Literal["LINEAR", "STEPPED", "SMOOTHSTEP", "SMOOTHERSTEP"]
4312
+ ):
4313
+ self.node.interpolation_type = value
4314
+
4315
+ @property
4316
+ def data_type(self) -> Literal["FLOAT", "FLOAT_VECTOR"]:
4317
+ return self.node.data_type
4318
+
4319
+ @data_type.setter
4320
+ def data_type(self, value: Literal["FLOAT", "FLOAT_VECTOR"]):
4321
+ self.node.data_type = value
4322
+
4323
+
4324
+ def _typed_index_switch(data_type: SOCKET_TYPES):
4325
+ @classmethod
4326
+ def method(cls, *args: TYPE_INPUT_ALL, index: TYPE_INPUT_INT = 0) -> "IndexSwitch":
4327
+ """Create an IndexSwitch node with a pre-set data_type"""
4328
+ return cls(*args, index=index, data_type=data_type)
4329
+
4330
+ return method
4331
+
4332
+
4333
+ class IndexSwitch(NodeBuilder):
4334
+ """Node builder for the Index Switch node"""
4335
+
4336
+ name = "GeometryNodeIndexSwitch"
4337
+ node: bpy.types.GeometryNodeIndexSwitch
4338
+ float = _typed_index_switch("FLOAT")
4339
+ integer = _typed_index_switch("INT")
4340
+ boolean = _typed_index_switch("BOOLEAN")
4341
+ vector = _typed_index_switch("VECTOR")
4342
+ color = _typed_index_switch("RGBA")
4343
+ rotation = _typed_index_switch("ROTATION")
4344
+ matrix = _typed_index_switch("MATRIX")
4345
+ string = _typed_index_switch("STRING")
4346
+ menu = _typed_index_switch("MENU")
4347
+ object = _typed_index_switch("OBJECT")
4348
+ geometry = _typed_index_switch("GEOMETRY")
4349
+ collection = _typed_index_switch("COLLECTION")
4350
+ image = _typed_index_switch("IMAGE")
4351
+ material = _typed_index_switch("MATERIAL")
4352
+ bundle = _typed_index_switch("BUNDLE")
4353
+ closure = _typed_index_switch("CLOSURE")
4354
+
4355
+ def __init__(
4356
+ self,
4357
+ *args: TYPE_INPUT_ALL,
4358
+ index: TYPE_INPUT_INT = 0,
4359
+ data_type: SOCKET_TYPES = "FLOAT",
4360
+ ):
4361
+ super().__init__()
4362
+ self.data_type = data_type
4363
+ key_args: dict[str, TYPE_INPUT_ALL] = {"Index": index}
4364
+ self.node.index_switch_items.clear()
4365
+ self._link_args(*args)
4366
+ self._establish_links(**key_args)
4367
+
4368
+ def _create_socket(self) -> NodeSocket:
4369
+ item = self.node.index_switch_items.new()
4370
+ return self.node.inputs[item.identifier]
4371
+
4372
+ def _link_args(self, *args: TYPE_INPUT_ALL):
4373
+ for arg in args:
4374
+ if _is_default_value(arg):
4375
+ socket = self._create_socket()
4376
+ socket.default_value = arg
4377
+ else:
4378
+ source = self._source_socket(arg)
4379
+ self.tree.link(source, self.node.inputs["__extend__"])
4380
+
4381
+ @property
4382
+ def inputs(self) -> list[SocketLinker]:
4383
+ """Input sockets"""
4384
+ return [
4385
+ SocketLinker(self.node.inputs[i + 1])
4386
+ for i in range(len(self.node.index_switch_items))
4387
+ ]
4388
+
4389
+ @property
4390
+ def i_index(self) -> SocketLinker:
4391
+ """Input socket: Index"""
4392
+ return self._input("Index")
4393
+
4394
+ @property
4395
+ def o_output(self) -> SocketLinker:
4396
+ """Output socket: Output"""
4397
+ return self._output("Output")
4398
+
4399
+ @property
4400
+ def data_type(self) -> SOCKET_TYPES:
4401
+ """Input socket: Data Type"""
4402
+ return self.node.data_type # type: ignore
4403
+
4404
+ @data_type.setter
4405
+ def data_type(self, value: SOCKET_TYPES):
4406
+ """Input socket: Data Type"""
4407
+ self.node.data_type = value
4408
+
4409
+
4410
+ def _typed_menu_switch(data_type: SOCKET_TYPES):
4411
+ @classmethod
4412
+ def method(
4413
+ cls,
4414
+ *args: TYPE_INPUT_ALL,
4415
+ menu: TYPE_INPUT_MENU = None,
4416
+ data_type: SOCKET_TYPES = "FLOAT",
4417
+ **kwargs: TYPE_INPUT_ALL,
4418
+ ) -> "IndexSwitch":
4419
+ """Create an IndexSwitch node with a pre-set data_type"""
4420
+ return cls(*args, menu=menu, data_type=data_type, **kwargs)
4421
+
4422
+ return method
4423
+
4424
+
4425
+ class MenuSwitch(NodeBuilder):
4426
+ """Node builder for the Index Switch node"""
4427
+
4428
+ name = "GeometryNodeMenuSwitch"
4429
+ node: bpy.types.GeometryNodeMenuSwitch
4430
+
4431
+ float = _typed_menu_switch("FLOAT")
4432
+ integer = _typed_menu_switch("INT")
4433
+ boolean = _typed_menu_switch("BOOLEAN")
4434
+ vector = _typed_menu_switch("VECTOR")
4435
+ color = _typed_menu_switch("RGBA")
4436
+ rotation = _typed_menu_switch("ROTATION")
4437
+ matrix = _typed_menu_switch("MATRIX")
4438
+ string = _typed_menu_switch("STRING")
4439
+ menu = _typed_menu_switch("MENU")
4440
+ object = _typed_menu_switch("OBJECT")
4441
+ geometry = _typed_menu_switch("GEOMETRY")
4442
+ collection = _typed_menu_switch("COLLECTION")
4443
+ image = _typed_menu_switch("IMAGE")
4444
+ material = _typed_menu_switch("MATERIAL")
4445
+ bundle = _typed_menu_switch("BUNDLE")
4446
+ closure = _typed_menu_switch("CLOSURE")
4447
+
4448
+ def __init__(
4449
+ self,
4450
+ *args: TYPE_INPUT_ALL,
4451
+ menu: TYPE_INPUT_MENU = None,
4452
+ data_type: SOCKET_TYPES = "FLOAT",
4453
+ **kwargs: TYPE_INPUT_ALL,
4454
+ ):
4455
+ super().__init__()
4456
+ self.data_type = data_type
4457
+ self.node.enum_items.clear()
4458
+ key_args = {"Menu": menu}
4459
+ self._link_args(*args, **kwargs)
4460
+ self._establish_links(**key_args)
4461
+
4462
+ def _link_args(self, *args: TYPE_INPUT_ALL, **kwargs: TYPE_INPUT_ALL):
4463
+ for arg in args:
4464
+ if _is_default_value(arg):
4465
+ socket = self._create_socket(f"Item_{len(self.node.enum_items)}")
4466
+ socket.default_value = arg
4467
+ else:
4468
+ source = self._source_socket(arg)
4469
+ self.tree.link(source, self.node.inputs["__extend__"])
4470
+
4471
+ for key, value in kwargs.items():
4472
+ if _is_default_value(value):
4473
+ socket = self._create_socket(key)
4474
+ socket.default_value = value
4475
+ else:
4476
+ source = self._source_socket(value) # type: ignore
4477
+ self._link(source, self.node.inputs["__extend__"])
4478
+ self.node.enum_items[-1].name = key
4479
+
4480
+ def _create_socket(self, name: str) -> bpy.types.NodeSocket:
4481
+ item = self.node.enum_items.new(name)
4482
+ return self.node.inputs[item.name]
4483
+
4484
+ @property
4485
+ def inputs(self) -> dict[str, SocketLinker]:
4486
+ """Input sockets"""
4487
+ return {
4488
+ item.name: SocketLinker(self.node.inputs[item.name])
4489
+ for item in self.node.enum_items
4490
+ }
4491
+
4492
+ @property
4493
+ def outputs(self) -> dict[str, SocketLinker]:
4494
+ """Input sockets"""
4495
+ return {
4496
+ item.name: SocketLinker(self.node.outputs[item.name])
4497
+ for item in self.node.enum_items
4498
+ }
4499
+
4500
+ @property
4501
+ def i_menu(self) -> SocketLinker:
4502
+ """Input socket: Menu"""
4503
+ return self._input("Menu")
4504
+
4505
+ @property
4506
+ def o_output(self) -> SocketLinker:
4507
+ """Output socket: Output"""
4508
+ return self._output("Output")
4509
+
4510
+ @property
4511
+ def data_type(self) -> SOCKET_TYPES:
4512
+ """Input socket: Data Type"""
4513
+ return self.node.data_type # type: ignore
4514
+
4515
+ @data_type.setter
4516
+ def data_type(self, value: SOCKET_TYPES):
4517
+ """Input socket: Data Type"""
4518
+ self.node.data_type = value