Commit 0218146c authored by sumpfralle's avatar sumpfralle

added support for EngraveCutter to the toolpath generator


git-svn-id: https://pycam.svn.sourceforge.net/svnroot/pycam/trunk@368 bbaffbd6-741e-11dd-a85d-61de82d9cad9
parent f12dc1a3
...@@ -20,37 +20,39 @@ You should have received a copy of the GNU General Public License ...@@ -20,37 +20,39 @@ You should have received a copy of the GNU General Public License
along with PyCAM. If not, see <http://www.gnu.org/licenses/>. along with PyCAM. If not, see <http://www.gnu.org/licenses/>.
""" """
from pycam.PathGenerators import DropCutter, PushCutter from pycam.PathGenerators import DropCutter, PushCutter, EngraveCutter
import pycam.PathProcessors import pycam.PathProcessors
import pycam.Cutters import pycam.Cutters
import pycam.Toolpath.SupportGrid import pycam.Toolpath.SupportGrid
import pycam.Geometry.Model
import sys import sys
DIRECTIONS = frozenset(("x", "y", "xy")) DIRECTIONS = frozenset(("x", "y", "xy"))
PATH_GENERATORS = frozenset(("DropCutter", "PushCutter")) PATH_GENERATORS = frozenset(("DropCutter", "PushCutter", "EngraveCutter"))
PATH_POSTPROCESSORS = frozenset(("ContourCutter", "PathAccumulator", "PolygonCutter", "SimpleCutter", "ZigZagCutter")) PATH_POSTPROCESSORS = frozenset(("ContourCutter", "PathAccumulator", "PolygonCutter", "SimpleCutter", "ZigZagCutter"))
CALCULATION_BACKENDS = frozenset((None, "ODE")) CALCULATION_BACKENDS = frozenset((None, "ODE"))
def generate_toolpath(model, tool_settings=None, bounds=None, direction="x", def generate_toolpath(model, tool_settings=None,
path_generator="DropCutter", path_postprocessor="ZigZagCutter", bounds=None, direction="x", path_generator="DropCutter",
material_allowance=0.0, safety_height=None, overlap=0.0, step_down=0.0, path_postprocessor="ZigZagCutter", material_allowance=0.0,
safety_height=None, overlap=0.0, step_down=0.0,
support_grid_distance=None, support_grid_thickness=None, support_grid_distance=None, support_grid_thickness=None,
calculation_backend=None, callback=None): calculation_backend=None, callback=None):
""" abstract interface for generating a toolpath """ abstract interface for generating a toolpath
@type model: pycam.Geometry.Model.Model @type model: pycam.Geometry.Model.Model
@value model: a model contains the surface triangles @value model: a model contains surface triangles or a contour
@type direction: str
@value direction: any member of the DIRECTIONS set (e.g. "x", "y" or "xy")
@type bounds: tuple(float) | list(float)
@value bounds: the processing boundary (relative to the center of the tool)
(order: minx, maxx, miny, maxy, minz, maxz)
@type tool_settings: dict @type tool_settings: dict
@value tool_settings: contains at least the following keys (depending on @value tool_settings: contains at least the following keys (depending on
the tool type): the tool type):
"shape": any of possible cutter shape (see "pycam.Cutters") "shape": any of possible cutter shape (see "pycam.Cutters")
"radius": main radius of the tools "radius": main radius of the tools
"torus_radius": (only for ToroidalCutter) second toroidal radius "torus_radius": (only for ToroidalCutter) second toroidal radius
@type bounds: tuple(float) | list(float)
@value bounds: the processing boundary (relative to the center of the tool)
(order: minx, maxx, miny, maxy, minz, maxz)
@type direction: str
@value direction: any member of the DIRECTIONS set (e.g. "x", "y" or "xy")
@type path_generator: str @type path_generator: str
@value path_generator: any member of the PATH_GENERATORS set @value path_generator: any member of the PATH_GENERATORS set
@type path_postprocessor: str @type path_postprocessor: str
...@@ -79,6 +81,13 @@ def generate_toolpath(model, tool_settings=None, bounds=None, direction="x", ...@@ -79,6 +81,13 @@ def generate_toolpath(model, tool_settings=None, bounds=None, direction="x",
minz, maxz = model.minz, model.maxz minz, maxz = model.minz, model.maxz
else: else:
minx, maxx, miny, maxy, minz, maxz = bounds minx, maxx, miny, maxy, minz, maxz = bounds
# trimesh model or contour model?
if isinstance(model, pycam.Geometry.Model.Model):
trimesh_model = model
contour_model = None
else:
trimesh_model = pycam.Geometry.Model.Model()
contour_model = model
# create the grid model if requested # create the grid model if requested
if (not support_grid_distance is None) \ if (not support_grid_distance is None) \
and (not support_grid_thickness is None): and (not support_grid_thickness is None):
...@@ -89,7 +98,7 @@ def generate_toolpath(model, tool_settings=None, bounds=None, direction="x", ...@@ -89,7 +98,7 @@ def generate_toolpath(model, tool_settings=None, bounds=None, direction="x",
support_grid_model = pycam.Toolpath.SupportGrid.get_support_grid( support_grid_model = pycam.Toolpath.SupportGrid.get_support_grid(
minx, maxx, miny, maxy, minz, support_grid_distance, minx, maxx, miny, maxy, minz, support_grid_distance,
support_grid_distance, support_grid_thickness) support_grid_distance, support_grid_thickness)
model += support_grid_model trimesh_model += support_grid_model
# Due to some weirdness the height of the drill must be bigger than the object's size. # Due to some weirdness the height of the drill must be bigger than the object's size.
# Otherwise some collisions are not detected. # Otherwise some collisions are not detected.
cutter_height = 4 * (maxy - miny) cutter_height = 4 * (maxy - miny)
...@@ -97,10 +106,10 @@ def generate_toolpath(model, tool_settings=None, bounds=None, direction="x", ...@@ -97,10 +106,10 @@ def generate_toolpath(model, tool_settings=None, bounds=None, direction="x",
if isinstance(cutter, basestring): if isinstance(cutter, basestring):
return cutter return cutter
cutter.set_required_distance(material_allowance) cutter.set_required_distance(material_allowance)
physics = _get_physics(model, cutter, calculation_backend) physics = _get_physics(trimesh_model, cutter, calculation_backend)
if isinstance(physics, basestring): if isinstance(physics, basestring):
return physics return physics
generator = _get_pathgenerator_instance(model, cutter, path_generator, path_postprocessor, material_allowance, safety_height, physics) generator = _get_pathgenerator_instance(trimesh_model, contour_model, cutter, path_generator, path_postprocessor, material_allowance, safety_height, physics)
if isinstance(generator, basestring): if isinstance(generator, basestring):
return generator return generator
if (overlap < 0) or (overlap >= 1): if (overlap < 0) or (overlap >= 1):
...@@ -117,7 +126,7 @@ def generate_toolpath(model, tool_settings=None, bounds=None, direction="x", ...@@ -117,7 +126,7 @@ def generate_toolpath(model, tool_settings=None, bounds=None, direction="x",
return "Safety height (%.4f) is within the bounding box height (%.4f) - this can cause collisions of the tool with the material." % (safety_height, maxz) return "Safety height (%.4f) is within the bounding box height (%.4f) - this can cause collisions of the tool with the material." % (safety_height, maxz)
toolpath = generator.GenerateToolPath(minx, maxx, miny, maxy, minz, maxz, toolpath = generator.GenerateToolPath(minx, maxx, miny, maxy, minz, maxz,
effective_toolradius, effective_toolradius, direction_param, callback) effective_toolradius, effective_toolradius, direction_param, callback)
else: elif path_generator == "PushCutter":
if step_down > 0: if step_down > 0:
dz = step_down dz = step_down
else: else:
...@@ -131,9 +140,16 @@ def generate_toolpath(model, tool_settings=None, bounds=None, direction="x", ...@@ -131,9 +140,16 @@ def generate_toolpath(model, tool_settings=None, bounds=None, direction="x",
else: else:
return "Invalid direction (%s): not one of %s" % (direction, DIRECTIONS) return "Invalid direction (%s): not one of %s" % (direction, DIRECTIONS)
toolpath = generator.GenerateToolPath(minx, maxx, miny, maxy, minz, maxz, dx, dy, dz, callback) toolpath = generator.GenerateToolPath(minx, maxx, miny, maxy, minz, maxz, dx, dy, dz, callback)
else:
# EngraveCutter
if step_down > 0:
dz = step_down
else:
dz = maxz - minz
toolpath = generator.GenerateToolPath(minz, maxz, effective_toolradius, dz, callback)
return toolpath return toolpath
def _get_pathgenerator_instance(model, cutter, pathgenerator, pathprocessor, def _get_pathgenerator_instance(trimesh_model, contour_model, cutter, pathgenerator, pathprocessor,
material_allowance, safety_height, physics): material_allowance, safety_height, physics):
if pathgenerator == "DropCutter": if pathgenerator == "DropCutter":
if pathprocessor == "ZigZagCutter": if pathprocessor == "ZigZagCutter":
...@@ -142,7 +158,7 @@ def _get_pathgenerator_instance(model, cutter, pathgenerator, pathprocessor, ...@@ -142,7 +158,7 @@ def _get_pathgenerator_instance(model, cutter, pathgenerator, pathprocessor,
processor = pycam.PathProcessors.PathAccumulator() processor = pycam.PathProcessors.PathAccumulator()
else: else:
return "Invalid postprocessor (%s) for 'DropCutter': only 'ZigZagCutter' or 'PathAccumulator' are allowed" % str(pathprocessor) return "Invalid postprocessor (%s) for 'DropCutter': only 'ZigZagCutter' or 'PathAccumulator' are allowed" % str(pathprocessor)
return DropCutter.DropCutter(cutter, model, processor, physics=physics, return DropCutter.DropCutter(cutter, trimesh_model, processor, physics=physics,
safety_height=safety_height) safety_height=safety_height)
elif pathgenerator == "PushCutter": elif pathgenerator == "PushCutter":
if pathprocessor == "PathAccumulator": if pathprocessor == "PathAccumulator":
...@@ -157,17 +173,25 @@ def _get_pathgenerator_instance(model, cutter, pathgenerator, pathprocessor, ...@@ -157,17 +173,25 @@ def _get_pathgenerator_instance(model, cutter, pathgenerator, pathprocessor,
processor = pycam.PathProcessors.ContourCutter() processor = pycam.PathProcessors.ContourCutter()
else: else:
return "Invalid postprocessor (%s) for 'PushCutter' - it should be one of these: %s" % (processor, PATH_POSTPROCESSORS) return "Invalid postprocessor (%s) for 'PushCutter' - it should be one of these: %s" % (processor, PATH_POSTPROCESSORS)
return PushCutter.PushCutter(cutter, model, processor, physics=physics) return PushCutter.PushCutter(cutter, trimesh_model, processor, physics=physics)
elif pathgenerator == "EngraveCutter":
if pathprocessor == "SimpleCutter":
processor = pycam.PathProcessors.SimpleCutter()
else:
return "Invalid postprocessor (%s) for 'EngraveCutter' - it should be one of these: %s" % (processor, PATH_POSTPROCESSORS)
if not contour_model:
return "The EngraveCutter requires a contour model (e.g. from a DXF file)."
return EngraveCutter.EngraveCutter(cutter, trimesh_model, contour_model, processor, physics=physics)
else: else:
return "Invalid path generator (%s): not one of %s" % (pathgenerator, PATH_GENERATORS) return "Invalid path generator (%s): not one of %s" % (pathgenerator, PATH_GENERATORS)
def _get_physics(model, cutter, calculation_backend): def _get_physics(trimesh_model, cutter, calculation_backend):
if calculation_backend is None: if calculation_backend is None:
# triangular collision detection does not need any physical model # triangular collision detection does not need any physical model
return None return None
elif calculation_backend == "ODE": elif calculation_backend == "ODE":
import pycam.Physics.ode_physics import pycam.Physics.ode_physics
return pycam.Physics.ode_physics.generate_physics(model, cutter) return pycam.Physics.ode_physics.generate_physics(trimesh_model, cutter)
else: else:
return "Invalid calculation backend (%s): not one of %s" % (calculation_backend, CALCULATION_BACKENDS) return "Invalid calculation backend (%s): not one of %s" % (calculation_backend, CALCULATION_BACKENDS)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment