Просмотр исходного кода

- added new parameters to improve Gerber parsing
- small optimizations in the Preferences UI

Marius Stanciu 6 лет назад
Родитель
Сommit
e745f3f836
6 измененных файлов с 102 добавлено и 38 удалено
  1. 5 0
      FlatCAMApp.py
  2. 1 2
      FlatCAMObj.py
  3. 5 0
      README.md
  4. 15 13
      flatcamEditors/FlatCAMGrbEditor.py
  5. 58 15
      flatcamGUI/PreferencesUI.py
  6. 18 8
      flatcamParsers/ParseGerber.py

+ 5 - 0
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,

+ 1 - 2
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()):

+ 5 - 0
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

+ 15 - 13
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
-
-                        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')
+                # 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 self.utility:
                 for elem in self.utility:

+ 58 - 15
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("<b>%s:</b>" % _("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('<b>%s:</b>' % _('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('<b>%s</b>' % _('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"
               "<<WARNING>>: 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,
             [

+ 18 - 8
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: