nodebpy 0.1.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,1356 @@
1
+ """
2
+ Some of the nodes need to be manually specified because they are a bit tricky to generate automatically.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import bpy
8
+ from bpy.types import NodeSocketFloat, NodeSocketVector
9
+ from typing import Iterable, Literal
10
+
11
+ from ..builder import (
12
+ NodeBuilder,
13
+ NodeSocket,
14
+ source_socket,
15
+ SocketLinker,
16
+ )
17
+ from . import types
18
+ from .types import LINKABLE, TYPE_INPUT_BOOLEAN, TYPE_INPUT_VECTOR, _AttributeDomains
19
+
20
+ _RANDOM_VALUE_DATA_TYPES = Literal["FLOAT", "INT", "BOOLEAN", "FLOAT_VECTOR"]
21
+
22
+
23
+ class RandomValue(NodeBuilder):
24
+ """Random Value node"""
25
+
26
+ name = "FunctionNodeRandomValue"
27
+ node: bpy.types.FunctionNodeRandomValue
28
+ _default_input_id = "ID"
29
+
30
+ def __init__(
31
+ self,
32
+ data_type: _RANDOM_VALUE_DATA_TYPES,
33
+ id: int | LINKABLE | None = None,
34
+ seed: int | LINKABLE | None = None,
35
+ **kwargs,
36
+ ):
37
+ super().__init__()
38
+ self.node.data_type = data_type
39
+ key_args = {
40
+ "ID": id,
41
+ "Seed": seed,
42
+ }
43
+ key_args.update(kwargs)
44
+
45
+ self._establish_links(**key_args)
46
+
47
+ @property
48
+ def data_type(self) -> _RANDOM_VALUE_DATA_TYPES:
49
+ return self.node.data_type # type: ignore
50
+
51
+ @data_type.setter
52
+ def data_type(self, value: _RANDOM_VALUE_DATA_TYPES):
53
+ self.node.data_type = value
54
+
55
+ @property
56
+ def o_value(self) -> NodeSocket:
57
+ """Output socket: Value"""
58
+ match self.data_type:
59
+ case "FLOAT":
60
+ return self._output("Value_001")
61
+ case "INT":
62
+ return self._output("Value_002")
63
+ case "BOOLEAN":
64
+ return self._output("Value_003")
65
+ case "FLOAT_VECTOR":
66
+ return self._output("Value")
67
+
68
+ def i_min(self) -> NodeSocket:
69
+ """Input socket: Minimum"""
70
+ match self.data_type:
71
+ case "FLOAT":
72
+ return self._input("Min_001")
73
+ case "INT":
74
+ return self._input("Min_002")
75
+ case "BOOLEAN":
76
+ raise ValueError(
77
+ "Boolean data type does not support minimum value, use 'Probability'"
78
+ )
79
+ case "FLOAT_VECTOR":
80
+ return self._input("Min")
81
+
82
+ def i_max(self) -> NodeSocket:
83
+ """Input socket: Maximum"""
84
+ match self.data_type:
85
+ case "FLOAT":
86
+ return self._input("Max_001")
87
+ case "INT":
88
+ return self._input("Max_002")
89
+ case "BOOLEAN":
90
+ raise ValueError(
91
+ "Boolean data type does not support maximum value, use 'Probability'"
92
+ )
93
+ case "FLOAT_VECTOR":
94
+ return self._input("Max")
95
+
96
+ def i_probability(self) -> NodeSocket:
97
+ """Input socket: Probability"""
98
+ if self.data_type != "BOOLEAN":
99
+ raise ValueError(
100
+ f"Probability socket is only supported for boolean data types, not for data type: {self.data_type}"
101
+ )
102
+
103
+ return self._input("Probability")
104
+
105
+ @classmethod
106
+ def float(
107
+ cls,
108
+ min: float | LINKABLE = 0.0,
109
+ max: float | LINKABLE = 1.0,
110
+ id: int | LINKABLE | None = None,
111
+ seed: int | LINKABLE = 1,
112
+ ) -> NodeBuilder:
113
+ buidler = cls(Min_001=min, Max_001=max, id=id, seed=seed, data_type="FLOAT")
114
+ buidler._default_output_id = "Value_001"
115
+ return buidler
116
+
117
+ @classmethod
118
+ def integer(
119
+ cls,
120
+ min: int | LINKABLE = 0,
121
+ max: int | LINKABLE = 1,
122
+ id: int | LINKABLE | None = None,
123
+ seed: int | LINKABLE = 1,
124
+ ) -> NodeBuilder:
125
+ buidler = cls(Min_002=min, Max_002=max, id=id, seed=seed, data_type="INT")
126
+ buidler._default_output_id = "Value_002"
127
+ return buidler
128
+
129
+ @classmethod
130
+ def boolean(
131
+ cls,
132
+ probability: float | LINKABLE = 0.5,
133
+ id: int | LINKABLE | None = None,
134
+ seed: int | LINKABLE = 1,
135
+ ) -> NodeBuilder:
136
+ builder = cls(Probability=probability, id=id, seed=seed, data_type="BOOLEAN")
137
+ builder._default_output_id = "Value_003"
138
+ return builder
139
+
140
+ @classmethod
141
+ def vector(
142
+ cls,
143
+ min: tuple[float, float, float] | list[float] | LINKABLE = (0.0, 0.0, 0.0),
144
+ max: tuple[float, float, float] | list[float] | LINKABLE = (1.0, 1.0, 1.0),
145
+ id: int | LINKABLE | None = None,
146
+ seed: int | LINKABLE = 1,
147
+ ) -> NodeBuilder:
148
+ buidler = cls(Min=min, Max=max, id=id, seed=seed, data_type="FLOAT_VECTOR")
149
+ buidler._default_output_id = "Value"
150
+ return buidler
151
+
152
+
153
+ class SeparateXYZ(NodeBuilder):
154
+ """Split a vector into its X, Y, and Z components"""
155
+
156
+ name = "ShaderNodeSeparateXYZ"
157
+ node: bpy.types.ShaderNodeSeparateXYZ # type: ignore
158
+
159
+ def __init__(self, vector: TYPE_INPUT_VECTOR | None = None):
160
+ super().__init__()
161
+ self._establish_links(**{"Vector": vector})
162
+
163
+ @property
164
+ def i_vector(self) -> NodeSocket:
165
+ """Input socket: Vector"""
166
+ return self._input("Vector")
167
+
168
+ @property
169
+ def o_x(self) -> NodeSocket:
170
+ """Output socket: X"""
171
+ return self._output("X")
172
+
173
+ @property
174
+ def o_y(self) -> NodeSocket:
175
+ """Output socket: Y"""
176
+ return self._output("Y")
177
+
178
+ @property
179
+ def o_z(self) -> NodeSocket:
180
+ """Output socket: Z"""
181
+ return self._output("Z")
182
+
183
+
184
+ class CombineXYZ(NodeBuilder):
185
+ """Create a vector from X, Y, and Z components"""
186
+
187
+ name = "ShaderNodeCombineXYZ"
188
+ node: bpy.types.ShaderNodeCombineXYZ # type: ignore
189
+
190
+ def __init__(
191
+ self,
192
+ x: float | LINKABLE = 0.0,
193
+ y: float | LINKABLE = 0.0,
194
+ z: float | LINKABLE = 0.0,
195
+ ):
196
+ super().__init__()
197
+ self._establish_links(**{"X": x, "Y": y, "Z": z})
198
+
199
+ @property
200
+ def o_vector(self) -> NodeSocket:
201
+ """Output socket: Vector"""
202
+ return self._output("Vector")
203
+
204
+ @property
205
+ def i_x(self) -> NodeSocket:
206
+ """Input socket: X"""
207
+ return self._input("X")
208
+
209
+ @property
210
+ def i_y(self) -> NodeSocket:
211
+ """Input socket: Y"""
212
+ return self._input("Y")
213
+
214
+ @property
215
+ def i_z(self) -> NodeSocket:
216
+ """Input socket: Z"""
217
+ return self._input("Z")
218
+
219
+
220
+ _MIX_VALUE_DATA_TYPES = Literal["FLOAT", "VECTOR", "COLOR", "ROTATION"]
221
+
222
+
223
+ class Mix(NodeBuilder):
224
+ """Mix values by a factor"""
225
+
226
+ name = "ShaderNodeMix"
227
+ node: bpy.types.ShaderNodeMix # type: ignore
228
+
229
+ def __init__(
230
+ self,
231
+ data_type: _MIX_VALUE_DATA_TYPES = "FLOAT",
232
+ **kwargs,
233
+ ):
234
+ super().__init__()
235
+ self._default_input_id = f"A_{data_type.title()}"
236
+ self._default_output_id = f"Result_{data_type.title()}"
237
+ self.node.data_type = "RGBA" if data_type == "COLOR" else data_type
238
+ key_args = {}
239
+ key_args.update(kwargs)
240
+ self._establish_links(**key_args)
241
+
242
+ @property
243
+ def data_type(self) -> str:
244
+ return self.node.data_type
245
+
246
+ @data_type.setter
247
+ def data_type(self, value: _MIX_VALUE_DATA_TYPES):
248
+ self.node.data_type = value # type: ignore
249
+
250
+ @property
251
+ def factor_mode(self) -> Literal["UNIFORM", "NON_UNIFORM"]:
252
+ return self.node.factor_mode
253
+
254
+ @factor_mode.setter
255
+ def factor_mode(self, value: Literal["NON_UNIFORM", "UNIFORM"]):
256
+ self.node.factor_mode = value
257
+
258
+ @property
259
+ def o_result(self) -> NodeSocket:
260
+ """Output socket: Result"""
261
+ return self._default_output_socket
262
+
263
+ @property
264
+ def i_factor(self) -> NodeSocket:
265
+ """Input socket: Factor"""
266
+ match self.data_type:
267
+ case "FLOAT":
268
+ name = "Factor_Float"
269
+ case "VECTOR":
270
+ name = (
271
+ "Factor_Float" if self.factor_mode == "UNIFORM" else "Factor_Vector"
272
+ )
273
+ case "RGBA":
274
+ name = "Factor_Color"
275
+ case "ROTATION":
276
+ name = "Factor_Rotation"
277
+ case _:
278
+ raise ValueError(f"Unsupported data type: {self.data_type}")
279
+
280
+ idx = self._input_idx(name)
281
+ return self.node.inputs[idx]
282
+
283
+ @property
284
+ def i_value_a(self) -> NodeSocket:
285
+ """Input socket: Value A"""
286
+ type_name = "Color" if self.data_type == "RGBA" else self.data_type
287
+ name = f"A_{type_name}"
288
+ idx = self._input_idx(name)
289
+ return self.node.inputs[idx]
290
+
291
+ @property
292
+ def i_value_b(self) -> NodeSocket:
293
+ """Input socket: Value B"""
294
+ type_name = "Color" if self.data_type == "RGBA" else self.data_type
295
+ name = f"B_{type_name}"
296
+ idx = self._input_idx(name)
297
+ return self.node.inputs[idx]
298
+
299
+ @classmethod
300
+ def float(
301
+ cls,
302
+ factor: float | LINKABLE = 0.5,
303
+ a: float | LINKABLE = 0.0,
304
+ b: float | LINKABLE = 0.0,
305
+ clamp_factor: bool | LINKABLE = True,
306
+ ) -> Mix:
307
+ builder = cls(
308
+ Factor_Float=factor,
309
+ A_Float=a,
310
+ B_Float=b,
311
+ data_type="COLOR",
312
+ )
313
+ builder.node.clamp_factor = clamp_factor
314
+ return builder
315
+
316
+ @classmethod
317
+ def vector(
318
+ cls,
319
+ factor: float | LINKABLE = 0.5,
320
+ a: tuple[float, float, float] | list[float] | LINKABLE = (0.0, 0.0, 0.0),
321
+ b: tuple[float, float, float] | list[float] | LINKABLE = (1.0, 1.0, 1.0),
322
+ clamp_factor: bool = True,
323
+ factor_mode: Literal["UNIFORM", "NON_UNIFORM"] = "UNIFORM",
324
+ ) -> Mix:
325
+ match factor_mode:
326
+ case "UNIFORM":
327
+ builder = cls(
328
+ Factor_Float=factor,
329
+ A_Vector=a,
330
+ B_Vector=b,
331
+ data_type="VECTOR",
332
+ )
333
+ case "NON_UNIFORM":
334
+ builder = cls(
335
+ Factor_Vector=factor,
336
+ A_Vector=a,
337
+ B_Vector=b,
338
+ data_type="VECTOR",
339
+ )
340
+
341
+ builder.node.clamp_factor = clamp_factor
342
+ return builder
343
+
344
+ @classmethod
345
+ def color(
346
+ cls,
347
+ factor: float | LINKABLE = 0.5,
348
+ a: tuple[float, float, float] | list[float] | LINKABLE = (0.0, 0.0, 0.0),
349
+ b: tuple[float, float, float] | list[float] | LINKABLE = (1.0, 1.0, 1.0),
350
+ blend_type: str = "add",
351
+ clamp_factor: bool = True,
352
+ clamp_result: bool = True,
353
+ ) -> Mix:
354
+ builder = cls(
355
+ Factor_Float=factor,
356
+ A_Color=a,
357
+ B_Color=b,
358
+ data_type="COLOR",
359
+ )
360
+ builder.node.blend_type = blend_type.capitalize()
361
+ builder.node.clamp_factor = clamp_factor
362
+ builder.node.clamp_result = clamp_result
363
+ return builder
364
+
365
+ @classmethod
366
+ def rotation(
367
+ cls,
368
+ a: tuple[float, float, float, float] | list[float] | LINKABLE | None = None,
369
+ b: tuple[float, float, float, float] | list[float] | LINKABLE | None = None,
370
+ factor: float | LINKABLE = 0.5,
371
+ clamp_factor: bool = True,
372
+ ) -> Mix:
373
+ builder = cls(
374
+ Factor_Float=factor,
375
+ A_Rotation=a,
376
+ B_Rotation=b,
377
+ data_type="ROTATION",
378
+ )
379
+ builder.node.clamp_factor = clamp_factor
380
+ return builder
381
+
382
+
383
+ _AttributeDataTypes = Literal[
384
+ "FLOAT", "INT", "BOOLEAN", "VECTOR", "RGBA", "ROTATION", "MATRIX"
385
+ ]
386
+
387
+
388
+ class CaptureAttribute(NodeBuilder):
389
+ """Store the result of a field on a geometry and output the data as a node socket. Allows remembering or interpolating data as the geometry changes, such as positions before deformation"""
390
+
391
+ name = "GeometryNodeCaptureAttribute"
392
+ node: bpy.types.GeometryNodeCaptureAttribute
393
+
394
+ def __init__(
395
+ self,
396
+ geometry: LINKABLE = None,
397
+ domain: _AttributeDomains = "POINT",
398
+ **kwargs,
399
+ ):
400
+ super().__init__()
401
+ key_args = {"Geometry": geometry}
402
+ key_args.update(kwargs)
403
+ self.domain = domain
404
+ self._establish_links(**key_args)
405
+
406
+ def capture(
407
+ self,
408
+ value: LINKABLE,
409
+ name: str | None = None,
410
+ data_type: _AttributeDataTypes | None = None,
411
+ ):
412
+ """Capture the value to store in the attribute"""
413
+ source = source_socket(value)
414
+ if name is None:
415
+ name = source.name
416
+ if data_type is None:
417
+ data_type = source.type # type: ignore
418
+
419
+ item = self._add_item(name, data_type) # type: ignore
420
+ self._establish_links(**{item.name: value})
421
+ return SocketLinker(self.node.outputs[item.name])
422
+
423
+ def _add_item(
424
+ self, name: str, data_type: _AttributeDataTypes = "FLOAT"
425
+ ) -> bpy.types.NodeGeometryCaptureAttributeItem:
426
+ """Add a new output socket to capture additional attributes"""
427
+ return self._items.new(data_type, name)
428
+
429
+ @property
430
+ def _items(self) -> bpy.types.NodeGeometryCaptureAttributeItems:
431
+ return self.node.capture_items
432
+
433
+ @property
434
+ def i_geometry(self) -> NodeSocket:
435
+ """Input socket: Geometry"""
436
+ return self._input("Geometry")
437
+
438
+ @property
439
+ def o_geometry(self) -> NodeSocket:
440
+ """Output socket: Geometry"""
441
+ return self._output("Geometry")
442
+
443
+ @property
444
+ def domain(
445
+ self,
446
+ ) -> _AttributeDomains:
447
+ return self.node.domain
448
+
449
+ @domain.setter
450
+ def domain(
451
+ self,
452
+ value: _AttributeDomains,
453
+ ):
454
+ self.node.domain = value
455
+
456
+
457
+ class JoinGeometry(NodeBuilder):
458
+ """Merge separately generated geometries into a single one"""
459
+
460
+ name = "GeometryNodeJoinGeometry"
461
+ node: bpy.types.GeometryNodeJoinGeometry
462
+
463
+ def __init__(self, geometry: NodeBuilder | Iterable[NodeBuilder] | None = None):
464
+ super().__init__()
465
+ if geometry is None:
466
+ return
467
+ elif isinstance(geometry, NodeBuilder):
468
+ geometry = [geometry]
469
+ for source in reversed(geometry):
470
+ self.link_from(source, self)
471
+
472
+ @property
473
+ def i_geometry(self) -> NodeSocket:
474
+ """Input socket: Geometry"""
475
+ return self._input("Geometry")
476
+
477
+ @property
478
+ def o_geometry(self) -> NodeSocket:
479
+ """Output socket: Geometry"""
480
+ return self._output("Geometry")
481
+
482
+
483
+ class Math(NodeBuilder):
484
+ """Perform math operations"""
485
+
486
+ name = "ShaderNodeMath"
487
+ node: bpy.types.ShaderNodeMath # type: ignore
488
+
489
+ def __init__(
490
+ self,
491
+ operation: types.NodeMathItems = "ADD",
492
+ use_clamp: bool = False,
493
+ **kwargs,
494
+ ):
495
+ super().__init__()
496
+ self.operation = operation
497
+ self.use_clamp = use_clamp
498
+ self._establish_links(**kwargs)
499
+
500
+ @property
501
+ def operation(self) -> types.NodeMathItems:
502
+ return self.node.operation
503
+
504
+ @operation.setter
505
+ def operation(self, value: types.NodeMathItems):
506
+ self.node.operation = value
507
+
508
+ @property
509
+ def use_clamp(self) -> bool:
510
+ return self.node.use_clamp
511
+
512
+ @use_clamp.setter
513
+ def use_clamp(self, value: bool):
514
+ self.node.use_clamp = value
515
+
516
+ @property
517
+ def o_value(self) -> NodeSocketFloat:
518
+ return self._output("Value") # type: ignore
519
+
520
+ def _input(self, identifier: str) -> NodeSocketFloat:
521
+ return self._input(identifier)
522
+
523
+ @property
524
+ def i_value(self) -> NodeSocketFloat:
525
+ return self._input("Value")
526
+
527
+ @property
528
+ def i_value_001(self) -> NodeSocketFloat:
529
+ return self._input("Value_001")
530
+
531
+ @property
532
+ def i_value_002(self) -> NodeSocketFloat:
533
+ return self._input("Value_002")
534
+
535
+ @classmethod
536
+ def add(
537
+ cls,
538
+ a: float | LINKABLE = 0.5,
539
+ b: float | LINKABLE = 0.5,
540
+ ) -> "Math":
541
+ """Create Math with operation of `a + b`."""
542
+ return cls(operation="ADD", Value=a, Value_001=b)
543
+
544
+ @classmethod
545
+ def subtract(
546
+ cls,
547
+ a: float | LINKABLE = 0.5,
548
+ b: float | LINKABLE = 0.5,
549
+ ) -> "Math":
550
+ """Create Math with operation of `a - b`."""
551
+ return cls(operation="SUBTRACT", Value=a, Value_001=b)
552
+
553
+ @classmethod
554
+ def multiply(
555
+ cls,
556
+ a: float | LINKABLE = 0.5,
557
+ b: float | LINKABLE = 0.5,
558
+ ) -> "Math":
559
+ """Create Math with operation of `a * b`."""
560
+ return cls(operation="MULTIPLY", Value=a, Value_001=b)
561
+
562
+ @classmethod
563
+ def divide(
564
+ cls,
565
+ a: float | LINKABLE = 0.5,
566
+ b: float | LINKABLE = 0.5,
567
+ ) -> "Math":
568
+ """Create Math with operation of `a / b`."""
569
+ return cls(operation="DIVIDE", Value=a, Value_001=b)
570
+
571
+ @classmethod
572
+ def multiply_add(
573
+ cls,
574
+ a: float | LINKABLE = 0.5,
575
+ b: float | LINKABLE = 0.5,
576
+ c: float | LINKABLE = 0.5,
577
+ ) -> "Math":
578
+ """Create Math with operation `a * b + c`."""
579
+ return cls(operation="MULTIPLY_ADD", Value=a, Value_001=b, value_002=c)
580
+
581
+ @classmethod
582
+ def power(
583
+ cls,
584
+ base: float | LINKABLE = 0.5,
585
+ exponent: float | LINKABLE = 0.5,
586
+ ) -> "Math":
587
+ """Create Math with operation `base ** exponent`."""
588
+ return cls(operation="POWER", Value=base, Value_001=exponent)
589
+
590
+ @classmethod
591
+ def logarithm(
592
+ cls,
593
+ value: float | LINKABLE = 0.5,
594
+ base: float | LINKABLE = 0.5,
595
+ ) -> "Math":
596
+ """Create Math with operation `log(value, base)`."""
597
+ return cls(operation="LOGARITHM", Value=value, Value_001=base)
598
+
599
+ @classmethod
600
+ def sqrt(
601
+ cls,
602
+ value: float | LINKABLE = 0.5,
603
+ ) -> "Math":
604
+ """Create Math with operation `sqrt(value)`."""
605
+ return cls(operation="SQRT", Value=value)
606
+
607
+ @classmethod
608
+ def inverse_sqrt(
609
+ cls,
610
+ value: float | LINKABLE = 0.5,
611
+ ) -> "Math":
612
+ """Create Math with operation `inverse_sqrt(value)`."""
613
+ return cls(operation="INVERSE_SQRT", Value=value)
614
+
615
+ @classmethod
616
+ def absolute(
617
+ cls,
618
+ value: float | LINKABLE = 0.5,
619
+ ) -> "Math":
620
+ """Create Math with operation `abs(value)`."""
621
+ return cls(operation="ABSOLUTE", Value=value)
622
+
623
+ @classmethod
624
+ def exponent(
625
+ cls,
626
+ value: float | LINKABLE = 0.5,
627
+ ) -> "Math":
628
+ """Create Math with operation `exp(value)`."""
629
+ return cls(operation="EXPONENT", Value=value)
630
+
631
+ @classmethod
632
+ def minimum(
633
+ cls,
634
+ a: float | LINKABLE = 0.5,
635
+ b: float | LINKABLE = 0.5,
636
+ ) -> "Math":
637
+ """Create Math with operation `min(a, b)`."""
638
+ return cls(operation="MINIMUM", Value=a, Value_001=b)
639
+
640
+ @classmethod
641
+ def maximum(
642
+ cls,
643
+ a: float | LINKABLE = 0.5,
644
+ b: float | LINKABLE = 0.5,
645
+ ) -> "Math":
646
+ """Create Math with operation `max(a, b)`."""
647
+ return cls(operation="MAXIMUM", Value=a, Value_001=b)
648
+
649
+ @classmethod
650
+ def less_than(
651
+ cls,
652
+ value: float | LINKABLE = 0.5,
653
+ threshold: float | LINKABLE = 0.5,
654
+ ) -> "Math":
655
+ """Create Math with operation `value < threshold` returning 1 or 0."""
656
+ return cls(operation="LESS_THAN", Value=value, Value_001=threshold)
657
+
658
+ @classmethod
659
+ def greater_than(
660
+ cls,
661
+ value: float | LINKABLE = 0.5,
662
+ threshold: float | LINKABLE = 0.5,
663
+ ) -> "Math":
664
+ """Create Math with operation `value > threshold` returning 1 or 0."""
665
+ return cls(operation="GREATER_THAN", Value=value, Value_001=threshold)
666
+
667
+ @classmethod
668
+ def sign(
669
+ cls,
670
+ value: float | LINKABLE = 0.5,
671
+ ) -> "Math":
672
+ """Create Math with operation `sign(value)` returning -1, 0, or 1."""
673
+ return cls(operation="SIGN", Value=value)
674
+
675
+ @classmethod
676
+ def compare(
677
+ cls,
678
+ a: float | LINKABLE = 0.5,
679
+ b: float | LINKABLE = 0.5,
680
+ epsilon: float | LINKABLE = 0.5,
681
+ ) -> "Math":
682
+ """Create Math with operation `compare(a, b, epsilon)` returning -1, 0, or 1."""
683
+ return cls(operation="COMPARE", Value=a, Value_001=b, value_002=epsilon)
684
+
685
+ @classmethod
686
+ def smooth_min(
687
+ cls,
688
+ a: float | LINKABLE = 0.5,
689
+ b: float | LINKABLE = 0.5,
690
+ distance: float | LINKABLE = 0.5,
691
+ ) -> "Math":
692
+ """Create Math with operation `smooth_min(a, b, distance)`."""
693
+ return cls(operation="SMOOTH_MIN", Value=a, Value_001=b, value_002=distance)
694
+
695
+ @classmethod
696
+ def smooth_max_(
697
+ cls,
698
+ a: float | LINKABLE = 0.5,
699
+ b: float | LINKABLE = 0.5,
700
+ distance: float | LINKABLE = 0.5,
701
+ ) -> "Math":
702
+ """Create Math with operation `smooth_max(a, b, distance)`."""
703
+ return cls(operation="SMOOTH_MAX", Value=a, Value_001=b, value_002=distance)
704
+
705
+ @classmethod
706
+ def round(
707
+ cls,
708
+ value: float | LINKABLE = 0.5,
709
+ ) -> "Math":
710
+ """Round A to the nearest integer. Round up if 0.5 or greater."""
711
+ return cls(operation="ROUND", Value=value)
712
+
713
+ @classmethod
714
+ def floor(
715
+ cls,
716
+ value: float | LINKABLE = 0.5,
717
+ ) -> "Math":
718
+ """The largest integer smaller than or equal to `value`"""
719
+ return cls(operation="FLOOR", Value=value)
720
+
721
+ @classmethod
722
+ def ceil(
723
+ cls,
724
+ value: float | LINKABLE = 0.5,
725
+ ) -> "Math":
726
+ """The smallest integer greater than or equal to `value`"""
727
+ return cls(operation="CEIL", Value=value)
728
+
729
+ @classmethod
730
+ def truncate(
731
+ cls,
732
+ value: float | LINKABLE = 0.5,
733
+ ) -> "Math":
734
+ """The integer part of `value` removing the fractional part"""
735
+ return cls(operation="TRUNC", Value=value)
736
+
737
+ @classmethod
738
+ def fraction(
739
+ cls,
740
+ value: float | LINKABLE = 0.5,
741
+ ) -> "Math":
742
+ """The fractional part of `value`"""
743
+ return cls(operation="FRACT", Value=value)
744
+
745
+ @classmethod
746
+ def truncated_modulo(
747
+ cls,
748
+ a: float | LINKABLE = 0.5,
749
+ b: float | LINKABLE = 0.5,
750
+ ) -> "Math":
751
+ """The remained of truncated division using fmod(a, b)"""
752
+ return cls(operation="MODULO", Value=a, Value_001=b)
753
+
754
+ @classmethod
755
+ def floored_modulo(
756
+ cls,
757
+ a: float | LINKABLE = 0.5,
758
+ b: float | LINKABLE = 0.5,
759
+ ) -> "Math":
760
+ """The remained of floored division"""
761
+ return cls(operation="FLOORED_MODULO", Value=a, Value_001=b)
762
+
763
+ @classmethod
764
+ def wrap(
765
+ cls,
766
+ value: float | LINKABLE = 0.5,
767
+ max: float | LINKABLE = 0.5,
768
+ min: float | LINKABLE = 0.5,
769
+ ) -> "Math":
770
+ """Wrap value to range, wrap(value, max, min)"""
771
+ return cls(operation="WRAP", Value=value, Value_001=max, value_002=min)
772
+
773
+ @classmethod
774
+ def snap(
775
+ cls,
776
+ value: float | LINKABLE = 0.5,
777
+ increment: float | LINKABLE = 0.5,
778
+ ) -> "Math":
779
+ """Snap to increment of `snap(value, increment)`"""
780
+ return cls(operation="SNAP", Value=value, Value_001=increment)
781
+
782
+ @classmethod
783
+ def ping_pong(
784
+ cls,
785
+ value: float | LINKABLE = 0.5,
786
+ scale: float | LINKABLE = 0.5,
787
+ ) -> "Math":
788
+ """Wraps a value and reverses every other cycle"""
789
+ return cls(operation="PINGPONG", Value=value, Value_001=scale)
790
+
791
+ @classmethod
792
+ def sine(
793
+ cls,
794
+ value: float | LINKABLE = 0.5,
795
+ ) -> "Math":
796
+ """Create Math with operation 'sin(value)'."""
797
+ return cls(operation="SINE", Value=value)
798
+
799
+ @classmethod
800
+ def cosine(
801
+ cls,
802
+ value: float | LINKABLE = 0.5,
803
+ ) -> "Math":
804
+ """Create Math with operation 'cos(value)'."""
805
+ return cls(operation="COSINE", Value=value)
806
+
807
+ @classmethod
808
+ def tangent(
809
+ cls,
810
+ value: float | LINKABLE = 0.5,
811
+ ) -> "Math":
812
+ """Create Math with operation 'tan(value)'."""
813
+ return cls(operation="TANGENT", Value=value)
814
+
815
+ @classmethod
816
+ def arcsine(
817
+ cls,
818
+ value: float | LINKABLE = 0.5,
819
+ ) -> "Math":
820
+ """Create Math with operation `arcsin(value)'."""
821
+ return cls(operation="ARCSINE", Value=value)
822
+
823
+ @classmethod
824
+ def arccosine(
825
+ cls,
826
+ value: float | LINKABLE = 0.5,
827
+ ) -> "Math":
828
+ """Create Math with operation 'arccos(value)'."""
829
+ return cls(operation="ARCCOSINE", Value=value)
830
+
831
+ @classmethod
832
+ def arctangent(
833
+ cls,
834
+ value: float | LINKABLE = 0.5,
835
+ ) -> "Math":
836
+ """Create Math with operation 'arctan(value)'."""
837
+ return cls(operation="ARCTANGENT", Value=value)
838
+
839
+ @classmethod
840
+ def arctan2(
841
+ cls,
842
+ a: float | LINKABLE = 0.5,
843
+ b: float | LINKABLE = 0.5,
844
+ ) -> "Math":
845
+ """Create Math with operation 'arctan(a / b)'."""
846
+ return cls(operation="ARCTAN2", Value=a, Value_001=b)
847
+
848
+ @classmethod
849
+ def sinh(
850
+ cls,
851
+ value: float | LINKABLE = 0.5,
852
+ ) -> "Math":
853
+ """Create Math with operation `sinh(value)`."""
854
+ return cls(operation="SINH", Value=value)
855
+
856
+ @classmethod
857
+ def cosh(
858
+ cls,
859
+ value: float | LINKABLE = 0.5,
860
+ ) -> "Math":
861
+ """Create Math with operation `cosh(value)`."""
862
+ return cls(operation="COSH", Value=value)
863
+
864
+ @classmethod
865
+ def tanh(
866
+ cls,
867
+ value: float | LINKABLE = 0.5,
868
+ ) -> "Math":
869
+ """Create Math with operation `tanh(value)`."""
870
+ return cls(operation="TANH", Value=value)
871
+
872
+ @classmethod
873
+ def radians(
874
+ cls,
875
+ degrees: float | LINKABLE = 0.5,
876
+ ) -> "Math":
877
+ """Create Math with operation `radians(degrees)`."""
878
+ return cls(operation="RADIANS", Value=degrees)
879
+
880
+ @classmethod
881
+ def degrees(
882
+ cls,
883
+ radians: float | LINKABLE = 0.5,
884
+ ) -> "Math":
885
+ """Create Math with operation 'To Degrees'."""
886
+ return cls(operation="DEGREES", Value=radians)
887
+
888
+
889
+ class BooleanMath(NodeBuilder):
890
+ """Boolean Math node"""
891
+
892
+ name = "FunctionNodeBooleanMath"
893
+ node: bpy.types.FunctionNodeBooleanMath
894
+
895
+ def __init__(self, operation: types.NodeBooleanMathItems = "AND", **kwargs):
896
+ super().__init__()
897
+ self.operator = operation
898
+ self._establish_links(**kwargs)
899
+
900
+ @property
901
+ def operation(self) -> types.NodeBooleanMathItems:
902
+ return self.node.operation
903
+
904
+ @operation.setter
905
+ def operation(self, value: types.NodeBooleanMathItems):
906
+ self.node.operation = value
907
+
908
+ @property
909
+ def i_boolean(self) -> bpy.types.NodeSocketBool:
910
+ return self._input("Boolean") # type: ignore
911
+
912
+ @property
913
+ def i_boolean_001(self) -> bpy.types.NodeSocketBool:
914
+ return self._input("Boolean_001") # type: ignore
915
+
916
+ @property
917
+ def o_boolean(self) -> bpy.types.NodeSocketBool:
918
+ return self._output("Boolean") # type: ignore
919
+
920
+ @classmethod
921
+ def l_and(
922
+ cls,
923
+ boolean: TYPE_INPUT_BOOLEAN = False,
924
+ boolean_001: TYPE_INPUT_BOOLEAN = False,
925
+ ) -> "BooleanMath":
926
+ """Create Boolean Math with operation 'AND'."""
927
+ return cls(operation="AND", Boolean=boolean, Boolean_001=boolean_001)
928
+
929
+ @classmethod
930
+ def l_or(
931
+ cls,
932
+ boolean: TYPE_INPUT_BOOLEAN = False,
933
+ boolean_001: TYPE_INPUT_BOOLEAN = False,
934
+ ) -> "BooleanMath":
935
+ """Create Boolean Math with operation 'OR'."""
936
+ return cls(operation="OR", Boolean=boolean, Boolean_001=boolean_001)
937
+
938
+ @classmethod
939
+ def l_not(cls, boolean: TYPE_INPUT_BOOLEAN = False) -> "BooleanMath":
940
+ """Create Boolean Math with operation 'NOT'."""
941
+ return cls(operation="NOT", Boolean=boolean)
942
+
943
+ @classmethod
944
+ def l_not_and(
945
+ cls,
946
+ boolean: TYPE_INPUT_BOOLEAN = False,
947
+ boolean_001: TYPE_INPUT_BOOLEAN = False,
948
+ ) -> "BooleanMath":
949
+ """Create Boolean Math with operation 'NAND'."""
950
+ return cls(operation="NAND", Boolean=boolean, Boolean_001=boolean_001)
951
+
952
+ @classmethod
953
+ def l_nor(
954
+ cls,
955
+ boolean: TYPE_INPUT_BOOLEAN = False,
956
+ boolean_001: TYPE_INPUT_BOOLEAN = False,
957
+ ) -> "BooleanMath":
958
+ """Create Boolean Math with operation 'NOR'."""
959
+ return cls(operation="NOR", Boolean=boolean, Boolean_001=boolean_001)
960
+
961
+ @classmethod
962
+ def l_equal(
963
+ cls,
964
+ boolean: TYPE_INPUT_BOOLEAN = False,
965
+ boolean_001: TYPE_INPUT_BOOLEAN = False,
966
+ ) -> "BooleanMath":
967
+ """Create Boolean Math with operation 'XNOR'."""
968
+ return cls(operation="XNOR", Boolean=boolean, Boolean_001=boolean_001)
969
+
970
+ @classmethod
971
+ def l_not_equal(
972
+ cls,
973
+ boolean: TYPE_INPUT_BOOLEAN = False,
974
+ boolean_001: TYPE_INPUT_BOOLEAN = False,
975
+ ) -> "BooleanMath":
976
+ """Create Boolean Math with operation 'XOR'."""
977
+ return cls(operation="XOR", Boolean=boolean, Boolean_001=boolean_001)
978
+
979
+ @classmethod
980
+ def l_imply(
981
+ cls,
982
+ boolean: TYPE_INPUT_BOOLEAN = False,
983
+ boolean_001: TYPE_INPUT_BOOLEAN = False,
984
+ ) -> "BooleanMath":
985
+ """Create Boolean Math with operation 'IMPLY'."""
986
+ return cls(operation="IMPLY", Boolean=boolean, Boolean_001=boolean_001)
987
+
988
+ @classmethod
989
+ def l_subtract(
990
+ cls,
991
+ boolean: TYPE_INPUT_BOOLEAN = False,
992
+ boolean_001: TYPE_INPUT_BOOLEAN = False,
993
+ ) -> "BooleanMath":
994
+ """Create Boolean Math with operation 'NIMPLY'."""
995
+ return cls(operation="NIMPLY", Boolean=boolean, Boolean_001=boolean_001)
996
+
997
+
998
+ _VectorMathOperations = Literal[
999
+ "ADD",
1000
+ "SUBTRACT",
1001
+ "MULTIPLY",
1002
+ "DIVIDE",
1003
+ "MULTIPLY_ADD",
1004
+ "CROSS_PRODUCT",
1005
+ "PROJECT",
1006
+ "REFLECT",
1007
+ "REFRACT",
1008
+ "FACEFORWARD",
1009
+ "DOT_PRODUCT",
1010
+ "DISTANCE",
1011
+ "LENGTH",
1012
+ "SCALE",
1013
+ "NORMALIZE",
1014
+ "ABSOLUTE",
1015
+ "POWER",
1016
+ "SIGN",
1017
+ "MINIMUM",
1018
+ "MAXIMUM",
1019
+ "FLOOR",
1020
+ "CEIL",
1021
+ "FRACTION",
1022
+ "MODULO",
1023
+ "WRAP",
1024
+ "SNAP",
1025
+ "SINE",
1026
+ "COSINE",
1027
+ "TANGENT",
1028
+ ]
1029
+
1030
+
1031
+ class VectorMath(NodeBuilder):
1032
+ """Perform vector math operation"""
1033
+
1034
+ name = "ShaderNodeVectorMath"
1035
+ node: bpy.types.ShaderNodeVectorMath
1036
+
1037
+ def __init__(
1038
+ self,
1039
+ operation: _VectorMathOperations = "ADD",
1040
+ **kwargs,
1041
+ ):
1042
+ super().__init__()
1043
+ self.operation = operation
1044
+ self._establish_links(**kwargs)
1045
+
1046
+ @property
1047
+ def i_vector(self) -> bpy.types.NodeSocketVector:
1048
+ """Input socket: Vector"""
1049
+ return self._input("Vector")
1050
+
1051
+ @property
1052
+ def i_vector_001(self) -> bpy.types.NodeSocketVector:
1053
+ """Input socket: Vector"""
1054
+ return self._input("Vector_001")
1055
+
1056
+ @property
1057
+ def o_vector(self) -> bpy.types.NodeSocketVector:
1058
+ """Output socket: Vector"""
1059
+ if self.operation in {"DOT_PRODUCT", "DISTANCE", "LENGTH"}:
1060
+ raise RuntimeError(
1061
+ f"Output 'Vector' is not available for operation '{self.operation}'"
1062
+ )
1063
+ return self._output("Vector")
1064
+
1065
+ def o_value(self) -> bpy.types.NodeSocketFloat:
1066
+ """Output socket: Value"""
1067
+ if self.operation not in {"DOT_PRODUCT", "DISTANCE", "LENGTH"}:
1068
+ raise RuntimeError(
1069
+ f"Output 'Value' is not available for operation '{self.operation}'"
1070
+ )
1071
+ return self._output("Value")
1072
+
1073
+ @property
1074
+ def _default_output_socket(self) -> NodeSocketFloat | NodeSocketVector:
1075
+ match self.operation:
1076
+ case "DOT_PRODUCT" | "DISTANCE" | "LENGTH":
1077
+ return self.o_value
1078
+ case _:
1079
+ return self.o_vector
1080
+
1081
+ @property
1082
+ def operation(
1083
+ self,
1084
+ ) -> _VectorMathOperations:
1085
+ return self.node.operation
1086
+
1087
+ @operation.setter
1088
+ def operation(
1089
+ self,
1090
+ value: _VectorMathOperations,
1091
+ ):
1092
+ self.node.operation = value
1093
+
1094
+ @classmethod
1095
+ def add(
1096
+ cls,
1097
+ a: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1098
+ b: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1099
+ ) -> "VectorMath":
1100
+ """Create Vector Math with operation `a + b`."""
1101
+ return cls(operation="ADD", Vector=a, Vector_001=b)
1102
+
1103
+ @classmethod
1104
+ def subtract(
1105
+ cls,
1106
+ a: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1107
+ b: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1108
+ ) -> "VectorMath":
1109
+ """Create Vector Math with operation `a - b`."""
1110
+ return cls(operation="SUBTRACT", Vector=a, Vector_001=b)
1111
+
1112
+ @classmethod
1113
+ def multiply(
1114
+ cls,
1115
+ a: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1116
+ b: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1117
+ ) -> "VectorMath":
1118
+ """Create Vector Math with operation `a * b` element-wise."""
1119
+ return cls(operation="MULTIPLY", Vector=a, Vector_001=b)
1120
+
1121
+ @classmethod
1122
+ def divide(
1123
+ cls,
1124
+ a: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1125
+ b: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1126
+ ) -> "VectorMath":
1127
+ """Create Vector Math with operation 'Divide'."""
1128
+ return cls(operation="DIVIDE", Vector=a, Vector_001=b)
1129
+
1130
+ @classmethod
1131
+ def multiply_add(
1132
+ cls,
1133
+ vector: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1134
+ multiplier: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1135
+ addend: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1136
+ ) -> "VectorMath":
1137
+ """Create Vector Math with operation 'Multiply Add'."""
1138
+ return cls(
1139
+ operation="MULTIPLY_ADD",
1140
+ Vector=vector,
1141
+ Vector_001=multiplier,
1142
+ Vector_002=addend,
1143
+ )
1144
+
1145
+ @classmethod
1146
+ def cross_product(
1147
+ cls,
1148
+ a: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1149
+ b: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1150
+ ) -> "VectorMath":
1151
+ """Create Vector Math with operation 'Cross Product'."""
1152
+ return cls(operation="CROSS_PRODUCT", Vector=a, Vector_001=b)
1153
+
1154
+ @classmethod
1155
+ def project(
1156
+ cls,
1157
+ a: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1158
+ b: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1159
+ ) -> "VectorMath":
1160
+ """Project A onto B."""
1161
+ return cls(operation="PROJECT", Vector=a, Vector_001=b)
1162
+
1163
+ @classmethod
1164
+ def reflect(
1165
+ cls,
1166
+ a: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1167
+ b: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1168
+ ) -> "VectorMath":
1169
+ """Reflect A around the normal B. B does not need to be normalized."""
1170
+ return cls(operation="REFLECT", Vector=a, Vector_001=b)
1171
+
1172
+ @classmethod
1173
+ def refract(
1174
+ cls,
1175
+ a: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1176
+ b: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1177
+ ior: LINKABLE | float = 1.0,
1178
+ ) -> "VectorMath":
1179
+ """For a given incident vector and surface normal (b) with an index of refraction (ior), return the refraction vector"""
1180
+ return cls(operation="REFRACT", Vector=a, Vector_001=b, Scale=ior)
1181
+
1182
+ @classmethod
1183
+ def face_forward(
1184
+ cls,
1185
+ vector: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1186
+ incidence: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1187
+ reference: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1188
+ ) -> "VectorMath":
1189
+ """Orients a vector to face away from a surface (incidence) defined by it's normal (reference)"""
1190
+ return cls(
1191
+ operation="FACEFORWARD",
1192
+ Vector=vector,
1193
+ Vector_001=incidence,
1194
+ Vector_002=reference,
1195
+ )
1196
+
1197
+ @classmethod
1198
+ def dot_product(
1199
+ cls,
1200
+ a: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1201
+ b: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1202
+ ) -> "VectorMath":
1203
+ """Create Vector Math with operation 'Dot Product'."""
1204
+ return cls(operation="DOT_PRODUCT", Vector=a, Vector_001=b)
1205
+
1206
+ @classmethod
1207
+ def distance(
1208
+ cls,
1209
+ a: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1210
+ b: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1211
+ ) -> "VectorMath":
1212
+ """Create Vector Math with operation 'Distance'."""
1213
+ return cls(operation="DISTANCE", Vector=a, Vector_001=b)
1214
+
1215
+ @classmethod
1216
+ def length(
1217
+ cls,
1218
+ vector: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1219
+ ) -> "VectorMath":
1220
+ """Create Vector Math with operation 'Length'."""
1221
+ return cls(operation="LENGTH", Vector=vector)
1222
+
1223
+ @classmethod
1224
+ def scale(
1225
+ cls,
1226
+ vector: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1227
+ scale: LINKABLE | float = 1.0,
1228
+ ) -> "VectorMath":
1229
+ """Create Vector Math with operation 'Scale'."""
1230
+ return cls(operation="SCALE", Vector=vector, Scale=scale)
1231
+
1232
+ @classmethod
1233
+ def normalize(
1234
+ cls,
1235
+ vector: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1236
+ ) -> "VectorMath":
1237
+ """Create Vector Math with operation 'Normalize'."""
1238
+ return cls(operation="NORMALIZE", Vector=vector)
1239
+
1240
+ @classmethod
1241
+ def absolute(
1242
+ cls,
1243
+ vector: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1244
+ ) -> "VectorMath":
1245
+ """Create Vector Math with operation 'Absolute'."""
1246
+ return cls(operation="ABSOLUTE", Vector=vector)
1247
+
1248
+ @classmethod
1249
+ def power(
1250
+ cls,
1251
+ base: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1252
+ exponent: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1253
+ ) -> "VectorMath":
1254
+ """Create Vector Math with operation 'Power'."""
1255
+ return cls(operation="POWER", Vector=base, Vector_001=exponent)
1256
+
1257
+ @classmethod
1258
+ def sign(
1259
+ cls,
1260
+ vector: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1261
+ ) -> "VectorMath":
1262
+ """Create Vector Math with operation 'Sign'."""
1263
+ return cls(operation="SIGN", Vector=vector)
1264
+
1265
+ @classmethod
1266
+ def minimum(
1267
+ cls,
1268
+ a: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1269
+ b: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1270
+ ) -> "VectorMath":
1271
+ """Create Vector Math with operation 'Minimum'."""
1272
+ return cls(operation="MINIMUM", Vector=a, Vector_001=b)
1273
+
1274
+ @classmethod
1275
+ def maximum(
1276
+ cls,
1277
+ a: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1278
+ b: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1279
+ ) -> "VectorMath":
1280
+ """Create Vector Math with operation 'Maximum'."""
1281
+ return cls(operation="MAXIMUM", Vector=a, Vector_001=b)
1282
+
1283
+ @classmethod
1284
+ def floor(
1285
+ cls,
1286
+ vector: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1287
+ ) -> "VectorMath":
1288
+ """Create Vector Math with operation 'Floor'."""
1289
+ return cls(operation="FLOOR", Vector=vector)
1290
+
1291
+ @classmethod
1292
+ def ceil(
1293
+ cls,
1294
+ vector: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1295
+ ) -> "VectorMath":
1296
+ """Create Vector Math with operation 'Ceil'."""
1297
+ return cls(operation="CEIL", Vector=vector)
1298
+
1299
+ @classmethod
1300
+ def fraction(
1301
+ cls,
1302
+ vector: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1303
+ ) -> "VectorMath":
1304
+ """Create Vector Math with operation 'Fraction'."""
1305
+ return cls(operation="FRACTION", Vector=vector)
1306
+
1307
+ @classmethod
1308
+ def modulo(
1309
+ cls,
1310
+ vector: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1311
+ ) -> "VectorMath":
1312
+ """Create Vector Math with operation 'Modulo'."""
1313
+ return cls(operation="MODULO", Vector=vector)
1314
+
1315
+ @classmethod
1316
+ def wrap(
1317
+ cls,
1318
+ vector: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1319
+ min: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1320
+ max: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1321
+ ) -> "VectorMath":
1322
+ """Create Vector Math with operation 'Wrap'."""
1323
+ return cls(operation="WRAP", Vector=vector, Vector_001=min, Vector_002=max)
1324
+
1325
+ @classmethod
1326
+ def snap(
1327
+ cls,
1328
+ vector: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1329
+ increment: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1330
+ ) -> "VectorMath":
1331
+ """Create Vector Math with operation 'Snap'."""
1332
+ return cls(operation="SNAP", Vector=vector, Vector_001=increment)
1333
+
1334
+ @classmethod
1335
+ def sine(
1336
+ cls,
1337
+ vector: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1338
+ ) -> "VectorMath":
1339
+ """Create Vector Math with operation 'Sine'."""
1340
+ return cls(operation="SINE", Vector=vector)
1341
+
1342
+ @classmethod
1343
+ def cosine(
1344
+ cls,
1345
+ vector: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1346
+ ) -> "VectorMath":
1347
+ """Create Vector Math with operation 'Cosine'."""
1348
+ return cls(operation="COSINE", Vector=vector)
1349
+
1350
+ @classmethod
1351
+ def tangent(
1352
+ cls,
1353
+ vector: TYPE_INPUT_VECTOR = [0.0, 0.0, 0.0],
1354
+ ) -> "VectorMath":
1355
+ """Create Vector Math with operation 'Tangent'."""
1356
+ return cls(operation="TANGENT", Vector=vector)