nodebpy 520.24.0__tar.gz → 520.26.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 (90) hide show
  1. {nodebpy-520.24.0 → nodebpy-520.26.0}/PKG-INFO +1 -1
  2. {nodebpy-520.24.0 → nodebpy-520.26.0}/pyproject.toml +1 -1
  3. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/assets/_library.py +106 -23
  4. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/assets/_pipeline.py +12 -0
  5. nodebpy-520.26.0/src/nodebpy/builder/_socket_order.py +1626 -0
  6. nodebpy-520.26.0/src/nodebpy/builder/layout.py +1036 -0
  7. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/builder/tree.py +32 -0
  8. nodebpy-520.26.0/src/nodebpy/export/plot.py +1400 -0
  9. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/lib/nodearrange/VENDORED.md +17 -0
  10. nodebpy-520.26.0/src/nodebpy/lib/nodearrange/arrange/balancing.py +182 -0
  11. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/lib/nodearrange/arrange/ranking.py +66 -0
  12. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/lib/nodearrange/arrange/sugiyama.py +7 -1
  13. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/lib/nodearrange/arrange/x_coords.py +4 -0
  14. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/lib/nodearrange/arrange/y_coords.py +15 -2
  15. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/lib/nodearrange/config.py +17 -1
  16. nodebpy-520.24.0/src/nodebpy/builder/layout.py +0 -602
  17. nodebpy-520.24.0/src/nodebpy/export/plot.py +0 -225
  18. {nodebpy-520.24.0 → nodebpy-520.26.0}/README.md +0 -0
  19. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/__init__.py +0 -0
  20. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/assets/__init__.py +0 -0
  21. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/assets/__main__.py +0 -0
  22. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/assets/_codegen.py +0 -0
  23. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/builder/__init__.py +0 -0
  24. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/builder/_registry.py +0 -0
  25. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/builder/_utils.py +0 -0
  26. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/builder/accessor.py +0 -0
  27. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/builder/asset.py +0 -0
  28. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/builder/items.py +0 -0
  29. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/builder/mixins.py +0 -0
  30. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/builder/node.py +0 -0
  31. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/builder/socket.py +0 -0
  32. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/export/__init__.py +0 -0
  33. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/export/codegen.py +0 -0
  34. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/export/diagram.py +0 -0
  35. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/export/parity.py +0 -0
  36. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/export/web_render.py +0 -0
  37. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/lib/nodearrange/__init__.py +0 -0
  38. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/lib/nodearrange/arrange/__init__.py +0 -0
  39. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/lib/nodearrange/arrange/graph.py +0 -0
  40. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/lib/nodearrange/arrange/ordering.py +0 -0
  41. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/lib/nodearrange/arrange/realize.py +0 -0
  42. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/lib/nodearrange/arrange/stacking.py +0 -0
  43. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/lib/nodearrange/arrange/structs.py +0 -0
  44. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/lib/nodearrange/utils.py +0 -0
  45. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/__init__.py +0 -0
  46. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/_mixins.py +0 -0
  47. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/compositor/__init__.py +0 -0
  48. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/compositor/assets.py +0 -0
  49. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/compositor/color.py +0 -0
  50. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/compositor/converter.py +0 -0
  51. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/compositor/distort.py +0 -0
  52. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/compositor/filter.py +0 -0
  53. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/compositor/group.py +0 -0
  54. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/compositor/input.py +0 -0
  55. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/compositor/interface.py +0 -0
  56. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/compositor/manual.py +0 -0
  57. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/compositor/matte.py +0 -0
  58. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/compositor/output.py +0 -0
  59. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/compositor/vector.py +0 -0
  60. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/geometry/__init__.py +0 -0
  61. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/geometry/assets.py +0 -0
  62. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/geometry/attribute.py +0 -0
  63. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/geometry/color.py +0 -0
  64. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/geometry/converter.py +0 -0
  65. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/geometry/geometry.py +0 -0
  66. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/geometry/grid.py +0 -0
  67. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/geometry/group.py +0 -0
  68. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/geometry/groups.py +0 -0
  69. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/geometry/input.py +0 -0
  70. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/geometry/interface.py +0 -0
  71. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/geometry/manual.py +0 -0
  72. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/geometry/output.py +0 -0
  73. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/geometry/texture.py +0 -0
  74. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/geometry/utilities.py +0 -0
  75. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/geometry/vector.py +0 -0
  76. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/geometry/zone.py +0 -0
  77. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/shader/__init__.py +0 -0
  78. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/shader/assets.py +0 -0
  79. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/shader/color.py +0 -0
  80. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/shader/converter.py +0 -0
  81. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/shader/grid.py +0 -0
  82. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/shader/group.py +0 -0
  83. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/shader/input.py +0 -0
  84. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/shader/manual.py +0 -0
  85. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/shader/output.py +0 -0
  86. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/shader/script.py +0 -0
  87. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/shader/shader.py +0 -0
  88. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/shader/texture.py +0 -0
  89. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/nodes/shader/vector.py +0 -0
  90. {nodebpy-520.24.0 → nodebpy-520.26.0}/src/nodebpy/types.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: nodebpy
3
- Version: 520.24.0
3
+ Version: 520.26.0
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 = "520.24.0"
3
+ version = "520.26.0"
4
4
  description = "Build nodes trees in Blender more elegantly with code"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -1275,24 +1275,33 @@ def plot_library(
1275
1275
  *,
1276
1276
  dpi: int = 150,
1277
1277
  arrange: SugiyamaOptions | None = None,
1278
+ tree: bool = True,
1279
+ node: bool = True,
1280
+ open_panels: bool = False,
1278
1281
  ) -> dict[str, Path]:
1279
1282
  """Render node groups from ``blend_path`` to PNG images under
1280
- ``output_dir`` — a headless look at node graphs, e.g. for posting in
1281
- pull requests (``python -m nodebpy.assets plot``).
1283
+ ``output_dir`` — a headless, Blender-styled look at node graphs, e.g.
1284
+ for reviewing new nodes in pull requests (``python -m nodebpy.assets
1285
+ plot``).
1282
1286
 
1283
1287
  ``names`` selects the groups to plot: exact names or :mod:`fnmatch`
1284
1288
  wildcard patterns (``"Style *"``), matched against *every* node group in
1285
1289
  the ``.blend`` (not just assets); ``None`` plots them all. A pattern
1286
- matching nothing raises. Each tree is drawn with
1287
- :func:`nodebpy.export.to_plot` (which needs the optional ``matplotlib``
1288
- dependency) at its stored layout, or re-arranged first when ``arrange``
1289
- options are given. Everything appended for plotting is removed from the
1290
- session again afterwards.
1290
+ matching nothing raises. Each group is rendered twice: its internals
1291
+ (``<name>.png``, :func:`nodebpy.export.to_plot`, drawn at the stored
1292
+ layout or re-arranged first when ``arrange`` options are given) and the
1293
+ single group node a user sees when adding it (``<name>_node.png``,
1294
+ ``to_plot(node=True)``, with every interface panel expanded when
1295
+ ``open_panels`` is set); ``tree=False`` / ``node=False`` skip either.
1296
+ Both need the optional ``matplotlib`` dependency.
1297
+ Everything appended for plotting is removed from the session again
1298
+ afterwards.
1291
1299
 
1292
1300
  Returns
1293
1301
  -------
1294
1302
  dict[str, Path]
1295
- Mapping of group name to the image it was written to.
1303
+ Mapping of group name (suffixed with ``" (node)"`` for the group-node
1304
+ render) to the image it was written to.
1296
1305
  """
1297
1306
  blend_path = Path(blend_path)
1298
1307
  output_dir = Path(output_dir)
@@ -1339,12 +1348,23 @@ def plot_library(
1339
1348
  try:
1340
1349
  output_dir.mkdir(parents=True, exist_ok=True)
1341
1350
  written: dict[str, Path] = {}
1342
- for tree in dst.node_groups:
1343
- assert tree is not None
1344
- if arrange is not None:
1345
- arrange_tree_nodes(tree, arrange)
1346
- slug = re.sub(r"[^A-Za-z0-9._-]+", "_", tree.name).lstrip(".") or "tree"
1347
- written[tree.name] = to_plot(tree, output_dir / f"{slug}.png", dpi=dpi)
1351
+ for group in dst.node_groups:
1352
+ assert group is not None
1353
+ if arrange is not None and tree:
1354
+ arrange_tree_nodes(group, arrange)
1355
+ slug = re.sub(r"[^A-Za-z0-9._-]+", "_", group.name).lstrip(".") or "tree"
1356
+ if tree:
1357
+ written[group.name] = to_plot(
1358
+ group, output_dir / f"{slug}.png", title=group.name, dpi=dpi
1359
+ )
1360
+ if node:
1361
+ written[f"{group.name} (node)"] = to_plot(
1362
+ group,
1363
+ output_dir / f"{slug}_node.png",
1364
+ dpi=dpi,
1365
+ node=True,
1366
+ open_panels=open_panels,
1367
+ )
1348
1368
  finally:
1349
1369
  for coll in _CLEANUP_COLLECTIONS:
1350
1370
  data = getattr(bpy.data, coll)
@@ -1386,7 +1406,7 @@ def _add_arrangement_flags(parser, description: str) -> None: # pragma: no cove
1386
1406
  choices=["LEFT_DOWN", "RIGHT_DOWN", "LEFT_UP", "RIGHT_UP", "BALANCED"],
1387
1407
  help=(
1388
1408
  "Direction of layout — which corner nodes align towards, or "
1389
- "'balanced' to even out the four extremes (default: right_up)."
1409
+ "'balanced' to even out the four extremes (default: balanced)."
1390
1410
  ),
1391
1411
  )
1392
1412
  layout.add_argument(
@@ -1422,6 +1442,38 @@ def _add_arrangement_flags(parser, description: str) -> None: # pragma: no cove
1422
1442
  action="store_true",
1423
1443
  help="Fit the widths of collapsed nodes to their display name.",
1424
1444
  )
1445
+ layout.add_argument(
1446
+ "--no-sequential-frames",
1447
+ dest="sequential_frames",
1448
+ action="store_false",
1449
+ help=(
1450
+ "Do not rank frames as stages of the flow (by default every node "
1451
+ "of a frame comes after every node of the frame feeding it, so "
1452
+ "frames line up left to right instead of stacking)."
1453
+ ),
1454
+ )
1455
+ layout.add_argument(
1456
+ "--no-balance-heights",
1457
+ dest="balance_heights",
1458
+ action="store_false",
1459
+ help=(
1460
+ "Do not promote feeder chains into emptier columns to shorten the "
1461
+ "tallest column."
1462
+ ),
1463
+ )
1464
+ layout.add_argument(
1465
+ "--balance-aspect",
1466
+ type=float,
1467
+ help=("Width-to-height ratio the height balancing aims for (default: 1.6)."),
1468
+ )
1469
+ layout.add_argument(
1470
+ "--reroute-margin-y-fac",
1471
+ type=float,
1472
+ help=(
1473
+ "Fraction of the vertical spacing kept between consecutive "
1474
+ "reroutes in a column (default: 0.35)."
1475
+ ),
1476
+ )
1425
1477
 
1426
1478
 
1427
1479
  def _arrange_options_from_args(args) -> SugiyamaOptions | None:
@@ -1446,6 +1498,14 @@ def _arrange_options_from_args(args) -> SugiyamaOptions | None:
1446
1498
  overrides["stack_margin_y_fac"] = args.stack_margin_y_fac
1447
1499
  if args.optimize_sizes:
1448
1500
  overrides["optimize_sizes"] = True
1501
+ if not args.sequential_frames:
1502
+ overrides["sequential_frames"] = False
1503
+ if not args.balance_heights:
1504
+ overrides["balance_heights"] = False
1505
+ if args.balance_aspect is not None:
1506
+ overrides["balance_aspect"] = args.balance_aspect
1507
+ if args.reroute_margin_y_fac is not None:
1508
+ overrides["reroute_margin_y_fac"] = args.reroute_margin_y_fac
1449
1509
  return SugiyamaOptions(**overrides) if overrides else None
1450
1510
 
1451
1511
 
@@ -1726,13 +1786,15 @@ def _parse_args(argv: list[str] | None = None):
1726
1786
  "plot",
1727
1787
  help="Render node groups from a .blend to PNG images.",
1728
1788
  description=(
1729
- "Render node groups from a .blend to PNG images — a headless "
1730
- "look at node graphs, e.g. for posting in pull requests. Selects "
1731
- "groups by exact name or fnmatch wildcard ('Style *'), matched "
1732
- "against every node group in the file (not just assets); with no "
1733
- "names, every group is plotted. Trees are drawn at their stored "
1734
- "layout unless --arrange (or any arrangement flag) is given. "
1735
- "Requires matplotlib (pip install nodebpy[plot])."
1789
+ "Render node groups from a .blend to PNG images — a headless, "
1790
+ "Blender-styled look at node graphs, e.g. for reviewing new nodes "
1791
+ "in pull requests. Selects groups by exact name or fnmatch "
1792
+ "wildcard ('Style *'), matched against every node group in the "
1793
+ "file (not just assets); with no names, every group is plotted. "
1794
+ "Each group gets two images: its internals (<name>.png, drawn at "
1795
+ "the stored layout unless --arrange or any arrangement flag is "
1796
+ "given) and the group node as seen from another tree "
1797
+ "(<name>_node.png). Requires matplotlib (pip install nodebpy[plot])."
1736
1798
  ),
1737
1799
  )
1738
1800
  plot.add_argument("blend", type=Path, help="The .blend holding the node groups.")
@@ -1746,6 +1808,24 @@ def _parse_args(argv: list[str] | None = None):
1746
1808
  ),
1747
1809
  )
1748
1810
  plot.add_argument("--dpi", type=int, default=150, help="Image DPI (default: 150).")
1811
+ plot.add_argument(
1812
+ "--tree-only",
1813
+ action="store_true",
1814
+ help="Only render the internals of each group (skip <name>_node.png).",
1815
+ )
1816
+ plot.add_argument(
1817
+ "--node-only",
1818
+ action="store_true",
1819
+ help="Only render each group as a group node (skip <name>.png).",
1820
+ )
1821
+ plot.add_argument(
1822
+ "--open-panels",
1823
+ action="store_true",
1824
+ help=(
1825
+ "Expand every interface panel in the group-node render instead "
1826
+ "of drawing panels in their default closed state."
1827
+ ),
1828
+ )
1749
1829
  plot.add_argument(
1750
1830
  "--arrange",
1751
1831
  action="store_true",
@@ -1835,10 +1915,13 @@ def main(argv: list[str] | None = None) -> None: # pragma: no cover - CLI wrapp
1835
1915
  args.names or None,
1836
1916
  dpi=args.dpi,
1837
1917
  arrange=method,
1918
+ tree=not args.node_only,
1919
+ node=not args.tree_only,
1920
+ open_panels=args.open_panels,
1838
1921
  )
1839
1922
  for name, path in written.items():
1840
1923
  print(f" {name}: {path}")
1841
- print(f"Plotted {len(written)} node groups to {args.output}")
1924
+ print(f"Wrote {len(written)} images to {args.output}")
1842
1925
  else:
1843
1926
  _build_command(args)
1844
1927
 
@@ -97,6 +97,10 @@ _CONFIG_KEYS: dict[str, tuple[str | None, str]] = {
97
97
  "no-stack-collapsed": ("stack_collapsed", "off"),
98
98
  "stack-margin-y-fac": ("stack_margin_y_fac", "value"),
99
99
  "optimize-sizes": ("optimize_sizes", "flag"),
100
+ "no-sequential-frames": ("sequential_frames", "off"),
101
+ "no-balance-heights": ("balance_heights", "off"),
102
+ "balance-aspect": ("balance_aspect", "value"),
103
+ "reroute-margin-y-fac": ("reroute_margin_y_fac", "value"),
100
104
  }
101
105
 
102
106
  # Which dest each positional config key fills, per subcommand: the dump
@@ -132,6 +136,10 @@ _OPTION_DEFAULTS: dict[str, object] = {
132
136
  "stack_collapsed": True,
133
137
  "stack_margin_y_fac": None,
134
138
  "optimize_sizes": False,
139
+ "sequential_frames": True,
140
+ "balance_heights": True,
141
+ "balance_aspect": None,
142
+ "reroute_margin_y_fac": None,
135
143
  }
136
144
 
137
145
  # The resolved options the stamp fingerprint covers: everything, beyond the
@@ -140,6 +148,8 @@ _OPTION_DEFAULTS: dict[str, object] = {
140
148
  # it gates the session check, not the output.
141
149
  _STAMP_DESTS = (
142
150
  "add_reroutes",
151
+ "balance_aspect",
152
+ "balance_heights",
143
153
  "compress",
144
154
  "direction",
145
155
  "iterations",
@@ -149,6 +159,8 @@ _STAMP_DESTS = (
149
159
  "nodebpy_pkg",
150
160
  "on_missing",
151
161
  "optimize_sizes",
162
+ "reroute_margin_y_fac",
163
+ "sequential_frames",
152
164
  "snapshot_positions",
153
165
  "socket_alignment",
154
166
  "spacing",