Sfoglia il codice sorgente

- in Tool Transform added a new feature named 'Buffer'. For Geometry and Gerber objects will create (and replace) a geometry at a distance from the original geometry and for Excellon will adjust the Tool diameters

Marius Stanciu 6 anni fa
parent
commit
b1b140634b

+ 4 - 0
FlatCAMApp.py

@@ -830,6 +830,8 @@ class App(QtCore.QObject):
             "tools_transform_offset_y": 0.0,
             "tools_transform_mirror_reference": False,
             "tools_transform_mirror_point": (0, 0),
+            "tools_transform_buffer_dis": 0.0,
+            "tools_transform_buffer_corner": True,
 
             # SolderPaste Tool
             "tools_solderpaste_tools": "1.0, 0.3",
@@ -1432,6 +1434,8 @@ class App(QtCore.QObject):
             "tools_transform_offset_y": self.ui.tools_defaults_form.tools_transform_group.offy_entry,
             "tools_transform_mirror_reference": self.ui.tools_defaults_form.tools_transform_group.mirror_reference_cb,
             "tools_transform_mirror_point": self.ui.tools_defaults_form.tools_transform_group.flip_ref_entry,
+            "tools_transform_buffer_dis": self.ui.tools_defaults_form.tools_transform_group.buffer_entry,
+            "tools_transform_buffer_corner": self.ui.tools_defaults_form.tools_transform_group.buffer_rounded_cb,
 
             # SolderPaste Dispensing Tool
             "tools_solderpaste_tools": self.ui.tools_defaults_form.tools_solderpaste_group.nozzle_tool_dia_entry,

+ 14 - 3
FlatCAMObj.py

@@ -599,7 +599,9 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
     def __init__(self, name):
         self.decimals = self.app.decimals
 
-        Gerber.__init__(self, steps_per_circle=int(self.app.defaults["gerber_circle_steps"]))
+        self.circle_steps = int(self.app.defaults["gerber_circle_steps"])
+
+        Gerber.__init__(self, steps_per_circle=self.circle_steps)
         FlatCAMObj.__init__(self, name)
 
         self.kind = "gerber"
@@ -2199,6 +2201,10 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
         Gerber.skew(self, angle_x=angle_x, angle_y=angle_y, point=point)
         self.replotApertures.emit()
 
+    def buffer(self, distance, join):
+        Gerber.buffer(self, distance=distance, join=join)
+        self.replotApertures.emit()
+
     def serialize(self):
         return {
             "options": self.options,
@@ -2217,7 +2223,9 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
     def __init__(self, name):
         self.decimals = self.app.decimals
 
-        Excellon.__init__(self, geo_steps_per_circle=int(self.app.defaults["geometry_circle_steps"]))
+        self.circle_steps = int(self.app.defaults["geometry_circle_steps"])
+
+        Excellon.__init__(self, geo_steps_per_circle=self.circle_steps)
         FlatCAMObj.__init__(self, name)
 
         self.kind = "excellon"
@@ -3545,8 +3553,11 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
 
     def __init__(self, name):
         self.decimals = self.app.decimals
+
+        self.circle_steps = int(self.app.defaults["geometry_circle_steps"])
+
         FlatCAMObj.__init__(self, name)
-        Geometry.__init__(self, geo_steps_per_circle=int(self.app.defaults["geometry_circle_steps"]))
+        Geometry.__init__(self, geo_steps_per_circle=self.circle_steps)
 
         self.kind = "geometry"
 

+ 0 - 5
ObjectCollection.py

@@ -787,11 +787,6 @@ class ObjectCollection(QtCore.QAbstractItemModel):
         # FlatCAMApp.App.log.debug("Current: %s, Previous %s" % (str(current), str(previous)))
 
         try:
-            # delete selection shape
-            self.app.delete_selection_shape()
-            for o in self.get_list():
-                o.selection_shape_drawn = False
-
             obj = current.indexes()[0].internalPointer().obj
             self.item_selected.emit(obj.options['name'])
 

+ 2 - 1
README.md

@@ -17,8 +17,9 @@ CAD program, and create G-Code for Isolation routing.
 - speed up the plotting in OpenGL(3D) graphic mode
 - spped up the color setting for Gerber object when using the OpenGL(3D) graphic mode
 - setting color for Gerber objects work on a selection of Gerber objects
-- when the selection is changed in the Project Tree the selection shape on canvas is deleted
+- ~~when the selection is changed in the Project Tree the selection shape on canvas is deleted~~
 - if an object is selected on Project Tree and it does not have the selection shape drawn, first click on canvas over it will draw the selection shape 
+- in Tool Transform added a new feature named 'Buffer'. For Geometry and Gerber objects will create (and replace) a geometry at a distance from the original geometry and for Excellon will adjust the Tool diameters
 
 22.12.2019
 

+ 63 - 0
camlib.py

@@ -2118,6 +2118,69 @@ class Geometry(object):
         #     self.solid_geometry = affinity.skew(self.solid_geometry, angle_x, angle_y,
         #                                         origin=(px, py))
 
+    def buffer(self, distance, join):
+        """
+
+        :param distance:
+        :param join:
+        :return:
+        """
+
+        log.debug("camlib.Geometry.buffer()")
+
+        if distance == 0:
+            return
+
+        def buffer_geom(obj):
+            if type(obj) is list:
+                new_obj = []
+                for g in obj:
+                    new_obj.append(buffer_geom(g))
+                return new_obj
+            else:
+                try:
+                    self.el_count += 1
+                    disp_number = int(np.interp(self.el_count, [0, self.geo_len], [0, 100]))
+                    if self.old_disp_number < disp_number <= 100:
+                        self.app.proc_container.update_view_text(' %d%%' % disp_number)
+                        self.old_disp_number = disp_number
+
+                    return obj.buffer(distance, resolution=self.geo_steps_per_circle, join_style=join)
+                except AttributeError:
+                    return obj
+
+        try:
+            if self.multigeo is True:
+                for tool in self.tools:
+                    # variables to display the percentage of work done
+                    self.geo_len = 0
+                    try:
+                        for __ in self.tools[tool]['solid_geometry']:
+                            self.geo_len += 1
+                    except TypeError:
+                        self.geo_len = 1
+                    self.old_disp_number = 0
+                    self.el_count = 0
+
+                    self.tools[tool]['solid_geometry'] = buffer_geom(self.tools[tool]['solid_geometry'])
+
+            # variables to display the percentage of work done
+            self.geo_len = 0
+            try:
+                for __ in self.solid_geometry:
+                    self.geo_len += 1
+            except TypeError:
+                self.geo_len = 1
+            self.old_disp_number = 0
+            self.el_count = 0
+
+            self.solid_geometry = buffer_geom(self.solid_geometry)
+
+            self.app.inform.emit('[success] %s...' %  _('Object was buffered'))
+        except AttributeError:
+            self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed to buffer. No object selected"))
+
+        self.app.proc_container.new_text = ''
 
 class AttrDict(dict):
     def __init__(self, *args, **kwargs):

+ 44 - 6
flatcamGUI/PreferencesUI.py

@@ -5378,7 +5378,7 @@ class ToolsTransformPrefGroupUI(OptionsGroupUI):
         grid0.addWidget(self.skewy_label, 4, 0)
         grid0.addWidget(self.skewy_entry, 4, 1)
 
-        # ## Scale factor on X axis
+        # ## Scale
         scale_title_lbl = QtWidgets.QLabel('<b>%s</b>' % _("Scale"))
         grid0.addWidget(scale_title_lbl, 5, 0, 1, 2)
 
@@ -5425,7 +5425,7 @@ class ToolsTransformPrefGroupUI(OptionsGroupUI):
         )
         grid0.addWidget(self.reference_cb, 8, 1)
 
-        # ## Offset distance on X axis
+        # ## Offset
         offset_title_lbl = QtWidgets.QLabel('<b>%s</b>' % _("Offset"))
         grid0.addWidget(offset_title_lbl, 9, 0, 1, 2)
 
@@ -5454,6 +5454,10 @@ class ToolsTransformPrefGroupUI(OptionsGroupUI):
         grid0.addWidget(self.offy_label, 11, 0)
         grid0.addWidget(self.offy_entry, 11, 1)
 
+        # ## Mirror
+        mirror_title_lbl = QtWidgets.QLabel('<b>%s</b>' % _("Mirror"))
+        grid0.addWidget(mirror_title_lbl, 12, 0, 1, 2)
+
         # ## Mirror (Flip) Reference Point
         self.mirror_reference_cb = FCCheckBox('%s' % _("Mirror Reference"))
         self.mirror_reference_cb.setToolTip(
@@ -5466,9 +5470,9 @@ class ToolsTransformPrefGroupUI(OptionsGroupUI):
               "Then click Add button to insert coordinates.\n"
               "Or enter the coords in format (x, y) in the\n"
               "Point Entry field and click Flip on X(Y)"))
-        grid0.addWidget(self.mirror_reference_cb, 12, 0, 1, 2)
+        grid0.addWidget(self.mirror_reference_cb, 13, 0, 1, 2)
 
-        self.flip_ref_label = QtWidgets.QLabel('<b>%s</b>' % _("Mirror Reference point"))
+        self.flip_ref_label = QtWidgets.QLabel('%s' % _("Mirror Reference point"))
         self.flip_ref_label.setToolTip(
             _("Coordinates in format (x, y) used as reference for mirroring.\n"
               "The 'x' in (x, y) will be used when using Flip on X and\n"
@@ -5476,8 +5480,42 @@ class ToolsTransformPrefGroupUI(OptionsGroupUI):
         )
         self.flip_ref_entry = EvalEntry2("(0, 0)")
 
-        grid0.addWidget(self.flip_ref_label, 13, 0, 1, 2)
-        grid0.addWidget(self.flip_ref_entry, 14, 0, 1, 2)
+        grid0.addWidget(self.flip_ref_label, 14, 0, 1, 2)
+        grid0.addWidget(self.flip_ref_entry, 15, 0, 1, 2)
+
+        # ## Buffer
+        buffer_title_lbl = QtWidgets.QLabel('<b>%s</b>' % _("Buffer"))
+        grid0.addWidget(buffer_title_lbl, 16, 0, 1, 2)
+
+        self.buffer_label = QtWidgets.QLabel('%s:' % _("Distance"))
+        self.buffer_label.setToolTip(
+            _("A positive value will create the effect of dilation,\n"
+              "while a negative value will create the effect of erosion.\n"
+              "Each geometry element of the object will be increased\n"
+              "or decreased with the 'distance'.")
+        )
+
+        self.buffer_entry = FCDoubleSpinner()
+        self.buffer_entry.set_precision(self.decimals)
+        self.buffer_entry.setSingleStep(0.1)
+        self.buffer_entry.setWrapping(True)
+        self.buffer_entry.set_range(-9999.9999, 9999.9999)
+
+        grid0.addWidget(self.buffer_label, 17, 0)
+        grid0.addWidget(self.buffer_entry, 17, 1)
+
+        self.buffer_rounded_cb = FCCheckBox()
+        self.buffer_rounded_cb.setText('%s' % _("Rounded"))
+        self.buffer_rounded_cb.setToolTip(
+            _("If checked then the buffer will surround the buffered shape,\n"
+              "every corner will be rounded.\n"
+              "If not checked then the buffer will follow the exact geometry\n"
+              "of the buffered shape.")
+        )
+
+        grid0.addWidget(self.buffer_rounded_cb, 18, 0, 1, 2)
+
+        grid0.addWidget(QtWidgets.QLabel(''), 19, 0, 1, 2)
 
         self.layout.addStretch()
 

+ 32 - 1
flatcamParsers/ParseExcellon.py

@@ -1457,4 +1457,35 @@ class Excellon(Geometry):
                 slot['start'] = affinity.rotate(slot['start'], angle, origin=(px, py))
 
         self.create_geometry()
-        self.app.proc_container.new_text = ''
+        self.app.proc_container.new_text = ''
+
+    def buffer(self, distance, join):
+        """
+
+        :param distance:
+        :param join:
+        :return:
+        """
+        log.debug("flatcamParsers.ParseExcellon.Excellon.buffer()")
+
+        if distance == 0:
+            return
+
+        def buffer_geom(obj):
+            if type(obj) is list:
+                new_obj = []
+                for g in obj:
+                    new_obj.append(buffer_geom(g))
+                return new_obj
+            else:
+                try:
+                    return obj.buffer(distance, resolution=self.geo_steps_per_circle)
+                except AttributeError:
+                    return obj
+
+        # buffer solid_geometry
+        for tool, tool_dict in list(self.tools.items()):
+            self.tools[tool]['solid_geometry'] = buffer_geom(tool_dict['solid_geometry'])
+            self.tools[tool]['C'] += distance
+
+        self.create_geometry()

+ 81 - 0
flatcamParsers/ParseGerber.py

@@ -2169,6 +2169,87 @@ class Gerber(Geometry):
                              _("Gerber Rotate done."))
         self.app.proc_container.new_text = ''
 
+    def buffer(self, distance, join):
+        """
+
+        :param distance:
+        :return:
+        """
+        log.debug("parseGerber.Gerber.buffer()")
+
+        if distance == 0:
+            return
+
+        # variables to display the percentage of work done
+        self.geo_len = 0
+        try:
+            for __ in self.solid_geometry:
+                self.geo_len += 1
+        except TypeError:
+            self.geo_len = 1
+
+        self.old_disp_number = 0
+        self.el_count = 0
+
+        def buffer_geom(obj):
+            if type(obj) is list:
+                new_obj = []
+                for g in obj:
+                    new_obj.append(buffer_geom(g))
+                return new_obj
+            else:
+                try:
+                    self.el_count += 1
+                    disp_number = int(np.interp(self.el_count, [0, self.geo_len], [0, 100]))
+                    if self.old_disp_number < disp_number <= 100:
+                        self.app.proc_container.update_view_text(' %d%%' % disp_number)
+                        self.old_disp_number = disp_number
+
+                    return obj.buffer(distance, resolution=self.steps_per_circle, join_style=join)
+                except AttributeError:
+                    return obj
+
+        self.solid_geometry = buffer_geom(self.solid_geometry)
+
+        # we need to buffer the geometry stored in the Gerber apertures, too
+        try:
+            for apid in self.apertures:
+                new_geometry = list()
+                if 'geometry' in self.apertures[apid]:
+                    for geo_el in self.apertures[apid]['geometry']:
+                        new_geo_el = dict()
+                        if 'solid' in geo_el:
+                            new_geo_el['solid'] = buffer_geom(geo_el['solid'])
+                        if 'follow' in geo_el:
+                            new_geo_el['follow'] = buffer_geom(geo_el['follow'])
+                        if 'clear' in geo_el:
+                            new_geo_el['clear'] = buffer_geom(geo_el['clear'])
+                        new_geometry.append(new_geo_el)
+
+                self.apertures[apid]['geometry'] = deepcopy(new_geometry)
+
+                try:
+                    if str(self.apertures[apid]['type']) == 'R' or str(self.apertures[apid]['type']) == 'O':
+                        self.apertures[apid]['width'] += (distance * 2)
+                        self.apertures[apid]['height'] += (distance * 2)
+                    elif str(self.apertures[apid]['type']) == 'P':
+                        self.apertures[apid]['diam'] += (distance * 2)
+                        self.apertures[apid]['nVertices'] += (distance * 2)
+                except KeyError:
+                    pass
+
+                try:
+                    if self.apertures[apid]['size'] is not None:
+                        self.apertures[apid]['size'] = float(self.apertures[apid]['size'] + (distance * 2))
+                except KeyError:
+                    pass
+        except Exception as e:
+            log.debug('camlib.Gerber.buffer() Exception --> %s' % str(e))
+            return 'fail'
+
+        self.app.inform.emit('[success] %s' % _("Gerber Buffer done."))
+        self.app.proc_container.new_text = ''
+
 
 def parse_gerber_number(strnumber, int_digits, frac_digits, zeros):
     """

+ 119 - 17
flatcamTools/ToolTransform.py

@@ -27,6 +27,7 @@ class ToolTransform(FlatCAMTool):
     scaleName = _("Scale")
     flipName = _("Mirror (Flip)")
     offsetName = _("Offset")
+    bufferName = _("Buffer")
 
     def __init__(self, app):
         FlatCAMTool.__init__(self, app)
@@ -255,11 +256,11 @@ class ToolTransform(FlatCAMTool):
         grid0.addWidget(self.offy_entry, 14, 1)
         grid0.addWidget(self.offy_button, 14, 2)
 
-        grid0.addWidget(QtWidgets.QLabel(''))
+        grid0.addWidget(QtWidgets.QLabel(''), 15, 0, 1, 3)
 
         # ## Flip Title
         flip_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.flipName)
-        self.transform_lay.addWidget(flip_title_label)
+        grid0.addWidget(flip_title_label, 16, 0, 1, 3)
 
         self.flipx_button = FCButton()
         self.flipx_button.set_value(_("Flip on X"))
@@ -274,7 +275,7 @@ class ToolTransform(FlatCAMTool):
         )
 
         hlay0 = QtWidgets.QHBoxLayout()
-        self.transform_lay.addLayout(hlay0)
+        grid0.addLayout(hlay0, 17, 0, 1, 3)
 
         hlay0.addWidget(self.flipx_button)
         hlay0.addWidget(self.flipy_button)
@@ -293,7 +294,7 @@ class ToolTransform(FlatCAMTool):
               "Or enter the coords in format (x, y) in the\n"
               "Point Entry field and click Flip on X(Y)"))
 
-        self.transform_lay.addWidget(self.flip_ref_cb)
+        grid0.addWidget(self.flip_ref_cb, 18, 0, 1, 3)
 
         self.flip_ref_label = QtWidgets.QLabel('%s:' % _("Ref. Point"))
         self.flip_ref_label.setToolTip(
@@ -315,12 +316,60 @@ class ToolTransform(FlatCAMTool):
         self.ois_flip = OptionalInputSection(self.flip_ref_cb, [self.flip_ref_entry, self.flip_ref_button], logic=True)
 
         hlay1 = QtWidgets.QHBoxLayout()
-        self.transform_lay.addLayout(hlay1)
+        grid0.addLayout(hlay1, 19, 0, 1, 3)
 
         hlay1.addWidget(self.flip_ref_label)
         hlay1.addWidget(self.flip_ref_entry)
 
-        self.transform_lay.addWidget(self.flip_ref_button)
+        grid0.addWidget(self.flip_ref_button, 20, 0, 1, 3)
+
+        grid0.addWidget(QtWidgets.QLabel(''), 21, 0, 1, 3)
+
+        # ## Buffer Title
+        buffer_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.bufferName)
+        grid0.addWidget(buffer_title_label, 22, 0, 1, 3)
+
+        self.buffer_label = QtWidgets.QLabel('%s:' % _("Distance"))
+        self.buffer_label.setToolTip(
+            _("A positive value will create the effect of dilation,\n"
+              "while a negative value will create the effect of erosion.\n"
+              "Each geometry element of the object will be increased\n"
+              "or decreased with the 'distance'.")
+        )
+
+        self.buffer_entry = FCDoubleSpinner()
+        self.buffer_entry.set_precision(self.decimals)
+        self.buffer_entry.setSingleStep(0.1)
+        self.buffer_entry.setWrapping(True)
+        self.buffer_entry.set_range(-9999.9999, 9999.9999)
+
+        # self.rotate_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+
+        self.buffer_button = FCButton()
+        self.buffer_button.set_value(_("Buffer"))
+        self.buffer_button.setToolTip(
+            _("Create the buffer effect on each geometry,\n"
+              "element from the selected object.")
+        )
+        self.buffer_button.setMinimumWidth(90)
+
+        grid0.addWidget(self.buffer_label, 23, 0)
+        grid0.addWidget(self.buffer_entry, 23, 1)
+        grid0.addWidget(self.buffer_button, 23, 2)
+
+        self.buffer_rounded_cb = FCCheckBox()
+        self.buffer_rounded_cb.setText('%s' % _("Rounded"))
+        self.buffer_rounded_cb.setToolTip(
+            _("If checked then the buffer will surround the buffered shape,\n"
+              "every corner will be rounded.\n"
+              "If not checked then the buffer will follow the exact geometry\n"
+              "of the buffered shape.")
+        )
+
+        grid0.addWidget(self.buffer_rounded_cb, 24, 0, 1, 3)
+
+        grid0.addWidget(QtWidgets.QLabel(''), 25, 0, 1, 3)
+
         self.transform_lay.addStretch()
 
         # ## Signals
@@ -334,14 +383,16 @@ class ToolTransform(FlatCAMTool):
         self.flipx_button.clicked.connect(self.on_flipx)
         self.flipy_button.clicked.connect(self.on_flipy)
         self.flip_ref_button.clicked.connect(self.on_flip_add_coords)
+        self.buffer_button.clicked.connect(self.on_buffer)
 
-        self.rotate_entry.returnPressed.connect(self.on_rotate)
-        self.skewx_entry.returnPressed.connect(self.on_skewx)
-        self.skewy_entry.returnPressed.connect(self.on_skewy)
-        self.scalex_entry.returnPressed.connect(self.on_scalex)
-        self.scaley_entry.returnPressed.connect(self.on_scaley)
-        self.offx_entry.returnPressed.connect(self.on_offx)
-        self.offy_entry.returnPressed.connect(self.on_offy)
+        # self.rotate_entry.returnPressed.connect(self.on_rotate)
+        # self.skewx_entry.returnPressed.connect(self.on_skewx)
+        # self.skewy_entry.returnPressed.connect(self.on_skewy)
+        # self.scalex_entry.returnPressed.connect(self.on_scalex)
+        # self.scaley_entry.returnPressed.connect(self.on_scaley)
+        # self.offx_entry.returnPressed.connect(self.on_offx)
+        # self.offy_entry.returnPressed.connect(self.on_offy)
+        # self.buffer_entry.returnPressed.connect(self.on_buffer)
 
     def run(self, toggle=True):
         self.app.report_usage("ToolTransform()")
@@ -430,6 +481,16 @@ class ToolTransform(FlatCAMTool):
         else:
             self.flip_ref_entry.set_value((0, 0))
 
+        if self.app.defaults["tools_transform_buffer_dis"]:
+            self.buffer_entry.set_value(self.app.defaults["tools_transform_buffer_dis"])
+        else:
+            self.buffer_entry.set_value(0.0)
+
+        if self.app.defaults["tools_transform_buffer_corner"]:
+            self.buffer_rounded_cb.set_value(self.app.defaults["tools_transform_buffer_corner"])
+        else:
+            self.buffer_rounded_cb.set_value(True)
+
     def on_rotate(self):
         value = float(self.rotate_entry.get_value())
         if value == 0:
@@ -511,8 +572,7 @@ class ToolTransform(FlatCAMTool):
     def on_offx(self):
         value = float(self.offx_entry.get_value())
         if value == 0:
-            self.app.inform.emit('[WARNING_NOTCL] %s' %
-                                 _("Offset transformation can not be done for a value of 0."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Offset transformation can not be done for a value of 0."))
             return
         axis = 'X'
 
@@ -522,14 +582,20 @@ class ToolTransform(FlatCAMTool):
     def on_offy(self):
         value = float(self.offy_entry.get_value())
         if value == 0:
-            self.app.inform.emit('[WARNING_NOTCL] %s' %
-                                 _("Offset transformation can not be done for a value of 0."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Offset transformation can not be done for a value of 0."))
             return
         axis = 'Y'
 
         self.app.worker_task.emit({'fcn': self.on_offset, 'params': [axis, value]})
         return
 
+    def on_buffer(self):
+        value = self.buffer_entry.get_value()
+        join = 1 if self.buffer_rounded_cb.get_value() else 2
+
+        self.app.worker_task.emit({'fcn': self.on_buffer_action, 'params': [value, join]})
+        return
+
     def on_rotate_action(self, num):
         obj_list = self.app.collection.get_selected()
         xminlist = []
@@ -808,4 +874,40 @@ class ToolTransform(FlatCAMTool):
                                          (_("Due of"), str(e),  _("action was not executed.")))
                     return
 
+    def on_buffer_action(self, value, join):
+        obj_list = self.app.collection.get_selected()
+
+        if not obj_list:
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("No object selected. Please Select an object to buffer!"))
+            return
+        else:
+            with self.app.proc_container.new(_("Applying Buffer")):
+                try:
+                    for sel_obj in obj_list:
+                        if isinstance(sel_obj, FlatCAMCNCjob):
+                            self.app.inform.emit(_("CNCJob objects can't be buffered."))
+                        elif sel_obj.kind.lower() == 'gerber':
+                            sel_obj.buffer(value, join)
+                            sel_obj.source_file = self.app.export_gerber(obj_name=sel_obj.options['name'],
+                                                                         filename=None, local_use=sel_obj,
+                                                                         use_thread=False)
+                        elif sel_obj.kind.lower() == 'excellon':
+                            sel_obj.buffer(value, join)
+                            sel_obj.source_file = self.app.export_excellon(obj_name=sel_obj.options['name'],
+                                                                           filename=None, local_use=sel_obj,
+                                                                           use_thread=False)
+                        elif sel_obj.kind.lower() == 'geometry':
+                            sel_obj.buffer(value, join)
+
+                        self.app.object_changed.emit(sel_obj)
+                        sel_obj.plot()
+
+                    self.app.inform.emit('[success] %s...' % _('Buffer done'))
+
+                except Exception as e:
+                    self.app.log.debug("ToolTransform.on_buffer_action() --> %s" % str(e))
+                    self.app.inform.emit('[ERROR_NOTCL] %s %s, %s.' %
+                                         (_("Due of"), str(e),  _("action was not executed.")))
+                    return
+
 # end of file