From a738ed730592a9c7f825ce3627c9ccd89d2b192b Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Wed, 1 Jan 2020 19:08:15 +0200 Subject: [PATCH 1/2] - in NCC Tool I've added a warning so the user is warned that the NCC margin has to have a value of at least the tool diameter that is doing an iso_op job in the Tool Table --- README.md | 2 +- flatcamTools/ToolNonCopperClear.py | 173 ++++++++++++++++----------- tclCommands/TclCommandDrillcncjob.py | 1 - 3 files changed, 106 insertions(+), 70 deletions(-) diff --git a/README.md b/README.md index b88766d1..d0c6fc60 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ CAD program, and create G-Code for Isolation routing. - fixed bug in NCC Tool: after trying to add a tool already in the Tool Table when trying to change the Tool Type the GUI does not change - final fix for app not quiting when running a script as argument, script that has the quit_flatcam Tcl command; fixed issue #360 - fixed issue #363. The Tcl command drillcncjob does not create tool cut, does not allow creation of gcode, it forces the usage of dwell and dwelltime parameters - +- in NCC Tool I've added a warning so the user is warned that the NCC margin has to have a value of at least the tool diameter that is doing an iso_op job in the Tool Table 30.12.2019 diff --git a/flatcamTools/ToolNonCopperClear.py b/flatcamTools/ToolNonCopperClear.py index 0a95370a..c8011073 100644 --- a/flatcamTools/ToolNonCopperClear.py +++ b/flatcamTools/ToolNonCopperClear.py @@ -1626,6 +1626,11 @@ class NonCopperClear(FlatCAMTool, Gerber): empty = self.get_ncc_empty_area(target=sol_geo, boundary=bounding_box) if empty == 'fail': return 'fail' + + if empty.is_empty: + app_obj.inform.emit('[ERROR_NOTCL] %s' % + _("Could not get the extent of the area to be non copper cleared.")) + return 'fail' elif isinstance(ncc_obj, FlatCAMGerber) and isotooldia: isolated_geo = [] @@ -1650,6 +1655,9 @@ class NonCopperClear(FlatCAMTool, Gerber): if isolated_geo == 'fail': app_obj.inform.emit('[ERROR_NOTCL] %s' % _("Isolation geometry could not be generated.")) else: + if ncc_margin < tool_iso: + app_obj.inform.emit('[WARNING_NOTCL] %s' % _("Isolation geometry is broken. Margin is less " + "than isolation tool diameter.")) try: for geo_elem in isolated_geo: # provide the app with a way to process the GUI events when in a blocking loop @@ -1723,26 +1731,28 @@ class NonCopperClear(FlatCAMTool, Gerber): if empty == 'fail': return 'fail' + if empty.is_empty: + app_obj.inform.emit('[ERROR_NOTCL] %s' % + _("Isolation geometry is broken. Margin is less than isolation tool diameter.")) + return 'fail' + elif isinstance(ncc_obj, FlatCAMGeometry): sol_geo = cascaded_union(ncc_obj.solid_geometry) if has_offset is True: - app_obj.inform.emit('[WARNING_NOTCL] %s ...' % - _("Buffering")) + app_obj.inform.emit('[WARNING_NOTCL] %s ...' % _("Buffering")) sol_geo = sol_geo.buffer(distance=ncc_offset) - app_obj.inform.emit('[success] %s ...' % - _("Buffering finished")) + app_obj.inform.emit('[success] %s ...' % _("Buffering finished")) empty = self.get_ncc_empty_area(target=sol_geo, boundary=bounding_box) if empty == 'fail': return 'fail' - else: - app_obj.inform.emit('[ERROR_NOTCL] %s' % - _('The selected object is not suitable for copper clearing.')) - return + if empty.is_empty: + app_obj.inform.emit('[ERROR_NOTCL] %s' % + _("Could not get the extent of the area to be non copper cleared.")) + return 'fail' - if empty.is_empty: - app_obj.inform.emit('[ERROR_NOTCL] %s' % - _("Could not get the extent of the area to be non copper cleared.")) + else: + app_obj.inform.emit('[ERROR_NOTCL] %s' % _('The selected object is not suitable for copper clearing.')) return 'fail' if type(empty) is Polygon: @@ -1762,11 +1772,8 @@ class NonCopperClear(FlatCAMTool, Gerber): # provide the app with a way to process the GUI events when in a blocking loop QtWidgets.QApplication.processEvents() - app_obj.inform.emit( - '[success] %s %s%s %s' % (_('NCC Tool clearing with tool diameter = '), - str(tool), - units.lower(), - _('started.')) + app_obj.inform.emit('[success] %s = %s%s %s' % ( + _('NCC Tool clearing with tool diameter'), str(tool), units.lower(), _('started.')) ) app_obj.proc_container.update_view_text(' %d%%' % 0) @@ -1836,7 +1843,7 @@ class NonCopperClear(FlatCAMTool, Gerber): poly_processed.append(False) log.warning("Polygon in MultiPolygon can not be cleared.") else: - log.warning("Geo in Iterable can not be cleared beacuse it is not Polygon. " + log.warning("Geo in Iterable can not be cleared because it is not Polygon. " "It is: %s" % str(type(pol))) except TypeError: if isinstance(p, Polygon): @@ -1942,32 +1949,59 @@ class NonCopperClear(FlatCAMTool, Gerber): if geo_obj.tools[tooluid]['solid_geometry']: has_solid_geo += 1 if has_solid_geo == 0: - app_obj.inform.emit('[ERROR] %s' % _("There is no NCC Geometry in the file.\n" - "Usually it means that the tool diameter is too big " - "for the painted geometry.\n" - "Change the painting parameters and try again.")) - return + app_obj.inform.emit('[ERROR] %s' % + _("There is no NCC Geometry in the file.\n" + "Usually it means that the tool diameter is too big for the painted geometry.\n" + "Change the painting parameters and try again.")) + return 'fail' + + # check to see if geo_obj.tools is empty + # it will be updated only if there is a solid_geometry for tools + if geo_obj.tools: + if warning_flag == 0: + self.app.inform.emit('[success] %s' % _("NCC Tool clear all done.")) + else: + self.app.inform.emit('[WARNING] %s: %s %s.' % ( + _("NCC Tool clear all done but the copper features isolation is broken for"), + str(warning_flag), + _("tools"))) + return + + # create the solid_geometry + geo_obj.solid_geometry = list() + for tooluid in geo_obj.tools: + if geo_obj.tools[tooluid]['solid_geometry']: + try: + for geo in geo_obj.tools[tooluid]['solid_geometry']: + geo_obj.solid_geometry.append(geo) + except TypeError: + geo_obj.solid_geometry.append(geo_obj.tools[tooluid]['solid_geometry']) + else: + # I will use this variable for this purpose although it was meant for something else + # signal that we have no geo in the object therefore don't create it + app_obj.poly_not_cleared = False + return "fail" # create the solid_geometry - geo_obj.solid_geometry = list() - for tooluid in geo_obj.tools: - if geo_obj.tools[tooluid]['solid_geometry']: - try: - for geo in geo_obj.tools[tooluid]['solid_geometry']: - geo_obj.solid_geometry.append(geo) - except TypeError: - geo_obj.solid_geometry.append(geo_obj.tools[tooluid]['solid_geometry']) - - # Experimental... - # print("Indexing...", end=' ') - # geo_obj.make_index() - if warning_flag == 0: - self.app.inform.emit('[success] %s' % _("NCC Tool clear all done.")) - else: - self.app.inform.emit('[WARNING] %s: %s %s.' % (_("NCC Tool clear all done but the copper features " - "isolation is broken for"), - str(warning_flag), - _("tools"))) + # geo_obj.solid_geometry = list() + # for tooluid in geo_obj.tools: + # if geo_obj.tools[tooluid]['solid_geometry']: + # try: + # for geo in geo_obj.tools[tooluid]['solid_geometry']: + # geo_obj.solid_geometry.append(geo) + # except TypeError: + # geo_obj.solid_geometry.append(geo_obj.tools[tooluid]['solid_geometry']) + # + # # Experimental... + # # print("Indexing...", end=' ') + # # geo_obj.make_index() + # if warning_flag == 0: + # self.app.inform.emit('[success] %s' % _("NCC Tool clear all done.")) + # else: + # self.app.inform.emit('[WARNING] %s: %s %s.' % ( + # _("NCC Tool clear all done but the copper features isolation is broken for"), + # str(warning_flag), + # _("tools"))) # ########################################################################################### # Initializes the new geometry object for the case of the rest-machining #################### @@ -2009,15 +2043,17 @@ class NonCopperClear(FlatCAMTool, Gerber): if isinstance(ncc_obj, FlatCAMGerber) and not isotooldia: sol_geo = ncc_obj.solid_geometry if has_offset is True: - app_obj.inform.emit('[WARNING_NOTCL] %s ...' % - _("Buffering")) + app_obj.inform.emit('[WARNING_NOTCL] %s ...' % _("Buffering")) sol_geo = sol_geo.buffer(distance=ncc_offset) - app_obj.inform.emit('[success] %s ...' % - _("Buffering finished")) + app_obj.inform.emit('[success] %s ...' % _("Buffering finished")) empty = self.get_ncc_empty_area(target=sol_geo, boundary=bounding_box) if empty == 'fail': return 'fail' + if empty.is_empty: + app_obj.inform.emit('[ERROR_NOTCL] %s' % + _("Could not get the extent of the area to be non copper cleared.")) + return 'fail' elif isinstance(ncc_obj, FlatCAMGerber) and isotooldia: isolated_geo = [] self.solid_geometry = ncc_obj.solid_geometry @@ -2036,6 +2072,9 @@ class NonCopperClear(FlatCAMTool, Gerber): if isolated_geo == 'fail': app_obj.inform.emit('[ERROR_NOTCL] %s' % _("Isolation geometry could not be generated.")) else: + app_obj.inform.emit('[WARNING_NOTCL] %s' % _("Isolation geometry is broken. Margin is less " + "than isolation tool diameter.")) + try: for geo_elem in isolated_geo: # provide the app with a way to process the GUI events when in a blocking loop @@ -2105,35 +2144,35 @@ class NonCopperClear(FlatCAMTool, Gerber): sol_geo = cascaded_union(isolated_geo) if has_offset is True: - app_obj.inform.emit('[WARNING_NOTCL] %s ...' % - _("Buffering")) + app_obj.inform.emit('[WARNING_NOTCL] %s ...' % _("Buffering")) sol_geo = sol_geo.buffer(distance=ncc_offset) - app_obj.inform.emit('[success] %s ...' % - _("Buffering finished")) + app_obj.inform.emit('[success] %s ...' % _("Buffering finished")) empty = self.get_ncc_empty_area(target=sol_geo, boundary=bounding_box) if empty == 'fail': return 'fail' + if empty.is_empty: + app_obj.inform.emit('[ERROR_NOTCL] %s' % + _("Isolation geometry is broken. Margin is less than isolation tool diameter.")) + return 'fail' + elif isinstance(ncc_obj, FlatCAMGeometry): sol_geo = cascaded_union(ncc_obj.solid_geometry) if has_offset is True: - app_obj.inform.emit('[WARNING_NOTCL] %s ...' % - _("Buffering")) + app_obj.inform.emit('[WARNING_NOTCL] %s ...' % _("Buffering")) sol_geo = sol_geo.buffer(distance=ncc_offset) - app_obj.inform.emit('[success] %s ...' % - _("Buffering finished")) + app_obj.inform.emit('[success] %s ...' % _("Buffering finished")) empty = self.get_ncc_empty_area(target=sol_geo, boundary=bounding_box) if empty == 'fail': return 'fail' - else: - app_obj.inform.emit('[ERROR_NOTCL] %s' % - _('The selected object is not suitable for copper clearing.')) - return - if empty.is_empty: - app_obj.inform.emit('[ERROR_NOTCL] %s' % - _("Could not get the extent of the area to be non copper cleared.")) - return 'fail' + if empty.is_empty: + app_obj.inform.emit('[ERROR_NOTCL] %s' % + _("Could not get the extent of the area to be non copper cleared.")) + return 'fail' + else: + app_obj.inform.emit('[ERROR_NOTCL] %s' % _('The selected object is not suitable for copper clearing.')) + return if self.app.abort_flag: # graceful abort requested by the user @@ -2156,11 +2195,8 @@ class NonCopperClear(FlatCAMTool, Gerber): tool = sorted_tools.pop(0) log.debug("Starting geometry processing for tool: %s" % str(tool)) - app_obj.inform.emit( - '[success] %s %s%s %s' % (_('NCC Tool clearing with tool diameter = '), - str(tool), - units.lower(), - _('started.')) + app_obj.inform.emit('[success] %s = %s%s %s' % ( + _('NCC Tool clearing with tool diameter'), str(tool), units.lower(), _('started.')) ) app_obj.proc_container.update_view_text(' %d%%' % 0) @@ -2240,7 +2276,8 @@ class NonCopperClear(FlatCAMTool, Gerber): elif isinstance(p, MultiPolygon): for poly in p: if poly is not None: - # provide the app with a way to process the GUI events when in a blocking loop + # provide the app with a way to process the GUI events when + # in a blocking loop QtWidgets.QApplication.processEvents() try: @@ -2337,7 +2374,7 @@ class NonCopperClear(FlatCAMTool, Gerber): self.app.inform.emit( '[WARNING] %s: %s %s.' % (_("NCC Tool Rest Machining clear all done but the copper features " "isolation is broken for"), str(warning_flag), _("tools"))) - return + return # create the solid_geometry geo_obj.solid_geometry = list() diff --git a/tclCommands/TclCommandDrillcncjob.py b/tclCommands/TclCommandDrillcncjob.py index 511e2862..3a85563e 100644 --- a/tclCommands/TclCommandDrillcncjob.py +++ b/tclCommands/TclCommandDrillcncjob.py @@ -221,7 +221,6 @@ class TclCommandDrillcncjob(TclCommandSignaled): # for now there is no tool offset support in this Tcl Command so we write the 0.0 value here job_obj.tool_offset[t_item] = 0.0 - print(job_obj.tool_offset) job_obj.origin_kind = 'excellon' job_obj.gcode_parse() From 779a6a75d4e55a148a5c4effd2ac920c4e29ae98 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Wed, 1 Jan 2020 20:42:59 +0200 Subject: [PATCH 2/2] - modified the Drillcncjob and Cncjob Tcl commands to be allowed to work without the 'dwell' and 'toolchange' arguments. If 'dwelltime' argument is present it will be assumed that the 'dwell' is True and the same for 'toolchangez' parameter, if present then 'toolchange' will be assumed to be True, else False --- README.md | 1 + tclCommands/TclCommandCncjob.py | 42 ++++++++++++++++++++-------- tclCommands/TclCommandDrillcncjob.py | 42 ++++++++++++++++++---------- 3 files changed, 58 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index d0c6fc60..5004a6bc 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ CAD program, and create G-Code for Isolation routing. - final fix for app not quiting when running a script as argument, script that has the quit_flatcam Tcl command; fixed issue #360 - fixed issue #363. The Tcl command drillcncjob does not create tool cut, does not allow creation of gcode, it forces the usage of dwell and dwelltime parameters - in NCC Tool I've added a warning so the user is warned that the NCC margin has to have a value of at least the tool diameter that is doing an iso_op job in the Tool Table +- modified the Drillcncjob and Cncjob Tcl commands to be allowed to work without the 'dwell' and 'toolchange' arguments. If 'dwelltime' argument is present it will be assumed that the 'dwell' is True and the same for 'toolchangez' parameter, if present then 'toolchange' will be assumed to be True, else False 30.12.2019 diff --git a/tclCommands/TclCommandCncjob.py b/tclCommands/TclCommandCncjob.py index 3b08e8f6..53e294ce 100644 --- a/tclCommands/TclCommandCncjob.py +++ b/tclCommands/TclCommandCncjob.py @@ -37,13 +37,11 @@ class TclCommandCncjob(TclCommandSignaled): ('extracut', bool), ('extracut_length', float), ('depthperpass', float), - ('toolchange', int), ('toolchangez', float), ('toolchangexy', tuple), ('startz', float), ('endz', float), ('spindlespeed', int), - ('dwell', bool), ('dwelltime', float), ('pp', str), ('muted', int), @@ -69,13 +67,14 @@ class TclCommandCncjob(TclCommandSignaled): ('extracut', 'The value for extra cnccut over the first point in path,in the job end; float'), ('depthperpass', 'Height of one layer for multidepth.'), ('toolchange', 'Enable tool changes (example: True).'), - ('toolchangez', 'Z distance for toolchange (example: 30.0).'), + ('toolchangez', 'Z distance for toolchange (example: 30.0).\n' + 'If used in the command then a toolchange event will be included in gcode'), ('toolchangexy', 'X, Y coordonates for toolchange in format (x, y) (example: (2.0, 3.1) ).'), ('startz', 'Height before the first move.'), ('endz', 'Height where the last move will park.'), ('spindlespeed', 'Speed of the spindle in rpm (example: 4000).'), - ('dwell', 'True or False; use (or not) the dwell'), - ('dwelltime', 'Time to pause to allow the spindle to reach the full speed'), + ('dwelltime', 'Time to pause to allow the spindle to reach the full speed.\n' + 'If it is not used in command then it will not be included'), ('outname', 'Name of the resulting Geometry object.'), ('pp', 'Name of the Geometry preprocessor. No quotes, case sensitive'), ('muted', 'It will not put errors in the Shell.') @@ -138,8 +137,12 @@ class TclCommandCncjob(TclCommandSignaled): args["multidepth"] = bool(args["multidepth"]) if "multidepth" in args else obj.options["multidepth"] args["extracut"] = bool(args["extracut"]) if "extracut" in args else obj.options["extracut"] - args["extracut_length"] = float(args["extracut_length"]) if "extracut_length" in args else \ - obj.options["extracut_length"] + + if "extracut_length" in args: + args["extracut_length"] = float(args["extracut_length"]) + else: + args["extracut_length"] = 0.0 + args["depthperpass"] = args["depthperpass"] if "depthperpass" in args and args["depthperpass"] else \ obj.options["depthperpass"] @@ -148,14 +151,29 @@ class TclCommandCncjob(TclCommandSignaled): args["endz"] = args["endz"] if "endz" in args and args["endz"] else obj.options["endz"] args["spindlespeed"] = args["spindlespeed"] if "spindlespeed" in args and args["spindlespeed"] != 0 else None - args["dwell"] = bool(args["dwell"]) if "dwell" in args else obj.options["dwell"] - args["dwelltime"] = args["dwelltime"] if "dwelltime" in args and args["dwelltime"] else obj.options["dwelltime"] + + if 'dwelltime' in args: + args["dwell"] = True + if args['dwelltime'] is not None: + args["dwelltime"] = float(args['dwelltime']) + else: + args["dwelltime"] = float(obj.options["dwelltime"]) + else: + args["dwell"] = False + args["dwelltime"] = 0.0 args["pp"] = args["pp"] if "pp" in args and args["pp"] else obj.options["ppname_g"] - args["toolchange"] = True if "toolchange" in args and args["toolchange"] == 1 else False - args["toolchangez"] = args["toolchangez"] if "toolchangez" in args and args["toolchangez"] else \ - obj.options["toolchangez"] + if "toolchangez" in args: + args["toolchange"] = True + if args["toolchangez"] is not None: + args["toolchangez"] = args["toolchangez"] + else: + args["toolchangez"] = obj.options["toolchangez"] + else: + args["toolchange"] = False + args["toolchangez"] = 0.0 + args["toolchangexy"] = args["toolchangexy"] if "toolchangexy" in args and args["toolchangexy"] else \ self.app.defaults["geometry_toolchangexy"] diff --git a/tclCommands/TclCommandDrillcncjob.py b/tclCommands/TclCommandDrillcncjob.py index 3a85563e..8178d9ad 100644 --- a/tclCommands/TclCommandDrillcncjob.py +++ b/tclCommands/TclCommandDrillcncjob.py @@ -48,18 +48,20 @@ class TclCommandDrillcncjob(TclCommandSignaled): 'args': collections.OrderedDict([ ('name', 'Name of the source object.'), ('drilled_dias', - 'Comma separated tool diameters of the drills to be drilled (example: 0.6,1.0 or 3.125). No space allowed'), + 'Comma separated tool diameters of the drills to be drilled (example: 0.6,1.0 or 3.125). ' + 'No space allowed'), ('drillz', 'Drill depth into material (example: -2.0).'), ('travelz', 'Travel distance above material (example: 2.0).'), ('feedrate', 'Drilling feed rate.'), ('feedrate_rapid', 'Rapid drilling feed rate.'), ('spindlespeed', 'Speed of the spindle in rpm (example: 4000).'), ('toolchange', 'Enable tool changes (example: True).'), - ('toolchangez', 'Z distance for toolchange (example: 30.0).'), + ('toolchangez', 'Z distance for toolchange (example: 30.0).\n' + 'If used in the command then a toolchange event will be included in gcode'), ('toolchangexy', 'X, Y coordonates for toolchange in format (x, y) (example: (2.0, 3.1) ).'), ('endz', 'Z distance at job end (example: 30.0).'), - ('dwell', 'True or False; use (or not) the dwell'), - ('dwelltime', 'Time to pause to allow the spindle to reach the full speed'), + ('dwelltime', 'Time to pause to allow the spindle to reach the full speed.\n' + 'If it is not used in command then it will not be included'), ('pp', 'This is the Excellon preprocessor name: case_sensitive, no_quotes'), ('outname', 'Name of the resulting Geometry object.'), ('opt_type', 'Name of move optimization type. B by default for Basic OR-Tools, M for Metaheuristic OR-Tools' @@ -73,7 +75,7 @@ class TclCommandDrillcncjob(TclCommandSignaled): ('muted', 'It will not put errors in the Shell or status bar.') ]), 'examples': ['drillcncjob test.TXT -drillz -1.5 -travelz 14 -feedrate 222 -feedrate_rapid 456 -spindlespeed 777' - ' -toolchange True -toolchangez 33 -endz 22 -pp default\n' + ' -toolchangez 33 -endz 22 -pp default\n' 'Usage of -feedrate_rapid matter only when the preprocessor is using it, like -marlin-.'] } @@ -118,7 +120,6 @@ class TclCommandDrillcncjob(TclCommandSignaled): def job_init(job_obj, app_obj): # tools = args["tools"] if "tools" in args else 'all' - units = self.app.defaults['units'].upper() try: if 'drilled_dias' in args and args['drilled_dias'] != 'all': @@ -171,11 +172,19 @@ class TclCommandDrillcncjob(TclCommandSignaled): else: return "fail" - drillz = args["drillz"] if "drillz" in args and args["drillz"] else obj.options["drillz"] - toolchangez = args["toolchangez"] if "toolchangez" in args and args["toolchangez"] else \ - obj.options["toolchangez"] - endz = args["endz"] if "endz" in args and args["endz"] else obj.options["endz"] - toolchange = True if "toolchange" in args and bool(args["toolchange"]) is True else False + drillz = args["drillz"] if "drillz" in args and args["drillz"] is not None else obj.options["drillz"] + + if "toolchangez" in args: + toolchange = True + if args["toolchangez"] is not None: + toolchangez = args["toolchangez"] + else: + toolchangez = obj.options["toolchangez"] + else: + toolchange = False + toolchangez = 0.0 + + endz = args["endz"] if "endz" in args and args["endz"] is not None else obj.options["endz"] opt_type = args["opt_type"] if "opt_type" in args and args["opt_type"] else 'B' job_obj.z_move = args["travelz"] if "travelz" in args and args["travelz"] else obj.options["travelz"] @@ -183,12 +192,15 @@ class TclCommandDrillcncjob(TclCommandSignaled): job_obj.feedrate_rapid = args["feedrate_rapid"] \ if "feedrate_rapid" in args and args["feedrate_rapid"] else obj.options["feedrate_rapid"] - if bool(args['dwell']) and args['dwelltime']: + if 'dwelltime' in args: job_obj.dwell = True - job_obj.dwelltime = float(args['dwelltime']) + if args['dwelltime'] is not None: + job_obj.dwelltime = float(args['dwelltime']) + else: + job_obj.dwelltime = float(obj.options["dwelltime"]) else: - job_obj.dwell = obj.options["dwell"] - job_obj.dwelltime = float(obj.options["dwelltime"]) + job_obj.dwell = False + job_obj.dwelltime = 0.0 job_obj.spindlespeed = args["spindlespeed"] if "spindlespeed" in args else None job_obj.pp_excellon_name = args["pp"] if "pp" in args and args["pp"] \