- 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
This commit is contained in:
Marius Stanciu 2019-04-14 15:16:37 +03:00
parent e92cab2e96
commit 1332601624
7 changed files with 185 additions and 53 deletions

View File

@ -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 - 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 - 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' - 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 13.04.2019

View File

@ -26,10 +26,11 @@ from rtree import index as rtindex
from lxml import etree as ET from lxml import etree as ET
# See: http://toblerity.org/shapely/manual.html # See: http://toblerity.org/shapely/manual.html
from shapely.geometry import Polygon, LineString, Point, LinearRing, MultiLineString from shapely.geometry import Polygon, LineString, Point, LinearRing, MultiLineString
from shapely.geometry import MultiPoint, MultiPolygon from shapely.geometry import MultiPoint, MultiPolygon
from shapely.geometry import box as shply_box 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 import shapely.affinity as affinity
from shapely.wkt import loads as sloads from shapely.wkt import loads as sloads
from shapely.wkt import dumps as sdumps from shapely.wkt import dumps as sdumps
@ -45,6 +46,7 @@ import ezdxf
# TODO: Commented for FlatCAM packaging with cx_freeze # TODO: Commented for FlatCAM packaging with cx_freeze
# from scipy.spatial import KDTree, Delaunay # from scipy.spatial import KDTree, Delaunay
from scipy.spatial import Delaunay
from flatcamParsers.ParseSVG import * from flatcamParsers.ParseSVG import *
from flatcamParsers.ParseDXF import * from flatcamParsers.ParseDXF import *
@ -7348,6 +7350,63 @@ def parse_gerber_number(strnumber, int_digits, frac_digits, zeros):
return ret_val 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): # def voronoi(P):
# """ # """
# Returns a list of all edges of the voronoi diagram for the given input points. # Returns a list of all edges of the voronoi diagram for the given input points.

View File

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

View File

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

View File

@ -476,6 +476,73 @@ class FCPadArray(FCShapeTool):
self.draw_app.plot_all() 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): class FCRegion(FCShapeTool):
""" """
Resulting type: Polygon Resulting type: Polygon
@ -1259,6 +1326,8 @@ class FlatCAMGrbEditor(QtCore.QObject):
"constructor": FCTrack}, "constructor": FCTrack},
"region": {"button": self.app.ui.grb_add_region_btn, "region": {"button": self.app.ui.grb_add_region_btn,
"constructor": FCRegion}, "constructor": FCRegion},
"poligonize": {"button": self.app.ui.grb_convert_poly_btn,
"constructor": FCPoligonize},
"buffer": {"button": self.app.ui.aperture_buffer_btn, "buffer": {"button": self.app.ui.aperture_buffer_btn,
"constructor": FCBuffer}, "constructor": FCBuffer},
"scale": {"button": self.app.ui.aperture_scale_btn, "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_press', self.on_canvas_click)
self.canvas.vis_connect('mouse_move', self.on_canvas_move) 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): def disconnect_canvas_event_handlers(self):
self.canvas.vis_disconnect('mouse_press', self.on_canvas_click) 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_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 # we restore the key and mouse control to FlatCAMApp method
self.app.plotcanvas.vis_connect('mouse_press', self.app.on_mouse_click_over_plot) self.app.plotcanvas.vis_connect('mouse_press', self.app.on_mouse_click_over_plot)
@ -2338,7 +2407,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
else: else:
self.app.log.debug("No active tool to respond to click!") 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) pos_canvas = self.canvas.vispy_canvas.translate_coords(event.pos)
self.modifiers = QtWidgets.QApplication.keyboardModifiers() self.modifiers = QtWidgets.QApplication.keyboardModifiers()

View File

@ -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.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_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_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.grb_edit_toolbar.addSeparator()
self.aperture_buffer_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/buffer16-2.png'), _('Buffer')) 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': if key == QtCore.Qt.Key_F3 or key == 'F3':
self.app.on_shortcut_list() self.app.on_shortcut_list()
return 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): def dragEnterEvent(self, event):
if event.mimeData().hasUrls: if event.mimeData().hasUrls:

View File

@ -112,6 +112,8 @@ class Measurement(FlatCAMTool):
# self.setVisible(False) # self.setVisible(False)
self.active = 0 self.active = 0
self.original_call_source = 'app'
# VisPy visuals # VisPy visuals
self.sel_shapes = ShapeCollection(parent=self.app.plotcanvas.vispy_canvas.view.scene, layers=1) 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")) 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: if self.app.ui.splitter.sizes()[0] == 0:
self.app.ui.splitter.setSizes([1, 1]) self.app.ui.splitter.setSizes([1, 1])
@ -155,29 +157,22 @@ class Measurement(FlatCAMTool):
self.distance_y_entry.set_value('0') self.distance_y_entry.set_value('0')
self.total_distance_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 # 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_move')
self.canvas.vis_disconnect('mouse_press') self.canvas.vis_disconnect('mouse_press')
self.canvas.vis_disconnect('mouse_release') self.canvas.vis_disconnect('mouse_release')
self.canvas.vis_disconnect('key_release')
# we can safely connect the app mouse events to the measurement tool # 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_move', self.on_mouse_move_meas)
self.canvas.vis_connect('mouse_release', self.on_mouse_click) self.canvas.vis_connect('mouse_release', self.on_mouse_click_release)
self.canvas.vis_connect('key_release', self.on_key_release_meas)
self.set_tool_ui() self.set_tool_ui()
def deactivate(self): def deactivate_measure_tool(self):
# disconnect the mouse/key events from functions of measurement tool # 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_move', self.on_mouse_move_meas)
self.canvas.vis_disconnect('mouse_release', self.on_mouse_click) self.canvas.vis_disconnect('mouse_release', self.on_mouse_click_release)
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)
if self.app.call_source == 'app': if self.app.call_source == 'app':
self.canvas.vis_connect('mouse_move', self.app.on_mouse_move_over_plot) 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': 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_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('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_geo_click_release)
self.canvas.vis_connect('mouse_release', self.app.geo_editor.on_canvas_click_release)
elif self.app.call_source == 'exc_editor': 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_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('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_exc_click_release)
self.canvas.vis_connect('mouse_release', self.app.exc_editor.on_canvas_click_release)
elif self.app.call_source == 'grb_editor': 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_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('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_grb_click_release)
self.canvas.vis_connect('mouse_release', self.app.grb_editor.on_canvas_click_release)
self.app.ui.notebook.setTabText(2, _("Tools")) self.app.ui.notebook.setTabText(2, _("Tools"))
self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab) self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
def on_measure(self, signal=None, activate=None): def on_measure(self, signal=None, activate=None):
log.debug("Measurement.on_measure()") log.debug("Measurement.on_measure()")
if activate is False or activate is None: if activate is True:
# DISABLE the Measuring TOOL # ENABLE the Measuring TOOL
self.deactivate() 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_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 self.app.command_active = None
# delete the measuring line # delete the measuring line
self.delete_shape() self.delete_shape()
log.debug("Measurement Tool --> exit tool") log.debug("Measurement Tool --> exit tool")
elif activate is True:
# ENABLE the Measuring TOOL
self.clicked_meas = 0
self.app.inform.emit(_("MEASURING: Click on the Start point ...")) def on_mouse_click_release(self, event):
self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower()
self.activate()
log.debug("Measurement Tool --> tool initialized")
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
if event.key == 'G':
# toggle grid status
self.app.ui.grid_snap_btn.trigger()
return
def on_mouse_click(self, event):
# mouse click releases will be accepted only if the left button is clicked # mouse click releases will be accepted only if the left button is clicked
# this is necessary because right mouse click or middle mouse click # this is necessary because right mouse click or middle mouse click
# are used for panning on the canvas # are used for panning on the canvas