From 3941d03702e4498e174d77e5261db22033544105 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Fri, 25 Jan 2019 20:05:27 +0200 Subject: [PATCH] - fixed TclCommand Cutout - added a new TclCommand named CutoutAny. Keyword: cutout_any --- FlatCAMApp.py | 3 +- README.md | 4 +- tclCommands/TclCommandCutout.py | 47 ++++++-- tclCommands/TclCommandCutoutAny.py | 179 +++++++++++++++++++++++++++++ tclCommands/__init__.py | 1 + 5 files changed, 222 insertions(+), 12 deletions(-) create mode 100644 tclCommands/TclCommandCutoutAny.py diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 7bf12c5e..c107caa4 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -1086,7 +1086,8 @@ class App(QtCore.QObject): # Auto-complete KEYWORDS self.tcl_commands_list = ['add_circle', 'add_poly', 'add_polygon', 'add_polyline', 'add_rectangle', 'aligndrill', 'clear', - 'aligndrillgrid', 'cncjob', 'cutout', 'delete', 'drillcncjob', 'export_gcode', + 'aligndrillgrid', 'cncjob', 'cutout', 'cutout_any', 'delete', 'drillcncjob', + 'export_gcode', 'export_svg', 'ext', 'exteriors', 'follow', 'geo_union', 'geocutout', 'get_names', 'get_sys', 'getsys', 'help', 'import_svg', 'interiors', 'isolate', 'join_excellon', 'join_excellons', 'join_geometries', 'join_geometry', 'list_sys', 'listsys', 'mill', diff --git a/README.md b/README.md index a24e38ca..ff289806 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,10 @@ CAD program, and create G-Code for Isolation routing. - deleted junk folders - remade the Panelize Tool: now it is much faster, it is multi-threaded, it works with multitool geometries and it works with multigeo geometries too. -- make sure to copy the options attribute to the final object in the case of: FlatCAMGeometry.merge(), FlatCAMGerber.merge() and for the Panelize Tool +- made sure to copy the options attribute to the final object in the case of: FlatCAMGeometry.merge(), FlatCAMGerber.merge() and for the Panelize Tool - modified the panelize TclCommand to take advantage of the new panelize() function; added a 'threaded' parameter (default value is 1) which controls the execution of the panelize TclCommand: threaded or non-threaded +- fixed TclCommand Cutout +- added a new TclCommand named CutoutAny. Keyword: cutout_any 24.01.2019 diff --git a/tclCommands/TclCommandCutout.py b/tclCommands/TclCommandCutout.py index 40e83b01..4deb48ad 100644 --- a/tclCommands/TclCommandCutout.py +++ b/tclCommands/TclCommandCutout.py @@ -4,7 +4,7 @@ from tclCommands.TclCommand import TclCommand class TclCommandCutout(TclCommand): """ - Tcl shell command to create a board cutout geometry. + Tcl shell command to create a board cutout geometry. Rectangular shape only. example: @@ -33,13 +33,13 @@ class TclCommandCutout(TclCommand): # structured help for current command, args needs to be ordered help = { - 'main': 'Creates board cutout.', + 'main': 'Creates board cutout from an object (Gerber or Geometry) with a rectangular shape', 'args': collections.OrderedDict([ ('name', 'Name of the object.'), - ('dia', 'Tool diameter.'), - ('margin', 'Margin over bounds.'), - ('gapsize', 'size of gap.'), - ('gaps', 'type of gaps.'), + ('dia', 'Tool diameter. Default = 0.1'), + ('margin', 'Margin over bounds. Default = 0.001'), + ('gapsize', 'Size of gap. Default = 0.1'), + ('gaps', "Type of gaps. Can be: 'tb' = top-bottom, 'lr' = left-right and '4' = one each side. Default = 4"), ]), 'examples': [] } @@ -52,7 +52,32 @@ class TclCommandCutout(TclCommand): :return: """ - name = args['name'] + if 'name' in args: + name = args['name'] + else: + self.app.inform.emit( + "[warning]The name of the object for which cutout is done is missing. Add it and retry.") + return + + if 'margin' in args: + margin_par = args['margin'] + else: + margin_par = 0.001 + + if 'dia' in args: + dia_par = args['dia'] + else: + dia_par = 0.1 + + if 'gaps' in args: + gaps_par = args['gaps'] + else: + gaps_par = 4 + + if 'gapsize' in args: + gapsize_par = args['gapsize'] + else: + gapsize_par = 0.1 try: obj = self.app.collection.get_by_name(str(name)) @@ -60,8 +85,9 @@ class TclCommandCutout(TclCommand): return "Could not retrieve object: %s" % name def geo_init_me(geo_obj, app_obj): - margin = args['margin'] + args['dia'] / 2 - gap_size = args['dia'] + args['gapsize'] + margin = margin_par + dia_par / 2 + gap_size = dia_par + gapsize_par + minx, miny, maxx, maxy = obj.bounds() minx -= margin maxx += margin @@ -90,10 +116,11 @@ class TclCommandCutout(TclCommand): [pts[3], pts[4], pts[5]], [pts[6], pts[7], pts[8]], [pts[9], pts[10], pts[11]]]} - cuts = cases[args['gaps']] + cuts = cases[gaps_par] geo_obj.solid_geometry = cascaded_union([LineString(segment) for segment in cuts]) try: obj.app.new_object("geometry", name + "_cutout", geo_init_me) + self.app.inform.emit("[success]Rectangular-form Cutout operation finished.") except Exception as e: return "Operation failed: %s" % str(e) diff --git a/tclCommands/TclCommandCutoutAny.py b/tclCommands/TclCommandCutoutAny.py new file mode 100644 index 00000000..548e1efa --- /dev/null +++ b/tclCommands/TclCommandCutoutAny.py @@ -0,0 +1,179 @@ +from ObjectCollection import * +from tclCommands.TclCommand import TclCommand + + +class TclCommandCutoutAny(TclCommand): + """ + Tcl shell command to create a board cutout geometry. Allow cutout for any shape. + + example: + + """ + + # List of all command aliases, to be able use old + # names for backward compatibility (add_poly, add_polygon) + aliases = ['cutout_any', 'cut_any'] + + # Dictionary of types from Tcl command, needs to be ordered + arg_names = collections.OrderedDict([ + ('name', str), + ]) + + # Dictionary of types from Tcl command, needs to be ordered, + # this is for options like -optionname value + option_types = collections.OrderedDict([ + ('dia', float), + ('margin', float), + ('gapsize', float), + ('gaps', str) + ]) + + # array of mandatory options for current Tcl command: required = {'name','outname'} + required = ['name'] + + # structured help for current command, args needs to be ordered + help = { + 'main': 'Creates board cutout from an object (Gerber or Geometry) of any shape', + 'args': collections.OrderedDict([ + ('name', 'Name of the object.'), + ('dia', 'Tool diameter.'), + ('margin', 'Margin over bounds.'), + ('gapsize', 'size of gap.'), + ('gaps', "type of gaps. Can be: 'tb' = top-bottom, 'lr' = left-right, '2tb' = 2top-2bottom, " + "'2lr' = 2left-2right, '4' = 4 cuts, '8' = 8 cuts") + ]), + 'examples': [] + } + + def execute(self, args, unnamed_args): + """ + + :param args: + :param unnamed_args: + :return: + """ + + def subtract_rectangle(obj_, x0, y0, x1, y1): + pts = [(x0, y0), (x1, y0), (x1, y1), (x0, y1)] + obj_.subtract_polygon(pts) + + if 'name' in args: + name = args['name'] + else: + self.app.inform.emit( + "[warning]The name of the object for which cutout is done is missing. Add it and retry.") + return + + if 'margin' in args: + margin = args['margin'] + else: + margin = 0.001 + + if 'dia' in args: + dia = args['dia'] + else: + dia = 0.1 + + if 'gaps' in args: + gaps = args['gaps'] + else: + gaps = 4 + + if 'gapsize' in args: + gapsize = args['gapsize'] + else: + gapsize = 0.1 + + # Get source object. + try: + cutout_obj = self.app.collection.get_by_name(str(name)) + except: + return "Could not retrieve object: %s" % name + + if 0 in {dia}: + self.app.inform.emit("[warning]Tool Diameter is zero value. Change it to a positive integer.") + return "Tool Diameter is zero value. Change it to a positive integer." + + if gaps not in ['lr', 'tb', '2lr', '2tb', 4, 8]: + self.app.inform.emit("[warning]Gaps value can be only one of: 'lr', 'tb', '2lr', '2tb', 4 or 8. " + "Fill in a correct value and retry. ") + return + + # Get min and max data for each object as we just cut rectangles across X or Y + xmin, ymin, xmax, ymax = cutout_obj.bounds() + px = 0.5 * (xmin + xmax) + margin + py = 0.5 * (ymin + ymax) + margin + lenghtx = (xmax - xmin) + (margin * 2) + lenghty = (ymax - ymin) + (margin * 2) + + gapsize = gapsize + (dia / 2) + + if isinstance(cutout_obj, FlatCAMGeometry): + # rename the obj name so it can be identified as cutout + cutout_obj.options["name"] += "_cutout" + elif isinstance(cutout_obj, FlatCAMGerber): + cutout_obj.isolate(dia=dia, passes=1, overlap=1, combine=False, outname="_temp") + ext_obj = self.app.collection.get_by_name("_temp") + + def geo_init(geo_obj, app_obj): + geo_obj.solid_geometry = obj_exteriors + + outname = cutout_obj.options["name"] + "_cutout" + + obj_exteriors = ext_obj.get_exteriors() + self.app.new_object('geometry', outname, geo_init) + + self.app.collection.set_all_inactive() + self.app.collection.set_active("_temp") + self.app.on_delete() + + cutout_obj = self.app.collection.get_by_name(outname) + else: + self.app.inform.emit("[error]Cancelled. Object type is not supported.") + return + + try: + gaps_u = int(gaps) + except ValueError: + gaps_u = gaps + + if gaps_u == 8 or gaps_u == '2lr': + subtract_rectangle(cutout_obj, + xmin - gapsize, # botleft_x + py - gapsize + lenghty / 4, # botleft_y + xmax + gapsize, # topright_x + py + gapsize + lenghty / 4) # topright_y + subtract_rectangle(cutout_obj, + xmin - gapsize, + py - gapsize - lenghty / 4, + xmax + gapsize, + py + gapsize - lenghty / 4) + + if gaps_u == 8 or gaps_u == '2tb': + subtract_rectangle(cutout_obj, + px - gapsize + lenghtx / 4, + ymin - gapsize, + px + gapsize + lenghtx / 4, + ymax + gapsize) + subtract_rectangle(cutout_obj, + px - gapsize - lenghtx / 4, + ymin - gapsize, + px + gapsize - lenghtx / 4, + ymax + gapsize) + + if gaps_u == 4 or gaps_u == 'lr': + subtract_rectangle(cutout_obj, + xmin - gapsize, + py - gapsize, + xmax + gapsize, + py + gapsize) + + if gaps_u == 4 or gaps_u == 'tb': + subtract_rectangle(cutout_obj, + px - gapsize, + ymin - gapsize, + px + gapsize, + ymax + gapsize) + + cutout_obj.plot() + self.app.inform.emit("[success]Any-form Cutout operation finished.") diff --git a/tclCommands/__init__.py b/tclCommands/__init__.py index b06014d7..911a3203 100644 --- a/tclCommands/__init__.py +++ b/tclCommands/__init__.py @@ -12,6 +12,7 @@ import tclCommands.TclCommandAlignDrillGrid import tclCommands.TclCommandClearShell import tclCommands.TclCommandCncjob import tclCommands.TclCommandCutout +import tclCommands.TclCommandCutoutAny import tclCommands.TclCommandDelete import tclCommands.TclCommandDrillcncjob import tclCommands.TclCommandExportGcode