From 370a2d95cc24bbb8097699b6ddb68f57dd712c53 Mon Sep 17 00:00:00 2001 From: Nicolas Predella Date: Tue, 2 Jun 2026 10:18:25 +0300 Subject: [PATCH] slice angled --- core/nodeslicer/nodes/path.py | 27 +++++++++++++++++++-------- doc/NODES.md | 2 +- doc/USER_GUIDE.md | 2 +- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/core/nodeslicer/nodes/path.py b/core/nodeslicer/nodes/path.py index 85e5c8d..cdc8c83 100644 --- a/core/nodeslicer/nodes/path.py +++ b/core/nodeslicer/nodes/path.py @@ -38,21 +38,27 @@ class SLICENODE_SliceShells(SliceNode): def init(self, context): self.add_input("SLICESOCK_Mesh") self.add_input("SLICESOCK_Float", "Layer Height").value = 0.2 + self.add_input("SLICESOCK_VectorRotation", "Rotation") self.add_output("SLICESOCK_Paths") def evaluate_output(self, socket_name: str, **_kwargs) -> SlicePaths: if socket_name != "Paths": return SlicePaths() - slicing_collection: SlicingCollection | None = self.resolve("Mesh") + coll: SlicingCollection | None = self.resolve("Mesh") layer_height: float = self.resolve("Layer Height") + rotation: Any = self.resolve("Rotation") or (0.0, 0.0, 0.0) - if not slicing_collection: return SlicePaths() + if not coll: return SlicePaths() merged_object = SlicingObject() - coll: SlicingCollection = self.resolve("Mesh") - if coll: - for obj in coll.objects: - merged_object.mesh = np.concatenate([merged_object.mesh, obj.mesh], axis=0) + for obj in coll.objects: + merged_object.mesh = np.concatenate([merged_object.mesh, obj.mesh], axis=0) + + if rotation and any(rotation): + 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 = merged_object.mesh[:,0:3,2:3].min() from math import floor @@ -63,9 +69,14 @@ class SLICENODE_SliceShells(SliceNode): slices_group: list[list[np.ndarray]] | None = slice_at_range([merged_object.mesh], z_values) assert slices_group - slices = slices_group[0] + slices = SlicePaths(slices_group[0]) - return SlicePaths(slices) + if rotation and any(rotation): + from mathutils import Euler + mat_inv = np.array(Euler(rotation).to_matrix()) + slices = slices.transform(mat_inv) + + return slices @register_class class SLICENODE_BufferShells(SliceNode): diff --git a/doc/NODES.md b/doc/NODES.md index 40acade..fcbda3c 100644 --- a/doc/NODES.md +++ b/doc/NODES.md @@ -57,7 +57,7 @@ All nodes are found in the **Add (Shift+A)** menu under the **SliceNodes** categ - **Extrusion Multiplier Attribute**: Mesh attribute to use for per-segment extrusion rates. - **Feedrate Attribute**: Mesh attribute to use for per-segment feedrates. - **Extra Attributes**: Dictionary mapping mesh attribute names to custom G-code prefixes (e.g., `opening_size` -> `S`). -- **Slice Shells**: Slices a `SlicingObject` into 2D `SlicePaths` at a given layer height. +- **Slice Shells**: Slices a `SlicingObject` into 2D `SlicePaths` at a given layer height. Supports rotation for slicing at an angle. - **Buffer Shells**: Offsets/buffers 2D `SlicePaths`. Supports rotated offsetting and multiple miter types. - **Infill**: Generates a cross-hatch grid or parallel lines clipped by boundary paths. - **Infill Type**: Switch between **Grid** (cross-hatch) and **Rectilinear** (alternating parallel lines). diff --git a/doc/USER_GUIDE.md b/doc/USER_GUIDE.md index 12fcb37..34bcc0d 100644 --- a/doc/USER_GUIDE.md +++ b/doc/USER_GUIDE.md @@ -67,5 +67,5 @@ Once you are happy with the preview: ## 5. Tips for Success * **Scale**: The slicer treats 1 Blender unit as 1 meter, but scales everything to **mm** for the G-code output. -* **Orientation**: The slicer currently assumes your object is oriented correctly for printing (Z is up). +* **Orientation**: The slicer currently assumes your object is oriented correctly for printing (Z is up). You can use the **Rotation** input on the **Slice Shells** node to slice at an angle if needed. * **Performance**: For very complex meshes, the "Get Mesh" and "Slice Shells" nodes might take a moment to compute. The result is cached, so changing settings further down the chain (like Infill density) will be much faster.