Path check.

This commit is contained in:
Juan Pablo Caram
2015-01-19 17:17:16 -05:00
parent 922aa6734d
commit 6361ad3338
2 changed files with 206 additions and 163 deletions

View File

@@ -133,6 +133,10 @@ class App(QtCore.QObject):
json.dump([], f)
f.close()
self.app_home = os.path.dirname(os.path.realpath(__file__))
App.log.debug("Application path is " + self.app_home)
App.log.debug("Started in " + os.getcwd())
QtCore.QObject.__init__(self)
self.ui = FlatCAMGUI(self.version)
@@ -243,7 +247,8 @@ class App(QtCore.QObject):
"zoom_ratio": 1.5,
"point_clipboard_format": "(%.4f, %.4f)",
"zdownrate": None,
"excellon_zeros": "L"
"excellon_zeros": "L",
"cncjob_coordinate_format": "X%.4fY%.4f"
})
###############################
@@ -1771,7 +1776,8 @@ class App(QtCore.QObject):
# Which objects to update the given parameters.
routes = {
"zdownrate": CNCjob,
"excellon_zeros": Excellon
"excellon_zeros": Excellon,
"cncjob_coordinate_format": CNCjob
}
for param in routes:
@@ -2187,7 +2193,7 @@ class App(QtCore.QObject):
od = collections.OrderedDict(sorted(commands.items()))
for cmd, val in od.iteritems():
#print cmd, '\n', ''.join(['~']*len(cmd))
output += cmd + ' \n' + ''.join(['~']*len(cmd)) + '\n'
output += cmd + ' \n' + ''.join(['~'] * len(cmd)) + '\n'
t = val['help']
usage_i = t.find('>')
@@ -2197,7 +2203,7 @@ class App(QtCore.QObject):
output += expl + '\n\n'
continue
expl = t[:usage_i-1]
expl = t[:usage_i - 1]
#print expl + '\n'
output += expl + '\n\n'
@@ -2250,8 +2256,25 @@ class App(QtCore.QObject):
return "ERROR: No such system parameter."
def set_sys(param, value):
# tcl string to python keywords:
tcl2py = {
"None": None,
"none": None,
"false": False,
"False": False,
"true": True,
"True": True
}
if param in self.defaults:
try:
value = tcl2py[value]
except KeyError:
pass
self.defaults[param] = value
self.propagate_defaults()
return "Ok"

338
camlib.py
View File

@@ -145,6 +145,17 @@ class Geometry(object):
return self.solid_geometry.bounds
def flatten(self, geometry=None, reset=True, pathonly=False):
"""
Creates a list of non-iterable linear geometry objects.
Polygons are expanded into its exterior and interiors if specified.
Results are placed in self.flat_geoemtry
:param geometry: Shapely type or list or list of list of such.
:param reset: Clears the contents of self.flat_geometry.
:param pathonly: Expands polygons into linear elements.
"""
if geometry is None:
geometry = self.solid_geometry
@@ -2166,7 +2177,8 @@ class CNCjob(Geometry):
"""
defaults = {
"zdownrate": None
"zdownrate": None,
"coordinate_format": "X%.4fY%.4f"
}
def __init__(self, units="in", kind="generic", z_move=0.1,
@@ -2240,43 +2252,44 @@ class CNCjob(Geometry):
return factor
def generate_from_excellon(self, exobj):
"""
Generates G-code for drilling from Excellon object.
self.gcode becomes a list, each element is a
different job for each tool in the excellon code.
"""
self.kind = "drill"
self.gcode = []
t = "G00 X%.4fY%.4f\n"
down = "G01 Z%.4f\n" % self.z_cut
up = "G01 Z%.4f\n" % self.z_move
for tool in exobj.tools:
points = []
for drill in exobj.drill:
if drill['tool'] == tool:
points.append(drill['point'])
gcode = self.unitcode[self.units.upper()] + "\n"
gcode += self.absolutecode + "\n"
gcode += self.feedminutecode + "\n"
gcode += "F%.2f\n" % self.feedrate
gcode += "G00 Z%.4f\n" % self.z_move # Move to travel height
gcode += "M03\n" # Spindle start
gcode += self.pausecode + "\n"
for point in points:
gcode += t % point
gcode += down + up
gcode += t % (0, 0)
gcode += "M05\n" # Spindle stop
self.gcode.append(gcode)
# def generate_from_excellon(self, exobj):
# """
# Generates G-code for drilling from Excellon object.
# self.gcode becomes a list, each element is a
# different job for each tool in the excellon code.
# """
# self.kind = "drill"
# self.gcode = []
#
# #t = "G00 X%.4fY%.4f\n"
# t = "G00 " + CNCjob.defaults["coordinate_format"] + "\n"
# down = "G01 Z%.4f\n" % self.z_cut
# up = "G01 Z%.4f\n" % self.z_move
#
# for tool in exobj.tools:
#
# points = []
#
# for drill in exobj.drill:
# if drill['tool'] == tool:
# points.append(drill['point'])
#
# gcode = self.unitcode[self.units.upper()] + "\n"
# gcode += self.absolutecode + "\n"
# gcode += self.feedminutecode + "\n"
# gcode += "F%.2f\n" % self.feedrate
# gcode += "G00 Z%.4f\n" % self.z_move # Move to travel height
# gcode += "M03\n" # Spindle start
# gcode += self.pausecode + "\n"
#
# for point in points:
# gcode += t % point
# gcode += down + up
#
# gcode += t % (0, 0)
# gcode += "M05\n" # Spindle stop
#
# self.gcode.append(gcode)
def generate_from_excellon_by_tool(self, exobj, tools="all"):
"""
@@ -2290,7 +2303,9 @@ class CNCjob(Geometry):
:return: None
:rtype: None
"""
log.debug("Creating CNC Job from Excellon...")
if tools == "all":
tools = [tool for tool in exobj.tools]
else:
@@ -2304,10 +2319,9 @@ class CNCjob(Geometry):
points.append(drill['point'])
log.debug("Found %d drills." % len(points))
#self.kind = "drill"
self.gcode = []
t = "G00 X%.4fY%.4f\n"
t = "G00 " + CNCjob.defaults["coordinate_format"] + "\n"
down = "G01 Z%.4f\n" % self.z_cut
up = "G01 Z%.4f\n" % self.z_move
@@ -2329,71 +2343,71 @@ class CNCjob(Geometry):
self.gcode = gcode
def generate_from_geometry(self, geometry, append=True, tooldia=None, tolerance=0):
"""
Generates G-Code from a Geometry object. Stores in ``self.gcode``.
Algorithm description:
----------------------
Follow geometry paths in the order they are being read. No attempt
to optimize.
:param geometry: Geometry defining the toolpath
:type geometry: Geometry
:param append: Wether to append to self.gcode or re-write it.
:type append: bool
:param tooldia: If given, sets the tooldia property but does
not affect the process in any other way.
:type tooldia: bool
:param tolerance: All points in the simplified object will be within the
tolerance distance of the original geometry.
:return: None
:rtype: None
"""
if tooldia is not None:
self.tooldia = tooldia
self.input_geometry_bounds = geometry.bounds()
if not append:
self.gcode = ""
# Initial G-Code
self.gcode = self.unitcode[self.units.upper()] + "\n"
self.gcode += self.absolutecode + "\n"
self.gcode += self.feedminutecode + "\n"
self.gcode += "F%.2f\n" % self.feedrate
self.gcode += "G00 Z%.4f\n" % self.z_move # Move (up) to travel height
self.gcode += "M03\n" # Spindle start
self.gcode += self.pausecode + "\n"
# Iterate over geometry and run individual methods
# depending on type
for geo in geometry.solid_geometry:
if type(geo) == Polygon:
self.gcode += self.polygon2gcode(geo, tolerance=tolerance)
continue
if type(geo) == LineString or type(geo) == LinearRing:
self.gcode += self.linear2gcode(geo, tolerance=tolerance)
continue
if type(geo) == Point:
self.gcode += self.point2gcode(geo)
continue
if type(geo) == MultiPolygon:
for poly in geo:
self.gcode += self.polygon2gcode(poly, tolerance=tolerance)
continue
log.warning("G-code generation not implemented for %s" % (str(type(geo))))
# Finish
self.gcode += "G00 Z%.4f\n" % self.z_move # Stop cutting
self.gcode += "G00 X0Y0\n"
self.gcode += "M05\n" # Spindle stop
# def generate_from_geometry(self, geometry, append=True, tooldia=None, tolerance=0):
# """
# Generates G-Code from a Geometry object. Stores in ``self.gcode``.
#
# Algorithm description:
# ----------------------
# Follow geometry paths in the order they are being read. No attempt
# to optimize.
#
# :param geometry: Geometry defining the toolpath
# :type geometry: Geometry
# :param append: Wether to append to self.gcode or re-write it.
# :type append: bool
# :param tooldia: If given, sets the tooldia property but does
# not affect the process in any other way.
# :type tooldia: bool
# :param tolerance: All points in the simplified object will be within the
# tolerance distance of the original geometry.
# :return: None
# :rtype: None
# """
# if tooldia is not None:
# self.tooldia = tooldia
#
# self.input_geometry_bounds = geometry.bounds()
#
# if not append:
# self.gcode = ""
#
# # Initial G-Code
# self.gcode = self.unitcode[self.units.upper()] + "\n"
# self.gcode += self.absolutecode + "\n"
# self.gcode += self.feedminutecode + "\n"
# self.gcode += "F%.2f\n" % self.feedrate
# self.gcode += "G00 Z%.4f\n" % self.z_move # Move (up) to travel height
# self.gcode += "M03\n" # Spindle start
# self.gcode += self.pausecode + "\n"
#
# # Iterate over geometry and run individual methods
# # depending on type
# for geo in geometry.solid_geometry:
#
# if type(geo) == Polygon:
# self.gcode += self.polygon2gcode(geo, tolerance=tolerance)
# continue
#
# if type(geo) == LineString or type(geo) == LinearRing:
# self.gcode += self.linear2gcode(geo, tolerance=tolerance)
# continue
#
# if type(geo) == Point:
# self.gcode += self.point2gcode(geo)
# continue
#
# if type(geo) == MultiPolygon:
# for poly in geo:
# self.gcode += self.polygon2gcode(poly, tolerance=tolerance)
# continue
#
# log.warning("G-code generation not implemented for %s" % (str(type(geo))))
#
# # Finish
# self.gcode += "G00 Z%.4f\n" % self.z_move # Stop cutting
# self.gcode += "G00 X0Y0\n"
# self.gcode += "M05\n" # Spindle stop
def generate_from_geometry_2(self, geometry, append=True, tooldia=None, tolerance=0):
"""
@@ -2412,6 +2426,7 @@ class CNCjob(Geometry):
assert isinstance(geometry, Geometry)
## Flatten the geometry
# Only linear elements (no polygons) remain.
flat_geometry = geometry.flatten(pathonly=True)
log.debug("%d paths" % len(flat_geometry))
@@ -2456,11 +2471,14 @@ class CNCjob(Geometry):
# deletion will fail.
storage.remove(geo)
# If last point in geometry is the nearest
# then reverse coordinates.
if list(pt) == list(geo.coords[-1]):
#print "Reversing"
geo.coords = list(geo.coords)[::-1]
# G-code
# Note: self.linear2gcode() and self.point2gcode() will
# lower and raise the tool every time.
if type(geo) == LineString or type(geo) == LinearRing:
self.gcode += self.linear2gcode(geo, tolerance=tolerance)
elif type(geo) == Point:
@@ -2536,7 +2554,7 @@ class CNCjob(Geometry):
# Separate codes in line
parts = []
for p in range(n_codes-1):
for p in range(n_codes - 1):
parts.append(line[codes_idx[p]:codes_idx[p+1]].strip())
parts.append(line[codes_idx[-1]:].strip())
@@ -2712,55 +2730,55 @@ class CNCjob(Geometry):
# TODO: This takes forever. Too much data?
self.solid_geometry = cascaded_union([geo['geom'] for geo in self.gcode_parsed])
def polygon2gcode(self, polygon, tolerance=0):
"""
Creates G-Code for the exterior and all interior paths
of a polygon.
:param polygon: A Shapely.Polygon
:type polygon: Shapely.Polygon
:param tolerance: All points in the simplified object will be within the
tolerance distance of the original geometry.
:type tolerance: float
:return: G-code to cut along polygon.
:rtype: str
"""
if tolerance > 0:
target_polygon = polygon.simplify(tolerance)
else:
target_polygon = polygon
gcode = ""
t = "G0%d X%.4fY%.4f\n"
path = list(target_polygon.exterior.coords) # Polygon exterior
gcode += t % (0, path[0][0], path[0][1]) # Move to first point
if self.zdownrate is not None:
gcode += "F%.2f\n" % self.zdownrate
gcode += "G01 Z%.4f\n" % self.z_cut # Start cutting
gcode += "F%.2f\n" % self.feedrate
else:
gcode += "G01 Z%.4f\n" % self.z_cut # Start cutting
for pt in path[1:]:
gcode += t % (1, pt[0], pt[1]) # Linear motion to point
gcode += "G00 Z%.4f\n" % self.z_move # Stop cutting
for ints in target_polygon.interiors: # Polygon interiors
path = list(ints.coords)
gcode += t % (0, path[0][0], path[0][1]) # Move to first point
if self.zdownrate is not None:
gcode += "F%.2f\n" % self.zdownrate
gcode += "G01 Z%.4f\n" % self.z_cut # Start cutting
gcode += "F%.2f\n" % self.feedrate
else:
gcode += "G01 Z%.4f\n" % self.z_cut # Start cutting
for pt in path[1:]:
gcode += t % (1, pt[0], pt[1]) # Linear motion to point
gcode += "G00 Z%.4f\n" % self.z_move # Stop cutting
return gcode
# def polygon2gcode(self, polygon, tolerance=0):
# """
# Creates G-Code for the exterior and all interior paths
# of a polygon.
#
# :param polygon: A Shapely.Polygon
# :type polygon: Shapely.Polygon
# :param tolerance: All points in the simplified object will be within the
# tolerance distance of the original geometry.
# :type tolerance: float
# :return: G-code to cut along polygon.
# :rtype: str
# """
#
# if tolerance > 0:
# target_polygon = polygon.simplify(tolerance)
# else:
# target_polygon = polygon
#
# gcode = ""
# t = "G0%d X%.4fY%.4f\n"
# path = list(target_polygon.exterior.coords) # Polygon exterior
# gcode += t % (0, path[0][0], path[0][1]) # Move to first point
#
# if self.zdownrate is not None:
# gcode += "F%.2f\n" % self.zdownrate
# gcode += "G01 Z%.4f\n" % self.z_cut # Start cutting
# gcode += "F%.2f\n" % self.feedrate
# else:
# gcode += "G01 Z%.4f\n" % self.z_cut # Start cutting
#
# for pt in path[1:]:
# gcode += t % (1, pt[0], pt[1]) # Linear motion to point
# gcode += "G00 Z%.4f\n" % self.z_move # Stop cutting
# for ints in target_polygon.interiors: # Polygon interiors
# path = list(ints.coords)
# gcode += t % (0, path[0][0], path[0][1]) # Move to first point
#
# if self.zdownrate is not None:
# gcode += "F%.2f\n" % self.zdownrate
# gcode += "G01 Z%.4f\n" % self.z_cut # Start cutting
# gcode += "F%.2f\n" % self.feedrate
# else:
# gcode += "G01 Z%.4f\n" % self.z_cut # Start cutting
#
# for pt in path[1:]:
# gcode += t % (1, pt[0], pt[1]) # Linear motion to point
# gcode += "G00 Z%.4f\n" % self.z_move # Stop cutting
# return gcode
def linear2gcode(self, linear, tolerance=0):
"""
@@ -2781,7 +2799,8 @@ class CNCjob(Geometry):
target_linear = linear
gcode = ""
t = "G0%d X%.4fY%.4f\n"
#t = "G0%d X%.4fY%.4f\n"
t = "G0%d " + CNCjob.defaults["coordinate_format"] + "\n"
path = list(target_linear.coords)
gcode += t % (0, path[0][0], path[0][1]) # Move to first point
@@ -2799,7 +2818,8 @@ class CNCjob(Geometry):
def point2gcode(self, point):
gcode = ""
t = "G0%d X%.4fY%.4f\n"
#t = "G0%d X%.4fY%.4f\n"
t = "G0%d " + CNCjob.defaults["coordinate_format"] + "\n"
path = list(point.coords)
gcode += t % (0, path[0][0], path[0][1]) # Move to first point
@@ -2906,7 +2926,7 @@ def arc(center, radius, start, stop, direction, steps_per_circ):
:return: The desired arc, as list of tuples
:rtype: list
"""
# TODO: Resolution should be established by fraction of total length, not angle.
# TODO: Resolution should be established by maximum error from the exact arc.
da_sign = {"cw": -1.0, "ccw": 1.0}
points = []