nodebpy 520.40.0__tar.gz → 520.41.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. {nodebpy-520.40.0 → nodebpy-520.41.0}/PKG-INFO +1 -1
  2. {nodebpy-520.40.0 → nodebpy-520.41.0}/pyproject.toml +1 -1
  3. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/builder/items.py +4 -0
  4. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/builder/mixins.py +5 -0
  5. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/export/codegen.py +41 -20
  6. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/compositor/distort.py +49 -4
  7. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/compositor/filter.py +11 -0
  8. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/compositor/input.py +49 -2
  9. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/compositor/matte.py +11 -0
  10. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/geometry/input.py +22 -2
  11. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/geometry/zone.py +20 -13
  12. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/shader/input.py +15 -1
  13. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/shader/output.py +267 -0
  14. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/shader/script.py +24 -2
  15. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/shader/texture.py +39 -2
  16. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/skills/nodebpy/references/zones-and-items.md +4 -3
  17. {nodebpy-520.40.0 → nodebpy-520.41.0}/LICENSE +0 -0
  18. {nodebpy-520.40.0 → nodebpy-520.41.0}/README.md +0 -0
  19. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/__init__.py +0 -0
  20. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/__main__.py +0 -0
  21. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/_lookup.py +0 -0
  22. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/_skill.py +0 -0
  23. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/assets/__init__.py +0 -0
  24. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/assets/__main__.py +0 -0
  25. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/assets/_codegen.py +0 -0
  26. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/assets/_library.py +0 -0
  27. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/assets/_pipeline.py +0 -0
  28. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/assets/_textconv.py +0 -0
  29. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/builder/__init__.py +0 -0
  30. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/builder/_combine.py +0 -0
  31. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/builder/_infer.py +0 -0
  32. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/builder/_registry.py +0 -0
  33. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/builder/_socket_order.py +0 -0
  34. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/builder/_utils.py +0 -0
  35. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/builder/accessor.py +0 -0
  36. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/builder/asset.py +0 -0
  37. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/builder/node.py +0 -0
  38. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/builder/socket.py +0 -0
  39. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/builder/tree.py +0 -0
  40. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/export/__init__.py +0 -0
  41. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/export/_floats.py +0 -0
  42. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/export/_groups.py +0 -0
  43. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/export/diagram.py +0 -0
  44. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/export/parity.py +0 -0
  45. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/export/plot.py +0 -0
  46. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/export/web_render.py +0 -0
  47. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/layout/DESIGN.md +0 -0
  48. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/layout/__init__.py +0 -0
  49. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/layout/api.py +0 -0
  50. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/layout/apply.py +0 -0
  51. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/layout/balancing.py +0 -0
  52. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/layout/build.py +0 -0
  53. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/layout/common.py +0 -0
  54. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/layout/config.py +0 -0
  55. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/layout/digraph.py +0 -0
  56. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/layout/dna.py +0 -0
  57. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/layout/edits.py +0 -0
  58. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/layout/extract.py +0 -0
  59. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/layout/feeders.py +0 -0
  60. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/layout/keep.py +0 -0
  61. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/layout/long_links.py +0 -0
  62. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/layout/model.py +0 -0
  63. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/layout/node_size.py +0 -0
  64. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/layout/ordering.py +0 -0
  65. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/layout/packing.py +0 -0
  66. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/layout/pipeline.py +0 -0
  67. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/layout/placement.py +0 -0
  68. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/layout/priority.py +0 -0
  69. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/layout/ranking.py +0 -0
  70. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/layout/realize.py +0 -0
  71. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/layout/reroutes.py +0 -0
  72. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/layout/routing.py +0 -0
  73. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/layout/snapping.py +0 -0
  74. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/layout/spacing.py +0 -0
  75. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/layout/stacking.py +0 -0
  76. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/layout/sugiyama.py +0 -0
  77. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/layout/zones.py +0 -0
  78. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/live.py +0 -0
  79. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/__init__.py +0 -0
  80. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/_mixins.py +0 -0
  81. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/compositor/__init__.py +0 -0
  82. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/compositor/assets.py +0 -0
  83. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/compositor/color.py +0 -0
  84. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/compositor/converter.py +0 -0
  85. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/compositor/group.py +0 -0
  86. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/compositor/interface.py +0 -0
  87. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/compositor/manual.py +0 -0
  88. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/compositor/output.py +0 -0
  89. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/compositor/vector.py +0 -0
  90. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/geometry/__init__.py +0 -0
  91. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/geometry/assets.py +0 -0
  92. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/geometry/attribute.py +0 -0
  93. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/geometry/color.py +0 -0
  94. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/geometry/converter.py +0 -0
  95. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/geometry/geometry.py +0 -0
  96. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/geometry/grid.py +0 -0
  97. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/geometry/group.py +0 -0
  98. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/geometry/groups.py +0 -0
  99. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/geometry/interface.py +0 -0
  100. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/geometry/manual.py +0 -0
  101. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/geometry/output.py +0 -0
  102. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/geometry/texture.py +0 -0
  103. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/geometry/utilities.py +0 -0
  104. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/geometry/vector.py +0 -0
  105. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/shader/__init__.py +0 -0
  106. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/shader/assets.py +0 -0
  107. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/shader/color.py +0 -0
  108. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/shader/converter.py +0 -0
  109. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/shader/grid.py +0 -0
  110. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/shader/group.py +0 -0
  111. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/shader/manual.py +0 -0
  112. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/shader/shader.py +0 -0
  113. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/nodes/shader/vector.py +0 -0
  114. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/py.typed +0 -0
  115. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/skills/nodebpy/SKILL.md +0 -0
  116. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/skills/nodebpy/references/existing-trees.md +0 -0
  117. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/skills/nodebpy/references/shader-compositor.md +0 -0
  118. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/skills/nodebpy/references/verifying.md +0 -0
  119. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/skills/nodebpy/references/writing-trees.md +0 -0
  120. {nodebpy-520.40.0 → nodebpy-520.41.0}/src/nodebpy/types.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nodebpy
3
- Version: 520.40.0
3
+ Version: 520.41.0
4
4
  Summary: Build nodes trees in Blender more elegantly with code
5
5
  Keywords: blender,bpy,nodes,geometry-nodes,shader,compositor,procedural
6
6
  Author: Brady Johnston
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nodebpy"
3
- version = "520.40.0"
3
+ version = "520.41.0"
4
4
  description = "Build nodes trees in Blender more elegantly with code"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -246,6 +246,10 @@ class Item(Generic[_InT, _OutT]):
246
246
  def name(self) -> str:
247
247
  return self._item.name
248
248
 
249
+ @name.setter
250
+ def name(self, value: str) -> None:
251
+ self._item.name = value
252
+
249
253
  @property
250
254
  def socket_type(self) -> str:
251
255
  # some collections (capture_items, grid_items) call this data_type
@@ -346,6 +346,11 @@ class LinkingMixin:
346
346
  (i for i in inputs if i.identifier.startswith("__extend__")), None
347
347
  )
348
348
  if extend is not None and outputs:
349
+ # a node with several (For Each's main and generation items)
350
+ # picks the one the source's type belongs to
351
+ pick = getattr(target, "_extend_input", None)
352
+ if pick is not None:
353
+ extend = pick(outputs[0])
349
354
  return extend, outputs[0]
350
355
  return None
351
356
 
@@ -35,7 +35,15 @@ from types import ModuleType
35
35
  from typing import TYPE_CHECKING, Any, Literal, NamedTuple
36
36
 
37
37
  import numpy as np
38
- from bpy.types import ID, FunctionNodeCompare, Node, NodeSocket, NodeTree
38
+ from bpy.types import (
39
+ ID,
40
+ FunctionNodeCompare,
41
+ Material,
42
+ Node,
43
+ NodeSocket,
44
+ NodeTree,
45
+ World,
46
+ )
39
47
 
40
48
  from ..types import Default
41
49
  from ._floats import (
@@ -4141,7 +4149,7 @@ def _format_with_ruff(code: str) -> str:
4141
4149
 
4142
4150
 
4143
4151
  def to_python(
4144
- tree: NodeTree | TreeBuilder[Any],
4152
+ tree: NodeTree | TreeBuilder[Any] | Material | World,
4145
4153
  min_chain_length: int = 3,
4146
4154
  strict: bool = True,
4147
4155
  max_inline_width: int | None = 88,
@@ -4163,8 +4171,10 @@ def to_python(
4163
4171
 
4164
4172
  Parameters
4165
4173
  ----------
4166
- tree: TreeBuilder[Any] | bpy.types.NodeTree
4167
- The node tree to export.
4174
+ tree: TreeBuilder[Any] | bpy.types.NodeTree | bpy.types.Material | bpy.types.World
4175
+ The node tree to export. With ``top_level="with"``, a material or
4176
+ world (or its node tree) is rebuilt with ``s.material(...)`` /
4177
+ ``s.world(...)``.
4168
4178
  min_chain_length: int
4169
4179
  Minimum number of items (including interface endpoints) for a linear
4170
4180
  pipeline to be expressed with ``>>`` syntax; shorter runs are emitted
@@ -4267,7 +4277,18 @@ def to_python(
4267
4277
  str
4268
4278
  Python source code as a string.
4269
4279
  """
4280
+ owner: Material | World | None = None
4281
+ if isinstance(tree, (Material, World)):
4282
+ owner = tree
4283
+ tree = tree.node_tree # ty: ignore[invalid-assignment]
4270
4284
  node_tree: NodeTree = tree.tree if hasattr(tree, "tree") else tree # ty: ignore[invalid-assignment]
4285
+ if not isinstance(node_tree, NodeTree):
4286
+ raise TypeError(
4287
+ "to_python() takes a node tree, TreeBuilder, material or world, "
4288
+ f"not {type(tree).__name__}"
4289
+ )
4290
+ if owner is None and node_tree.is_embedded_data:
4291
+ owner = _embedded_tree_owner(node_tree)
4271
4292
 
4272
4293
  class_names = dict(group_class_names or {})
4273
4294
  externals = set(external_groups or ())
@@ -4328,6 +4349,8 @@ def to_python(
4328
4349
  else:
4329
4350
  emission = _emit_tree(node_tree, collector)
4330
4351
  used_aliases = emission.used_aliases | collector.used_aliases
4352
+ if owner is not None:
4353
+ used_aliases.add("s")
4331
4354
 
4332
4355
  # 5. Assemble the module: imports, group classes, then the tree block (only
4333
4356
  # in "with" mode), each top-level block separated by two blank lines.
@@ -4371,6 +4394,9 @@ def to_python(
4371
4394
 
4372
4395
  constructor = _TREE_CONSTRUCTORS.get(node_tree.bl_idname, "TreeBuilder")
4373
4396
  ctor_args = [_fmt(node_tree.name)]
4397
+ if owner is not None:
4398
+ constructor = "s.material" if isinstance(owner, Material) else "s.world"
4399
+ ctor_args = [_fmt(owner.name)]
4374
4400
  # Auto-layout dissolves reroutes and overwrites locations, so disable it
4375
4401
  # for either option.
4376
4402
  if snapshot_positions or keep_reroutes:
@@ -5032,6 +5058,16 @@ def _render_group_class(
5032
5058
  return "\n".join(header + body)
5033
5059
 
5034
5060
 
5061
+ def _embedded_tree_owner(node_tree: NodeTree) -> Material | World | None:
5062
+ """The material or world whose node tree ``node_tree`` is, if any."""
5063
+ import bpy
5064
+
5065
+ for owner in (*bpy.data.materials, *bpy.data.worlds):
5066
+ if owner.node_tree == node_tree:
5067
+ return owner
5068
+ return None
5069
+
5070
+
5035
5071
  def _emit_tree(node_tree, collector: _GroupCollector) -> _TreeEmission:
5036
5072
  """Generate the interface/body/output lines for one tree. Nested group
5037
5073
  nodes register their classes on ``collector`` as a side effect."""
@@ -6557,17 +6593,6 @@ def _emit_foreach_input(node, ctx: EmitContext) -> _Val:
6557
6593
  f" {zone_ref.name} = {Call(ctor, kwargs=kwargs).render()}"
6558
6594
  )
6559
6595
 
6560
- generation_items = list(out_node.generation_items)
6561
- if (
6562
- not generation_items
6563
- or generation_items[0].socket_type != "GEOMETRY"
6564
- or generation_items[0].name != "Geometry"
6565
- ):
6566
- raise CodegenError(
6567
- f"foreach zone '{node.name}': the first generation item is not "
6568
- "the default Geometry item, which the zone wrapper cannot rebuild"
6569
- )
6570
-
6571
6596
  plans: list[_ZoneItemPlan] = []
6572
6597
  in_inputs = _prefixed_sockets(node, "Input_")
6573
6598
  in_outputs = _prefixed_sockets(node, "Input_", output=True)
@@ -6603,9 +6628,7 @@ def _emit_foreach_input(node, ctx: EmitContext) -> _Val:
6603
6628
  )
6604
6629
  gen_inputs = _prefixed_sockets(out_node, "Generation_")
6605
6630
  gen_outputs = _prefixed_sockets(out_node, "Generation_", output=True)
6606
- for i, item in enumerate(generation_items):
6607
- if i == 0:
6608
- continue # the wrapper creates the default Geometry item itself
6631
+ for i, item in enumerate(out_node.generation_items):
6609
6632
  extra: dict[str, Expr] = {}
6610
6633
  if item.domain != "POINT":
6611
6634
  extra["domain"] = Lit(item.domain)
@@ -6628,8 +6651,6 @@ def _emit_foreach_input(node, ctx: EmitContext) -> _Val:
6628
6651
  )
6629
6652
  current_map["Index"] = Attr(zone_ref, "index")
6630
6653
  current_map["Element"] = Attr(zone_ref, "input.o.element")
6631
- targets[gen_inputs[0].identifier] = Attr(zone_ref, "generation.input")
6632
- outputs[gen_outputs[0].identifier] = Attr(zone_ref, "generation.output")
6633
6654
  outputs["Geometry"] = Attr(zone_ref, "output")
6634
6655
 
6635
6656
  ctx.zones[out_node.name] = _ZoneState(targets, outputs)
@@ -616,21 +616,43 @@ class MovieDistortion(BaseNode):
616
616
  self,
617
617
  image: InputColor = (0.8, 0.8, 0.8, 1.0),
618
618
  type: InputMenu | Literal["Undistort", "Distort"] = "Undistort",
619
+ *,
620
+ clip: bpy.types.MovieClip | None = None,
619
621
  ):
620
622
  super().__init__()
621
623
  key_args = {"Image": image, "Type": type}
622
624
 
623
625
  self._establish_links(**key_args)
626
+ if clip is not None:
627
+ self.clip = clip
624
628
 
625
629
  @classmethod
626
- def undistort(cls, image: InputColor = (0.8, 0.8, 0.8, 1.0)) -> "MovieDistortion":
630
+ def undistort(
631
+ cls,
632
+ image: InputColor = (0.8, 0.8, 0.8, 1.0),
633
+ *,
634
+ clip: bpy.types.MovieClip | None = None,
635
+ ) -> "MovieDistortion":
627
636
  """Create Movie Distortion node with type 'Undistort'."""
628
- return cls(image=image, type="Undistort")
637
+ return cls(image=image, clip=clip, type="Undistort")
629
638
 
630
639
  @classmethod
631
- def distort(cls, image: InputColor = (0.8, 0.8, 0.8, 1.0)) -> "MovieDistortion":
640
+ def distort(
641
+ cls,
642
+ image: InputColor = (0.8, 0.8, 0.8, 1.0),
643
+ *,
644
+ clip: bpy.types.MovieClip | None = None,
645
+ ) -> "MovieDistortion":
632
646
  """Create Movie Distortion node with type 'Distort'."""
633
- return cls(image=image, type="Distort")
647
+ return cls(image=image, clip=clip, type="Distort")
648
+
649
+ @property
650
+ def clip(self) -> bpy.types.MovieClip | None:
651
+ return self.node.clip
652
+
653
+ @clip.setter
654
+ def clip(self, value: bpy.types.MovieClip | None) -> None:
655
+ self.node.clip = value
634
656
 
635
657
 
636
658
  class PlaneTrackDeform(BaseNode):
@@ -703,6 +725,7 @@ class PlaneTrackDeform(BaseNode):
703
725
  samples: InputInteger = 16,
704
726
  shutter: InputFloat = 0.5,
705
727
  *,
728
+ clip: bpy.types.MovieClip | None = None,
706
729
  tracking_object: str = "",
707
730
  plane_track_name: str = "",
708
731
  ):
@@ -716,6 +739,16 @@ class PlaneTrackDeform(BaseNode):
716
739
  self.tracking_object = tracking_object
717
740
  self.plane_track_name = plane_track_name
718
741
  self._establish_links(**key_args)
742
+ if clip is not None:
743
+ self.clip = clip
744
+
745
+ @property
746
+ def clip(self) -> bpy.types.MovieClip | None:
747
+ return self.node.clip
748
+
749
+ @clip.setter
750
+ def clip(self, value: bpy.types.MovieClip | None) -> None:
751
+ self.node.clip = value
719
752
 
720
753
  @property
721
754
  def tracking_object(self) -> str:
@@ -1084,6 +1117,8 @@ class Stabilize2D(BaseNode):
1084
1117
  | Literal["Nearest", "Bilinear", "Bicubic", "Anisotropic"] = "Bilinear",
1085
1118
  extension_x: InputMenu | Literal["Clip", "Extend", "Repeat"] = "Clip",
1086
1119
  extension_y: InputMenu | Literal["Clip", "Extend", "Repeat"] = "Clip",
1120
+ *,
1121
+ clip: bpy.types.MovieClip | None = None,
1087
1122
  ):
1088
1123
  super().__init__()
1089
1124
  key_args = {
@@ -1096,6 +1131,16 @@ class Stabilize2D(BaseNode):
1096
1131
  }
1097
1132
 
1098
1133
  self._establish_links(**key_args)
1134
+ if clip is not None:
1135
+ self.clip = clip
1136
+
1137
+ @property
1138
+ def clip(self) -> bpy.types.MovieClip | None:
1139
+ return self.node.clip
1140
+
1141
+ @clip.setter
1142
+ def clip(self, value: bpy.types.MovieClip | None) -> None:
1143
+ self.node.clip = value
1099
1144
 
1100
1145
 
1101
1146
  class Transform(BaseNode):
@@ -612,6 +612,7 @@ class Defocus(BaseNode):
612
612
  image: InputColor = (1.0, 1.0, 1.0, 1.0),
613
613
  z: InputFloat = 1.0,
614
614
  *,
615
+ scene: bpy.types.Scene | None = None,
615
616
  bokeh: Literal[
616
617
  "OCTAGON", "HEPTAGON", "HEXAGON", "PENTAGON", "SQUARE", "TRIANGLE", "CIRCLE"
617
618
  ] = "CIRCLE",
@@ -630,6 +631,16 @@ class Defocus(BaseNode):
630
631
  self.use_zbuffer = use_zbuffer
631
632
  self.z_scale = z_scale
632
633
  self._establish_links(**key_args)
634
+ if scene is not None:
635
+ self.scene = scene
636
+
637
+ @property
638
+ def scene(self) -> bpy.types.Scene | None:
639
+ return self.node.scene
640
+
641
+ @scene.setter
642
+ def scene(self, value: bpy.types.Scene | None) -> None:
643
+ self.node.scene = value
633
644
 
634
645
  @property
635
646
  def bokeh(
@@ -399,6 +399,8 @@ class Mask(BaseNode):
399
399
  motion_blur: InputBoolean = False,
400
400
  samples: InputInteger = 16,
401
401
  shutter: InputFloat = 0.5,
402
+ *,
403
+ mask: bpy.types.Mask | None = None,
402
404
  ):
403
405
  super().__init__()
404
406
  key_args = {
@@ -412,6 +414,16 @@ class Mask(BaseNode):
412
414
  }
413
415
 
414
416
  self._establish_links(**key_args)
417
+ if mask is not None:
418
+ self.mask = mask
419
+
420
+ @property
421
+ def mask(self) -> bpy.types.Mask | None:
422
+ return self.node.mask
423
+
424
+ @mask.setter
425
+ def mask(self, value: bpy.types.Mask | None) -> None:
426
+ self.node.mask = value
415
427
 
416
428
 
417
429
  class MovieClip(BaseNode):
@@ -461,11 +473,21 @@ class MovieClip(BaseNode):
461
473
  @property
462
474
  def o(self) -> _Outputs: ...
463
475
 
464
- def __init__(self) -> None:
476
+ def __init__(self, clip: bpy.types.MovieClip | None = None):
465
477
  super().__init__()
466
478
  key_args = {}
467
479
 
468
480
  self._establish_links(**key_args)
481
+ if clip is not None:
482
+ self.clip = clip
483
+
484
+ @property
485
+ def clip(self) -> bpy.types.MovieClip | None:
486
+ return self.node.clip
487
+
488
+ @clip.setter
489
+ def clip(self, value: bpy.types.MovieClip | None) -> None:
490
+ self.node.clip = value
469
491
 
470
492
 
471
493
  class Normal(BaseNode):
@@ -533,11 +555,25 @@ class RenderLayers(BaseNode):
533
555
  @property
534
556
  def o(self) -> _Outputs: ...
535
557
 
536
- def __init__(self, layer: str = "ViewLayer"):
558
+ def __init__(
559
+ self,
560
+ scene: bpy.types.Scene | None = None,
561
+ layer: str = "ViewLayer",
562
+ ):
537
563
  super().__init__()
538
564
  key_args = {}
539
565
  self.layer = layer
540
566
  self._establish_links(**key_args)
567
+ if scene is not None:
568
+ self.scene = scene
569
+
570
+ @property
571
+ def scene(self) -> bpy.types.Scene | None:
572
+ return self.node.scene
573
+
574
+ @scene.setter
575
+ def scene(self, value: bpy.types.Scene | None) -> None:
576
+ self.node.scene = value
541
577
 
542
578
  @property
543
579
  def layer(self) -> str:
@@ -855,6 +891,7 @@ class TrackPosition(BaseNode):
855
891
  ] = "Absolute",
856
892
  frame: InputInteger = 0,
857
893
  *,
894
+ clip: bpy.types.MovieClip | None = None,
858
895
  tracking_object: str = "",
859
896
  track_name: str = "",
860
897
  ):
@@ -863,6 +900,16 @@ class TrackPosition(BaseNode):
863
900
  self.tracking_object = tracking_object
864
901
  self.track_name = track_name
865
902
  self._establish_links(**key_args)
903
+ if clip is not None:
904
+ self.clip = clip
905
+
906
+ @property
907
+ def clip(self) -> bpy.types.MovieClip | None:
908
+ return self.node.clip
909
+
910
+ @clip.setter
911
+ def clip(self, value: bpy.types.MovieClip | None) -> None:
912
+ self.node.clip = value
866
913
 
867
914
  @property
868
915
  def tracking_object(self) -> str:
@@ -1105,12 +1105,23 @@ class KeyingScreen(BaseNode):
1105
1105
  self,
1106
1106
  smoothness: InputFloat = 0.0,
1107
1107
  *,
1108
+ clip: bpy.types.MovieClip | None = None,
1108
1109
  tracking_object: str = "",
1109
1110
  ):
1110
1111
  super().__init__()
1111
1112
  key_args = {"Smoothness": smoothness}
1112
1113
  self.tracking_object = tracking_object
1113
1114
  self._establish_links(**key_args)
1115
+ if clip is not None:
1116
+ self.clip = clip
1117
+
1118
+ @property
1119
+ def clip(self) -> bpy.types.MovieClip | None:
1120
+ return self.node.clip
1121
+
1122
+ @clip.setter
1123
+ def clip(self, value: bpy.types.MovieClip | None) -> None:
1124
+ self.node.clip = value
1114
1125
 
1115
1126
  @property
1116
1127
  def tracking_object(self) -> str:
@@ -1562,11 +1562,21 @@ class Font(BaseNode):
1562
1562
  @property
1563
1563
  def o(self) -> _Outputs: ...
1564
1564
 
1565
- def __init__(self) -> None:
1565
+ def __init__(self, font: bpy.types.VectorFont | None = None):
1566
1566
  super().__init__()
1567
1567
  key_args = {}
1568
1568
 
1569
1569
  self._establish_links(**key_args)
1570
+ if font is not None:
1571
+ self.font = font
1572
+
1573
+ @property
1574
+ def font(self) -> bpy.types.VectorFont | None:
1575
+ return self.node.font
1576
+
1577
+ @font.setter
1578
+ def font(self, value: bpy.types.VectorFont | None) -> None:
1579
+ self.node.font = value
1570
1580
 
1571
1581
 
1572
1582
  class HandleTypeSelection(_HandleModeMixin, BaseNode):
@@ -1697,11 +1707,21 @@ class Image(BaseNode):
1697
1707
  @property
1698
1708
  def o(self) -> _Outputs: ...
1699
1709
 
1700
- def __init__(self) -> None:
1710
+ def __init__(self, image: bpy.types.Image | None = None):
1701
1711
  super().__init__()
1702
1712
  key_args = {}
1703
1713
 
1704
1714
  self._establish_links(**key_args)
1715
+ if image is not None:
1716
+ self.image = image
1717
+
1718
+ @property
1719
+ def image(self) -> bpy.types.Image | None:
1720
+ return self.node.image
1721
+
1722
+ @image.setter
1723
+ def image(self, value: bpy.types.Image | None) -> None:
1724
+ self.node.image = value
1705
1725
 
1706
1726
 
1707
1727
  class ImageInfo(BaseNode):
@@ -637,6 +637,9 @@ class ForEachGeometryElementZone(_ZonePair):
637
637
  self.input = ForEachGeometryElementInput()
638
638
  self.output = ForEachGeometryElementOutput()
639
639
  self._pair()
640
+ # Like the other zones, start without Blender's default Geometry item;
641
+ # declare generated geometry with ``generated_items.geometry()``.
642
+ self.output.node.generation_items.clear()
640
643
  self.output.domain = domain
641
644
  self.input._establish_links(Geometry=geometry, Selection=selection)
642
645
 
@@ -664,12 +667,18 @@ class ForEachGeometryElementZone(_ZonePair):
664
667
  """The current element as geometry, read inside the zone body."""
665
668
  return self.input.o.element
666
669
 
670
+ # -- deprecated entry points, removed in 530 --
671
+
667
672
  @property
668
673
  def generation(self) -> "Item[GeometrySocket]":
669
- """Handle for the default generation item (the generated geometry)."""
670
- return cast("Item[GeometrySocket]", self.generated_items[0])
671
-
672
- # -- deprecated entry points, removed in 530 --
674
+ """The ``Geometry`` generation item, added when the zone has none."""
675
+ _deprecated(
676
+ "zone.generation", 'zone.generated_items.geometry(value, "Geometry")'
677
+ )
678
+ for item in self.generated_items:
679
+ if item.name == "Geometry" and item.socket_type == "GEOMETRY":
680
+ return cast("Item[GeometrySocket]", item)
681
+ return self.generated_items.geometry(name="Geometry")
673
682
 
674
683
  @property
675
684
  def inputs(self) -> _ForEachItems:
@@ -777,21 +786,12 @@ class ForEachGeometryElementOutput(BaseZoneOutput):
777
786
  _bl_idname = "GeometryNodeForeachGeometryElementOutput"
778
787
  node: bpy.types.GeometryNodeForeachGeometryElementOutput
779
788
 
780
- class _Inputs(SocketAccessor):
781
- generation_0: GeometrySocket
782
- """The geometry to generate elements from."""
783
-
784
789
  class _Outputs(SocketAccessor):
785
790
  geometry: GeometrySocket
786
791
  """The output geometry after processing all elements."""
787
- generation_0: GeometrySocket
788
- """The generated geometry output."""
789
792
 
790
793
  if TYPE_CHECKING:
791
794
 
792
- @property
793
- def i(self) -> _Inputs: ...
794
-
795
795
  @property
796
796
  def o(self) -> _Outputs: ...
797
797
 
@@ -806,6 +806,13 @@ class ForEachGeometryElementOutput(BaseZoneOutput):
806
806
  self.domain = domain
807
807
  self._establish_links(**key_args)
808
808
 
809
+ def _extend_input(self, source: bpy.types.NodeSocket) -> bpy.types.NodeSocket:
810
+ # Main items can't hold geometry: geometry linked into the output is
811
+ # added as a generation item.
812
+ kind = "generation" if source.type == "GEOMETRY" else "main"
813
+ assert self.node.inputs is not None
814
+ return next(s for s in self.node.inputs if s.identifier == f"__extend__{kind}")
815
+
809
816
  @property
810
817
  def items_generated(
811
818
  self,
@@ -1086,11 +1086,25 @@ class TextureCoordinate(BaseNode):
1086
1086
  @property
1087
1087
  def o(self) -> _Outputs: ...
1088
1088
 
1089
- def __init__(self, from_instancer: bool = False):
1089
+ def __init__(
1090
+ self,
1091
+ object: bpy.types.Object | None = None,
1092
+ from_instancer: bool = False,
1093
+ ):
1090
1094
  super().__init__()
1091
1095
  key_args = {}
1092
1096
  self.from_instancer = from_instancer
1093
1097
  self._establish_links(**key_args)
1098
+ if object is not None:
1099
+ self.object = object
1100
+
1101
+ @property
1102
+ def object(self) -> bpy.types.Object | None:
1103
+ return self.node.object
1104
+
1105
+ @object.setter
1106
+ def object(self, value: bpy.types.Object | None) -> None:
1107
+ self.node.object = value
1094
1108
 
1095
1109
  @property
1096
1110
  def from_instancer(self) -> bool: