From bdd13a3800efc7e1663633b3d20ac4839d6a620c Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Tue, 7 May 2019 21:13:20 +0300 Subject: [PATCH] - work in Gerber Export: finished the body export but have some errors with clear geometry (LPC) --- FlatCAMApp.py | 45 ++--- FlatCAMObj.py | 291 ++++++++++++++++------------- README.md | 1 + camlib.py | 30 +-- flatcamEditors/FlatCAMGrbEditor.py | 21 ++- 5 files changed, 210 insertions(+), 178 deletions(-) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 4b3e3990..21a4a2b7 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -7093,56 +7093,57 @@ class App(QtCore.QObject): time_str = "{:%A, %d %B %Y at %H:%M}".format(datetime.now()) header = 'G04*\n' - header += ';GERBER GENERATED BY FLATCAM v%s - www.flatcam.org - Version Date: %s\n' % \ + header += 'G04 RS-274X GERBER GENERATED BY FLATCAM v%s - www.flatcam.org - Version Date: %s*\n' % \ (str(self.version), str(self.version_date)) - header += ';Filename: %s' % str(obj_name) + '\n' - header += ';Created on : %s' % time_str + '\n' + header += 'G04 Filename: %s*' % str(obj_name) + '\n' + header += 'G04 Created on : %s*' % time_str + '\n' header += '%%FS%sAX%s%sY%s%s*%%\n' % (gzeros, gwhole, gfract, gwhole, gfract) - - # gerber_code = obj.export_gerber(gwhole, gfract, form='ndec', e_zeros=gzeros, factor=factor) + header += "%MO{units}*%\n".format(units=gunits) for apid in obj.apertures: if obj.apertures[apid]['type'] == 'C': - header += "%ADD{apid}{type},{size}*%\n".format(apid=str(apid), - type='C', - size=obj.apertures[apid]['size']) + header += "%ADD{apid}{type},{size}*%\n".format( + apid=str(apid), + type='C', + size=(factor * obj.apertures[apid]['size']) + ) elif obj.apertures[apid]['type'] == 'R': header += "%ADD{apid}{type},{width}X{height}*%\n".format( apid=str(apid), type='R', - width=obj.apertures[apid]['width'], - height=obj.apertures[apid]['height'] + width=(factor * obj.apertures[apid]['width']), + height=(factor * obj.apertures[apid]['height']) ) elif obj.apertures[apid]['type'] == 'O': header += "%ADD{apid}{type},{width}X{height}*%\n".format( apid=str(apid), type='O', - width=obj.apertures[apid]['width'], - height=obj.apertures[apid]['height'] + width=(factor * obj.apertures[apid]['width']), + height=(factor * obj.apertures[apid]['height']) ) header += '\n' - header += "%MO{units}*%\n".format(units=gunits) - header += "G04*\n" - if gunits == 'IN': - header += 'G71*\n' - else: - header += 'G70*\n' - header += 'G75*\n' + # obsolete units but some software may need it if gunits == 'IN': - header += 'G91*\n' + header += 'G70*\n' else: - header += 'G90*\n' + header += 'G71*\n' + + # Absolute Mode + header += 'G90*\n' header += 'G01*\n' + # positive polarity header += '%LPD*%\n' footer = 'M02*\n' + gerber_code = obj.export_gerber(gwhole, gfract, g_zeros=gzeros, factor=factor) + exported_gerber = header - # exported_gerber += gerber_code + exported_gerber += gerber_code exported_gerber += footer with open(filename, 'w') as fp: diff --git a/FlatCAMObj.py b/FlatCAMObj.py index afbb620b..62905b2e 100644 --- a/FlatCAMObj.py +++ b/FlatCAMObj.py @@ -1258,160 +1258,185 @@ class FlatCAMGerber(FlatCAMObj, Gerber): self.ui_connect() - def export_gerber(self, whole, fract, g_zeros=None, factor=1): + def export_gerber(self, whole, fract, g_zeros='L', factor=1): """ :return: Gerber_code """ + def tz_format(x, y ,fac): + x_c = x * fac + y_c = y * fac + + x_form = "{:.{dec}f}".format(x_c, dec=fract) + y_form = "{:.{dec}f}".format(y_c, dec=fract) + + # extract whole part and decimal part + x_form = x_form.partition('.') + y_form = y_form.partition('.') + + # left padd the 'whole' part with zeros + x_whole = x_form[0].rjust(whole, '0') + y_whole = y_form[0].rjust(whole, '0') + + # restore the coordinate padded in the left with 0 and added the decimal part + # without the decinal dot + x_form = x_whole + x_form[2] + y_form = y_whole + y_form[2] + return x_form, y_form + + def lz_format(x, y, fac): + x_c = x * fac + y_c = y * fac + + x_form = "{:.{dec}f}".format(x_c, dec=fract).replace('.', '') + y_form = "{:.{dec}f}".format(y_c, dec=fract).replace('.', '') + + # pad with rear zeros + x_form.ljust(length, '0') + y_form.ljust(length, '0') + + return x_form, y_form + + # Gerber code is stored here gerber_code = '' - # store here if the file has slots, return 1 if any slots, 0 if only drills - has_slots = 0 - - # drills processing + # apertures processing try: - if self.apertures: - length = whole + fract - for apid in self.apertures: - if apid == '0': + length = whole + fract + if '0' in self.apertures: + if 'solid_geometry' in self.apertures['0']: + for geo in self.apertures['0']['solid_geometry']: gerber_code += 'G36*\n' + geo_coords = list(geo.exterior.coords) + # first command is a move with pen-up D02 at the beginning of the geo + if g_zeros == 'T': + x_formatted, y_formatted = tz_format(geo_coords[0][0], geo_coords[0][1], factor) + gerber_code += "X{xform}Y{yform}D02*\n".format(xform=x_formatted, + yform=y_formatted) + else: + x_formatted, y_formatted = lz_format(geo_coords[0][0], geo_coords[0][1], factor) + gerber_code += "X{xform}Y{yform}D02*\n".format(xform=x_formatted, + yform=y_formatted) + for coord in geo_coords[1:]: + if g_zeros == 'T': + x_formatted, y_formatted = tz_format(coord[0], coord[1], factor) + gerber_code += "X{xform}Y{yform}D01*\n".format(xform=x_formatted, + yform=y_formatted) + else: + x_formatted, y_formatted = lz_format(coord[0], coord[1], factor) + gerber_code += "X{xform}Y{yform}D01*\n".format(xform=x_formatted, + yform=y_formatted) gerber_code += 'D02*\n' gerber_code += 'G37*\n' - else: - gerber_code += 'D%s*\n' % str(apid) - for geo in self.apertures[apid]['follow_geometry']: + if 'clear_geometry' in self.apertures['0']: + gerber_code += '%LPC*%\n' + for geo in self.apertures['0']['clear_geometry']: + gerber_code += 'G36*\n' + geo_coords = list(geo.exterior.coords) + + # first command is a move with pen-up D02 at the beginning of the geo if g_zeros == 'T': - if isinstance(geo, Point): - #TODO - pass + x_formatted, y_formatted = tz_format(geo_coords[0][0], geo_coords[0][1], factor) + gerber_code += "X{xform}Y{yform}D02*\n".format(xform=x_formatted, + yform=y_formatted) + else: + x_formatted, y_formatted = lz_format(geo_coords[0][0], geo_coords[0][1], factor) + gerber_code += "X{xform}Y{yform}D02*\n".format(xform=x_formatted, + yform=y_formatted) + for coord in geo_coords[1:]: + if g_zeros == 'T': + x_formatted, y_formatted = tz_format(coord[0], coord[1], factor) + gerber_code += "X{xform}Y{yform}D01*\n".format(xform=x_formatted, + yform=y_formatted) else: - drill_x = drill['point'].x * factor - drill_y = drill['point'].y * factor + x_formatted, y_formatted = lz_format(coord[0], coord[1], factor) + gerber_code += "X{xform}Y{yform}D01*\n".format(xform=x_formatted, + yform=y_formatted) + gerber_code += 'D02*\n' + gerber_code += 'G37*\n' + gerber_code += '%LPD*%\n' - exc_x_formatted = "{:.{dec}f}".format(drill_x, dec=fract) - exc_y_formatted = "{:.{dec}f}".format(drill_y, dec=fract) + for apid in self.apertures: + if apid == '0': + continue + else: + gerber_code += 'D%s*\n' % str(apid) - # extract whole part and decimal part - exc_x_formatted = exc_x_formatted.partition('.') - exc_y_formatted = exc_y_formatted.partition('.') + if 'follow_geometry' in self.apertures[apid]: + for geo in self.apertures[apid]['follow_geometry']: + if isinstance(geo, Point): + if g_zeros == 'T': + x_formatted, y_formatted = tz_format(geo.x, geo.y, factor) + gerber_code += "X{xform}Y{yform}D03*\n".format(xform=x_formatted, + yform=y_formatted) + else: + x_formatted, y_formatted = lz_format(geo.x, geo.y, factor) + gerber_code += "X{xform}Y{yform}D03*\n".format(xform=x_formatted, + yform=y_formatted) + else: + geo_coords = list(geo.coords) + # first command is a move with pen-up D02 at the beginning of the geo + if g_zeros == 'T': + x_formatted, y_formatted = tz_format(geo_coords[0][0], geo_coords[0][1], factor) + gerber_code += "X{xform}Y{yform}D02*\n".format(xform=x_formatted, + yform=y_formatted) + else: + x_formatted, y_formatted = lz_format(geo_coords[0][0], geo_coords[0][1], factor) + gerber_code += "X{xform}Y{yform}D02*\n".format(xform=x_formatted, + yform=y_formatted) + for coord in geo_coords[1:]: + if g_zeros == 'T': + x_formatted, y_formatted = tz_format(coord[0], coord[1], factor) + gerber_code += "X{xform}Y{yform}D01*\n".format(xform=x_formatted, + yform=y_formatted) + else: + x_formatted, y_formatted = lz_format(coord[0], coord[1], factor) + gerber_code += "X{xform}Y{yform}D01*\n".format(xform=x_formatted, + yform=y_formatted) + if 'clear_follow_geometry' in self.apertures[apid]: + gerber_code += '%LPC*%\n' + for geo in self.apertures[apid]['clear_follow_geometry']: + if isinstance(geo, Point): + if g_zeros == 'T': + x_formatted, y_formatted = tz_format(geo.x, geo.y, factor) + gerber_code += "X{xform}Y{yform}D03*\n".format(xform=x_formatted, + yform=y_formatted) + else: + x_formatted, y_formatted = lz_format(geo.x, geo.y, factor) + gerber_code += "X{xform}Y{yform}D03*\n".format(xform=x_formatted, + yform=y_formatted) + else: + geo_coords = list(geo.coords) + # first command is a move with pen-up D02 at the beginning of the geo + if g_zeros == 'T': + x_formatted, y_formatted = tz_format(geo_coords[0][0], geo_coords[0][1], factor) + gerber_code += "X{xform}Y{yform}D02*\n".format(xform=x_formatted, + yform=y_formatted) + else: + x_formatted, y_formatted = lz_format(geo_coords[0][0], geo_coords[0][1], factor) + gerber_code += "X{xform}Y{yform}D02*\n".format(xform=x_formatted, + yform=y_formatted) + for coord in geo_coords[1:]: + if g_zeros == 'T': + x_formatted, y_formatted = tz_format(coord[0], coord[1], factor) + gerber_code += "X{xform}Y{yform}D01*\n".format(xform=x_formatted, + yform=y_formatted) + else: + x_formatted, y_formatted = lz_format(coord[0], coord[1], factor) + gerber_code += "X{xform}Y{yform}D01*\n".format(xform=x_formatted, + yform=y_formatted) + gerber_code += '%LPD*%\n' - # left padd the 'whole' part with zeros - x_whole = exc_x_formatted[0].rjust(whole, '0') - y_whole = exc_y_formatted[0].rjust(whole, '0') - - # restore the coordinate padded in the left with 0 and added the decimal part - # without the decinal dot - exc_x_formatted = x_whole + exc_x_formatted[2] - exc_y_formatted = y_whole + exc_y_formatted[2] - - gerber_code += "X{xform}Y{yform}\n".format(xform=exc_x_formatted, - yform=exc_y_formatted) - elif tool == drill['tool']: - drill_x = drill['point'].x * factor - drill_y = drill['point'].y * factor - - exc_x_formatted = "{:.{dec}f}".format(drill_x, dec=fract).replace('.', '') - exc_y_formatted = "{:.{dec}f}".format(drill_y, dec=fract).replace('.', '') - - # pad with rear zeros - exc_x_formatted.ljust(length, '0') - exc_y_formatted.ljust(length, '0') - - excellon_code += "X{xform}Y{yform}\n".format(xform=exc_x_formatted, - yform=exc_y_formatted) except Exception as e: - log.debug(str(e)) + log.debug("FlatCAMObj.FlatCAMGerber.export_gerber() --> %s" % str(e)) - # slots processing - try: - if self.slots: - has_slots = 1 - for tool in self.tools: - if int(tool) < 10: - excellon_code += 'T0' + str(tool) + '\n' - else: - excellon_code += 'T' + str(tool) + '\n' - - for slot in self.slots: - if form == 'dec' and tool == slot['tool']: - start_slot_x = slot['start'].x * factor - start_slot_y = slot['start'].y * factor - stop_slot_x = slot['stop'].x * factor - stop_slot_y = slot['stop'].y * factor - - excellon_code += "G00X{:.{dec}f}Y{:.{dec}f}\nM15\n".format(start_slot_x, - start_slot_y, - dec=fract) - excellon_code += "G00X{:.{dec}f}Y{:.{dec}f}\nM16\n".format(stop_slot_x, - stop_slot_y, - dec=fract) - - elif e_zeros == 'LZ' and tool == slot['tool']: - start_slot_x = slot['start'].x * factor - start_slot_y = slot['start'].y * factor - stop_slot_x = slot['stop'].x * factor - stop_slot_y = slot['stop'].y * factor - - start_slot_x_formatted = "{:.{dec}f}".format(start_slot_x, dec=fract).replace('.', '') - start_slot_y_formatted = "{:.{dec}f}".format(start_slot_y, dec=fract).replace('.', '') - stop_slot_x_formatted = "{:.{dec}f}".format(stop_slot_x, dec=fract).replace('.', '') - stop_slot_y_formatted = "{:.{dec}f}".format(stop_slot_y, dec=fract).replace('.', '') - - # extract whole part and decimal part - start_slot_x_formatted = start_slot_x_formatted.partition('.') - start_slot_y_formatted = start_slot_y_formatted.partition('.') - stop_slot_x_formatted = stop_slot_x_formatted.partition('.') - stop_slot_y_formatted = stop_slot_y_formatted.partition('.') - - # left padd the 'whole' part with zeros - start_x_whole = start_slot_x_formatted[0].rjust(whole, '0') - start_y_whole = start_slot_y_formatted[0].rjust(whole, '0') - stop_x_whole = stop_slot_x_formatted[0].rjust(whole, '0') - stop_y_whole = stop_slot_y_formatted[0].rjust(whole, '0') - - # restore the coordinate padded in the left with 0 and added the decimal part - # without the decinal dot - start_slot_x_formatted = start_x_whole + start_slot_x_formatted[2] - start_slot_y_formatted = start_y_whole + start_slot_y_formatted[2] - stop_slot_x_formatted = stop_x_whole + stop_slot_x_formatted[2] - stop_slot_y_formatted = stop_y_whole + stop_slot_y_formatted[2] - - excellon_code += "G00X{xstart}Y{ystart}\nM15\n".format(xstart=start_slot_x_formatted, - ystart=start_slot_y_formatted) - excellon_code += "G00X{xstop}Y{ystop}\nM16\n".format(xstop=stop_slot_x_formatted, - ystop=stop_slot_y_formatted) - elif tool == slot['tool']: - start_slot_x = slot['start'].x * factor - start_slot_y = slot['start'].y * factor - stop_slot_x = slot['stop'].x * factor - stop_slot_y = slot['stop'].y * factor - length = whole + fract - - start_slot_x_formatted = "{:.{dec}f}".format(start_slot_x, dec=fract).replace('.', '') - start_slot_y_formatted = "{:.{dec}f}".format(start_slot_y, dec=fract).replace('.', '') - stop_slot_x_formatted = "{:.{dec}f}".format(stop_slot_x, dec=fract).replace('.', '') - stop_slot_y_formatted = "{:.{dec}f}".format(stop_slot_y, dec=fract).replace('.', '') - - # pad with rear zeros - start_slot_x_formatted.ljust(length, '0') - start_slot_y_formatted.ljust(length, '0') - stop_slot_x_formatted.ljust(length, '0') - stop_slot_y_formatted.ljust(length, '0') - - excellon_code += "G00X{xstart}Y{ystart}\nM15\n".format(xstart=start_slot_x_formatted, - ystart=start_slot_y_formatted) - excellon_code += "G00X{xstop}Y{ystop}\nM16\n".format(xstop=stop_slot_x_formatted, - ystop=stop_slot_y_formatted) - except Exception as e: - log.debug(str(e)) - - if not self.drills and not self.slots: - log.debug("FlatCAMObj.FlatCAMExcellon.export_excellon() --> Excellon Object is empty: no drills, no slots.") + if not self.apertures: + log.debug("FlatCAMObj.FlatCAMGerber.export_gerber() --> Gerber Object is empty: no apertures.") return 'fail' - return has_slots, excellon_code + return gerber_code def mirror(self, axis, point): Gerber.mirror(self, axis=axis, point=point) diff --git a/README.md b/README.md index 56bf4397..6743881b 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ CAD program, and create G-Code for Isolation routing. - remade the Tool Panelize GUI - work in Gerber Export: finished the header export - fixed the Gerber Object and Gerber Editor Apertures Table to not show extra rows when there are aperture macros in the object +- work in Gerber Export: finished the body export but have some errors with clear geometry (LPC) 6.05.2019 diff --git a/camlib.py b/camlib.py index 43096a52..45475306 100644 --- a/camlib.py +++ b/camlib.py @@ -2486,17 +2486,18 @@ class Gerber (Geometry): # do nothing because 'R' type moving aperture is none at once pass else: - # --- Buffered ---- - width = self.apertures[last_path_aperture]["size"] + geo = LineString(path) if not geo.is_empty: follow_buffer.append(geo) try: - self.apertures[current_aperture]['follow_geometry'].append(geo) + self.apertures[last_path_aperture]['follow_geometry'].append(geo) except KeyError: - self.apertures[current_aperture]['follow_geometry'] = [] - self.apertures[current_aperture]['follow_geometry'].append(geo) + self.apertures[last_path_aperture]['follow_geometry'] = [] + self.apertures[last_path_aperture]['follow_geometry'].append(geo) + # --- Buffered ---- + width = self.apertures[last_path_aperture]["size"] geo = LineString(path).buffer(width / 1.999, int(self.steps_per_circle / 4)) if not geo.is_empty: poly_buffer.append(geo) @@ -2869,17 +2870,18 @@ class Gerber (Geometry): if self.apertures[last_path_aperture]["type"] != 'R': follow_buffer.append(geo) try: - self.apertures[current_aperture]['follow_geometry'].append(geo) + self.apertures[last_path_aperture]['follow_geometry'].append(geo) except KeyError: - self.apertures[current_aperture]['follow_geometry'] = [] - self.apertures[current_aperture]['follow_geometry'].append(geo) - except: + self.apertures[last_path_aperture]['follow_geometry'] = [] + self.apertures[last_path_aperture]['follow_geometry'].append(geo) + except Exception as e: + log.debug("camlib.Gerber.parse_lines() --> G01 match D03 --> %s" % str(e)) follow_buffer.append(geo) try: - self.apertures[current_aperture]['follow_geometry'].append(geo) + self.apertures[last_path_aperture]['follow_geometry'].append(geo) except KeyError: - self.apertures[current_aperture]['follow_geometry'] = [] - self.apertures[current_aperture]['follow_geometry'].append(geo) + self.apertures[last_path_aperture]['follow_geometry'] = [] + self.apertures[last_path_aperture]['follow_geometry'].append(geo) # this treats the case when we are storing geometry as solids width = self.apertures[last_path_aperture]["size"] @@ -3217,7 +3219,7 @@ class Gerber (Geometry): if 'clear_geometry' in self.apertures[apid]: for pol in self.apertures[apid]['clear_geometry']: global_clear_geo.append(pol) - self.apertures[apid].pop('clear_geometry', None) + # self.apertures[apid].pop('clear_geometry', None) log.warning("Found %d clear polygons." % len(global_clear_geo)) temp_geo = [] @@ -3496,6 +3498,8 @@ class Gerber (Geometry): try: for apid in self.apertures: self.apertures[apid]['solid_geometry'] = scale_geom(self.apertures[apid]['solid_geometry']) + self.apertures[apid]['clear_geometry'] = scale_geom(self.apertures[apid]['clear_geometry']) + except Exception as e: log.debug('FlatCAMGeometry.scale() --> %s' % str(e)) diff --git a/flatcamEditors/FlatCAMGrbEditor.py b/flatcamEditors/FlatCAMGrbEditor.py index d1b29a12..9ca91698 100644 --- a/flatcamEditors/FlatCAMGrbEditor.py +++ b/flatcamEditors/FlatCAMGrbEditor.py @@ -3864,16 +3864,17 @@ class FlatCAMGrbEditor(QtCore.QObject): def hide_tool(self, tool_name): # self.app.ui.notebook.setTabText(2, _("Tools")) - - if tool_name == 'all': - self.apertures_frame.hide() - if tool_name == 'select': - self.apertures_frame.show() - if tool_name == 'buffer' or tool_name == 'all': - self.buffer_tool_frame.hide() - if tool_name == 'scale' or tool_name == 'all': - self.scale_tool_frame.hide() - + try: + if tool_name == 'all': + self.apertures_frame.hide() + if tool_name == 'select': + self.apertures_frame.show() + if tool_name == 'buffer' or tool_name == 'all': + self.buffer_tool_frame.hide() + if tool_name == 'scale' or tool_name == 'all': + self.scale_tool_frame.hide() + except Exception as e: + log.debug("FlatCAMGrbEditor.hide_tool() --> %s" % str(e)) self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab)