from __future__ import annotations from typing import Any import numpy as np from ...infra.blender.blender_mesh_capture import SlicingCollection, SlicingGroup, SlicingObject from ..slice import SlicePaths, slice_at_range def _finish_slice_paths( merged_object: Any, coll: Any, objects: list[Any], z_values: np.ndarray, rotation: Any, ignore_holes: bool, fl_height: float | None, layer_height: float, ) -> SlicePaths: import sys path_mod = sys.modules.get("NodeSlicer.core.nodeslicer.nodes.path") or sys.modules.get("core.nodeslicer.nodes.path") sar_func = getattr(path_mod, "slice_at_range", slice_at_range) if path_mod else slice_at_range slices_group: list[list[np.ndarray]] | None = sar_func( [merged_object.mesh], z_values, ignore_holes=ignore_holes ) if not slices_group or not slices_group[0]: return SlicePaths() paths = slices_group[0] z_to_group: dict[int, int] = {} groups = [] for p in paths: if len(p) > 0: z_k = int(round(p[0, 2] * 1000)) if z_k not in z_to_group: z_to_group[z_k] = len(z_to_group) groups.append(z_to_group[z_k]) else: groups.append(0) slices = SlicePaths(paths, groups=groups) slices.layer_height = layer_height if fl_height is not None: slices.first_layer_height = fl_height slices.transforms = list(getattr(coll, "transforms", [])) or list(getattr(merged_object, "transforms", [])) names = [] if objects: for o in objects: n = getattr(o, "name", "") if n: if n.endswith("_merged"): n = n[:-7] if n and n not in names: names.append(n) if not names and hasattr(coll, "name") and getattr(coll, "name"): names.append(str(getattr(coll, "name"))) if names: slices.name = names[0] slices.objects_info = {"objects": [{"name": n, "polygon": []} for n in names]} if len(z_values) > 0 and fl_height is not None and layer_height > 0: mult = fl_height / layer_height z_first_um = int(round(z_values[0] * 1000)) for i, p in enumerate(slices): if len(p) > 0 and int(round(p[0, 2] * 1000)) == z_first_um: slices.extrusion_rate[i] = slices.extrusion_rate[i] * mult else: break has_rot = bool(rotation and any(rotation)) if has_rot: from mathutils import Euler mat_inv = np.array(Euler(rotation).to_matrix()) slices = slices.transform(mat_inv) slices = slices.align_seams(mode="sharpest") return slices def _slice_single_mesh_item(coll: Any, layer_height: float, fl_height: float | None, rotation: Any, ignore_holes: bool) -> SlicePaths: if not coll: return SlicePaths() # Handle SlicingGroup, SlicingCollection, SlicingObject if isinstance(coll, SlicingGroup): objects = [] for c in coll.collections.values(): objects.extend(c.objects) elif isinstance(coll, SlicingCollection): objects = list(coll.objects) elif isinstance(coll, SlicingObject): objects = [coll] else: objects = getattr(coll, "objects", [coll] if hasattr(coll, "mesh") else []) merged_object = SlicingObject.merge(objects) if merged_object.mesh is None or merged_object.mesh.size == 0: return SlicePaths() has_rot = bool(rotation and any(rotation)) if has_rot: from mathutils import Euler mat = np.array(Euler(rotation).to_matrix().inverted()) merged_object.mesh[:, :3, :] = merged_object.mesh[:, :3, :] @ mat.T merged_object.mesh[:, 3:4, :] = merged_object.mesh[:, 3:4, :] @ mat.T min_z = float(merged_object.mesh[:, :3, 2].min()) if abs(min_z) < 1e-6: min_z = 0.0 max_z = float(merged_object.mesh[:, :3, 2].max()) h_first = fl_height if fl_height is not None else layer_height min_z_um = int(round(min_z * 1000)) lh_um = int(round(layer_height * 1000)) flh_um = int(round(h_first * 1000)) max_z_um = int(round(max_z * 1000)) z_first_um = min_z_um + flh_um z_rest_start_um = z_first_um + lh_um if z_first_um <= max_z_um: if z_rest_start_um <= max_z_um and lh_um > 0: z_um_values = np.arange(z_rest_start_um, max_z_um + 1, lh_um, dtype=np.int64) z_values = np.concatenate(([z_first_um], z_um_values)) / 1000.0 else: z_values = np.array([z_first_um], dtype=np.float64) / 1000.0 else: z_values = np.array([], dtype=np.float64) return _finish_slice_paths(merged_object, coll, objects, z_values, rotation, ignore_holes, fl_height, layer_height) def _slice_single_mesh_item_with_layers(coll: Any, layers: Any, rotation: Any, ignore_holes: bool) -> SlicePaths: if not coll or layers is None: return SlicePaths() # Handle SlicingGroup, SlicingCollection, SlicingObject if isinstance(coll, SlicingGroup): objects = [] for c in coll.collections.values(): objects.extend(c.objects) elif isinstance(coll, SlicingCollection): objects = list(coll.objects) elif isinstance(coll, SlicingObject): objects = [coll] else: objects = getattr(coll, "objects", [coll] if hasattr(coll, "mesh") else []) merged_object = SlicingObject.merge(objects) if merged_object.mesh is None or merged_object.mesh.size == 0: return SlicePaths() has_rot = bool(rotation and any(rotation)) if has_rot: from mathutils import Euler mat = np.array(Euler(rotation).to_matrix().inverted()) merged_object.mesh[:, :3, :] = merged_object.mesh[:, :3, :] @ mat.T merged_object.mesh[:, 3:4, :] = merged_object.mesh[:, 3:4, :] @ mat.T min_z = float(merged_object.mesh[:, :3, 2].min()) if abs(min_z) < 1e-6: min_z = 0.0 max_z = float(merged_object.mesh[:, :3, 2].max()) min_z_um = int(round(min_z * 1000)) max_z_um = int(round(max_z * 1000)) raw_layers = layers if isinstance(layers, (list, tuple, np.ndarray)) else [layers] valid_z: list[float] = [] for z in raw_layers: try: zf = float(z) z_um = int(round(zf * 1000)) if min_z_um <= z_um <= max_z_um: valid_z.append(zf) except (ValueError, TypeError): continue if not valid_z: return SlicePaths() z_values = np.array(valid_z, dtype=np.float64) fl_height = float(z_values[0]) if len(z_values) > 0 else None layer_height = float(z_values[1] - z_values[0]) if len(z_values) > 1 else (fl_height or 0.2) return _finish_slice_paths(merged_object, coll, objects, z_values, rotation, ignore_holes, fl_height, layer_height) # Public aliases slice_single_mesh_item = _slice_single_mesh_item finish_slice_paths = _finish_slice_paths slice_single_mesh_item_with_layers = _slice_single_mesh_item_with_layers