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

- fixed an error in the Gerber parser; it did not took into consideration the aperture size declared before the beginning of a Gerber region. Detected for Gerber files generated by KiCAD 5.x
- in Panelize Tool made sure that for Gerber objects if one of the apertures is without geometry then it is ignored

Marius Stanciu 6 лет назад
Родитель
Сommit
217316c732
3 измененных файлов с 123 добавлено и 104 удалено
  1. 2 0
      README.md
  2. 80 58
      flatcamParsers/ParseGerber.py
  3. 41 46
      flatcamTools/ToolPanelize.py

+ 2 - 0
README.md

@@ -18,6 +18,8 @@ CAD program, and create G-Code for Isolation routing.
 - remade the GUI in Preferences -> General grouping the settings in a more clear way
 - remade the GUI in Preferences -> General grouping the settings in a more clear way
 - made available the Jump To function in Excellon Editor
 - made available the Jump To function in Excellon Editor
 - added a clean_up() method in all the Editor Tools that need it, to be run when aborting using the ESC key
 - added a clean_up() method in all the Editor Tools that need it, to be run when aborting using the ESC key
+- fixed an error in the Gerber parser; it did not took into consideration the aperture size declared before the beginning of a Gerber region. Detected for Gerber files generated by KiCAD 5.x
+- in Panelize Tool made sure that for Gerber objects if one of the apertures is without geometry then it is ignored
 
 
 25.12.2019
 25.12.2019
 
 

+ 80 - 58
flatcamParsers/ParseGerber.py

@@ -437,10 +437,10 @@ class Gerber(Geometry):
                 gline = gline.strip(' \r\n')
                 gline = gline.strip(' \r\n')
                 # log.debug("Line=%3s %s" % (line_num, gline))
                 # log.debug("Line=%3s %s" % (line_num, gline))
 
 
-                # ###################
-                # Ignored lines #####
-                # Comments      #####
-                # ###################
+                # ###############################################################
+                # ################   Ignored lines   ############################
+                # ################     Comments      ############################
+                # ###############################################################
                 match = self.comm_re.search(gline)
                 match = self.comm_re.search(gline)
                 if match:
                 if match:
                     continue
                     continue
@@ -504,11 +504,11 @@ class Gerber(Geometry):
                     current_polarity = new_polarity
                     current_polarity = new_polarity
                     continue
                     continue
 
 
-                # ############################################################# ##
-                # Number format ############################################### ##
-                # Example: %FSLAX24Y24*%
-                # ############################################################# ##
-                # TODO: This is ignoring most of the format. Implement the rest.
+                # ################################################################
+                # #####################  Number format ###########################
+                # #####################  Example: %FSLAX24Y24*%  #################
+                # ################################################################
+
                 match = self.fmt_re.search(gline)
                 match = self.fmt_re.search(gline)
                 if match:
                 if match:
                     absolute = {'A': 'Absolute', 'I': 'Relative'}[match.group(2)]
                     absolute = {'A': 'Absolute', 'I': 'Relative'}[match.group(2)]
@@ -524,8 +524,10 @@ class Gerber(Geometry):
                     log.debug("Gerber format found. Coordinates type = %s (Absolute or Relative)" % absolute)
                     log.debug("Gerber format found. Coordinates type = %s (Absolute or Relative)" % absolute)
                     continue
                     continue
 
 
-                # ## Mode (IN/MM)
-                # Example: %MOIN*%
+                # ################################################################
+                # ######################## Mode (IN/MM)    #######################
+                # #####################    Example: %MOIN*%  #####################
+                # ################################################################
                 match = self.mode_re.search(gline)
                 match = self.mode_re.search(gline)
                 if match:
                 if match:
                     self.units = match.group(1)
                     self.units = match.group(1)
@@ -535,9 +537,9 @@ class Gerber(Geometry):
                     self.conversion_done = True
                     self.conversion_done = True
                     continue
                     continue
 
 
-                # ############################################################# ##
-                # Combined Number format and Mode --- Allegro does this ####### ##
-                # ############################################################# ##
+                # ################################################################
+                # Combined Number format and Mode --- Allegro does this ##########
+                # ################################################################
                 match = self.fmt_re_alt.search(gline)
                 match = self.fmt_re_alt.search(gline)
                 if match:
                 if match:
                     absolute = {'A': 'Absolute', 'I': 'Relative'}[match.group(2)]
                     absolute = {'A': 'Absolute', 'I': 'Relative'}[match.group(2)]
@@ -558,9 +560,9 @@ class Gerber(Geometry):
                     self.conversion_done = True
                     self.conversion_done = True
                     continue
                     continue
 
 
-                # ############################################################# ##
-                # Search for OrCAD way for having Number format
-                # ############################################################# ##
+                # ################################################################
+                # ####     Search for OrCAD way for having Number format  ########
+                # ################################################################
                 match = self.fmt_re_orcad.search(gline)
                 match = self.fmt_re_orcad.search(gline)
                 if match:
                 if match:
                     if match.group(1) is not None:
                     if match.group(1) is not None:
@@ -587,9 +589,9 @@ class Gerber(Geometry):
                         self.conversion_done = True
                         self.conversion_done = True
                         continue
                         continue
 
 
-                # ############################################################# ##
-                # Units (G70/1) OBSOLETE
-                # ############################################################# ##
+                # ################################################################
+                # ############     Units (G70/1) OBSOLETE   ######################
+                # ################################################################
                 match = self.units_re.search(gline)
                 match = self.units_re.search(gline)
                 if match:
                 if match:
                     obs_gerber_units = {'0': 'IN', '1': 'MM'}[match.group(1)]
                     obs_gerber_units = {'0': 'IN', '1': 'MM'}[match.group(1)]
@@ -599,21 +601,21 @@ class Gerber(Geometry):
                     self.conversion_done = True
                     self.conversion_done = True
                     continue
                     continue
 
 
-                # ############################################################# ##
-                # Absolute/relative coordinates G90/1 OBSOLETE ######## ##
-                # ##################################################### ##
+                # ################################################################
+                # #####   Absolute/relative coordinates G90/1 OBSOLETE ###########
+                # ################################################################
                 match = self.absrel_re.search(gline)
                 match = self.absrel_re.search(gline)
                 if match:
                 if match:
                     absolute = {'0': "Absolute", '1': "Relative"}[match.group(1)]
                     absolute = {'0': "Absolute", '1': "Relative"}[match.group(1)]
                     log.warning("Gerber obsolete coordinates type found = %s (Absolute or Relative) " % absolute)
                     log.warning("Gerber obsolete coordinates type found = %s (Absolute or Relative) " % absolute)
                     continue
                     continue
 
 
-                # ############################################################# ##
-                # Aperture Macros ##################################### ##
+                # ################################################################
+                # Aperture Macros ################################################
                 # Having this at the beginning will slow things down
                 # Having this at the beginning will slow things down
                 # but macros can have complicated statements than could
                 # but macros can have complicated statements than could
                 # be caught by other patterns.
                 # be caught by other patterns.
-                # ############################################################# ##
+                # ################################################################
                 if current_macro is None:  # No macro started yet
                 if current_macro is None:  # No macro started yet
                     match = self.am1_re.search(gline)
                     match = self.am1_re.search(gline)
                     # Start macro if match, else not an AM, carry on.
                     # Start macro if match, else not an AM, carry on.
@@ -640,18 +642,20 @@ class Gerber(Geometry):
                         self.aperture_macros[current_macro].append(gline)
                         self.aperture_macros[current_macro].append(gline)
                     continue
                     continue
 
 
-                # ## Aperture definitions %ADD...
+                # ################################################################
+                # ##############   Aperture definitions %ADD...  #################
+                # ################################################################
                 match = self.ad_re.search(gline)
                 match = self.ad_re.search(gline)
                 if match:
                 if match:
                     # log.info("Found aperture definition. Line %d: %s" % (line_num, gline))
                     # log.info("Found aperture definition. Line %d: %s" % (line_num, gline))
                     self.aperture_parse(match.group(1), match.group(2), match.group(3))
                     self.aperture_parse(match.group(1), match.group(2), match.group(3))
                     continue
                     continue
 
 
-                # ############################################################# ##
-                # Operation code alone ###################### ##
-                # Operation code alone, usually just D03 (Flash)
+                # ################################################################
+                # ################  Operation code alone #########################
+                # ###########   Operation code alone, usually just D03 (Flash) ###
                 # self.opcode_re = re.compile(r'^D0?([123])\*$')
                 # self.opcode_re = re.compile(r'^D0?([123])\*$')
-                # ############################################################# ##
+                # ################################################################
                 match = self.opcode_re.search(gline)
                 match = self.opcode_re.search(gline)
                 if match:
                 if match:
                     current_operation_code = int(match.group(1))
                     current_operation_code = int(match.group(1))
@@ -690,10 +694,10 @@ class Gerber(Geometry):
 
 
                     continue
                     continue
 
 
-                # ############################################################# ##
-                # Tool/aperture change
-                # Example: D12*
-                # ############################################################# ##
+                # ################################################################
+                # ################  Tool/aperture change  ########################
+                # ################  Example: D12*         ########################
+                # ################################################################
                 match = self.tool_re.search(gline)
                 match = self.tool_re.search(gline)
                 if match:
                 if match:
                     current_aperture = match.group(1)
                     current_aperture = match.group(1)
@@ -740,12 +744,11 @@ class Gerber(Geometry):
                             self.apertures[last_path_aperture]['geometry'].append(deepcopy(geo_dict))
                             self.apertures[last_path_aperture]['geometry'].append(deepcopy(geo_dict))
 
 
                             path = [path[-1]]
                             path = [path[-1]]
-
                     continue
                     continue
 
 
-                # ############################################################# ##
-                # G36* - Begin region
-                # ############################################################# ##
+                # ################################################################
+                # ################  G36* - Begin region   ########################
+                # ################################################################
                 if self.regionon_re.search(gline):
                 if self.regionon_re.search(gline):
                     if len(path) > 1:
                     if len(path) > 1:
                         # Take care of what is left in the path
                         # Take care of what is left in the path
@@ -780,9 +783,9 @@ class Gerber(Geometry):
                     making_region = True
                     making_region = True
                     continue
                     continue
 
 
-                # ############################################################# ##
-                # G37* - End region
-                # ############################################################# ##
+                # ################################################################
+                # ################  G37* - End region     ########################
+                # ################################################################
                 if self.regionoff_re.search(gline):
                 if self.regionoff_re.search(gline):
                     making_region = False
                     making_region = False
 
 
@@ -830,20 +833,26 @@ class Gerber(Geometry):
 
 
                     # --- Buffered ---
                     # --- Buffered ---
                     geo_dict = dict()
                     geo_dict = dict()
-                    region_f = Polygon(path).exterior
+                    if current_aperture in self.apertures:
+                        buff_value = float(self.apertures[current_aperture]['size']) / 2.0
+                        region_geo = Polygon(path).buffer(buff_value, int(self.steps_per_circle))
+                    else:
+                        region_geo = Polygon(path)
+
+                    region_f = region_geo.exterior
                     if not region_f.is_empty:
                     if not region_f.is_empty:
                         follow_buffer.append(region_f)
                         follow_buffer.append(region_f)
                         geo_dict['follow'] = region_f
                         geo_dict['follow'] = region_f
 
 
-                    region_s = Polygon(path)
+                    region_s = region_geo
                     if not region_s.is_valid:
                     if not region_s.is_valid:
-                        region_s = region_s.buffer(0, int(self.steps_per_circle / 4))
-
+                        region_s = region_s.buffer(0, int(self.steps_per_circle))
                     if not region_s.is_empty:
                     if not region_s.is_empty:
                         if self.app.defaults['gerber_simplification']:
                         if self.app.defaults['gerber_simplification']:
                             poly_buffer.append(region_s.simplify(s_tol))
                             poly_buffer.append(region_s.simplify(s_tol))
                         else:
                         else:
                             poly_buffer.append(region_s)
                             poly_buffer.append(region_s)
+
                         if self.is_lpc is True:
                         if self.is_lpc is True:
                             geo_dict['clear'] = region_s
                             geo_dict['clear'] = region_s
                         else:
                         else:
@@ -855,18 +864,22 @@ class Gerber(Geometry):
                     path = [[current_x, current_y]]  # Start new path
                     path = [[current_x, current_y]]  # Start new path
                     continue
                     continue
 
 
-                # ## G01/2/3* - Interpolation mode change
-                # Can occur along with coordinates and operation code but
-                # sometimes by itself (handled here).
-                # Example: G01*
+                # ################################################################
+                # ################  G01/2/3* - Interpolation mode change #########
+                # ####  Can occur along with coordinates and operation code but ##
+                # ####  sometimes by itself (handled here).  #####################
+                # ####  Example: G01*                        #####################
+                # ################################################################
                 match = self.interp_re.search(gline)
                 match = self.interp_re.search(gline)
                 if match:
                 if match:
                     current_interpolation_mode = int(match.group(1))
                     current_interpolation_mode = int(match.group(1))
                     continue
                     continue
 
 
-                # ## G01 - Linear interpolation plus flashes
-                # Operation code (D0x) missing is deprecated... oh well I will support it.
+                # ################################################################
+                # ######### G01 - Linear interpolation plus flashes  #############
+                # ######### Operation code (D0x) missing is deprecated   #########
                 # REGEX: r'^(?:G0?(1))?(?:X(-?\d+))?(?:Y(-?\d+))?(?:D0([123]))?\*$'
                 # REGEX: r'^(?:G0?(1))?(?:X(-?\d+))?(?:Y(-?\d+))?(?:D0([123]))?\*$'
+                # ################################################################
                 match = self.lin_re.search(gline)
                 match = self.lin_re.search(gline)
                 if match:
                 if match:
                     # Dxx alone?
                     # Dxx alone?
@@ -1147,7 +1160,9 @@ class Gerber(Geometry):
                     # log.debug("Line_number=%3s X=%s Y=%s (%s)" % (line_num, linear_x, linear_y, gline))
                     # log.debug("Line_number=%3s X=%s Y=%s (%s)" % (line_num, linear_x, linear_y, gline))
                     continue
                     continue
 
 
-                # ## G74/75* - Single or multiple quadrant arcs
+                # ################################################################
+                # ######### G74/75* - Single or multiple quadrant arcs  ##########
+                # ################################################################
                 match = self.quad_re.search(gline)
                 match = self.quad_re.search(gline)
                 if match:
                 if match:
                     if match.group(1) == '4':
                     if match.group(1) == '4':
@@ -1156,9 +1171,12 @@ class Gerber(Geometry):
                         quadrant_mode = 'MULTI'
                         quadrant_mode = 'MULTI'
                     continue
                     continue
 
 
-                # ## G02/3 - Circular interpolation
-                # 2-clockwise, 3-counterclockwise
-                # Ex. format: G03 X0 Y50 I-50 J0 where the X, Y coords are the coords of the End Point
+                # ################################################################
+                # ######### G02/3 - Circular interpolation   #####################
+                # ######### 2-clockwise, 3-counterclockwise  #####################
+                # ######### Ex. format: G03 X0 Y50 I-50 J0 where the     #########
+                # ######### X, Y coords are the coords of the End Point  #########
+                # ################################################################
                 match = self.circ_re.search(gline)
                 match = self.circ_re.search(gline)
                 if match:
                 if match:
                     arcdir = [None, None, "cw", "ccw"]
                     arcdir = [None, None, "cw", "ccw"]
@@ -1339,12 +1357,16 @@ class Gerber(Geometry):
                         else:
                         else:
                             log.warning("Invalid arc in line %d." % line_num)
                             log.warning("Invalid arc in line %d." % line_num)
 
 
-                # ## EOF
+                # ################################################################
+                # ######### EOF - END OF FILE ####################################
+                # ################################################################
                 match = self.eof_re.search(gline)
                 match = self.eof_re.search(gline)
                 if match:
                 if match:
                     continue
                     continue
 
 
-                # ## Line did not match any pattern. Warn user.
+                # ################################################################
+                # ######### Line did not match any pattern. Warn user.  ##########
+                # ################################################################
                 log.warning("Line ignored (%d): %s" % (line_num, gline))
                 log.warning("Line ignored (%d): %s" % (line_num, gline))
 
 
             if len(path) > 1:
             if len(path) > 1:

+ 41 - 46
flatcamTools/ToolPanelize.py

@@ -608,22 +608,22 @@ class Panelize(FlatCAMTool):
                         if panel_obj.multigeo is True:
                         if panel_obj.multigeo is True:
                             for tool in panel_obj.tools:
                             for tool in panel_obj.tools:
                                 try:
                                 try:
-                                    for pol in panel_obj.tools[tool]['solid_geometry']:
-                                        geo_len += 1
+                                    geo_len += len(panel_obj.tools[tool]['solid_geometry'])
                                 except TypeError:
                                 except TypeError:
-                                    geo_len = 1
+                                    geo_len += 1
                         else:
                         else:
                             try:
                             try:
-                                for pol in panel_obj.solid_geometry:
-                                    geo_len += 1
+                                geo_len = len(panel_obj.solid_geometry)
                             except TypeError:
                             except TypeError:
                                 geo_len = 1
                                 geo_len = 1
                     elif isinstance(panel_obj, FlatCAMGerber):
                     elif isinstance(panel_obj, FlatCAMGerber):
                         for ap in panel_obj.apertures:
                         for ap in panel_obj.apertures:
-                            for elem in panel_obj.apertures[ap]['geometry']:
-                                geo_len += 1
+                            if 'geometry' in panel_obj.apertures[ap]:
+                                try:
+                                    geo_len += len(panel_obj.apertures[ap]['geometry'])
+                                except TypeError:
+                                    geo_len += 1
 
 
-                    self.app.progress.emit(0)
                     element = 0
                     element = 0
                     for row in range(rows):
                     for row in range(rows):
                         currentx = 0.0
                         currentx = 0.0
@@ -724,49 +724,48 @@ class Panelize(FlatCAMTool):
                                     if self.app.abort_flag:
                                     if self.app.abort_flag:
                                         # graceful abort requested by the user
                                         # graceful abort requested by the user
                                         raise FlatCAMApp.GracefulException
                                         raise FlatCAMApp.GracefulException
+                                    if 'geometry' in panel_obj.apertures[apid]:
+                                        try:
+                                            # calculate the number of polygons
+                                            geo_len = len(panel_obj.apertures[apid]['geometry'])
+                                        except TypeError:
+                                            geo_len = 1
+                                        pol_nr = 0
+                                        for el in panel_obj.apertures[apid]['geometry']:
+                                            if self.app.abort_flag:
+                                                # graceful abort requested by the user
+                                                raise FlatCAMApp.GracefulException
 
 
-                                    try:
-                                        # calculate the number of polygons
-                                        geo_len = len(panel_obj.apertures[apid]['geometry'])
-                                    except TypeError:
-                                        geo_len = 1
-                                    pol_nr = 0
-                                    for el in panel_obj.apertures[apid]['geometry']:
-                                        if self.app.abort_flag:
-                                            # graceful abort requested by the user
-                                            raise FlatCAMApp.GracefulException
-
-                                        new_el = dict()
-                                        if 'solid' in el:
-                                            geo_aper = translate_recursion(el['solid'])
-                                            new_el['solid'] = geo_aper
+                                            new_el = dict()
+                                            if 'solid' in el:
+                                                geo_aper = translate_recursion(el['solid'])
+                                                new_el['solid'] = geo_aper
 
 
-                                        if 'clear' in el:
-                                            geo_aper = translate_recursion(el['clear'])
-                                            new_el['clear'] = geo_aper
+                                            if 'clear' in el:
+                                                geo_aper = translate_recursion(el['clear'])
+                                                new_el['clear'] = geo_aper
 
 
-                                        if 'follow' in el:
-                                            geo_aper = translate_recursion(el['follow'])
-                                            new_el['follow'] = geo_aper
+                                            if 'follow' in el:
+                                                geo_aper = translate_recursion(el['follow'])
+                                                new_el['follow'] = geo_aper
 
 
-                                        obj_fin.apertures[apid]['geometry'].append(deepcopy(new_el))
+                                            obj_fin.apertures[apid]['geometry'].append(deepcopy(new_el))
 
 
-                                        pol_nr += 1
-                                        disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100]))
+                                            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"),
-                                                                                      int(element),
-                                                                                      disp_number))
-                                            old_disp_number = disp_number
+                                            if old_disp_number < disp_number <= 100:
+                                                self.app.proc_container.update_view_text(' %s: %d %d%%' %
+                                                                                         (_("Copy"),
+                                                                                          int(element),
+                                                                                          disp_number))
+                                                old_disp_number = disp_number
 
 
                             currentx += lenghtx
                             currentx += lenghtx
                         currenty += lenghty
                         currenty += lenghty
 
 
                     if panel_type == 'gerber':
                     if panel_type == 'gerber':
-                        self.app.inform.emit('%s' %
-                                             _("Generating panel ... Adding the Gerber code."))
+                        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,
                         obj_fin.source_file = self.app.export_gerber(obj_name=self.outname, filename=None,
                                                                      local_use=obj_fin, use_thread=False)
                                                                      local_use=obj_fin, use_thread=False)
 
 
@@ -777,15 +776,11 @@ class Panelize(FlatCAMTool):
                     # app_obj.log.debug("Finished creating a cascaded union for the panel.")
                     # app_obj.log.debug("Finished creating a cascaded union for the panel.")
                     self.app.proc_container.update_view_text('')
                     self.app.proc_container.update_view_text('')
 
 
-                self.app.inform.emit('%s: %d' %
-                                     (_("Generating panel... Spawning copies"), (int(rows * columns))))
+                self.app.inform.emit('%s: %d' % (_("Generating panel... Spawning copies"), (int(rows * columns))))
                 if isinstance(panel_obj, FlatCAMExcellon):
                 if isinstance(panel_obj, FlatCAMExcellon):
-                    self.app.progress.emit(50)
                     self.app.new_object("excellon", self.outname, job_init_excellon, plot=True, autoselected=True)
                     self.app.new_object("excellon", self.outname, job_init_excellon, plot=True, autoselected=True)
                 else:
                 else:
-                    self.app.progress.emit(50)
-                    self.app.new_object(panel_type, self.outname, job_init_geometry,
-                                        plot=True, autoselected=True)
+                    self.app.new_object(panel_type, self.outname, job_init_geometry, plot=True, autoselected=True)
 
 
         if self.constrain_flag is False:
         if self.constrain_flag is False:
             self.app.inform.emit('[success] %s' % _("Panel done..."))
             self.app.inform.emit('[success] %s' % _("Panel done..."))