258 lines
11 KiB
Python
258 lines
11 KiB
Python
from __future__ import annotations
|
|
import copy
|
|
import re
|
|
from typing import Any
|
|
import numpy as np
|
|
|
|
from ...infra.blender.blender_mesh_capture import PdfProperties, SlicingCollection, SlicingGroup, SlicingObject
|
|
from ..slice import SlicePaths
|
|
from ..pdf_exporter.geometry import transform_slice_paths
|
|
from ..geometry import update_transform
|
|
from ..pdf_exporter.metadata import get_first_attribute_value, resolve_object_properties
|
|
|
|
|
|
def _copy_slicing_object(so: SlicingObject) -> SlicingObject:
|
|
new_so = SlicingObject()
|
|
new_so.name = so.name
|
|
new_so.parent = so.parent
|
|
new_so.mesh = so.mesh.copy() if so.mesh is not None else np.empty((0, 4, 3))
|
|
new_so.attributes = {}
|
|
for k, v in so.attributes.items():
|
|
if isinstance(v, dict):
|
|
new_so.attributes[k] = {
|
|
"domain": v.get("domain", "face"),
|
|
"type": v.get("type", "string"),
|
|
"values": v["values"].copy() if isinstance(v.get("values"), np.ndarray) else v.get("values")
|
|
}
|
|
elif isinstance(v, np.ndarray):
|
|
new_so.attributes[k] = v.copy()
|
|
elif isinstance(v, list):
|
|
new_so.attributes[k] = list(v)
|
|
else:
|
|
new_so.attributes[k] = copy.deepcopy(v)
|
|
new_so.transforms = [dict(t) for t in getattr(so, "transforms", [])]
|
|
new_so.pdf_properties = copy.deepcopy(so.pdf_properties) if hasattr(so, "pdf_properties") and so.pdf_properties else PdfProperties()
|
|
new_so.pdf_object_properties = list(getattr(so, "pdf_object_properties", []))
|
|
return new_so
|
|
|
|
|
|
def _copy_slicing_collection(sc: SlicingCollection) -> SlicingCollection:
|
|
new_sc = SlicingCollection([], sc.name)
|
|
new_sc.objects = [_copy_slicing_object(so) for so in sc.objects]
|
|
new_sc.transforms = [dict(t) for t in getattr(sc, "transforms", [])]
|
|
return new_sc
|
|
|
|
|
|
def _copy_slicing_group(sg: SlicingGroup) -> SlicingGroup:
|
|
new_sg = SlicingGroup([])
|
|
new_sg.collections = {k: _copy_slicing_collection(sc) for k, sc in sg.collections.items()}
|
|
new_sg.transforms = [dict(t) for t in getattr(sg, "transforms", [])]
|
|
return new_sg
|
|
|
|
|
|
def _transform_geometry_item(geom: Any, t_arr: np.ndarray, r_arr: np.ndarray, s_arr: np.ndarray) -> Any:
|
|
if geom is None:
|
|
return None
|
|
elif isinstance(geom, SlicePaths):
|
|
copied_data = transform_slice_paths(geom, t_arr, r_arr, s_arr)
|
|
copied_data.transforms = update_transform(getattr(geom, "transforms", []), t_arr, r_arr, s_arr)
|
|
return copied_data
|
|
elif isinstance(geom, (list, tuple)):
|
|
return [_transform_geometry_item(item, t_arr, r_arr, s_arr) for item in geom]
|
|
elif isinstance(geom, SlicingGroup):
|
|
copied_data = _copy_slicing_group(geom)
|
|
for col in copied_data.collections.values():
|
|
if any(s != 1.0 for s in s_arr):
|
|
col.scale(s_arr)
|
|
if any(r != 0.0 for r in r_arr):
|
|
col.rotate(r_arr)
|
|
if any(t != 0.0 for t in t_arr):
|
|
col.offset(t_arr)
|
|
col.transforms = update_transform(getattr(col, "transforms", []), t_arr, r_arr, s_arr)
|
|
for so in col.objects:
|
|
so.transforms = update_transform(getattr(so, "transforms", []), t_arr, r_arr, s_arr)
|
|
|
|
copied_data.transforms = update_transform(getattr(geom, "transforms", []), t_arr, r_arr, s_arr)
|
|
return copied_data
|
|
elif isinstance(geom, SlicingCollection):
|
|
copied_data = _copy_slicing_collection(geom)
|
|
if any(s != 1.0 for s in s_arr):
|
|
copied_data.scale(s_arr)
|
|
if any(r != 0.0 for r in r_arr):
|
|
copied_data.rotate(r_arr)
|
|
if any(t != 0.0 for t in t_arr):
|
|
copied_data.offset(t_arr)
|
|
|
|
copied_data.transforms = update_transform(getattr(geom, "transforms", []), t_arr, r_arr, s_arr)
|
|
for so in copied_data.objects:
|
|
so.transforms = update_transform(getattr(so, "transforms", []), t_arr, r_arr, s_arr)
|
|
return copied_data
|
|
elif isinstance(geom, SlicingObject):
|
|
copied_data = _copy_slicing_object(geom)
|
|
if any(s != 1.0 for s in s_arr):
|
|
copied_data.scale(s_arr)
|
|
if any(r != 0.0 for r in r_arr):
|
|
copied_data.rotate(r_arr)
|
|
if any(t != 0.0 for t in t_arr):
|
|
copied_data.offset(t_arr)
|
|
|
|
copied_data.transforms = update_transform(getattr(geom, "transforms", []), t_arr, r_arr, s_arr)
|
|
return copied_data
|
|
elif hasattr(geom, "location") and hasattr(geom, "rotation_euler") and hasattr(geom, "scale") and not hasattr(geom, "mesh"):
|
|
from ...infra.blender.blender_slice_nodes_generic import object_to_mesh
|
|
mesh_geom = object_to_mesh(geom, include_children=True)
|
|
return _transform_geometry_item(mesh_geom, t_arr, r_arr, s_arr)
|
|
return geom
|
|
|
|
|
|
def format_name_template(template: str, so: Any, default_old_name: str = "") -> str:
|
|
if not template or "{" not in template or "}" not in template:
|
|
return template
|
|
|
|
def replace_match(match: re.Match[str]) -> str:
|
|
attr_name = match.group(1).strip()
|
|
val = get_first_attribute_value(so, attr_name)
|
|
if val is None and attr_name in ("Name", "name"):
|
|
val = getattr(so, "name", "") or default_old_name
|
|
if val is None:
|
|
return ""
|
|
if isinstance(val, float):
|
|
return f"{val:g}"
|
|
return str(val)
|
|
|
|
return re.sub(r"\{([^}]+)\}", replace_match, template)
|
|
|
|
|
|
def _rename_slicing_item(geom: Any, new_name: str) -> Any:
|
|
if geom is None or not new_name:
|
|
return geom
|
|
elif isinstance(geom, SlicePaths):
|
|
copied = geom.copy_with(paths=list(geom))
|
|
target_name = format_name_template(new_name, copied, default_old_name=getattr(copied, "name", ""))
|
|
copied.name = target_name
|
|
return copied
|
|
elif isinstance(geom, (list, tuple)):
|
|
return [_rename_slicing_item(item, f"{new_name}_{i}" if (len(geom) > 1 and "{" not in new_name) else new_name) for i, item in enumerate(geom)]
|
|
elif isinstance(geom, SlicingGroup):
|
|
copied = _copy_slicing_group(geom)
|
|
old_colls = dict(copied.collections)
|
|
new_colls = {}
|
|
for i, (old_key, coll) in enumerate(old_colls.items()):
|
|
coll_old_name = getattr(coll, "name", old_key)
|
|
target_name = format_name_template(new_name, coll, default_old_name=coll_old_name)
|
|
coll_new_name = f"{target_name}_{i}" if (len(old_colls) > 1 and "{" not in new_name and target_name == new_name) else target_name
|
|
new_colls[coll_new_name] = _rename_slicing_item(coll, coll_new_name)
|
|
copied.collections = new_colls
|
|
return copied
|
|
elif isinstance(geom, SlicingCollection):
|
|
copied = _copy_slicing_collection(geom)
|
|
target_name = format_name_template(new_name, copied, default_old_name=getattr(copied, "name", ""))
|
|
copied.name = target_name
|
|
for i, so in enumerate(copied.objects):
|
|
so.parent = target_name
|
|
so_old_name = getattr(so, "name", "")
|
|
so_target = format_name_template(new_name, so, default_old_name=so_old_name)
|
|
so.name = f"{so_target}_{i}" if (len(copied.objects) > 1 and "{" not in new_name and so_target == new_name) else so_target
|
|
return copied
|
|
elif isinstance(geom, SlicingObject):
|
|
copied = _copy_slicing_object(geom)
|
|
target_name = format_name_template(new_name, copied, default_old_name=getattr(copied, "name", ""))
|
|
copied.name = target_name
|
|
return copied
|
|
return geom
|
|
|
|
|
|
def _assign_pdf_properties_to_item(geom: Any, line_width: float, line_color: tuple[float, float, float] | None, show_name: bool) -> Any:
|
|
if geom is None:
|
|
return None
|
|
elif isinstance(geom, SlicePaths):
|
|
copied = geom.copy_with(paths=list(geom))
|
|
copied.pdf_properties = PdfProperties(line_width=line_width, line_color=line_color, show_name=show_name)
|
|
return copied
|
|
elif isinstance(geom, (list, tuple)):
|
|
return [_assign_pdf_properties_to_item(item, line_width, line_color, show_name) for item in geom]
|
|
elif isinstance(geom, SlicingGroup):
|
|
copied = _copy_slicing_group(geom)
|
|
new_colls = {}
|
|
for k, coll in copied.collections.items():
|
|
new_colls[k] = _assign_pdf_properties_to_item(coll, line_width, line_color, show_name)
|
|
copied.collections = new_colls
|
|
return copied
|
|
elif isinstance(geom, SlicingCollection):
|
|
copied = _copy_slicing_collection(geom)
|
|
copied.objects = [_assign_pdf_properties_to_item(so, line_width, line_color, show_name) for so in copied.objects]
|
|
return copied
|
|
elif isinstance(geom, SlicingObject):
|
|
copied = _copy_slicing_object(geom)
|
|
copied.pdf_properties = PdfProperties(line_width=line_width, line_color=line_color, show_name=show_name)
|
|
return copied
|
|
return geom
|
|
|
|
|
|
def _parse_settings_props(raw_settings: Any) -> list[tuple[str, str]]:
|
|
props: list[tuple[str, str]] = []
|
|
if raw_settings:
|
|
if isinstance(raw_settings, dict):
|
|
for k, v in raw_settings.items():
|
|
props.append((str(k), str(v)))
|
|
elif isinstance(raw_settings, (list, tuple)):
|
|
for item in raw_settings:
|
|
if isinstance(item, tuple) and len(item) == 2:
|
|
props.append((str(item[0]), str(item[1])))
|
|
elif isinstance(item, dict):
|
|
for k, v in item.items():
|
|
props.append((str(k), str(v)))
|
|
else:
|
|
props.append(("", str(item)))
|
|
return props
|
|
|
|
|
|
def _is_single_settings_spec(val: Any) -> bool:
|
|
if isinstance(val, dict):
|
|
return True
|
|
if isinstance(val, (list, tuple)):
|
|
if len(val) > 0 and all(isinstance(x, tuple) and len(x) == 2 and not isinstance(x[0], (list, dict, tuple)) for x in val):
|
|
return True
|
|
if len(val) == 0:
|
|
return True
|
|
return False
|
|
return True
|
|
|
|
|
|
def _assign_properties_to_item(geom: Any, props: list[tuple[str, str]]) -> Any:
|
|
if geom is None:
|
|
return None
|
|
elif isinstance(geom, SlicePaths):
|
|
copied = geom.copy_with(paths=list(geom))
|
|
copied.pdf_object_properties = resolve_object_properties(copied, props)
|
|
return copied
|
|
elif isinstance(geom, (list, tuple)):
|
|
return [_assign_properties_to_item(item, props) for item in geom]
|
|
elif isinstance(geom, SlicingGroup):
|
|
copied = _copy_slicing_group(geom)
|
|
for coll in copied.collections.values():
|
|
for obj in coll.objects:
|
|
obj.pdf_object_properties = resolve_object_properties(obj, props)
|
|
return copied
|
|
elif isinstance(geom, SlicingCollection):
|
|
copied = _copy_slicing_collection(geom)
|
|
for obj in copied.objects:
|
|
obj.pdf_object_properties = resolve_object_properties(obj, props)
|
|
return copied
|
|
elif isinstance(geom, SlicingObject):
|
|
copied = _copy_slicing_object(geom)
|
|
copied.pdf_object_properties = resolve_object_properties(geom, props)
|
|
return copied
|
|
return geom
|
|
|
|
|
|
# Public aliases
|
|
copy_slicing_object = _copy_slicing_object
|
|
copy_slicing_collection = _copy_slicing_collection
|
|
copy_slicing_group = _copy_slicing_group
|
|
transform_geometry_item = _transform_geometry_item
|
|
rename_slicing_item = _rename_slicing_item
|
|
assign_pdf_properties_to_item = _assign_pdf_properties_to_item
|
|
parse_settings_props = _parse_settings_props
|
|
is_single_settings_spec = _is_single_settings_spec
|
|
assign_properties_to_item = _assign_properties_to_item
|