Selaa lähdekoodia

- updated the CutOut tool so it will work on single PCB Gerbers or on PCB panel Gerbers

Marius Stanciu 6 vuotta sitten
vanhempi
commit
dce0d971cd
4 muutettua tiedostoa jossa 229 lisäystä ja 135 poistoa
  1. 2 0
      FlatCAMApp.py
  2. 4 0
      README.md
  3. 23 8
      flatcamGUI/FlatCAMGUI.py
  4. 200 127
      flatcamTools/ToolCutOut.py

+ 2 - 0
FlatCAMApp.py

@@ -504,6 +504,7 @@ class App(QtCore.QObject):
 
             # CutOut Tool
             "tools_cutouttooldia": self.ui.tools_defaults_form.tools_cutout_group.cutout_tooldia_entry,
+            "tools_cutoutkind": self.ui.tools_defaults_form.tools_cutout_group.obj_kind_combo,
             "tools_cutoutmargin": self.ui.tools_defaults_form.tools_cutout_group.cutout_margin_entry,
             "tools_cutoutgapsize": self.ui.tools_defaults_form.tools_cutout_group.cutout_gap_entry,
             "tools_gaps_ff": self.ui.tools_defaults_form.tools_cutout_group.gaps_combo,
@@ -839,6 +840,7 @@ class App(QtCore.QObject):
             "tools_nccrest": False,
 
             "tools_cutouttooldia": 0.00393701,
+            "tools_cutoutkind": "single",
             "tools_cutoutmargin": 0.00393701,
             "tools_cutoutgapsize": 0.005905512,
             "tools_gaps_ff": "8",

+ 4 - 0
README.md

@@ -9,6 +9,10 @@ CAD program, and create G-Code for Isolation routing.
 
 =================================================
 
+20.07.2019
+
+- updated the CutOut tool so it will work on single PCB Gerbers or on PCB panel Gerbers
+
 19.07.2019
 
 - fixed bug in FlatCAMObj.FlatCAMGeometry.ui_disconnect(); the widgets signals were not disconnected from handlers when required therefore the signals were connected in an exponential way

+ 23 - 8
flatcamGUI/FlatCAMGUI.py

@@ -5909,14 +5909,29 @@ class ToolsCutoutPrefGroupUI(OptionsGroupUI):
         self.cutout_tooldia_entry = LengthEntry()
         grid0.addWidget(self.cutout_tooldia_entry, 0, 1)
 
+        # Object kind
+        kindlabel = QtWidgets.QLabel(_('Obj kind:'))
+        kindlabel.setToolTip(
+            _("Choice of what kind the object we want to cutout is.<BR>"
+              "- <B>Single</B>: contain a single PCB Gerber outline object.<BR>"
+              "- <B>Panel</B>: a panel PCB Gerber object, which is made\n"
+              "out of many individual PCB outlines.")
+        )
+        grid0.addWidget(kindlabel, 1, 0)
+        self.obj_kind_combo = RadioSet([
+            {"label": _("Single"), "value": "single"},
+            {"label": _("Panel"), "value": "panel"},
+        ])
+        grid0.addWidget(self.obj_kind_combo, 1, 1)
+
         marginlabel = QtWidgets.QLabel(_('Margin:'))
         marginlabel.setToolTip(
             _("Distance from objects at which\n"
               "to draw the cutout.")
         )
-        grid0.addWidget(marginlabel, 1, 0)
+        grid0.addWidget(marginlabel, 2, 0)
         self.cutout_margin_entry = LengthEntry()
-        grid0.addWidget(self.cutout_margin_entry, 1, 1)
+        grid0.addWidget(self.cutout_margin_entry, 2, 1)
 
         gaplabel = QtWidgets.QLabel(_('Gap size:'))
         gaplabel.setToolTip(
@@ -5924,9 +5939,9 @@ class ToolsCutoutPrefGroupUI(OptionsGroupUI):
               "that will remain to hold the\n"
               "board in place.")
         )
-        grid0.addWidget(gaplabel, 2, 0)
+        grid0.addWidget(gaplabel, 3, 0)
         self.cutout_gap_entry = LengthEntry()
-        grid0.addWidget(self.cutout_gap_entry, 2, 1)
+        grid0.addWidget(self.cutout_gap_entry, 3, 1)
 
         gaps_label = QtWidgets.QLabel(_('Gaps:'))
         gaps_label.setToolTip(
@@ -5940,9 +5955,9 @@ class ToolsCutoutPrefGroupUI(OptionsGroupUI):
               "- 2tb  - 2*top + 2*bottom\n"
               "- 8     - 2*left + 2*right +2*top + 2*bottom")
         )
-        grid0.addWidget(gaps_label, 3, 0)
+        grid0.addWidget(gaps_label, 4, 0)
         self.gaps_combo = FCComboBox()
-        grid0.addWidget(self.gaps_combo, 3, 1)
+        grid0.addWidget(self.gaps_combo, 4, 1)
 
         gaps_items = ['LR', 'TB', '4', '2LR', '2TB', '8']
         for it in gaps_items:
@@ -5955,8 +5970,8 @@ class ToolsCutoutPrefGroupUI(OptionsGroupUI):
         self.convex_box_label.setToolTip(
             _("Create a convex shape surrounding the entire PCB.")
         )
-        grid0.addWidget(self.convex_box_label, 4, 0)
-        grid0.addWidget(self.convex_box, 4, 1)
+        grid0.addWidget(self.convex_box_label, 5, 0)
+        grid0.addWidget(self.convex_box, 5, 1)
 
         self.layout.addStretch()
 

+ 200 - 127
flatcamTools/ToolCutOut.py

@@ -73,6 +73,20 @@ class CutOut(FlatCAMTool):
         )
         form_layout.addRow(self.object_label, self.obj_combo)
 
+        # Object kind
+        self.kindlabel = QtWidgets.QLabel(_('Obj kind:'))
+        self.kindlabel.setToolTip(
+            _("Choice of what kind the object we want to cutout is.<BR>"
+              "- <B>Single</B>: contain a single PCB Gerber outline object.<BR>"
+              "- <B>Panel</B>: a panel PCB Gerber object, which is made\n"
+              "out of many individual PCB outlines.")
+        )
+        self.obj_kind_combo = RadioSet([
+            {"label": _("Single"), "value": "single"},
+            {"label": _("Panel"), "value": "panel"},
+        ])
+        form_layout.addRow(self.kindlabel, self.obj_kind_combo)
+
         # Tool Diameter
         self.dia = FCEntry()
         self.dia_label = QtWidgets.QLabel(_("Tool Dia:"))
@@ -320,6 +334,7 @@ class CutOut(FlatCAMTool):
         self.reset_fields()
 
         self.dia.set_value(float(self.app.defaults["tools_cutouttooldia"]))
+        self.obj_kind_combo.set_value(self.app.defaults["tools_cutoutkind"])
         self.margin.set_value(float(self.app.defaults["tools_cutoutmargin"]))
         self.gapsize.set_value(float(self.app.defaults["tools_cutoutgapsize"]))
         self.gaps.set_value(self.app.defaults["tools_gaps_ff"])
@@ -338,7 +353,8 @@ class CutOut(FlatCAMTool):
         # Get source object.
         try:
             cutout_obj = self.app.collection.get_by_name(str(name))
-        except:
+        except Exception as e:
+            log.debug("CutOut.on_freeform_cutout() --> %s" % str(e))
             self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve object: %s") % name)
             return "Could not retrieve object: %s" % name
 
@@ -361,6 +377,11 @@ class CutOut(FlatCAMTool):
             self.app.inform.emit(_("[WARNING_NOTCL] Tool Diameter is zero value. Change it to a positive real number."))
             return "Tool Diameter is zero value. Change it to a positive real number."
 
+        try:
+            kind = self.obj_kind_combo.get_value()
+        except ValueError:
+            return
+
         try:
             margin = float(self.margin.get_value())
         except ValueError:
@@ -415,71 +436,89 @@ class CutOut(FlatCAMTool):
             else:
                 object_geo = cutout_obj.solid_geometry
 
-            # try:
-            #     __ = iter(object_geo)
-            # except TypeError:
-            #     object_geo = [object_geo]
+            def cutout_handler(geom):
+                # Get min and max data for each object as we just cut rectangles across X or Y
+                xmin, ymin, xmax, ymax = recursive_bounds(geom)
+
+                px = 0.5 * (xmin + xmax) + margin
+                py = 0.5 * (ymin + ymax) + margin
+                lenx = (xmax - xmin) + (margin * 2)
+                leny = (ymax - ymin) + (margin * 2)
+
+                proc_geometry = []
+
+                if gaps == '8' or gaps == '2LR':
+                    geom = self.subtract_poly_from_geo(geom,
+                                                       xmin - gapsize,  # botleft_x
+                                                       py - gapsize + leny / 4,  # botleft_y
+                                                       xmax + gapsize,  # topright_x
+                                                       py + gapsize + leny / 4)  # topright_y
+                    geom = self.subtract_poly_from_geo(geom,
+                                                       xmin - gapsize,
+                                                       py - gapsize - leny / 4,
+                                                       xmax + gapsize,
+                                                       py + gapsize - leny / 4)
+
+                if gaps == '8' or gaps == '2TB':
+                    geom = self.subtract_poly_from_geo(geom,
+                                                       px - gapsize + lenx / 4,
+                                                       ymin - gapsize,
+                                                       px + gapsize + lenx / 4,
+                                                       ymax + gapsize)
+                    geom = self.subtract_poly_from_geo(geom,
+                                                       px - gapsize - lenx / 4,
+                                                       ymin - gapsize,
+                                                       px + gapsize - lenx / 4,
+                                                       ymax + gapsize)
+
+                if gaps == '4' or gaps == 'LR':
+                    geom = self.subtract_poly_from_geo(geom,
+                                                       xmin - gapsize,
+                                                       py - gapsize,
+                                                       xmax + gapsize,
+                                                       py + gapsize)
+
+                if gaps == '4' or gaps == 'TB':
+                    geom = self.subtract_poly_from_geo(geom,
+                                                       px - gapsize,
+                                                       ymin - gapsize,
+                                                       px + gapsize,
+                                                       ymax + gapsize)
 
-            object_geo = unary_union(object_geo)
+                try:
+                    for g in geom:
+                        proc_geometry.append(g)
+                except TypeError:
+                    proc_geometry.append(geom)
 
-            # for geo in object_geo:
-            if isinstance(cutout_obj, FlatCAMGerber):
-                geo = object_geo.buffer(margin + abs(dia / 2))
-                geo = geo.exterior
+                return proc_geometry
+
+            if kind == 'single':
+                object_geo = unary_union(object_geo)
+
+                # for geo in object_geo:
+                if isinstance(cutout_obj, FlatCAMGerber):
+                    if isinstance(object_geo, MultiPolygon):
+                        x0, y0, x1, y1 = object_geo.bounds
+                        object_geo = box(x0, y0, x1, y1)
+
+                    geo_buf = object_geo.buffer(margin + abs(dia / 2))
+                    geo = geo_buf.exterior
+                else:
+                    geo = object_geo
+
+                solid_geo = cutout_handler(geom=geo)
             else:
-                geo = object_geo
-
-            # Get min and max data for each object as we just cut rectangles across X or Y
-            xmin, ymin, xmax, ymax = recursive_bounds(geo)
-
-            px = 0.5 * (xmin + xmax) + margin
-            py = 0.5 * (ymin + ymax) + margin
-            lenx = (xmax - xmin) + (margin * 2)
-            leny = (ymax - ymin) + (margin * 2)
-
-            if gaps == '8' or gaps == '2LR':
-                geo = self.subtract_poly_from_geo(geo,
-                                                  xmin - gapsize,           # botleft_x
-                                                  py - gapsize + leny / 4,  # botleft_y
-                                                  xmax + gapsize,           # topright_x
-                                                  py + gapsize + leny / 4)  # topright_y
-                geo = self.subtract_poly_from_geo(geo,
-                                                  xmin - gapsize,
-                                                  py - gapsize - leny / 4,
-                                                  xmax + gapsize,
-                                                  py + gapsize - leny / 4)
-
-            if gaps == '8' or gaps == '2TB':
-                geo = self.subtract_poly_from_geo(geo,
-                                                  px - gapsize + lenx / 4,
-                                                  ymin - gapsize,
-                                                  px + gapsize + lenx / 4,
-                                                  ymax + gapsize)
-                geo = self.subtract_poly_from_geo(geo,
-                                                  px - gapsize - lenx / 4,
-                                                  ymin - gapsize,
-                                                  px + gapsize - lenx / 4,
-                                                  ymax + gapsize)
-
-            if gaps == '4' or gaps == 'LR':
-                geo = self.subtract_poly_from_geo(geo,
-                                                  xmin - gapsize,
-                                                  py - gapsize,
-                                                  xmax + gapsize,
-                                                  py + gapsize)
-
-            if gaps == '4' or gaps == 'TB':
-                geo = self.subtract_poly_from_geo(geo,
-                                                  px - gapsize,
-                                                  ymin - gapsize,
-                                                  px + gapsize,
-                                                  ymax + gapsize)
+                try:
+                    __ = iter(object_geo)
+                except TypeError:
+                    object_geo = [object_geo]
 
-            try:
-                for g in geo:
-                    solid_geo.append(g)
-            except TypeError:
-                solid_geo.append(geo)
+                for geom_struct in object_geo:
+                    if isinstance(cutout_obj, FlatCAMGerber):
+                        geom_struct = (geom_struct.buffer(margin + abs(dia / 2))).exterior
+
+                    solid_geo += cutout_handler(geom=geom_struct)
 
             geo_obj.solid_geometry = deepcopy(solid_geo)
             xmin, ymin, xmax, ymax = recursive_bounds(geo_obj.solid_geometry)
@@ -508,7 +547,8 @@ class CutOut(FlatCAMTool):
         # Get source object.
         try:
             cutout_obj = self.app.collection.get_by_name(str(name))
-        except:
+        except Exception as e:
+            log.debug("CutOut.on_rectangular_cutout() --> %s" % str(e))
             self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve object: %s") % name)
             return "Could not retrieve object: %s" % name
 
@@ -530,6 +570,11 @@ class CutOut(FlatCAMTool):
             self.app.inform.emit(_("[ERROR_NOTCL] Tool Diameter is zero value. Change it to a positive real number."))
             return "Tool Diameter is zero value. Change it to a positive real number."
 
+        try:
+            kind = self.obj_kind_combo.get_value()
+        except ValueError:
+            return
+
         try:
             margin = float(self.margin.get_value())
         except ValueError:
@@ -577,68 +622,85 @@ class CutOut(FlatCAMTool):
             solid_geo = []
             object_geo = cutout_obj.solid_geometry
 
-            try:
-                __ = iter(object_geo)
-            except TypeError:
-                object_geo = [object_geo]
+            def cutout_rect_handler(geom):
+                proc_geometry = []
+
+                px = 0.5 * (xmin + xmax) + margin
+                py = 0.5 * (ymin + ymax) + margin
+                lenx = (xmax - xmin) + (margin * 2)
+                leny = (ymax - ymin) + (margin * 2)
+
+                if gaps == '8' or gaps == '2LR':
+                    geom = self.subtract_poly_from_geo(geom,
+                                                       xmin - gapsize,  # botleft_x
+                                                       py - gapsize + leny / 4,  # botleft_y
+                                                       xmax + gapsize,  # topright_x
+                                                       py + gapsize + leny / 4)  # topright_y
+                    geom = self.subtract_poly_from_geo(geom,
+                                                       xmin - gapsize,
+                                                       py - gapsize - leny / 4,
+                                                       xmax + gapsize,
+                                                       py + gapsize - leny / 4)
+
+                if gaps == '8' or gaps == '2TB':
+                    geom = self.subtract_poly_from_geo(geom,
+                                                       px - gapsize + lenx / 4,
+                                                       ymin - gapsize,
+                                                       px + gapsize + lenx / 4,
+                                                       ymax + gapsize)
+                    geom = self.subtract_poly_from_geo(geom,
+                                                       px - gapsize - lenx / 4,
+                                                       ymin - gapsize,
+                                                       px + gapsize - lenx / 4,
+                                                       ymax + gapsize)
+
+                if gaps == '4' or gaps == 'LR':
+                    geom = self.subtract_poly_from_geo(geom,
+                                                       xmin - gapsize,
+                                                       py - gapsize,
+                                                       xmax + gapsize,
+                                                       py + gapsize)
+
+                if gaps == '4' or gaps == 'TB':
+                    geom = self.subtract_poly_from_geo(geom,
+                                                       px - gapsize,
+                                                       ymin - gapsize,
+                                                       px + gapsize,
+                                                       ymax + gapsize)
+                try:
+                    for g in geom:
+                        proc_geometry.append(g)
+                except TypeError:
+                    proc_geometry.append(geom)
+                return proc_geometry
 
-            object_geo = unary_union(object_geo)
+            if kind == 'single':
+                object_geo = unary_union(object_geo)
 
-            xmin, ymin, xmax, ymax = object_geo.bounds
-            geo = box(xmin, ymin, xmax, ymax)
+                xmin, ymin, xmax, ymax = object_geo.bounds
+                geo = box(xmin, ymin, xmax, ymax)
 
-            # if Gerber create a buffer at a distance
-            # if Geometry then cut through the geometry
-            if isinstance(cutout_obj, FlatCAMGerber):
-                geo = geo.buffer(margin + abs(dia / 2))
+                # if Gerber create a buffer at a distance
+                # if Geometry then cut through the geometry
+                if isinstance(cutout_obj, FlatCAMGerber):
+                    geo = geo.buffer(margin + abs(dia / 2))
 
-            px = 0.5 * (xmin + xmax) + margin
-            py = 0.5 * (ymin + ymax) + margin
-            lenx = (xmax - xmin) + (margin * 2)
-            leny = (ymax - ymin) + (margin * 2)
-
-            if gaps == '8' or gaps == '2LR':
-                geo = self.subtract_poly_from_geo(geo,
-                                                  xmin - gapsize,  # botleft_x
-                                                  py - gapsize + leny / 4,  # botleft_y
-                                                  xmax + gapsize,  # topright_x
-                                                  py + gapsize + leny / 4)  # topright_y
-                geo = self.subtract_poly_from_geo(geo,
-                                                  xmin - gapsize,
-                                                  py - gapsize - leny / 4,
-                                                  xmax + gapsize,
-                                                  py + gapsize - leny / 4)
-
-            if gaps == '8' or gaps == '2TB':
-                geo = self.subtract_poly_from_geo(geo,
-                                                  px - gapsize + lenx / 4,
-                                                  ymin - gapsize,
-                                                  px + gapsize + lenx / 4,
-                                                  ymax + gapsize)
-                geo = self.subtract_poly_from_geo(geo,
-                                                  px - gapsize - lenx / 4,
-                                                  ymin - gapsize,
-                                                  px + gapsize - lenx / 4,
-                                                  ymax + gapsize)
-
-            if gaps == '4' or gaps == 'LR':
-                geo = self.subtract_poly_from_geo(geo,
-                                                  xmin - gapsize,
-                                                  py - gapsize,
-                                                  xmax + gapsize,
-                                                  py + gapsize)
-
-            if gaps == '4' or gaps == 'TB':
-                geo = self.subtract_poly_from_geo(geo,
-                                                  px - gapsize,
-                                                  ymin - gapsize,
-                                                  px + gapsize,
-                                                  ymax + gapsize)
-            try:
-                for g in geo:
-                    solid_geo.append(g)
-            except TypeError:
-                solid_geo.append(geo)
+                solid_geo = cutout_rect_handler(geom=geo)
+            else:
+                try:
+                    __ = iter(object_geo)
+                except TypeError:
+                    object_geo = [object_geo]
+
+                for geom_struct in object_geo:
+                    geom_struct = unary_union(geom_struct)
+                    xmin, ymin, xmax, ymax = geom_struct.bounds
+                    geom_struct = box(xmin, ymin, xmax, ymax)
+
+                    if isinstance(cutout_obj, FlatCAMGerber):
+                        geom_struct = geom_struct.buffer(margin + abs(dia / 2))
+
+                    solid_geo += cutout_rect_handler(geom=geom_struct)
 
             geo_obj.solid_geometry = deepcopy(solid_geo)
             geo_obj.options['cnctooldia'] = str(dia)
@@ -715,7 +777,8 @@ class CutOut(FlatCAMTool):
         # Get source object.
         try:
             cutout_obj = self.app.collection.get_by_name(str(name))
-        except:
+        except Exception as e:
+            log.debug("CutOut.on_manual_cutout() --> %s" % str(e))
             self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve Geometry object: %s") % name)
             return "Could not retrieve object: %s" % name
 
@@ -746,18 +809,19 @@ class CutOut(FlatCAMTool):
         # Get source object.
         try:
             cutout_obj = self.app.collection.get_by_name(str(name))
-        except:
+        except Exception as e:
+            log.debug("CutOut.on_manual_geo() --> %s" % str(e))
             self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve Gerber object: %s") % name)
             return "Could not retrieve object: %s" % name
 
         if cutout_obj is None:
             self.app.inform.emit(_("[ERROR_NOTCL] There is no Gerber object selected for Cutout.\n"
-                                 "Select one and try again."))
+                                   "Select one and try again."))
             return
 
         if not isinstance(cutout_obj, FlatCAMGerber):
             self.app.inform.emit(_("[ERROR_NOTCL] The selected object has to be of Gerber type.\n"
-                                 "Select a Gerber file and try again."))
+                                   "Select a Gerber file and try again."))
             return
 
         try:
@@ -768,13 +832,18 @@ class CutOut(FlatCAMTool):
                 dia = float(self.dia.get_value().replace(',', '.'))
             except ValueError:
                 self.app.inform.emit(_("[WARNING_NOTCL] Tool diameter value is missing or wrong format. "
-                                     "Add it and retry."))
+                                       "Add it and retry."))
                 return
 
         if 0 in {dia}:
             self.app.inform.emit(_("[ERROR_NOTCL] Tool Diameter is zero value. Change it to a positive real number."))
             return "Tool Diameter is zero value. Change it to a positive real number."
 
+        try:
+            kind = self.obj_kind_combo.get_value()
+        except ValueError:
+            return
+
         try:
             margin = float(self.margin.get_value())
         except ValueError:
@@ -783,7 +852,7 @@ class CutOut(FlatCAMTool):
                 margin = float(self.margin.get_value().replace(',', '.'))
             except ValueError:
                 self.app.inform.emit(_("[WARNING_NOTCL] Margin value is missing or wrong format. "
-                                     "Add it and retry."))
+                                       "Add it and retry."))
                 return
 
         convex_box = self.convex_box.get_value()
@@ -794,6 +863,10 @@ class CutOut(FlatCAMTool):
             if convex_box:
                 geo = geo_union.convex_hull
                 geo_obj.solid_geometry = geo.buffer(margin + abs(dia / 2))
+            elif kind == 'single':
+                x0, y0, x1, y1 = geo_union.bounds
+                geo = box(x0, y0, x1, y1)
+                geo_obj.solid_geometry = geo.buffer(margin + abs(dia / 2))
             else:
                 geo = geo_union
                 geo = geo.buffer(margin + abs(dia / 2))