Просмотр исходного кода

- moved the key handler out of the Measurement tool to flatcamGUI.FlatCAMGui.keyPressEvent()
- Gerber Editor: started to add new function of poligonize which should make a filled polygon out of a shape

Marius Stanciu 6 лет назад
Родитель
Сommit
1332601624

+ 2 - 0
README.md

@@ -16,6 +16,8 @@ CAD program, and create G-Code for Isolation routing.
 - Editors: activated an old function that was no longer active: each tool can have it's own set of shortcut keys, the Editor general shortcut keys that are letters are overridden
 - Gerber and Geometry editors, when using the Backspace keys for certain tools, they will backtrack one point but now the utility geometry is immediately updated
 - In Geometry Editor I fixed bug in Arc modes. Arc mode shortcut key is now key 'M' and arc direction change shortcut key is 'D'
+- moved the key handler out of the Measurement tool to flatcamGUI.FlatCAMGui.keyPressEvent()
+- Gerber Editor: started to add new function of poligonize which should make a filled polygon out of a shape
 
 13.04.2019
 

+ 60 - 1
camlib.py

@@ -26,10 +26,11 @@ from rtree import index as rtindex
 from lxml import etree as ET
 
 # See: http://toblerity.org/shapely/manual.html
+
 from shapely.geometry import Polygon, LineString, Point, LinearRing, MultiLineString
 from shapely.geometry import MultiPoint, MultiPolygon
 from shapely.geometry import box as shply_box
-from shapely.ops import cascaded_union, unary_union
+from shapely.ops import cascaded_union, unary_union, polygonize
 import shapely.affinity as affinity
 from shapely.wkt import loads as sloads
 from shapely.wkt import dumps as sdumps
@@ -45,6 +46,7 @@ import ezdxf
 
 # TODO: Commented for FlatCAM packaging with cx_freeze
 # from scipy.spatial import KDTree, Delaunay
+from scipy.spatial import Delaunay
 
 from flatcamParsers.ParseSVG import *
 from flatcamParsers.ParseDXF import *
@@ -7348,6 +7350,63 @@ def parse_gerber_number(strnumber, int_digits, frac_digits, zeros):
     return ret_val
 
 
+def alpha_shape(points, alpha):
+    """
+    Compute the alpha shape (concave hull) of a set of points.
+
+    @param points: Iterable container of points.
+    @param alpha: alpha value to influence the gooeyness of the border. Smaller
+                  numbers don't fall inward as much as larger numbers. Too large,
+                  and you lose everything!
+    """
+    if len(points) < 4:
+        # When you have a triangle, there is no sense in computing an alpha
+        # shape.
+        return MultiPoint(list(points)).convex_hull
+
+    def add_edge(edges, edge_points, coords, i, j):
+        """Add a line between the i-th and j-th points, if not in the list already"""
+        if (i, j) in edges or (j, i) in edges:
+            # already added
+            return
+        edges.add( (i, j) )
+        edge_points.append(coords[ [i, j] ])
+
+    coords = np.array([point.coords[0] for point in points])
+
+    tri = Delaunay(coords)
+    edges = set()
+    edge_points = []
+    # loop over triangles:
+    # ia, ib, ic = indices of corner points of the triangle
+    for ia, ib, ic in tri.vertices:
+        pa = coords[ia]
+        pb = coords[ib]
+        pc = coords[ic]
+
+        # Lengths of sides of triangle
+        a = math.sqrt((pa[0]-pb[0])**2 + (pa[1]-pb[1])**2)
+        b = math.sqrt((pb[0]-pc[0])**2 + (pb[1]-pc[1])**2)
+        c = math.sqrt((pc[0]-pa[0])**2 + (pc[1]-pa[1])**2)
+
+        # Semiperimeter of triangle
+        s = (a + b + c)/2.0
+
+        # Area of triangle by Heron's formula
+        area = math.sqrt(s*(s-a)*(s-b)*(s-c))
+        circum_r = a*b*c/(4.0*area)
+
+        # Here's the radius filter.
+        #print circum_r
+        if circum_r < 1.0/alpha:
+            add_edge(edges, edge_points, coords, ia, ib)
+            add_edge(edges, edge_points, coords, ib, ic)
+            add_edge(edges, edge_points, coords, ic, ia)
+
+    m = MultiLineString(edge_points)
+    triangles = list(polygonize(m))
+    return cascaded_union(triangles), edge_points
+
 # def voronoi(P):
 #     """
 #     Returns a list of all edges of the voronoi diagram for the given input points.

+ 3 - 3
flatcamEditors/FlatCAMExcEditor.py

@@ -1683,12 +1683,12 @@ class FlatCAMExcEditor(QtCore.QObject):
 
         self.canvas.vis_connect('mouse_press', self.on_canvas_click)
         self.canvas.vis_connect('mouse_move', self.on_canvas_move)
-        self.canvas.vis_connect('mouse_release', self.on_canvas_click_release)
+        self.canvas.vis_connect('mouse_release', self.on_exc_click_release)
 
     def disconnect_canvas_event_handlers(self):
         self.canvas.vis_disconnect('mouse_press', self.on_canvas_click)
         self.canvas.vis_disconnect('mouse_move', self.on_canvas_move)
-        self.canvas.vis_disconnect('mouse_release', self.on_canvas_click_release)
+        self.canvas.vis_disconnect('mouse_release', self.on_exc_click_release)
 
         # we restore the key and mouse control to FlatCAMApp method
         self.app.plotcanvas.vis_connect('mouse_press', self.app.on_mouse_click_over_plot)
@@ -2136,7 +2136,7 @@ class FlatCAMExcEditor(QtCore.QObject):
         else:
             self.storage.insert(shape)  # TODO: Check performance
 
-    def on_canvas_click_release(self, event):
+    def on_exc_click_release(self, event):
         pos_canvas = self.canvas.vispy_canvas.translate_coords(event.pos)
 
         self.modifiers = QtWidgets.QApplication.keyboardModifiers()

+ 3 - 3
flatcamEditors/FlatCAMGeoEditor.py

@@ -2973,13 +2973,13 @@ class FlatCAMGeoEditor(QtCore.QObject):
 
         self.canvas.vis_connect('mouse_press', self.on_canvas_click)
         self.canvas.vis_connect('mouse_move', self.on_canvas_move)
-        self.canvas.vis_connect('mouse_release', self.on_canvas_click_release)
+        self.canvas.vis_connect('mouse_release', self.on_geo_click_release)
 
     def disconnect_canvas_event_handlers(self):
 
         self.canvas.vis_disconnect('mouse_press', self.on_canvas_click)
         self.canvas.vis_disconnect('mouse_move', self.on_canvas_move)
-        self.canvas.vis_disconnect('mouse_release', self.on_canvas_click_release)
+        self.canvas.vis_disconnect('mouse_release', self.on_geo_click_release)
 
         # we restore the key and mouse control to FlatCAMApp method
         self.app.plotcanvas.vis_connect('mouse_press', self.app.on_mouse_click_over_plot)
@@ -3310,7 +3310,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
         # Update cursor
         self.app.app_cursor.set_data(np.asarray([(x, y)]), symbol='++', edge_color='black', size=20)
 
-    def on_canvas_click_release(self, event):
+    def on_geo_click_release(self, event):
         pos_canvas = self.canvas.vispy_canvas.translate_coords(event.pos)
 
         if self.app.grid_status():

+ 72 - 3
flatcamEditors/FlatCAMGrbEditor.py

@@ -476,6 +476,73 @@ class FCPadArray(FCShapeTool):
         self.draw_app.plot_all()
 
 
+class FCPoligonize(FCShapeTool):
+    """
+    Resulting type: Polygon
+    """
+
+    def __init__(self, draw_app):
+        DrawTool.__init__(self, draw_app)
+        self.name = 'poligonize'
+        self.draw_app = draw_app
+
+        self.start_msg = _("Select shape(s) and then click ...")
+        self.draw_app.in_action = True
+        self.make()
+
+    def click(self, point):
+        # self.draw_app.in_action = True
+        # if self.draw_app.selected:
+        #     self.make()
+        # else:
+        #     self.draw_app.app.inform.emit(_("[WARNING_NOTCL] No shapes are selected. Select shapes and try again ..."))
+
+        return ""
+
+    def make(self):
+        geo = []
+
+        for shape in self.draw_app.selected:
+            current_storage = self.draw_app.storage_dict[self.draw_app.last_aperture_selected]['solid_geometry']
+            print(self.draw_app.active_tool)
+            aha = []
+            if shape.geo:
+                shape_points = list(shape.geo.exterior.coords)
+                for pt in shape_points:
+                    aha.append(Point(pt))
+                concave_hull, bla_bla = alpha_shape(points=aha, alpha=0.5)
+                geo.append(concave_hull)
+                print(geo)
+                self.geometry = DrawToolShape(geo)
+                self.draw_app.on_grb_shape_complete(current_storage)
+
+        self.draw_app.in_action = False
+        self.complete = True
+        self.draw_app.app.inform.emit(_("[success] Done. Poligonize completed."))
+
+        self.draw_app.build_ui()
+        # MS: always return to the Select Tool if modifier key is not pressed
+        # else return to the current tool
+
+        key_modifier = QtWidgets.QApplication.keyboardModifiers()
+        if self.draw_app.app.defaults["global_mselect_key"] == 'Control':
+            modifier_to_use = Qt.ControlModifier
+        else:
+            modifier_to_use = Qt.ShiftModifier
+        # if modifier key is pressed then we add to the selected list the current shape but if it's already
+        # in the selected list, we removed it. Therefore first click selects, second deselects.
+        if key_modifier == modifier_to_use:
+            self.draw_app.select_tool(self.draw_app.active_tool.name)
+        else:
+            self.draw_app.select_tool("select")
+            return
+
+    def clean_up(self):
+        self.draw_app.selected = []
+        self.draw_app.apertures_table.clearSelection()
+        self.draw_app.plot_all()
+
+
 class FCRegion(FCShapeTool):
     """
     Resulting type: Polygon
@@ -1259,6 +1326,8 @@ class FlatCAMGrbEditor(QtCore.QObject):
                               "constructor": FCTrack},
             "region": {"button": self.app.ui.grb_add_region_btn,
                               "constructor": FCRegion},
+            "poligonize": {"button": self.app.ui.grb_convert_poly_btn,
+                       "constructor": FCPoligonize},
             "buffer": {"button": self.app.ui.aperture_buffer_btn,
                                 "constructor": FCBuffer},
             "scale": {"button": self.app.ui.aperture_scale_btn,
@@ -1918,12 +1987,12 @@ class FlatCAMGrbEditor(QtCore.QObject):
 
         self.canvas.vis_connect('mouse_press', self.on_canvas_click)
         self.canvas.vis_connect('mouse_move', self.on_canvas_move)
-        self.canvas.vis_connect('mouse_release', self.on_canvas_click_release)
+        self.canvas.vis_connect('mouse_release', self.on_grb_click_release)
 
     def disconnect_canvas_event_handlers(self):
         self.canvas.vis_disconnect('mouse_press', self.on_canvas_click)
         self.canvas.vis_disconnect('mouse_move', self.on_canvas_move)
-        self.canvas.vis_disconnect('mouse_release', self.on_canvas_click_release)
+        self.canvas.vis_disconnect('mouse_release', self.on_grb_click_release)
 
         # we restore the key and mouse control to FlatCAMApp method
         self.app.plotcanvas.vis_connect('mouse_press', self.app.on_mouse_click_over_plot)
@@ -2338,7 +2407,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
             else:
                 self.app.log.debug("No active tool to respond to click!")
 
-    def on_canvas_click_release(self, event):
+    def on_grb_click_release(self, event):
         pos_canvas = self.canvas.vispy_canvas.translate_coords(event.pos)
 
         self.modifiers = QtWidgets.QApplication.keyboardModifiers()

+ 20 - 0
flatcamGUI/FlatCAMGUI.py

@@ -690,6 +690,8 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
         self.add_pad_ar_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/padarray32.png'), _('Add Pad Array'))
         self.grb_add_track_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/track32.png'), _("Add Track"))
         self.grb_add_region_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/polygon32.png'), _("Add Region"))
+        self.grb_convert_poly_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/polygon32.png'), _("Poligonize"))
+
         self.grb_edit_toolbar.addSeparator()
 
         self.aperture_buffer_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/buffer16-2.png'), _('Buffer'))
@@ -2911,6 +2913,24 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                 if key == QtCore.Qt.Key_F3 or key == 'F3':
                     self.app.on_shortcut_list()
                     return
+        elif self.app.call_source == 'measurement':
+            if modifiers == QtCore.Qt.ControlModifier:
+                pass
+            elif modifiers == QtCore.Qt.AltModifier:
+                pass
+            elif modifiers == QtCore.Qt.ShiftModifier:
+                pass
+            elif modifiers == QtCore.Qt.NoModifier:
+                if key == QtCore.Qt.Key_Escape or key == 'Escape':
+                    # abort the measurement action
+                    self.app.measurement_tool.on_measure(activate=False)
+                    self.app.measurement_tool.deactivate_measure_tool()
+                    self.app.inform.emit(_("Measurement Tool exit..."))
+                    return
+
+                if key == QtCore.Qt.Key_G or key == 'G':
+                    self.app.ui.grid_snap_btn.trigger()
+                    return
 
     def dragEnterEvent(self, event):
         if event.mimeData().hasUrls:

+ 23 - 41
flatcamTools/ToolMeasurement.py

@@ -112,6 +112,8 @@ class Measurement(FlatCAMTool):
         # self.setVisible(False)
         self.active = 0
 
+        self.original_call_source = 'app'
+
         # VisPy visuals
         self.sel_shapes = ShapeCollection(parent=self.app.plotcanvas.vispy_canvas.view.scene, layers=1)
 
@@ -125,7 +127,7 @@ class Measurement(FlatCAMTool):
 
         self.app.ui.notebook.setTabText(2, _("Meas. Tool"))
 
-        # if the splitter is hidden, display it, else hide it but only if the current widget is the same
+        # if the splitter is hidden, display it
         if self.app.ui.splitter.sizes()[0] == 0:
             self.app.ui.splitter.setSizes([1, 1])
 
@@ -155,29 +157,22 @@ class Measurement(FlatCAMTool):
         self.distance_y_entry.set_value('0')
         self.total_distance_entry.set_value('0')
 
-    def activate(self):
+    def activate_measure_tool(self):
         # we disconnect the mouse/key handlers from wherever the measurement tool was called
-        self.canvas.vis_disconnect('key_press')
         self.canvas.vis_disconnect('mouse_move')
         self.canvas.vis_disconnect('mouse_press')
         self.canvas.vis_disconnect('mouse_release')
-        self.canvas.vis_disconnect('key_release')
 
         # we can safely connect the app mouse events to the measurement tool
         self.canvas.vis_connect('mouse_move', self.on_mouse_move_meas)
-        self.canvas.vis_connect('mouse_release', self.on_mouse_click)
-        self.canvas.vis_connect('key_release', self.on_key_release_meas)
+        self.canvas.vis_connect('mouse_release', self.on_mouse_click_release)
 
         self.set_tool_ui()
 
-    def deactivate(self):
+    def deactivate_measure_tool(self):
         # disconnect the mouse/key events from functions of measurement tool
         self.canvas.vis_disconnect('mouse_move', self.on_mouse_move_meas)
-        self.canvas.vis_disconnect('mouse_release', self.on_mouse_click)
-        self.canvas.vis_disconnect('key_release', self.on_key_release_meas)
-
-        # reconnect the mouse/key events to the functions from where the tool was called
-        self.canvas.vis_connect('key_press', self.app.ui.keyPressEvent)
+        self.canvas.vis_disconnect('mouse_release', self.on_mouse_click_release)
 
         if self.app.call_source == 'app':
             self.canvas.vis_connect('mouse_move', self.app.on_mouse_move_over_plot)
@@ -186,57 +181,44 @@ class Measurement(FlatCAMTool):
         elif self.app.call_source == 'geo_editor':
             self.canvas.vis_connect('mouse_move', self.app.geo_editor.on_canvas_move)
             self.canvas.vis_connect('mouse_press', self.app.geo_editor.on_canvas_click)
-            # self.canvas.vis_connect('key_press', self.app.geo_editor.on_canvas_key)
-            self.canvas.vis_connect('mouse_release', self.app.geo_editor.on_canvas_click_release)
+            self.canvas.vis_connect('mouse_release', self.app.geo_editor.on_geo_click_release)
         elif self.app.call_source == 'exc_editor':
             self.canvas.vis_connect('mouse_move', self.app.exc_editor.on_canvas_move)
             self.canvas.vis_connect('mouse_press', self.app.exc_editor.on_canvas_click)
-            # self.canvas.vis_connect('key_press', self.app.exc_editor.on_canvas_key)
-            self.canvas.vis_connect('mouse_release', self.app.exc_editor.on_canvas_click_release)
+            self.canvas.vis_connect('mouse_release', self.app.exc_editor.on_exc_click_release)
         elif self.app.call_source == 'grb_editor':
             self.canvas.vis_connect('mouse_move', self.app.grb_editor.on_canvas_move)
             self.canvas.vis_connect('mouse_press', self.app.grb_editor.on_canvas_click)
-            # self.canvas.vis_connect('key_press', self.app.grb_editor.on_canvas_key)
-            self.canvas.vis_connect('mouse_release', self.app.grb_editor.on_canvas_click_release)
+            self.canvas.vis_connect('mouse_release', self.app.grb_editor.on_grb_click_release)
 
         self.app.ui.notebook.setTabText(2, _("Tools"))
         self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
 
     def on_measure(self, signal=None, activate=None):
         log.debug("Measurement.on_measure()")
-        if activate is False or activate is None:
-            # DISABLE the Measuring TOOL
-            self.deactivate()
-
-            self.app.command_active = None
-
-            # delete the measuring line
-            self.delete_shape()
-
-            log.debug("Measurement Tool --> exit tool")
-        elif activate is True:
+        if activate is True:
             # ENABLE the Measuring TOOL
             self.clicked_meas = 0
+            self.original_call_source = copy(self.app.call_source)
+            self.app.call_source = 'measurement'
 
             self.app.inform.emit(_("MEASURING: Click on the Start point ..."))
             self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower()
 
-            self.activate()
+            self.activate_measure_tool()
             log.debug("Measurement Tool --> tool initialized")
+        else:
+            # DISABLE the Measuring TOOL
+            self.deactivate_measure_tool()
+            self.app.call_source = copy(self.original_call_source)
+            self.app.command_active = None
 
-    def on_key_release_meas(self, event):
-        if event.key == 'escape':
-            # abort the measurement action
-            self.on_measure(activate=False)
-            self.app.inform.emit(_("Measurement Tool exit..."))
-            return
+            # delete the measuring line
+            self.delete_shape()
 
-        if event.key == 'G':
-            # toggle grid status
-            self.app.ui.grid_snap_btn.trigger()
-            return
+            log.debug("Measurement Tool --> exit tool")
 
-    def on_mouse_click(self, event):
+    def on_mouse_click_release(self, event):
         # mouse click releases will be accepted only if the left button is clicked
         # this is necessary because right mouse click or middle mouse click
         # are used for panning on the canvas