rotate
This commit is contained in:
parent
3c186aae9f
commit
dbc631e3dd
5 changed files with 98 additions and 6 deletions
|
|
@ -4,7 +4,7 @@ import numpy as np
|
|||
|
||||
from ..bpy_ns_types import NfEnumProperty, NfBoolProperty, notify_tree_changed
|
||||
from ....registry import register_class
|
||||
from .base import SliceNode, SlicePaths, SlicingObject, SlicingCollection, slice_at_range, SliceNodeJoiner
|
||||
from .base import SliceNode, SlicePaths, SlicingObject, SlicingCollection, slice_at_range, SliceNodeJoiner, SlicingGroup
|
||||
|
||||
@register_class
|
||||
class SLICENODE_GetPaths(SliceNode):
|
||||
|
|
@ -44,13 +44,21 @@ class SLICENODE_SliceShells(SliceNode):
|
|||
def evaluate_output(self, socket_name: str, **_kwargs) -> SlicePaths:
|
||||
if socket_name != "Paths": return SlicePaths()
|
||||
|
||||
coll: SlicingCollection | None = self.resolve("Mesh")
|
||||
coll: SlicingCollection | SlicingGroup | 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 coll: return SlicePaths()
|
||||
|
||||
merged_object = SlicingObject.merge(coll.objects)
|
||||
# Handle SlicingGroup flattening
|
||||
if isinstance(coll, SlicingGroup):
|
||||
objects = []
|
||||
for c in coll.collections.values():
|
||||
objects.extend(c.objects)
|
||||
else:
|
||||
objects = coll.objects
|
||||
|
||||
merged_object = SlicingObject.merge(objects)
|
||||
|
||||
if rotation and any(rotation):
|
||||
from mathutils import Euler
|
||||
|
|
@ -127,6 +135,27 @@ class SLICENODE_PathOffset(SliceNode):
|
|||
|
||||
return paths.offset(np.array(offset, dtype=np.float32))
|
||||
|
||||
@register_class
|
||||
class SLICENODE_RotatePath(SliceNode):
|
||||
bl_label = "Rotate Path"
|
||||
|
||||
def init(self, context):
|
||||
self.add_input("SLICESOCK_Paths", "Paths")
|
||||
self.add_input("SLICESOCK_Float", "Angle").value = 0.0
|
||||
self.add_input("SLICESOCK_Vector", "Center")
|
||||
self.add_output("SLICESOCK_Paths", "Paths")
|
||||
|
||||
def evaluate_output(self, socket_name: str, **_kwargs) -> Any:
|
||||
paths: SlicePaths = self.resolve("Paths")
|
||||
angle: float = self.resolve("Angle") or 0.0
|
||||
center: Any = self.resolve("Center")
|
||||
|
||||
if not paths: return SlicePaths()
|
||||
|
||||
center_vec = np.array(center, dtype=np.float32) if center and any(center) else None
|
||||
|
||||
return paths.rotate(angle, center=center_vec)
|
||||
|
||||
@register_class
|
||||
class SLICENODE_BufferShellsMultiple(SliceNode):
|
||||
bl_label = "Buffer Shells Multiple"
|
||||
|
|
|
|||
|
|
@ -505,6 +505,7 @@ class SLICENODE_MT_Geometry(Menu):
|
|||
layout.operator("node.add_node", text="Buffer Shells").type = "SLICENODE_BufferShells"
|
||||
layout.operator("node.add_node", text="Buffer Shells Multiple").type = "SLICENODE_BufferShellsMultiple"
|
||||
layout.operator("node.add_node", text="Path Offset").type = "SLICENODE_PathOffset"
|
||||
layout.operator("node.add_node", text="Rotate Path").type = "SLICENODE_RotatePath"
|
||||
layout.operator("node.add_node", text="Infill").type = "SLICENODE_Infill"
|
||||
layout.operator("node.add_node", text="Boolean Paths").type = "SLICENODE_JoinSlices"
|
||||
layout.separator()
|
||||
|
|
|
|||
|
|
@ -102,6 +102,50 @@ class SlicePaths(list[np.ndarray]):
|
|||
|
||||
return new_paths
|
||||
|
||||
def rotate(self, angle_deg: float, center: np.ndarray | None = None) -> SlicePaths:
|
||||
"""Rotates the paths around Z-axis."""
|
||||
if angle_deg == 0:
|
||||
return self
|
||||
|
||||
if center is None:
|
||||
center = self.get_first_layer_midpoint()
|
||||
|
||||
angle_rad = np.radians(angle_deg)
|
||||
c, s = np.cos(angle_rad), np.sin(angle_rad)
|
||||
rot_mat = np.array([
|
||||
[c, -s, 0],
|
||||
[s, c, 0],
|
||||
[0, 0, 1]
|
||||
], dtype=np.float32)
|
||||
|
||||
new_paths = []
|
||||
for p in self:
|
||||
# Shift to center, rotate, shift back
|
||||
new_p = (p - center) @ rot_mat.T + center
|
||||
new_paths.append(new_p)
|
||||
|
||||
return self.copy_with(new_paths)
|
||||
|
||||
def get_first_layer_midpoint(self) -> np.ndarray:
|
||||
if not self:
|
||||
return np.zeros(3)
|
||||
|
||||
# Find minimum group index
|
||||
min_g = min(self.groups)
|
||||
first_layer_indices = [i for i, g in enumerate(self.groups) if g == min_g]
|
||||
|
||||
all_pts = []
|
||||
for i in first_layer_indices:
|
||||
all_pts.append(self[i])
|
||||
|
||||
if not all_pts:
|
||||
return np.zeros(3)
|
||||
|
||||
pts = np.concatenate(all_pts)
|
||||
min_pts = np.min(pts, axis=0)
|
||||
max_pts = np.max(pts, axis=0)
|
||||
return (min_pts + max_pts) / 2.0
|
||||
|
||||
def boolean(self, other: SlicePaths, operation: str = 'UNION', connect_along_boundary: bool = False) -> SlicePaths:
|
||||
return boolean_slice(self, other, operation, connect_along_boundary=connect_along_boundary)
|
||||
|
||||
|
|
@ -840,10 +884,10 @@ class Pather(object):
|
|||
gcode.append(f";AFTER_LAYER_CHANGE")
|
||||
last_z = z0
|
||||
|
||||
gcode.append(f"G0 X{x0:.3f} Y{y0:.3f} Z{z0:.3f}")
|
||||
old_x, old_y, old_z = x0, y0, z0
|
||||
old_fr = f_rate[0] if f_rate[0] > 0 else self.feedrate
|
||||
gcode.append(f"G0 X{x0:.3f} Y{y0:.3f} Z{z0:.3f} F{old_fr}")
|
||||
|
||||
old_fr = 0
|
||||
old_x, old_y, old_z = x0, y0, z0
|
||||
old_extras = {prefix: None for prefix in extras}
|
||||
for j, (p1, p2) in enumerate(zip(coords, coords[1:])):
|
||||
seg_len: float = segment_length_3d(p1, p2)
|
||||
|
|
|
|||
|
|
@ -57,6 +57,10 @@ 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`).
|
||||
- **Path Offset**: Offsets `SlicePaths` by a 3D vector.
|
||||
- **Rotate Path**: Rotates `SlicePaths` around the Z-axis.
|
||||
- **Angle**: Rotation angle in degrees.
|
||||
- **Center**: Optional center of rotation. If not provided, it defaults to the midpoint of the first layer.
|
||||
- **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.
|
||||
|
|
|
|||
14
gcode_snippets/concrete_printer_header.gcode
Normal file
14
gcode_snippets/concrete_printer_header.gcode
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
G90
|
||||
M83
|
||||
; process 25mm-TRUE
|
||||
; layer 1, Z = 10.000
|
||||
T0
|
||||
G92 E0.0000
|
||||
G1 E-2.0000 F4800
|
||||
; feature inner perimeter
|
||||
; tool H10.000 W29.700
|
||||
G1 Z15.000 F6000
|
||||
G1 X755.261 Y904.113 F6000
|
||||
G1 Z10.000 F6000
|
||||
G1 E0.0000 F4800
|
||||
G92 E0.0000
|
||||
Loading…
Add table
Reference in a new issue