From 74b3a38a71252b7af9d2c36b5fffae53f0615161 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Tue, 17 Dec 2019 01:38:39 +0200 Subject: [PATCH] - more optimizations in NCC Tool - optimizations in Paint Tool - maximum range for Cut Z is now zero to deal with the situation when using V-shape with tip-dia same value with cut width --- README.md | 6 + flatcamGUI/ObjectUI.py | 2 +- flatcamGUI/PreferencesUI.py | 16 +- flatcamTools/ToolNonCopperClear.py | 13 +- flatcamTools/ToolPaint.py | 228 +++++++++++++++++++++-------- 5 files changed, 192 insertions(+), 73 deletions(-) diff --git a/README.md b/README.md index c0adcc5e..39817398 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,12 @@ CAD program, and create G-Code for Isolation routing. ================================================= +17.12.2019 + +- more optimizations in NCC Tool +- optimizations in Paint Tool +- maximum range for Cut Z is now zero to deal with the situation when using V-shape with tip-dia same value with cut width + 16.12.2019 - in Geometry Editor added support for Jump To function such as that it works within the Editor Tools themselves. For now it works only in absolute jumps diff --git a/flatcamGUI/ObjectUI.py b/flatcamGUI/ObjectUI.py index c5df313d..8cf74fc1 100644 --- a/flatcamGUI/ObjectUI.py +++ b/flatcamGUI/ObjectUI.py @@ -348,7 +348,7 @@ class GerberObjectUI(ObjectUI): "below the copper surface.") ) self.cutz_spinner = FCDoubleSpinner() - self.cutz_spinner.set_range(-9999.9999, -0.0001) + self.cutz_spinner.set_range(-9999.9999, 0.0000) self.cutz_spinner.set_precision(self.decimals) self.cutz_spinner.setSingleStep(0.1) self.cutz_spinner.setWrapping(True) diff --git a/flatcamGUI/PreferencesUI.py b/flatcamGUI/PreferencesUI.py index 4e1c1c28..cb033589 100644 --- a/flatcamGUI/PreferencesUI.py +++ b/flatcamGUI/PreferencesUI.py @@ -1682,7 +1682,7 @@ class GerberAdvOptPrefGroupUI(OptionsGroupUI): ) self.cutz_spinner = FCDoubleSpinner() self.cutz_spinner.set_precision(self.decimals) - self.cutz_spinner.set_range(-99.9999, -0.0001) + self.cutz_spinner.set_range(-99.9999, 0.0000) self.cutz_spinner.setSingleStep(0.1) self.cutz_spinner.setWrapping(True) @@ -2352,7 +2352,7 @@ class ExcellonOptPrefGroupUI(OptionsGroupUI): self.cutz_entry = FCDoubleSpinner() if machinist_setting == 0: - self.cutz_entry.set_range(-9999.9999, -0.000001) + self.cutz_entry.set_range(-9999.9999, 0.0000) else: self.cutz_entry.set_range(-9999.9999, 9999.9999) @@ -2617,7 +2617,7 @@ class ExcellonAdvOptPrefGroupUI(OptionsGroupUI): ) self.pdepth_entry = FCDoubleSpinner() self.pdepth_entry.set_precision(self.decimals) - self.pdepth_entry.set_range(-99999, -0.000001) + self.pdepth_entry.set_range(-99999.9999, 0.0000) grid1.addWidget(self.pdepth_label, 4, 0) grid1.addWidget(self.pdepth_entry, 4, 1) @@ -3196,7 +3196,7 @@ class GeometryOptPrefGroupUI(OptionsGroupUI): self.cutz_entry = FCDoubleSpinner() if machinist_setting == 0: - self.cutz_entry.set_range(-9999.9999, -0.000001) + self.cutz_entry.set_range(-9999.9999, 0.0000) else: self.cutz_entry.set_range(-9999.9999, 9999.9999) @@ -3486,7 +3486,7 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI): "to probe. Negative value, in current units.") ) self.pdepth_entry = FCDoubleSpinner() - self.pdepth_entry.set_range(-99999, -0.000001) + self.pdepth_entry.set_range(-99999, 0.0000) self.pdepth_entry.set_precision(self.decimals) self.pdepth_entry.setSingleStep(0.1) self.pdepth_entry.setWrapping(True) @@ -4052,7 +4052,7 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI): ) self.cutz_entry = FCDoubleSpinner() self.cutz_entry.set_precision(self.decimals) - self.cutz_entry.set_range(-9999.9999, -0.000001) + self.cutz_entry.set_range(-9999.9999, 0.0000) self.cutz_entry.setSingleStep(0.1) self.cutz_entry.setToolTip( @@ -4310,7 +4310,7 @@ class ToolsCutoutPrefGroupUI(OptionsGroupUI): self.cutz_entry.set_precision(self.decimals) if machinist_setting == 0: - self.cutz_entry.setRange(-9999.9999, -0.00001) + self.cutz_entry.setRange(-9999.9999, 0.0000) else: self.cutz_entry.setRange(-9999.9999, 9999.9999) @@ -5119,7 +5119,7 @@ class ToolsCalculatorsPrefGroupUI(OptionsGroupUI): # ## Depth-of-cut Cut Z self.cut_z_entry = FCDoubleSpinner() - self.cut_z_entry.set_range(-9999.9999, -0.00001) + self.cut_z_entry.set_range(-9999.9999, 0.0000) self.cut_z_entry.set_precision(self.decimals) self.cut_z_entry.setSingleStep(0.01) diff --git a/flatcamTools/ToolNonCopperClear.py b/flatcamTools/ToolNonCopperClear.py index 07c99879..a5f874b4 100644 --- a/flatcamTools/ToolNonCopperClear.py +++ b/flatcamTools/ToolNonCopperClear.py @@ -258,7 +258,7 @@ class NonCopperClear(FlatCAMTool, Gerber): ) self.cutz_entry = FCDoubleSpinner() self.cutz_entry.set_precision(self.decimals) - self.cutz_entry.set_range(-99999, -0.00000000000001) + self.cutz_entry.set_range(-99999.9999, 0.0000) self.cutz_entry.setToolTip( _("Depth of cut into material. Negative value.\n" @@ -1810,7 +1810,11 @@ class NonCopperClear(FlatCAMTool, Gerber): if self.app.abort_flag: # graceful abort requested by the user raise FlatCAMApp.GracefulException - if p is not None: + + # clean the polygon + p = p.buffer(0) + + if p is not None and p.is_valid: poly_processed = list() try: for pol in p: @@ -2201,7 +2205,10 @@ class NonCopperClear(FlatCAMTool, Gerber): # graceful abort requested by the user raise FlatCAMApp.GracefulException - if p is not None: + # clean the polygon + p = p.buffer(0) + + if p is not None and p.is_valid: # provide the app with a way to process the GUI events when in a blocking loop QtWidgets.QApplication.processEvents() diff --git a/flatcamTools/ToolPaint.py b/flatcamTools/ToolPaint.py index 1d04def1..20756dbe 100644 --- a/flatcamTools/ToolPaint.py +++ b/flatcamTools/ToolPaint.py @@ -1450,8 +1450,7 @@ class ToolPaint(FlatCAMTool, Gerber): geo_obj.solid_geometry += list(cpoly.get_objects()) return cpoly else: - app_obj.inform.emit('[ERROR_NOTCL] %s' % - _('Geometry could not be painted completely')) + app_obj.inform.emit('[ERROR_NOTCL] %s' % _('Geometry could not be painted completely')) return None current_uid = int(1) @@ -1769,62 +1768,162 @@ class ToolPaint(FlatCAMTool, Gerber): pol_nr = 0 for geo in painted_area: - try: - # Polygons are the only really paintable geometries, lines in theory have no area to be painted - if not isinstance(geo, Polygon): + + # provide the app with a way to process the GUI events when in a blocking loop + QtWidgets.QApplication.processEvents() + + if self.app.abort_flag: + # graceful abort requested by the user + raise FlatCAMApp.GracefulException + + # try to clean the Polygon but it may result into a MultiPolygon + geo = geo.buffer(0) + poly_buf = geo.buffer(-paint_margin) + + if geo is not None and geo.is_valid: + poly_processed = list() + try: + for pol in poly_buf: + if pol is not None and isinstance(pol, Polygon): + if paint_method == 'standard': + cp = self.clear_polygon(pol, + tooldia=tool_dia, + steps_per_circle=self.app.defaults[ + "geometry_circle_steps"], + overlap=over, + contour=cont, + connect=conn, + prog_plot=prog_plot) + elif paint_method == 'seed': + cp = self.clear_polygon2(pol, + tooldia=tool_dia, + steps_per_circle=self.app.defaults[ + "geometry_circle_steps"], + overlap=over, + contour=cont, + connect=conn, + prog_plot=prog_plot) + else: + cp = self.clear_polygon3(pol, + tooldia=tool_dia, + steps_per_circle=self.app.defaults[ + "geometry_circle_steps"], + overlap=over, + contour=cont, + connect=conn, + prog_plot=prog_plot) + if cp: + total_geometry += list(cp.get_objects()) + poly_processed.append(True) + else: + poly_processed.append(False) + log.warning("Polygon in MultiPolygon can not be cleared.") + else: + log.warning("Geo in Iterable can not be cleared because it is not Polygon. " + "It is: %s" % str(type(pol))) + except TypeError: + if isinstance(poly_buf, Polygon): + if paint_method == 'standard': + cp = self.clear_polygon(poly_buf, + tooldia=tool_dia, + steps_per_circle=self.app.defaults[ + "geometry_circle_steps"], + overlap=over, + contour=cont, + connect=conn, + prog_plot=prog_plot) + elif paint_method == 'seed': + cp = self.clear_polygon2(poly_buf, + tooldia=tool_dia, + steps_per_circle=self.app.defaults[ + "geometry_circle_steps"], + overlap=over, + contour=cont, + connect=conn, + prog_plot=prog_plot) + else: + cp = self.clear_polygon3(poly_buf, + tooldia=tool_dia, + steps_per_circle=self.app.defaults[ + "geometry_circle_steps"], + overlap=over, + contour=cont, + connect=conn, + prog_plot=prog_plot) + if cp: + total_geometry += list(cp.get_objects()) + poly_processed.append(True) + else: + poly_processed.append(False) + log.warning("Polygon can not be cleared.") + else: + log.warning("Geo can not be cleared because it is: %s" % str(type(poly_buf))) + + p_cleared = poly_processed.count(True) + p_not_cleared = poly_processed.count(False) + + if p_not_cleared: + app_obj.poly_not_cleared = True + + if p_cleared == 0: continue - poly_buf = geo.buffer(-paint_margin) - if paint_method == "seed": - # Type(cp) == FlatCAMRTreeStorage | None - cp = self.clear_polygon2(poly_buf, - tooldia=tool_dia, - steps_per_circle=self.app.defaults["geometry_circle_steps"], - overlap=over, - contour=cont, - connect=conn, - prog_plot=prog_plot) + # try: + # # Polygons are the only really paintable geometries, lines in theory have no area to be painted + # if not isinstance(geo, Polygon): + # continue + # poly_buf = geo.buffer(-paint_margin) + # + # if paint_method == "seed": + # # Type(cp) == FlatCAMRTreeStorage | None + # cp = self.clear_polygon2(poly_buf, + # tooldia=tool_dia, + # steps_per_circle=self.app.defaults["geometry_circle_steps"], + # overlap=over, + # contour=cont, + # connect=conn, + # prog_plot=prog_plot) + # + # elif paint_method == "lines": + # # Type(cp) == FlatCAMRTreeStorage | None + # cp = self.clear_polygon3(poly_buf, + # tooldia=tool_dia, + # steps_per_circle=self.app.defaults["geometry_circle_steps"], + # overlap=over, + # contour=cont, + # connect=conn, + # prog_plot=prog_plot) + # + # else: + # # Type(cp) == FlatCAMRTreeStorage | None + # cp = self.clear_polygon(poly_buf, + # tooldia=tool_dia, + # steps_per_circle=self.app.defaults["geometry_circle_steps"], + # overlap=over, + # contour=cont, + # connect=conn, + # prog_plot=prog_plot) + # + # if cp is not None: + # total_geometry += list(cp.get_objects()) + # except FlatCAMApp.GracefulException: + # return "fail" + # except Exception as e: + # log.debug("Could not Paint the polygons. %s" % str(e)) + # self.app.inform.emit('[ERROR] %s\n%s' % + # (_("Could not do Paint All. Try a different combination of parameters. " + # "Or a different Method of paint"), + # str(e))) + # return "fail" - elif paint_method == "lines": - # Type(cp) == FlatCAMRTreeStorage | None - cp = self.clear_polygon3(poly_buf, - tooldia=tool_dia, - steps_per_circle=self.app.defaults["geometry_circle_steps"], - overlap=over, - contour=cont, - connect=conn, - prog_plot=prog_plot) + pol_nr += 1 + disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100])) + # log.debug("Polygons cleared: %d" % pol_nr) - else: - # Type(cp) == FlatCAMRTreeStorage | None - cp = self.clear_polygon(poly_buf, - tooldia=tool_dia, - steps_per_circle=self.app.defaults["geometry_circle_steps"], - overlap=over, - contour=cont, - connect=conn, - prog_plot=prog_plot) - - if cp is not None: - total_geometry += list(cp.get_objects()) - except FlatCAMApp.GracefulException: - return "fail" - except Exception as e: - log.debug("Could not Paint the polygons. %s" % str(e)) - self.app.inform.emit('[ERROR] %s\n%s' % - (_("Could not do Paint All. Try a different combination of parameters. " - "Or a different Method of paint"), - str(e))) - return "fail" - - pol_nr += 1 - disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100])) - # log.debug("Polygons cleared: %d" % pol_nr) - - if old_disp_number < disp_number <= 100: - app_obj.proc_container.update_view_text(' %d%%' % disp_number) - old_disp_number = disp_number - # log.debug("Polygons cleared: %d. Percentage done: %d%%" % (pol_nr, disp_number)) + if old_disp_number < disp_number <= 100: + app_obj.proc_container.update_view_text(' %d%%' % disp_number) + old_disp_number = disp_number + # log.debug("Polygons cleared: %d. Percentage done: %d%%" % (pol_nr, disp_number)) # add the solid_geometry to the current too in self.paint_tools (tools_storage) # dictionary and then reset the temporary list that stored that solid_geometry @@ -1837,17 +1936,24 @@ class ToolPaint(FlatCAMTool, Gerber): if self.app.defaults["tools_paint_plotting"] == 'progressive': self.temp_shapes.clear(update=True) + # # delete tools with empty geometry + # keys_to_delete = [] + # # look for keys in the tools_storage dict that have 'solid_geometry' values empty + # for uid in tools_storage: + # # if the solid_geometry (type=list) is empty + # if not tools_storage[uid]['solid_geometry']: + # keys_to_delete.append(uid) + # + # # actual delete of keys from the tools_storage dict + # for k in keys_to_delete: + # tools_storage.pop(k, None) + # delete tools with empty geometry - keys_to_delete = [] # look for keys in the tools_storage dict that have 'solid_geometry' values empty - for uid in tools_storage: + for uid in list(tools_storage.keys()): # if the solid_geometry (type=list) is empty if not tools_storage[uid]['solid_geometry']: - keys_to_delete.append(uid) - - # actual delete of keys from the tools_storage dict - for k in keys_to_delete: - tools_storage.pop(k, None) + tools_storage.pop(uid, None) geo_obj.options["cnctooldia"] = str(tool_dia) # this turn on the FlatCAMCNCJob plot for multiple tools