nodebpy 0.7.1__tar.gz → 0.7.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. {nodebpy-0.7.1 → nodebpy-0.7.2}/PKG-INFO +1 -1
  2. {nodebpy-0.7.1 → nodebpy-0.7.2}/pyproject.toml +1 -1
  3. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/builder.py +58 -28
  4. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/types.py +2 -1
  5. {nodebpy-0.7.1 → nodebpy-0.7.2}/README.md +0 -0
  6. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/__init__.py +0 -0
  7. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/arrange.py +0 -0
  8. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/lib/nodearrange/__init__.py +0 -0
  9. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/lib/nodearrange/arrange/graph.py +0 -0
  10. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/lib/nodearrange/arrange/ordering.py +0 -0
  11. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/lib/nodearrange/arrange/ranking.py +0 -0
  12. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/lib/nodearrange/arrange/realize.py +0 -0
  13. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/lib/nodearrange/arrange/stacking.py +0 -0
  14. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/lib/nodearrange/arrange/structs.py +0 -0
  15. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/lib/nodearrange/arrange/sugiyama.py +0 -0
  16. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/lib/nodearrange/arrange/x_coords.py +0 -0
  17. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/lib/nodearrange/arrange/y_coords.py +0 -0
  18. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/lib/nodearrange/config.py +0 -0
  19. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/lib/nodearrange/utils.py +0 -0
  20. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/__init__.py +0 -0
  21. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/compositor/__init__.py +0 -0
  22. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/compositor/color.py +0 -0
  23. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/compositor/converter.py +0 -0
  24. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/compositor/distort.py +0 -0
  25. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/compositor/filter.py +0 -0
  26. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/compositor/group.py +0 -0
  27. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/compositor/input.py +0 -0
  28. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/compositor/interface.py +0 -0
  29. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/compositor/manual.py +0 -0
  30. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/compositor/matte.py +0 -0
  31. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/compositor/output.py +0 -0
  32. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/compositor/texture.py +0 -0
  33. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/compositor/vector.py +0 -0
  34. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/geometry/__init__.py +0 -0
  35. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/geometry/attribute.py +0 -0
  36. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/geometry/color.py +0 -0
  37. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/geometry/converter.py +0 -0
  38. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/geometry/geometry.py +0 -0
  39. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/geometry/grid.py +0 -0
  40. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/geometry/group.py +0 -0
  41. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/geometry/input.py +0 -0
  42. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/geometry/interface.py +0 -0
  43. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/geometry/manual.py +0 -0
  44. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/geometry/output.py +0 -0
  45. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/geometry/texture.py +0 -0
  46. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/geometry/vector.py +0 -0
  47. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/geometry/zone.py +0 -0
  48. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/shader/__init__.py +0 -0
  49. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/shader/color.py +0 -0
  50. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/shader/converter.py +0 -0
  51. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/shader/grid.py +0 -0
  52. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/shader/group.py +0 -0
  53. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/shader/input.py +0 -0
  54. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/shader/interface.py +0 -0
  55. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/shader/manual.py +0 -0
  56. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/shader/output.py +0 -0
  57. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/shader/script.py +0 -0
  58. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/shader/shader.py +0 -0
  59. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/shader/texture.py +0 -0
  60. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/shader/vector.py +0 -0
  61. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/nodes/shader/zone.py +0 -0
  62. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/screenshot.py +0 -0
  63. {nodebpy-0.7.1 → nodebpy-0.7.2}/src/nodebpy/sockets.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: nodebpy
3
- Version: 0.7.1
3
+ Version: 0.7.2
4
4
  Summary: Build nodes trees in Blender more elegantly with code
5
5
  Author: Brady Johnston
6
6
  Author-email: Brady Johnston <brady.johnston@me.com>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nodebpy"
3
- version = "0.7.1"
3
+ version = "0.7.2"
4
4
  description = "Build nodes trees in Blender more elegantly with code"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -4,7 +4,7 @@ from typing import TYPE_CHECKING, Any, ClassVar, Iterable, Literal
4
4
 
5
5
  if TYPE_CHECKING:
6
6
  from .nodes.geometry import IntegerMath, Math, VectorMath
7
- from .nodes.geometry.converter import BooleanMath, MultiplyMatrices
7
+ from .nodes.geometry.converter import BooleanMath, MultiplyMatrices, TransformPoint
8
8
  from .nodes.geometry.manual import Compare
9
9
 
10
10
  import bpy
@@ -139,7 +139,7 @@ class SocketContext:
139
139
  class DirectionalContext(SocketContext):
140
140
  """Base class for directional socket contexts"""
141
141
 
142
- _direction: Literal["INPUT", "OUTPUT"] = "INPUT"
142
+ _direction = "INPUT"
143
143
  _active_context = None
144
144
 
145
145
 
@@ -249,14 +249,14 @@ class TreeBuilder:
249
249
 
250
250
  @fake_user.setter
251
251
  def fake_user(self, value: bool) -> None:
252
- self.tree.use_extra_user = value
252
+ self.tree.use_fake_user = value
253
253
 
254
254
  def activate_tree(self) -> None:
255
255
  """Make this tree the active tree for all new node creation."""
256
256
  TreeBuilder._tree_contexts.append(self)
257
257
 
258
258
  def deactivate_tree(self) -> None:
259
- """Whatever tree ws previously active is set to be the active one (or None if no previously active tree)."""
259
+ """Whatever tree was previously active is set to be the active one (or None if no previously active tree)."""
260
260
  TreeBuilder._tree_contexts.pop()
261
261
 
262
262
  def __enter__(self):
@@ -271,9 +271,9 @@ class TreeBuilder:
271
271
 
272
272
  def _apply_input_defaults(self) -> None:
273
273
  for key, value in self._menu_defaults.items():
274
- for item in self.tree.interface.items_tree:
275
- if item.identifier == key:
276
- item.default_value = value
274
+ for item in self.tree.interface.items_tree: # type: ignore
275
+ if item.identifier == key: # type: ignore
276
+ item.default_value = value # type: ignore
277
277
  break
278
278
 
279
279
  def __len__(self) -> int:
@@ -336,7 +336,7 @@ class TreeBuilder:
336
336
  for socket in [socket1, socket2]:
337
337
  # we want to be loud about it if we end up linking an inactive socket to a node that is not a switch
338
338
  if socket.is_inactive and (
339
- socket.node.bl_idname
339
+ socket.node.bl_idname # type: ignore
340
340
  not in ( # type: ignore
341
341
  "GeometryNodeIndexSwitch",
342
342
  "GeometryNodeMenuSwitch",
@@ -360,13 +360,14 @@ class TreeBuilder:
360
360
  class NodeBuilder:
361
361
  """Base class for all geometry node wrappers."""
362
362
 
363
- node: Any
363
+ node: bpy.types.Node
364
364
  _bl_idname: str
365
365
  _tree: "TreeBuilder"
366
366
  _link_target: str | None = None
367
367
  _from_socket: NodeSocket | None = None
368
368
  _default_input_id: str | None = None
369
369
  _default_output_id: str | None = None
370
+ _placeholder_inputs: list[str]
370
371
  __array_ufunc__ = None
371
372
 
372
373
  def __init__(self):
@@ -384,6 +385,7 @@ class NodeBuilder:
384
385
 
385
386
  self._tree = tree
386
387
  self._link_target = None
388
+ self._placeholder_inputs = []
387
389
  if self.__class__.name is not None:
388
390
  self.node = self._tree.add(self.__class__._bl_idname)
389
391
  else:
@@ -506,7 +508,7 @@ class NodeBuilder:
506
508
  return sorted(possible_combos, key=lambda x: x[0])[0][1]
507
509
 
508
510
  raise SocketError(
509
- f"Cannot link any output from {source.node.name} to any input of {target.node.name}. "
511
+ f"Cannot link any output from {source.node.name} to any input of {target.node.name}. " # type: ignore
510
512
  f"Available output types: {[f'{o.name}:{o.type}' for o in outputs]}, "
511
513
  f"Available input types: {[f'{i.name}:{i.type}' for i in inputs]}"
512
514
  )
@@ -530,7 +532,10 @@ class NodeBuilder:
530
532
  if identifier in input_names:
531
533
  return input_names.index(identifier)
532
534
 
533
- raise RuntimeError()
535
+ raise RuntimeError(
536
+ f"Input '{identifier}' not found on {self.node.bl_idname}. "
537
+ f"Available inputs: {input_names}"
538
+ )
534
539
 
535
540
  def _output_idx(self, identifier: str) -> int:
536
541
  output_ids = [output.identifier for output in self.node.outputs]
@@ -593,10 +598,11 @@ class NodeBuilder:
593
598
  value = node
594
599
 
595
600
  if value is ...:
596
- # Ellipsis indicates this input should receive links from >> operator
597
- # which can potentially target multiple inputs on the new node
601
+ # Ellipsis marks this input as a placeholder for the >> operator
602
+ self._placeholder_inputs.append(name)
598
603
  if self._from_socket is not None:
599
604
  self._link_from(self._from_socket, name)
605
+ continue
600
606
 
601
607
  elif isinstance(value, SocketLinker):
602
608
  self._link_from(value, name)
@@ -628,6 +634,14 @@ class NodeBuilder:
628
634
  source = self._default_output_socket
629
635
  target = other.socket
630
636
  other._from_socket = source
637
+ elif getattr(other, "_placeholder_inputs", None):
638
+ # Link to the first placeholder input marked with ...
639
+ name = other._placeholder_inputs.pop(0)
640
+ try:
641
+ target = other.node.inputs[name]
642
+ except KeyError:
643
+ target = other.node.inputs[other._input_idx(name)]
644
+ source = self._best_output_socket(target.type)
631
645
  else:
632
646
  try:
633
647
  source, target = self._find_best_socket_pair(self, other)
@@ -701,7 +715,7 @@ class NodeBuilder:
701
715
 
702
716
  # only the Geometry Node Tree supports integer math currently, potential
703
717
  # to support other trees when Blender supports it
704
- is_geometry_tree = self._tree.tree.bl_idname in ["GeometryNodeTree"]
718
+ is_geometry_tree = self._tree.tree.bl_idname == "GeometryNodeTree"
705
719
  if (
706
720
  is_geometry_tree
707
721
  and isinstance(other, int)
@@ -846,16 +860,16 @@ class NodeBuilder:
846
860
  result = Math.subtract(1.0, result._default_output_socket)
847
861
  return result
848
862
 
849
- def __lt__(self, other: Any) -> "Compare":
863
+ def __lt__(self, other: Any) -> "Compare | Math":
850
864
  return self._apply_compare_operation(other, "less_than")
851
865
 
852
- def __gt__(self, other: Any) -> "Compare":
866
+ def __gt__(self, other: Any) -> "Compare | Math":
853
867
  return self._apply_compare_operation(other, "greater_than")
854
868
 
855
- def __le__(self, other: Any) -> "Compare":
869
+ def __le__(self, other: Any) -> "Compare | Math":
856
870
  return self._apply_compare_operation(other, "less_equal")
857
871
 
858
- def __ge__(self, other: Any) -> "Compare":
872
+ def __ge__(self, other: Any) -> "Compare | Math":
859
873
  return self._apply_compare_operation(other, "greater_equal")
860
874
 
861
875
  def _apply_boolean_operation(self, other: Any, operation: str) -> "BooleanMath":
@@ -902,15 +916,31 @@ class NodeBuilder:
902
916
  else:
903
917
  return value
904
918
 
905
- def __matmul__(self, other: Any) -> "MultiplyMatrices":
906
- from .nodes.geometry.converter import MultiplyMatrices
919
+ def __matmul__(self, other: Any) -> "MultiplyMatrices | TransformPoint":
920
+ from .nodes.geometry.converter import MultiplyMatrices, TransformPoint
907
921
 
908
- return MultiplyMatrices(self, self._cast_to_matrix(other))
922
+ other = self._cast_to_matrix(other)
923
+ socket = self._default_output_socket
924
+ other_type = getattr(other, "type", None)
925
+
926
+ # matrix @ vector → TransformPoint (standard M @ v)
927
+ if socket.type == "MATRIX" and other_type == "VECTOR":
928
+ return TransformPoint(other, socket)
929
+
930
+ return MultiplyMatrices(self, other)
931
+
932
+ def __rmatmul__(self, other: Any) -> "MultiplyMatrices | TransformPoint":
933
+ from .nodes.geometry.converter import MultiplyMatrices, TransformPoint
934
+
935
+ other = self._cast_to_matrix(other)
936
+ socket = self._default_output_socket
937
+ other_type = getattr(other, "type", None)
909
938
 
910
- def __rmatmul__(self, other: Any) -> "MultiplyMatrices":
911
- from .nodes.geometry.converter import MultiplyMatrices
939
+ # matrix @ vector: other is matrix (non-NodeBuilder cast), self is vector
940
+ if socket.type == "VECTOR" and other_type == "MATRIX":
941
+ return TransformPoint(socket, other)
912
942
 
913
- return MultiplyMatrices(self._cast_to_matrix(other), self)
943
+ return MultiplyMatrices(other, self)
914
944
 
915
945
 
916
946
  class DynamicInputsMixin:
@@ -938,7 +968,7 @@ class DynamicInputsMixin:
938
968
  self, source: NodeBuilder | NodeSocket, target: NodeBuilder | NodeSocket
939
969
  ) -> tuple[NodeSocket, NodeSocket]:
940
970
  try:
941
- return super()._find_best_socket_pair(source, target)
971
+ return super()._find_best_socket_pair(source, target) # type: ignore
942
972
  except SocketError:
943
973
  if target == self:
944
974
  target_name, source_socket = list(target._add_inputs(source).items())[0]
@@ -1030,7 +1060,7 @@ class SocketBase(SocketLinker):
1030
1060
  def __init__(self, name: str, description: str = ""):
1031
1061
  self.description = description
1032
1062
 
1033
- self._socket_context: SocketContext = SocketContext._active_context
1063
+ self._socket_context = SocketContext._active_context
1034
1064
  self.interface_socket = self._socket_context._create_socket(self, name)
1035
1065
  self._tree = self._socket_context.builder
1036
1066
  if self._socket_context._direction == "INPUT":
@@ -1060,7 +1090,7 @@ class SocketBase(SocketLinker):
1060
1090
  raise AttributeError(
1061
1091
  f"'{self.__class__.__name__}' object has no attribute 'default_value'"
1062
1092
  )
1063
- return self.interface_socket.default_value
1093
+ return getattr(self.interface_socket, "default_value")
1064
1094
 
1065
1095
  @default_value.setter
1066
1096
  def default_value(self, value):
@@ -1068,7 +1098,7 @@ class SocketBase(SocketLinker):
1068
1098
  raise AttributeError(
1069
1099
  f"'{self.__class__.__name__}' object has no attribute 'default_value'"
1070
1100
  )
1071
- self.interface_socket.default_value = value
1101
+ setattr(self.interface_socket, "default_value", value)
1072
1102
 
1073
1103
 
1074
1104
  class SocketFloat(SocketBase):
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import typing
4
+ from types import EllipsisType
4
5
  from typing import Literal
5
6
 
6
7
  from bpy.types import (
@@ -35,7 +36,7 @@ def _is_default_value(value: TYPE_INPUT_ALL):
35
36
 
36
37
 
37
38
  # Type aliases for node inputs using typing.Union for runtime compatibility
38
- LINKABLE = typing.Union["NodeBuilder", "SocketLinker", NodeSocket, None]
39
+ LINKABLE = typing.Union["NodeBuilder", "SocketLinker", NodeSocket, None, EllipsisType]
39
40
  TYPE_INPUT_ROTATION = typing.Union[
40
41
  tuple[float, float, float], float, int, Euler, LINKABLE
41
42
  ]
File without changes
File without changes
File without changes
File without changes