procfunc 0.30.0__py3-none-any.whl
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.
- procfunc/__init__.py +87 -0
- procfunc/color.py +57 -0
- procfunc/compute_graph/__init__.py +28 -0
- procfunc/compute_graph/compute_graph.py +115 -0
- procfunc/compute_graph/node.py +200 -0
- procfunc/compute_graph/operators_info.py +92 -0
- procfunc/compute_graph/proxy.py +173 -0
- procfunc/compute_graph/util.py +282 -0
- procfunc/context.py +115 -0
- procfunc/control.py +174 -0
- procfunc/nodes/__init__.py +66 -0
- procfunc/nodes/bindings_util.py +196 -0
- procfunc/nodes/bpy_node_info.py +280 -0
- procfunc/nodes/compositor.py +2242 -0
- procfunc/nodes/execute/construct_nodes.py +571 -0
- procfunc/nodes/execute/construct_special_cases.py +246 -0
- procfunc/nodes/execute/execute.py +548 -0
- procfunc/nodes/execute/infer_runtime_data_type.py +195 -0
- procfunc/nodes/execute/util.py +247 -0
- procfunc/nodes/func.py +1417 -0
- procfunc/nodes/geo.py +4240 -0
- procfunc/nodes/manifest.json +8769 -0
- procfunc/nodes/math.py +644 -0
- procfunc/nodes/node_function.py +160 -0
- procfunc/nodes/shader.py +2359 -0
- procfunc/nodes/types.py +347 -0
- procfunc/ops/__init__.py +35 -0
- procfunc/ops/_util.py +275 -0
- procfunc/ops/addons.py +59 -0
- procfunc/ops/attr.py +426 -0
- procfunc/ops/collection.py +90 -0
- procfunc/ops/curve.py +18 -0
- procfunc/ops/file.py +126 -0
- procfunc/ops/manifest.json +39149 -0
- procfunc/ops/mesh.py +1510 -0
- procfunc/ops/modifier.py +603 -0
- procfunc/ops/object.py +258 -0
- procfunc/ops/primitives/__init__.py +31 -0
- procfunc/ops/primitives/camera.py +45 -0
- procfunc/ops/primitives/curve.py +71 -0
- procfunc/ops/primitives/light.py +114 -0
- procfunc/ops/primitives/mesh.py +358 -0
- procfunc/ops/uv.py +271 -0
- procfunc/random.py +247 -0
- procfunc/tracer/__init__.py +43 -0
- procfunc/tracer/decorator.py +121 -0
- procfunc/tracer/patch.py +494 -0
- procfunc/tracer/proxy.py +127 -0
- procfunc/tracer/trace.py +222 -0
- procfunc/transforms/__init__.py +49 -0
- procfunc/transforms/cleanup.py +214 -0
- procfunc/transforms/convert.py +20 -0
- procfunc/transforms/distribution.py +191 -0
- procfunc/transforms/extract_materials.py +116 -0
- procfunc/transforms/infer_distribution.py +326 -0
- procfunc/transforms/parameters.py +15 -0
- procfunc/transforms/util.py +35 -0
- procfunc/transpiler/__init__.py +24 -0
- procfunc/transpiler/bpy_to_computegraph.py +1348 -0
- procfunc/transpiler/codegen.py +919 -0
- procfunc/transpiler/identifiers.py +595 -0
- procfunc/transpiler/main.py +299 -0
- procfunc/types.py +380 -0
- procfunc/util/__init__.py +0 -0
- procfunc/util/bpy_info.py +145 -0
- procfunc/util/camera.py +0 -0
- procfunc/util/keyframe.py +70 -0
- procfunc/util/log.py +96 -0
- procfunc/util/manifest.py +121 -0
- procfunc/util/pytree.py +343 -0
- procfunc/util/teardown.py +37 -0
- procfunc-0.30.0.dist-info/METADATA +120 -0
- procfunc-0.30.0.dist-info/RECORD +76 -0
- procfunc-0.30.0.dist-info/WHEEL +5 -0
- procfunc-0.30.0.dist-info/licenses/LICENSE.md +11 -0
- procfunc-0.30.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,595 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import re
|
|
3
|
+
from collections import Counter, defaultdict
|
|
4
|
+
from typing import Iterable, Literal, TypeVar
|
|
5
|
+
|
|
6
|
+
import bpy
|
|
7
|
+
|
|
8
|
+
from procfunc import compute_graph as cg
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def pascal_to_snake(name: str) -> str:
|
|
14
|
+
s = re.sub(r"[./\-\s]+", "_", name or "")
|
|
15
|
+
s = re.sub(r"([A-Z]+)([A-Z][a-z])", r"\1_\2", s)
|
|
16
|
+
s = re.sub(r"([a-z0-9])([A-Z])", r"\1_\2", s)
|
|
17
|
+
return "_".join(part.lower() for part in s.split("_") if part)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def snake_to_pascal(name: str) -> str:
|
|
21
|
+
return "".join(part.capitalize() for part in name.split("_"))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def bpy_name_to_pythonid(name: str) -> str:
|
|
25
|
+
# remove .00X
|
|
26
|
+
# name = re.sub(r"\.\d+$", "", name)
|
|
27
|
+
|
|
28
|
+
# blender often pascal
|
|
29
|
+
name = pascal_to_snake(name)
|
|
30
|
+
|
|
31
|
+
# must come after pascal_to_snake or else can jam noncaps together?
|
|
32
|
+
name = name.replace(".", "_")
|
|
33
|
+
name = name.replace(" ", "_")
|
|
34
|
+
name = name.replace(",", "_")
|
|
35
|
+
name = name.replace("=", "")
|
|
36
|
+
|
|
37
|
+
name = name.lower()
|
|
38
|
+
|
|
39
|
+
name = re.sub(r"_+", "_", name).strip("_")
|
|
40
|
+
|
|
41
|
+
# move number terms to end
|
|
42
|
+
parts = name.split("_")
|
|
43
|
+
for i in range(len(parts)):
|
|
44
|
+
if parts[0][0].isdigit():
|
|
45
|
+
parts = parts[1:] + [parts[0]]
|
|
46
|
+
|
|
47
|
+
name = "_".join(parts)
|
|
48
|
+
|
|
49
|
+
return name
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def is_valid_snake_identifier(name: str) -> bool:
|
|
53
|
+
if name == "":
|
|
54
|
+
return False
|
|
55
|
+
if name is None:
|
|
56
|
+
raise ValueError(f"Name {name=!r} is None")
|
|
57
|
+
if "." in name or " " in name:
|
|
58
|
+
return False
|
|
59
|
+
if name[0].isdigit():
|
|
60
|
+
return False
|
|
61
|
+
if name != name.lower():
|
|
62
|
+
return False # this is opinionated
|
|
63
|
+
return True
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _find_reducible(
|
|
67
|
+
curr_names: dict[int, str],
|
|
68
|
+
mode: Literal["prefix", "postfix"] = "postfix",
|
|
69
|
+
separator: str = ".",
|
|
70
|
+
existing: dict[int, str] | None = None,
|
|
71
|
+
limit_min_fields: int = 2,
|
|
72
|
+
min_str_len_reduce: int = 10,
|
|
73
|
+
) -> set[int]:
|
|
74
|
+
new_matched_counts = defaultdict(lambda: [[], 0])
|
|
75
|
+
|
|
76
|
+
if existing is not None:
|
|
77
|
+
for node_id, name in existing.items():
|
|
78
|
+
entry = new_matched_counts[name]
|
|
79
|
+
entry[0].append(node_id)
|
|
80
|
+
entry[1] += 1
|
|
81
|
+
|
|
82
|
+
def _next_name(name: str) -> str:
|
|
83
|
+
return (
|
|
84
|
+
name[name.find(separator) + 1 :]
|
|
85
|
+
if mode == "postfix"
|
|
86
|
+
else name[name.find(separator) + 1 :]
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
def _stop_reduce(newname: str) -> bool:
|
|
90
|
+
return (
|
|
91
|
+
len(newname.split(separator)) <= limit_min_fields
|
|
92
|
+
or len(newname) < min_str_len_reduce
|
|
93
|
+
or not is_valid_snake_identifier(newname)
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
for node_id, name in curr_names.items():
|
|
97
|
+
matchval = _next_name(name)
|
|
98
|
+
if _stop_reduce(matchval):
|
|
99
|
+
matchval = name
|
|
100
|
+
if separator in name:
|
|
101
|
+
new_matched_counts[matchval][0].append(node_id)
|
|
102
|
+
new_matched_counts[matchval][1] += 1
|
|
103
|
+
|
|
104
|
+
continue_reduce_ids = set()
|
|
105
|
+
for name, (ids, count) in new_matched_counts.items():
|
|
106
|
+
if count != 1 or _stop_reduce(_next_name(name)):
|
|
107
|
+
continue
|
|
108
|
+
filtered = [
|
|
109
|
+
nodeid for nodeid in ids if existing is None or nodeid not in existing
|
|
110
|
+
]
|
|
111
|
+
continue_reduce_ids.update(filtered)
|
|
112
|
+
|
|
113
|
+
return continue_reduce_ids
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def duplicate_names(names: dict[int, str]) -> list[str]:
|
|
117
|
+
return [
|
|
118
|
+
(name, count) for name, count in Counter(names.values()).items() if count > 1
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def reduce_name_prefix_suffix(
|
|
123
|
+
names: dict[int, str],
|
|
124
|
+
mode: Literal["prefix", "postfix"] = "postfix",
|
|
125
|
+
separator: str = ".",
|
|
126
|
+
existing: dict[int, str] | None = None,
|
|
127
|
+
) -> dict[int, str]:
|
|
128
|
+
"""
|
|
129
|
+
remove the maximum number of `.`-separated prefix-fields from every name,
|
|
130
|
+
but make sure the names remain as unique as possible.
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
# logger.debug(f"{reduce_name_prefix_suffix.__name__} for {mode=} {separator=} reducing {len(names)=}")
|
|
134
|
+
|
|
135
|
+
curr_names = names.copy()
|
|
136
|
+
|
|
137
|
+
while continue_reduce := _find_reducible(
|
|
138
|
+
curr_names, mode=mode, separator=separator, existing=existing
|
|
139
|
+
):
|
|
140
|
+
logger.debug(f"{reduce_name_prefix_suffix.__name__} reducing ")
|
|
141
|
+
for node_id in continue_reduce:
|
|
142
|
+
# TODO: this is not correct, we need to find the longest prefix that is shared by all nodes in the set.
|
|
143
|
+
if mode == "prefix":
|
|
144
|
+
curr_names[node_id] = curr_names[node_id].split(separator, 1)[1]
|
|
145
|
+
else:
|
|
146
|
+
raise NotImplementedError(f"Unsupported mode: {mode}")
|
|
147
|
+
curr_names[node_id] = curr_names[node_id].split(separator, 1)[0]
|
|
148
|
+
|
|
149
|
+
output_dups = duplicate_names(curr_names)
|
|
150
|
+
if output_dups: # and not duplicate_names(names):
|
|
151
|
+
raise ValueError(
|
|
152
|
+
f"{reduce_name_prefix_suffix.__name__} created duplicate names {output_dups} - "
|
|
153
|
+
f"should be impossible, please contact the developers. input names were {names.values()}"
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
return curr_names
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def apply_panel_names_to_input_names(
|
|
160
|
+
node_tree: bpy.types.NodeTree,
|
|
161
|
+
names: dict[str, str],
|
|
162
|
+
only_dedup: bool = False,
|
|
163
|
+
) -> dict[str, str]:
|
|
164
|
+
"""
|
|
165
|
+
apply the names of the panel sockets to the input names
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
node_tree: the node tree to apply the panel names to
|
|
169
|
+
names: the input names to apply the panel names to
|
|
170
|
+
only_dedup: If True, only add the panel name if there are multiple sockets with the same name
|
|
171
|
+
"""
|
|
172
|
+
|
|
173
|
+
panel_sockets = [
|
|
174
|
+
socket
|
|
175
|
+
for socket in node_tree.interface.items_tree.values()
|
|
176
|
+
if socket.item_type == "PANEL"
|
|
177
|
+
]
|
|
178
|
+
|
|
179
|
+
if len(panel_sockets) == 0:
|
|
180
|
+
return names
|
|
181
|
+
|
|
182
|
+
if logger.isEnabledFor(logging.DEBUG):
|
|
183
|
+
socketnames = [socket.name for socket in panel_sockets]
|
|
184
|
+
logger.debug(
|
|
185
|
+
f"apply_panel_names_to_input_names on {node_tree.name=} {only_dedup=} found panel sockets {socketnames=}"
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
basename_counts = defaultdict(lambda: 0)
|
|
189
|
+
for socket in node_tree.interface.items_tree.values():
|
|
190
|
+
if socket.item_type == "PANEL":
|
|
191
|
+
continue
|
|
192
|
+
basename_counts[socket.name] += 1
|
|
193
|
+
|
|
194
|
+
seen_panel_members = set()
|
|
195
|
+
for panel_socket in panel_sockets:
|
|
196
|
+
for socket in panel_socket.interface_items.values():
|
|
197
|
+
if socket.identifier in seen_panel_members:
|
|
198
|
+
raise ValueError(
|
|
199
|
+
f"Panel socket {socket.identifier=} {socket.name=} appeared in multiple panels. "
|
|
200
|
+
"Contact the developers if this is needed."
|
|
201
|
+
)
|
|
202
|
+
seen_panel_members.add(socket.identifier)
|
|
203
|
+
|
|
204
|
+
# we will leave .-separation for now so that we can try to dedup unnecessary ones
|
|
205
|
+
panel_name = bpy_name_to_pythonid(panel_socket.name)
|
|
206
|
+
socket_name = bpy_name_to_pythonid(names[socket.identifier])
|
|
207
|
+
if only_dedup and basename_counts[socket.name] == 1:
|
|
208
|
+
names[socket.identifier] = socket_name
|
|
209
|
+
else:
|
|
210
|
+
names[socket.identifier] = panel_name + "." + socket_name
|
|
211
|
+
|
|
212
|
+
# any . that werent removed must now become _ to be valid python identifiers
|
|
213
|
+
for k, v in names.items():
|
|
214
|
+
names[k] = v.replace(".", "_")
|
|
215
|
+
|
|
216
|
+
return names
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
TKey = TypeVar("TKey")
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def dedup_names_with_suffix(
|
|
223
|
+
names: dict[TKey, str],
|
|
224
|
+
existing: dict[TKey, str] | None = None,
|
|
225
|
+
separator: str = ".",
|
|
226
|
+
order: Iterable[TKey] | None = None,
|
|
227
|
+
first_use_suffix: bool = False,
|
|
228
|
+
) -> dict[TKey, str]:
|
|
229
|
+
seen_counts: dict[str, int] = {}
|
|
230
|
+
result_names: dict[TKey, str] = {}
|
|
231
|
+
|
|
232
|
+
for nid, name in names.items():
|
|
233
|
+
parts = name.split(separator)
|
|
234
|
+
if len(parts) > 1 and parts[-1].isdigit():
|
|
235
|
+
newname = separator.join(parts[:-1])
|
|
236
|
+
names[nid] = newname
|
|
237
|
+
|
|
238
|
+
total_counts = Counter(names.values())
|
|
239
|
+
|
|
240
|
+
if existing is not None:
|
|
241
|
+
for name in existing.values():
|
|
242
|
+
if not isinstance(name, str):
|
|
243
|
+
continue
|
|
244
|
+
seen_counts[name] = 1
|
|
245
|
+
if "." in name:
|
|
246
|
+
seen_counts[name.split(".")[0]] = 1
|
|
247
|
+
|
|
248
|
+
if order is None:
|
|
249
|
+
order = list(names.keys())
|
|
250
|
+
|
|
251
|
+
for node_id in order:
|
|
252
|
+
if node_id not in names:
|
|
253
|
+
continue
|
|
254
|
+
if existing and node_id in existing:
|
|
255
|
+
continue
|
|
256
|
+
orig_name = names[node_id]
|
|
257
|
+
|
|
258
|
+
count = seen_counts.get(orig_name, 0)
|
|
259
|
+
if count == 0 and not (first_use_suffix and total_counts[orig_name] > 1):
|
|
260
|
+
result_names[node_id] = orig_name
|
|
261
|
+
else:
|
|
262
|
+
newname = f"{orig_name}{separator}{count}"
|
|
263
|
+
while newname in seen_counts:
|
|
264
|
+
count += 1
|
|
265
|
+
newname = f"{orig_name}{separator}{count}"
|
|
266
|
+
seen_counts[newname] = count + 1
|
|
267
|
+
result_names[node_id] = newname
|
|
268
|
+
|
|
269
|
+
seen_counts[orig_name] = count + 1
|
|
270
|
+
|
|
271
|
+
if dups := duplicate_names(result_names):
|
|
272
|
+
raise ValueError(
|
|
273
|
+
f"{dedup_names_with_suffix.__name__} created duplicate names {dups} - "
|
|
274
|
+
"should be impossible, please contact the developers. "
|
|
275
|
+
f"{result_names.values()=}"
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
return result_names
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def _propogate_one_step(
|
|
282
|
+
child: cg.Node,
|
|
283
|
+
parent: cg.Node | None,
|
|
284
|
+
argname: str | int,
|
|
285
|
+
node_names_parts: dict[int, list[str]],
|
|
286
|
+
limit_n_fields: int | None,
|
|
287
|
+
usages: dict[int, list[cg.Node]],
|
|
288
|
+
fold_map: dict[int, bool] | None,
|
|
289
|
+
skip_propogate_words: list[str] | None = None,
|
|
290
|
+
):
|
|
291
|
+
if parent is None:
|
|
292
|
+
assert isinstance(argname, str), (argname, parent, child)
|
|
293
|
+
return [argname]
|
|
294
|
+
elif (
|
|
295
|
+
fold_map is not None
|
|
296
|
+
and fold_map.get(id(parent), False)
|
|
297
|
+
and id(parent) not in node_names_parts
|
|
298
|
+
):
|
|
299
|
+
assert isinstance(argname, str), (argname, parent, child)
|
|
300
|
+
return [argname]
|
|
301
|
+
elif id(parent) not in node_names_parts:
|
|
302
|
+
raise ValueError(
|
|
303
|
+
f"Visited {id(child)} as {argname=} of {id(parent)} {parent=} before parent was named?"
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
parent_parts = node_names_parts[id(parent)]
|
|
307
|
+
|
|
308
|
+
while len(parent_parts) > 1 and parent_parts[-1] in skip_propogate_words:
|
|
309
|
+
parent_parts = parent_parts[:-1]
|
|
310
|
+
|
|
311
|
+
if limit_n_fields is not None and len(parent_parts) > limit_n_fields - 1:
|
|
312
|
+
prefix = parent_parts[0] if parent_parts[0] != "result" else parent_parts[1]
|
|
313
|
+
return [prefix, argname]
|
|
314
|
+
elif fold_map is not None and all(
|
|
315
|
+
fold_map.get(id(usage_parent), False) for usage_parent in usages[id(child)]
|
|
316
|
+
):
|
|
317
|
+
# no need to have a different name from parent if that parent name is always folded / never used
|
|
318
|
+
return parent_parts
|
|
319
|
+
# elif (
|
|
320
|
+
# len(usages[id(child)]) == 1
|
|
321
|
+
# and child.kind == parent.kind
|
|
322
|
+
# and child.target == parent.target
|
|
323
|
+
# ):
|
|
324
|
+
# # repeatedly applying a function to the same variable can reuse the same name
|
|
325
|
+
# return parent_parts
|
|
326
|
+
else:
|
|
327
|
+
return parent_parts + [argname]
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def propogate_names_with_parts(
|
|
331
|
+
graph: cg.ComputeGraph,
|
|
332
|
+
fixed_names: dict[int, str] | None = None,
|
|
333
|
+
seen_subgraphs: set[int] | None = None,
|
|
334
|
+
limit_n_fields: int | None = None,
|
|
335
|
+
fold_map: dict[int, bool] | None = None,
|
|
336
|
+
skip_propogate_words: list[str] | None = None,
|
|
337
|
+
) -> dict[int, list[str]]:
|
|
338
|
+
# logger.debug(f"{propogate_names_with_parts.__name__} for {graph.name}")
|
|
339
|
+
|
|
340
|
+
node_names_parts: dict[int, list[str]] = {}
|
|
341
|
+
fixed_name_ids: set[int] = set()
|
|
342
|
+
|
|
343
|
+
if fixed_names is not None:
|
|
344
|
+
for nid, v in fixed_names.items():
|
|
345
|
+
node_names_parts[nid] = list(v.split("_"))
|
|
346
|
+
fixed_name_ids.add(nid)
|
|
347
|
+
|
|
348
|
+
for i, (name, outnode) in enumerate(graph.outputs.items()):
|
|
349
|
+
if name == "":
|
|
350
|
+
if len(graph.outputs) == 1:
|
|
351
|
+
name = graph.name + "_result"
|
|
352
|
+
name = f"result_{i}"
|
|
353
|
+
node_names_parts[id(outnode)] = [name]
|
|
354
|
+
|
|
355
|
+
if seen_subgraphs is None:
|
|
356
|
+
seen_subgraphs = set()
|
|
357
|
+
|
|
358
|
+
usages = cg.usages_per_node(graph)
|
|
359
|
+
|
|
360
|
+
for argname, parent, child in cg.traverse_breadth_first(
|
|
361
|
+
graph,
|
|
362
|
+
yield_parent=True,
|
|
363
|
+
yield_name=True,
|
|
364
|
+
):
|
|
365
|
+
if parent is None:
|
|
366
|
+
continue
|
|
367
|
+
|
|
368
|
+
assert isinstance(parent, cg.Node), (parent, child, argname)
|
|
369
|
+
assert argname is not None, (parent, child, argname)
|
|
370
|
+
|
|
371
|
+
result = _propogate_one_step(
|
|
372
|
+
child,
|
|
373
|
+
parent,
|
|
374
|
+
argname,
|
|
375
|
+
node_names_parts,
|
|
376
|
+
limit_n_fields,
|
|
377
|
+
usages,
|
|
378
|
+
fold_map,
|
|
379
|
+
skip_propogate_words=skip_propogate_words,
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
resname = "_".join(result)
|
|
383
|
+
if not is_valid_snake_identifier(resname):
|
|
384
|
+
parent_name = "_".join(node_names_parts.get(id(parent), []))
|
|
385
|
+
logger.warning(
|
|
386
|
+
f"Propogate from {parent} {parent_name=} to {child=} produced invalid identifier {resname=}"
|
|
387
|
+
)
|
|
388
|
+
continue
|
|
389
|
+
|
|
390
|
+
if id(child) not in node_names_parts:
|
|
391
|
+
node_names_parts[id(child)] = result
|
|
392
|
+
elif id(child) not in fixed_name_ids:
|
|
393
|
+
newlen = sum(len(x) for x in result)
|
|
394
|
+
oldlen = sum(len(x) for x in node_names_parts[id(child)])
|
|
395
|
+
if newlen < oldlen:
|
|
396
|
+
node_names_parts[id(child)] = result
|
|
397
|
+
|
|
398
|
+
return node_names_parts
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
def _infill_names_propogate(
|
|
402
|
+
graph: cg.ComputeGraph,
|
|
403
|
+
node_names: dict[int, str],
|
|
404
|
+
fold_map: dict[int, bool],
|
|
405
|
+
existing: dict[int, str],
|
|
406
|
+
skip_propogate_words: list[str] | None = None,
|
|
407
|
+
):
|
|
408
|
+
propogated = propogate_names_with_parts(
|
|
409
|
+
graph,
|
|
410
|
+
fixed_names=node_names,
|
|
411
|
+
limit_n_fields=4,
|
|
412
|
+
fold_map=fold_map,
|
|
413
|
+
skip_propogate_words=skip_propogate_words,
|
|
414
|
+
)
|
|
415
|
+
|
|
416
|
+
propogated_names = {
|
|
417
|
+
id: "_".join(parts)
|
|
418
|
+
for id, parts in propogated.items()
|
|
419
|
+
if not fold_map.get(id, False)
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
propogated_names = dedup_names_with_suffix(
|
|
423
|
+
propogated_names,
|
|
424
|
+
existing={**existing, **node_names},
|
|
425
|
+
separator="_",
|
|
426
|
+
order=reversed([id(n) for n in cg.traverse_depth_first(graph)]),
|
|
427
|
+
first_use_suffix=True,
|
|
428
|
+
)
|
|
429
|
+
|
|
430
|
+
# propogated_names = reduce_name_prefix_suffix(
|
|
431
|
+
# propogated_names,
|
|
432
|
+
# mode="prefix",
|
|
433
|
+
# separator="_",
|
|
434
|
+
# existing={**existing, **node_names},
|
|
435
|
+
# )
|
|
436
|
+
|
|
437
|
+
if intersection := set(propogated_names.values()).intersection(
|
|
438
|
+
set(node_names.values())
|
|
439
|
+
):
|
|
440
|
+
raise ValueError(f"Propogated and node names had overlap: {intersection=}")
|
|
441
|
+
|
|
442
|
+
return propogated_names
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
def _name_from_functionality(node: cg.Node) -> str:
|
|
446
|
+
match node:
|
|
447
|
+
case cg.FunctionCallNode(func=func):
|
|
448
|
+
return func.__name__
|
|
449
|
+
case cg.MethodCallNode(method_name=method_name):
|
|
450
|
+
return method_name
|
|
451
|
+
case cg.SubgraphCallNode(subgraph=subgraph):
|
|
452
|
+
return subgraph.name
|
|
453
|
+
case cg.GetAttributeNode(attribute_name=attribute_name):
|
|
454
|
+
return attribute_name
|
|
455
|
+
case cg.MutatedArgumentNode():
|
|
456
|
+
return _name_from_functionality(node.args[0])
|
|
457
|
+
case cg.ConstantNode():
|
|
458
|
+
return "const"
|
|
459
|
+
case cg.InputPlaceholderNode(input_name=name):
|
|
460
|
+
return name if name else "input"
|
|
461
|
+
case _:
|
|
462
|
+
raise NotImplementedError(f"Unsupported node: {node}")
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
def _infill_names_function(
|
|
466
|
+
graph: cg.ComputeGraph,
|
|
467
|
+
node_names: dict[int, str],
|
|
468
|
+
fold_map: dict[int, bool],
|
|
469
|
+
existing: dict[int, str],
|
|
470
|
+
):
|
|
471
|
+
result = {}
|
|
472
|
+
for node in cg.traverse_depth_first(graph):
|
|
473
|
+
if id(node) in existing:
|
|
474
|
+
continue
|
|
475
|
+
if fold_map[id(node)]:
|
|
476
|
+
continue
|
|
477
|
+
if node_names.get(id(node), None) is not None:
|
|
478
|
+
continue
|
|
479
|
+
result[id(node)] = _name_from_functionality(node)
|
|
480
|
+
|
|
481
|
+
result = dedup_names_with_suffix(
|
|
482
|
+
result,
|
|
483
|
+
existing={**existing, **node_names},
|
|
484
|
+
separator="_",
|
|
485
|
+
order=reversed([id(n) for n in cg.traverse_depth_first(graph)]),
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
if intersection := set(result.values()).intersection(set(node_names.values())):
|
|
489
|
+
raise ValueError(f"Function and node names had overlap: {intersection=}")
|
|
490
|
+
|
|
491
|
+
return result
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
def _fixed_name_for_node(
|
|
495
|
+
node: cg.Node,
|
|
496
|
+
scope_expressions: dict[int, str],
|
|
497
|
+
avoid_parts: list[str] = [],
|
|
498
|
+
) -> str | None:
|
|
499
|
+
"""
|
|
500
|
+
If `node` is significant enough to be given a name, rather than being named after
|
|
501
|
+
its later usage, then we will return a str name for it here
|
|
502
|
+
"""
|
|
503
|
+
|
|
504
|
+
match node:
|
|
505
|
+
case cg.Node(metadata={"varname": varname}):
|
|
506
|
+
return varname
|
|
507
|
+
case cg.SubgraphCallNode(subgraph=subgraph):
|
|
508
|
+
return subgraph.name + "_result"
|
|
509
|
+
case cg.FunctionCallNode(func=func):
|
|
510
|
+
func_resolve = scope_expressions.get(id(func), None)
|
|
511
|
+
module = getattr(func, "__module__", "") or ""
|
|
512
|
+
if not isinstance(func_resolve, str):
|
|
513
|
+
return None
|
|
514
|
+
if not (
|
|
515
|
+
".geo." in func_resolve
|
|
516
|
+
or ".shader." in func_resolve
|
|
517
|
+
or module.startswith("infinigen_v2.")
|
|
518
|
+
):
|
|
519
|
+
return None
|
|
520
|
+
name = "_".join(
|
|
521
|
+
part for part in func.__name__.split("_") if part not in avoid_parts
|
|
522
|
+
)
|
|
523
|
+
if "." in func_resolve:
|
|
524
|
+
module_alias = func_resolve.rsplit(".", 1)[0]
|
|
525
|
+
if name == module_alias:
|
|
526
|
+
name = name + "_result"
|
|
527
|
+
return name
|
|
528
|
+
case _:
|
|
529
|
+
return None
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
NONDESCRIPTIVE_NODE_NAME_PARTS = [
|
|
533
|
+
"mesh",
|
|
534
|
+
"geometry",
|
|
535
|
+
"bsdf",
|
|
536
|
+
"result",
|
|
537
|
+
"distribution",
|
|
538
|
+
]
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
def nodenames_from_fixed_and_infill(
|
|
542
|
+
graph: cg.ComputeGraph,
|
|
543
|
+
fold_map: dict[int, bool],
|
|
544
|
+
scope_expressions: dict[int, str],
|
|
545
|
+
avoid_parts: list[str] | None = None,
|
|
546
|
+
) -> dict[int, str]:
|
|
547
|
+
if avoid_parts is None:
|
|
548
|
+
avoid_parts = NONDESCRIPTIVE_NODE_NAME_PARTS
|
|
549
|
+
|
|
550
|
+
node_names = {}
|
|
551
|
+
|
|
552
|
+
for name, output in graph.outputs.items():
|
|
553
|
+
if name == "":
|
|
554
|
+
name = _name_from_functionality(output)
|
|
555
|
+
if not fold_map.get(id(output), False):
|
|
556
|
+
node_names[id(output)] = name
|
|
557
|
+
|
|
558
|
+
for name, input in graph.inputs.items():
|
|
559
|
+
node_names[id(input)] = name
|
|
560
|
+
|
|
561
|
+
for node in cg.traverse_depth_first(graph):
|
|
562
|
+
if fold_map.get(id(node), False):
|
|
563
|
+
continue
|
|
564
|
+
if name := _fixed_name_for_node(
|
|
565
|
+
node, scope_expressions, avoid_parts=avoid_parts
|
|
566
|
+
):
|
|
567
|
+
node_names[id(node)] = name
|
|
568
|
+
|
|
569
|
+
node_names = dedup_names_with_suffix(
|
|
570
|
+
node_names,
|
|
571
|
+
existing=scope_expressions,
|
|
572
|
+
separator="_",
|
|
573
|
+
)
|
|
574
|
+
|
|
575
|
+
# node_names = reduce_name_prefix_suffix(
|
|
576
|
+
# node_names,
|
|
577
|
+
# mode="prefix",
|
|
578
|
+
# separator="_",
|
|
579
|
+
# existing=scope_expressions,
|
|
580
|
+
# )
|
|
581
|
+
|
|
582
|
+
result = _infill_names_propogate(
|
|
583
|
+
graph,
|
|
584
|
+
node_names,
|
|
585
|
+
fold_map,
|
|
586
|
+
existing=scope_expressions,
|
|
587
|
+
skip_propogate_words=avoid_parts,
|
|
588
|
+
)
|
|
589
|
+
result.update(node_names)
|
|
590
|
+
|
|
591
|
+
for name in result.values():
|
|
592
|
+
if not is_valid_snake_identifier(name):
|
|
593
|
+
logger.warning(f"Invalid node name: {name}")
|
|
594
|
+
|
|
595
|
+
return result
|