From cf51e4ce2c292f35177c7db9fcc1a51d5f82f803 Mon Sep 17 00:00:00 2001 From: Kamil Sopko Date: Sat, 20 Feb 2016 10:56:32 +0100 Subject: [PATCH 01/18] implement del_polygon from geometry --- camlib.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/camlib.py b/camlib.py index 7d756a12..b919f398 100644 --- a/camlib.py +++ b/camlib.py @@ -136,6 +136,29 @@ class Geometry(object): log.error("Failed to run union on polygons.") raise + def del_polygon(self, points): + """ + Delete a polygon from the object + + :param points: The vertices of the polygon. + :return: None + """ + if self.solid_geometry is None: + self.solid_geometry = [] + + + flat_geometry = self.flatten(pathonly=True) + log.debug("%d paths" % len(flat_geometry)) + polygon=Polygon(points) + toolgeo=cascaded_union(polygon) + diffs=[] + for target in flat_geometry: + if type(target) == LineString or type(target) == LinearRing: + diffs.append(target.difference(toolgeo)) + else: + log.warning("Not implemented.") + return cascaded_union(diffs) + def bounds(self): """ Returns coordinates of rectangular bounds From 5acdbd51e3b4b5630e44d3cae1e964a5e9b7bf18 Mon Sep 17 00:00:00 2001 From: Kamil Sopko Date: Sat, 20 Feb 2016 11:38:35 +0100 Subject: [PATCH 02/18] implement some new shell commands, which helps automate system of milling and cutting out shapes like arduino uno board etc. shell commands: aligndrill - Create excellon with drills for aligment. geocutout - Cut holding gaps closed geometry. del_poly - Remove a polygon from the given Geometry object. del_rect - Delete a rectange from the given Geometry object. --- FlatCAMApp.py | 252 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 252 insertions(+) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 7c72897f..11f3d462 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -2134,6 +2134,42 @@ class App(QtCore.QObject): return 'Ok' + + def geocutout(name, *args): + """ + cut gaps in current geometry + + :param name: + :param args: + :return: + """ + a, kwa = h(*args) + types = {'dia': float, + 'gapsize': float, + 'gaps': str} + + for key in kwa: + if key not in types: + return 'Unknown parameter: %s' % key + kwa[key] = types[key](kwa[key]) + + try: + obj = self.collection.get_by_name(str(name)) + except: + return "Could not retrieve object: %s" % name + + + + xmin, ymin, xmax, ymax = obj.bounds() + px = 0.5 * (xmin + xmax) + py = 0.5 * (ymin + ymax) + gapsize = kwa['gapsize']+kwa['dia']/2 + if kwa['gaps'] == '4' or kwa['gaps']=='lr': + del_rectangle(name,xmin-gapsize,py-gapsize,xmax+gapsize,py+gapsize) + if kwa['gaps'] == '4' or kwa['gaps']=='tb': + del_rectangle(name,px-gapsize,ymin-gapsize,px+gapsize,ymax+gapsize) + return 'Ok' + def mirror(name, *args): a, kwa = h(*args) types = {'box': str, @@ -2202,6 +2238,142 @@ class App(QtCore.QObject): return 'Ok' + def aligndrill(name, *args): + a, kwa = h(*args) + types = {'box': str, + 'axis': str, + 'holes': str, + 'grid': float, + 'gridoffset': float, + 'axisoffset': float, + 'dia': float, + 'dist': float} + + for key in kwa: + if key not in types: + return 'Unknown parameter: %s' % key + kwa[key] = types[key](kwa[key]) + + # Get source object. + try: + obj = self.collection.get_by_name(str(name)) + except: + return "Could not retrieve object: %s" % name + + if obj is None: + return "Object not found: %s" % name + + if not isinstance(obj, FlatCAMGerber) and not isinstance(obj, FlatCAMExcellon): + return "ERROR: Only Gerber and Excellon objects can be used." + + + # Axis + try: + axis = kwa['axis'].upper() + except KeyError: + return "ERROR: Specify -axis X or -axis Y" + + if not ('holes' in kwa or ('grid' in kwa and 'gridoffset' in kwa)): + return "ERROR: Specify -holes or -grid with -gridoffset " + + if 'holes' in kwa: + try: + holes = eval("[" + kwa['holes'] + "]") + except KeyError: + return "ERROR: Wrong -holes format (X1,Y1),(X2,Y2)" + + xscale, yscale = {"X": (1.0, -1.0), "Y": (-1.0, 1.0)}[axis] + + # Tools + tools = {"1": {"C": kwa['dia']}} + + def alligndrill_init_me(init_obj, app_obj): + + drills = [] + if 'holes' in kwa: + for hole in holes: + point = Point(hole) + point_mirror = affinity.scale(point, xscale, yscale, origin=(px, py)) + drills.append({"point": point, "tool": "1"}) + drills.append({"point": point_mirror, "tool": "1"}) + else: + if not 'box' in kwa: + return "ERROR: -grid can be used only for -box" + + if 'axisoffset' in kwa: + axisoffset=kwa['axisoffset'] + else: + axisoffset=0 + + + if axis == "X": + firstpoint=-kwa['gridoffset']+xmin + #-5 + minlenght=(xmax-xmin+2*kwa['gridoffset']) + #57+10=67 + gridstripped=(minlenght//kwa['grid'])*kwa['grid'] + #67//10=60 + if (minlenght-gridstripped) >kwa['gridoffset']: + gridstripped=gridstripped+kwa['grid'] + lastpoint=(firstpoint+gridstripped) + localHoles=(firstpoint,axisoffset),(lastpoint,axisoffset) + else: + firstpoint=-kwa['gridoffset']+ymin + minlenght=(ymax-ymin+2*kwa['gridoffset']) + gridstripped=minlenght//kwa['grid']*kwa['grid'] + if (minlenght-gridstripped) >kwa['gridoffset']: + gridstripped=gridstripped+kwa['grid'] + lastpoint=(firstpoint+gridstripped) + localHoles=(axisoffset,firstpoint),(axisoffset,lastpoint) + + for hole in localHoles: + point = Point(hole) + point_mirror = affinity.scale(point, xscale, yscale, origin=(px, py)) + drills.append({"point": point, "tool": "1"}) + drills.append({"point": point_mirror, "tool": "1"}) + + init_obj.tools = tools + init_obj.drills = drills + init_obj.create_geometry() + + # Box + if 'box' in kwa: + try: + box = self.collection.get_by_name(kwa['box']) + except: + return "Could not retrieve object box: %s" % kwa['box'] + + if box is None: + return "Object box not found: %s" % kwa['box'] + + try: + xmin, ymin, xmax, ymax = box.bounds() + px = 0.5 * (xmin + xmax) + py = 0.5 * (ymin + ymax) + + obj.app.new_object("excellon", name + "_aligndrill", alligndrill_init_me) + + except Exception, e: + return "Operation failed: %s" % str(e) + + else: + try: + dist = float(kwa['dist']) + except KeyError: + dist = 0.0 + except ValueError: + return "Invalid distance: %s" % kwa['dist'] + + try: + px=dist + py=dist + obj.app.new_object("excellon", name + "_alligndrill", alligndrill_init_me) + except Exception, e: + return "Operation failed: %s" % str(e) + + return 'Ok' + + def drillcncjob(name, *args): a, kwa = h(*args) types = {'tools': str, @@ -2492,6 +2664,34 @@ class App(QtCore.QObject): return add_poly(obj_name, botleft_x, botleft_y, botleft_x, topright_y, topright_x, topright_y, topright_x, botleft_y) + def del_poly(obj_name, *args): + if len(args) % 2 != 0: + return "Incomplete coordinate." + + points = [[float(args[2*i]), float(args[2*i+1])] for i in range(len(args)/2)] + + try: + obj = self.collection.get_by_name(str(obj_name)) + except: + return "Could not retrieve object: %s" % obj_name + if obj is None: + return "Object not found: %s" % obj_name + + def init_obj_me(init_obj, app): + assert isinstance(init_obj, FlatCAMGeometry) + init_obj.solid_geometry=cascaded_union(diff) + + diff= obj.del_polygon(points) + try: + delete(obj_name) + obj.app.new_object("geometry", obj_name, init_obj_me) + except Exception as e: + return "Failed: %s" % str(e) + + def del_rectangle(obj_name, botleft_x, botleft_y, topright_x, topright_y): + return del_poly(obj_name, botleft_x, botleft_y, botleft_x, topright_y, + topright_x, topright_y, topright_x, botleft_y) + def add_circle(obj_name, center_x, center_y, radius): try: obj = self.collection.get_by_name(str(obj_name)) @@ -2721,6 +2921,30 @@ class App(QtCore.QObject): " gapsize: size of gap\n" + " gaps: type of gaps" }, + 'geocutout': { + 'fcn': geocutout, + 'help': "Cut holding gaps closed geometry.\n" + + "> geocutout [-dia <3.0 (float)>] [-margin <0.0 (float)>] [-gapsize <0.5 (float)>] [-gaps ]\n" + + " name: Name of the geometry object\n" + + " dia: Tool diameter\n" + + " margin: Margin over bounds\n" + + " gapsize: size of gap\n" + + " gaps: type of gaps\n" + + "\n" + + " example:\n" + + "\n" + + " #isolate margin for example from fritzing arduino shield or any svg etc\n" + + " isolate BCu_margin -dia 3 -overlap 1\n" + + "\n" + + " #create exteriors from isolated object\n" + + " exteriors BCu_margin_iso -outname BCu_margin_iso_exterior\n" + + "\n" + + " #delete isolated object if you dond need id anymore\n" + + " delete BCu_margin_iso\n" + + "\n" + + " #finally cut holding gaps\n" + + " geocutout BCu_margin_iso_exterior -dia 3 -gapsize 0.6 -gaps 4\n" + }, 'mirror': { 'fcn': mirror, 'help': "Mirror a layer.\n" + @@ -2730,6 +2954,19 @@ class App(QtCore.QObject): " axis: Mirror axis parallel to the X or Y axis.\n" + " dist: Distance of the mirror axis to the X or Y axis." }, + 'aligndrill': { + 'fcn': aligndrill, + 'help': "Create excellon with drills for aligment.\n" + + "> aligndrill [-dia <3.0 (float)>] -axis [-box [-grid <10 (float)> -gridoffset <5 (float)> [-axisoffset <0 (float)>]] | -dist ]\n" + + " name: Name of the object (Gerber or Excellon) to mirror.\n" + + " dia: Tool diameter\n" + + " box: Name of object which act as box (cutout for example.)\n" + + " grid: aligning to grid, for thouse, who have aligning pins inside table in grid (-5,0),(5,0),(15,0)..." + + " gridoffset: offset from pcb from 0 position and minimal offset to grid on max" + + " axisoffset: offset on second axis before aligment holes" + + " axis: Mirror axis parallel to the X or Y axis.\n" + + " dist: Distance of the mirror axis to the X or Y axis." + }, 'exteriors': { 'fcn': exteriors, 'help': "Get exteriors of polygons.\n" + @@ -2827,6 +3064,13 @@ class App(QtCore.QObject): ' name: Name of the geometry object to which to append the polygon.\n' + ' xi, yi: Coordinates of points in the polygon.' }, + 'del_poly': { + 'fcn': del_poly, + 'help': ' - Remove a polygon from the given Geometry object.\n' + + '> del_poly [x3 y3 [...]]\n' + + ' name: Name of the geometry object to which to remove the polygon.\n' + + ' xi, yi: Coordinates of points in the polygon.' + }, 'delete': { 'fcn': delete, 'help': 'Deletes the give object.\n' + @@ -2850,6 +3094,14 @@ class App(QtCore.QObject): ' out_name: Name of the new geometry object.' + ' obj_name_0... names of the objects to join' }, + 'del_rect': { + 'fcn': del_rectangle, + 'help': 'Delete a rectange from the given Geometry object.\n' + + '> del_rect \n' + + ' name: Name of the geometry object to which to remove the rectangle.\n' + + ' botleft_x, botleft_y: Coordinates of the bottom left corner.\n' + + ' topright_x, topright_y Coordinates of the top right corner.' + }, 'add_rect': { 'fcn': add_rectangle, 'help': 'Creates a rectange in the given Geometry object.\n' + From 9d897d0fcb52135fc8e3f56bc716e65ec41b5f70 Mon Sep 17 00:00:00 2001 From: Kamil Sopko Date: Sat, 20 Feb 2016 12:21:46 +0000 Subject: [PATCH 03/18] README.md edited online with Bitbucket --- README.md | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/README.md b/README.md index 5b156d13..0ccea79d 100644 --- a/README.md +++ b/README.md @@ -6,3 +6,102 @@ FlatCAM: 2D Computer-Aided PCB Manufacturing FlatCAM is a program for preparing CNC jobs for making PCBs on a CNC router. Among other things, it can take a Gerber file generated by your favorite PCB CAD program, and create G-Code for Isolation routing. + + +####### my own shell script + +new +set_sys units MM + + +# ######### BOTTOM layer + +# LOAD +open_gerber /path/to/Gerber/Loop_contour.gm1 -outname BCu_margin +open_gerber /path/to/Gerber/Loop_copperBottom.gbl -outname BCu +open_excellon /path/to/Gerber/Loop_drill.txt -outname BCu_drills + +#MIRROR +mirror BCu -box BCu_margin -axis X +mirror BCu_drills -box BCu_margin -axis X + +#ALIGNHOLES +aligndrill BCu_margin -dia 3 -box BCu_margin -grid 10 -gridoffset 5 -axisoffset 0 -axis X + +#CUTOUT +isolate BCu_margin -dia 3 -overlap 1 +exteriors BCu_margin_iso -outname BCu_margin_iso_exterior +delete BCu_margin_iso +geocutout BCu_margin_iso_exterior -dia 3 -gapsize 0.2 -gaps 4 + +#ISOLATE TRACES +exteriors BCu_margin -outname BCu_exterior +isolate BCu -dia 0.8 -overlap 1 + +#JOIN TRACES and basic exterior +join_geometries BCu_join_iso BCu_iso BCu_exterior + +#CNCJOBS +drillcncjob BCu_drills -tools 100,101,102,103,104 -drillz -2 -travelz 2 -feedrate 5 -outname BCu_drills_0.8 +drillcncjob BCu_margin_aligndrill -tools 1 -drillz -2 -travelz 2 -feedrate 5 -outname BCu_drills_3 + +cncjob BCu_join_iso -tooldia 0.6 +#cncjob BCu_margin_cutout -tooldia 3 +cncjob BCu_margin_iso_exterior -tooldia 3 + + + +#GENERATE GCODE + +write_gcode BCu_join_iso_cnc /path/to/Gerber/output/Loop-BCu.pngc +write_gcode BCu_margin_iso_exterior_cnc /path/to/Gerber/output/Loop-BCu-Margin.ngc +write_gcode BCu_drills_0.8 /path/to/Gerber/output/Loop-BCu.drl_0.8.ngc +write_gcode BCu_drills_3 /path/to/Gerber/output/Loop-BCu.drl_3.ngc + + + +# ######### TOP layer + + +# LOAD +open_gerber /path/to/Gerber/Loop_contour.gm1 -outname FCu_margin +open_gerber /path/to/Gerber/Loop_copperTop.gtl -outname FCu +open_excellon /path/to/Gerber/Loop_drill.txt -outname FCu_drills + +#ALIGNHOLES +aligndrill FCu_margin -dia 3 -box FCu_margin -grid 10 -gridoffset 5 -axisoffset 0 -axis X + +#CUTOUT +isolate FCu_margin -dia 3 -overlap 1 +exteriors FCu_margin_iso -outname FCu_margin_iso_exterior +delete FCu_margin_iso +geocutout FCu_margin_iso_exterior -dia 3 -gapsize 0.2 -gaps 4 + +#ISOLATE TRACES +exteriors FCu_margin -outname FCu_exterior +isolate FCu -dia 0.8 -overlap 1 + +#JOIN TRACES and basic exterior +join_geometries FCu_join_iso FCu_iso FCu_exterior + +#CNCJOBS +drillcncjob FCu_drills -tools 100,101,102,103,104 -drillz -2 -travelz 2 -feedrate 5 -outname FCu_drills_0.8 +drillcncjob FCu_margin_aligndrill -tools 1 -drillz -2 -travelz 2 -feedrate 5 -outname FCu_drills_3 + +cncjob FCu_join_iso -tooldia 0.6 +#cncjob FCu_margin_cutout -tooldia 3 +cncjob FCu_margin_iso_exterior -tooldia 3 + + + +#GENERATE GCODE + +write_gcode FCu_join_iso_cnc /path/to/Gerber/output/Loop-FCu.pngc +write_gcode FCu_margin_iso_exterior_cnc /path/to/Gerber/output/Loop-FCu-Margin.ngc +write_gcode FCu_drills_0.8 /path/to/Gerber/output/Loop-FCu.drl_0.8.ngc +write_gcode FCu_drills_3 /path/to/Gerber/output/Loop-FCu.drl_3.ngc + + + + + From 2b8b9e12702a9624120cee08f0195e341267374f Mon Sep 17 00:00:00 2001 From: Kamil Sopko Date: Sat, 20 Feb 2016 12:22:20 +0000 Subject: [PATCH 04/18] README.md edited online with Bitbucket --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 0ccea79d..3961abd6 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,11 @@ Among other things, it can take a Gerber file generated by your favorite PCB CAD program, and create G-Code for Isolation routing. -####### my own shell script +### my own shell script + +``` +#!python + new set_sys units MM @@ -100,8 +104,4 @@ write_gcode FCu_join_iso_cnc /path/to/Gerber/output/Loop-FCu.pngc write_gcode FCu_margin_iso_exterior_cnc /path/to/Gerber/output/Loop-FCu-Margin.ngc write_gcode FCu_drills_0.8 /path/to/Gerber/output/Loop-FCu.drl_0.8.ngc write_gcode FCu_drills_3 /path/to/Gerber/output/Loop-FCu.drl_3.ngc - - - - - +``` \ No newline at end of file From e94fe513b3a466bd3ca45945c508b38c9ac1d970 Mon Sep 17 00:00:00 2001 From: Kamil Sopko Date: Sat, 20 Feb 2016 12:26:46 +0000 Subject: [PATCH 05/18] README.md edited online with Bitbucket --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3961abd6..c044962b 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Among other things, it can take a Gerber file generated by your favorite PCB CAD program, and create G-Code for Isolation routing. -### my own shell script +### double sided shell script used for Loop Arduino Shield example from [fritzing](Link URL)http://fritzing.org/ ``` #!python From d7bdfe231dd2e254c09c0c7b7fc151e055e21083 Mon Sep 17 00:00:00 2001 From: Kamil Sopko Date: Sat, 20 Feb 2016 13:38:42 +0100 Subject: [PATCH 06/18] Revert "README.md edited online with Bitbucket" This reverts commit e94fe513b3a466bd3ca45945c508b38c9ac1d970. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c044962b..3961abd6 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Among other things, it can take a Gerber file generated by your favorite PCB CAD program, and create G-Code for Isolation routing. -### double sided shell script used for Loop Arduino Shield example from [fritzing](Link URL)http://fritzing.org/ +### my own shell script ``` #!python From 2e07b6dfa58052bd4b978a24dc826b0eb3cd0fbc Mon Sep 17 00:00:00 2001 From: Kamil Sopko Date: Sat, 20 Feb 2016 13:40:15 +0100 Subject: [PATCH 07/18] Revert "README.md edited online with Bitbucket" This reverts commit 2b8b9e12702a9624120cee08f0195e341267374f. --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 3961abd6..0ccea79d 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,7 @@ Among other things, it can take a Gerber file generated by your favorite PCB CAD program, and create G-Code for Isolation routing. -### my own shell script - -``` -#!python - +####### my own shell script new set_sys units MM @@ -104,4 +100,8 @@ write_gcode FCu_join_iso_cnc /path/to/Gerber/output/Loop-FCu.pngc write_gcode FCu_margin_iso_exterior_cnc /path/to/Gerber/output/Loop-FCu-Margin.ngc write_gcode FCu_drills_0.8 /path/to/Gerber/output/Loop-FCu.drl_0.8.ngc write_gcode FCu_drills_3 /path/to/Gerber/output/Loop-FCu.drl_3.ngc -``` \ No newline at end of file + + + + + From 14be36f277c5868ea313a12f5d493c1008ed3634 Mon Sep 17 00:00:00 2001 From: Kamil Sopko Date: Sat, 20 Feb 2016 13:41:16 +0100 Subject: [PATCH 08/18] Revert "README.md edited online with Bitbucket" This reverts commit 9d897d0fcb52135fc8e3f56bc716e65ec41b5f70. --- README.md | 99 ------------------------------------------------------- 1 file changed, 99 deletions(-) diff --git a/README.md b/README.md index 0ccea79d..5b156d13 100644 --- a/README.md +++ b/README.md @@ -6,102 +6,3 @@ FlatCAM: 2D Computer-Aided PCB Manufacturing FlatCAM is a program for preparing CNC jobs for making PCBs on a CNC router. Among other things, it can take a Gerber file generated by your favorite PCB CAD program, and create G-Code for Isolation routing. - - -####### my own shell script - -new -set_sys units MM - - -# ######### BOTTOM layer - -# LOAD -open_gerber /path/to/Gerber/Loop_contour.gm1 -outname BCu_margin -open_gerber /path/to/Gerber/Loop_copperBottom.gbl -outname BCu -open_excellon /path/to/Gerber/Loop_drill.txt -outname BCu_drills - -#MIRROR -mirror BCu -box BCu_margin -axis X -mirror BCu_drills -box BCu_margin -axis X - -#ALIGNHOLES -aligndrill BCu_margin -dia 3 -box BCu_margin -grid 10 -gridoffset 5 -axisoffset 0 -axis X - -#CUTOUT -isolate BCu_margin -dia 3 -overlap 1 -exteriors BCu_margin_iso -outname BCu_margin_iso_exterior -delete BCu_margin_iso -geocutout BCu_margin_iso_exterior -dia 3 -gapsize 0.2 -gaps 4 - -#ISOLATE TRACES -exteriors BCu_margin -outname BCu_exterior -isolate BCu -dia 0.8 -overlap 1 - -#JOIN TRACES and basic exterior -join_geometries BCu_join_iso BCu_iso BCu_exterior - -#CNCJOBS -drillcncjob BCu_drills -tools 100,101,102,103,104 -drillz -2 -travelz 2 -feedrate 5 -outname BCu_drills_0.8 -drillcncjob BCu_margin_aligndrill -tools 1 -drillz -2 -travelz 2 -feedrate 5 -outname BCu_drills_3 - -cncjob BCu_join_iso -tooldia 0.6 -#cncjob BCu_margin_cutout -tooldia 3 -cncjob BCu_margin_iso_exterior -tooldia 3 - - - -#GENERATE GCODE - -write_gcode BCu_join_iso_cnc /path/to/Gerber/output/Loop-BCu.pngc -write_gcode BCu_margin_iso_exterior_cnc /path/to/Gerber/output/Loop-BCu-Margin.ngc -write_gcode BCu_drills_0.8 /path/to/Gerber/output/Loop-BCu.drl_0.8.ngc -write_gcode BCu_drills_3 /path/to/Gerber/output/Loop-BCu.drl_3.ngc - - - -# ######### TOP layer - - -# LOAD -open_gerber /path/to/Gerber/Loop_contour.gm1 -outname FCu_margin -open_gerber /path/to/Gerber/Loop_copperTop.gtl -outname FCu -open_excellon /path/to/Gerber/Loop_drill.txt -outname FCu_drills - -#ALIGNHOLES -aligndrill FCu_margin -dia 3 -box FCu_margin -grid 10 -gridoffset 5 -axisoffset 0 -axis X - -#CUTOUT -isolate FCu_margin -dia 3 -overlap 1 -exteriors FCu_margin_iso -outname FCu_margin_iso_exterior -delete FCu_margin_iso -geocutout FCu_margin_iso_exterior -dia 3 -gapsize 0.2 -gaps 4 - -#ISOLATE TRACES -exteriors FCu_margin -outname FCu_exterior -isolate FCu -dia 0.8 -overlap 1 - -#JOIN TRACES and basic exterior -join_geometries FCu_join_iso FCu_iso FCu_exterior - -#CNCJOBS -drillcncjob FCu_drills -tools 100,101,102,103,104 -drillz -2 -travelz 2 -feedrate 5 -outname FCu_drills_0.8 -drillcncjob FCu_margin_aligndrill -tools 1 -drillz -2 -travelz 2 -feedrate 5 -outname FCu_drills_3 - -cncjob FCu_join_iso -tooldia 0.6 -#cncjob FCu_margin_cutout -tooldia 3 -cncjob FCu_margin_iso_exterior -tooldia 3 - - - -#GENERATE GCODE - -write_gcode FCu_join_iso_cnc /path/to/Gerber/output/Loop-FCu.pngc -write_gcode FCu_margin_iso_exterior_cnc /path/to/Gerber/output/Loop-FCu-Margin.ngc -write_gcode FCu_drills_0.8 /path/to/Gerber/output/Loop-FCu.drl_0.8.ngc -write_gcode FCu_drills_3 /path/to/Gerber/output/Loop-FCu.drl_3.ngc - - - - - From 84322882e92a05a1cea3786a65dee219ca53a92c Mon Sep 17 00:00:00 2001 From: Kamil Sopko Date: Sun, 21 Feb 2016 17:03:59 +0100 Subject: [PATCH 09/18] fix FlatCamObj.offset - offset does not work on joined geometries, if tree was not flat it send list into affinity.translate. implement FlatCAMExcellon.merge - to be able join more excellons into one job --- FlatCAMObj.py | 84 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 79 insertions(+), 5 deletions(-) diff --git a/FlatCAMObj.py b/FlatCAMObj.py index 8b7126a0..fb785aed 100644 --- a/FlatCAMObj.py +++ b/FlatCAMObj.py @@ -630,6 +630,75 @@ class FlatCAMExcellon(FlatCAMObj, Excellon): # from predecessors. self.ser_attrs += ['options', 'kind'] + @staticmethod + def merge(exc_list, exc_final): + FlatCAMExcellon.merge(exc_list,exc_final,False) + + @staticmethod + def merge(exc_list, exc_final, copy_options): + """ + Merges(copy if used on one) the excellon of objects in exc_list into + options have same like exc_final + the geometry of geo_final. + + :param exc_list: List of FlatCAMExcellon Objects to join. + :param exc_final: Destination FlatCAMExcellon object. + :return: None + """ + + if type(exc_list) is not list: + exc_list_real= list() + exc_list_real.append(exc_list) + else: + exc_list_real=exc_list + + + for exc in exc_list_real: + # Expand lists + if type(exc) is list: + FlatCAMExcellon.merge(exc, exc_final, copy_options) + + # If not list, just append + else: + if copy_options is True: + exc_final.options["plot"]=exc.options["plot"] + exc_final.options["solid"]=exc.options["solid"] + exc_final.options["drillz"]=exc.options["drillz"] + exc_final.options["travelz"]=exc.options["travelz"] + exc_final.options["feedrate"]=exc.options["feedrate"] + exc_final.options["tooldia"]=exc.options["tooldia"] + exc_final.options["toolchange"]=exc.options["toolchange"] + exc_final.options["toolchangez"]=exc.options["toolchangez"] + exc_final.options["spindlespeed"]=exc.options["spindlespeed"] + + + for drill in exc.drills: + point = Point(drill['point'].x,drill['point'].y) + exc_final.drills.append({"point": point, "tool": drill['tool']}) + toolsrework=dict() + max_numeric_tool=0 + for toolname in exc.tools.iterkeys(): + numeric_tool=int(toolname) + if numeric_tool>max_numeric_tool: + max_numeric_tool=numeric_tool + toolsrework[exc.tools[toolname]['C']]=toolname + + #final as last becouse names from final tools will be used + for toolname in exc_final.tools.iterkeys(): + numeric_tool=int(toolname) + if numeric_tool>max_numeric_tool: + max_numeric_tool=numeric_tool + toolsrework[exc_final.tools[toolname]['C']]=toolname + + for toolvalues in toolsrework.iterkeys(): + if toolsrework[toolvalues] in exc_final.tools: + if exc_final.tools[toolsrework[toolvalues]]!={"C": toolvalues}: + exc_final.tools[str(max_numeric_tool+1)]={"C": toolvalues} + else: + exc_final.tools[toolsrework[toolvalues]]={"C": toolvalues} + exc_final.create_geometry() + + def build_ui(self): FlatCAMObj.build_ui(self) @@ -1264,11 +1333,16 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): dx, dy = vect - if type(self.solid_geometry) == list: - self.solid_geometry = [affinity.translate(g, xoff=dx, yoff=dy) - for g in self.solid_geometry] - else: - self.solid_geometry = affinity.translate(self.solid_geometry, xoff=dx, yoff=dy) + def translate_recursion(geom): + if type(geom) == list: + geoms=list() + for local_geom in geom: + geoms.append(translate_recursion(local_geom)) + return geoms + else: + return affinity.translate(geom, xoff=dx, yoff=dy) + + self.solid_geometry=translate_recursion(self.solid_geometry) def convert_units(self, units): factor = Geometry.convert_units(self, units) From 62816a614e10b8799e19166ce196cefb85b08407 Mon Sep 17 00:00:00 2001 From: Kamil Sopko Date: Sun, 21 Feb 2016 17:17:05 +0100 Subject: [PATCH 10/18] OK python does not allow overloading for methods --- FlatCAMObj.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/FlatCAMObj.py b/FlatCAMObj.py index fb785aed..69eea0de 100644 --- a/FlatCAMObj.py +++ b/FlatCAMObj.py @@ -630,10 +630,6 @@ class FlatCAMExcellon(FlatCAMObj, Excellon): # from predecessors. self.ser_attrs += ['options', 'kind'] - @staticmethod - def merge(exc_list, exc_final): - FlatCAMExcellon.merge(exc_list,exc_final,False) - @staticmethod def merge(exc_list, exc_final, copy_options): """ From f73c1b81dcb81595854939680508786bb181ef81 Mon Sep 17 00:00:00 2001 From: Kamil Sopko Date: Sun, 21 Feb 2016 17:21:51 +0100 Subject: [PATCH 11/18] implement some new shell commands, which helps panelize milling operations shell commands: join_excellons - ability to join excellons together panelize - placing geometries and excellons in columns and rows --- FlatCAMApp.py | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 11f3d462..c32fdce1 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -2741,6 +2741,120 @@ class App(QtCore.QObject): if objs is not None: self.new_object("geometry", obj_name, initialize) + def join_excellons(obj_name, *obj_names): + objs = [] + for obj_n in obj_names: + obj = self.collection.get_by_name(str(obj_n)) + if obj is None: + return "Object not found: %s" % obj_n + else: + objs.append(obj) + + def initialize(obj, app): + FlatCAMExcellon.merge(objs, obj,True) + + if objs is not None: + self.new_object("excellon", obj_name, initialize) + + def panelize(name, *args): + a, kwa = h(*args) + types = {'box': str, + 'spacing_columns': float, + 'spacing_rows': float, + 'columns': int, + 'rows': int, + 'outname': str} + + for key in kwa: + if key not in types: + return 'Unknown parameter: %s' % key + kwa[key] = types[key](kwa[key]) + + # Get source object. + try: + obj = self.collection.get_by_name(str(name)) + except: + return "Could not retrieve object: %s" % name + + if obj is None: + return "Object not found: %s" % name + + if 'box' in kwa: + boxname=kwa['box'] + try: + box = self.collection.get_by_name(boxname) + except: + return "Could not retrieve object: %s" % name + else: + box=obj + + if 'columns' not in kwa or 'rows' not in kwa: + return "ERROR: Specify -columns and -rows" + + if 'outname' in kwa: + outname=kwa['outname'] + else: + outname=name+'_panelized' + + if 'spacing_columns' in kwa: + spacing_columns=kwa['spacing_columns'] + else: + spacing_columns=5 + + if 'spacing_rows' in kwa: + spacing_rows=kwa['spacing_rows'] + else: + spacing_rows=5 + + xmin, ymin, xmax, ymax = box.bounds() + lenghtx = xmax-xmin+spacing_columns + lenghty = ymax-ymin+spacing_rows + + currenty=0 + def initialize_local(obj_init, app): + obj_init.solid_geometry = obj.solid_geometry + obj_init.offset([float(currentx), float(currenty)]), + + def initialize_local_excellon(obj_init, app): + FlatCAMExcellon.merge(obj, obj_init,True) + obj_init.offset([float(currentx), float(currenty)]), + + def initialize_geometry(obj_init, app): + FlatCAMGeometry.merge(objs, obj_init) + + def initialize_excellon(obj_init, app): + FlatCAMExcellon.merge(objs, obj_init,True) + + objs=[] + if obj is not None: + + for row in range(kwa['rows']): + currentx=0 + for col in range(kwa['columns']): + local_outname=outname+".tmp."+str(col)+"."+str(row) + if isinstance(obj, FlatCAMExcellon): + new_obj=self.new_object("excellon", local_outname, initialize_local_excellon) + else: + new_obj=self.new_object("geometry", local_outname, initialize_local) + objs.append(new_obj) + currentx=currentx+lenghtx + currenty=currenty+lenghty + + if isinstance(obj, FlatCAMExcellon): + self.new_object("excellon", outname, initialize_excellon) + else: + self.new_object("geometry", outname, initialize_geometry) + + + for delobj in objs: + self.collection.set_active(delobj.options['name']) + self.on_delete() + + else: + return "ERROR: obj is None" + + return "Ok" + def make_docs(): output = '' import collections @@ -3094,6 +3208,26 @@ class App(QtCore.QObject): ' out_name: Name of the new geometry object.' + ' obj_name_0... names of the objects to join' }, + 'join_excellons': { + 'fcn': join_excellons, + 'help': 'Runs a merge operation (join) on the excellon ' + + 'objects.' + + '> join_excellons ....\n' + + ' out_name: Name of the new excellon object.' + + ' obj_name_0... names of the objects to join' + }, + 'panelize': { + 'fcn': panelize, + 'help': "Simple panelize geometries.\n" + + "> panelize [-box ] [-spacing_columns <5 (float)>] [-spacing_rows <5 (float)>] -columns -rows [-outname ]\n" + + " name: Name of the object to panelize.\n" + + " box: Name of object which act as box (cutout for example.) for cutout boundary. Object from name is used if not specified.\n" + + " spacing_columns: spacing between columns\n"+ + " spacing_rows: spacing between rows\n"+ + " columns: number of columns\n"+ + " rows: number of rows\n"+ + " outname: Name of the new geometry object." + }, 'del_rect': { 'fcn': del_rectangle, 'help': 'Delete a rectange from the given Geometry object.\n' + From 1d663c4efe4ecfa149c9b448c51021b55dcb8e81 Mon Sep 17 00:00:00 2001 From: Kamil Sopko Date: Sun, 21 Feb 2016 17:39:26 +0100 Subject: [PATCH 12/18] allow use aligndrill also for geometries --- FlatCAMApp.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index c32fdce1..2e6b0ada 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -2263,9 +2263,8 @@ class App(QtCore.QObject): if obj is None: return "Object not found: %s" % name - if not isinstance(obj, FlatCAMGerber) and not isinstance(obj, FlatCAMExcellon): - return "ERROR: Only Gerber and Excellon objects can be used." - + if not isinstance(obj, FlatCAMGeometry) and not isinstance(obj, FlatCAMGerber) and not isinstance(obj, FlatCAMExcellon): + return "ERROR: Only Gerber, Geometry and Excellon objects can be used." # Axis try: From f119f4de034098ef7a83fb7e6a99ff5cd2d8780e Mon Sep 17 00:00:00 2001 From: Kamil Sopko Date: Mon, 22 Feb 2016 10:50:06 +0100 Subject: [PATCH 13/18] implement command aligndrillgrid, which creates grid of holes to bed --- FlatCAMApp.py | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 2e6b0ada..4af101b6 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -2238,6 +2238,61 @@ class App(QtCore.QObject): return 'Ok' + def aligndrillgrid(outname, *args): + a, kwa = h(*args) + types = {'gridx': float, + 'gridy': float, + 'gridoffsetx': float, + 'gridoffsety': float, + 'columns':int, + 'rows':int, + 'dia': float + } + for key in kwa: + if key not in types: + return 'Unknown parameter: %s' % key + kwa[key] = types[key](kwa[key]) + + + if 'columns' not in kwa or 'rows' not in kwa: + return "ERROR: Specify -columns and -rows" + + if 'gridx' not in kwa or 'gridy' not in kwa: + return "ERROR: Specify -gridx and -gridy" + + if 'dia' not in kwa: + return "ERROR: Specify -dia" + + if 'gridoffsetx' not in kwa: + gridoffsetx=0 + else: + gridoffsetx=kwa['gridoffsetx'] + + if 'gridoffsety' not in kwa: + gridoffsety=0 + else: + gridoffsety=kwa['gridoffsety'] + + + # Tools + tools = {"1": {"C": kwa['dia']}} + + def aligndrillgrid_init_me(init_obj, app_obj): + drills = [] + currenty=0 + for row in range(kwa['rows']): + currentx=0 + for col in range(kwa['columns']): + point = Point(currentx-gridoffsetx,currenty-gridoffsety) + drills.append({"point": point, "tool": "1"}) + currentx=currentx+kwa['gridx'] + currenty=currenty+kwa['gridy'] + init_obj.tools = tools + init_obj.drills = drills + init_obj.create_geometry() + + self.new_object("excellon", outname , aligndrillgrid_init_me) + def aligndrill(name, *args): a, kwa = h(*args) types = {'box': str, @@ -3067,6 +3122,19 @@ class App(QtCore.QObject): " axis: Mirror axis parallel to the X or Y axis.\n" + " dist: Distance of the mirror axis to the X or Y axis." }, + 'aligndrillgrid': { + 'fcn': aligndrillgrid, + 'help': "Create excellon with drills for aligment grid.\n" + + "> aligndrillgrid [-dia <3.0 (float)>] -gridx [-gridoffsetx <0 (float)>] -gridy [-gridoffsety <0 (float)>] -columns -rows \n" + + " outname: Name of the object to create.\n" + + " dia: Tool diameter\n" + + " gridx: grid size in X axis\n" + + " gridoffsetx: move grid from origin\n" + + " gridy: grid size in Y axis\n" + + " gridoffsety: move grid from origin\n" + + " colums: grid holes on X axis\n" + + " rows: grid holes on Y axis\n" + }, 'aligndrill': { 'fcn': aligndrill, 'help': "Create excellon with drills for aligment.\n" + From 23d5d7bd64889372747dc7bcc55300f1e9ae0e22 Mon Sep 17 00:00:00 2001 From: Kamil Sopko Date: Mon, 22 Feb 2016 11:19:30 +0100 Subject: [PATCH 14/18] aligndrillgrid - fix offset direction -5 should be -x axis --- FlatCAMApp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 4af101b6..9455ed8b 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -2283,7 +2283,7 @@ class App(QtCore.QObject): for row in range(kwa['rows']): currentx=0 for col in range(kwa['columns']): - point = Point(currentx-gridoffsetx,currenty-gridoffsety) + point = Point(currentx+gridoffsetx,currenty+gridoffsety) drills.append({"point": point, "tool": "1"}) currentx=currentx+kwa['gridx'] currenty=currenty+kwa['gridy'] From a827e184b74b49bac96fb1b1e0fd388b3148d15d Mon Sep 17 00:00:00 2001 From: Kamil Sopko Date: Tue, 23 Feb 2016 00:23:27 +0100 Subject: [PATCH 15/18] rename del_polygon to subtract_polygon correctly modify current geometry and dont leave it as path fix shellcommands to follow new names tweak geocutout to be able cut 8 gaps --- FlatCAMApp.py | 80 +++++++++++++++++++++++++++++---------------------- camlib.py | 10 +++---- 2 files changed, 50 insertions(+), 40 deletions(-) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 9455ed8b..ed30657b 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -2137,7 +2137,7 @@ class App(QtCore.QObject): def geocutout(name, *args): """ - cut gaps in current geometry + subtract gaps from geometry, this will not create new object :param name: :param args: @@ -2148,6 +2148,14 @@ class App(QtCore.QObject): 'gapsize': float, 'gaps': str} + #way gaps wil be rendered: + # lr - left + right + # tb - top + bottom + # 4 - left + right +top + bottom + # 2lr - 2*left + 2*right + # 2tb - 2*top + 2*bottom + # 8 - 2*left + 2*right +2*top + 2*bottom + for key in kwa: if key not in types: return 'Unknown parameter: %s' % key @@ -2159,15 +2167,23 @@ class App(QtCore.QObject): return "Could not retrieve object: %s" % name - + #get min and max data for each object as we just cut rectangles across X or Y xmin, ymin, xmax, ymax = obj.bounds() px = 0.5 * (xmin + xmax) py = 0.5 * (ymin + ymax) + lenghtx = (xmax - xmin) + lenghty = (ymax - ymin) gapsize = kwa['gapsize']+kwa['dia']/2 + if kwa['gaps'] == '8' or kwa['gaps']=='2lr': + subtract_rectangle(name,xmin-gapsize,py-gapsize+lenghty/4,xmax+gapsize,py+gapsize+lenghty/4) + subtract_rectangle(name,xmin-gapsize,py-gapsize-lenghty/4,xmax+gapsize,py+gapsize-lenghty/4) + if kwa['gaps'] == '8' or kwa['gaps']=='2tb': + subtract_rectangle(name,px-gapsize+lenghtx/4,ymin-gapsize,px+gapsize+lenghtx/4,ymax+gapsize) + subtract_rectangle(name,px-gapsize-lenghtx/4,ymin-gapsize,px+gapsize-lenghtx/4,ymax+gapsize) if kwa['gaps'] == '4' or kwa['gaps']=='lr': - del_rectangle(name,xmin-gapsize,py-gapsize,xmax+gapsize,py+gapsize) + subtract_rectangle(name,xmin-gapsize,py-gapsize,xmax+gapsize,py+gapsize) if kwa['gaps'] == '4' or kwa['gaps']=='tb': - del_rectangle(name,px-gapsize,ymin-gapsize,px+gapsize,ymax+gapsize) + subtract_rectangle(name,px-gapsize,ymin-gapsize,px+gapsize,ymax+gapsize) return 'Ok' def mirror(name, *args): @@ -2456,26 +2472,26 @@ class App(QtCore.QObject): return "ERROR: Only Excellon objects can be drilled." try: - + # Get the tools from the list job_name = kwa["outname"] - + # Object initialization function for app.new_object() def job_init(job_obj, app_obj): assert isinstance(job_obj, FlatCAMCNCjob), \ "Initializer expected FlatCAMCNCjob, got %s" % type(job_obj) - + job_obj.z_cut = kwa["drillz"] job_obj.z_move = kwa["travelz"] job_obj.feedrate = kwa["feedrate"] job_obj.spindlespeed = kwa["spindlespeed"] if "spindlespeed" in kwa else None toolchange = True if "toolchange" in kwa and kwa["toolchange"] == 1 else False job_obj.generate_from_excellon_by_tool(obj, kwa["tools"], toolchange) - + job_obj.gcode_parse() - + job_obj.create_geometry() - + obj.app.new_object("cncjob", job_name, job_init) except Exception, e: @@ -2600,7 +2616,7 @@ class App(QtCore.QObject): types = {'dia': float, 'passes': int, 'overlap': float, - 'outname': str, + 'outname': str, 'combine': int} for key in kwa: @@ -2718,7 +2734,7 @@ class App(QtCore.QObject): return add_poly(obj_name, botleft_x, botleft_y, botleft_x, topright_y, topright_x, topright_y, topright_x, botleft_y) - def del_poly(obj_name, *args): + def subtract_poly(obj_name, *args): if len(args) % 2 != 0: return "Incomplete coordinate." @@ -2731,19 +2747,13 @@ class App(QtCore.QObject): if obj is None: return "Object not found: %s" % obj_name - def init_obj_me(init_obj, app): - assert isinstance(init_obj, FlatCAMGeometry) - init_obj.solid_geometry=cascaded_union(diff) + obj.subtract_polygon(points) + obj.plot() - diff= obj.del_polygon(points) - try: - delete(obj_name) - obj.app.new_object("geometry", obj_name, init_obj_me) - except Exception as e: - return "Failed: %s" % str(e) + return "OK." - def del_rectangle(obj_name, botleft_x, botleft_y, topright_x, topright_y): - return del_poly(obj_name, botleft_x, botleft_y, botleft_x, topright_y, + def subtract_rectangle(obj_name, botleft_x, botleft_y, topright_x, topright_y): + return subtract_poly(obj_name, botleft_x, botleft_y, botleft_x, topright_y, topright_x, topright_y, topright_x, botleft_y) def add_circle(obj_name, center_x, center_y, radius): @@ -3091,8 +3101,8 @@ class App(QtCore.QObject): }, 'geocutout': { 'fcn': geocutout, - 'help': "Cut holding gaps closed geometry.\n" + - "> geocutout [-dia <3.0 (float)>] [-margin <0.0 (float)>] [-gapsize <0.5 (float)>] [-gaps ]\n" + + 'help': "Cut holding gaps from geometry.\n" + + "> geocutout [-dia <3.0 (float)>] [-margin <0.0 (float)>] [-gapsize <0.5 (float)>] [-gaps ]\n" + " name: Name of the geometry object\n" + " dia: Tool diameter\n" + " margin: Margin over bounds\n" + @@ -3245,11 +3255,11 @@ class App(QtCore.QObject): ' name: Name of the geometry object to which to append the polygon.\n' + ' xi, yi: Coordinates of points in the polygon.' }, - 'del_poly': { - 'fcn': del_poly, - 'help': ' - Remove a polygon from the given Geometry object.\n' + - '> del_poly [x3 y3 [...]]\n' + - ' name: Name of the geometry object to which to remove the polygon.\n' + + 'subtract_poly': { + 'fcn': subtract_poly, + 'help': 'Subtract polygon from the given Geometry object.\n' + + '> subtract_poly [x3 y3 [...]]\n' + + ' name: Name of the geometry object, which will be sutracted.\n' + ' xi, yi: Coordinates of points in the polygon.' }, 'delete': { @@ -3295,11 +3305,11 @@ class App(QtCore.QObject): " rows: number of rows\n"+ " outname: Name of the new geometry object." }, - 'del_rect': { - 'fcn': del_rectangle, - 'help': 'Delete a rectange from the given Geometry object.\n' + - '> del_rect \n' + - ' name: Name of the geometry object to which to remove the rectangle.\n' + + 'subtract_rect': { + 'fcn': subtract_rectangle, + 'help': 'Subtract rectange from the given Geometry object.\n' + + '> subtract_rect \n' + + ' name: Name of the geometry object, which will be subtracted.\n' + ' botleft_x, botleft_y: Coordinates of the bottom left corner.\n' + ' topright_x, topright_y Coordinates of the top right corner.' }, diff --git a/camlib.py b/camlib.py index b919f398..f576ed98 100644 --- a/camlib.py +++ b/camlib.py @@ -136,17 +136,17 @@ class Geometry(object): log.error("Failed to run union on polygons.") raise - def del_polygon(self, points): + def subtract_polygon(self, points): """ - Delete a polygon from the object + Subtract polygon from the given object. This only operates on the paths in the original geometry, i.e. it converts polygons into paths. :param points: The vertices of the polygon. - :return: None + :return: none """ if self.solid_geometry is None: self.solid_geometry = [] - + #pathonly should be allways True, otherwise polygons are not subtracted flat_geometry = self.flatten(pathonly=True) log.debug("%d paths" % len(flat_geometry)) polygon=Polygon(points) @@ -157,7 +157,7 @@ class Geometry(object): diffs.append(target.difference(toolgeo)) else: log.warning("Not implemented.") - return cascaded_union(diffs) + self.solid_geometry=cascaded_union(diffs) def bounds(self): """ From a3ccbac3621009b14ea75257b353f6a4b8c048a1 Mon Sep 17 00:00:00 2001 From: Kamil Sopko Date: Tue, 23 Feb 2016 12:00:30 +0100 Subject: [PATCH 16/18] add set_all_inactive and set_inactive, to be able deselect objects mainly to avoid accidental delete --- ObjectCollection.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/ObjectCollection.py b/ObjectCollection.py index 005f2bcc..727358df 100644 --- a/ObjectCollection.py +++ b/ObjectCollection.py @@ -244,6 +244,27 @@ class ObjectCollection(QtCore.QAbstractListModel): iobj = self.createIndex(self.get_names().index(name), 0) # Column 0 self.view.selectionModel().select(iobj, QtGui.QItemSelectionModel.Select) + def set_inactive(self, name): + """ + Unselect object by name from the project list. This triggers the + list_selection_changed event and call on_list_selection_changed. + + :param name: Name of the FlatCAM Object + :return: None + """ + iobj = self.createIndex(self.get_names().index(name), 0) # Column 0 + self.view.selectionModel().select(iobj, QtGui.QItemSelectionModel.Deselect) + + def set_all_inactive(self): + """ + Unselect all objects from the project list. This triggers the + list_selection_changed event and call on_list_selection_changed. + + :return: None + """ + for name in self.get_names(): + self.set_inactive(name) + def on_list_selection_change(self, current, previous): FlatCAMApp.App.log.debug("on_list_selection_change()") FlatCAMApp.App.log.debug("Current: %s, Previous %s" % (str(current), str(previous))) From c3e544ac6c75eaa7fd347c81184adbbbd61b211d Mon Sep 17 00:00:00 2001 From: Kamil Sopko Date: Tue, 23 Feb 2016 12:21:57 +0100 Subject: [PATCH 17/18] FlatCAMObj - to_form,read_form,read_form_item cleanups for better debuging and cleanup Excellon merge method FlatCAMApp - fix accidentall delete issue, change calling to understand FlatCAMObj changes --- FlatCAMApp.py | 11 +++++--- FlatCAMObj.py | 74 ++++++++++++++++++++++++++++++++------------------- 2 files changed, 53 insertions(+), 32 deletions(-) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index ed30657b..4d36fc94 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -2774,6 +2774,8 @@ class App(QtCore.QObject): def delete(obj_name): try: + #deselect all to avoid delete selected object when run delete from shell + self.collection.set_all_inactive() self.collection.set_active(str(obj_name)) self.on_delete() except Exception, e: @@ -2815,7 +2817,7 @@ class App(QtCore.QObject): objs.append(obj) def initialize(obj, app): - FlatCAMExcellon.merge(objs, obj,True) + FlatCAMExcellon.merge(objs, obj) if objs is not None: self.new_object("excellon", obj_name, initialize) @@ -2880,14 +2882,14 @@ class App(QtCore.QObject): obj_init.offset([float(currentx), float(currenty)]), def initialize_local_excellon(obj_init, app): - FlatCAMExcellon.merge(obj, obj_init,True) + FlatCAMExcellon.merge(obj, obj_init) obj_init.offset([float(currentx), float(currenty)]), def initialize_geometry(obj_init, app): FlatCAMGeometry.merge(objs, obj_init) def initialize_excellon(obj_init, app): - FlatCAMExcellon.merge(objs, obj_init,True) + FlatCAMExcellon.merge(objs, obj_init) objs=[] if obj is not None: @@ -2909,7 +2911,8 @@ class App(QtCore.QObject): else: self.new_object("geometry", outname, initialize_geometry) - + #deselect all to avoid delete selected object when run delete from shell + self.collection.set_all_inactive() for delobj in objs: self.collection.set_active(delobj.options['name']) self.on_delete() diff --git a/FlatCAMObj.py b/FlatCAMObj.py index 69eea0de..c6043026 100644 --- a/FlatCAMObj.py +++ b/FlatCAMObj.py @@ -123,8 +123,12 @@ class FlatCAMObj(QtCore.QObject): :return: None """ + FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + "--> FlatCAMObj.to_form()") for option in self.options: - self.set_form_item(option) + try: + self.set_form_item(option) + except: + self.app.log.warning("Unexpected error:", sys.exc_info()) def read_form(self): """ @@ -135,7 +139,11 @@ class FlatCAMObj(QtCore.QObject): """ FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + "--> FlatCAMObj.read_form()") for option in self.options: - self.read_form_item(option) + try: + self.read_form_item(option) + except: + self.app.log.warning("Unexpected error:", sys.exc_info()) + def build_ui(self): """ @@ -191,11 +199,16 @@ class FlatCAMObj(QtCore.QObject): :type option: str :return: None """ - - try: - self.options[option] = self.form_fields[option].get_value() - except KeyError: - self.app.log.warning("Failed to read option from field: %s" % option) + #try read field only when option have equivalent in form_fields + if option in self.form_fields: + option_type=type(self.options[option]) + try: + value=self.form_fields[option].get_value() + #catch per option as it was ignored anyway, also when syntax error (probably uninitialized field),don't read either. + except (KeyError,SyntaxError): + self.app.log.warning("Failed to read option from field: %s" % option) + else: + self.app.log.warning("Form fied does not exists: %s" % option) def plot(self): """ @@ -631,13 +644,16 @@ class FlatCAMExcellon(FlatCAMObj, Excellon): self.ser_attrs += ['options', 'kind'] @staticmethod - def merge(exc_list, exc_final, copy_options): + def merge(exc_list, exc_final): """ - Merges(copy if used on one) the excellon of objects in exc_list into - options have same like exc_final - the geometry of geo_final. + Merge excellons in exc_list into exc_final. + Options are allways copied from source . - :param exc_list: List of FlatCAMExcellon Objects to join. + Tools are also merged, if name for tool is same and size differs, then as name is used next available number from both lists + + if only one object is specified in exc_list then this acts as copy only + + :param exc_list: List or one object of FlatCAMExcellon Objects to join. :param exc_final: Destination FlatCAMExcellon object. :return: None """ @@ -648,26 +664,27 @@ class FlatCAMExcellon(FlatCAMObj, Excellon): else: exc_list_real=exc_list - for exc in exc_list_real: # Expand lists if type(exc) is list: - FlatCAMExcellon.merge(exc, exc_final, copy_options) - - # If not list, just append + FlatCAMExcellon.merge(exc, exc_final) + # If not list, merge excellons else: - if copy_options is True: - exc_final.options["plot"]=exc.options["plot"] - exc_final.options["solid"]=exc.options["solid"] - exc_final.options["drillz"]=exc.options["drillz"] - exc_final.options["travelz"]=exc.options["travelz"] - exc_final.options["feedrate"]=exc.options["feedrate"] - exc_final.options["tooldia"]=exc.options["tooldia"] - exc_final.options["toolchange"]=exc.options["toolchange"] - exc_final.options["toolchangez"]=exc.options["toolchangez"] - exc_final.options["spindlespeed"]=exc.options["spindlespeed"] + # TODO: I realize forms does not save values into options , when object is deselected + # leave this here for future use + # this reinitialize options based on forms, all steps may not be necessary + # exc.app.collection.set_active(exc.options['name']) + # exc.to_form() + # exc.read_form() + for option in exc.options: + if option is not 'name': + try: + exc_final.options[option] = exc.options[option] + except: + exc.app.log.warning("Failed to copy option.",option) + #deep copy of all drills,to avoid any references for drill in exc.drills: point = Point(drill['point'].x,drill['point'].y) exc_final.drills.append({"point": point, "tool": drill['tool']}) @@ -679,7 +696,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon): max_numeric_tool=numeric_tool toolsrework[exc.tools[toolname]['C']]=toolname - #final as last becouse names from final tools will be used + #exc_final as last because names from final tools will be used for toolname in exc_final.tools.iterkeys(): numeric_tool=int(toolname) if numeric_tool>max_numeric_tool: @@ -692,9 +709,10 @@ class FlatCAMExcellon(FlatCAMObj, Excellon): exc_final.tools[str(max_numeric_tool+1)]={"C": toolvalues} else: exc_final.tools[toolsrework[toolvalues]]={"C": toolvalues} + #this value was not co + exc_final.zeros=exc.zeros exc_final.create_geometry() - def build_ui(self): FlatCAMObj.build_ui(self) From ba94aef0692d4d440dd7301bf3c9604cd4d3d861 Mon Sep 17 00:00:00 2001 From: Kamil Sopko Date: Wed, 24 Feb 2016 22:37:23 +0100 Subject: [PATCH 18/18] fix aligndrill and also logicical errors in it --- FlatCAMApp.py | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 4d36fc94..4d871376 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -2315,6 +2315,7 @@ class App(QtCore.QObject): 'axis': str, 'holes': str, 'grid': float, + 'minoffset': float, 'gridoffset': float, 'axisoffset': float, 'dia': float, @@ -2375,25 +2376,22 @@ class App(QtCore.QObject): else: axisoffset=0 - + #this will align hole to given aligngridoffset and minimal offset from pcb, based on selected axis if axis == "X": - firstpoint=-kwa['gridoffset']+xmin - #-5 - minlenght=(xmax-xmin+2*kwa['gridoffset']) - #57+10=67 - gridstripped=(minlenght//kwa['grid'])*kwa['grid'] - #67//10=60 - if (minlenght-gridstripped) >kwa['gridoffset']: - gridstripped=gridstripped+kwa['grid'] - lastpoint=(firstpoint+gridstripped) + firstpoint=kwa['gridoffset'] + while (xmin-kwa['minoffset'])lastpoint: + lastpoint=lastpoint+kwa['grid'] localHoles=(firstpoint,axisoffset),(lastpoint,axisoffset) else: - firstpoint=-kwa['gridoffset']+ymin - minlenght=(ymax-ymin+2*kwa['gridoffset']) - gridstripped=minlenght//kwa['grid']*kwa['grid'] - if (minlenght-gridstripped) >kwa['gridoffset']: - gridstripped=gridstripped+kwa['grid'] - lastpoint=(firstpoint+gridstripped) + firstpoint=kwa['gridoffset'] + while (ymin-kwa['minoffset'])lastpoint: + lastpoint=lastpoint+kwa['grid'] localHoles=(axisoffset,firstpoint),(axisoffset,lastpoint) for hole in localHoles: @@ -3151,12 +3149,13 @@ class App(QtCore.QObject): 'aligndrill': { 'fcn': aligndrill, 'help': "Create excellon with drills for aligment.\n" + - "> aligndrill [-dia <3.0 (float)>] -axis [-box [-grid <10 (float)> -gridoffset <5 (float)> [-axisoffset <0 (float)>]] | -dist ]\n" + + "> aligndrill [-dia <3.0 (float)>] -axis [-box -minoffset [-grid <10 (float)> -gridoffset <5 (float)> [-axisoffset <0 (float)>]] | -dist ]\n" + " name: Name of the object (Gerber or Excellon) to mirror.\n" + " dia: Tool diameter\n" + " box: Name of object which act as box (cutout for example.)\n" + " grid: aligning to grid, for thouse, who have aligning pins inside table in grid (-5,0),(5,0),(15,0)..." + - " gridoffset: offset from pcb from 0 position and minimal offset to grid on max" + + " gridoffset: offset of grid from 0 position" + + " minoffset: min and max distance between align hole and pcb" + " axisoffset: offset on second axis before aligment holes" + " axis: Mirror axis parallel to the X or Y axis.\n" + " dist: Distance of the mirror axis to the X or Y axis."