From d34b1f1c713ab17788f247ac0cc9b78a0c32b417 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Fri, 27 Nov 2020 01:09:43 +0200 Subject: [PATCH] - in Tool Follow made sure that the resulting geometry elements are all valid and not empty and also do not contain Points as those are irrelevant in the Follow Tool - in Geometry Editor fixed the selection on the Geometry Table - in Geometry Editor - made sure that the edited Geometry do not contain Shapely Points and that when adding shapes, that the geometry of that shapes is valid and non empty and it is not a Shapely Point - in Geometry Editor - added new information's (length, coordinates and vertex points number) for the geometric element selected in the Geometry Table --- CHANGELOG.md | 7 ++ appEditors/AppGeoEditor.py | 128 ++++++++++++++++++++++++++++++------- appTools/ToolFollow.py | 21 +++++- 3 files changed, 131 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 39c35b2c..b8561629 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ CHANGELOG for FlatCAM beta ================================================= +27.11.2020 + +- in Geometry Editor fixed the selection on the Geometry Table +- in Geometry Editor - made sure that the edited Geometry do not contain Shapely Points and that when adding shapes, that the geometry of that shapes is valid and non empty and it is not a Shapely Point +- in Geometry Editor - added new information's (length, coordinates and vertex points number) for the geometric element selected in the Geometry Table + 26.11.2020 - fixed error in Tools Database when deleting first tool; remade the tool deletion method @@ -14,6 +20,7 @@ CHANGELOG for FlatCAM beta - in Tools Database made sure that editing the Target and Tool diameter in the right side section will update the values in the left TreeWidget - in Tools Database added a Sort by Target context menu entry and functionality; fixed the Sort by Dia functionality - in Tools Database - clicking the header sections of the TreeWidget for columns Target and Diameter will sort the tools by that criteria +- in Tool Follow made sure that the resulting geometry elements are all valid and not empty and also do not contain Points as those are irrelevant in the Follow Tool 25.11.2020 diff --git a/appEditors/AppGeoEditor.py b/appEditors/AppGeoEditor.py index f1fb4130..802c3588 100644 --- a/appEditors/AppGeoEditor.py +++ b/appEditors/AppGeoEditor.py @@ -17,7 +17,7 @@ from PyQt5.QtCore import Qt from camlib import distance, arc, three_point_circle, Geometry, FlatCAMRTreeStorage from appTool import AppTool from appGUI.GUIElements import OptionalInputSection, FCCheckBox, FCLabel, FCComboBox, FCTextAreaRich, \ - FCDoubleSpinner, FCButton, FCInputDoubleSpinner, FCTree, NumericalEvalTupleEntry + FCDoubleSpinner, FCButton, FCInputDoubleSpinner, FCTree, NumericalEvalTupleEntry, FCEntry, FCTextEdit from appParsers.ParseFont import * from shapely.geometry import LineString, LinearRing, MultiLineString, Polygon, MultiPolygon, Point @@ -2637,7 +2637,7 @@ class FCSelect(DrawTool): except Exception as e: log.error("[ERROR] AppGeoEditor.FCSelect.click_release() -> Something went bad. %s" % str(e)) - # if selection is done on canvas update the Tree in Selected Tab with the selection + # if selection is done on canvas update the Tree in Properties Tab with the selection try: self.draw_app.tw.itemSelectionChanged.disconnect(self.draw_app.on_tree_selection_change) except (AttributeError, TypeError): @@ -2649,7 +2649,7 @@ class FCSelect(DrawTool): while iterator.value(): item = iterator.value() try: - if int(item.text(1)) == id(sel_shape): + if int(item.text(0)) == id(sel_shape): item.setSelected(True) except ValueError: pass @@ -3368,18 +3368,59 @@ class AppGeoEditor(QtCore.QObject): self.title_box.addWidget(self.title_label, stretch=1) self.title_box.addWidget(FCLabel('')) + grid0 = QtWidgets.QGridLayout() + grid0.setColumnStretch(0, 0) + grid0.setColumnStretch(1, 1) + self.tools_box.addLayout(grid0) + + # Tree Widget Title + tw_label = FCLabel('%s:' % _("Geometry Table")) + tw_label.setToolTip( + _("The list of geometry elements inside the edited object.") + ) + grid0.addWidget(tw_label, 0, 0, 1, 2) + + # Tree Widget self.tw = FCTree(columns=3, header_hidden=False, protected_column=[0, 1], extended_sel=True) self.tw.setHeaderLabels(["ID", _("Type"), _("Name")]) self.tw.setIndentation(0) self.tw.header().setStretchLastSection(True) self.tw.header().setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents) - self.tools_box.addWidget(self.tw) + grid0.addWidget(self.tw, 2, 0, 1, 2) self.geo_font = QtGui.QFont() self.geo_font.setBold(True) - self.geo_parent = self.tw.invisibleRootItem() + # Length + len_lbl = FCLabel('%s:' % _("Length")) + len_lbl.setToolTip( + _("The length of the geometry element.") + ) + self.geo_len_entry = FCEntry(decimals=self.decimals) + grid0.addWidget(len_lbl, 4, 0) + grid0.addWidget(self.geo_len_entry, 4, 1) + + # Coordinates + coords_lbl = FCLabel('%s:' % _("Coordinates")) + coords_lbl.setToolTip( + _("The coordinates of the selected geometry element.") + ) + grid0.addWidget(coords_lbl, 6, 0, 1, 2) + + self.geo_coords_entry = FCTextEdit() + grid0.addWidget(self.geo_coords_entry, 8, 0, 1, 2) + + # Vertex Points Number + vertex_lbl = FCLabel('%s:' % _("Vertex Points")) + vertex_lbl.setToolTip( + _("The number of vertex points in the selected geometry element.") + ) + self.geo_vertex_entry = FCEntry(decimals=self.decimals) + + grid0.addWidget(vertex_lbl, 10, 0) + grid0.addWidget(self.geo_vertex_entry, 10, 1) + layout.addStretch() # Editor @@ -3690,14 +3731,37 @@ class AppGeoEditor(QtCore.QObject): def on_tree_selection_change(self): self.selected = [] + last_obj_shape = None + selected_tree_items = self.tw.selectedItems() for sel in selected_tree_items: for obj_shape in self.storage.get_objects(): try: if id(obj_shape) == int(sel.text(0)): self.selected.append(obj_shape) + last_obj_shape = obj_shape except ValueError: pass + + if last_obj_shape: + last_sel_geo = last_obj_shape.geo + if last_sel_geo.geom_type in ['LinearRing', 'LineString', 'MultiLineString']: + length = last_sel_geo.length + coords = list(last_sel_geo.coords) + vertex_nr = len(coords) + elif last_sel_geo.geom_type == 'Polygon': + length = last_sel_geo.exterior.length + coords = list(last_sel_geo.exterior.coords) + vertex_nr = len(coords) + else: + length = 0.0 + coords = 'None' + vertex_nr = 0 + + self.geo_len_entry.set_value(length, decimals=self.decimals) + self.geo_coords_entry.setText(str(coords)) + self.geo_vertex_entry.set_value(vertex_nr) + self.replot() def activate(self): @@ -4029,10 +4093,17 @@ class AppGeoEditor(QtCore.QObject): return # List of DrawToolShape? - if isinstance(shape, list): + # if isinstance(shape, list): + # for subshape in shape: + # self.add_shape(subshape) + # return + + try: for subshape in shape: self.add_shape(subshape) return + except TypeError: + pass assert isinstance(shape, DrawToolShape), "Expected a DrawToolShape, got %s" % type(shape) assert shape.geo is not None, "Shape object has empty geometry (None)" @@ -4042,12 +4113,14 @@ class AppGeoEditor(QtCore.QObject): if isinstance(shape, DrawToolUtilityShape): self.utility.append(shape) else: - try: - self.storage.insert(shape) - except Exception as err: - self.app.inform_shell.emit('%s\n%s' % ( _("Error on inserting shapes into storage."), str(err))) - if build_ui is True: - self.build_ui_sig.emit() # Build UI + geometry = shape.geo + if geometry and geometry.is_valid and not geometry.is_empty and geometry.geom_type != 'Point': + try: + self.storage.insert(shape) + except Exception as err: + self.app.inform_shell.emit('%s\n%s' % ( _("Error on inserting shapes into storage."), str(err))) + if build_ui is True: + self.build_ui_sig.emit() # Build UI def delete_utility_geometry(self): """ @@ -4416,7 +4489,7 @@ class AppGeoEditor(QtCore.QObject): while iterator.value(): item = iterator.value() try: - if int(item.text(1)) == id(sel_shape): + if int(item.text(0)) == id(sel_shape): item.setSelected(True) except ValueError: pass @@ -4801,14 +4874,22 @@ class AppGeoEditor(QtCore.QObject): else: geo_to_edit = editor_obj.flatten(geometry=fcgeometry.solid_geometry, orient_val=milling_type) - for shape in geo_to_edit: - if shape is not None: - if type(shape) == Polygon: - editor_obj.add_shape(DrawToolShape(shape.exterior), build_ui=False) - for inter in shape.interiors: - editor_obj.add_shape(DrawToolShape(inter), build_ui=False) - else: - editor_obj.add_shape(DrawToolShape(shape), build_ui=False) + # #################################################################################################### + # remove the invalid geometry and also the Points as those are not relevant for the Editor + # #################################################################################################### + try: + __ = iter(geo_to_edit) + except TypeError: + geo_to_edit = [geo_to_edit] + cleaned_geo = [g for g in geo_to_edit if g and not g.is_empty and g.is_valid and g.geom_type != 'Point'] + + for shape in cleaned_geo: + if shape.geom_type == 'Polygon': + editor_obj.add_shape(DrawToolShape(shape.exterior), build_ui=False) + for inter in shape.interiors: + editor_obj.add_shape(DrawToolShape(inter), build_ui=False) + else: + editor_obj.add_shape(DrawToolShape(shape), build_ui=False) editor_obj.replot() @@ -5007,8 +5088,9 @@ class AppGeoEditor(QtCore.QObject): tools = selected[1:] toolgeo = unary_union([deepcopy(shp.geo) for shp in tools]).buffer(0.0000001) target = deepcopy(selected[0].geo) - result = DrawToolShape(target.difference(toolgeo)) - self.add_shape(result) + + result = target.difference(toolgeo) + self.add_shape(DrawToolShape(result)) for_deletion = [s for s in self.get_selected()] for shape in for_deletion: diff --git a/appTools/ToolFollow.py b/appTools/ToolFollow.py index 68244ceb..4cfb4034 100644 --- a/appTools/ToolFollow.py +++ b/appTools/ToolFollow.py @@ -238,9 +238,19 @@ class ToolFollow(AppTool, Gerber): oname = opt_key[len('tools_mill') + 1:] new_data[oname] = app_obj.options[opt_key] + try: + __ = iter(followed_obj.follow_geometry) + except TypeError: + followed_obj.follow_geometry = [followed_obj.follow_geometry] + + follow_geo = [ + g for g in followed_obj.follow_geometry if g and not g.is_empty and g.is_valid and + g.geom_type != 'Point' + ] + # Propagate options new_obj.options["cnctooldia"] = app_obj.defaults["geometry_cnctooldia"] - new_obj.solid_geometry = deepcopy(followed_obj.follow_geometry) + new_obj.solid_geometry = follow_geo new_obj.tools = { 1: { 'tooldia': app_obj.dec_format(float(tools_list[0]), self.decimals), @@ -304,7 +314,14 @@ class ToolFollow(AppTool, Gerber): self.sel_rect[:] = [] self.points = [] - new_obj.solid_geometry = deepcopy(area_follow) + try: + __ = iter(area_follow) + except TypeError: + area_follow = [area_follow] + + cleaned_area_follow = [g for g in area_follow if not g.is_empty and g.is_valid and g.geom_type != 'Point'] + + new_obj.solid_geometry = deepcopy(cleaned_area_follow) new_obj.tools = { 1: { 'tooldia': app_obj.dec_format(float(tools_list[0]), self.decimals),