procfunc 0.35.1__tar.gz → 0.36.1__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 (114) hide show
  1. {procfunc-0.35.1/src/procfunc.egg-info → procfunc-0.36.1}/PKG-INFO +2 -2
  2. {procfunc-0.35.1 → procfunc-0.36.1}/README.md +1 -1
  3. {procfunc-0.35.1 → procfunc-0.36.1}/pyproject.toml +1 -0
  4. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/__init__.py +2 -1
  5. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/codegen/codegen.py +26 -17
  6. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/codegen/repr.py +4 -1
  7. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/compute_graph/operators_info.py +12 -0
  8. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/context.py +16 -0
  9. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/control/__init__.py +7 -1
  10. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/nodes/__init__.py +2 -0
  11. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/nodes/color.py +16 -1
  12. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/nodes/compositor.py +227 -30
  13. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/nodes/execute/construct_special_cases.py +121 -31
  14. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/nodes/execute/construct_standard.py +2 -2
  15. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/nodes/execute/infer_runtime_data_type.py +10 -0
  16. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/nodes/func.py +15 -2
  17. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/nodes/geo.py +200 -67
  18. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/nodes/manifest.json +1107 -39
  19. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/nodes/math.py +63 -19
  20. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/nodes/shader.py +235 -18
  21. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/nodes/texture.py +131 -28
  22. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/nodes/types.py +18 -1
  23. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/nodes/util/bindings_util.py +1 -0
  24. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/nodes/util/bpy_node_info.py +0 -14
  25. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/nodes/util/node_function.py +67 -7
  26. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/ops/_util.py +18 -13
  27. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/ops/addons.py +11 -6
  28. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/ops/curve.py +2 -1
  29. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/ops/file.py +26 -14
  30. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/ops/manifest.json +43 -0
  31. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/ops/mesh.py +6 -5
  32. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/ops/modifier.py +83 -14
  33. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/ops/object.py +1 -1
  34. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/ops/primitives/camera.py +3 -2
  35. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/ops/primitives/curve.py +3 -2
  36. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/ops/primitives/light.py +5 -4
  37. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/ops/primitives/mesh.py +21 -10
  38. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/random.py +17 -17
  39. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/tracer/trace.py +5 -6
  40. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/transpiler/bpy_to_computegraph.py +29 -158
  41. procfunc-0.36.1/src/procfunc/transpiler/parse_attrs.py +137 -0
  42. procfunc-0.36.1/src/procfunc/transpiler/parse_special_cases.py +467 -0
  43. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/util/teardown.py +8 -0
  44. {procfunc-0.35.1 → procfunc-0.36.1/src/procfunc.egg-info}/PKG-INFO +2 -2
  45. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc.egg-info/SOURCES.txt +3 -0
  46. {procfunc-0.35.1 → procfunc-0.36.1}/tests/test_codegen.py +59 -0
  47. procfunc-0.36.1/tests/test_control.py +52 -0
  48. procfunc-0.36.1/tests/test_node_function.py +131 -0
  49. {procfunc-0.35.1 → procfunc-0.36.1}/tests/test_ops.py +166 -67
  50. procfunc-0.36.1/tests/test_random_mutation.py +92 -0
  51. procfunc-0.36.1/tests/test_teardown.py +99 -0
  52. procfunc-0.35.1/src/procfunc/transpiler/parse_special_cases.py +0 -158
  53. procfunc-0.35.1/tests/test_node_function.py +0 -28
  54. procfunc-0.35.1/tests/test_teardown.py +0 -51
  55. {procfunc-0.35.1 → procfunc-0.36.1}/LICENSE.md +0 -0
  56. {procfunc-0.35.1 → procfunc-0.36.1}/setup.cfg +0 -0
  57. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/cli/__init__.py +0 -0
  58. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/cli/main.py +0 -0
  59. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/codegen/__init__.py +0 -0
  60. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/codegen/identifiers.py +0 -0
  61. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/color.py +0 -0
  62. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/compute_graph/__init__.py +0 -0
  63. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/compute_graph/compute_graph.py +0 -0
  64. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/compute_graph/node.py +0 -0
  65. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/compute_graph/proxy.py +0 -0
  66. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/compute_graph/util.py +0 -0
  67. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/nodes/execute/construct_nodes.py +0 -0
  68. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/nodes/execute/construct_operator.py +0 -0
  69. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/nodes/execute/execute.py +0 -0
  70. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/nodes/execute/realize.py +0 -0
  71. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/nodes/execute/util.py +0 -0
  72. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/nodes/util/__init__.py +0 -0
  73. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/ops/__init__.py +0 -0
  74. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/ops/attr.py +0 -0
  75. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/ops/collection.py +0 -0
  76. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/ops/primitives/__init__.py +0 -0
  77. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/ops/uv.py +0 -0
  78. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/tracer/__init__.py +0 -0
  79. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/tracer/decorator.py +0 -0
  80. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/tracer/patch.py +0 -0
  81. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/tracer/proxy.py +0 -0
  82. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/transforms/__init__.py +0 -0
  83. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/transforms/cleanup.py +0 -0
  84. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/transforms/convert.py +0 -0
  85. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/transforms/distribution.py +0 -0
  86. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/transforms/extract_materials.py +0 -0
  87. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/transforms/infer_distribution.py +0 -0
  88. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/transforms/parameters.py +0 -0
  89. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/transforms/util.py +0 -0
  90. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/transpiler/__init__.py +0 -0
  91. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/transpiler/main.py +0 -0
  92. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/transpiler/parse_default_values.py +0 -0
  93. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/types.py +0 -0
  94. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/util/__init__.py +0 -0
  95. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/util/bpy_data.py +0 -0
  96. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/util/bpy_info.py +0 -0
  97. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/util/camera.py +0 -0
  98. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/util/keyframe.py +0 -0
  99. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/util/log.py +0 -0
  100. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/util/manifest.py +0 -0
  101. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc/util/pytree.py +0 -0
  102. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc.egg-info/dependency_links.txt +0 -0
  103. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc.egg-info/entry_points.txt +0 -0
  104. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc.egg-info/requires.txt +0 -0
  105. {procfunc-0.35.1 → procfunc-0.36.1}/src/procfunc.egg-info/top_level.txt +0 -0
  106. {procfunc-0.35.1 → procfunc-0.36.1}/tests/test_asset.py +0 -0
  107. {procfunc-0.35.1 → procfunc-0.36.1}/tests/test_bpy_data_cleanup.py +0 -0
  108. {procfunc-0.35.1 → procfunc-0.36.1}/tests/test_cli_transpile.py +0 -0
  109. {procfunc-0.35.1 → procfunc-0.36.1}/tests/test_codegen_matrix.py +0 -0
  110. {procfunc-0.35.1 → procfunc-0.36.1}/tests/test_compute_graph.py +0 -0
  111. {procfunc-0.35.1 → procfunc-0.36.1}/tests/test_pytree.py +0 -0
  112. {procfunc-0.35.1 → procfunc-0.36.1}/tests/test_random.py +0 -0
  113. {procfunc-0.35.1 → procfunc-0.36.1}/tests/test_trace.py +0 -0
  114. {procfunc-0.35.1 → procfunc-0.36.1}/tests/test_transforms.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: procfunc
3
- Version: 0.35.1
3
+ Version: 0.36.1
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
@@ -37,7 +37,7 @@ Dynamic: license-file
37
37
 
38
38
  This repository contains only the Primitives API, Transpiler and Tracer from our research paper.
39
39
 
40
- The pre-made procedural generators are coming soon as part of [infinigen](https://github.com/princeton-vl/infinigen).
40
+ Please see the infinigen2 [documentation](https://infinigen.cs.princeton.edu/docs/latest/) and [sourcecode](https://github.com/princeton-vl/infinigen/tree/3f58bb886bb1bda681d41240344fe3126ac0e9bd/src/infinigen2) to access the paper's library of new procedural generators
41
41
 
42
42
  ### Installation
43
43
 
@@ -8,7 +8,7 @@
8
8
 
9
9
  This repository contains only the Primitives API, Transpiler and Tracer from our research paper.
10
10
 
11
- The pre-made procedural generators are coming soon as part of [infinigen](https://github.com/princeton-vl/infinigen).
11
+ Please see the infinigen2 [documentation](https://infinigen.cs.princeton.edu/docs/latest/) and [sourcecode](https://github.com/princeton-vl/infinigen/tree/3f58bb886bb1bda681d41240344fe3126ac0e9bd/src/infinigen2) to access the paper's library of new procedural generators
12
12
 
13
13
  ### Installation
14
14
 
@@ -52,6 +52,7 @@ procfunc = ["nodes/manifest.json", "ops/manifest.json"]
52
52
 
53
53
  [tool.pytest.ini_options]
54
54
  testpaths = "tests"
55
+ markers = ["render: requires a render-capable Blender environment"]
55
56
 
56
57
  [tool.ruff]
57
58
  exclude = ["examples", "experiments"]
@@ -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.35.1"
5
+ __version__ = "0.36.1"
6
6
 
7
7
  from numpy.random import Generator as RNG
8
8
 
@@ -54,6 +54,7 @@ from .nodes import ProcNode, Shader, NodeDataType
54
54
  from .tracer import trace, autowrap_module, add_search_scope
55
55
  from .util.manifest import module_path
56
56
 
57
+ bpy.context.preferences.edit.use_global_undo = False
57
58
  autowrap_module(random)
58
59
  autowrap_module(color)
59
60
  add_search_scope(nodes)
@@ -240,9 +240,12 @@ def _operator_call_operands(
240
240
 
241
241
  operands = [bound.arguments[name] for name in operand_names]
242
242
 
243
- # a default epsilon stays tolerant unlike exact ==/!=, so require exact operands
244
- if template in _EPSILON_EQUALITY_TEMPLATES and not all(
245
- _is_exact_compare_operand(v) for v in operands
243
+ operator_type = FUNCTIONS_TO_OPERATORS.get(node.func)
244
+ native_equality = operator_type in (OperatorType.EQUAL, OperatorType.NOT_EQUAL)
245
+ if (
246
+ template in _EPSILON_EQUALITY_TEMPLATES
247
+ and not native_equality
248
+ and not all(_is_exact_compare_operand(v) for v in operands)
246
249
  ):
247
250
  return None
248
251
 
@@ -603,7 +606,8 @@ def _codegen_for_assignment(
603
606
  def _expressions_scope_for_graph(
604
607
  graph: cg.ComputeGraph,
605
608
  scope_expressions: dict[int, str | list[str]],
606
- ) -> tuple[dict[int, str | list[str]], dict[int, bool]]:
609
+ fold_map: dict[int, bool],
610
+ ) -> dict[int, str | list[str]]:
607
611
  expressions: dict[int, str | list[str]] = {
608
612
  **scope_expressions.copy(),
609
613
  **_check_graph_input_names(graph, scope_expressions),
@@ -612,9 +616,6 @@ def _expressions_scope_for_graph(
612
616
  # when we want to refer to a value, what string should we insert?
613
617
  # - for most nodes: refer to a variable name
614
618
  # - for inlined expressions: emplace a expression string
615
- usages = cg.usages_per_node(graph)
616
- fold_map = _expression_fold_map(graph, expressions, usages=usages)
617
-
618
619
  node_names = identifiers.nodenames_from_fixed_and_infill(
619
620
  graph,
620
621
  fold_map=fold_map,
@@ -627,7 +628,7 @@ def _expressions_scope_for_graph(
627
628
  raise ValueError(f"Scope and node names had overlap: {intersection=}")
628
629
  expressions.update(node_names)
629
630
 
630
- return expressions, fold_map
631
+ return expressions
631
632
 
632
633
 
633
634
  def _codegen_for_graph(
@@ -643,17 +644,13 @@ def _codegen_for_graph(
643
644
  if add_version_comment:
644
645
  code_lines.append(f"# Code generated by procfunc v{pf.__version__}")
645
646
 
646
- expressions, fold_map = _expressions_scope_for_graph(graph, scope_expressions)
647
+ usages = cg.usages_per_node(graph)
648
+ fold_map = _expression_fold_map(graph, scope_expressions, usages)
649
+ expressions = _expressions_scope_for_graph(graph, scope_expressions, fold_map)
647
650
  _input_ids = set(id(node) for node in graph.inputs.values()) # noqa: F841
648
-
651
+ output_ids = {id(node) for node in graph.outputs.values()}
649
652
  last_varname: str = ""
650
653
 
651
- # Collect mutator call nodes so they emit as bare statements (no assignment)
652
- mutator_call_ids = set()
653
- for node in cg.traverse_depth_first(graph):
654
- if isinstance(node, cg.MutatedArgumentNode):
655
- mutator_call_ids.add(id(node.args[1]))
656
-
657
654
  for node in cg.traverse_depth_first(graph):
658
655
  if isinstance(node, cg.InputPlaceholderNode):
659
656
  continue # arguments are defined in _codegen_graph_inputs
@@ -670,7 +667,17 @@ def _codegen_for_graph(
670
667
  expressions[id(node)] = node_code
671
668
  continue
672
669
 
673
- if id(node) in mutator_call_ids:
670
+ node_usages = usages.get(id(node), [])
671
+ mutation_usages = [
672
+ usage
673
+ for usage in node_usages
674
+ if isinstance(usage, cg.MutatedArgumentNode) and usage.args[1] is node
675
+ ]
676
+ if (
677
+ mutation_usages
678
+ and mutation_usages == node_usages
679
+ and id(node) not in output_ids
680
+ ):
674
681
  code_lines.extend(node_code if isinstance(node_code, list) else [node_code])
675
682
  continue
676
683
 
@@ -713,6 +720,8 @@ def _resolve_func(func: Any) -> tuple[str | None, str]:
713
720
  raise NotImplementedError(f"Unsupported function: {func}")
714
721
  elif module == "builtins":
715
722
  return None, func.__name__
723
+ elif module == "_operator":
724
+ return "import operator", f"operator.{func.__name__}"
716
725
  elif module.startswith("procfunc."):
717
726
  callsite = "pf." + module[len("procfunc.") :] + "." + func.__name__
718
727
  importstring = "import procfunc as pf"
@@ -27,7 +27,8 @@ def repr_type(x: Any) -> str:
27
27
  if origin is Union or origin is types.UnionType:
28
28
  first_args = get_args(args[0])
29
29
  if (
30
- get_origin(args[0]) is nt.ProcNode
30
+ len(args) == 2
31
+ and get_origin(args[0]) is nt.ProcNode
31
32
  and first_args
32
33
  and first_args[0] is args[1]
33
34
  ):
@@ -82,6 +83,8 @@ def repr_value(value: Any) -> str:
82
83
 
83
84
  if isinstance(value, np.random.Generator):
84
85
  return "np.random.default_rng()"
86
+ elif get_origin(value) in (Union, types.UnionType, nt.ProcNode):
87
+ return repr_type(value)
85
88
  elif isinstance(value, type):
86
89
  return repr_type(value)
87
90
  elif isinstance(value, np.ndarray):
@@ -7,6 +7,7 @@ class OperatorType(Enum):
7
7
  SUB = "sub"
8
8
  MUL = "mul"
9
9
  DIV = "div"
10
+ FLOOR_DIV = "floordiv"
10
11
  TRUEDIV = "truediv"
11
12
  POW = "pow"
12
13
  MOD = "mod"
@@ -17,6 +18,8 @@ class OperatorType(Enum):
17
18
  EQUAL = "eq"
18
19
  NOT_EQUAL = "ne"
19
20
  NOT = "invert"
21
+ NEG = "neg"
22
+ POS = "pos"
20
23
  LSHIFT = "lshift"
21
24
  RSHIFT = "rshift"
22
25
  BIT_AND = "and"
@@ -34,6 +37,7 @@ OPERATOR_TEMPLATES = {
34
37
  OperatorType.SUB: "{} - {}",
35
38
  OperatorType.MUL: "{} * {}",
36
39
  OperatorType.DIV: "{} / {}",
40
+ OperatorType.FLOOR_DIV: "{} // {}",
37
41
  OperatorType.POW: "{} ** {}",
38
42
  OperatorType.MOD: "{} % {}",
39
43
  OperatorType.LESS_THAN: "{} < {}",
@@ -42,6 +46,9 @@ OPERATOR_TEMPLATES = {
42
46
  OperatorType.GREATER_THAN_EQUAL: "{} >= {}",
43
47
  OperatorType.EQUAL: "{} == {}",
44
48
  OperatorType.NOT_EQUAL: "{} != {}",
49
+ OperatorType.NOT: "~{}",
50
+ OperatorType.NEG: "-{}",
51
+ OperatorType.POS: "+{}",
45
52
  OperatorType.VECTOR_PACK: "({}, {}, {})",
46
53
  OperatorType.NOOP: "{}",
47
54
  OperatorType.LSHIFT: "{} << {}",
@@ -57,6 +64,7 @@ OPERATORS_TO_FUNCTIONS = {
57
64
  OperatorType.SUB: operator.sub,
58
65
  OperatorType.MUL: operator.mul,
59
66
  OperatorType.DIV: operator.truediv,
67
+ OperatorType.FLOOR_DIV: operator.floordiv,
60
68
  OperatorType.MOD: operator.mod,
61
69
  OperatorType.POW: operator.pow,
62
70
  OperatorType.LESS_THAN: operator.lt,
@@ -65,6 +73,9 @@ OPERATORS_TO_FUNCTIONS = {
65
73
  OperatorType.GREATER_THAN_EQUAL: operator.ge,
66
74
  OperatorType.EQUAL: operator.eq,
67
75
  OperatorType.NOT_EQUAL: operator.ne,
76
+ OperatorType.NOT: operator.invert,
77
+ OperatorType.NEG: operator.neg,
78
+ OperatorType.POS: operator.pos,
68
79
  OperatorType.LSHIFT: operator.lshift,
69
80
  OperatorType.RSHIFT: operator.rshift,
70
81
  OperatorType.BIT_AND: operator.and_,
@@ -80,6 +91,7 @@ REFLECTABLE_OPERATORS = {
80
91
  OperatorType.SUB,
81
92
  OperatorType.MUL,
82
93
  OperatorType.DIV,
94
+ OperatorType.FLOOR_DIV,
83
95
  OperatorType.MOD,
84
96
  OperatorType.POW,
85
97
  OperatorType.LSHIFT,
@@ -19,6 +19,14 @@ class ProcfuncContext:
19
19
  'ignore' silently returns an empty mesh, 'warn' logs a warning, 'throw' raises an error.
20
20
  """
21
21
 
22
+ warn_mode_transpile_dropped_attrs: Literal["ignore", "warn", "throw"]
23
+ """
24
+ Controls behavior when transpiling a bpy node whose attributes carry render-affecting
25
+ state procfunc has no binding for, so the transpiled result would render differently
26
+ (e.g. a non-identity texture_mapping on a ShaderNodeTex* node).
27
+ 'ignore' drops it silently, 'warn' logs a warning, 'throw' raises an error.
28
+ """
29
+
22
30
  record_node_definitions: bool = False
23
31
  """
24
32
  Record the user-space file/line/function that constructed each ProcNode, used only to
@@ -34,10 +42,12 @@ class ProcfuncContext:
34
42
  def set_strict(self):
35
43
  """Set all warning modes to 'throw'"""
36
44
  self.warn_mode_empty_geonodes = "throw"
45
+ self.warn_mode_transpile_dropped_attrs = "throw"
37
46
 
38
47
  def set_warn(self):
39
48
  """Set all warning modes to 'warn'"""
40
49
  self.warn_mode_empty_geonodes = "warn"
50
+ self.warn_mode_transpile_dropped_attrs = "warn"
41
51
 
42
52
 
43
53
  # Global context instance
@@ -47,9 +57,15 @@ warn_modes = ["ignore", "warn", "throw"]
47
57
  _warn_mode_empty_geonodes = os.environ.get("PROCFUNC_WARN_MODE_EMPTY_GEONODES", "warn")
48
58
  assert _warn_mode_empty_geonodes in warn_modes
49
59
 
60
+ _warn_mode_transpile_dropped_attrs = os.environ.get(
61
+ "PROCFUNC_WARN_MODE_TRANSPILE_DROPPED_ATTRS", "throw"
62
+ )
63
+ assert _warn_mode_transpile_dropped_attrs in warn_modes
64
+
50
65
  globals = ProcfuncContext(
51
66
  num_cpu_cores=int(os.environ.get("PROCFUNC_NUM_CPU_CORES", 0)),
52
67
  warn_mode_empty_geonodes=_warn_mode_empty_geonodes, # type: ignore[invalid-assignment]
68
+ warn_mode_transpile_dropped_attrs=_warn_mode_transpile_dropped_attrs, # type: ignore[invalid-assignment]
53
69
  current_trace_level=None,
54
70
  record_node_definitions=os.environ.get("PROCFUNC_RECORD_NODE_DEFINITIONS", "0")
55
71
  == "1",
@@ -33,6 +33,7 @@ def choice_idx(rng: np.random.Generator, weights: list[float]) -> int:
33
33
  def _peekthrough_execute_all_choices(
34
34
  choice_options: list[tuple[Callable[..., T], float]],
35
35
  chosen_idx: int,
36
+ choice_rng: cg.Node,
36
37
  args: tuple,
37
38
  kwargs: dict,
38
39
  ) -> cg.Proxy:
@@ -43,6 +44,7 @@ def _peekthrough_execute_all_choices(
43
44
  evald.append((res, weight))
44
45
 
45
46
  new_kwargs = dict(
47
+ choice_rng=choice_rng,
46
48
  choice_options=evald,
47
49
  chosen_idx=chosen_idx,
48
50
  )
@@ -67,6 +69,7 @@ class ChoiceResultProxy(cg.Proxy):
67
69
  return _peekthrough_execute_all_choices(
68
70
  self.node.kwargs["choice_options"],
69
71
  self.node.kwargs["chosen_idx"],
72
+ self.node.kwargs["choice_rng"],
70
73
  args,
71
74
  kwargs,
72
75
  )
@@ -92,13 +95,16 @@ def _choice_create_custom_tracer_wrapper(
92
95
  @functools.wraps(choice)
93
96
  def wrapper(
94
97
  choice_rng: RngProxy,
95
- choice_options: list[tuple[T, float]] | None = None,
98
+ choice_options: list[tuple[T, float]],
96
99
  chosen_idx: int | None = None,
97
100
  ):
98
101
  choice_values, choice_weights = zip(*choice_options)
99
102
  if chosen_idx is None:
100
103
  chosen_idx = choice_idx(choice_rng.rng, choice_weights)
101
104
 
105
+ if patcher.trace_level < TraceLevel.RANDOM_CONTROL:
106
+ return choice_values[chosen_idx]
107
+
102
108
  unwrapped_options = [
103
109
  (pytree.PyTree(v).map(_unwrap_proxy).obj(), w) for v, w in choice_options
104
110
  ]
@@ -28,6 +28,7 @@ from .execute.execute import (
28
28
  )
29
29
  from .execute.util import NODE_OPERATOR_TABLE
30
30
  from .types import (
31
+ HandleType,
31
32
  ProcNode,
32
33
  Shader,
33
34
  SocketOrVal,
@@ -51,6 +52,7 @@ __all__ = [
51
52
  "ProcNode",
52
53
  "Shader",
53
54
  "SocketOrVal",
55
+ "HandleType",
54
56
  "NodeDataType",
55
57
  "NodeGroupType",
56
58
  "SocketType",
@@ -61,16 +61,31 @@ def rgb_curve(
61
61
  fac: nt.SocketOrVal[float],
62
62
  color: nt.SocketOrVal[pt.Color],
63
63
  curves: list[np.ndarray] | np.ndarray | None = None,
64
+ handle_types: list[list[nt.HandleType]] | None = None,
65
+ use_clip: bool = True,
66
+ extend: Literal["HORIZONTAL", "EXTRAPOLATED"] = "EXTRAPOLATED",
67
+ clip_min: tuple[float, float] | None = None,
68
+ clip_max: tuple[float, float] | None = None,
64
69
  ) -> nt.ProcNode:
65
70
  """
66
71
  Uses a RGBCurve Shader Node.
67
72
 
73
+ `handle_types[i]` contains the handles for the i-th curve, and
74
+ `handle_types[i][j]` is the handle type for `curves[i][j]`.
75
+
68
76
  See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/color/rgb_curves.html
69
77
  """
70
78
  return nt.ProcNode.from_nodetype(
71
79
  node_type="ShaderNodeRGBCurve",
72
80
  inputs={"Fac": fac, "Color": color},
73
- attrs={"curves": curves},
81
+ attrs={
82
+ "curves": curves,
83
+ "handle_types": handle_types,
84
+ "use_clip": use_clip,
85
+ "extend": extend,
86
+ "clip_min": clip_min,
87
+ "clip_max": clip_max,
88
+ },
74
89
  )
75
90
 
76
91