196 lines
6.9 KiB
Python
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
|