From 7fda555fc0f1e38f5d634f3665348bcdb1740a11 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Wed, 24 May 2023 17:46:25 +0300 Subject: [PATCH] - for all Editors now there is a shortcut key (key "C") that will toggle the cursor data in case the cursor data is in the way - Geometry Editor: the Path tool now has a new behavior: when key modifier SHIFT is pressed and the Grid is OFF, when the line is drawn and it is near the 0, 45 and 90 degrees, in each quadrant, then the drawn line will follow the nearest target angle (0 or 45 or 90 degrees) allowing the drawing of straight lines. --- CHANGELOG.md | 5 ++ appEditors/AppExcEditor.py | 57 +++++++++++++++ appEditors/AppGeoEditor.py | 129 +++++++++++++++++++++++++++++++++- appEditors/AppGerberEditor.py | 36 ++++++++++ 4 files changed, 224 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b040e816..043a1a1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ CHANGELOG for FlatCAM Evo beta ================================================= +24.05.2023 + +- for all Editors now there is a shortcut key (key "C") that will toggle the cursor data in case the cursor data is in the way +- Geometry Editor: the Path tool now has a new behavior: when key modifier SHIFT is pressed and the Grid is OFF, when the line is drawn and it is near the 0, 45 and 90 degrees, in each quadrant, then the drawn line will follow the nearest target angle (0 or 45 or 90 degrees) allowing the drawing of straight lines. + 19.05.2023 - CutOut Plugin - fixed the manual adding of gaps diff --git a/appEditors/AppExcEditor.py b/appEditors/AppExcEditor.py index 3c563507..18a9e050 100644 --- a/appEditors/AppExcEditor.py +++ b/appEditors/AppExcEditor.py @@ -245,6 +245,8 @@ class DrillAdd(FCShapeTool): self.draw_app = draw_app self.app = self.draw_app.app + self.cursor_data_control = True + self.selected_dia = None try: self.draw_app.app.inform.emit(_("Click to place ...")) @@ -372,6 +374,10 @@ class DrillAdd(FCShapeTool): self.draw_app.app.inform.emit('[success] %s' % _("Done.")) def draw_cursor_data(self, pos=None, delete=False): + if self.cursor_data_control is False: + self.draw_app.app.plotcanvas.text_cursor.text = "" + return + if pos is None: pos = self.draw_app.snap_x, self.draw_app.snap_y @@ -469,6 +475,9 @@ class DrillAdd(FCShapeTool): self.draw_app.app.on_jump_to(custom_location=(new_x, new_y), fit_center=False) self.add_drill(drill_pos=(new_x, new_y)) + if key == 'C' or key == QtCore.Qt.Key.Key_C: + self.cursor_data_control = not self.cursor_data_control + def add_drill(self, drill_pos): curr_pos = self.draw_app.app.geo_editor.snap(drill_pos[0], drill_pos[1]) self.draw_app.snap_x = curr_pos[0] @@ -533,6 +542,8 @@ class DrillArray(FCShapeTool): self.pt = [] + self.cursor_data_control = True + try: self.draw_app.app.inform.emit(_("Click to place ...")) self.selected_dia = self.draw_app.tool2tooldia[self.draw_app.last_tool_selected] @@ -836,6 +847,10 @@ class DrillArray(FCShapeTool): pass def draw_cursor_data(self, pos=None, delete=False): + if self.cursor_data_control is False: + self.draw_app.app.plotcanvas.text_cursor.text = "" + return + if pos is None: pos = self.draw_app.snap_x, self.draw_app.snap_y @@ -908,6 +923,9 @@ class DrillArray(FCShapeTool): # ## Utility geometry (animated) self.draw_app.update_utility_geometry(data=(self.draw_app.snap_x, self.draw_app.snap_y)) + if key == 'C' or key == QtCore.Qt.Key.Key_C: + self.cursor_data_control = not self.cursor_data_control + # Jump to coords if key == QtCore.Qt.Key.Key_J or key == 'J': self.draw_app.app.on_jump_to() @@ -972,6 +990,9 @@ class SlotAdd(FCShapeTool): self.half_width = 0.0 self.selected_dia = None + + self.cursor_data_control = True + try: self.draw_app.app.inform.emit(_("Click to place ...")) self.selected_dia = self.draw_app.tool2tooldia[self.draw_app.last_tool_selected] @@ -1174,6 +1195,10 @@ class SlotAdd(FCShapeTool): self.draw_app.app.inform.emit('[success] %s' % _("Done.")) def draw_cursor_data(self, pos=None, delete=False): + if self.cursor_data_control is False: + self.draw_app.app.plotcanvas.text_cursor.text = "" + return + if pos is None: pos = self.draw_app.snap_x, self.draw_app.snap_y @@ -1283,6 +1308,9 @@ class SlotAdd(FCShapeTool): self.draw_app.app.on_jump_to(custom_location=(new_x, new_y), fit_center=False) self.add_slot(slot_pos=(new_x, new_y)) + if key == 'C' or key == QtCore.Qt.Key.Key_C: + self.cursor_data_control = not self.cursor_data_control + def add_slot(self, slot_pos): curr_pos = self.draw_app.app.geo_editor.snap(slot_pos[0], slot_pos[1]) self.draw_app.snap_x = curr_pos[0] @@ -1353,6 +1381,8 @@ class SlotArray(FCShapeTool): self.pt = [] + self.cursor_data_control = True + try: self.draw_app.app.inform.emit(_("Click to place ...")) self.selected_dia = self.draw_app.tool2tooldia[self.draw_app.last_tool_selected] @@ -1756,6 +1786,10 @@ class SlotArray(FCShapeTool): pass def draw_cursor_data(self, pos=None, delete=False): + if self.cursor_data_control is False: + self.draw_app.app.plotcanvas.text_cursor.text = "" + return + if pos is None: pos = self.draw_app.snap_x, self.draw_app.snap_y @@ -1828,6 +1862,9 @@ class SlotArray(FCShapeTool): # ## Utility geometry (animated) self.draw_app.update_utility_geometry(data=(self.draw_app.snap_x, self.draw_app.snap_y)) elif mod_key is None: + if key == 'C' or key == QtCore.Qt.Key.Key_C: + self.cursor_data_control = not self.cursor_data_control + # Jump to coords if key == QtCore.Qt.Key.Key_J or key == 'J': self.draw_app.app.on_jump_to() @@ -1840,6 +1877,7 @@ class SlotArray(FCShapeTool): self.ui.slot_direction_radio.set_value('A') elif self.ui.slot_direction_radio.get_value() == 'A': self.ui.slot_direction_radio.set_value('X') + # ## Utility geometry (animated) self.draw_app.update_utility_geometry(data=(self.draw_app.snap_x, self.draw_app.snap_y)) @@ -1913,6 +1951,8 @@ class ResizeEditorExc(FCShapeTool): self.geometry = [] self.destination_storage = None + self.cursor_data_control = True + try: QtGui.QGuiApplication.restoreOverrideCursor() except Exception: @@ -2183,6 +2223,10 @@ class ResizeEditorExc(FCShapeTool): self.clean_up() def draw_cursor_data(self, pos=None, delete=False): + if self.cursor_data_control is False: + self.draw_app.app.plotcanvas.text_cursor.text = "" + return + if pos is None: pos = self.draw_app.snap_x, self.draw_app.snap_y @@ -2232,6 +2276,10 @@ class ResizeEditorExc(FCShapeTool): if self.draw_app.app.plotcanvas.text_cursor.parent is None: self.draw_app.app.plotcanvas.text_cursor.parent = self.draw_app.app.plotcanvas.view.scene + def on_key(self, key): + if key == 'C' or key == QtCore.Qt.Key.Key_C: + self.cursor_data_control = not self.cursor_data_control + def clean_up(self): self.draw_app.selected = [] self.draw_app.ui.tools_table_exc.clearSelection() @@ -2417,6 +2465,8 @@ class CopyEditorExc(FCShapeTool): self.current_storage = None self.geometry = [] + self.cursor_data_control = True + for index in self.draw_app.ui.tools_table_exc.selectedIndexes(): row = index.row() # on column 1 in tool tables we hold the diameters, and we retrieve them as strings @@ -2770,6 +2820,10 @@ class CopyEditorExc(FCShapeTool): return circular_geo def draw_cursor_data(self, pos=None, delete=False): + if self.cursor_data_control is False: + self.draw_app.app.plotcanvas.text_cursor.text = "" + return + if pos is None: pos = self.draw_app.snap_x, self.draw_app.snap_y @@ -2818,6 +2872,9 @@ class CopyEditorExc(FCShapeTool): self.draw_app.app.plotcanvas.text_cursor.parent = self.draw_app.app.plotcanvas.view.scene def on_key(self, key): + if key == 'C' or key == QtCore.Qt.Key.Key_C: + self.cursor_data_control = not self.cursor_data_control + # Jump to coords if key == QtCore.Qt.Key.Key_J or key == 'J': self.draw_app.app.on_jump_to() diff --git a/appEditors/AppGeoEditor.py b/appEditors/AppGeoEditor.py index e18c2fce..68fec469 100644 --- a/appEditors/AppGeoEditor.py +++ b/appEditors/AppGeoEditor.py @@ -49,6 +49,8 @@ from rtree import index as rtindex from copy import deepcopy # from vispy.io import read_png +from typing import Union + import gettext import appTranslation as fcTranslate import builtins @@ -136,6 +138,10 @@ class DrawToolShape(object): # fixed issue of getting bounds only for one level lists of objects # now it can get bounds for nested lists of objects + + if self.geo is None: + return 0, 0, 0, 0 + def bounds_rec(shape_el): if type(shape_el) is list: minx = np.Inf @@ -388,7 +394,7 @@ class DrawTool(object): self.points = [] self.geometry = None # DrawToolShape or None - def click(self, point): + def click(self, point: Union[list[float, float], tuple[float, float]]): """ :param point: [x, y] Coordinate pair. """ @@ -467,6 +473,8 @@ class FCCircle(FCShapeTool): self.storage = self.draw_app.storage self.util_geo = None + self.cursor_data_control = True + try: QtGui.QGuiApplication.restoreOverrideCursor() except Exception: @@ -593,6 +601,10 @@ class FCCircle(FCShapeTool): return None def draw_cursor_data(self, pos=None, delete=False): + if self.cursor_data_control is False: + self.draw_app.app.plotcanvas.text_cursor.text = "" + return + if pos is None: pos = self.draw_app.snap_x, self.draw_app.snap_y @@ -703,6 +715,9 @@ class FCCircle(FCShapeTool): self.draw_app.select_tool("select") return "Done." + if key == 'C' or key == QtCore.Qt.Key.Key_C: + self.cursor_data_control = not self.cursor_data_control + def make(self): try: QtGui.QGuiApplication.restoreOverrideCursor() @@ -997,6 +1012,8 @@ class FCRectangle(FCShapeTool): self.util_geo = None + self.cursor_data_control = True + try: QtGui.QGuiApplication.restoreOverrideCursor() except Exception: @@ -1127,6 +1144,10 @@ class FCRectangle(FCShapeTool): self.draw_app.app.inform.emit('[success] %s' % _("Done.")) def draw_cursor_data(self, pos=None, delete=False): + if self.cursor_data_control is False: + self.draw_app.app.plotcanvas.text_cursor.text = "" + return + if pos is None: pos = self.draw_app.snap_x, self.draw_app.snap_y @@ -1174,6 +1195,9 @@ class FCRectangle(FCShapeTool): self.draw_app.app.plotcanvas.text_cursor.parent = self.draw_app.app.plotcanvas.view.scene def on_key(self, key): + if key == 'C' or key == QtCore.Qt.Key.Key_C: + self.cursor_data_control = not self.cursor_data_control + # Jump to coords if key == QtCore.Qt.Key.Key_J or key == 'J': self.draw_app.app.on_jump_to() @@ -1281,6 +1305,8 @@ class FCPolygon(FCShapeTool): self.polygon_tool.run() self.new_segment = True + self.cursor_data_control = True + self.app.ui.notebook.setTabText(2, self.plugin_name) if self.draw_app.app.ui.splitter.sizes()[0] == 0: self.draw_app.app.ui.splitter.setSizes([1, 1]) @@ -1336,6 +1362,10 @@ class FCPolygon(FCShapeTool): return None def draw_cursor_data(self, pos=None, delete=False): + if self.cursor_data_control is False: + self.draw_app.app.plotcanvas.text_cursor.text = "" + return + if pos is None: pos = self.draw_app.snap_x, self.draw_app.snap_y @@ -1383,6 +1413,9 @@ class FCPolygon(FCShapeTool): self.draw_app.app.plotcanvas.text_cursor.parent = self.draw_app.app.plotcanvas.view.scene def on_key(self, key): + if key == 'C' or key == QtCore.Qt.Key.Key_C: + self.cursor_data_control = not self.cursor_data_control + # Jump to coords if key == QtCore.Qt.Key.Key_J or key == 'J': self.draw_app.app.on_jump_to() @@ -1477,6 +1510,9 @@ class FCPath(FCShapeTool): self.name = 'path' self.app = self.draw_app.app + # show the cursor data + self.cursor_data_control = True + try: QtGui.QGuiApplication.restoreOverrideCursor() except Exception: @@ -1534,13 +1570,79 @@ class FCPath(FCShapeTool): def utility_geometry(self, data=None): if len(self.points) > 0: - temp_points = [x for x in self.points] - temp_points.append(data) + modifier = QtWidgets.QApplication.keyboardModifiers() + if modifier == Qt.KeyboardModifier.ShiftModifier: + temp_points = [x for x in self.points] + if not temp_points: + return DrawToolUtilityShape(None) + + x_start = temp_points[-1][0] + y_start = temp_points[-1][1] + x_end = data[0] + y_end = data[1] + dx = x_end - x_start + dy = y_end - y_start + det_angle = self.update_angle(dx, dy) + + new_x, new_y = data + + if 0 <= det_angle <= 10: + new_x = data[0] + new_y = y_start + + if 80 <= det_angle <= 90: + new_x = x_start + new_y = data[1] + + if 35 <= det_angle <= 55: + new_x, new_y = self.closest_point_to_45_degrees(origin=temp_points[-1], current=data) + + temp_points.append([new_x, new_y]) + else: + temp_points = [x for x in self.points] + temp_points.append(data) + return DrawToolUtilityShape(LineString(temp_points)) return None + @staticmethod + def closest_point_to_45_degrees(origin, current): + # Calculate vector from origin to current point + vector_x = current[0] - origin[0] + vector_y = current[1] - origin[1] + + # Calculate the angle between the vector and the x-axis + angle = math.atan2(vector_y, vector_x) + + # Calculate the angle of the 45-degree line + angle_45 = math.radians(45) + + # Determine the angle to the closest point on the 45-degree line + closest_angle = round(angle / angle_45) * angle_45 + + # Calculate the coordinates of the closest point + closest_distance = math.sqrt(vector_x ** 2 + vector_y ** 2) + closest_point_x = origin[0] + closest_distance * math.cos(closest_angle) + closest_point_y = origin[1] + closest_distance * math.sin(closest_angle) + + return closest_point_x, closest_point_y + + def update_angle(self, dx, dy): + try: + angle = math.degrees(math.atan2(abs(dy), abs(dx))) + # if angle < 0: + # angle += 360 + except Exception as e: + self.app.log.error("FCPath.update_angle() -> %s" % str(e)) + return None + return angle + def draw_cursor_data(self, pos=None, delete=False): + if self.cursor_data_control is False: + self.draw_app.app.plotcanvas.text_cursor.text = "" + return + if pos is None: pos = self.draw_app.snap_x, self.draw_app.snap_y @@ -1588,6 +1690,9 @@ class FCPath(FCShapeTool): self.draw_app.app.plotcanvas.text_cursor.parent = self.draw_app.app.plotcanvas.view.scene def on_key(self, key): + if key == 'C' or key == QtCore.Qt.Key.Key_C: + self.cursor_data_control = not self.cursor_data_control + # Jump to coords if key == QtCore.Qt.Key.Key_J or key == 'J': self.draw_app.app.on_jump_to() @@ -1899,6 +2004,8 @@ class FCMove(FCShapeTool): self.sel_limit = self.draw_app.app.options["geometry_editor_sel_limit"] self.selection_shape = self.selection_bbox() + self.cursor_data_control = True + if len(self.draw_app.get_selected()) == 0: self.has_selection = False self.draw_app.app.inform.emit('[WARNING_NOTCL] %s %s' % @@ -2099,6 +2206,10 @@ class FCMove(FCShapeTool): raise def draw_cursor_data(self, pos=None, delete=False): + if self.cursor_data_control is False: + self.draw_app.app.plotcanvas.text_cursor.text = "" + return + if pos is None: pos = self.draw_app.snap_x, self.draw_app.snap_y @@ -2146,6 +2257,9 @@ class FCMove(FCShapeTool): self.draw_app.app.plotcanvas.text_cursor.parent = self.draw_app.app.plotcanvas.view.scene def on_key(self, key): + if key == 'C' or key == QtCore.Qt.Key.Key_C: + self.cursor_data_control = not self.cursor_data_control + # Jump to coords if key == QtCore.Qt.Key.Key_J or key == 'J': self.draw_app.app.on_jump_to() @@ -2237,6 +2351,8 @@ class FCCopy(FCShapeTool): self.clicked_postion = None + self.cursor_data_control = True + if len(self.draw_app.get_selected()) == 0: self.has_selection = False self.draw_app.app.inform.emit('[WARNING_NOTCL] %s %s' % @@ -2615,6 +2731,10 @@ class FCCopy(FCShapeTool): raise def draw_cursor_data(self, pos=None, delete=False): + if self.cursor_data_control is False: + self.draw_app.app.plotcanvas.text_cursor.text = "" + return + if pos is None: pos = self.draw_app.snap_x, self.draw_app.snap_y @@ -2662,6 +2782,9 @@ class FCCopy(FCShapeTool): self.draw_app.app.plotcanvas.text_cursor.parent = self.draw_app.app.plotcanvas.view.scene def on_key(self, key): + if key == 'C' or key == QtCore.Qt.Key.Key_C: + self.cursor_data_control = not self.cursor_data_control + # Jump to coords if key == QtCore.Qt.Key.Key_J or key == 'J': self.draw_app.app.on_jump_to() diff --git a/appEditors/AppGerberEditor.py b/appEditors/AppGerberEditor.py index ec363717..7b63a922 100644 --- a/appEditors/AppGerberEditor.py +++ b/appEditors/AppGerberEditor.py @@ -57,6 +57,8 @@ class PadEditorGrb(ShapeToolEditorGrb): self.app = self.draw_app.app self.dont_execute = False + self.cursor_data_control = True + try: QtGui.QGuiApplication.restoreOverrideCursor() except Exception: @@ -290,6 +292,10 @@ class PadEditorGrb(ShapeToolEditorGrb): self.draw_app.app.inform.emit('[success] %s' % _("Done.")) def draw_cursor_data(self, pos=None, delete=False): + if self.cursor_data_control is False: + self.draw_app.app.plotcanvas.text_cursor.text = "" + return + if pos is None: pos = self.draw_app.snap_x, self.draw_app.snap_y @@ -393,6 +399,8 @@ class PadEditorGrb(ShapeToolEditorGrb): self.draw_app.app.inform.emit(msg) # self.interpolate_length = '' # return "Click on next point or hit ENTER to complete ..." + if key == 'C' or key == QtCore.Qt.Key.Key_C: + self.cursor_data_control = not self.cursor_data_control def clean_up(self): self.draw_app.selected = [] @@ -421,6 +429,8 @@ class PadArrayEditorGrb(ShapeToolEditorGrb): self.app = self.draw_app.app self.dont_execute = False + self.cursor_data_control = True + try: self.radius = float(self.draw_app.storage_dict[self.draw_app.last_aperture_selected]['size']) / 2 except KeyError: @@ -883,6 +893,10 @@ class PadArrayEditorGrb(ShapeToolEditorGrb): pass def draw_cursor_data(self, pos=None, delete=False): + if self.cursor_data_control is False: + self.draw_app.app.plotcanvas.text_cursor.text = "" + return + if pos is None: pos = self.draw_app.snap_x, self.draw_app.snap_y @@ -957,6 +971,9 @@ class PadArrayEditorGrb(ShapeToolEditorGrb): # ## Utility geometry (animated) self.draw_app.update_utility_geometry(data=(self.draw_app.snap_x, self.draw_app.snap_y)) + elif key == 'C' or key == QtCore.Qt.Key.Key_C: + self.cursor_data_control = not self.cursor_data_control + def add_pad_array(self, array_pos): self.radius = self.ui.radius_entry.get_value() self.array_type = self.ui.array_type_radio.get_value() @@ -1141,6 +1158,8 @@ class RegionEditorGrb(ShapeToolEditorGrb): # this will store the inflexion point in the geometry self.inter_point = None + self.cursor_data_control = True + try: QtGui.QGuiApplication.restoreOverrideCursor() except Exception as e: @@ -1409,6 +1428,10 @@ class RegionEditorGrb(ShapeToolEditorGrb): self.draw_app.app.inform.emit('[success] %s' % _("Done.")) def draw_cursor_data(self, pos=None, delete=False): + if self.cursor_data_control is False: + self.draw_app.app.plotcanvas.text_cursor.text = "" + return + if pos is None: pos = self.draw_app.snap_x, self.draw_app.snap_y @@ -1459,6 +1482,9 @@ class RegionEditorGrb(ShapeToolEditorGrb): self.draw_app.app.plotcanvas.text_cursor.parent = self.draw_app.app.plotcanvas.view.scene def on_key(self, key): + if key == 'C' or key == QtCore.Qt.Key.Key_C: + self.cursor_data_control = not self.cursor_data_control + # Jump to coords if key == QtCore.Qt.Key.Key_J or key == 'J': self.draw_app.app.on_jump_to() @@ -1622,6 +1648,9 @@ class TrackEditorGrb(ShapeToolEditorGrb): self.current_point = None self.final_click = False + + self.cursor_data_control = True + try: QtGui.QGuiApplication.restoreOverrideCursor() except Exception as e: @@ -1813,6 +1842,10 @@ class TrackEditorGrb(ShapeToolEditorGrb): self.draw_app.app.inform.emit('[success] %s' % _("Done.")) def draw_cursor_data(self, pos=None, delete=False): + if self.cursor_data_control is False: + self.draw_app.app.plotcanvas.text_cursor.text = "" + return + if pos is None: pos = self.draw_app.snap_x, self.draw_app.snap_y @@ -1876,6 +1909,9 @@ class TrackEditorGrb(ShapeToolEditorGrb): self.draw_app.draw_utility_geometry(geo_shape=geo) return _("Backtracked one point ...") + if key == 'C' or key == QtCore.Qt.Key.Key_C: + self.cursor_data_control = not self.cursor_data_control + # Jump to coords if key == QtCore.Qt.Key.Key_G or key == 'G': self.draw_app.app.ui.grid_snap_btn.trigger()