Commit c6f0edc9 authored by sumpfralle's avatar sumpfralle

moved the toolpath generation code to a separate module

renamed configuration setting "overlap" to "overlap_percent"


git-svn-id: https://pycam.svn.sourceforge.net/svnroot/pycam/trunk@326 bbaffbd6-741e-11dd-a85d-61de82d9cad9
parent 409a2478
Version 0.2.5 - NOT RELEASED
* changed name of configuration setting "overlap" to "overlap_percent"
(you may need to change this name in your custom config files)
Version 0.2.4 - 2010-04-12
* added a simple simulation mode for visualizing the material penetration of a toolpath
* join tangential moves (removes the inner points in a colinear set of adjacent path points)
......
......@@ -9,15 +9,17 @@ class BaseCutter:
id = 0
vertical = Point(0,0,-1)
def __init__(self, radius, location=None, height=10):
def __init__(self, radius, location=None, height=None):
if location is None:
location = Point(0, 0, 0)
self.location = location
if height is None:
height = 10
self.height = height
self.id = BaseCutter.id
BaseCutter.id += 1
self.radius = radius
self.radiussq = radius*radius
self.height = height
self.required_distance = 0
self.distance_radius = self.radius
self.distance_radiussq = self.distance_radius * self.distance_radius
......
......@@ -5,3 +5,34 @@ from BaseCutter import BaseCutter
from SphericalCutter import SphericalCutter
from CylindricalCutter import CylindricalCutter
from ToroidalCutter import ToroidalCutter
def get_tool_from_settings(tool_settings, height=None):
""" get the tool specified by the relevant settings
The settings must include:
- "shape": one of "SphericalCutter", "CylindricalCutter" and
"ToroidalCutter"
- "radius": the tool radius
The following settings are optional or shape specific:
- "torus_radius": necessary for ToroidalCutter
@type tool_settings: dict
@value tool_settings: contains the attributes of the tool
@type height: float
@value height: the height of the tool
@rtype: BaseCutter | basestring
@return: a tool object or an error string
"""
cuttername = tool_settings["shape"]
radius = tool_settings["radius"]
if cuttername == "SphericalCutter":
return SphericalCutter(radius, height=height)
elif cuttername == "CylindricalCutter":
return CylindricalCutter(radius, height=height)
elif cuttername == "ToroidalCutter":
toroid = tool_settings["torus_radius"]
return ToroidalCutter(radius, toroid, height=height)
else:
return "Invalid cutter shape: '%s' is not one of %s" % (cuttername, TOOL_SHAPES)
This diff is collapsed.
......@@ -120,7 +120,7 @@ path_generator: PushCutter
path_postprocessor: PolygonCutter
material_allowance: 0.5
step_down: 0.8
overlap: 0
overlap_percent: 0
[Process1]
name: Semi-finish
......@@ -128,7 +128,7 @@ path_generator: PushCutter
path_postprocessor: ContourCutter
material_allowance: 0.2
step_down: 0.5
overlap: 20
overlap_percent: 20
[Process2]
name: Finish
......@@ -136,7 +136,7 @@ path_generator: DropCutter
path_postprocessor: ZigZagCutter
material_allowance: 0.0
step_down: 1.0
overlap: 60
overlap_percent: 60
[TaskDefault]
enabled: 1
......@@ -166,7 +166,7 @@ process: 2
"path_postprocessor": str,
"safety_height": float,
"material_allowance": float,
"overlap": int,
"overlap_percent": int,
"step_down": float,
"tool": object,
"process": object,
......@@ -182,7 +182,7 @@ process: 2
CATEGORY_KEYS = {
"tool": ("name", "shape", "tool_radius", "torus_radius", "feedrate", "speed"),
"process": ("name", "path_generator", "path_postprocessor", "path_direction",
"safety_height", "material_allowance", "overlap", "step_down"),
"safety_height", "material_allowance", "overlap_percent", "step_down"),
"task": ("tool", "process", "enabled"),
}
......
import pycam.Cutters
from pycam.Geometry.Point import Point
import ode
try:
......@@ -8,8 +9,8 @@ except:
class ODEBlocks:
def __init__(self, cutter, (minx, maxx, miny, maxy, minz, maxz), x_steps=None, y_steps=None):
self.cutter = cutter
def __init__(self, tool_settings, (minx, maxx, miny, maxy, minz, maxz), x_steps=None, y_steps=None):
self.cutter = pycam.Cutters.get_tool_from_settings(tool_settings)
# we don't want to use the "material allowance" distance
self.cutter.set_required_distance(0)
dimx = maxx - minx
......
import pycam.PathGenerators
import pycam.PathProcessors
import pycam.Cutters
import sys
DIRECTIONS = frozenset(("x", "y", "xy"))
PATH_GENERATORS = frozenset(("DropCutter", "PushCutter"))
PATH_POSTPROCESSORS = frozenset(("ContourCutter", "PathAccumulator", "PolygonCutter", "SimpleCutter", "ZigZagCutter"))
CALCULATION_BACKENDS = frozenset((None, "ODE"))
def generate_toolpath(model, tool_settings=None, bounds=None, direction="x",
path_generator="DropCutter", path_postprocessor="ZigZagCutter",
material_allowance=0.0, safety_height=None, overlap=0.0,
step_down=0.0, calculation_backend=None, callback=None):
""" abstract interface for generating a toolpath
@type model: pycam.Geometry.Model.Model
@value model: a model contains the surface triangles
@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
@value tool_settings: contains at least the following keys (depending on
the tool type):
"shape": any of possible cutter shape (see "pycam.Cutters")
"radius": main radius of the tools
"torus_radius": (only for ToroidalCutter) second toroidal radius
@type path_generator: str
@value path_generator: any member of the PATH_GENERATORS set
@type path_postprocessor: str
@value path_postprocessor: any member of the PATH_POSTPROCESSORS set
@type material_allowance: float
@value material_allowance: the minimum distance between the tool and the model
@type overlap: float
@value overlap: the overlap between two adjacent tool paths (0 <= overlap < 1)
@type calculation_backend: str | None
@value calculation_backend: any member of the CALCULATION_BACKENDS set
The default is the triangular collision detection.
@rtype: pycam.Toolpath.ToolPath | str
@return: the resulting toolpath object or an error string in case of invalid
arguments
"""
if bounds is None:
# no bounds were given - we use the boundaries of the model
minx, maxx = model.minx, model.maxx
miny, maxy = model.miny, model.maxy
minz, maxz = model.minz, model.maxz
else:
minx, maxx, miny, maxy, minz, maxz = bounds
# Due to some weirdness the height of the drill must be bigger than the object's size.
# Otherwise some collisions are not detected.
cutter_height = 4 * (maxy - miny)
cutter = pycam.Cutters.get_tool_from_settings(tool_settings, cutter_height)
if isinstance(cutter, basestring):
return cutter
cutter.set_required_distance(material_allowance)
physics = _get_physics(cutter, calculation_backend)
if isinstance(physics, basestring):
return physics
generator = _get_pathgenerator_instance(model, cutter, path_generator, path_postprocessor, material_allowance, safety_height, physics)
if isinstance(generator, basestring):
return generator
if (overlap < 0) or (overlap >= 1):
return "Invalid overlap value (%f): should be greater or equal 0 and lower than 1"
effective_toolradius = tool_settings["radius"] * (1.0 - overlap)
if path_generator == "DropCutter":
if direction == "x":
direction_param = 0
elif direction == "y":
direction_param = 1
else:
return "Invalid direction value (%s): not one of %s" % (direction, DIRECTIONS)
toolpath = generator.GenerateToolPath(minx, maxx, miny, maxy, minz, maxz,
effective_toolradius, effective_toolradius, direction_param, callback)
else:
if step_down > 0:
dz = step_down
else:
dz = maxz - minz
if direction == "x":
dx, dy = 0, effective_toolradius
elif direction == "y":
dx, dy = effective_toolradius, 0
elif direction == "xy":
dx, dy = effective_toolradius, effective_toolradius
else:
return "Invalid direction (%s): not one of %s" % (direction, DIRECTIONS)
toolpath = generator.GenerateToolPath(minx, maxx, miny, maxy, minz, maxz, dx, dy, dz, callback)
return toolpath
def _get_pathgenerator_instance(model, cutter, pathgenerator, pathprocessor,
material_allowance, safety_height, physics):
if pathgenerator == "DropCutter":
if pathprocessor == "ZigZagCutter":
processor = pycam.PathProcessors.PathAccumulator(zigzag=True)
elif pathprocessor == "PathAccumulator":
processor = pycam.PathProcessors.PathAccumulator()
else:
return "Invalid postprocessor (%s) for 'DropCutter': only 'ZigZagCutter' or 'PathAccumulator' are allowed" % str(pathprocessor)
return pycam.PathGenerators.DropCutter(cutter,
model, processor, physics=physics,
safety_height=safety_height)
elif pathgenerator == "PushCutter":
if pathprocessor == "PathAccumulator":
processor = pycam.PathProcessors.PathAccumulator()
elif pathprocessor == "SimpleCutter":
processor = pycam.PathProcessors.SimpleCutter()
elif pathprocessor == "ZigZagCutter":
processor = pycam.PathProcessors.ZigZagCutter()
elif pathprocessor == "PolygonCutter":
processor = pycam.PathProcessors.PolygonCutter()
elif pathprocessor == "ContourCutter":
processor = pycam.PathProcessors.ContourCutter()
else:
return "Invalid postprocessor (%s) for 'PushCutter' - it should be one of these: %s" % (processor, PATH_POSTPROCESSORS)
return pycam.PathGenerators.PushCutter(cutter,
model, processor, physics=physics)
else:
return "Invalid path generator (%s): not one of %s" % (pathgenerator, PATH_GENERATORS)
def _get_physics(cutter, calculation_backend):
if calculation_backend is None:
# triangular collision detection does not need any physical model
return None
elif calculation_backend == "ODE":
import pycam.Physics.ode_physics
return pycam.Physics.ode_physics.generate_physics(model, cutter)
else:
return "Invalid calculation backend (%s): not one of %s" % (calculation_backend, CALCULATION_BACKENDS)
__all__ = ["ToolPathList", "ToolPath", "Generator"]
import random
class ToolPathList(list):
def add_toolpath(self, toolpath, name, cutter, *args):
self.append(ToolPath(toolpath, name, cutter, *args))
def add_toolpath(self, toolpath, name, tool_settings, *args):
self.append(ToolPath(toolpath, name, tool_settings, *args))
class ToolPath:
def __init__(self, toolpath, name, cutter, drill_id, speed, feedrate,
material_allowance, safety_height, unit, start_x, start_y, start_z, bounding_box):
def __init__(self, toolpath, name, tool_settings, tool_id, speed,
feedrate, material_allowance, safety_height, unit, start_x,
start_y, start_z, bounding_box):
self.toolpath = toolpath
self.name = name
self.visible = True
self.drill_id = drill_id
self.drill = cutter
self.drill_size = cutter.radius
self.tool_id = tool_id
self.tool_settings = tool_settings
self.speed = speed
self.feedrate = feedrate
self.material_allowance = material_allowance
......
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