- Geometry Editor: made the tool to be able to continuously move until the tool is exited either by ESC key or by right mouse button click

- Geometry Editor Move Tool: if no shape is selected when triggering this tool, now it is possible to make the selection inside the tool
- Gerber editor Move Tool: fixed a bug that repeated the plotting function unnecessarily
- Gerber editor Move Tool: if no shape is selected the tool will exit
This commit is contained in:
Marius Stanciu 2019-05-08 20:01:06 +03:00
parent 0a150fba79
commit 28fce82432
4 changed files with 163 additions and 46 deletions

View File

@ -15,6 +15,10 @@ CAD program, and create G-Code for Isolation routing.
- added move action for solid_geometry stored in the gerber_obj.apertures - added move action for solid_geometry stored in the gerber_obj.apertures
- fixed camlib.Gerber skew, rotate, offset, mirror functions to work for geometry stored in the Gerber apertures - fixed camlib.Gerber skew, rotate, offset, mirror functions to work for geometry stored in the Gerber apertures
- fixed Gerber Editor follow_geometry reconstruction - fixed Gerber Editor follow_geometry reconstruction
- Geometry Editor: made the tool to be able to continuously move until the tool is exited either by ESC key or by right mouse button click
- Geometry Editor Move Tool: if no shape is selected when triggering this tool, now it is possible to make the selection inside the tool
- Gerber editor Move Tool: fixed a bug that repeated the plotting function unnecessarily
- Gerber editor Move Tool: if no shape is selected the tool will exit
7.05.2019 7.05.2019

View File

@ -7269,13 +7269,13 @@ class CNCjob(Geometry):
self.create_geometry() self.create_geometry()
def get_bounds(geometry_list): def get_bounds(geometry_list):
xmin = Inf xmin = Inf
ymin = Inf ymin = Inf
xmax = -Inf xmax = -Inf
ymax = -Inf ymax = -Inf
#print "Getting bounds of:", str(geometry_set)
for gs in geometry_list: for gs in geometry_list:
try: try:
gxmin, gymin, gxmax, gymax = gs.bounds() gxmin, gymin, gxmax, gymax = gs.bounds()

View File

@ -18,6 +18,7 @@ from FlatCAMTool import FlatCAMTool
from flatcamGUI.ObjectUI import LengthEntry, RadioSet from flatcamGUI.ObjectUI import LengthEntry, RadioSet
from shapely.geometry import LineString, LinearRing, MultiLineString from shapely.geometry import LineString, LinearRing, MultiLineString
# from shapely.geometry import mapping
from shapely.ops import cascaded_union, unary_union from shapely.ops import cascaded_union, unary_union
import shapely.affinity as affinity import shapely.affinity as affinity
@ -29,6 +30,7 @@ from flatcamGUI.GUIElements import OptionalInputSection, FCCheckBox, FCEntry, FC
FCTable, FCDoubleSpinner, FCButton, EvalEntry2, FCInputDialog FCTable, FCDoubleSpinner, FCButton, EvalEntry2, FCInputDialog
from flatcamParsers.ParseFont import * from flatcamParsers.ParseFont import *
# from vispy.io import read_png
import gettext import gettext
import FlatCAMTranslation as fcTranslate import FlatCAMTranslation as fcTranslate
@ -1861,7 +1863,6 @@ class DrawTool(object):
def __init__(self, draw_app): def __init__(self, draw_app):
self.draw_app = draw_app self.draw_app = draw_app
self.complete = False self.complete = False
self.start_msg = "Click on 1st point..."
self.points = [] self.points = []
self.geometry = None # DrawToolShape or None self.geometry = None # DrawToolShape or None
@ -1939,7 +1940,6 @@ class FCCircle(FCShapeTool):
self.cursor = QtGui.QCursor(QtGui.QPixmap('share/aero_circle_geo.png')) self.cursor = QtGui.QCursor(QtGui.QPixmap('share/aero_circle_geo.png'))
QtGui.QGuiApplication.setOverrideCursor(self.cursor) QtGui.QGuiApplication.setOverrideCursor(self.cursor)
self.start_msg = _("Click on Center point ...")
self.draw_app.app.inform.emit(_("Click on Center point ...")) self.draw_app.app.inform.emit(_("Click on Center point ..."))
self.steps_per_circ = self.draw_app.app.defaults["geometry_circle_steps"] self.steps_per_circ = self.draw_app.app.defaults["geometry_circle_steps"]
@ -1991,7 +1991,6 @@ class FCArc(FCShapeTool):
self.cursor = QtGui.QCursor(QtGui.QPixmap('share/aero_arc.png')) self.cursor = QtGui.QCursor(QtGui.QPixmap('share/aero_arc.png'))
QtGui.QGuiApplication.setOverrideCursor(self.cursor) QtGui.QGuiApplication.setOverrideCursor(self.cursor)
self.start_msg = _("Click on Center point ...")
self.draw_app.app.inform.emit(_("Click on Center point ...")) self.draw_app.app.inform.emit(_("Click on Center point ..."))
# Direction of rotation between point 1 and 2. # Direction of rotation between point 1 and 2.
@ -2210,12 +2209,13 @@ class FCRectangle(FCShapeTool):
self.cursor = QtGui.QCursor(QtGui.QPixmap('share/aero.png')) self.cursor = QtGui.QCursor(QtGui.QPixmap('share/aero.png'))
QtGui.QGuiApplication.setOverrideCursor(self.cursor) QtGui.QGuiApplication.setOverrideCursor(self.cursor)
self.start_msg = _("Click on 1st corner ...") self.draw_app.app.inform.emit( _("Click on 1st corner ..."))
def click(self, point): def click(self, point):
self.points.append(point) self.points.append(point)
if len(self.points) == 1: if len(self.points) == 1:
self.draw_app.app.inform.emit(_("Click on opposite corner to complete ..."))
return "Click on opposite corner to complete ..." return "Click on opposite corner to complete ..."
if len(self.points) == 2: if len(self.points) == 2:
@ -2262,14 +2262,14 @@ class FCPolygon(FCShapeTool):
self.cursor = QtGui.QCursor(QtGui.QPixmap('share/aero.png')) self.cursor = QtGui.QCursor(QtGui.QPixmap('share/aero.png'))
QtGui.QGuiApplication.setOverrideCursor(self.cursor) QtGui.QGuiApplication.setOverrideCursor(self.cursor)
self.start_msg = _("Click on 1st point ...") self.draw_app.app.inform.emit(_("Click on 1st corner ..."))
def click(self, point): def click(self, point):
self.draw_app.in_action = True self.draw_app.in_action = True
self.points.append(point) self.points.append(point)
if len(self.points) > 0: if len(self.points) > 0:
self.draw_app.app.inform.emit(_("Click on next Point or click Right mouse button to complete ...")) self.draw_app.app.inform.emit(_("Click on next Point or click right mouse button to complete ..."))
return "Click on next point or hit ENTER to complete ..." return "Click on next point or hit ENTER to complete ..."
return "" return ""
@ -2445,21 +2445,33 @@ class FCMove(FCShapeTool):
FCShapeTool.__init__(self, draw_app) FCShapeTool.__init__(self, draw_app)
self.name = 'move' self.name = 'move'
# self.shape_buffer = self.draw_app.shape_buffer try:
if not self.draw_app.selected: QtGui.QGuiApplication.restoreOverrideCursor()
self.draw_app.app.inform.emit(_("[WARNING_NOTCL] Move cancelled. No shape selected.")) except:
return pass
self.storage = self.draw_app.storage
self.origin = None self.origin = None
self.destination = None self.destination = None
self.start_msg = _("Click on reference point.")
if len(self.draw_app.get_selected()) == 0:
self.draw_app.app.inform.emit(_("[WARNING_NOTCL] MOVE: No shape selected. Select a shape to move ..."))
else:
self.draw_app.app.inform.emit(_(" MOVE: Click on reference point ..."))
def set_origin(self, origin): def set_origin(self, origin):
self.draw_app.app.inform.emit(_("Click on destination point.")) self.draw_app.app.inform.emit(_(" Click on destination point ..."))
self.origin = origin self.origin = origin
def click(self, point): def click(self, point):
if len(self.draw_app.get_selected()) == 0: if len(self.draw_app.get_selected()) == 0:
return "Nothing to move." # self.complete = True
# self.draw_app.app.inform.emit(_("[WARNING_NOTCL] Move cancelled. No shape selected."))
self.select_shapes(point)
self.draw_app.replot()
self.draw_app.app.inform.emit(_(" MOVE: Click on reference point ..."))
return
if self.origin is None: if self.origin is None:
self.set_origin(point) self.set_origin(point)
@ -2517,6 +2529,58 @@ class FCMove(FCShapeTool):
# return DrawToolUtilityShape([affinity.translate(geom.geo, xoff=dx, yoff=dy) # return DrawToolUtilityShape([affinity.translate(geom.geo, xoff=dx, yoff=dy)
# for geom in self.draw_app.get_selected()]) # for geom in self.draw_app.get_selected()])
def select_shapes(self, pos):
# list where we store the overlapped shapes under our mouse left click position
over_shape_list = []
try:
_, closest_shape = self.storage.nearest(pos)
except StopIteration:
return ""
over_shape_list.append(closest_shape)
try:
# if there is no shape under our click then deselect all shapes
# it will not work for 3rd method of click selection
if not over_shape_list:
self.draw_app.selected = []
self.draw_app.draw_shape_idx = -1
else:
# if there are shapes under our click then advance through the list of them, one at the time in a
# circular way
self.draw_app.draw_shape_idx = (FlatCAMGeoEditor.draw_shape_idx + 1) % len(over_shape_list)
try:
obj_to_add = over_shape_list[int(FlatCAMGeoEditor.draw_shape_idx)]
except IndexError:
return
key_modifier = QtWidgets.QApplication.keyboardModifiers()
if self.draw_app.app.defaults["global_mselect_key"] == 'Control':
# if CONTROL 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 == Qt.ControlModifier:
if obj_to_add in self.draw_app.selected:
self.draw_app.selected.remove(obj_to_add)
else:
self.draw_app.selected.append(obj_to_add)
else:
self.draw_app.selected = []
self.draw_app.selected.append(obj_to_add)
else:
if key_modifier == Qt.ShiftModifier:
if obj_to_add in self.draw_app.selected:
self.draw_app.selected.remove(obj_to_add)
else:
self.draw_app.selected.append(obj_to_add)
else:
self.draw_app.selected = []
self.draw_app.selected.append(obj_to_add)
except Exception as e:
log.error("[ERROR] Something went bad. %s" % str(e))
raise
class FCCopy(FCMove): class FCCopy(FCMove):
def __init__(self, draw_app): def __init__(self, draw_app):
@ -2550,7 +2614,7 @@ class FCText(FCShapeTool):
self.draw_app = draw_app self.draw_app = draw_app
self.app = draw_app.app self.app = draw_app.app
self.start_msg = _("Click on the Destination point...") self.draw_app.app.inform.emit(_("Click on 1st corner ..."))
self.origin = (0, 0) self.origin = (0, 0)
self.text_gui = TextInputTool(self.app) self.text_gui = TextInputTool(self.app)
@ -2602,7 +2666,7 @@ class FCBuffer(FCShapeTool):
self.draw_app = draw_app self.draw_app = draw_app
self.app = draw_app.app self.app = draw_app.app
self.start_msg = _("Create buffer geometry ...") self.draw_app.app.inform.emit(_("Create buffer geometry ..."))
self.origin = (0, 0) self.origin = (0, 0)
self.buff_tool = BufferSelectionTool(self.app, self.draw_app) self.buff_tool = BufferSelectionTool(self.app, self.draw_app)
self.buff_tool.run() self.buff_tool.run()
@ -2720,7 +2784,7 @@ class FCPaint(FCShapeTool):
self.draw_app = draw_app self.draw_app = draw_app
self.app = draw_app.app self.app = draw_app.app
self.start_msg = _("Create Paint geometry ...") self.draw_app.app.inform.emit(_("Create Paint geometry ..."))
self.origin = (0, 0) self.origin = (0, 0)
self.draw_app.paint_tool.run() self.draw_app.paint_tool.run()
@ -2734,7 +2798,7 @@ class FCTransform(FCShapeTool):
self.draw_app = draw_app self.draw_app = draw_app
self.app = draw_app.app self.app = draw_app.app
self.start_msg = _("Shape transformations ...") self.draw_app.app.infrom.emit(_("Shape transformations ..."))
self.origin = (0, 0) self.origin = (0, 0)
self.draw_app.transform_tool.run() self.draw_app.transform_tool.run()
@ -3159,6 +3223,9 @@ class FlatCAMGeoEditor(QtCore.QObject):
:return: None :return: None
""" """
if shape is None:
return
# List of DrawToolShape? # List of DrawToolShape?
if isinstance(shape, list): if isinstance(shape, list):
for subshape in shape: for subshape in shape:
@ -3280,8 +3347,6 @@ class FlatCAMGeoEditor(QtCore.QObject):
self.tools[t]["button"].setChecked(False) self.tools[t]["button"].setChecked(False)
self.active_tool = self.tools[tool]["constructor"](self) self.active_tool = self.tools[tool]["constructor"](self)
if not isinstance(self.active_tool, FCSelect):
self.app.inform.emit(self.active_tool.start_msg)
else: else:
self.app.log.debug("%s is NOT checked." % tool) self.app.log.debug("%s is NOT checked." % tool)
for t in self.tools: for t in self.tools:
@ -3341,6 +3406,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
# Selection with left mouse button # Selection with left mouse button
if self.active_tool is not None and event.button is 1: if self.active_tool is not None and event.button is 1:
# Dispatch event to active_tool # Dispatch event to active_tool
msg = self.active_tool.click(self.snap(self.pos[0], self.pos[1])) msg = self.active_tool.click(self.snap(self.pos[0], self.pos[1]))
@ -3348,28 +3414,11 @@ class FlatCAMGeoEditor(QtCore.QObject):
if isinstance(self.active_tool, FCShapeTool) and self.active_tool.complete: if isinstance(self.active_tool, FCShapeTool) and self.active_tool.complete:
self.on_shape_complete() self.on_shape_complete()
# 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.app.defaults["global_mselect_key"] == 'Control':
modifier_to_use = Qt.ControlModifier
else:
modifier_to_use = Qt.ShiftModifier
if isinstance(self.active_tool, FCText): if isinstance(self.active_tool, FCText):
self.select_tool("select") self.select_tool("select")
else: else:
self.select_tool(self.active_tool.name) self.select_tool(self.active_tool.name)
# 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.select_tool(self.active_tool.name)
# else:
# self.select_tool("select")
# return
if isinstance(self.active_tool, FCSelect): if isinstance(self.active_tool, FCSelect):
# self.app.log.debug("Replotting after click.") # self.app.log.debug("Replotting after click.")
self.replot() self.replot()
@ -3616,13 +3665,13 @@ class FlatCAMGeoEditor(QtCore.QObject):
self.selected.remove(shape) # TODO: Check performance self.selected.remove(shape) # TODO: Check performance
def on_move(self): def on_move(self):
# if not self.selected:
# self.app.inform.emit(_("[WARNING_NOTCL] Move cancelled. No shape selected."))
# return
self.app.ui.geo_move_btn.setChecked(True) self.app.ui.geo_move_btn.setChecked(True)
self.on_tool_select('move') self.on_tool_select('move')
def on_move_click(self): def on_move_click(self):
if not self.selected:
self.app.inform.emit(_("[WARNING_NOTCL] Move cancelled. No shape selected."))
return
self.on_move() self.on_move()
self.active_tool.set_origin(self.snap(self.x, self.y)) self.active_tool.set_origin(self.snap(self.x, self.y))

View File

@ -2,6 +2,7 @@ from PyQt5 import QtGui, QtCore, QtWidgets
from PyQt5.QtCore import Qt, QSettings from PyQt5.QtCore import Qt, QSettings
from shapely.geometry import LineString, LinearRing, MultiLineString from shapely.geometry import LineString, LinearRing, MultiLineString
# from shapely.geometry import mapping
from shapely.ops import cascaded_union, unary_union from shapely.ops import cascaded_union, unary_union
import shapely.affinity as affinity import shapely.affinity as affinity
@ -19,6 +20,9 @@ from FlatCAMTool import FlatCAMTool
from numpy.linalg import norm as numpy_norm from numpy.linalg import norm as numpy_norm
# from vispy.io import read_png
# import pngcanvas
import gettext import gettext
import FlatCAMTranslation as fcTranslate import FlatCAMTranslation as fcTranslate
@ -1449,11 +1453,18 @@ class FCApertureMove(FCShapeTool):
self.destination = None self.destination = None
self.selected_apertures = [] self.selected_apertures = []
if len(self.draw_app.get_selected()) == 0:
self.draw_app.app.inform.emit(_("[WARNING_NOTCL] Nothing selected to move ..."))
self.complete = True
self.draw_app.select_tool("select")
return
if self.draw_app.launched_from_shortcuts is True: if self.draw_app.launched_from_shortcuts is True:
self.draw_app.launched_from_shortcuts = False self.draw_app.launched_from_shortcuts = False
self.draw_app.app.inform.emit(_("Click on target location ...")) self.draw_app.app.inform.emit(_("Click on target location ..."))
else: else:
self.draw_app.app.inform.emit(_("Click on reference location ...")) self.draw_app.app.inform.emit(_("Click on reference location ..."))
self.current_storage = None self.current_storage = None
self.geometry = [] self.geometry = []
@ -1485,6 +1496,37 @@ class FCApertureMove(FCShapeTool):
self.draw_app.select_tool("select") self.draw_app.select_tool("select")
return return
# def create_png(self):
# """
# Create a PNG file out of a list of Shapely polygons
# :return:
# """
# if len(self.draw_app.get_selected()) == 0:
# return None
#
# geo_list = [geoms.geo for geoms in self.draw_app.get_selected()]
# xmin, ymin, xmax, ymax = get_shapely_list_bounds(geo_list)
#
# iwidth = (xmax - xmin)
# iwidth = int(round(iwidth))
# iheight = (ymax - ymin)
# iheight = int(round(iheight))
# c = pngcanvas.PNGCanvas(iwidth, iheight)
#
# pixels = []
# for geom in self.draw_app.get_selected():
# m = mapping(geom.geo.exterior)
# pixels += [[coord[0], coord[1]] for coord in m['coordinates']]
# for g in geom.geo.interiors:
# m = mapping(g)
# pixels += [[coord[0], coord[1]] for coord in m['coordinates']]
# c.polyline(pixels)
# pixels = []
#
# f = open("%s.png" % 'D:\\shapely_image', "wb")
# f.write(c.dump())
# f.close()
def make(self): def make(self):
# Create new geometry # Create new geometry
dx = self.destination[0] - self.origin[0] dx = self.destination[0] - self.origin[0]
@ -1499,13 +1541,14 @@ class FCApertureMove(FCShapeTool):
self.geometry.append(DrawToolShape(affinity.translate(select_shape.geo, xoff=dx, yoff=dy))) self.geometry.append(DrawToolShape(affinity.translate(select_shape.geo, xoff=dx, yoff=dy)))
self.current_storage.remove(select_shape) self.current_storage.remove(select_shape)
sel_shapes_to_be_deleted.append(select_shape) sel_shapes_to_be_deleted.append(select_shape)
self.draw_app.on_grb_shape_complete(self.current_storage) self.draw_app.on_grb_shape_complete(self.current_storage, noplot=True)
self.geometry = [] self.geometry = []
for shp in sel_shapes_to_be_deleted: for shp in sel_shapes_to_be_deleted:
self.draw_app.selected.remove(shp) self.draw_app.selected.remove(shp)
sel_shapes_to_be_deleted = [] sel_shapes_to_be_deleted = []
self.draw_app.plot_all()
self.draw_app.build_ui() self.draw_app.build_ui()
self.draw_app.app.inform.emit(_("[success] Done. Apertures Move completed.")) self.draw_app.app.inform.emit(_("[success] Done. Apertures Move completed."))
@ -1531,8 +1574,9 @@ class FCApertureMove(FCShapeTool):
dx = data[0] - self.origin[0] dx = data[0] - self.origin[0]
dy = data[1] - self.origin[1] dy = data[1] - self.origin[1]
for geom in self.draw_app.get_selected(): # for geom in self.draw_app.get_selected():
geo_list.append(affinity.translate(geom.geo, xoff=dx, yoff=dy)) # geo_list.append(affinity.translate(geom.geo, xoff=dx, yoff=dy))
geo_list = [affinity.translate(geom.geo, xoff=dx, yoff=dy) for geom in self.draw_app.get_selected()]
return DrawToolUtilityShape(geo_list) return DrawToolUtilityShape(geo_list)
@ -3188,7 +3232,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
self.options[key] = self.sender().isChecked() self.options[key] = self.sender().isChecked()
return self.options[key] return self.options[key]
def on_grb_shape_complete(self, storage=None, specific_shape=None): def on_grb_shape_complete(self, storage=None, specific_shape=None, noplot=False):
self.app.log.debug("on_shape_complete()") self.app.log.debug("on_shape_complete()")
if specific_shape: if specific_shape:
@ -3209,8 +3253,9 @@ class FlatCAMGrbEditor(QtCore.QObject):
self.delete_utility_geometry() self.delete_utility_geometry()
self.tool_shape.clear(update=True) self.tool_shape.clear(update=True)
# Replot and reset tool. if noplot is False:
self.plot_all() # Replot and reset tool.
self.plot_all()
def add_gerber_shape(self, shape, storage): def add_gerber_shape(self, shape, storage):
""" """
@ -4860,3 +4905,22 @@ class TransformEditorTool(FlatCAMTool):
else: else:
self.app.inform.emit( self.app.inform.emit(
_("[WARNING_NOTCL] Geometry shape skew Y cancelled...")) _("[WARNING_NOTCL] Geometry shape skew Y cancelled..."))
def get_shapely_list_bounds(geometry_list):
xmin = Inf
ymin = Inf
xmax = -Inf
ymax = -Inf
for gs in geometry_list:
try:
gxmin, gymin, gxmax, gymax = gs.bounds
xmin = min([xmin, gxmin])
ymin = min([ymin, gymin])
xmax = max([xmax, gxmax])
ymax = max([ymax, gymax])
except:
log.warning("DEVELOPMENT: Tried to get bounds of empty geometry.")
return [xmin, ymin, xmax, ymax]