-
Notifications
You must be signed in to change notification settings - Fork 19
Open
Description
The simple cutter scans along an axis (x,y) introduce loads of dropcutter calls. The same procedure can be accomplished if the same z-level paths are grouped by proximity.
Below is a patch that accomplishes this (for x-axis scans only, though it could easily be generalized). Importantly, it reduces milling time for material removal by about 50%.
From ae5a97982677b550a2a8045cd674366b27c0b6f9 Mon Sep 17 00:00:00 2001
From: Adam Shaver <adam_dot_shaver@gmail.com>
Date: Wed, 10 Apr 2013 08:15:29 -0500
Subject: [PATCH] Code to shorten the path if pushcutter is selected.
On initial testing, this shortened the mill time by about 50% for material
removal. On the default stl included with pycam the time decreased
from 121 min to 42 minutes.
The code re-orders existing paths and inserts additional paths such that
the cutter can be moved from one line scan to another if
--the line scan paths are adjacent (e.g., one lies on top of another)
--the terminal end point of the current path is interstitial to the
next path, or, the start point of the next path is interstitial to the
current path.
Note, this current code only works with the x-axis chosen as the scan
direction. However, it is easily generalizable to any scan axis (x,y,z).
---
pycam/PathGenerators/PushCutter.py | 108 ++++++++++++++++++++++++++++++++++++
pycam/Toolpath/Generator.py | 1 +
2 files changed, 109 insertions(+), 0 deletions(-)
diff --git a/pycam/PathGenerators/PushCutter.py b/pycam/PathGenerators/PushCutter.py
index 186e331..ad72da5 100755
--- a/pycam/PathGenerators/PushCutter.py
+++ b/pycam/PathGenerators/PushCutter.py
@@ -25,6 +25,8 @@ from pycam.PathGenerators import get_free_paths_ode, get_free_paths_triangles
import pycam.PathProcessors
from pycam.Geometry.utils import ceil
from pycam.Utils.threading import run_in_parallel
+from pycam.Geometry import Path
+from pycam.Geometry import Point
from pycam.Utils import ProgressCounter
import pycam.Utils.log
import math
@@ -147,3 +149,109 @@ class PushCutter(object):
# quit requested
break
+ if type(self.pa) == pycam.PathProcessors.SimpleCutter.SimpleCutter().__class__:
+ self.removeRaiseCutter( (2 * cutter.radius) )
+
+ def removeRaiseCutter(self, scan_width):
+ #==============================================================================
+ # This function re-arranges the toolpath and injects some short moves such
+ # the cutter can stay on the same z-plane for awhile.
+ #
+ # The toolpath is just a set of paths, for which each path holds points. Our goal is
+ # to construct a new set of paths, that re-orders the current and possibly adds
+ # some in-plane transfers, so we don't have to do drop-cutter calls.
+ #
+ #==============================================================================
+
+ # First get all the Z-layers. Note, we do this here to be insulateds
+ # from the toolpath generator, though it might be better to do it at that
+ # level (in path construction).
+ zdepths = []
+ zgroups = []
+ c=-1
+ for path in self.pa.paths:
+ c+=1
+ for p in path.points:
+ if not p.z in zdepths:
+ zdepths.append(p.z)
+ zgroups.append([c])
+ else:
+ zidx = zdepths.index(p.z)
+ zgroups[zidx].append(c)
+ break
+
+ # Now, for each z-group, split them into contiguous sections. We exploit 2 things
+ # (i) the raster scans start at the 'top' or the 'bottom' and work to the opposite
+ # side, so we will encounter things on one side first, and(ii) the path's have
+ # direction, and we only care aobut something that is close to the end point.
+ newtoolpath = []
+ for layer in zgroups:
+ active = [True]*len(self.pa.paths)
+ for ipath in layer:
+ path = self.pa.paths[ipath]
+ if not active[ipath]:
+ continue
+ else:
+ # Start with this path
+ p1,p2 = path.points
+ active[ipath] = False
+ newtoolpath.append(path)
+
+ # Find other possible paths
+ c = -1
+ while c < len(layer):
+ c+=1
+ for inextpath in layer:
+ nextpath = self.pa.paths[inextpath]
+ if not active[inextpath]:
+ continue
+
+ np1, np2 = nextpath.points
+ if abs(np1.y-p2.y) <= scan_width \
+ and abs(np1.x-p2.x) <= (5*scan_width):
+ # Point is close. Poosibly inject paths to take us to the line
+
+ if not np1 == p2:
+ if np1.x <= max(p2.x,p1.x) and np1.x >= min(p1.x,p2.x):
+ # np1 point is interstitial to current path, go back and up
+ pathnew = Path.Path()
+ pathnew.append(p2)
+ p3 = Point.Point(np1.x, p2.y, p2.z)
+ if p3.y == np1.y:
+ continue
+ elif not (p3.x == p2.x and p3.y == p2.y):
+ pathnew.append(p3)
+ newtoolpath.append(pathnew)
+
+ pathnew2 = Path.Path()
+ p4 = Point.Point(p3.x, np1.y, p3.z)
+ pathnew2.append(p3)
+ pathnew2.append(p4)
+ newtoolpath.append(pathnew2)
+ elif p2.x >= min(np1.x, np2.x) and p2.x <= max(np1.x, np2.x):
+
+ # p2 point is interstitial to next path, go up and to the np1.x point
+ pathnew = Path.Path()
+ pathnew.append(p2)
+ p3 = Point.Point(p2.x, np1.y, p2.z)
+ if p3.x == np1.x:
+ continue
+ elif not (p3.x == p2.x and p3.y == p2.y):
+ pathnew.append(p3)
+ newtoolpath.append(pathnew)
+
+ pathnew2 = Path.Path()
+ p4 = Point.Point(np1.x, p3.y, p3.z)
+ pathnew2.append(p3)
+ pathnew2.append(p4)
+ newtoolpath.append(pathnew2)
+ else:
+ continue
+
+ # append the point
+ active[inextpath] = False
+ newtoolpath.append(nextpath)
+ p1 = np1
+ p2 = np2
+
+ self.pa.paths = newtoolpath
diff --git a/pycam/Toolpath/Generator.py b/pycam/Toolpath/Generator.py
index 1036551..2365607 100644
--- a/pycam/Toolpath/Generator.py
+++ b/pycam/Toolpath/Generator.py
@@ -269,6 +269,7 @@ def generate_toolpath(model, tool_settings=None,
callback)
else:
toolpath = generator.GenerateToolPath(motion_grid, callback)
+ toolpath = generator.removeRaiseCutter(toolpath, line_stepping)
elif path_generator == "EngraveCutter":
if step_down > 0:
dz = step_down
--
1.7.0.4
Metadata
Metadata
Assignees
Labels
No labels