diff --git a/CHANGELOG.md b/CHANGELOG.md index 30424148..b7d5532b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ CHANGELOG for FlatCAM beta ================================================= +29.01.2022 + +- upgraded/fixed the Panelize Tcl command + 28.01.2022 - in Tcl command `geocutout` fixed an error due of a no longer used parameter diff --git a/appPlugins/ToolPanelize.py b/appPlugins/ToolPanelize.py index 80b93669..783c371e 100644 --- a/appPlugins/ToolPanelize.py +++ b/appPlugins/ToolPanelize.py @@ -408,6 +408,8 @@ class Panelize(AppTool): self.app.inform.emit(_("Generating panel ... ")) def job_init_excellon(obj_fin, app_obj): + obj_fin.multitool = True + currenty = 0.0 # init the storage for drills and for slots for tool in copied_tools: @@ -785,7 +787,7 @@ class Panelize(AppTool): default_data[opt_key] = self.app.options[opt_key] new_obj.tools = {} - new_tid = 0 + new_tid = 10 for apid in new_obj.tools: new_tid += 1 new_sgeo = [g['solid'] for g in new_obj.tools[apid]['geometry'] if 'solid' in g] @@ -809,7 +811,7 @@ class Panelize(AppTool): 'data': deepcopy(default_data), 'solid_geometry': deepcopy(new_obj.solid_geometry) } - del new_obj.tools + del new_obj.tools # TODO what the hack is this? First we create and then immediately delete? app_obj.inform.emit('%s' % _("Generating panel ... Adding the source code.")) new_obj.source_file = self.app.f_handlers.export_dxf(obj_name=self.outname, filename=None, diff --git a/tclCommands/TclCommandPanelize.py b/tclCommands/TclCommandPanelize.py index 9c6c741d..671a5f78 100644 --- a/tclCommands/TclCommandPanelize.py +++ b/tclCommands/TclCommandPanelize.py @@ -1,6 +1,7 @@ from tclCommands.TclCommand import TclCommand import shapely.affinity as affinity +from shapely.geometry import MultiPolygon, MultiLineString import logging from copy import deepcopy @@ -202,20 +203,36 @@ class TclCommandPanelize(TclCommand): # if ret_value == 'fail': # return 'fail' - def panelize_2(): + # ############################################################################################################ + # make a copy of the panelized Excellon or Geometry tools + # ############################################################################################################ + if obj.kind == 'excellon' or obj.kind == 'geometry': + copied_tools = {} + for tt, tt_val in list(obj.tools.items()): + copied_tools[tt] = deepcopy(tt_val) + + # ############################################################################################################ + # make a copy of the panelized Gerber apertures + # ############################################################################################################ + if obj.kind == 'gerber': + copied_apertures = {} + for tt, tt_val in list(obj.tools.items()): + copied_apertures[tt] = deepcopy(tt_val) + + def panelize_handler(): if obj is not None: self.app.inform.emit("Generating panel ... Please wait.") def job_init_excellon(obj_fin, app_obj): - currenty = 0.0 + obj_fin.multitool = True - obj_fin.tools = obj.tools.copy() - if 'drills' not in obj_fin.tools: - obj_fin.tools['drills'] = [] - if 'slots' not in obj_fin.tools: - obj_fin.tools['slots'] = [] - if 'solid_geometry' not in obj_fin.tools: - obj_fin.tools['solid_geometry'] = [] + currenty = 0.0 + # init the storage for drills and for slots + for tool in copied_tools: + copied_tools[tool]['drills'] = [] + copied_tools[tool]['slots'] = [] + obj_fin.tools = copied_tools + obj_fin.solid_geometry = [] for option in obj.options: if option != 'name': @@ -223,28 +240,54 @@ class TclCommandPanelize(TclCommand): obj_fin.options[option] = obj.options[option] except Exception as e: app_obj.log.error("Failed to copy option: %s" % str(option)) - app_obj.log.error("TclCommandPanelize.execute().panelize2() --> %s" % str(e)) + app_obj.log.error( + "TclCommandPanelize.execute().panelize2.job_init_excellon() Options:--> %s" % + str(e)) + # calculate the total number of drills and slots + geo_len_drills = 0 + geo_len_slots = 0 + for tool in copied_tools: + geo_len_drills += len(copied_tools[tool]['drills']) + geo_len_slots += len(copied_tools[tool]['slots']) + + # panelization for row in range(rows): currentx = 0.0 for col in range(columns): - if 'drills' in obj.tools: - for drill_pt in obj.tools['drills']: - point_offseted = affinity.translate(drill_pt, currentx, currenty) - obj_fin.tools['drills'].append(point_offseted) - if 'slots' in obj.tools: - for slot_tuple in obj.tools['slots']: - start_offseted = affinity.translate(slot_tuple[0], currentx, currenty) - stop_offseted = affinity.translate(slot_tuple[1], currentx, currenty) - obj_fin.tools['slots'].append( - (start_offseted, stop_offseted) - ) + for tool in obj.tools: + if 'drills' in obj.tools[tool]: + if obj.tools[tool]['drills']: + for drill in obj.tools[tool]['drills']: + # offset / panelization + point_offseted = affinity.translate(drill, currentx, currenty) + obj_fin.tools[tool]['drills'].append(point_offseted) + else: + obj.tools[tool]['drills'] = [] + + if 'slots' in obj.tools[tool]: + if obj.tools[tool]['slots']: + for slot in obj.tools[tool]['slots']: + # offset / panelization + start_offseted = affinity.translate(slot[0], currentx, currenty) + stop_offseted = affinity.translate(slot[1], currentx, currenty) + offseted_slot = ( + start_offseted, + stop_offseted + ) + obj_fin.tools[tool]['slots'].append(offseted_slot) + else: + obj.tools[tool]['slots'] = [] + currentx += lenghtx currenty += lenghty obj_fin.create_geometry() obj_fin.zeros = obj.zeros obj_fin.units = obj.units + app_obj.inform.emit('%s' % _("Generating panel ... Adding the source code.")) + obj_fin.source_file = app_obj.f_handlers.export_excellon(obj_name=outname, filename=None, + local_use=obj_fin, use_thread=False) def job_init_geometry(obj_fin, app_obj): currentx = 0.0 @@ -261,27 +304,42 @@ class TclCommandPanelize(TclCommand): obj_fin.solid_geometry = [] - if obj.kind == 'geometry': - obj_fin.multigeo = obj.multigeo - obj_fin.tools = deepcopy(obj.tools) - if obj.multigeo is True: - for tool in obj.tools: - obj_fin.tools[tool]['solid_geometry'][:] = [] + # create the initial structure on which to create the panel + obj_fin.multigeo = obj.multigeo + obj_fin.tools = copied_tools + if obj.multigeo is True: + for tool in obj.tools: + obj_fin.tools[tool]['solid_geometry'][:] = [] for row in range(rows): currentx = 0.0 - for col in range(columns): - if obj.kind == 'geometry': - if obj.multigeo is True: - for tool in obj.tools: - obj_fin.tools[tool]['solid_geometry'].append(translate_recursion( - obj.tools[tool]['solid_geometry']) - ) - else: - obj_fin.solid_geometry.append( - translate_recursion(obj.solid_geometry) - ) + if obj.multigeo is True: + for tool in obj.tools: + trans_geo = translate_recursion(obj.tools[tool]['solid_geometry']) + try: + work_geo = trans_geo.geoms if \ + isinstance(trans_geo, (MultiPolygon, MultiLineString)) else trans_geo + for trans_it in work_geo: + if not trans_it.is_empty: + obj_fin.tools[tool]['solid_geometry'].append(trans_it) + except TypeError: + if not trans_geo.is_empty: + obj_fin.tools[tool]['solid_geometry'].append(trans_geo) + + # ############################################################################# + # ########## Panelize the solid_geometry - always done ##################### + # ############################################################################# + try: + sol_geo = obj.solid_geometry + work_geo = sol_geo.geoms if \ + isinstance(sol_geo, (MultiPolygon, MultiLineString)) else sol_geo + for geo_el in work_geo: + trans_geo = translate_recursion(geo_el) + obj_fin.solid_geometry.append(trans_geo) + except TypeError: + trans_geo = translate_recursion(obj.solid_geometry) + obj_fin.solid_geometry.append(trans_geo) else: obj_fin.solid_geometry.append( translate_recursion(obj.solid_geometry) @@ -289,25 +347,92 @@ class TclCommandPanelize(TclCommand): currentx += lenghtx currenty += lenghty + obj_fin.source_file = app_obj.f_handlers.export_dxf(obj_name=outname, filename=None, + local_use=obj_fin, use_thread=False) + + def job_init_gerber(obj_fin, app_obj): + currentx = 0.0 + currenty = 0.0 + + def translate_recursion(geom): + if type(geom) == list: + geoms = [] + for local_geom in geom: + res_geo = translate_recursion(local_geom) + try: + geoms += res_geo + except TypeError: + geoms.append(res_geo) + return geoms + else: + return affinity.translate(geom, xoff=currentx, yoff=currenty) + + obj_fin.solid_geometry = [] + + # create the initial structure on which to create the panel + obj_fin.tools = copied_apertures + for ap in obj_fin.tools: + obj_fin.tools[ap]['geometry'] = [] + + for row in range(rows): + currentx = 0.0 + for col in range(columns): + # Will panelize a Gerber Object + for apid in obj.tools: + if 'geometry' in obj.tools[apid]: + # panelization -> apertures + for el in obj.tools[apid]['geometry']: + new_el = {} + 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 'follow' in el: + geo_aper = translate_recursion(el['follow']) + new_el['follow'] = geo_aper + obj_fin.tools[apid]['geometry'].append(deepcopy(new_el)) + + # ##################################################################################### + # ########## Panelize the solid_geometry - always done ############################# + # ##################################################################################### + try: + for geo_el in obj.solid_geometry: + trans_geo = translate_recursion(geo_el) + obj_fin.solid_geometry.append(trans_geo) + except TypeError: + trans_geo = translate_recursion(obj.solid_geometry) + obj_fin.solid_geometry.append(trans_geo) + + currentx += lenghtx + currenty += lenghty + + obj_fin.source_file = app_obj.f_handlers.export_gerber(obj_name=outname, filename=None, + local_use=obj_fin, use_thread=False) if obj.kind == 'excellon': self.app.app_obj.new_object("excellon", outname, job_init_excellon, plot=False, autoselected=True) - else: + elif obj.kind == 'geometry': self.app.app_obj.new_object("geometry", outname, job_init_geometry, plot=False, autoselected=True) - + else: + self.app.app_obj.new_object("gerber", outname, job_init_gerber, plot=False, autoselected=True) if threaded is True: self.app.proc_container.new('%s...' % _("Working")) def job_thread(app_obj): try: - panelize_2() + panelize_handler() app_obj.inform.emit('[success] %s' % _("Done.")) - except Exception as ee: - log.error(str(ee)) + except Exception as err: + app_obj.log.error('TclCommandPanelize.execute.job_thread() -> %s' % str(err)) return self.app.collection.promise(outname) self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]}) else: - panelize_2() - self.app.inform.emit('[success] %s' % _("Done.")) + try: + panelize_handler() + self.app.inform.emit('[success] %s' % _("Done.")) + except Exception as ee: + self.app.log.error('TclCommandPanelize.execute() non-threaded -> %s' % str(ee))