diff --git a/core/nodeslicer/nodes/path.py b/core/nodeslicer/nodes/path.py index 90f39a4..eb5c9e5 100644 --- a/core/nodeslicer/nodes/path.py +++ b/core/nodeslicer/nodes/path.py @@ -369,3 +369,50 @@ class SLICENODE_JoinPaths(SliceNodeJoiner): return merged return SlicePaths() + + +@register_class +class SLICENODE_SimplifyPaths(SliceNode): + bl_label = "Simplify Paths" + + preserve_topology: NfBoolProperty( # pyright: ignore[reportInvalidTypeForm] + name="Preserve Topology", + description="Prevents swapping of contained/containing geometry during simplification", + default=True + ) + + def init(self, context): + self.add_input("SLICESOCK_Paths") + self.add_input("SLICESOCK_Float", "Tolerance").value = 0.1 + self.add_output("SLICESOCK_Paths") + + def draw_buttons(self, context, layout): + assert layout + layout.prop(self, "preserve_topology") + + def evaluate_output(self, socket_name: str, **_kwargs) -> SlicePaths: + if socket_name != "Paths": + return SlicePaths() + + paths: SlicePaths = self.resolve("Paths") + tolerance: float = self.resolve("Tolerance") or 0.0 + + if not paths or tolerance <= 0: + return paths + + from shapely.geometry import LineString + import numpy as np + + simplified = [] + for p in paths: + if len(p) < 3: + simplified.append(p) + continue + line = LineString(p) + simplified_line = line.simplify(tolerance, preserve_topology=self.preserve_topology) + if simplified_line.geom_type == 'LineString' and len(simplified_line.coords) >= 2: + simplified.append(np.array(simplified_line.coords, dtype=p.dtype)) + else: + simplified.append(p) + + return paths.copy_with(simplified) diff --git a/core/nodeslicer/tree.py b/core/nodeslicer/tree.py index 5d7e39e..f1e2487 100644 --- a/core/nodeslicer/tree.py +++ b/core/nodeslicer/tree.py @@ -506,6 +506,7 @@ class SLICENODE_MT_Geometry(Menu): 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="Simplify Paths").type = "SLICENODE_SimplifyPaths" layout.operator("node.add_node", text="Infill").type = "SLICENODE_Infill" layout.operator("node.add_node", text="Boolean Paths").type = "SLICENODE_JoinSlices" layout.separator()