Przeglądaj źródła

- modified the Cutout Tool to generate multi-geo objects therefore the set geometry parameters will populate the Geometry Object UI
- modified the Panelize Tool to optimize the output from Cutout Tool such that there are no longer overlapping cuts
- some string corrections

Marius Stanciu 5 lat temu
rodzic
commit
ada271cbd6

+ 3 - 0
CHANGELOG.md

@@ -14,6 +14,9 @@ CHANGELOG for FlatCAM beta
 - fixed bug in Gerber Editor in which the units conversion wasn't calculated correct
 - fixed bug in Gerber Editor in which the QThread that is started on object edit was not stopped at clean up stage
 - fixed bug in Gerber Editor that kept all the apertures (including the geometry) of a previously edited object that was not saved after edit
+- modified the Cutout Tool to generate multi-geo objects therefore the set geometry parameters will populate the Geometry Object UI
+- modified the Panelize Tool to optimize the output from Cutout Tool such that there are no longer overlapping cuts
+- some string corrections
 
 01.05.2020
 

+ 4 - 2
FlatCAMApp.py

@@ -4111,9 +4111,11 @@ class App(QtCore.QObject):
         obj.multigeo = True
         for tooluid, dict_value in obj.tools.items():
             dict_value['solid_geometry'] = deepcopy(obj.solid_geometry)
+
         if not isinstance(obj.solid_geometry, list):
             obj.solid_geometry = [obj.solid_geometry]
-        obj.solid_geometry[:] = []
+
+        # obj.solid_geometry[:] = []
         obj.plot()
 
         self.should_we_save = True
@@ -5024,7 +5026,7 @@ class App(QtCore.QObject):
                 self.paste_tool.on_add_tool_by_key()
 
     # It's meant to delete tools in tool tables via a 'Delete' shortcut key but only if certain conditions are met
-    # See description bellow.
+    # See description below.
     def on_delete_keypress(self):
         notebook_widget_name = self.ui.notebook.currentWidget().objectName()
 

+ 2 - 2
flatcamEditors/FlatCAMExcEditor.py

@@ -3226,7 +3226,7 @@ class FlatCAMExcEditor(QtCore.QObject):
             spec = {"C": float(tool_dia[0])}
             self.new_tools[name] = spec
 
-            # add in self.tools the 'solid_geometry' key, the value (a list) is populated bellow
+            # add in self.tools the 'solid_geometry' key, the value (a list) is populated below
             self.new_tools[name]['solid_geometry'] = []
 
             # create the self.drills for the new Excellon object (the one with edited content)
@@ -3258,7 +3258,7 @@ class FlatCAMExcEditor(QtCore.QObject):
                 spec = {"C": float(tool_dia[0])}
                 self.new_tools[name] = spec
 
-                # add in self.tools the 'solid_geometry' key, the value (a list) is populated bellow
+                # add in self.tools the 'solid_geometry' key, the value (a list) is populated below
                 self.new_tools[name]['solid_geometry'] = []
 
             # create the self.slots for the new Excellon object (the one with edited content)

+ 1 - 1
flatcamGUI/FlatCAMGUI.py

@@ -3232,7 +3232,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                         else:
                             self.app.collection.set_active(names_list[active_index-1])
 
-                # Select the object in the Tree bellow the current one
+                # Select the object in the Tree below the current one
                 if key == QtCore.Qt.Key_Down:
                     # make sure it works only for the Project Tab who is an instance of KeySensitiveListView
                     focused_wdg = QtWidgets.QApplication.focusWidget()

+ 1 - 1
flatcamGUI/preferences/PreferencesUIManager.py

@@ -42,7 +42,7 @@ class PreferencesUIManager:
         # if Preferences are changed in the Edit -> Preferences tab the value will be set to True
         self.preferences_changed_flag = False
 
-        # when adding entries here read the comments in the  method found bellow named:
+        # when adding entries here read the comments in the  method found below named:
         # def new_object(self, kind, name, initialize, active=True, fit=True, plot=True)
         self.defaults_form_fields = {
             # General App

+ 1 - 1
flatcamObjects/FlatCAMCNCJob.py

@@ -364,7 +364,7 @@ class CNCJobObject(FlatCAMObj, CNCjob):
         self.units_found = self.app.defaults['units']
 
         # this signal has to be connected to it's slot before the defaults are populated
-        # the decision done in the slot has to override the default value set bellow
+        # the decision done in the slot has to override the default value set below
         self.ui.toolchange_cb.toggled.connect(self.on_toolchange_custom_clicked)
 
         self.form_fields.update({

+ 1 - 1
flatcamParsers/ParseExcellon.py

@@ -426,7 +426,7 @@ class Excellon(Geometry):
                                     # it's possible that tool definition has only tool number and no diameter info
                                     # (those could be in another file like PCB Wizard do)
                                     # then match.group(2) = None and float(None) will create the exception
-                                    # the bellow construction is so each tool will have a slightly different diameter
+                                    # the below construction is so each tool will have a slightly different diameter
                                     # starting with a default value, to allow Excellon editing after that
                                     self.diameterless = True
                                     self.app.inform.emit('[WARNING] %s%s %s' %

+ 106 - 9
flatcamTools/ToolCutOut.py

@@ -293,12 +293,12 @@ class CutOut(FlatCAMTool):
                             font-weight: bold;
                         }
                         """)
-        self.layout.addWidget(self.rect_cutout_object_btn)
+        grid0.addWidget(self.rect_cutout_object_btn, 21, 0, 1, 2)
 
         separator_line = QtWidgets.QFrame()
         separator_line.setFrameShape(QtWidgets.QFrame.HLine)
         separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 21, 0, 1, 2)
+        grid0.addWidget(separator_line, 22, 0, 1, 2)
 
         # Title5
         title_manual_label = QtWidgets.QLabel("<font size=4><b>%s</b></font>" % _('B. Manual Bridge Gaps'))
@@ -307,7 +307,7 @@ class CutOut(FlatCAMTool):
               "This is done by mouse clicking on the perimeter of the\n"
               "Geometry object that is used as a cutout object. ")
         )
-        grid0.addWidget(title_manual_label, 22, 0, 1, 2)
+        grid0.addWidget(title_manual_label, 23, 0, 1, 2)
 
         # Manual Geo Object
         self.man_object_combo = FCComboBox()
@@ -322,8 +322,8 @@ class CutOut(FlatCAMTool):
         )
         # self.man_object_label.setMinimumWidth(60)
 
-        grid0.addWidget(self.man_object_label, 23, 0, 1, 2)
-        grid0.addWidget(self.man_object_combo, 24, 0, 1, 2)
+        grid0.addWidget(self.man_object_label, 25, 0, 1, 2)
+        grid0.addWidget(self.man_object_combo, 26, 0, 1, 2)
 
         self.man_geo_creation_btn = FCButton(_("Generate Manual Geometry"))
         self.man_geo_creation_btn.setToolTip(
@@ -338,7 +338,7 @@ class CutOut(FlatCAMTool):
                             font-weight: bold;
                         }
                         """)
-        grid0.addWidget(self.man_geo_creation_btn, 25, 0, 1, 2)
+        grid0.addWidget(self.man_geo_creation_btn, 28, 0, 1, 2)
 
         self.man_gaps_creation_btn = FCButton(_("Manual Add Bridge Gaps"))
         self.man_gaps_creation_btn.setToolTip(
@@ -354,7 +354,7 @@ class CutOut(FlatCAMTool):
                             font-weight: bold;
                         }
                         """)
-        grid0.addWidget(self.man_gaps_creation_btn, 27, 0, 1, 2)
+        grid0.addWidget(self.man_gaps_creation_btn, 30, 0, 1, 2)
 
         self.layout.addStretch()
 
@@ -394,6 +394,9 @@ class CutOut(FlatCAMTool):
         self.x_pos = None
         self.y_pos = None
 
+        # store the default data for the resulting Geometry Object
+        self.default_data = {}
+
         # Signals
         self.ff_cutout_object_btn.clicked.connect(self.on_freeform_cutout)
         self.rect_cutout_object_btn.clicked.connect(self.on_rectangular_cutout)
@@ -454,6 +457,48 @@ class CutOut(FlatCAMTool):
         self.convex_box.set_value(self.app.defaults['tools_cutout_convexshape'])
         self.type_obj_radio.set_value('grb')
 
+        self.default_data.update({
+            "plot":             True,
+            "cutz":             float(self.app.defaults["geometry_cutz"]),
+            "multidepth":       self.app.defaults["geometry_multidepth"],
+            "depthperpass":     float(self.app.defaults["geometry_depthperpass"]),
+            "vtipdia":          float(self.app.defaults["geometry_vtipdia"]),
+            "vtipangle":        float(self.app.defaults["geometry_vtipangle"]),
+            "travelz":          float(self.app.defaults["geometry_travelz"]),
+            "feedrate":         float(self.app.defaults["geometry_feedrate"]),
+            "feedrate_z":       float(self.app.defaults["geometry_feedrate_z"]),
+            "feedrate_rapid":   float(self.app.defaults["geometry_feedrate_rapid"]),
+            "spindlespeed":     self.app.defaults["geometry_spindlespeed"],
+            "dwell":            self.app.defaults["geometry_dwell"],
+            "dwelltime":        float(self.app.defaults["geometry_dwelltime"]),
+            "ppname_g":         self.app.defaults["geometry_ppname_g"],
+            "extracut":         self.app.defaults["geometry_extracut"],
+            "extracut_length":  float(self.app.defaults["geometry_extracut_length"]),
+            "toolchange":       self.app.defaults["geometry_toolchange"],
+            "toolchangexy":     self.app.defaults["geometry_toolchangexy"],
+            "toolchangez":      float(self.app.defaults["geometry_toolchangez"]),
+            "startz":           self.app.defaults["geometry_startz"],
+            "endz":             float(self.app.defaults["geometry_endz"]),
+
+            # NCC
+            "tools_nccoperation":       self.app.defaults["tools_nccoperation"],
+            "tools_nccmilling_type":    self.app.defaults["tools_nccmilling_type"],
+            "tools_nccoverlap":         float(self.app.defaults["tools_nccoverlap"]),
+            "tools_nccmargin":          float(self.app.defaults["tools_nccmargin"]),
+            "tools_nccmethod":          self.app.defaults["tools_nccmethod"],
+            "tools_nccconnect":         self.app.defaults["tools_nccconnect"],
+            "tools_ncccontour":         self.app.defaults["tools_ncccontour"],
+            "tools_ncc_offset_choice":  self.app.defaults["tools_ncc_offset_choice"],
+            "tools_ncc_offset_value":   float(self.app.defaults["tools_ncc_offset_value"]),
+
+            # Paint
+            "tools_paintoverlap":       float(self.app.defaults["tools_paintoverlap"]),
+            "tools_paintmargin":        float(self.app.defaults["tools_paintmargin"]),
+            "tools_paintmethod":        self.app.defaults["tools_paintmethod"],
+            "tools_pathconnect":        self.app.defaults["tools_pathconnect"],
+            "tools_paintcontour":       self.app.defaults["tools_paintcontour"],
+        })
+
     def on_freeform_cutout(self):
         log.debug("Cutout.on_freeform_cutout() was launched ...")
 
@@ -622,8 +667,8 @@ class CutOut(FlatCAMTool):
 
                     solid_geo += cutout_handler(geom=geom_struct)
 
-            geo_obj.solid_geometry = deepcopy(solid_geo)
             xmin, ymin, xmax, ymax = recursive_bounds(geo_obj.solid_geometry)
+            geo_obj.solid_geometry = deepcopy(solid_geo)
             geo_obj.options['xmin'] = xmin
             geo_obj.options['ymin'] = ymin
             geo_obj.options['xmax'] = xmax
@@ -633,6 +678,23 @@ class CutOut(FlatCAMTool):
             geo_obj.options['multidepth'] = self.mpass_cb.get_value()
             geo_obj.options['depthperpass'] = self.maxdepth_entry.get_value()
 
+            geo_obj.tools.update({
+                1: {
+                    'tooldia': str(dia),
+                    'offset': 'Path',
+                    'offset_value': 0.0,
+                    'type': _('Rough'),
+                    'tool_type': 'C1',
+                    'data': self.default_data,
+                    'solid_geometry': geo_obj.solid_geometry
+                }
+            })
+            geo_obj.multigeo = True
+            geo_obj.tools[1]['data']['name'] = outname
+            geo_obj.tools[1]['data']['cutz'] = self.cutz_entry.get_value()
+            geo_obj.tools[1]['data']['multidepth'] = self.mpass_cb.get_value()
+            geo_obj.tools[1]['data']['depthperpass'] = self.maxdepth_entry.get_value()
+
         outname = cutout_obj.options["name"] + "_cutout"
         self.app.new_object('geometry', outname, geo_init)
 
@@ -759,6 +821,7 @@ class CutOut(FlatCAMTool):
                 return proc_geometry
 
             if kind == 'single':
+                # fuse the lines
                 object_geo = unary_union(object_geo)
 
                 xmin, ymin, xmax, ymax = object_geo.bounds
@@ -805,11 +868,28 @@ class CutOut(FlatCAMTool):
                                          _("Rectangular cutout with negative margin is not possible."))
                     return "fail"
 
-            geo_obj.solid_geometry = deepcopy(solid_geo)
             geo_obj.options['cnctooldia'] = str(dia)
             geo_obj.options['cutz'] = self.cutz_entry.get_value()
             geo_obj.options['multidepth'] = self.mpass_cb.get_value()
             geo_obj.options['depthperpass'] = self.maxdepth_entry.get_value()
+            geo_obj.solid_geometry = deepcopy(solid_geo)
+
+            geo_obj.tools.update({
+                1: {
+                    'tooldia': str(dia),
+                    'offset': 'Path',
+                    'offset_value': 0.0,
+                    'type': _('Rough'),
+                    'tool_type': 'C1',
+                    'data': self.default_data,
+                    'solid_geometry': geo_obj.solid_geometry
+                }
+            })
+            geo_obj.multigeo = True
+            geo_obj.tools[1]['data']['name'] = outname
+            geo_obj.tools[1]['data']['cutz'] = self.cutz_entry.get_value()
+            geo_obj.tools[1]['data']['multidepth'] = self.mpass_cb.get_value()
+            geo_obj.tools[1]['data']['depthperpass'] = self.maxdepth_entry.get_value()
 
         outname = cutout_obj.options["name"] + "_cutout"
         ret = self.app.new_object('geometry', outname, geo_init)
@@ -954,6 +1034,23 @@ class CutOut(FlatCAMTool):
             geo_obj.options['multidepth'] = self.mpass_cb.get_value()
             geo_obj.options['depthperpass'] = self.maxdepth_entry.get_value()
 
+            geo_obj.tools.update({
+                1: {
+                    'tooldia': str(dia),
+                    'offset': 'Path',
+                    'offset_value': 0.0,
+                    'type': _('Rough'),
+                    'tool_type': 'C1',
+                    'data': self.default_data,
+                    'solid_geometry': geo_obj.solid_geometry
+                }
+            })
+            geo_obj.multigeo = True
+            geo_obj.tools[1]['data']['name'] = outname
+            geo_obj.tools[1]['data']['cutz'] = self.cutz_entry.get_value()
+            geo_obj.tools[1]['data']['multidepth'] = self.mpass_cb.get_value()
+            geo_obj.tools[1]['data']['depthperpass'] = self.maxdepth_entry.get_value()
+
         outname = cutout_obj.options["name"] + "_cutout"
         self.app.new_object('geometry', outname, geo_init)
 

+ 77 - 78
flatcamTools/ToolPanelize.py

@@ -14,6 +14,8 @@ from copy import deepcopy
 import numpy as np
 
 import shapely.affinity as affinity
+from shapely.ops import unary_union
+from shapely.geometry import LineString
 
 import gettext
 import FlatCAMTranslation as fcTranslate
@@ -402,19 +404,18 @@ class Panelize(FlatCAMTool):
     def on_panelize(self):
         name = self.object_combo.currentText()
 
-        # Get source object.
+        # Get source object to be panelized.
         try:
-            panel_obj = self.app.collection.get_by_name(str(name))
+            panel_source_obj = self.app.collection.get_by_name(str(name))
         except Exception as e:
             log.debug("Panelize.on_panelize() --> %s" % str(e))
-            self.app.inform.emit('[ERROR_NOTCL] %s: %s' %
-                                 (_("Could not retrieve object"), name))
+            self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), name))
             return "Could not retrieve object: %s" % name
 
-        if panel_obj is None:
+        if panel_source_obj is None:
             self.app.inform.emit('[ERROR_NOTCL] %s: %s' %
-                                 (_("Object not found"), panel_obj))
-            return "Object not found: %s" % panel_obj
+                                 (_("Object not found"), panel_source_obj))
+            return "Object not found: %s" % panel_source_obj
 
         boxname = self.box_combo.currentText()
 
@@ -422,17 +423,15 @@ class Panelize(FlatCAMTool):
             box = self.app.collection.get_by_name(boxname)
         except Exception as e:
             log.debug("Panelize.on_panelize() --> %s" % str(e))
-            self.app.inform.emit('[ERROR_NOTCL] %s: %s' %
-                                 (_("Could not retrieve object"), boxname))
+            self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), boxname))
             return "Could not retrieve object: %s" % boxname
 
         if box is None:
-            self.app.inform.emit('[WARNING_NOTCL]%s: %s' %
-                                 (_("No object Box. Using instead"), panel_obj))
+            self.app.inform.emit('[WARNING_NOTCL]%s: %s' % (_("No object Box. Using instead"), panel_source_obj))
             self.reference_radio.set_value('bbox')
 
         if self.reference_radio.get_value() == 'bbox':
-            box = panel_obj
+            box = panel_source_obj
 
         self.outname = name + '_panelized'
 
@@ -478,20 +477,20 @@ class Panelize(FlatCAMTool):
                     rows -= 1
                     panel_lengthy = ((ymax - ymin) * rows) + (spacing_rows * (rows - 1))
 
-        if panel_obj.kind == 'excellon' or panel_obj.kind == 'geometry':
+        if panel_source_obj.kind == 'excellon' or panel_source_obj.kind == 'geometry':
             # make a copy of the panelized Excellon or Geometry tools
             copied_tools = {}
-            for tt, tt_val in list(panel_obj.tools.items()):
+            for tt, tt_val in list(panel_source_obj.tools.items()):
                 copied_tools[tt] = deepcopy(tt_val)
 
-        if panel_obj.kind == 'gerber':
+        if panel_source_obj.kind == 'gerber':
             # make a copy of the panelized Gerber apertures
             copied_apertures = {}
-            for tt, tt_val in list(panel_obj.apertures.items()):
+            for tt, tt_val in list(panel_source_obj.apertures.items()):
                 copied_apertures[tt] = deepcopy(tt_val)
 
-        def panelize_2():
-            if panel_obj is not None:
+        def panelize_worker():
+            if panel_source_obj is not None:
                 self.app.inform.emit(_("Generating panel ... "))
 
                 def job_init_excellon(obj_fin, app_obj):
@@ -501,15 +500,15 @@ class Panelize(FlatCAMTool):
                     obj_fin.slots = []
                     obj_fin.solid_geometry = []
 
-                    for option in panel_obj.options:
+                    for option in panel_source_obj.options:
                         if option != 'name':
                             try:
-                                obj_fin.options[option] = panel_obj.options[option]
+                                obj_fin.options[option] = panel_source_obj.options[option]
                             except KeyError:
                                 log.warning("Failed to copy option. %s" % str(option))
 
-                    geo_len_drills = len(panel_obj.drills) if panel_obj.drills else 0
-                    geo_len_slots = len(panel_obj.slots) if panel_obj.slots else 0
+                    geo_len_drills = len(panel_source_obj.drills) if panel_source_obj.drills else 0
+                    geo_len_slots = len(panel_source_obj.slots) if panel_source_obj.slots else 0
 
                     element = 0
                     for row in range(rows):
@@ -518,9 +517,9 @@ class Panelize(FlatCAMTool):
                             element += 1
                             old_disp_number = 0
 
-                            if panel_obj.drills:
+                            if panel_source_obj.drills:
                                 drill_nr = 0
-                                for tool_dict in panel_obj.drills:
+                                for tool_dict in panel_source_obj.drills:
                                     if self.app.abort_flag:
                                         # graceful abort requested by the user
                                         raise grace
@@ -543,9 +542,9 @@ class Panelize(FlatCAMTool):
                                                                                   disp_number))
                                         old_disp_number = disp_number
 
-                            if panel_obj.slots:
+                            if panel_source_obj.slots:
                                 slot_nr = 0
-                                for tool_dict in panel_obj.slots:
+                                for tool_dict in panel_source_obj.slots:
                                     if self.app.abort_flag:
                                         # graceful abort requested by the user
                                         raise grace
@@ -574,8 +573,8 @@ class Panelize(FlatCAMTool):
                         currenty += lenghty
 
                     obj_fin.create_geometry()
-                    obj_fin.zeros = panel_obj.zeros
-                    obj_fin.units = panel_obj.units
+                    obj_fin.zeros = panel_source_obj.zeros
+                    obj_fin.units = panel_source_obj.units
                     self.app.proc_container.update_view_text('')
 
                 def job_init_geometry(obj_fin, app_obj):
@@ -598,36 +597,36 @@ class Panelize(FlatCAMTool):
                     obj_fin.solid_geometry = []
 
                     # create the initial structure on which to create the panel
-                    if panel_obj.kind == 'geometry':
-                        obj_fin.multigeo = panel_obj.multigeo
+                    if panel_source_obj.kind == 'geometry':
+                        obj_fin.multigeo = panel_source_obj.multigeo
                         obj_fin.tools = copied_tools
-                        if panel_obj.multigeo is True:
-                            for tool in panel_obj.tools:
+                        if panel_source_obj.multigeo is True:
+                            for tool in panel_source_obj.tools:
                                 obj_fin.tools[tool]['solid_geometry'][:] = []
-                    elif panel_obj.kind == 'gerber':
+                    elif panel_source_obj.kind == 'gerber':
                         obj_fin.apertures = copied_apertures
                         for ap in obj_fin.apertures:
                             obj_fin.apertures[ap]['geometry'] = []
 
                     # find the number of polygons in the source solid_geometry
                     geo_len = 0
-                    if panel_obj.kind == 'geometry':
-                        if panel_obj.multigeo is True:
-                            for tool in panel_obj.tools:
+                    if panel_source_obj.kind == 'geometry':
+                        if panel_source_obj.multigeo is True:
+                            for tool in panel_source_obj.tools:
                                 try:
-                                    geo_len += len(panel_obj.tools[tool]['solid_geometry'])
+                                    geo_len += len(panel_source_obj.tools[tool]['solid_geometry'])
                                 except TypeError:
                                     geo_len += 1
                         else:
                             try:
-                                geo_len = len(panel_obj.solid_geometry)
+                                geo_len = len(panel_source_obj.solid_geometry)
                             except TypeError:
                                 geo_len = 1
-                    elif panel_obj.kind == 'gerber':
-                        for ap in panel_obj.apertures:
-                            if 'geometry' in panel_obj.apertures[ap]:
+                    elif panel_source_obj.kind == 'gerber':
+                        for ap in panel_source_obj.apertures:
+                            if 'geometry' in panel_source_obj.apertures[ap]:
                                 try:
-                                    geo_len += len(panel_obj.apertures[ap]['geometry'])
+                                    geo_len += len(panel_source_obj.apertures[ap]['geometry'])
                                 except TypeError:
                                     geo_len += 1
 
@@ -639,29 +638,23 @@ class Panelize(FlatCAMTool):
                             element += 1
                             old_disp_number = 0
 
-                            if panel_obj.kind == 'geometry':
-                                if panel_obj.multigeo is True:
-                                    for tool in panel_obj.tools:
+                            # Will panelize a Geometry Object
+                            if panel_source_obj.kind == 'geometry':
+                                if panel_source_obj.multigeo is True:
+                                    for tool in panel_source_obj.tools:
                                         if self.app.abort_flag:
                                             # graceful abort requested by the user
                                             raise grace
 
-                                        # geo = translate_recursion(panel_obj.tools[tool]['solid_geometry'])
-                                        # if isinstance(geo, list):
-                                        #     obj_fin.tools[tool]['solid_geometry'] += geo
-                                        # else:
-                                        #     obj_fin.tools[tool]['solid_geometry'].append(geo)
-
                                         # calculate the number of polygons
-                                        geo_len = len(panel_obj.tools[tool]['solid_geometry'])
+                                        geo_len = len(panel_source_obj.tools[tool]['solid_geometry'])
                                         pol_nr = 0
-                                        for geo_el in panel_obj.tools[tool]['solid_geometry']:
+                                        for geo_el in panel_source_obj.tools[tool]['solid_geometry']:
                                             trans_geo = translate_recursion(geo_el)
                                             obj_fin.tools[tool]['solid_geometry'].append(trans_geo)
 
                                             pol_nr += 1
                                             disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100]))
-
                                             if old_disp_number < disp_number <= 100:
                                                 self.app.proc_container.update_view_text(' %s: %d %d%%' %
                                                                                          (_("Copy"),
@@ -669,23 +662,18 @@ class Panelize(FlatCAMTool):
                                                                                           disp_number))
                                                 old_disp_number = disp_number
                                 else:
-                                    # geo = translate_recursion(panel_obj.solid_geometry)
-                                    # if isinstance(geo, list):
-                                    #     obj_fin.solid_geometry += geo
-                                    # else:
-                                    #     obj_fin.solid_geometry.append(geo)
                                     if self.app.abort_flag:
                                         # graceful abort requested by the user
                                         raise grace
 
                                     try:
                                         # calculate the number of polygons
-                                        geo_len = len(panel_obj.solid_geometry)
+                                        geo_len = len(panel_source_obj.solid_geometry)
                                     except TypeError:
                                         geo_len = 1
                                     pol_nr = 0
                                     try:
-                                        for geo_el in panel_obj.solid_geometry:
+                                        for geo_el in panel_source_obj.solid_geometry:
                                             if self.app.abort_flag:
                                                 # graceful abort requested by the user
                                                 raise grace
@@ -702,21 +690,18 @@ class Panelize(FlatCAMTool):
                                                                                           int(element),
                                                                                           disp_number))
                                                 old_disp_number = disp_number
+
                                     except TypeError:
-                                        trans_geo = translate_recursion(panel_obj.solid_geometry)
+                                        trans_geo = translate_recursion(panel_source_obj.solid_geometry)
                                         obj_fin.solid_geometry.append(trans_geo)
+                            # Will panelize a Gerber Object
                             else:
-                                # geo = translate_recursion(panel_obj.solid_geometry)
-                                # if isinstance(geo, list):
-                                #     obj_fin.solid_geometry += geo
-                                # else:
-                                #     obj_fin.solid_geometry.append(geo)
                                 if self.app.abort_flag:
                                     # graceful abort requested by the user
                                     raise grace
 
                                 try:
-                                    for geo_el in panel_obj.solid_geometry:
+                                    for geo_el in panel_source_obj.solid_geometry:
                                         if self.app.abort_flag:
                                             # graceful abort requested by the user
                                             raise grace
@@ -724,21 +709,21 @@ class Panelize(FlatCAMTool):
                                         trans_geo = translate_recursion(geo_el)
                                         obj_fin.solid_geometry.append(trans_geo)
                                 except TypeError:
-                                    trans_geo = translate_recursion(panel_obj.solid_geometry)
+                                    trans_geo = translate_recursion(panel_source_obj.solid_geometry)
                                     obj_fin.solid_geometry.append(trans_geo)
 
-                                for apid in panel_obj.apertures:
+                                for apid in panel_source_obj.apertures:
                                     if self.app.abort_flag:
                                         # graceful abort requested by the user
                                         raise grace
-                                    if 'geometry' in panel_obj.apertures[apid]:
+                                    if 'geometry' in panel_source_obj.apertures[apid]:
                                         try:
                                             # calculate the number of polygons
-                                            geo_len = len(panel_obj.apertures[apid]['geometry'])
+                                            geo_len = len(panel_source_obj.apertures[apid]['geometry'])
                                         except TypeError:
                                             geo_len = 1
                                         pol_nr = 0
-                                        for el in panel_obj.apertures[apid]['geometry']:
+                                        for el in panel_source_obj.apertures[apid]['geometry']:
                                             if self.app.abort_flag:
                                                 # graceful abort requested by the user
                                                 raise grace
@@ -771,20 +756,34 @@ class Panelize(FlatCAMTool):
                             currentx += lenghtx
                         currenty += lenghty
 
+                    print("before", obj_fin.tools)
+                    if panel_source_obj.kind == 'geometry' and panel_source_obj.multigeo is True:
+                        # I'm going to do this only here as a fix for panelizing cutouts
+                        # I'm going to separate linestrings out of the solid geometry from other
+                        # possible type of elements and apply unary_union on them to fuse them
+                        for tool in obj_fin.tools:
+                            lines = []
+                            other_geo = []
+                            for geo in obj_fin.tools[tool]['solid_geometry']:
+                                if isinstance(geo, LineString):
+                                    lines.append(geo)
+                                else:
+                                    other_geo.append(geo)
+                            fused_lines = list(unary_union(lines))
+                            obj_fin.tools[tool]['solid_geometry'] = fused_lines + other_geo
+                    print("after", obj_fin.tools)
+
                     if panel_type == 'gerber':
                         self.app.inform.emit('%s' % _("Generating panel ... Adding the Gerber code."))
                         obj_fin.source_file = self.app.export_gerber(obj_name=self.outname, filename=None,
                                                                      local_use=obj_fin, use_thread=False)
 
-                    # app_obj.log.debug("Found %s geometries. Creating a panel geometry cascaded union ..." %
-                    #                   len(obj_fin.solid_geometry))
-
                     # obj_fin.solid_geometry = cascaded_union(obj_fin.solid_geometry)
                     # app_obj.log.debug("Finished creating a cascaded union for the panel.")
                     self.app.proc_container.update_view_text('')
 
                 self.app.inform.emit('%s: %d' % (_("Generating panel... Spawning copies"), (int(rows * columns))))
-                if panel_obj.kind == 'excellon':
+                if panel_source_obj.kind == 'excellon':
                     self.app.new_object("excellon", self.outname, job_init_excellon, plot=True, autoselected=True)
                 else:
                     self.app.new_object(panel_type, self.outname, job_init_geometry, plot=True, autoselected=True)
@@ -801,7 +800,7 @@ class Panelize(FlatCAMTool):
 
         def job_thread(app_obj):
             try:
-                panelize_2()
+                panelize_worker()
                 self.app.inform.emit('[success] %s' % _("Panel created successfully."))
             except Exception as ee:
                 proc.done()

+ 1 - 1
flatcamTools/ToolSolderPaste.py

@@ -155,7 +155,7 @@ class SolderPaste(FlatCAMTool):
         step1_lbl = QtWidgets.QLabel("<b>%s:</b>" % _('STEP 1'))
         step1_lbl.setToolTip(
             _("First step is to select a number of nozzle tools for usage\n"
-              "and then optionally modify the GCode parameters bellow.")
+              "and then optionally modify the GCode parameters below.")
         )
         step1_description_lbl = QtWidgets.QLabel(_("Select tools.\n"
                                                    "Modify parameters."))

+ 1 - 1
flatcamTools/ToolSub.py

@@ -97,7 +97,7 @@ class ToolSub(FlatCAMTool):
 
         form_layout.addRow(self.sub_gerber_label, self.sub_gerber_combo)
 
-        self.intersect_btn = FCButton(_('Substract Gerber'))
+        self.intersect_btn = FCButton(_('Subtract Gerber'))
         self.intersect_btn.setToolTip(
             _("Will remove the area occupied by the subtractor\n"
               "Gerber from the Target Gerber.\n"

+ 3 - 3
locale/de/LC_MESSAGES/strings.po

@@ -17325,7 +17325,7 @@ msgstr "SCHRITT 1"
 #: flatcamTools/ToolSolderPaste.py:157
 msgid ""
 "First step is to select a number of nozzle tools for usage\n"
-"and then optionally modify the GCode parameters bellow."
+"and then optionally modify the GCode parameters below."
 msgstr ""
 "Zunächst müssen Sie eine Reihe von Düsenwerkzeugen auswählen\n"
 "und ändern Sie dann optional die GCode-Parameter."
@@ -17565,7 +17565,7 @@ msgstr ""
 "vom Zielobjekt Gerber."
 
 #: flatcamTools/ToolSub.py:100
-msgid "Substract Gerber"
+msgid "Subtract Gerber"
 msgstr "Gerber abziehen"
 
 #: flatcamTools/ToolSub.py:102
@@ -20547,7 +20547,7 @@ msgstr ""
 #~ msgid "[success] Paint Area Done."
 #~ msgstr "[success] Lackierbereich fertig."
 
-#~ msgid "...proccessing... [%s]"
+#~ msgid "...processing... [%s]"
 #~ msgstr "...wird bearbeitet...[%s]"
 
 #~ msgid "Parsing aperture %s geometry ..."

+ 6 - 6
locale/en/LC_MESSAGES/strings.po

@@ -16981,10 +16981,10 @@ msgstr "STEP 1"
 #: flatcamTools/ToolSolderPaste.py:157
 msgid ""
 "First step is to select a number of nozzle tools for usage\n"
-"and then optionally modify the GCode parameters bellow."
+"and then optionally modify the GCode parameters below."
 msgstr ""
 "First step is to select a number of nozzle tools for usage\n"
-"and then optionally modify the GCode parameters bellow."
+"and then optionally modify the GCode parameters below."
 
 #: flatcamTools/ToolSolderPaste.py:160
 msgid ""
@@ -17216,8 +17216,8 @@ msgstr ""
 "from the target Gerber object."
 
 #: flatcamTools/ToolSub.py:100
-msgid "Substract Gerber"
-msgstr "Substract Gerber"
+msgid "Subtract Gerber"
+msgstr "Subtract Gerber"
 
 #: flatcamTools/ToolSub.py:102
 msgid ""
@@ -20512,8 +20512,8 @@ msgstr "No Geometry name in args. Provide a name and try again."
 #~ msgid "Generating panel ... Please wait."
 #~ msgstr "Generating panel ... Please wait."
 
-#~ msgid "...proccessing... [%s]"
-#~ msgstr "...proccessing... [%s]"
+#~ msgid "...processing... [%s]"
+#~ msgstr "...processing... [%s]"
 
 #~ msgid "Parsing aperture %s geometry ..."
 #~ msgstr "Parsing aperture %s geometry ..."

+ 3 - 3
locale/es/LC_MESSAGES/strings.po

@@ -17257,7 +17257,7 @@ msgstr "PASO 1"
 #: flatcamTools/ToolSolderPaste.py:157
 msgid ""
 "First step is to select a number of nozzle tools for usage\n"
-"and then optionally modify the GCode parameters bellow."
+"and then optionally modify the GCode parameters below."
 msgstr ""
 "El primer paso es seleccionar una serie de herramientas de boquillas para su "
 "uso\n"
@@ -17502,7 +17502,7 @@ msgstr ""
 "del objeto objetivo de Gerber."
 
 #: flatcamTools/ToolSub.py:100
-msgid "Substract Gerber"
+msgid "Subtract Gerber"
 msgstr "Restar Gerber"
 
 #: flatcamTools/ToolSub.py:102
@@ -19224,7 +19224,7 @@ msgstr ""
 #~ msgid "[success] Paint Area Done."
 #~ msgstr "[éxito] Área de pintura realizada."
 
-#~ msgid "...proccessing... [%s]"
+#~ msgid "...processing... [%s]"
 #~ msgstr "... procesando ... [% s]"
 
 #~ msgid "Parsing aperture %s geometry ..."

+ 2 - 2
locale/fr/LC_MESSAGES/strings.po

@@ -17254,7 +17254,7 @@ msgstr "ÉTAPE 1"
 #: flatcamTools/ToolSolderPaste.py:157
 msgid ""
 "First step is to select a number of nozzle tools for usage\n"
-"and then optionally modify the GCode parameters bellow."
+"and then optionally modify the GCode parameters below."
 msgstr ""
 "La première étape consiste à sélectionner un certain nombre d’outils de buse "
 "à utiliser.\n"
@@ -17498,7 +17498,7 @@ msgstr ""
 "à partir de l'objet Gerber cible."
 
 #: flatcamTools/ToolSub.py:100
-msgid "Substract Gerber"
+msgid "Subtract Gerber"
 msgstr "Soustraire Gerber"
 
 #: flatcamTools/ToolSub.py:102

+ 8 - 8
locale/hu/LC_MESSAGES/strings.po

@@ -7235,11 +7235,11 @@ msgstr "Except"
 #: flatcamGUI/ObjectUI.py:436
 msgid ""
 "When the isolation geometry is generated,\n"
-"by checking this, the area of the object bellow\n"
+"by checking this, the area of the object below\n"
 "will be subtracted from the isolation geometry."
 msgstr ""
 "When the isolation geometry is generated,\n"
-"by checking this, the area of the object bellow\n"
+"by checking this, the area of the object below\n"
 "will be subtracted from the isolation geometry."
 
 #: flatcamGUI/ObjectUI.py:449 flatcamGUI/PreferencesUI.py:6527
@@ -16986,10 +16986,10 @@ msgstr "STEP 1"
 #: flatcamTools/ToolSolderPaste.py:157
 msgid ""
 "First step is to select a number of nozzle tools for usage\n"
-"and then optionally modify the GCode parameters bellow."
+"and then optionally modify the GCode parameters below."
 msgstr ""
 "First step is to select a number of nozzle tools for usage\n"
-"and then optionally modify the GCode parameters bellow."
+"and then optionally modify the GCode parameters below."
 
 #: flatcamTools/ToolSolderPaste.py:160
 msgid ""
@@ -17221,8 +17221,8 @@ msgstr ""
 "from the target Gerber object."
 
 #: flatcamTools/ToolSub.py:100
-msgid "Substract Gerber"
-msgstr "Substract Gerber"
+msgid "Subtract Gerber"
+msgstr "Subtract Gerber"
 
 #: flatcamTools/ToolSub.py:102
 msgid ""
@@ -20477,8 +20477,8 @@ msgstr "No Geometry name in args. Provide a name and try again."
 #~ msgid "Generating panel ... Please wait."
 #~ msgstr "Generating panel ... Please wait."
 
-#~ msgid "...proccessing... [%s]"
-#~ msgstr "...proccessing... [%s]"
+#~ msgid "...processing... [%s]"
+#~ msgstr "...processing... [%s]"
 
 #~ msgid "Parsing aperture %s geometry ..."
 #~ msgstr "Parsing aperture %s geometry ..."

+ 4 - 4
locale/it/LC_MESSAGES/strings.po

@@ -6804,7 +6804,7 @@ msgstr ""
 #: flatcamGUI/ObjectUI.py:431
 msgid ""
 "When the isolation geometry is generated,\n"
-"by checking this, the area of the object bellow\n"
+"by checking this, the area of the object below\n"
 "will be subtracted from the isolation geometry."
 msgstr ""
 
@@ -14198,7 +14198,7 @@ msgid "Violations: There are no violations for the current rule."
 msgstr ""
 
 #: flatcamTools/ToolShell.py:70 flatcamTools/ToolShell.py:72
-msgid "...proccessing..."
+msgid "...processing..."
 msgstr ""
 
 #: flatcamTools/ToolSolderPaste.py:37
@@ -14251,7 +14251,7 @@ msgstr ""
 #: flatcamTools/ToolSolderPaste.py:156
 msgid ""
 "First step is to select a number of nozzle tools for usage\n"
-"and then optionally modify the GCode parameters bellow."
+"and then optionally modify the GCode parameters below."
 msgstr ""
 
 #: flatcamTools/ToolSolderPaste.py:159
@@ -14452,7 +14452,7 @@ msgid ""
 msgstr ""
 
 #: flatcamTools/ToolSub.py:97
-msgid "Substract Gerber"
+msgid "Subtract Gerber"
 msgstr ""
 
 #: flatcamTools/ToolSub.py:99

+ 3 - 3
locale/pt_BR/LC_MESSAGES/strings.po

@@ -17045,7 +17045,7 @@ msgstr "PASSO 1"
 #: flatcamTools/ToolSolderPaste.py:157
 msgid ""
 "First step is to select a number of nozzle tools for usage\n"
-"and then optionally modify the GCode parameters bellow."
+"and then optionally modify the GCode parameters below."
 msgstr ""
 "O primeiro passo é selecionar um número de ferramentas de bico para usar,\n"
 "e opcionalmente, modificar os parâmetros do G-Code abaixo."
@@ -17286,7 +17286,7 @@ msgstr ""
 "do objeto Gerber de destino."
 
 #: flatcamTools/ToolSub.py:100
-msgid "Substract Gerber"
+msgid "Subtract Gerber"
 msgstr "Subtrair Gerber"
 
 #: flatcamTools/ToolSub.py:102
@@ -20225,7 +20225,7 @@ msgstr "Nenhum nome de geometria nos argumentos. Altere e tente novamente."
 #~ msgid "Generating panel ... Please wait."
 #~ msgstr "Gerando painel ... Por favor, aguarde."
 
-#~ msgid "...proccessing... [%s]"
+#~ msgid "...processing... [%s]"
 #~ msgstr "...processando... [%s]"
 
 #~ msgid "Parsing aperture %s geometry ..."

+ 3 - 3
locale/ro/LC_MESSAGES/strings.po

@@ -17265,7 +17265,7 @@ msgstr "PAS 1"
 #: flatcamTools/ToolSolderPaste.py:157
 msgid ""
 "First step is to select a number of nozzle tools for usage\n"
-"and then optionally modify the GCode parameters bellow."
+"and then optionally modify the GCode parameters below."
 msgstr ""
 "Primul pas este să se efectueza o selecţie de unelte Nozzl pt \n"
 "utilizare și apoi in mod optional, să se modifice parametrii\n"
@@ -17512,7 +17512,7 @@ msgstr ""
 "obiectul Gerber tintă."
 
 #: flatcamTools/ToolSub.py:100
-msgid "Substract Gerber"
+msgid "Subtract Gerber"
 msgstr "Execută"
 
 #: flatcamTools/ToolSub.py:102
@@ -20553,7 +20553,7 @@ msgstr ""
 #~ msgid "Generating panel ... Please wait."
 #~ msgstr "Se generează panelul ... Va rugăm asteptati."
 
-#~ msgid "...proccessing... [%s]"
+#~ msgid "...processing... [%s]"
 #~ msgstr "...in procesare... [%s]"
 
 #~ msgid "Parsing aperture %s geometry ..."

+ 3 - 3
locale/ru/LC_MESSAGES/strings.po

@@ -17107,7 +17107,7 @@ msgstr "ШАГ 1"
 #: flatcamTools/ToolSolderPaste.py:157
 msgid ""
 "First step is to select a number of nozzle tools for usage\n"
-"and then optionally modify the GCode parameters bellow."
+"and then optionally modify the GCode parameters below."
 msgstr ""
 "Первый шаг - выбрать несколько инструментов для использования насадок.\n"
 "а затем при необходимости измените параметры кода G ниже."
@@ -17349,7 +17349,7 @@ msgstr ""
 "из целевого Gerber объекта."
 
 #: flatcamTools/ToolSub.py:100
-msgid "Substract Gerber"
+msgid "Subtract Gerber"
 msgstr "Вычесть Gerber"
 
 #: flatcamTools/ToolSub.py:102
@@ -20369,7 +20369,7 @@ msgstr "Нет имени геометрии в аргументах. Укажи
 #~ msgid "Generating panel ... Please wait."
 #~ msgstr "Выполняется панелизация ... Пожалуйста, подождите."
 
-#~ msgid "...proccessing... [%s]"
+#~ msgid "...processing... [%s]"
 #~ msgstr "...обработка... [%s]"
 
 #~ msgid "Parsing aperture %s geometry ..."

+ 3 - 3
locale_template/strings.pot

@@ -6789,7 +6789,7 @@ msgstr ""
 #: flatcamGUI/ObjectUI.py:436
 msgid ""
 "When the isolation geometry is generated,\n"
-"by checking this, the area of the object bellow\n"
+"by checking this, the area of the object below\n"
 "will be subtracted from the isolation geometry."
 msgstr ""
 
@@ -14604,7 +14604,7 @@ msgstr ""
 #: flatcamTools/ToolSolderPaste.py:158
 msgid ""
 "First step is to select a number of nozzle tools for usage\n"
-"and then optionally modify the GCode parameters bellow."
+"and then optionally modify the GCode parameters below."
 msgstr ""
 
 #: flatcamTools/ToolSolderPaste.py:161
@@ -14800,7 +14800,7 @@ msgid ""
 msgstr ""
 
 #: flatcamTools/ToolSub.py:100
-msgid "Substract Gerber"
+msgid "Subtract Gerber"
 msgstr ""
 
 #: flatcamTools/ToolSub.py:102

+ 1 - 1
tclCommands/TclCommandJoinExcellon.py

@@ -34,7 +34,7 @@ class TclCommandJoinExcellon(TclCommand):
     help = {
         'main': "Runs a merge operation (join) on the Excellon objects.\n"
                 "The names of the Excellon objects to be merged will be entered after the outname,\n"
-                "separated by spaces. See the example bellow.\n"
+                "separated by spaces. See the example below.\n"
                 "WARNING: if the name of an Excellon objects has spaces, enclose the name with quotes.",
         'args': collections.OrderedDict([
             ('outname', 'Name of the new Excellon Object made by joining of other Excellon objects. Required'),

+ 1 - 1
tclCommands/TclCommandJoinGeometry.py

@@ -34,7 +34,7 @@ class TclCommandJoinGeometry(TclCommand):
     help = {
         'main': "Runs a merge operation (join) on the Geometry objects.\n"
                 "The names of the Geometry objects to be merged will be entered after the outname,\n"
-                "separated by spaces. See the example bellow.\n"
+                "separated by spaces. See the example below.\n"
                 "WARNING: if the name of an Geometry objects has spaces, enclose the name with quotes.",
         'args': collections.OrderedDict([
             ('outname', 'Name of the new Geometry Object made by joining of other Geometry objects. Required'),