From e745f3f83640dcc8c6ba08af20a0c453325f695f Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Wed, 18 Dec 2019 03:14:17 +0200 Subject: [PATCH] - added new parameters to improve Gerber parsing - small optimizations in the Preferences UI --- FlatCAMApp.py | 5 ++ FlatCAMObj.py | 3 +- README.md | 5 ++ flatcamEditors/FlatCAMGrbEditor.py | 26 ++++++----- flatcamGUI/PreferencesUI.py | 73 ++++++++++++++++++++++++------ flatcamParsers/ParseGerber.py | 26 +++++++---- 6 files changed, 101 insertions(+), 37 deletions(-) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 22e9bdac..0804b646 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -511,6 +511,9 @@ class App(QtCore.QObject): "gerber_multicolored": False, "gerber_circle_steps": 64, "gerber_use_buffer_for_union": True, + "gerber_clean_apertures": True, + "gerber_extra_buffering": True, + "gerber_def_units": 'IN', "gerber_def_zeros": 'L', "gerber_save_filters": "Gerber File (*.gbr);;Gerber File (*.bot);;Gerber File (*.bsm);;" @@ -1121,6 +1124,8 @@ class App(QtCore.QObject): "gerber_circle_steps": self.ui.gerber_defaults_form.gerber_gen_group.circle_steps_entry, "gerber_def_units": self.ui.gerber_defaults_form.gerber_gen_group.gerber_units_radio, "gerber_def_zeros": self.ui.gerber_defaults_form.gerber_gen_group.gerber_zeros_radio, + "gerber_clean_apertures": self.ui.gerber_defaults_form.gerber_gen_group.gerber_clean_cb, + "gerber_extra_buffering": self.ui.gerber_defaults_form.gerber_gen_group.gerber_extra_buffering, # Gerber Options "gerber_isotooldia": self.ui.gerber_defaults_form.gerber_opt_group.iso_tool_dia_entry, diff --git a/FlatCAMObj.py b/FlatCAMObj.py index b658b63b..671678e4 100644 --- a/FlatCAMObj.py +++ b/FlatCAMObj.py @@ -5173,8 +5173,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): if self.tools[tooluid_key]['solid_geometry'] is None: a += 1 if a == len(self.tools): - self.app.inform.emit('[ERROR_NOTCL] %s...' % - _('Cancelled. Empty file, it has no geometry')) + self.app.inform.emit('[ERROR_NOTCL] %s...' % _('Cancelled. Empty file, it has no geometry')) return 'fail' for tooluid_key in list(tools_dict.keys()): diff --git a/README.md b/README.md index 4521336d..1f8a04b3 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,11 @@ CAD program, and create G-Code for Isolation routing. ================================================= +18.12.2019 + +- added new parameters to improve Gerber parsing +- small optimizations in the Preferences UI + 17.12.2019 - more optimizations in NCC Tool diff --git a/flatcamEditors/FlatCAMGrbEditor.py b/flatcamEditors/FlatCAMGrbEditor.py index 5233314c..1779e8c5 100644 --- a/flatcamEditors/FlatCAMGrbEditor.py +++ b/flatcamEditors/FlatCAMGrbEditor.py @@ -4594,19 +4594,21 @@ class FlatCAMGrbEditor(QtCore.QObject): self.shapes.clear(update=True) for storage in self.storage_dict: - for elem in self.storage_dict[storage]['geometry']: - if 'solid' in elem.geo: - geometric_data = elem.geo['solid'] - if geometric_data is None: - continue + # fix for apertures with now geometry inside + if 'geometry' in self.storage_dict[storage]: + for elem in self.storage_dict[storage]['geometry']: + if 'solid' in elem.geo: + geometric_data = elem.geo['solid'] + if geometric_data is None: + continue - if elem in self.selected: - self.plot_shape(geometry=geometric_data, - color=self.app.defaults['global_sel_draw_color'] + 'FF', - linewidth=2) - else: - self.plot_shape(geometry=geometric_data, - color=self.app.defaults['global_draw_color'] + 'FF') + if elem in self.selected: + self.plot_shape(geometry=geometric_data, + color=self.app.defaults['global_sel_draw_color'] + 'FF', + linewidth=2) + else: + self.plot_shape(geometry=geometric_data, + color=self.app.defaults['global_draw_color'] + 'FF') if self.utility: for elem in self.utility: diff --git a/flatcamGUI/PreferencesUI.py b/flatcamGUI/PreferencesUI.py index cb033589..6aaeabc3 100644 --- a/flatcamGUI/PreferencesUI.py +++ b/flatcamGUI/PreferencesUI.py @@ -1435,6 +1435,29 @@ class GerberGenPrefGroupUI(OptionsGroupUI): grid0.addWidget(self.gerber_zeros_label, 5, 0) grid0.addWidget(self.gerber_zeros_radio, 5, 1, 1, 2) + separator_line = QtWidgets.QFrame() + separator_line.setFrameShape(QtWidgets.QFrame.HLine) + separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) + grid0.addWidget(separator_line, 6, 0, 1, 3) + + # Apertures Cleaning + self.gerber_clean_cb = FCCheckBox(label='%s' % _('Clean Apertures')) + self.gerber_clean_cb.setToolTip( + _("Will remove apertures that do not have geometry\n" + "thus lowering the number of apertures in the Gerber object.") + ) + grid0.addWidget(self.gerber_clean_cb, 7, 0, 1, 3) + + # Apply Extra Buffering + self.gerber_extra_buffering = FCCheckBox(label='%s' % _('Polarity change buffer')) + self.gerber_extra_buffering.setToolTip( + _("Will apply extra buffering for the\n" + "solid geometry when we have polarity changes.\n" + "May help loading Gerber files that otherwise\n" + "do not load correctly.") + ) + grid0.addWidget(self.gerber_extra_buffering, 8, 0, 1, 3) + self.layout.addStretch() @@ -1528,6 +1551,11 @@ class GerberOptPrefGroupUI(OptionsGroupUI): ) grid0.addWidget(self.combine_passes_cb, 5, 0, 1, 2) + separator_line = QtWidgets.QFrame() + separator_line.setFrameShape(QtWidgets.QFrame.HLine) + separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) + grid0.addWidget(separator_line, 6, 0, 1, 2) + # ## Clear non-copper regions self.clearcopper_label = QtWidgets.QLabel("%s:" % _("Non-copper regions")) self.clearcopper_label.setToolTip( @@ -1564,6 +1592,11 @@ class GerberOptPrefGroupUI(OptionsGroupUI): ) grid1.addWidget(self.noncopper_rounded_cb, 1, 0, 1, 2) + separator_line = QtWidgets.QFrame() + separator_line.setFrameShape(QtWidgets.QFrame.HLine) + separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) + grid1.addWidget(separator_line, 2, 0, 1, 2) + # ## Bounding box self.boundingbox_label = QtWidgets.QLabel('%s:' % _('Bounding Box')) self.layout.addWidget(self.boundingbox_label) @@ -1634,6 +1667,11 @@ class GerberAdvOptPrefGroupUI(OptionsGroupUI): ) grid0.addWidget(self.aperture_table_visibility_cb, 1, 0, 1, 2) + separator_line = QtWidgets.QFrame() + separator_line.setFrameShape(QtWidgets.QFrame.HLine) + separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) + grid0.addWidget(separator_line, 2, 0, 1, 2) + # Tool Type self.tool_type_label = QtWidgets.QLabel('%s' % _('Tool Type')) self.tool_type_label.setToolTip( @@ -1645,8 +1683,8 @@ class GerberAdvOptPrefGroupUI(OptionsGroupUI): self.tool_type_radio = RadioSet([{'label': 'Circular', 'value': 'circular'}, {'label': 'V-Shape', 'value': 'v'}]) - grid0.addWidget(self.tool_type_label, 2, 0) - grid0.addWidget(self.tool_type_radio, 2, 1, 1, 2) + grid0.addWidget(self.tool_type_label, 3, 0) + grid0.addWidget(self.tool_type_radio, 3, 1, 1, 2) # Tip Dia self.tipdialabel = QtWidgets.QLabel('%s:' % _('V-Tip Dia')) @@ -1658,8 +1696,8 @@ class GerberAdvOptPrefGroupUI(OptionsGroupUI): self.tipdia_spinner.set_range(-99.9999, 99.9999) self.tipdia_spinner.setSingleStep(0.1) self.tipdia_spinner.setWrapping(True) - grid0.addWidget(self.tipdialabel, 3, 0) - grid0.addWidget(self.tipdia_spinner, 3, 1, 1, 2) + grid0.addWidget(self.tipdialabel, 4, 0) + grid0.addWidget(self.tipdia_spinner, 4, 1, 1, 2) # Tip Angle self.tipanglelabel = QtWidgets.QLabel('%s:' % _('V-Tip Angle')) @@ -1671,8 +1709,8 @@ class GerberAdvOptPrefGroupUI(OptionsGroupUI): self.tipangle_spinner.set_range(0, 180) self.tipangle_spinner.setSingleStep(5) self.tipangle_spinner.setWrapping(True) - grid0.addWidget(self.tipanglelabel, 4, 0) - grid0.addWidget(self.tipangle_spinner, 4, 1, 1, 2) + grid0.addWidget(self.tipanglelabel, 5, 0) + grid0.addWidget(self.tipangle_spinner, 5, 1, 1, 2) # Cut Z self.cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z')) @@ -1686,8 +1724,8 @@ class GerberAdvOptPrefGroupUI(OptionsGroupUI): self.cutz_spinner.setSingleStep(0.1) self.cutz_spinner.setWrapping(True) - grid0.addWidget(self.cutzlabel, 5, 0) - grid0.addWidget(self.cutz_spinner, 5, 1, 1, 2) + grid0.addWidget(self.cutzlabel, 6, 0) + grid0.addWidget(self.cutz_spinner, 6, 1, 1, 2) # Isolation Type self.iso_type_label = QtWidgets.QLabel('%s:' % _('Isolation Type')) @@ -1705,8 +1743,13 @@ class GerberAdvOptPrefGroupUI(OptionsGroupUI): {'label': _('Exterior'), 'value': 'ext'}, {'label': _('Interior'), 'value': 'int'}]) - grid0.addWidget(self.iso_type_label, 6, 0,) - grid0.addWidget(self.iso_type_radio, 6, 1, 1, 2) + grid0.addWidget(self.iso_type_label, 7, 0,) + grid0.addWidget(self.iso_type_radio, 7, 1, 1, 2) + + separator_line = QtWidgets.QFrame() + separator_line.setFrameShape(QtWidgets.QFrame.HLine) + separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) + grid0.addWidget(separator_line, 8, 0, 1, 2) # Buffering Type buffering_label = QtWidgets.QLabel('%s:' % _('Buffering')) @@ -1718,8 +1761,8 @@ class GerberAdvOptPrefGroupUI(OptionsGroupUI): ) self.buffering_radio = RadioSet([{'label': _('None'), 'value': 'no'}, {'label': _('Full'), 'value': 'full'}]) - grid0.addWidget(buffering_label, 7, 0) - grid0.addWidget(self.buffering_radio, 7, 1) + grid0.addWidget(buffering_label, 9, 0) + grid0.addWidget(self.buffering_radio, 9, 1) # Simplification self.simplify_cb = FCCheckBox(label=_('Simplify')) @@ -1728,7 +1771,7 @@ class GerberAdvOptPrefGroupUI(OptionsGroupUI): "loaded with simplification having a set tolerance.\n" "<>: Don't change this unless you know what you are doing !!!") ) - grid0.addWidget(self.simplify_cb, 8, 0, 1, 2) + grid0.addWidget(self.simplify_cb, 10, 0, 1, 2) # Simplification tolerance self.simplification_tol_label = QtWidgets.QLabel(_('Tolerance')) @@ -1740,8 +1783,8 @@ class GerberAdvOptPrefGroupUI(OptionsGroupUI): self.simplification_tol_spinner.setRange(0.00000, 0.01000) self.simplification_tol_spinner.setSingleStep(0.0001) - grid0.addWidget(self.simplification_tol_label, 9, 0) - grid0.addWidget(self.simplification_tol_spinner, 9, 1) + grid0.addWidget(self.simplification_tol_label, 11, 0) + grid0.addWidget(self.simplification_tol_spinner, 11, 1) self.ois_simplif = OptionalInputSection( self.simplify_cb, [ diff --git a/flatcamParsers/ParseGerber.py b/flatcamParsers/ParseGerber.py index 1615f000..f8829c5c 100644 --- a/flatcamParsers/ParseGerber.py +++ b/flatcamParsers/ParseGerber.py @@ -72,6 +72,8 @@ class Gerber(Geometry): # "use_buffer_for_union": True # } + app = None + def __init__(self, steps_per_circle=None): """ The constructor takes no parameters. Use ``gerber.parse_files()`` @@ -1412,14 +1414,7 @@ class Gerber(Geometry): if current_polarity == 'D': self.app.inform.emit('%s' % _("Gerber processing. Applying Gerber polarity.")) if new_poly.is_valid: - # self.solid_geometry = self.solid_geometry.union(new_poly) - # FIX for issue #347 - Sprint Layout generate strange Gerber files when the copper pour is enabled - # it use a filled bounding box polygon to which add clear polygons (negative) to isolate the copper - # features - candidate_geo = list() - for p in self.solid_geometry.union(new_poly): - candidate_geo.append(p.buffer(-0.0000001)) - self.solid_geometry = candidate_geo + self.solid_geometry = self.solid_geometry.union(new_poly) else: # I do this so whenever the parsed geometry of the file is not valid (intersections) it is still # loaded. Instead of applying a union I add to a list of polygons. @@ -1438,6 +1433,15 @@ class Gerber(Geometry): self.solid_geometry = final_poly + # FIX for issue #347 - Sprint Layout generate strange Gerber files when the copper pour is enabled + # it use a filled bounding box polygon to which add clear polygons (negative) to isolate the copper + # features + if self.app.defaults['gerber_extra_buffering']: + candidate_geo = list() + for p in self.solid_geometry: + candidate_geo.append(p.buffer(0.0000001)) + self.solid_geometry = candidate_geo + # try: # self.solid_geometry = self.solid_geometry.union(new_poly) # except Exception as e: @@ -1450,6 +1454,12 @@ class Gerber(Geometry): else: self.solid_geometry = self.solid_geometry.difference(new_poly) + if self.app.defaults['gerber_clean_apertures']: + # clean the Gerber file of apertures with no geometry + for apid, apvalue in list(self.apertures.items()): + if 'geometry' not in apvalue: + self.apertures.pop(apid) + # init this for the following operations self.conversion_done = False except Exception as err: