From 198e05532850b7d3abb5b1bf9eec824018d20b5b Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Mon, 9 Sep 2019 02:41:14 +0300 Subject: [PATCH] - added a setting in Preferences -> Gerber -> Gerber General named Buffering. If set to 'no' the Gerber objects load a lot more faster (perhaps 10 times faster than when set to 'full') but the visual look is not so great as all the aperture polygons can be seen --- FlatCAMApp.py | 23 ++++++++--- README.md | 5 +++ camlib.py | 17 +++++--- flatcamGUI/FlatCAMGUI.py | 16 +++++++- flatcamGUI/VisPyVisuals.py | 2 +- flatcamTools/ToolNonCopperClear.py | 64 +++++++++++++++++++++++++++--- 6 files changed, 108 insertions(+), 19 deletions(-) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 1e498390..71081ba9 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -423,6 +423,7 @@ class App(QtCore.QObject): "gerber_solid": self.ui.gerber_defaults_form.gerber_gen_group.solid_cb, "gerber_multicolored": self.ui.gerber_defaults_form.gerber_gen_group.multicolored_cb, "gerber_circle_steps": self.ui.gerber_defaults_form.gerber_gen_group.circle_steps_entry, + "gerber_buffering": self.ui.gerber_defaults_form.gerber_gen_group.buffering_radio, # Gerber Options "gerber_isotooldia": self.ui.gerber_defaults_form.gerber_opt_group.iso_tool_dia_entry, @@ -824,19 +825,20 @@ class App(QtCore.QObject): "gerber_plot": True, "gerber_solid": True, "gerber_multicolored": False, + "gerber_circle_steps": 128, + "gerber_use_buffer_for_union": True, + "gerber_buffering": "full", + + # Gerber Options "gerber_isotooldia": 0.00787402, "gerber_isopasses": 1, "gerber_isooverlap": 0.00393701, - - # Gerber Options - "gerber_combine_passes": False, "gerber_milling_type": "cl", + "gerber_combine_passes": False, "gerber_noncoppermargin": 0.00393701, "gerber_noncopperrounded": False, "gerber_bboxmargin": 0.00393701, "gerber_bboxrounded": False, - "gerber_circle_steps": 128, - "gerber_use_buffer_for_union": True, # Gerber Advanced Options "gerber_aperture_display": False, @@ -3724,6 +3726,17 @@ class App(QtCore.QObject): # Icon and title self.setWindowIcon(parent.app_icon) self.setWindowTitle("FlatCAM") + # self.setStyleSheet("background-image: url(share/flatcam_icon256.png); background-attachment: fixed") + # self.setStyleSheet( + # "border-image: url(share/flatcam_icon256.png) 0 0 0 0 stretch stretch; " + # "background-attachment: fixed" + # ) + + # bgimage = QtGui.QImage('share/flatcam_icon256.png') + # s_bgimage = bgimage.scaled(QtCore.QSize(self.frameGeometry().width(), self.frameGeometry().height())) + # palette = QtGui.QPalette() + # palette.setBrush(10, QtGui.QBrush(bgimage)) # 10 = Windowrole + # self.setPalette(palette) layout1 = QtWidgets.QVBoxLayout() self.setLayout(layout1) diff --git a/README.md b/README.md index 8aa84c97..5374b48a 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,11 @@ CAD program, and create G-Code for Isolation routing. ================================================= +9.09.2019 + +- changed the triangulation type in VisPyVisuals for ShapeCollectionVisual class +- added a setting in Preferences -> Gerber -> Gerber General named Buffering. If set to 'no' the Gerber objects load a lot more faster (perhaps 10 times faster than when set to 'full') but the visual look is not so great as all the aperture polygons can be seen + 8.09.2019 - added some documentation strings for methods in FlatCAMApp.App class diff --git a/camlib.py b/camlib.py index 19f8e253..03121e45 100644 --- a/camlib.py +++ b/camlib.py @@ -2368,6 +2368,8 @@ class Gerber (Geometry): # ### Parsing starts here ## ## line_num = 0 gline = "" + + self.app.inform.emit('%s %d %s.' % (_("Gerber processing. Parsing"), len(glines), _("lines"))) try: for gline in glines: if self.app.abort_flag: @@ -3277,18 +3279,21 @@ class Gerber (Geometry): self.follow_geometry = follow_buffer # this treats the case when we are storing geometry as solids - log.warning("Joining %d polygons." % len(poly_buffer)) if len(poly_buffer) == 0: log.error("Object is not Gerber file or empty. Aborting Object creation.") return 'fail' + log.warning("Joining %d polygons." % len(poly_buffer)) + self.app.inform.emit('%s %d %s.' % (_("Gerber processing. Joining"), len(poly_buffer), _("polygons"))) + if self.use_buffer_for_union: log.debug("Union by buffer...") new_poly = MultiPolygon(poly_buffer) - new_poly = new_poly.buffer(0.00000001) - new_poly = new_poly.buffer(-0.00000001) + if self.app.defaults["gerber_buffering"] == 'full': + new_poly = new_poly.buffer(0.00000001) + new_poly = new_poly.buffer(-0.00000001) log.warning("Union(buffer) done.") else: log.debug("Union by union()...") @@ -3299,15 +3304,15 @@ class Gerber (Geometry): self.solid_geometry = self.solid_geometry.union(new_poly) else: self.solid_geometry = self.solid_geometry.difference(new_poly) - except Exception as err: ex_type, ex, tb = sys.exc_info() traceback.print_tb(tb) # print traceback.format_exc() log.error("Gerber PARSING FAILED. Line %d: %s" % (line_num, gline)) - loc = 'Gerber Line #%d Gerber Line Content: %s\n' % (line_num, gline) + repr(err) - self.app.inform.emit(_("[ERROR]Gerber Parser ERROR.\n%s:") % loc) + + loc = '%s #%d %s: %s\n' % (_("Gerber Line"), line_num, _("Gerber Line Content"), gline) + repr(err) + self.app.inform.emit('[ERROR] %s\n%s:' % (_("Gerber Parser ERROR"), loc)) @staticmethod def create_flash_geometry(location, aperture, steps_per_circle=None): diff --git a/flatcamGUI/FlatCAMGUI.py b/flatcamGUI/FlatCAMGUI.py index 4a45e06a..23e599c3 100644 --- a/flatcamGUI/FlatCAMGUI.py +++ b/flatcamGUI/FlatCAMGUI.py @@ -4314,10 +4314,23 @@ class GerberGenPrefGroupUI(OptionsGroupUI): _("The number of circle steps for Gerber \n" "circular aperture linear approximation.") ) - grid0.addWidget(self.circle_steps_label, 1, 0) self.circle_steps_entry = IntEntry() + grid0.addWidget(self.circle_steps_label, 1, 0) grid0.addWidget(self.circle_steps_entry, 1, 1) + # Milling Type + buffering_label = QtWidgets.QLabel('%s:' % _('Buffering')) + buffering_label.setToolTip( + _("Buffering type:\n" + "- None --> best performance, fast file loading but no so good display\n" + "- Full --> slow file loading but good visuals. This is the default.\n" + "<>: Don't change this unless you know what you are doing !!!") + ) + self.buffering_radio = RadioSet([{'label': _('None'), 'value': 'no'}, + {'label': _('Full'), 'value': 'full'}]) + grid0.addWidget(buffering_label, 2, 0) + grid0.addWidget(self.buffering_radio, 2, 1) + self.layout.addStretch() @@ -4370,6 +4383,7 @@ class GerberOptPrefGroupUI(OptionsGroupUI): self.iso_overlap_entry = FloatEntry() grid0.addWidget(self.iso_overlap_entry, 2, 1) + # Milling Type milling_type_label = QtWidgets.QLabel('%s:' % _('Milling Type')) milling_type_label.setToolTip( _("Milling type:\n" diff --git a/flatcamGUI/VisPyVisuals.py b/flatcamGUI/VisPyVisuals.py index 11639ba8..dd7c6ef8 100644 --- a/flatcamGUI/VisPyVisuals.py +++ b/flatcamGUI/VisPyVisuals.py @@ -187,7 +187,7 @@ class ShapeGroup(object): class ShapeCollectionVisual(CompoundVisual): - def __init__(self, line_width=1, triangulation='gpc', layers=3, pool=None, **kwargs): + def __init__(self, line_width=1, triangulation='vispy', layers=3, pool=None, **kwargs): """ Represents collection of shapes to draw on VisPy scene :param line_width: float diff --git a/flatcamTools/ToolNonCopperClear.py b/flatcamTools/ToolNonCopperClear.py index 8f64fee7..27355d82 100644 --- a/flatcamTools/ToolNonCopperClear.py +++ b/flatcamTools/ToolNonCopperClear.py @@ -1515,7 +1515,13 @@ class NonCopperClear(FlatCAMTool, Gerber): self.app.inform.emit(_("NCC Tool. Calculate 'empty' area.")) if isinstance(ncc_obj, FlatCAMGerber) and not isotooldia: - sol_geo = ncc_obj.solid_geometry + # unfortunately for this function to work time efficient, + # if the Gerber was loaded without buffering then it require the buffering now. + if self.app.defaults['gerber_buffering'] == 'no': + sol_geo = ncc_obj.solid_geometry.buffer(0) + else: + sol_geo = ncc_obj.solid_geometry + if has_offset is True: app_obj.inform.emit('[WARNING_NOTCL] %s ...' % _("Buffering")) @@ -1523,9 +1529,17 @@ class NonCopperClear(FlatCAMTool, Gerber): 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' elif isinstance(ncc_obj, FlatCAMGerber) and isotooldia: isolated_geo = [] - self.solid_geometry = ncc_obj.solid_geometry + + # unfortunately for this function to work time efficient, + # if the Gerber was loaded without buffering then it require the buffering now. + if self.app.defaults['gerber_buffering'] == 'no': + self.solid_geometry = ncc_obj.solid_geometry.buffer(0) + else: + self.solid_geometry = ncc_obj.solid_geometry # if milling type is climb then the move is counter-clockwise around features milling_type = self.milling_type_radio.get_value() @@ -1609,6 +1623,9 @@ class NonCopperClear(FlatCAMTool, Gerber): 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' + elif isinstance(ncc_obj, FlatCAMGeometry): sol_geo = cascaded_union(ncc_obj.solid_geometry) if has_offset is True: @@ -1618,6 +1635,9 @@ class NonCopperClear(FlatCAMTool, Gerber): 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.')) @@ -1832,6 +1852,8 @@ class NonCopperClear(FlatCAMTool, Gerber): 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' elif isinstance(ncc_obj, FlatCAMGerber) and isotooldia: isolated_geo = [] @@ -1922,6 +1944,8 @@ class NonCopperClear(FlatCAMTool, Gerber): 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' elif isinstance(ncc_obj, FlatCAMGeometry): sol_geo = cascaded_union(ncc_obj.solid_geometry) @@ -1932,7 +1956,8 @@ class NonCopperClear(FlatCAMTool, Gerber): 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.')) @@ -2509,16 +2534,43 @@ class NonCopperClear(FlatCAMTool, Gerber): # # Background # self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]}) - @staticmethod - def get_ncc_empty_area(target, boundary=None): + def get_ncc_empty_area(self, target, boundary=None): """ Returns the complement of target geometry within the given boundary polygon. If not specified, it defaults to the rectangular bounding box of target geometry. """ + geo_len = len(target) + pol_nr = 0 + old_disp_number = 0 + if boundary is None: boundary = target.envelope - return boundary.difference(target) + else: + boundary = boundary + try: + ret_val = boundary.difference(target) + except Exception as e: + try: + for el in target: + if self.app.abort_flag: + # graceful abort requested by the user + raise FlatCAMApp.GracefulException + boundary = boundary.difference(el) + pol_nr += 1 + disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 99])) + + if disp_number > old_disp_number and disp_number <= 100: + self.app.proc_container.update_view_text(' %d%%' % disp_number) + old_disp_number = disp_number + return boundary + except Exception as e: + self.app.inform.emit('[ERROR_NOTCL] %s' % + _("Try to use the Buffering Type = Full in Preferences -> Gerber General. " + "Reload the Gerber file after this change.")) + return 'fail' + + return ret_val def reset_fields(self): self.object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))