commit
d8eb1d0690
|
@ -23,6 +23,8 @@ CHANGELOG for FlatCAM beta
|
|||
- fixed the usage for Tools Database in Unix-like OS's
|
||||
- done some modest refactoring
|
||||
- fixed the Search and Add feature in Geometry Object UI
|
||||
- fixed issue with preamble not being inserted when used alone
|
||||
- modified the way that the start GCode is stored such that now the bug in GCode Editor that did not allowed selection of the first tool is now solved
|
||||
|
||||
28.10.2020
|
||||
|
||||
|
|
|
@ -2164,7 +2164,7 @@ class CNCJobObject(FlatCAMObj, CNCjob):
|
|||
if preamble == '':
|
||||
preamble = self.app.defaults["cncjob_prepend"]
|
||||
if postamble == '':
|
||||
preamble = self.app.defaults["cncjob_append"]
|
||||
postamble = self.app.defaults["cncjob_append"]
|
||||
|
||||
try:
|
||||
if self.special_group:
|
||||
|
@ -2190,7 +2190,6 @@ class CNCJobObject(FlatCAMObj, CNCjob):
|
|||
|
||||
gcode = ''
|
||||
if include_header is False:
|
||||
g = preamble
|
||||
# detect if using multi-tool and make the Gcode summation correctly for each case
|
||||
if self.multitool is True:
|
||||
for tooluid_key in self.cnc_tools:
|
||||
|
@ -2201,7 +2200,7 @@ class CNCJobObject(FlatCAMObj, CNCjob):
|
|||
else:
|
||||
gcode += self.gcode
|
||||
|
||||
g = g + gcode + postamble
|
||||
g = preamble + '\n' + gcode + '\n' + postamble
|
||||
else:
|
||||
# search for the GCode beginning which is usually a G20 or G21
|
||||
# fix so the preamble gets inserted in between the comments header and the actual start of GCODE
|
||||
|
@ -2251,39 +2250,51 @@ class CNCJobObject(FlatCAMObj, CNCjob):
|
|||
break
|
||||
|
||||
if hpgl:
|
||||
processed_gcode = ''
|
||||
processed_body_gcode = ''
|
||||
pa_re = re.compile(r"^PA\s*(-?\d+\.\d*),?\s*(-?\d+\.\d*)*;?$")
|
||||
|
||||
# process body gcode
|
||||
for gline in gcode.splitlines():
|
||||
match = pa_re.search(gline)
|
||||
if match:
|
||||
x_int = int(float(match.group(1)))
|
||||
y_int = int(float(match.group(2)))
|
||||
new_line = 'PA%d,%d;\n' % (x_int, y_int)
|
||||
processed_gcode += new_line
|
||||
processed_body_gcode += new_line
|
||||
else:
|
||||
processed_gcode += gline + '\n'
|
||||
processed_body_gcode += gline + '\n'
|
||||
|
||||
gcode = processed_gcode
|
||||
g = self.gc_header + '\n' + preamble + '\n' + gcode + postamble + end_gcode
|
||||
gcode = processed_body_gcode
|
||||
g = self.gc_header + '\n' + self.gc_start + '\n' + preamble + '\n' + \
|
||||
gcode + '\n' + postamble + end_gcode
|
||||
else:
|
||||
try:
|
||||
g_idx = gcode.index('G94')
|
||||
if preamble != '' and postamble != '':
|
||||
g = self.gc_header + gcode[:g_idx + 3] + '\n' + preamble + '\n' + \
|
||||
gcode[(g_idx + 3):] + postamble + end_gcode
|
||||
elif preamble == '':
|
||||
g = self.gc_header + gcode[:g_idx + 3] + '\n' + \
|
||||
gcode[(g_idx + 3):] + postamble + end_gcode
|
||||
elif postamble == '':
|
||||
g = self.gc_header + gcode[:g_idx + 3] + '\n' + preamble + '\n' + \
|
||||
gcode[(g_idx + 3):] + end_gcode
|
||||
else:
|
||||
g = self.gc_header + gcode[:g_idx + 3] + gcode[(g_idx + 3):] + end_gcode
|
||||
except ValueError:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' %
|
||||
_("G-code does not have a G94 code.\n"
|
||||
"Append Code snippet will not be used.."))
|
||||
g = self.gc_header + '\n' + gcode + postamble + end_gcode
|
||||
# try:
|
||||
# g_idx = gcode.index('G94')
|
||||
# if preamble != '' and postamble != '':
|
||||
# g = self.gc_header + gcode[:g_idx + 3] + '\n' + preamble + '\n' + \
|
||||
# gcode[(g_idx + 3):] + postamble + end_gcode
|
||||
# elif preamble == '':
|
||||
# g = self.gc_header + gcode[:g_idx + 3] + '\n' + \
|
||||
# gcode[(g_idx + 3):] + postamble + end_gcode
|
||||
# elif postamble == '':
|
||||
# g = self.gc_header + gcode[:g_idx + 3] + '\n' + preamble + '\n' + \
|
||||
# gcode[(g_idx + 3):] + end_gcode
|
||||
# else:
|
||||
# g = self.gc_header + gcode[:g_idx + 3] + gcode[(g_idx + 3):] + end_gcode
|
||||
# except ValueError:
|
||||
# self.app.inform.emit('[ERROR_NOTCL] %s' %
|
||||
# _("G-code does not have a G94 code.\n"
|
||||
# "Append Code snippet will not be used.."))
|
||||
# g = self.gc_header + '\n' + gcode + postamble + end_gcode
|
||||
if preamble != '' and postamble != '':
|
||||
g = self.gc_header + self.gc_start + '\n' + preamble + '\n' + gcode + '\n' + \
|
||||
postamble + '\n' + end_gcode
|
||||
if preamble == '':
|
||||
g = self.gc_header + self.gc_start + '\n' + gcode + '\n' + postamble + '\n' + end_gcode
|
||||
if postamble == '':
|
||||
g = self.gc_header + self.gc_start + '\n' + preamble + '\n' + gcode + '\n' + end_gcode
|
||||
if preamble == '' and postamble == '':
|
||||
g = self.gc_header + self.gc_start + '\n' + gcode + '\n' + end_gcode
|
||||
|
||||
# if toolchange custom is used, replace M6 code with the code from the Toolchange Custom Text box
|
||||
# if self.ui.toolchange_cb.get_value() is True:
|
||||
|
|
|
@ -2172,10 +2172,13 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||
job_obj.options['type'] = 'Geometry'
|
||||
job_obj.options['tool_dia'] = tooldia_val
|
||||
|
||||
tool_lst = list(tools_dict.keys())
|
||||
is_first = True if tooluid_key == tool_lst[0] else False
|
||||
|
||||
# it seems that the tolerance needs to be a lot lower value than 0.01 and it was hardcoded initially
|
||||
# to a value of 0.0005 which is 20 times less than 0.01
|
||||
tol = float(self.app.defaults['global_tolerance']) / 20
|
||||
res = job_obj.generate_from_geometry_2(
|
||||
res, start_gcode = job_obj.generate_from_geometry_2(
|
||||
self, tooldia=tooldia_val, offset=tool_offset, tolerance=tol,
|
||||
z_cut=z_cut, z_move=z_move,
|
||||
feedrate=feedrate, feedrate_z=feedrate_z, feedrate_rapid=feedrate_rapid,
|
||||
|
@ -2184,13 +2187,15 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||
extracut=extracut, extracut_length=extracut_length, startz=startz, endz=endz, endxy=endxy,
|
||||
toolchange=toolchange, toolchangez=toolchangez, toolchangexy=toolchangexy,
|
||||
pp_geometry_name=pp_geometry_name,
|
||||
tool_no=tool_cnt)
|
||||
tool_no=tool_cnt, is_first=is_first)
|
||||
|
||||
if res == 'fail':
|
||||
log.debug("GeometryObject.mtool_gen_cncjob() --> generate_from_geometry2() failed")
|
||||
return 'fail'
|
||||
else:
|
||||
dia_cnc_dict['gcode'] = res
|
||||
|
||||
dia_cnc_dict['gcode'] = res
|
||||
if start_gcode != '':
|
||||
job_obj.gc_start = start_gcode
|
||||
|
||||
total_gcode += res
|
||||
|
||||
|
@ -2216,7 +2221,7 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||
})
|
||||
dia_cnc_dict.clear()
|
||||
|
||||
job_obj.source_file = total_gcode
|
||||
job_obj.source_file = job_obj.gc_start + total_gcode
|
||||
|
||||
# Object initialization function for app.app_obj.new_object()
|
||||
# RUNNING ON SEPARATE THREAD!
|
||||
|
@ -2513,18 +2518,17 @@ class GeometryObject(FlatCAMObj, Geometry):
|
|||
# it seems that the tolerance needs to be a lot lower value than 0.01 and it was hardcoded initially
|
||||
# to a value of 0.0005 which is 20 times less than 0.01
|
||||
tol = float(self.app.defaults['global_tolerance']) / 20
|
||||
res = job_obj.generate_from_geometry_2(self, tooldia=tooldia, offset=offset, tolerance=tol,
|
||||
z_cut=z_cut, z_move=z_move, feedrate=feedrate,
|
||||
feedrate_z=feedrate_z, feedrate_rapid=feedrate_rapid,
|
||||
spindlespeed=spindlespeed, dwell=dwell, dwelltime=dwelltime,
|
||||
multidepth=multidepth, depthpercut=depthperpass,
|
||||
toolchange=toolchange, toolchangez=toolchangez,
|
||||
toolchangexy=toolchangexy,
|
||||
extracut=extracut, extracut_length=extracut_length,
|
||||
startz=startz, endz=endz, endxy=endxy,
|
||||
pp_geometry_name=ppname_g)
|
||||
res, start_gcode = job_obj.generate_from_geometry_2(
|
||||
self, tooldia=tooldia, offset=offset, tolerance=tol, z_cut=z_cut, z_move=z_move, feedrate=feedrate,
|
||||
feedrate_z=feedrate_z, feedrate_rapid=feedrate_rapid, spindlespeed=spindlespeed, dwell=dwell,
|
||||
dwelltime=dwelltime, multidepth=multidepth, depthpercut=depthperpass, toolchange=toolchange,
|
||||
toolchangez=toolchangez, toolchangexy=toolchangexy, extracut=extracut, extracut_length=extracut_length,
|
||||
startz=startz, endz=endz, endxy=endxy, pp_geometry_name=ppname_g, is_first=True)
|
||||
|
||||
job_obj.source_file = res
|
||||
if start_gcode != '':
|
||||
job_obj.gc_start = start_gcode
|
||||
|
||||
job_obj.source_file = start_gcode + res
|
||||
# tell gcode_parse from which point to start drawing the lines depending on what kind of object is the
|
||||
# source of gcode
|
||||
job_obj.toolchange_xy_type = "geometry"
|
||||
|
|
844
camlib.py
844
camlib.py
|
@ -3109,6 +3109,7 @@ class CNCjob(Geometry):
|
|||
self.app.inform.emit('[WARNING] %s.' % _("The Cut Z parameter is zero. There will be no cut, aborting"))
|
||||
return 'fail'
|
||||
|
||||
# used in Tool Drilling
|
||||
def excellon_tool_gcode_gen(self, tool, points, tools, first_pt, is_first=False, is_last=False, opt_type='T',
|
||||
toolchange=False):
|
||||
"""
|
||||
|
@ -3295,7 +3296,7 @@ class CNCjob(Geometry):
|
|||
start_gcode = ''
|
||||
if is_first:
|
||||
start_gcode = self.doformat(p.start_code)
|
||||
t_gcode += start_gcode
|
||||
# t_gcode += start_gcode
|
||||
|
||||
# do the ToolChange event
|
||||
t_gcode += self.doformat(p.z_feedrate_code)
|
||||
|
@ -3308,11 +3309,9 @@ class CNCjob(Geometry):
|
|||
if self.dwell is True:
|
||||
t_gcode += self.doformat(p.dwell_code)
|
||||
|
||||
current_tooldia = float('%.*f' % (self.decimals, float(tools[tool]["tooldia"])))
|
||||
current_tooldia = self.app.dec_format(float(tools[tool]["tooldia"]), self.decimals)
|
||||
self.app.inform.emit(
|
||||
'%s: %s%s.' % (_("Starting G-Code for tool with diameter"),
|
||||
str(current_tooldia),
|
||||
str(self.units))
|
||||
'%s: %s%s.' % (_("Starting G-Code for tool with diameter"), str(current_tooldia), str(self.units))
|
||||
)
|
||||
|
||||
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
@ -3340,6 +3339,7 @@ class CNCjob(Geometry):
|
|||
# graceful abort requested by the user
|
||||
raise grace
|
||||
|
||||
# if we use Traveling Salesman Algorithm as an optimization
|
||||
if opt_type == 'T':
|
||||
locx = point[0]
|
||||
locy = point[1]
|
||||
|
@ -3447,21 +3447,395 @@ class CNCjob(Geometry):
|
|||
self.app.inform.emit('%s %s' % (_("Finished G-Code generation for tool:"), str(tool)))
|
||||
return t_gcode, (locx, locy), start_gcode
|
||||
|
||||
def generate_from_excellon_by_tool(self, exobj, tools="all", order='fwd', use_ui=False):
|
||||
# used in Geometry (and soon in Tool Milling)
|
||||
def geometry_tool_gcode_gen(self, tool, tools, first_pt, tolerance, is_first=False, is_last=False,
|
||||
toolchange=False):
|
||||
"""
|
||||
Algorithm to generate GCode from multitool Geometry.
|
||||
|
||||
:param tool: tool number for which to generate GCode
|
||||
:type tool: int
|
||||
:param tools: a dictionary holding all the tools and data
|
||||
:type tools: dict
|
||||
:param first_pt: a tuple of coordinates for the first point of the current tool
|
||||
:type first_pt: tuple
|
||||
:param tolerance: geometry tolerance
|
||||
:type tolerance:
|
||||
:param is_first: if the current tool is the first tool (for this we need to add start GCode)
|
||||
:type is_first: bool
|
||||
:param is_last: if the current tool is the last tool (for this we need to add the end GCode)
|
||||
:type is_last: bool
|
||||
:param toolchange: add toolchange event
|
||||
:type toolchange: bool
|
||||
:return: GCode
|
||||
:rtype: str
|
||||
"""
|
||||
|
||||
log.debug("geometry_tool_gcode_gen()")
|
||||
|
||||
t_gcode = ''
|
||||
temp_solid_geometry = []
|
||||
|
||||
# The Geometry from which we create GCode
|
||||
geometry = tools[tool]['solid_geometry']
|
||||
# ## Flatten the geometry. Only linear elements (no polygons) remain.
|
||||
flat_geometry = self.flatten(geometry, reset=True, pathonly=True)
|
||||
log.debug("%d paths" % len(flat_geometry))
|
||||
|
||||
# #########################################################################################################
|
||||
# #########################################################################################################
|
||||
# ############# PARAMETERS used in PREPROCESSORS so they need to be updated ###############################
|
||||
# #########################################################################################################
|
||||
# #########################################################################################################
|
||||
self.tool = str(tool)
|
||||
tool_dict = tools[tool]['data']
|
||||
# this is the tool diameter, it is used as such to accommodate the preprocessor who need the tool diameter
|
||||
# given under the name 'toolC'
|
||||
self.postdata['toolC'] = float(tools[tool]['tooldia'])
|
||||
self.tooldia = float(tools[tool]['tooldia'])
|
||||
self.use_ui = True
|
||||
self.tolerance = tolerance
|
||||
|
||||
# Optimization type. Can be: 'M', 'B', 'T', 'R', 'No'
|
||||
opt_type = tool_dict['optimization_type']
|
||||
opt_time = tool_dict['search_time'] if 'search_time' in tool_dict else 'R'
|
||||
|
||||
if opt_type == 'M':
|
||||
log.debug("Using OR-Tools Metaheuristic Guided Local Search path optimization.")
|
||||
elif opt_type == 'B':
|
||||
log.debug("Using OR-Tools Basic path optimization.")
|
||||
elif opt_type == 'T':
|
||||
log.debug("Using Travelling Salesman path optimization.")
|
||||
elif opt_type == 'R':
|
||||
log.debug("Using RTree path optimization.")
|
||||
else:
|
||||
log.debug("Using no path optimization.")
|
||||
|
||||
# Preprocessor
|
||||
self.pp_geometry_name = tool_dict['ppname_g']
|
||||
self.pp_geometry = self.app.preprocessors[self.pp_geometry_name]
|
||||
p = self.pp_geometry
|
||||
|
||||
# Offset the Geometry if it is the case
|
||||
# FIXME need to test if in ["Path", "In", "Out", "Custom"]. For now only 'Custom' is somehow done
|
||||
offset = tools[tool]['offset_value']
|
||||
if offset != 0.0:
|
||||
for it in flat_geometry:
|
||||
# if the geometry is a closed shape then create a Polygon out of it
|
||||
if isinstance(it, LineString):
|
||||
if it.is_ring:
|
||||
it = Polygon(it)
|
||||
temp_solid_geometry.append(it.buffer(offset, join_style=2))
|
||||
temp_solid_geometry = self.flatten(temp_solid_geometry, reset=True, pathonly=True)
|
||||
else:
|
||||
temp_solid_geometry = flat_geometry
|
||||
|
||||
if self.z_cut is None:
|
||||
if 'laser' not in self.pp_geometry_name:
|
||||
self.app.inform.emit(
|
||||
'[ERROR_NOTCL] %s' % _("Cut_Z parameter is None or zero. Most likely a bad combinations of "
|
||||
"other parameters."))
|
||||
return 'fail'
|
||||
else:
|
||||
self.z_cut = 0
|
||||
if self.machinist_setting == 0:
|
||||
if self.z_cut > 0:
|
||||
self.app.inform.emit('[WARNING] %s' %
|
||||
_("The Cut Z parameter has positive value. "
|
||||
"It is the depth value to cut into material.\n"
|
||||
"The Cut Z parameter needs to have a negative value, assuming it is a typo "
|
||||
"therefore the app will convert the value to negative."
|
||||
"Check the resulting CNC code (Gcode etc)."))
|
||||
self.z_cut = -self.z_cut
|
||||
elif self.z_cut == 0 and 'laser' not in self.pp_geometry_name:
|
||||
self.app.inform.emit('[WARNING] %s: %s' %
|
||||
(_("The Cut Z parameter is zero. There will be no cut, skipping file"),
|
||||
self.options['name']))
|
||||
return 'fail'
|
||||
|
||||
if self.z_move is None:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' % _("Travel Z parameter is None or zero."))
|
||||
return 'fail'
|
||||
|
||||
if self.z_move < 0:
|
||||
self.app.inform.emit('[WARNING] %s' %
|
||||
_("The Travel Z parameter has negative value. "
|
||||
"It is the height value to travel between cuts.\n"
|
||||
"The Z Travel parameter needs to have a positive value, assuming it is a typo "
|
||||
"therefore the app will convert the value to positive."
|
||||
"Check the resulting CNC code (Gcode etc)."))
|
||||
self.z_move = -self.z_move
|
||||
elif self.z_move == 0:
|
||||
self.app.inform.emit('[WARNING] %s: %s' %
|
||||
(_("The Z Travel parameter is zero. This is dangerous, skipping file"),
|
||||
self.options['name']))
|
||||
return 'fail'
|
||||
|
||||
# made sure that depth_per_cut is no more then the z_cut
|
||||
if abs(self.z_cut) < self.z_depthpercut:
|
||||
self.z_depthpercut = abs(self.z_cut)
|
||||
|
||||
# Depth parameters
|
||||
self.z_cut = float(tool_dict['cutz'])
|
||||
self.multidepth = tool_dict['multidepth']
|
||||
self.z_depthpercut = float(tool_dict['depthperpass'])
|
||||
self.z_move = float(tool_dict['travelz'])
|
||||
self.f_plunge = self.app.defaults["geometry_f_plunge"]
|
||||
|
||||
self.feedrate = float(tool_dict['feedrate'])
|
||||
self.z_feedrate = float(tool_dict['feedrate_z'])
|
||||
self.feedrate_rapid = float(tool_dict['feedrate_rapid'])
|
||||
|
||||
self.spindlespeed = float(tool_dict['spindlespeed'])
|
||||
try:
|
||||
self.spindledir = tool_dict['spindledir']
|
||||
except KeyError:
|
||||
self.spindledir = self.app.defaults["geometry_spindledir"]
|
||||
|
||||
self.dwell = tool_dict['dwell']
|
||||
self.dwelltime = float(tool_dict['dwelltime'])
|
||||
|
||||
self.startz = float(tool_dict['startz']) if tool_dict['startz'] else None
|
||||
if self.startz == '':
|
||||
self.startz = None
|
||||
|
||||
self.z_end = float(tool_dict['endz'])
|
||||
self.xy_end = tool_dict['endxy']
|
||||
try:
|
||||
if self.xy_end == '' or self.xy_end is None:
|
||||
self.xy_end = None
|
||||
else:
|
||||
# either originally it was a string or not, xy_end will be made string
|
||||
self.xy_end = re.sub('[()\[\]]', '', str(self.xy_end)) if self.xy_end else None
|
||||
|
||||
# and now, xy_end is made into a list of floats in format [x, y]
|
||||
if self.xy_end:
|
||||
self.xy_end = [float(eval(a)) for a in self.xy_end.split(",")]
|
||||
|
||||
if self.xy_end and len(self.xy_end) != 2:
|
||||
self.app.inform.emit('[ERROR]%s' % _("The End X,Y format has to be (x, y)."))
|
||||
return 'fail'
|
||||
except Exception as e:
|
||||
log.debug("camlib.CNCJob.geometry_from_excellon_by_tool() xy_end --> %s" % str(e))
|
||||
self.xy_end = [0, 0]
|
||||
|
||||
self.z_toolchange = tool_dict['toolchangez']
|
||||
self.xy_toolchange = tool_dict["toolchangexy"]
|
||||
try:
|
||||
if self.xy_toolchange == '':
|
||||
self.xy_toolchange = None
|
||||
else:
|
||||
# either originally it was a string or not, xy_toolchange will be made string
|
||||
self.xy_toolchange = re.sub('[()\[\]]', '', str(self.xy_toolchange)) if self.xy_toolchange else None
|
||||
|
||||
# and now, xy_toolchange is made into a list of floats in format [x, y]
|
||||
if self.xy_toolchange:
|
||||
self.xy_toolchange = [float(eval(a)) for a in self.xy_toolchange.split(",")]
|
||||
|
||||
if self.xy_toolchange and len(self.xy_toolchange) != 2:
|
||||
self.app.inform.emit('[ERROR] %s' % _("The Toolchange X,Y format has to be (x, y)."))
|
||||
return 'fail'
|
||||
except Exception as e:
|
||||
log.debug("camlib.CNCJob.geometry_from_excellon_by_tool() --> %s" % str(e))
|
||||
pass
|
||||
|
||||
self.extracut = tool_dict['extracut']
|
||||
self.extracut_length = tool_dict['extracut_length']
|
||||
|
||||
# Probe parameters
|
||||
# self.z_pdepth = tool_dict["tools_drill_z_pdepth"]
|
||||
# self.feedrate_probe = tool_dict["tools_drill_feedrate_probe"]
|
||||
|
||||
# #########################################################################################################
|
||||
# ############ Create the data. ###########################################################################
|
||||
# #########################################################################################################
|
||||
optimized_path = []
|
||||
|
||||
geo_storage = {}
|
||||
for geo in temp_solid_geometry:
|
||||
if not geo is None:
|
||||
geo_storage[geo.coords[0]] = geo
|
||||
locations = list(geo_storage.keys())
|
||||
|
||||
if opt_type == 'M':
|
||||
# if there are no locations then go to the next tool
|
||||
if not locations:
|
||||
return 'fail'
|
||||
optimized_locations = self.optimized_ortools_meta(locations=locations, opt_time=opt_time)
|
||||
optimized_path = [(locations[loc], geo_storage[locations[loc]]) for loc in optimized_locations]
|
||||
elif opt_type == 'B':
|
||||
# if there are no locations then go to the next tool
|
||||
if not locations:
|
||||
return 'fail'
|
||||
optimized_locations = self.optimized_ortools_basic(locations=locations)
|
||||
optimized_path = [(locations[loc], geo_storage[locations[loc]]) for loc in optimized_locations]
|
||||
elif opt_type == 'T':
|
||||
# if there are no locations then go to the next tool
|
||||
if not locations:
|
||||
return 'fail'
|
||||
optimized_locations = self.optimized_travelling_salesman(locations)
|
||||
optimized_path = [(loc, geo_storage[loc]) for loc in optimized_locations]
|
||||
elif opt_type == 'R':
|
||||
optimized_path = self.geo_optimized_rtree(temp_solid_geometry)
|
||||
if optimized_path == 'fail':
|
||||
return 'fail'
|
||||
else:
|
||||
# it's actually not optimized path but here we build a list of (x,y) coordinates
|
||||
# out of the tool's drills
|
||||
for geo in temp_solid_geometry:
|
||||
optimized_path.append(geo.coords[0])
|
||||
# #########################################################################################################
|
||||
# #########################################################################################################
|
||||
|
||||
# Only if there are locations to mill
|
||||
if not optimized_path:
|
||||
log.debug("camlib.CNCJob.geometry_tool_gcode_gen() -> Optimized path is empty.")
|
||||
return 'fail'
|
||||
|
||||
if self.app.abort_flag:
|
||||
# graceful abort requested by the user
|
||||
raise grace
|
||||
|
||||
# #############################################################################################################
|
||||
# #############################################################################################################
|
||||
# ################# MILLING !!! ##############################################################################
|
||||
# #############################################################################################################
|
||||
# #############################################################################################################
|
||||
log.debug("Starting G-Code...")
|
||||
|
||||
current_tooldia = float('%.*f' % (self.decimals, float(self.tooldia)))
|
||||
self.app.inform.emit('%s: %s%s.' % (_("Starting G-Code for tool with diameter"),
|
||||
str(current_tooldia),
|
||||
str(self.units)))
|
||||
|
||||
# Measurements
|
||||
total_travel = 0.0
|
||||
total_cut = 0.0
|
||||
|
||||
# Start GCode
|
||||
start_gcode = ''
|
||||
if is_first:
|
||||
start_gcode = self.doformat(p.start_code)
|
||||
# t_gcode += start_gcode
|
||||
|
||||
# Toolchange code
|
||||
t_gcode += self.doformat(p.feedrate_code) # sets the feed rate
|
||||
if toolchange:
|
||||
t_gcode += self.doformat(p.toolchange_code)
|
||||
|
||||
if 'laser' not in self.pp_geometry_name.lower():
|
||||
t_gcode += self.doformat(p.spindle_code) # Spindle start
|
||||
else:
|
||||
# for laser this will disable the laser
|
||||
t_gcode += self.doformat(p.lift_code, x=self.oldx, y=self.oldy) # Move (up) to travel height
|
||||
|
||||
if self.dwell:
|
||||
t_gcode += self.doformat(p.dwell_code) # Dwell time
|
||||
else:
|
||||
t_gcode += self.doformat(p.lift_code, x=0, y=0) # Move (up) to travel height
|
||||
t_gcode += self.doformat(p.startz_code, x=0, y=0)
|
||||
|
||||
if 'laser' not in self.pp_geometry_name.lower():
|
||||
t_gcode += self.doformat(p.spindle_code) # Spindle start
|
||||
|
||||
if self.dwell is True:
|
||||
t_gcode += self.doformat(p.dwell_code) # Dwell time
|
||||
t_gcode += self.doformat(p.feedrate_code) # sets the feed rate
|
||||
|
||||
# ## Iterate over geometry paths getting the nearest each time.
|
||||
path_count = 0
|
||||
|
||||
# variables to display the percentage of work done
|
||||
geo_len = len(flat_geometry)
|
||||
log.warning("Number of paths for which to generate GCode: %s" % str(geo_len))
|
||||
old_disp_number = 0
|
||||
|
||||
current_pt = (0, 0)
|
||||
for pt, geo in optimized_path:
|
||||
if self.app.abort_flag:
|
||||
# graceful abort requested by the user
|
||||
raise grace
|
||||
|
||||
path_count += 1
|
||||
|
||||
# If last point in geometry is the nearest but prefer the first one if last point == first point
|
||||
# then reverse coordinates.
|
||||
if pt != geo.coords[0] and pt == geo.coords[-1]:
|
||||
geo = LineString(list(geo.coords)[::-1])
|
||||
|
||||
# ---------- Single depth/pass --------
|
||||
if not self.multidepth:
|
||||
# calculate the cut distance
|
||||
total_cut = total_cut + geo.length
|
||||
|
||||
t_gcode += self.create_gcode_single_pass(geo, current_tooldia, self.extracut,
|
||||
self.extracut_length, self.tolerance,
|
||||
z_move=self.z_move, old_point=current_pt)
|
||||
|
||||
# --------- Multi-pass ---------
|
||||
else:
|
||||
# calculate the cut distance
|
||||
# due of the number of cuts (multi depth) it has to multiplied by the number of cuts
|
||||
nr_cuts = 0
|
||||
depth = abs(self.z_cut)
|
||||
while depth > 0:
|
||||
nr_cuts += 1
|
||||
depth -= float(self.z_depthpercut)
|
||||
|
||||
total_cut += (geo.length * nr_cuts)
|
||||
|
||||
gc, geo = self.create_gcode_multi_pass(geo, current_tooldia, self.extracut,
|
||||
self.extracut_length, self.tolerance,
|
||||
z_move=self.z_move, postproc=p, old_point=current_pt)
|
||||
t_gcode += gc
|
||||
|
||||
# calculate the total distance
|
||||
total_travel = total_travel + abs(distance(pt1=current_pt, pt2=pt))
|
||||
current_pt = geo.coords[-1]
|
||||
|
||||
disp_number = int(np.interp(path_count, [0, geo_len], [0, 100]))
|
||||
if old_disp_number < disp_number <= 100:
|
||||
self.app.proc_container.update_view_text(' %d%%' % disp_number)
|
||||
old_disp_number = disp_number
|
||||
|
||||
log.debug("Finished G-Code... %s paths traced." % path_count)
|
||||
|
||||
# add move to end position
|
||||
total_travel += abs(distance_euclidian(current_pt[0], current_pt[1], 0, 0))
|
||||
self.travel_distance += total_travel + total_cut
|
||||
self.routing_time += total_cut / self.feedrate
|
||||
|
||||
# Finish
|
||||
if is_last:
|
||||
t_gcode += self.doformat(p.spindle_stop_code)
|
||||
t_gcode += self.doformat(p.lift_code, x=current_pt[0], y=current_pt[1])
|
||||
t_gcode += self.doformat(p.end_code, x=0, y=0)
|
||||
self.app.inform.emit(
|
||||
'%s... %s %s.' % (_("Finished G-Code generation"), str(path_count), _("paths traced"))
|
||||
)
|
||||
|
||||
self.gcode = t_gcode
|
||||
return self.gcode, start_gcode
|
||||
|
||||
# used by the Tcl command Drillcncjob
|
||||
def generate_from_excellon_by_tool(self, exobj, tools="all", order='fwd', is_first=False, use_ui=False):
|
||||
"""
|
||||
Creates Gcode for this object from an Excellon object
|
||||
for the specified tools.
|
||||
|
||||
:param exobj: Excellon object to process
|
||||
:type exobj: Excellon
|
||||
:param tools: Comma separated tool names
|
||||
:type tools: str
|
||||
:param order: order of tools processing: "fwd", "rev" or "no"
|
||||
:type order: str
|
||||
:param use_ui: if True the method will use parameters set in UI
|
||||
:type use_ui: bool
|
||||
:return: None
|
||||
:rtype: None
|
||||
:param exobj: Excellon object to process
|
||||
:type exobj: Excellon
|
||||
:param tools: Comma separated tool names
|
||||
:type tools: str
|
||||
:param order: order of tools processing: "fwd", "rev" or "no"
|
||||
:type order: str
|
||||
:param is_first: if the tool is the first one should generate the start gcode (not that it matter much
|
||||
which is the one doing it)
|
||||
:type is_first: bool
|
||||
:param use_ui: if True the method will use parameters set in UI
|
||||
:type use_ui: bool
|
||||
:return: None
|
||||
:rtype: None
|
||||
"""
|
||||
|
||||
# #############################################################################################################
|
||||
|
@ -3651,7 +4025,11 @@ class CNCjob(Geometry):
|
|||
# Initialization
|
||||
# #############################################################################################################
|
||||
# #############################################################################################################
|
||||
gcode = self.doformat(p.start_code)
|
||||
gcode = ''
|
||||
start_gcode = ''
|
||||
if is_first:
|
||||
start_gcode = self.doformat(p.start_code)
|
||||
|
||||
if use_ui is False:
|
||||
gcode += self.doformat(p.z_feedrate_code)
|
||||
|
||||
|
@ -4829,8 +5207,9 @@ class CNCjob(Geometry):
|
|||
self.gcode = gcode
|
||||
|
||||
self.app.inform.emit(_("Finished G-Code generation..."))
|
||||
return 'OK'
|
||||
return gcode, start_gcode
|
||||
|
||||
# no longer used
|
||||
def generate_from_multitool_geometry(self, geometry, append=True, tooldia=None, offset=0.0, tolerance=0, z_cut=1.0,
|
||||
z_move=2.0, feedrate=2.0, feedrate_z=2.0, feedrate_rapid=30,
|
||||
spindlespeed=None, spindledir='CW', dwell=False, dwelltime=1.0,
|
||||
|
@ -5163,381 +5542,12 @@ class CNCjob(Geometry):
|
|||
)
|
||||
return self.gcode
|
||||
|
||||
def geometry_tool_gcode_gen(self, tool, tools, first_pt, tolerance, is_first=False, is_last=False,
|
||||
toolchange=False):
|
||||
"""
|
||||
Algorithm to generate GCode from multitool Geometry.
|
||||
|
||||
:param tool: tool number for which to generate GCode
|
||||
:type tool: int
|
||||
:param tools: a dictionary holding all the tools and data
|
||||
:type tools: dict
|
||||
:param first_pt: a tuple of coordinates for the first point of the current tool
|
||||
:type first_pt: tuple
|
||||
:param tolerance: geometry tolerance
|
||||
:type tolerance:
|
||||
:param is_first: if the current tool is the first tool (for this we need to add start GCode)
|
||||
:type is_first: bool
|
||||
:param is_last: if the current tool is the last tool (for this we need to add the end GCode)
|
||||
:type is_last: bool
|
||||
:param toolchange: add toolchange event
|
||||
:type toolchange: bool
|
||||
:return: GCode
|
||||
:rtype: str
|
||||
"""
|
||||
|
||||
log.debug("geometry_tool_gcode_gen()")
|
||||
|
||||
t_gcode = ''
|
||||
temp_solid_geometry = []
|
||||
|
||||
# The Geometry from which we create GCode
|
||||
geometry = tools[tool]['solid_geometry']
|
||||
# ## Flatten the geometry. Only linear elements (no polygons) remain.
|
||||
flat_geometry = self.flatten(geometry, reset=True, pathonly=True)
|
||||
log.debug("%d paths" % len(flat_geometry))
|
||||
|
||||
# #########################################################################################################
|
||||
# #########################################################################################################
|
||||
# ############# PARAMETERS used in PREPROCESSORS so they need to be updated ###############################
|
||||
# #########################################################################################################
|
||||
# #########################################################################################################
|
||||
self.tool = str(tool)
|
||||
tool_dict = tools[tool]['data']
|
||||
# this is the tool diameter, it is used as such to accommodate the preprocessor who need the tool diameter
|
||||
# given under the name 'toolC'
|
||||
self.postdata['toolC'] = float(tools[tool]['tooldia'])
|
||||
self.tooldia = float(tools[tool]['tooldia'])
|
||||
self.use_ui = True
|
||||
self.tolerance = tolerance
|
||||
|
||||
# Optimization type. Can be: 'M', 'B', 'T', 'R', 'No'
|
||||
opt_type = tool_dict['optimization_type']
|
||||
opt_time = tool_dict['search_time'] if 'search_time' in tool_dict else 'R'
|
||||
|
||||
if opt_type == 'M':
|
||||
log.debug("Using OR-Tools Metaheuristic Guided Local Search path optimization.")
|
||||
elif opt_type == 'B':
|
||||
log.debug("Using OR-Tools Basic path optimization.")
|
||||
elif opt_type == 'T':
|
||||
log.debug("Using Travelling Salesman path optimization.")
|
||||
elif opt_type == 'R':
|
||||
log.debug("Using RTree path optimization.")
|
||||
else:
|
||||
log.debug("Using no path optimization.")
|
||||
|
||||
# Preprocessor
|
||||
self.pp_geometry_name = tool_dict['ppname_g']
|
||||
self.pp_geometry = self.app.preprocessors[self.pp_geometry_name]
|
||||
p = self.pp_geometry
|
||||
|
||||
# Offset the Geometry if it is the case
|
||||
# FIXME need to test if in ["Path", "In", "Out", "Custom"]. For now only 'Custom' is somehow done
|
||||
offset = tools[tool]['offset_value']
|
||||
if offset != 0.0:
|
||||
for it in flat_geometry:
|
||||
# if the geometry is a closed shape then create a Polygon out of it
|
||||
if isinstance(it, LineString):
|
||||
if it.is_ring:
|
||||
it = Polygon(it)
|
||||
temp_solid_geometry.append(it.buffer(offset, join_style=2))
|
||||
temp_solid_geometry = self.flatten(temp_solid_geometry, reset=True, pathonly=True)
|
||||
else:
|
||||
temp_solid_geometry = flat_geometry
|
||||
|
||||
if self.z_cut is None:
|
||||
if 'laser' not in self.pp_geometry_name:
|
||||
self.app.inform.emit(
|
||||
'[ERROR_NOTCL] %s' % _("Cut_Z parameter is None or zero. Most likely a bad combinations of "
|
||||
"other parameters."))
|
||||
return 'fail'
|
||||
else:
|
||||
self.z_cut = 0
|
||||
if self.machinist_setting == 0:
|
||||
if self.z_cut > 0:
|
||||
self.app.inform.emit('[WARNING] %s' %
|
||||
_("The Cut Z parameter has positive value. "
|
||||
"It is the depth value to cut into material.\n"
|
||||
"The Cut Z parameter needs to have a negative value, assuming it is a typo "
|
||||
"therefore the app will convert the value to negative."
|
||||
"Check the resulting CNC code (Gcode etc)."))
|
||||
self.z_cut = -self.z_cut
|
||||
elif self.z_cut == 0 and 'laser' not in self.pp_geometry_name:
|
||||
self.app.inform.emit('[WARNING] %s: %s' %
|
||||
(_("The Cut Z parameter is zero. There will be no cut, skipping file"),
|
||||
self.options['name']))
|
||||
return 'fail'
|
||||
|
||||
if self.z_move is None:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' % _("Travel Z parameter is None or zero."))
|
||||
return 'fail'
|
||||
|
||||
if self.z_move < 0:
|
||||
self.app.inform.emit('[WARNING] %s' %
|
||||
_("The Travel Z parameter has negative value. "
|
||||
"It is the height value to travel between cuts.\n"
|
||||
"The Z Travel parameter needs to have a positive value, assuming it is a typo "
|
||||
"therefore the app will convert the value to positive."
|
||||
"Check the resulting CNC code (Gcode etc)."))
|
||||
self.z_move = -self.z_move
|
||||
elif self.z_move == 0:
|
||||
self.app.inform.emit('[WARNING] %s: %s' %
|
||||
(_("The Z Travel parameter is zero. This is dangerous, skipping file"),
|
||||
self.options['name']))
|
||||
return 'fail'
|
||||
|
||||
# made sure that depth_per_cut is no more then the z_cut
|
||||
if abs(self.z_cut) < self.z_depthpercut:
|
||||
self.z_depthpercut = abs(self.z_cut)
|
||||
|
||||
# Depth parameters
|
||||
self.z_cut = float(tool_dict['cutz'])
|
||||
self.multidepth = tool_dict['multidepth']
|
||||
self.z_depthpercut = float(tool_dict['depthperpass'])
|
||||
self.z_move = float(tool_dict['travelz'])
|
||||
self.f_plunge = self.app.defaults["geometry_f_plunge"]
|
||||
|
||||
self.feedrate = float(tool_dict['feedrate'])
|
||||
self.z_feedrate = float(tool_dict['feedrate_z'])
|
||||
self.feedrate_rapid = float(tool_dict['feedrate_rapid'])
|
||||
|
||||
self.spindlespeed = float(tool_dict['spindlespeed'])
|
||||
try:
|
||||
self.spindledir = tool_dict['spindledir']
|
||||
except KeyError:
|
||||
self.spindledir = self.app.defaults["geometry_spindledir"]
|
||||
|
||||
self.dwell = tool_dict['dwell']
|
||||
self.dwelltime = float(tool_dict['dwelltime'])
|
||||
|
||||
self.startz = float(tool_dict['startz']) if tool_dict['startz'] else None
|
||||
if self.startz == '':
|
||||
self.startz = None
|
||||
|
||||
self.z_end = float(tool_dict['endz'])
|
||||
self.xy_end = tool_dict['endxy']
|
||||
try:
|
||||
if self.xy_end == '' or self.xy_end is None:
|
||||
self.xy_end = None
|
||||
else:
|
||||
# either originally it was a string or not, xy_end will be made string
|
||||
self.xy_end = re.sub('[()\[\]]', '', str(self.xy_end)) if self.xy_end else None
|
||||
|
||||
# and now, xy_end is made into a list of floats in format [x, y]
|
||||
if self.xy_end:
|
||||
self.xy_end = [float(eval(a)) for a in self.xy_end.split(",")]
|
||||
|
||||
if self.xy_end and len(self.xy_end) != 2:
|
||||
self.app.inform.emit('[ERROR]%s' % _("The End X,Y format has to be (x, y)."))
|
||||
return 'fail'
|
||||
except Exception as e:
|
||||
log.debug("camlib.CNCJob.geometry_from_excellon_by_tool() xy_end --> %s" % str(e))
|
||||
self.xy_end = [0, 0]
|
||||
|
||||
self.z_toolchange = tool_dict['toolchangez']
|
||||
self.xy_toolchange = tool_dict["toolchangexy"]
|
||||
try:
|
||||
if self.xy_toolchange == '':
|
||||
self.xy_toolchange = None
|
||||
else:
|
||||
# either originally it was a string or not, xy_toolchange will be made string
|
||||
self.xy_toolchange = re.sub('[()\[\]]', '', str(self.xy_toolchange)) if self.xy_toolchange else None
|
||||
|
||||
# and now, xy_toolchange is made into a list of floats in format [x, y]
|
||||
if self.xy_toolchange:
|
||||
self.xy_toolchange = [float(eval(a)) for a in self.xy_toolchange.split(",")]
|
||||
|
||||
if self.xy_toolchange and len(self.xy_toolchange) != 2:
|
||||
self.app.inform.emit('[ERROR] %s' % _("The Toolchange X,Y format has to be (x, y)."))
|
||||
return 'fail'
|
||||
except Exception as e:
|
||||
log.debug("camlib.CNCJob.geometry_from_excellon_by_tool() --> %s" % str(e))
|
||||
pass
|
||||
|
||||
self.extracut = tool_dict['extracut']
|
||||
self.extracut_length = tool_dict['extracut_length']
|
||||
|
||||
# Probe parameters
|
||||
# self.z_pdepth = tool_dict["tools_drill_z_pdepth"]
|
||||
# self.feedrate_probe = tool_dict["tools_drill_feedrate_probe"]
|
||||
|
||||
# #########################################################################################################
|
||||
# ############ Create the data. ###########################################################################
|
||||
# #########################################################################################################
|
||||
optimized_path = []
|
||||
|
||||
geo_storage = {}
|
||||
for geo in temp_solid_geometry:
|
||||
if not geo is None:
|
||||
geo_storage[geo.coords[0]] = geo
|
||||
locations = list(geo_storage.keys())
|
||||
|
||||
if opt_type == 'M':
|
||||
# if there are no locations then go to the next tool
|
||||
if not locations:
|
||||
return 'fail'
|
||||
optimized_locations = self.optimized_ortools_meta(locations=locations, opt_time=opt_time)
|
||||
optimized_path = [(locations[loc], geo_storage[locations[loc]]) for loc in optimized_locations]
|
||||
elif opt_type == 'B':
|
||||
# if there are no locations then go to the next tool
|
||||
if not locations:
|
||||
return 'fail'
|
||||
optimized_locations = self.optimized_ortools_basic(locations=locations)
|
||||
optimized_path = [(locations[loc], geo_storage[locations[loc]]) for loc in optimized_locations]
|
||||
elif opt_type == 'T':
|
||||
# if there are no locations then go to the next tool
|
||||
if not locations:
|
||||
return 'fail'
|
||||
optimized_locations = self.optimized_travelling_salesman(locations)
|
||||
optimized_path = [(loc, geo_storage[loc]) for loc in optimized_locations]
|
||||
elif opt_type == 'R':
|
||||
optimized_path = self.geo_optimized_rtree(temp_solid_geometry)
|
||||
if optimized_path == 'fail':
|
||||
return 'fail'
|
||||
else:
|
||||
# it's actually not optimized path but here we build a list of (x,y) coordinates
|
||||
# out of the tool's drills
|
||||
for geo in temp_solid_geometry:
|
||||
optimized_path.append(geo.coords[0])
|
||||
# #########################################################################################################
|
||||
# #########################################################################################################
|
||||
|
||||
# Only if there are locations to mill
|
||||
if not optimized_path:
|
||||
log.debug("camlib.CNCJob.geometry_tool_gcode_gen() -> Optimized path is empty.")
|
||||
return 'fail'
|
||||
|
||||
if self.app.abort_flag:
|
||||
# graceful abort requested by the user
|
||||
raise grace
|
||||
|
||||
# #############################################################################################################
|
||||
# #############################################################################################################
|
||||
# ################# MILLING !!! ##############################################################################
|
||||
# #############################################################################################################
|
||||
# #############################################################################################################
|
||||
log.debug("Starting G-Code...")
|
||||
|
||||
current_tooldia = float('%.*f' % (self.decimals, float(self.tooldia)))
|
||||
self.app.inform.emit('%s: %s%s.' % (_("Starting G-Code for tool with diameter"),
|
||||
str(current_tooldia),
|
||||
str(self.units)))
|
||||
|
||||
# Measurements
|
||||
total_travel = 0.0
|
||||
total_cut = 0.0
|
||||
|
||||
# Start GCode
|
||||
start_gcode = ''
|
||||
if is_first:
|
||||
start_gcode = self.doformat(p.start_code)
|
||||
t_gcode += start_gcode
|
||||
|
||||
# Toolchange code
|
||||
t_gcode += self.doformat(p.feedrate_code) # sets the feed rate
|
||||
if toolchange:
|
||||
t_gcode += self.doformat(p.toolchange_code)
|
||||
|
||||
if 'laser' not in self.pp_geometry_name.lower():
|
||||
t_gcode += self.doformat(p.spindle_code) # Spindle start
|
||||
else:
|
||||
# for laser this will disable the laser
|
||||
t_gcode += self.doformat(p.lift_code, x=self.oldx, y=self.oldy) # Move (up) to travel height
|
||||
|
||||
if self.dwell:
|
||||
t_gcode += self.doformat(p.dwell_code) # Dwell time
|
||||
else:
|
||||
t_gcode += self.doformat(p.lift_code, x=0, y=0) # Move (up) to travel height
|
||||
t_gcode += self.doformat(p.startz_code, x=0, y=0)
|
||||
|
||||
if 'laser' not in self.pp_geometry_name.lower():
|
||||
t_gcode += self.doformat(p.spindle_code) # Spindle start
|
||||
|
||||
if self.dwell is True:
|
||||
t_gcode += self.doformat(p.dwell_code) # Dwell time
|
||||
t_gcode += self.doformat(p.feedrate_code) # sets the feed rate
|
||||
|
||||
# ## Iterate over geometry paths getting the nearest each time.
|
||||
path_count = 0
|
||||
|
||||
# variables to display the percentage of work done
|
||||
geo_len = len(flat_geometry)
|
||||
log.warning("Number of paths for which to generate GCode: %s" % str(geo_len))
|
||||
old_disp_number = 0
|
||||
|
||||
current_pt = (0, 0)
|
||||
for pt, geo in optimized_path:
|
||||
if self.app.abort_flag:
|
||||
# graceful abort requested by the user
|
||||
raise grace
|
||||
|
||||
path_count += 1
|
||||
|
||||
# If last point in geometry is the nearest but prefer the first one if last point == first point
|
||||
# then reverse coordinates.
|
||||
if pt != geo.coords[0] and pt == geo.coords[-1]:
|
||||
geo = LineString(list(geo.coords)[::-1])
|
||||
|
||||
# ---------- Single depth/pass --------
|
||||
if not self.multidepth:
|
||||
# calculate the cut distance
|
||||
total_cut = total_cut + geo.length
|
||||
|
||||
t_gcode += self.create_gcode_single_pass(geo, current_tooldia, self.extracut,
|
||||
self.extracut_length, self.tolerance,
|
||||
z_move=self.z_move, old_point=current_pt)
|
||||
|
||||
# --------- Multi-pass ---------
|
||||
else:
|
||||
# calculate the cut distance
|
||||
# due of the number of cuts (multi depth) it has to multiplied by the number of cuts
|
||||
nr_cuts = 0
|
||||
depth = abs(self.z_cut)
|
||||
while depth > 0:
|
||||
nr_cuts += 1
|
||||
depth -= float(self.z_depthpercut)
|
||||
|
||||
total_cut += (geo.length * nr_cuts)
|
||||
|
||||
gc, geo = self.create_gcode_multi_pass(geo, current_tooldia, self.extracut,
|
||||
self.extracut_length, self.tolerance,
|
||||
z_move=self.z_move, postproc=p, old_point=current_pt)
|
||||
t_gcode += gc
|
||||
|
||||
# calculate the total distance
|
||||
total_travel = total_travel + abs(distance(pt1=current_pt, pt2=pt))
|
||||
current_pt = geo.coords[-1]
|
||||
|
||||
disp_number = int(np.interp(path_count, [0, geo_len], [0, 100]))
|
||||
if old_disp_number < disp_number <= 100:
|
||||
self.app.proc_container.update_view_text(' %d%%' % disp_number)
|
||||
old_disp_number = disp_number
|
||||
|
||||
log.debug("Finished G-Code... %s paths traced." % path_count)
|
||||
|
||||
# add move to end position
|
||||
total_travel += abs(distance_euclidian(current_pt[0], current_pt[1], 0, 0))
|
||||
self.travel_distance += total_travel + total_cut
|
||||
self.routing_time += total_cut / self.feedrate
|
||||
|
||||
# Finish
|
||||
if is_last:
|
||||
t_gcode += self.doformat(p.spindle_stop_code)
|
||||
t_gcode += self.doformat(p.lift_code, x=current_pt[0], y=current_pt[1])
|
||||
t_gcode += self.doformat(p.end_code, x=0, y=0)
|
||||
self.app.inform.emit(
|
||||
'%s... %s %s.' % (_("Finished G-Code generation"), str(path_count), _("paths traced"))
|
||||
)
|
||||
|
||||
self.gcode = t_gcode
|
||||
return self.gcode, start_gcode
|
||||
|
||||
def generate_from_geometry_2(self, geometry, append=True, tooldia=None, offset=0.0, tolerance=0, z_cut=None,
|
||||
z_move=None, feedrate=None, feedrate_z=None, feedrate_rapid=None, spindlespeed=None,
|
||||
spindledir='CW', dwell=False, dwelltime=None, multidepth=False, depthpercut=None,
|
||||
toolchange=False, toolchangez=None, toolchangexy="0.0, 0.0", extracut=False,
|
||||
extracut_length=None, startz=None, endz=None, endxy='', pp_geometry_name=None,
|
||||
tool_no=1):
|
||||
tool_no=1, is_first=False):
|
||||
"""
|
||||
Second algorithm to generate from Geometry.
|
||||
|
||||
|
@ -5572,6 +5582,7 @@ class CNCjob(Geometry):
|
|||
:param endxy:
|
||||
:param pp_geometry_name:
|
||||
:param tool_no:
|
||||
:param is_first: if the processed tool is the first one and if we should process the start gcode
|
||||
:return: None
|
||||
"""
|
||||
log.debug("Executing camlib.CNCJob.generate_from_geometry_2()")
|
||||
|
@ -5811,7 +5822,11 @@ class CNCjob(Geometry):
|
|||
self.oldx = 0.0
|
||||
self.oldy = 0.0
|
||||
|
||||
self.gcode = self.doformat(p.start_code)
|
||||
start_gcode = ''
|
||||
if is_first:
|
||||
start_gcode = self.doformat(p.start_code)
|
||||
|
||||
# self.gcode = self.doformat(p.start_code)
|
||||
self.gcode += self.doformat(p.feedrate_code) # sets the feed rate
|
||||
|
||||
if toolchange is False:
|
||||
|
@ -5936,7 +5951,7 @@ class CNCjob(Geometry):
|
|||
'%s... %s %s' % (_("Finished G-Code generation"), str(path_count), _(" paths traced."))
|
||||
)
|
||||
|
||||
return self.gcode
|
||||
return self.gcode, start_gcode
|
||||
|
||||
def generate_gcode_from_solderpaste_geo(self, **kwargs):
|
||||
"""
|
||||
|
@ -7551,24 +7566,45 @@ class CNCjob(Geometry):
|
|||
miny = np.Inf
|
||||
maxx = -np.Inf
|
||||
maxy = -np.Inf
|
||||
for k, v in self.cnc_tools.items():
|
||||
minx = np.Inf
|
||||
miny = np.Inf
|
||||
maxx = -np.Inf
|
||||
maxy = -np.Inf
|
||||
try:
|
||||
for k in v['solid_geometry']:
|
||||
minx_, miny_, maxx_, maxy_ = bounds_rec(k)
|
||||
if self.cnc_tools:
|
||||
for k, v in self.cnc_tools.items():
|
||||
minx = np.Inf
|
||||
miny = np.Inf
|
||||
maxx = -np.Inf
|
||||
maxy = -np.Inf
|
||||
try:
|
||||
for k in v['solid_geometry']:
|
||||
minx_, miny_, maxx_, maxy_ = bounds_rec(k)
|
||||
minx = min(minx, minx_)
|
||||
miny = min(miny, miny_)
|
||||
maxx = max(maxx, maxx_)
|
||||
maxy = max(maxy, maxy_)
|
||||
except TypeError:
|
||||
minx_, miny_, maxx_, maxy_ = bounds_rec(v['solid_geometry'])
|
||||
minx = min(minx, minx_)
|
||||
miny = min(miny, miny_)
|
||||
maxx = max(maxx, maxx_)
|
||||
maxy = max(maxy, maxy_)
|
||||
|
||||
if self.exc_cnc_tools:
|
||||
for k, v in self.exc_cnc_tools.items():
|
||||
minx = np.Inf
|
||||
miny = np.Inf
|
||||
maxx = -np.Inf
|
||||
maxy = -np.Inf
|
||||
try:
|
||||
for k in v['solid_geometry']:
|
||||
minx_, miny_, maxx_, maxy_ = bounds_rec(k)
|
||||
minx = min(minx, minx_)
|
||||
miny = min(miny, miny_)
|
||||
maxx = max(maxx, maxx_)
|
||||
maxy = max(maxy, maxy_)
|
||||
except TypeError:
|
||||
minx_, miny_, maxx_, maxy_ = bounds_rec(v['solid_geometry'])
|
||||
minx = min(minx, minx_)
|
||||
miny = min(miny, miny_)
|
||||
maxx = max(maxx, maxx_)
|
||||
maxy = max(maxy, maxy_)
|
||||
except TypeError:
|
||||
minx_, miny_, maxx_, maxy_ = bounds_rec(v['solid_geometry'])
|
||||
minx = min(minx, minx_)
|
||||
miny = min(miny, miny_)
|
||||
maxx = max(maxx, maxx_)
|
||||
maxy = max(maxy, maxy_)
|
||||
|
||||
bounds_coords = minx, miny, maxx, maxy
|
||||
return bounds_coords
|
||||
|
|
|
@ -337,6 +337,7 @@ class TclCommandDrillcncjob(TclCommandSignaled):
|
|||
|
||||
if ret_val == 'fail':
|
||||
return 'fail'
|
||||
job_obj.gc_start = ret_val[1]
|
||||
|
||||
for t_item in job_obj.exc_cnc_tools:
|
||||
job_obj.exc_cnc_tools[t_item]['data']['tools_drill_offset'] = \
|
||||
|
|
Loading…
Reference in New Issue