procfunc 0.30.1__tar.gz → 0.31.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 (93) hide show
  1. {procfunc-0.30.1/src/procfunc.egg-info → procfunc-0.31.0}/PKG-INFO +3 -3
  2. {procfunc-0.30.1 → procfunc-0.31.0}/README.md +1 -2
  3. {procfunc-0.30.1 → procfunc-0.31.0}/pyproject.toml +6 -2
  4. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/__init__.py +2 -1
  5. procfunc-0.31.0/src/procfunc/cli.py +35 -0
  6. procfunc-0.31.0/src/procfunc/codegen/__init__.py +8 -0
  7. {procfunc-0.30.1/src/procfunc/transpiler → procfunc-0.31.0/src/procfunc/codegen}/codegen.py +11 -4
  8. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/compute_graph/node.py +2 -9
  9. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/nodes/__init__.py +5 -1
  10. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/nodes/bindings_util.py +112 -2
  11. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/nodes/bpy_node_info.py +7 -0
  12. procfunc-0.31.0/src/procfunc/nodes/color.py +334 -0
  13. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/nodes/compositor.py +125 -353
  14. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/nodes/execute/construct_nodes.py +37 -6
  15. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/nodes/execute/construct_special_cases.py +25 -14
  16. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/nodes/execute/util.py +3 -3
  17. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/nodes/func.py +55 -468
  18. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/nodes/geo.py +195 -184
  19. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/nodes/manifest.json +2508 -300
  20. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/nodes/math.py +321 -140
  21. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/nodes/shader.py +3 -880
  22. procfunc-0.31.0/src/procfunc/nodes/texture.py +686 -0
  23. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/nodes/types.py +0 -18
  24. procfunc-0.31.0/src/procfunc/ops/file.py +279 -0
  25. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/transforms/__init__.py +2 -0
  26. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/transpiler/__init__.py +0 -6
  27. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/transpiler/bpy_to_computegraph.py +90 -32
  28. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/transpiler/main.py +85 -21
  29. {procfunc-0.30.1 → procfunc-0.31.0/src/procfunc.egg-info}/PKG-INFO +3 -3
  30. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc.egg-info/SOURCES.txt +8 -3
  31. procfunc-0.31.0/src/procfunc.egg-info/entry_points.txt +2 -0
  32. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc.egg-info/requires.txt +1 -0
  33. procfunc-0.30.1/tests/test_dedup_names.py → procfunc-0.31.0/tests/test_codegen.py +1 -1
  34. {procfunc-0.30.1 → procfunc-0.31.0}/tests/test_ops.py +31 -0
  35. {procfunc-0.30.1 → procfunc-0.31.0}/tests/test_trace.py +1 -1
  36. procfunc-0.30.1/src/procfunc/ops/file.py +0 -126
  37. {procfunc-0.30.1 → procfunc-0.31.0}/LICENSE.md +0 -0
  38. {procfunc-0.30.1 → procfunc-0.31.0}/setup.cfg +0 -0
  39. {procfunc-0.30.1/src/procfunc/transpiler → procfunc-0.31.0/src/procfunc/codegen}/identifiers.py +0 -0
  40. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/color.py +0 -0
  41. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/compute_graph/__init__.py +0 -0
  42. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/compute_graph/compute_graph.py +0 -0
  43. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/compute_graph/operators_info.py +0 -0
  44. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/compute_graph/proxy.py +0 -0
  45. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/compute_graph/util.py +0 -0
  46. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/context.py +0 -0
  47. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/control.py +0 -0
  48. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/nodes/execute/execute.py +0 -0
  49. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/nodes/execute/infer_runtime_data_type.py +0 -0
  50. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/nodes/node_function.py +0 -0
  51. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/ops/__init__.py +0 -0
  52. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/ops/_util.py +0 -0
  53. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/ops/addons.py +0 -0
  54. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/ops/attr.py +0 -0
  55. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/ops/collection.py +0 -0
  56. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/ops/curve.py +0 -0
  57. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/ops/manifest.json +0 -0
  58. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/ops/mesh.py +0 -0
  59. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/ops/modifier.py +0 -0
  60. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/ops/object.py +0 -0
  61. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/ops/primitives/__init__.py +0 -0
  62. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/ops/primitives/camera.py +0 -0
  63. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/ops/primitives/curve.py +0 -0
  64. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/ops/primitives/light.py +0 -0
  65. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/ops/primitives/mesh.py +0 -0
  66. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/ops/uv.py +0 -0
  67. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/random.py +0 -0
  68. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/tracer/__init__.py +0 -0
  69. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/tracer/decorator.py +0 -0
  70. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/tracer/patch.py +0 -0
  71. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/tracer/proxy.py +0 -0
  72. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/tracer/trace.py +0 -0
  73. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/transforms/cleanup.py +0 -0
  74. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/transforms/convert.py +0 -0
  75. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/transforms/distribution.py +0 -0
  76. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/transforms/extract_materials.py +0 -0
  77. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/transforms/infer_distribution.py +0 -0
  78. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/transforms/parameters.py +0 -0
  79. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/transforms/util.py +0 -0
  80. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/types.py +0 -0
  81. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/util/__init__.py +0 -0
  82. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/util/bpy_info.py +0 -0
  83. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/util/camera.py +0 -0
  84. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/util/keyframe.py +0 -0
  85. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/util/log.py +0 -0
  86. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/util/manifest.py +0 -0
  87. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/util/pytree.py +0 -0
  88. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc/util/teardown.py +0 -0
  89. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc.egg-info/dependency_links.txt +0 -0
  90. {procfunc-0.30.1 → procfunc-0.31.0}/src/procfunc.egg-info/top_level.txt +0 -0
  91. {procfunc-0.30.1 → procfunc-0.31.0}/tests/test_asset.py +0 -0
  92. {procfunc-0.30.1 → procfunc-0.31.0}/tests/test_compute_graph.py +0 -0
  93. {procfunc-0.30.1 → procfunc-0.31.0}/tests/test_pytree.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: procfunc
3
- Version: 0.30.1
3
+ Version: 0.31.0
4
4
  Summary: Function-Oriented Abstractions for Procedural 3D Generation in Python
5
5
  License-Expression: BSD-3-Clause
6
6
  Project-URL: Homepage, https://github.com/princeton-vl/procfunc
@@ -19,13 +19,13 @@ Requires-Dist: ty; extra == "dev"
19
19
  Provides-Extra: docs
20
20
  Requires-Dist: sphinx; extra == "docs"
21
21
  Requires-Dist: sphinx-rtd-theme; extra == "docs"
22
+ Requires-Dist: sphinx-argparse; extra == "docs"
22
23
  Dynamic: license-file
23
24
 
24
25
  # ProcFunc: Function-Oriented Abstractions for Procedural 3D Generation in Python
25
26
 
26
27
  [**Documentation**](#documentation)
27
28
  | [**Research Paper**](https://arxiv.org/abs/2604.26943)
28
- | [**Documentation**](https://procfunc.readthedocs.io)
29
29
  | [**Transpiling**](#transpile-a-blender-file-to-procfunc-code)
30
30
  | [**Experiments**](#experiments)
31
31
  | [**Contributing**](#contributing)
@@ -62,7 +62,7 @@ Please create Github Issues for any bugs or unclear interfaces!
62
62
  Convert a Blender geometry node tree into procfunc Python code by downloading our example blend and executing the transpiler:
63
63
  ```bash
64
64
  wget https://raw.githubusercontent.com/princeton-vl/procfunc/main/examples/transpile_simple_chair/simple_chair.blend
65
- uv run python -m procfunc.transpiler.main simple_chair.blend --node_trees simple_chair --output transpiled_code.py
65
+ uv run procfunc transpile simple_chair.blend --node_trees simple_chair --output transpiled_code.py
66
66
  ```
67
67
 
68
68
  See the expected output in [`examples/transpile_simple_chair/transpiled_code.py`](examples/transpile_simple_chair/transpiled_code.py).
@@ -2,7 +2,6 @@
2
2
 
3
3
  [**Documentation**](#documentation)
4
4
  | [**Research Paper**](https://arxiv.org/abs/2604.26943)
5
- | [**Documentation**](https://procfunc.readthedocs.io)
6
5
  | [**Transpiling**](#transpile-a-blender-file-to-procfunc-code)
7
6
  | [**Experiments**](#experiments)
8
7
  | [**Contributing**](#contributing)
@@ -39,7 +38,7 @@ Please create Github Issues for any bugs or unclear interfaces!
39
38
  Convert a Blender geometry node tree into procfunc Python code by downloading our example blend and executing the transpiler:
40
39
  ```bash
41
40
  wget https://raw.githubusercontent.com/princeton-vl/procfunc/main/examples/transpile_simple_chair/simple_chair.blend
42
- uv run python -m procfunc.transpiler.main simple_chair.blend --node_trees simple_chair --output transpiled_code.py
41
+ uv run procfunc transpile simple_chair.blend --node_trees simple_chair --output transpiled_code.py
43
42
  ```
44
43
 
45
44
  See the expected output in [`examples/transpile_simple_chair/transpiled_code.py`](examples/transpile_simple_chair/transpiled_code.py).
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "procfunc"
7
- version = "0.30.1"
7
+ version = "0.31.0"
8
8
  description = "Function-Oriented Abstractions for Procedural 3D Generation in Python"
9
9
  readme = "README.md"
10
10
  license = "BSD-3-Clause"
@@ -19,6 +19,9 @@ dependencies = [
19
19
  Homepage = "https://github.com/princeton-vl/procfunc"
20
20
  Repository = "https://github.com/princeton-vl/procfunc"
21
21
 
22
+ [project.scripts]
23
+ procfunc = "procfunc.cli:cli"
24
+
22
25
  [project.optional-dependencies]
23
26
  dev = [
24
27
  "pytest",
@@ -29,13 +32,14 @@ dev = [
29
32
  docs = [
30
33
  "sphinx",
31
34
  "sphinx-rtd-theme",
35
+ "sphinx-argparse",
32
36
  ]
33
37
 
34
38
  [tool.setuptools.packages.find]
35
39
  where = ["src"]
36
40
 
37
41
  [tool.setuptools.package-data]
38
- procfunc = ["**/*.json"]
42
+ procfunc = ["nodes/manifest.json", "ops/manifest.json"]
39
43
 
40
44
  [tool.pytest.ini_options]
41
45
  testpaths = "tests"
@@ -2,7 +2,7 @@
2
2
  # ensure this gets imported first so that mathutils etc is available even if later modules dont import bpy
3
3
  import bpy
4
4
 
5
- __version__ = "0.30.1"
5
+ __version__ = "0.30.2"
6
6
 
7
7
  from numpy.random import Generator as RNG
8
8
 
@@ -41,6 +41,7 @@ from .types import (
41
41
  LightObject,
42
42
  LightProbeObject,
43
43
  MetaObject,
44
+ PointCloudObject,
44
45
  Material,
45
46
  Texture,
46
47
  Collection,
@@ -0,0 +1,35 @@
1
+ import argparse
2
+
3
+ from procfunc.transpiler import main as transpiler_main
4
+ from procfunc.util.teardown import skip_teardown_on_exit
5
+
6
+
7
+ def get_parser() -> argparse.ArgumentParser:
8
+ parser = argparse.ArgumentParser(prog="procfunc")
9
+ subparsers = parser.add_subparsers(dest="command", required=True)
10
+
11
+ transpile_parser = subparsers.add_parser(
12
+ "transpile", help="Transpile a Blender file to procfunc Python code."
13
+ )
14
+ transpiler_main.add_transpile_arguments(transpile_parser)
15
+
16
+ return parser
17
+
18
+
19
+ def main():
20
+ parser = get_parser()
21
+ args = parser.parse_args()
22
+
23
+ if args.command == "transpile":
24
+ transpiler_main.run(args)
25
+ else:
26
+ parser.error(f"Unknown command: {args.command}")
27
+
28
+
29
+ def cli():
30
+ with skip_teardown_on_exit():
31
+ main()
32
+
33
+
34
+ if __name__ == "__main__":
35
+ cli()
@@ -0,0 +1,8 @@
1
+ from .codegen import default_func_resolution_map, to_python
2
+ from .identifiers import dedup_names_with_suffix
3
+
4
+ __all__ = [
5
+ "default_func_resolution_map",
6
+ "to_python",
7
+ "dedup_names_with_suffix",
8
+ ]
@@ -11,13 +11,13 @@ import numpy as np
11
11
 
12
12
  import procfunc as pf
13
13
  from procfunc import compute_graph as cg
14
+ from procfunc.codegen import identifiers
14
15
  from procfunc.compute_graph.operators_info import (
15
16
  FUNCTIONS_TO_OPERATORS,
16
17
  OPERATOR_TEMPLATES,
17
18
  OperatorType,
18
19
  )
19
20
  from procfunc.nodes import types as nt
20
- from procfunc.transpiler import identifiers
21
21
  from procfunc.util import pytree
22
22
 
23
23
  logger = logging.getLogger(__name__)
@@ -97,7 +97,7 @@ def _repr_value(value: Any) -> str:
97
97
  elif isinstance(value, np.dtype):
98
98
  return f"np.dtype('{value}')"
99
99
  elif isinstance(value, (pf.Color, pf.Vector, pf.Euler, pf.Quaternion, pf.Matrix)):
100
- x = tuple(round(x, 6) for x in value)
100
+ x = tuple(round(x, 8) for x in value)
101
101
  return f"pf.{value.__class__.__name__}({x})"
102
102
  elif isinstance(value, enum.Enum):
103
103
  return f"{type(value).__name__}.{value.name}"
@@ -111,6 +111,11 @@ def _repr_value(value: Any) -> str:
111
111
  return f"{type(value).__name__}({args_str})"
112
112
  elif isinstance(value, list):
113
113
  return f"[{', '.join([_repr_value(x) for x in value])}]"
114
+ elif isinstance(value, tuple):
115
+ inner = ", ".join(_repr_value(x) for x in value)
116
+ return f"({inner},)" if len(value) == 1 else f"({inner})"
117
+ elif isinstance(value, float) and not isinstance(value, bool):
118
+ return repr(round(value, 8))
114
119
  else:
115
120
  return repr(value)
116
121
 
@@ -389,7 +394,9 @@ def _codegen_for_outputs(
389
394
  scope_expressions: dict[int, str | list[str]],
390
395
  ) -> tuple[str | None, list[str], list[str]]:
391
396
  if len(graph.outputs) == 0:
392
- return None, [], []
397
+ # Sink graphs (no return values) still need a body statement so the
398
+ # generated function parses; emit `pass` when nothing else fills it.
399
+ return None, [], ["pass"]
393
400
  if len(graph.outputs) == 1:
394
401
  single_output = next(graph.outputs.values())
395
402
  vt = single_output.metadata.get("known_value_type", None)
@@ -720,7 +727,7 @@ def default_func_resolution_map(
720
727
  skip_funcs: set | None = None,
721
728
  ) -> tuple[dict[Any, str | OperatorType], list[str]]:
722
729
  func_resolution = {}
723
- import_lines = set()
730
+ import_lines = {"import procfunc as pf"}
724
731
 
725
732
  for graph in cg.traverse_nested_graphs(toplevel_graph):
726
733
  assert isinstance(graph, cg.ComputeGraph), graph
@@ -56,13 +56,6 @@ class FunctionCallNode(Node):
56
56
 
57
57
 
58
58
  class MethodCallNode(Node):
59
- """
60
- represents an {args[0]}.{method_name}(*args[1:], **kwargs) call
61
-
62
- - the node to be used as `self` is the first arg, since it is a dynamic value
63
- - the method name is assumed to be const
64
- """
65
-
66
59
  def __init__(
67
60
  self,
68
61
  callee: Node,
@@ -161,7 +154,7 @@ def normalize_args_to_kwargs(
161
154
  """
162
155
  Try to fully populate kwargs, by moving over positional args & filling in defaults
163
156
 
164
- Some args may not be able to be converted to kwargs, e.g. *args have no names that work
157
+ Some args may not be able to be converted to kwargs, e.g. ``*args`` have no names that work
165
158
 
166
159
  Args:
167
160
  func: The function whose signature we should respect
@@ -171,7 +164,7 @@ def normalize_args_to_kwargs(
171
164
  Returns:
172
165
  A tuple of (args, kwargs) where args is a tuple of positional arguments and kwargs is a dictionary of keyword arguments.
173
166
 
174
- GUARANTEE: func(*returned_args, **returned_kwargs) == func(*args, **kwargs) and does not crash
167
+ GUARANTEE: ``func(*returned_args, **returned_kwargs) == func(*args, **kwargs)`` and does not crash
175
168
  """
176
169
 
177
170
  sig = inspect.signature(func)
@@ -3,15 +3,17 @@ from pandas import read_json as _read_json
3
3
  from procfunc.tracer import autowrap_module as _autowrap
4
4
  from procfunc.util.manifest import module_path
5
5
 
6
- from . import compositor, func, geo, math, shader
6
+ from . import color, compositor, func, geo, math, shader, texture
7
7
  from .bpy_node_info import NodeDataType, NodeGroupType, SocketType
8
8
 
9
9
  # ruff: noqa: E402
10
+ _autowrap(color, allow_exec=False)
10
11
  _autowrap(compositor, allow_exec=False)
11
12
  _autowrap(func, allow_exec=False)
12
13
  _autowrap(geo, allow_exec=False)
13
14
  _autowrap(math, allow_exec=False)
14
15
  _autowrap(shader, allow_exec=False)
16
+ _autowrap(texture, allow_exec=False)
15
17
 
16
18
  from .execute.execute import (
17
19
  as_nodegroup,
@@ -38,11 +40,13 @@ NODES_MANIFEST = _read_json(NODES_MANIFEST_PATH)
38
40
 
39
41
  __all__ = [
40
42
  # Node category submodules
43
+ "color",
41
44
  "compositor",
42
45
  "func",
43
46
  "geo",
44
47
  "math",
45
48
  "shader",
49
+ "texture",
46
50
  # Core types
47
51
  "ProcNode",
48
52
  "Shader",
@@ -24,6 +24,11 @@ class ContextualNode(Enum):
24
24
  GROUP = "ContextualGroup"
25
25
  MIX_RGB = "ContextualMixRGB"
26
26
  MATH = "ContextualMath"
27
+ COLOR_RAMP = "ContextualColorRamp"
28
+ VECTOR_CURVE = "ContextualVectorCurve"
29
+ COMBINE_XYZ = "ContextualCombineXYZ"
30
+ MAP_RANGE = "ContextualMapRange"
31
+ HUE_SATURATION = "ContextualHueSaturation"
27
32
 
28
33
  @classmethod
29
34
  def parse_name(cls, from_name: str) -> Self | None:
@@ -39,6 +44,7 @@ class NodeContextResolution:
39
44
  node_group_type: NodeGroupType
40
45
  node_type: str
41
46
  input_keys_map: dict[str, tuple[str, int] | str | None] | None
47
+ output_keys_map: dict[str, str] | None = None
42
48
 
43
49
 
44
50
  # Mapping to map unified version of nodes to context-specific blender versions
@@ -64,6 +70,12 @@ CONTEXTUAL_NODE_MAPPING = [
64
70
  node_type="FunctionNodeCombineColor",
65
71
  input_keys_map=None,
66
72
  ),
73
+ NodeContextResolution(
74
+ contextual_node=ContextualNode.COMBINE_COLOR,
75
+ node_group_type=NodeGroupType.COMPOSITOR,
76
+ node_type="CompositorNodeCombineColor",
77
+ input_keys_map=None,
78
+ ),
67
79
  NodeContextResolution(
68
80
  contextual_node=ContextualNode.SEPARATE_COLOR,
69
81
  node_group_type=NodeGroupType.SHADER,
@@ -130,6 +142,12 @@ CONTEXTUAL_NODE_MAPPING = [
130
142
  node_type="ShaderNodeMath",
131
143
  input_keys_map=None,
132
144
  ),
145
+ NodeContextResolution(
146
+ contextual_node=ContextualNode.MATH,
147
+ node_group_type=NodeGroupType.GEOMETRY,
148
+ node_type="ShaderNodeMath",
149
+ input_keys_map=None,
150
+ ),
133
151
  NodeContextResolution(
134
152
  contextual_node=ContextualNode.MATH,
135
153
  node_group_type=NodeGroupType.COMPOSITOR,
@@ -142,12 +160,104 @@ CONTEXTUAL_NODE_MAPPING = [
142
160
  node_type="TextureNodeMath",
143
161
  input_keys_map=None,
144
162
  ),
163
+ NodeContextResolution(
164
+ contextual_node=ContextualNode.COLOR_RAMP,
165
+ node_group_type=NodeGroupType.SHADER,
166
+ node_type="ShaderNodeValToRGB",
167
+ input_keys_map=None,
168
+ ),
169
+ NodeContextResolution(
170
+ contextual_node=ContextualNode.COLOR_RAMP,
171
+ node_group_type=NodeGroupType.GEOMETRY,
172
+ node_type="ShaderNodeValToRGB",
173
+ input_keys_map=None,
174
+ ),
175
+ NodeContextResolution(
176
+ contextual_node=ContextualNode.COLOR_RAMP,
177
+ node_group_type=NodeGroupType.COMPOSITOR,
178
+ node_type="CompositorNodeValToRGB",
179
+ input_keys_map=None,
180
+ output_keys_map={"Color": "Image"},
181
+ ),
182
+ NodeContextResolution(
183
+ contextual_node=ContextualNode.VECTOR_CURVE,
184
+ node_group_type=NodeGroupType.SHADER,
185
+ node_type="ShaderNodeVectorCurve",
186
+ input_keys_map=None,
187
+ ),
188
+ NodeContextResolution(
189
+ contextual_node=ContextualNode.VECTOR_CURVE,
190
+ node_group_type=NodeGroupType.GEOMETRY,
191
+ node_type="ShaderNodeVectorCurve",
192
+ input_keys_map=None,
193
+ ),
194
+ NodeContextResolution(
195
+ contextual_node=ContextualNode.VECTOR_CURVE,
196
+ node_group_type=NodeGroupType.COMPOSITOR,
197
+ # CompositorNodeCurveVec has no Fac input; the wrapper's `fac` arg is dropped
198
+ # silently in compositor context. Honoring fac would require synthesizing an
199
+ # extra mix node.
200
+ node_type="CompositorNodeCurveVec",
201
+ input_keys_map={"Fac": None},
202
+ ),
203
+ NodeContextResolution(
204
+ contextual_node=ContextualNode.COMBINE_XYZ,
205
+ node_group_type=NodeGroupType.SHADER,
206
+ node_type="ShaderNodeCombineXYZ",
207
+ input_keys_map=None,
208
+ ),
209
+ NodeContextResolution(
210
+ contextual_node=ContextualNode.COMBINE_XYZ,
211
+ node_group_type=NodeGroupType.GEOMETRY,
212
+ node_type="ShaderNodeCombineXYZ",
213
+ input_keys_map=None,
214
+ ),
215
+ NodeContextResolution(
216
+ contextual_node=ContextualNode.COMBINE_XYZ,
217
+ node_group_type=NodeGroupType.COMPOSITOR,
218
+ node_type="CompositorNodeCombineXYZ",
219
+ input_keys_map=None,
220
+ ),
221
+ NodeContextResolution(
222
+ contextual_node=ContextualNode.MAP_RANGE,
223
+ node_group_type=NodeGroupType.SHADER,
224
+ node_type="ShaderNodeMapRange",
225
+ input_keys_map=None,
226
+ ),
227
+ NodeContextResolution(
228
+ contextual_node=ContextualNode.MAP_RANGE,
229
+ node_group_type=NodeGroupType.GEOMETRY,
230
+ node_type="ShaderNodeMapRange",
231
+ input_keys_map=None,
232
+ ),
233
+ NodeContextResolution(
234
+ contextual_node=ContextualNode.MAP_RANGE,
235
+ node_group_type=NodeGroupType.COMPOSITOR,
236
+ # CompositorNodeMapRange exposes only float Value and use_clamp; it has
237
+ # no interpolation_type and no vector data_type. The wrapper omits
238
+ # those attrs when at default, so setattr only fails (with its own
239
+ # "no attribute" error) when a user actually requested a non-default.
240
+ node_type="CompositorNodeMapRange",
241
+ input_keys_map={"clamp": "use_clamp"},
242
+ ),
243
+ NodeContextResolution(
244
+ contextual_node=ContextualNode.HUE_SATURATION,
245
+ node_group_type=NodeGroupType.SHADER,
246
+ node_type="ShaderNodeHueSaturation",
247
+ input_keys_map=None,
248
+ ),
249
+ NodeContextResolution(
250
+ contextual_node=ContextualNode.HUE_SATURATION,
251
+ node_group_type=NodeGroupType.COMPOSITOR,
252
+ node_type="CompositorNodeHueSat",
253
+ input_keys_map={"Color": "Image"},
254
+ ),
145
255
  ]
146
256
 
147
257
 
148
258
  def resolve_contextual_node(
149
259
  node: list[NodeContextResolution], group: NodeGroupType
150
- ) -> tuple[str, dict[str, str]]:
260
+ ) -> NodeContextResolution:
151
261
  item = next(
152
262
  (
153
263
  item
@@ -160,7 +270,7 @@ def resolve_contextual_node(
160
270
  if item is None:
161
271
  raise ValueError(f"No contextual node found for {node} in {group}")
162
272
 
163
- return item.node_type, item.input_keys_map
273
+ return item
164
274
 
165
275
 
166
276
  def raise_shader_normal_error(node_func_name: str, logger: logging.Logger):
@@ -58,6 +58,8 @@ class SocketType(Enum):
58
58
  COLLECTION = "NodeSocketCollection"
59
59
  TEXTURE = "NodeSocketTexture"
60
60
  MATERIAL = "NodeSocketMaterial"
61
+ MATRIX = "NodeSocketMatrix"
62
+ IMAGE = "NodeSocketImage"
61
63
 
62
64
 
63
65
  class SocketDType(Enum):
@@ -150,6 +152,10 @@ SOCKET_TYPE_TO_PYTHON_TYPE = {
150
152
  SocketType.COLLECTION: pt.Collection,
151
153
  SocketType.MATERIAL: pt.Material,
152
154
  SocketType.SHADER: nt.Shader,
155
+ SocketType.OBJECT: pt.Object,
156
+ SocketType.TEXTURE: pt.Texture,
157
+ SocketType.MATRIX: pt.Matrix,
158
+ SocketType.IMAGE: pt.Image,
153
159
  }
154
160
 
155
161
  # Socket types that can be implicitly converted between each other
@@ -263,6 +269,7 @@ SPECIAL_CASE_ATTR_NAMES = set(
263
269
  "interface",
264
270
  "node_tree",
265
271
  "tag_need_exec",
272
+ "index_switch_items",
266
273
  ]
267
274
  )
268
275