nodeslicer/core/functions/path_functions.py
2026-10-15 14:13:39 +03:00

196 lines
6.9 KiB
Python

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