chore: snapshot workflow migration progress

This commit is contained in:
2026-04-12 11:49:04 +02:00
parent 0cd02513d5
commit 3e810c74a3
163 changed files with 31774 additions and 2753 deletions
@@ -0,0 +1,183 @@
"""Generic template-input application for Blender template scenes.
Template inputs are exposed to the scene via custom properties and optional
visibility/selection markers. This keeps legacy templates untouched while
allowing graph workflows to pass structured overrides into `.blend` scenes.
"""
from __future__ import annotations
import json
import re
from typing import Any
import bpy # type: ignore[import]
_MARKER_PROP_NAMES = (
"hartomat_template_input",
"hartomat.template_input",
"template_input",
"schaeffler_template_input",
)
_MARKER_KEY_PROP_NAMES = (
"hartomat_template_input_key",
"hartomat.template_input_key",
"template_input_key",
"schaeffler_template_input_key",
)
_MARKER_VALUE_PROP_NAMES = (
"hartomat_template_input_value",
"hartomat.template_input_value",
"template_input_value",
"schaeffler_template_input_value",
)
_NAME_PATTERNS = (
re.compile(r"template_input__(?P<key>[^_]+)__(?P<value>[^_]+)", re.IGNORECASE),
re.compile(r"template-input:(?P<key>[^=]+)=(?P<value>.+)", re.IGNORECASE),
re.compile(r"ti::(?P<key>[^:]+)::(?P<value>.+)", re.IGNORECASE),
)
def _normalize_template_inputs(template_inputs: dict[str, Any] | None) -> dict[str, str]:
normalized: dict[str, str] = {}
for raw_key, raw_value in (template_inputs or {}).items():
key = str(raw_key or "").strip()
if not key or raw_value is None:
continue
if isinstance(raw_value, bool):
value = "true" if raw_value else "false"
else:
value = str(raw_value).strip()
if value:
normalized[key] = value
return normalized
def _scene_targets():
yield ("collection", bpy.data.collections)
yield ("object", bpy.data.objects)
yield ("world", bpy.data.worlds)
def _extract_marker(target) -> tuple[str, str] | None:
for prop_name in _MARKER_PROP_NAMES:
raw = target.get(prop_name)
if not raw:
continue
if isinstance(raw, str):
text = raw.strip()
if not text:
continue
if text.startswith("{"):
try:
payload = json.loads(text)
except Exception:
payload = None
if isinstance(payload, dict):
key = str(payload.get("key", "")).strip()
value = str(payload.get("value", "")).strip()
if key and value:
return key, value
if "=" in text:
key, value = text.split("=", 1)
key = key.strip()
value = value.strip()
if key and value:
return key, value
key = None
value = None
for prop_name in _MARKER_KEY_PROP_NAMES:
raw = target.get(prop_name)
if raw:
key = str(raw).strip()
if key:
break
for prop_name in _MARKER_VALUE_PROP_NAMES:
raw = target.get(prop_name)
if raw is not None:
value = str(raw).strip()
if value:
break
if key and value:
return key, value
name = getattr(target, "name", "") or ""
for pattern in _NAME_PATTERNS:
match = pattern.search(name)
if not match:
continue
key = match.group("key").strip()
value = match.group("value").strip()
if key and value:
return key, value
return None
def _find_layer_collection(layer_collection, collection_name: str):
if layer_collection.collection.name == collection_name:
return layer_collection
for child in layer_collection.children:
found = _find_layer_collection(child, collection_name)
if found is not None:
return found
return None
def _apply_collection_visibility(collection, *, enabled: bool) -> None:
collection.hide_render = not enabled
collection.hide_viewport = not enabled
for view_layer in bpy.context.scene.view_layers:
layer_collection = _find_layer_collection(view_layer.layer_collection, collection.name)
if layer_collection is not None:
layer_collection.exclude = not enabled
layer_collection.hide_viewport = not enabled
def _apply_object_visibility(obj, *, enabled: bool) -> None:
obj.hide_render = not enabled
obj.hide_viewport = not enabled
try:
obj.hide_set(not enabled)
except Exception:
pass
def _apply_world_selection(world, *, enabled: bool) -> None:
if enabled:
bpy.context.scene.world = world
def apply_template_inputs(template_inputs: dict[str, Any] | None) -> None:
normalized = _normalize_template_inputs(template_inputs)
if not normalized:
return
scene = bpy.context.scene
for key, value in normalized.items():
scene[f"template_input__{key}"] = value
scene[f"hartomat_template_input__{key}"] = value
matched_targets = 0
for kind, targets in _scene_targets():
for target in targets:
marker = _extract_marker(target)
if marker is None:
continue
key, expected_value = marker
if key not in normalized:
continue
enabled = normalized[key] == expected_value
matched_targets += 1
if kind == "collection":
_apply_collection_visibility(target, enabled=enabled)
elif kind == "object":
_apply_object_visibility(target, enabled=enabled)
elif kind == "world":
_apply_world_selection(target, enabled=enabled)
print(
f"[blender_render] applied template_inputs keys={sorted(normalized)} matched_targets={matched_targets}",
flush=True,
)