- 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
This commit is contained in:
Marius Stanciu 2019-12-26 03:15:17 +02:00 committed by Marius
parent 0268a02f3b
commit 217316c732
3 changed files with 123 additions and 104 deletions

View File

@ -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
- 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
- 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

View File

@ -437,10 +437,10 @@ class Gerber(Geometry):
gline = gline.strip(' \r\n')
# log.debug("Line=%3s %s" % (line_num, gline))
# ###################
# Ignored lines #####
# Comments #####
# ###################
# ###############################################################
# ################ Ignored lines ############################
# ################ Comments ############################
# ###############################################################
match = self.comm_re.search(gline)
if match:
continue
@ -504,11 +504,11 @@ class Gerber(Geometry):
current_polarity = new_polarity
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)
if match:
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)
continue
# ## Mode (IN/MM)
# Example: %MOIN*%
# ################################################################
# ######################## Mode (IN/MM) #######################
# ##################### Example: %MOIN*% #####################
# ################################################################
match = self.mode_re.search(gline)
if match:
self.units = match.group(1)
@ -535,9 +537,9 @@ class Gerber(Geometry):
self.conversion_done = True
continue
# ############################################################# ##
# Combined Number format and Mode --- Allegro does this ####### ##
# ############################################################# ##
# ################################################################
# Combined Number format and Mode --- Allegro does this ##########
# ################################################################
match = self.fmt_re_alt.search(gline)
if match:
absolute = {'A': 'Absolute', 'I': 'Relative'}[match.group(2)]
@ -558,9 +560,9 @@ class Gerber(Geometry):
self.conversion_done = True
continue
# ############################################################# ##
# Search for OrCAD way for having Number format
# ############################################################# ##
# ################################################################
# #### Search for OrCAD way for having Number format ########
# ################################################################
match = self.fmt_re_orcad.search(gline)
if match:
if match.group(1) is not None:
@ -587,9 +589,9 @@ class Gerber(Geometry):
self.conversion_done = True
continue
# ############################################################# ##
# Units (G70/1) OBSOLETE
# ############################################################# ##
# ################################################################
# ############ Units (G70/1) OBSOLETE ######################
# ################################################################
match = self.units_re.search(gline)
if match:
obs_gerber_units = {'0': 'IN', '1': 'MM'}[match.group(1)]
@ -599,21 +601,21 @@ class Gerber(Geometry):
self.conversion_done = True
continue
# ############################################################# ##
# Absolute/relative coordinates G90/1 OBSOLETE ######## ##
# ##################################################### ##
# ################################################################
# ##### Absolute/relative coordinates G90/1 OBSOLETE ###########
# ################################################################
match = self.absrel_re.search(gline)
if match:
absolute = {'0': "Absolute", '1': "Relative"}[match.group(1)]
log.warning("Gerber obsolete coordinates type found = %s (Absolute or Relative) " % absolute)
continue
# ############################################################# ##
# Aperture Macros ##################################### ##
# ################################################################
# Aperture Macros ################################################
# Having this at the beginning will slow things down
# but macros can have complicated statements than could
# be caught by other patterns.
# ############################################################# ##
# ################################################################
if current_macro is None: # No macro started yet
match = self.am1_re.search(gline)
# Start macro if match, else not an AM, carry on.
@ -640,18 +642,20 @@ class Gerber(Geometry):
self.aperture_macros[current_macro].append(gline)
continue
# ## Aperture definitions %ADD...
# ################################################################
# ############## Aperture definitions %ADD... #################
# ################################################################
match = self.ad_re.search(gline)
if match:
# log.info("Found aperture definition. Line %d: %s" % (line_num, gline))
self.aperture_parse(match.group(1), match.group(2), match.group(3))
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])\*$')
# ############################################################# ##
# ################################################################
match = self.opcode_re.search(gline)
if match:
current_operation_code = int(match.group(1))
@ -690,10 +694,10 @@ class Gerber(Geometry):
continue
# ############################################################# ##
# Tool/aperture change
# Example: D12*
# ############################################################# ##
# ################################################################
# ################ Tool/aperture change ########################
# ################ Example: D12* ########################
# ################################################################
match = self.tool_re.search(gline)
if match:
current_aperture = match.group(1)
@ -740,12 +744,11 @@ class Gerber(Geometry):
self.apertures[last_path_aperture]['geometry'].append(deepcopy(geo_dict))
path = [path[-1]]
continue
# ############################################################# ##
# G36* - Begin region
# ############################################################# ##
# ################################################################
# ################ G36* - Begin region ########################
# ################################################################
if self.regionon_re.search(gline):
if len(path) > 1:
# Take care of what is left in the path
@ -780,9 +783,9 @@ class Gerber(Geometry):
making_region = True
continue
# ############################################################# ##
# G37* - End region
# ############################################################# ##
# ################################################################
# ################ G37* - End region ########################
# ################################################################
if self.regionoff_re.search(gline):
making_region = False
@ -830,20 +833,26 @@ class Gerber(Geometry):
# --- Buffered ---
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:
follow_buffer.append(region_f)
geo_dict['follow'] = region_f
region_s = Polygon(path)
region_s = region_geo
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 self.app.defaults['gerber_simplification']:
poly_buffer.append(region_s.simplify(s_tol))
else:
poly_buffer.append(region_s)
if self.is_lpc is True:
geo_dict['clear'] = region_s
else:
@ -855,18 +864,22 @@ class Gerber(Geometry):
path = [[current_x, current_y]] # Start new path
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)
if match:
current_interpolation_mode = int(match.group(1))
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]))?\*$'
# ################################################################
match = self.lin_re.search(gline)
if match:
# 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))
continue
# ## G74/75* - Single or multiple quadrant arcs
# ################################################################
# ######### G74/75* - Single or multiple quadrant arcs ##########
# ################################################################
match = self.quad_re.search(gline)
if match:
if match.group(1) == '4':
@ -1156,9 +1171,12 @@ class Gerber(Geometry):
quadrant_mode = 'MULTI'
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)
if match:
arcdir = [None, None, "cw", "ccw"]
@ -1339,12 +1357,16 @@ class Gerber(Geometry):
else:
log.warning("Invalid arc in line %d." % line_num)
# ## EOF
# ################################################################
# ######### EOF - END OF FILE ####################################
# ################################################################
match = self.eof_re.search(gline)
if match:
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))
if len(path) > 1:

View File

@ -608,22 +608,22 @@ class Panelize(FlatCAMTool):
if panel_obj.multigeo is True:
for tool in panel_obj.tools:
try:
for pol in panel_obj.tools[tool]['solid_geometry']:
geo_len += 1
geo_len += len(panel_obj.tools[tool]['solid_geometry'])
except TypeError:
geo_len = 1
geo_len += 1
else:
try:
for pol in panel_obj.solid_geometry:
geo_len += 1
geo_len = len(panel_obj.solid_geometry)
except TypeError:
geo_len = 1
elif isinstance(panel_obj, FlatCAMGerber):
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
for row in range(rows):
currentx = 0.0
@ -724,49 +724,48 @@ class Panelize(FlatCAMTool):
if self.app.abort_flag:
# graceful abort requested by the user
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
currenty += lenghty
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,
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.")
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):
self.app.progress.emit(50)
self.app.new_object("excellon", self.outname, job_init_excellon, plot=True, autoselected=True)
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:
self.app.inform.emit('[success] %s' % _("Panel done..."))