From 86e36d3ca6638ce8ba68490360cb70abaa0aa566 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Wed, 3 Jun 2020 02:41:25 +0300 Subject: [PATCH] - updated Transform Tool to have a selection of possible references for the transformations that are now selectable in the GUI - Transform Tool - compacted the UI --- AppGUI/GUIElements.py | 6 +- AppGUI/preferences/PreferencesUIManager.py | 14 +- .../tools/ToolsTransformPrefGroupUI.py | 180 +++-- AppTools/ToolTransform.py | 730 +++++++++--------- CHANGELOG.md | 5 + defaults.py | 11 +- 6 files changed, 482 insertions(+), 464 deletions(-) diff --git a/AppGUI/GUIElements.py b/AppGUI/GUIElements.py index 1f07acde..1378d190 100644 --- a/AppGUI/GUIElements.py +++ b/AppGUI/GUIElements.py @@ -673,7 +673,7 @@ class NumericalEvalEntry(EvalEntry): class NumericalEvalTupleEntry(FCEntry): """ - Will evaluate the input and return a value. Accepts only float numbers and formulas using the operators: /,*,+,-,% + Will return a text value. Accepts only float numbers and formulas using the operators: /,*,+,-,% """ def __init__(self, border_color=None): super().__init__(border_color=border_color) @@ -2712,14 +2712,14 @@ class DialogBoxRadio(QtWidgets.QDialog): "If the reference is Relative then the Jump will be at the (x,y) distance\n" "from the current mouse location point.") ) - self.lineEdit = EvalEntry(self) + self.lineEdit = EvalEntry(parent=self) self.lineEdit.setText(str(self.location).replace('(', '').replace(')', '')) self.lineEdit.selectAll() self.lineEdit.setFocus() self.form.addRow(self.loc_label, self.lineEdit) self.button_box = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel, - Qt.Horizontal, parent=self) + orientation=Qt.Horizontal, parent=self) self.form.addRow(self.button_box) self.button_box.accepted.connect(self.accept) diff --git a/AppGUI/preferences/PreferencesUIManager.py b/AppGUI/preferences/PreferencesUIManager.py index 0d6fe6c9..70115d03 100644 --- a/AppGUI/preferences/PreferencesUIManager.py +++ b/AppGUI/preferences/PreferencesUIManager.py @@ -445,17 +445,23 @@ class PreferencesUIManager: "tools_calc_electro_growth": self.ui.tools_defaults_form.tools_calculators_group.growth_entry, # Transformations Tool + "tools_transform_reference": self.ui.tools_defaults_form.tools_transform_group.ref_combo, + "tools_transform_ref_object": self.ui.tools_defaults_form.tools_transform_group.type_obj_combo, + "tools_transform_ref_point": self.ui.tools_defaults_form.tools_transform_group.point_entry, + "tools_transform_rotate": self.ui.tools_defaults_form.tools_transform_group.rotate_entry, + "tools_transform_skew_x": self.ui.tools_defaults_form.tools_transform_group.skewx_entry, "tools_transform_skew_y": self.ui.tools_defaults_form.tools_transform_group.skewy_entry, + "tools_transform_skew_link": self.ui.tools_defaults_form.tools_transform_group.skew_link_cb, + "tools_transform_scale_x": self.ui.tools_defaults_form.tools_transform_group.scalex_entry, "tools_transform_scale_y": self.ui.tools_defaults_form.tools_transform_group.scaley_entry, - "tools_transform_scale_link": self.ui.tools_defaults_form.tools_transform_group.link_cb, - "tools_transform_scale_reference": self.ui.tools_defaults_form.tools_transform_group.reference_cb, + "tools_transform_scale_link": self.ui.tools_defaults_form.tools_transform_group.scale_link_cb, + "tools_transform_offset_x": self.ui.tools_defaults_form.tools_transform_group.offx_entry, "tools_transform_offset_y": self.ui.tools_defaults_form.tools_transform_group.offy_entry, - "tools_transform_mirror_reference": self.ui.tools_defaults_form.tools_transform_group.mirror_reference_cb, - "tools_transform_mirror_point": self.ui.tools_defaults_form.tools_transform_group.flip_ref_entry, + "tools_transform_buffer_dis": self.ui.tools_defaults_form.tools_transform_group.buffer_entry, "tools_transform_buffer_factor": self.ui.tools_defaults_form.tools_transform_group.buffer_factor_entry, "tools_transform_buffer_corner": self.ui.tools_defaults_form.tools_transform_group.buffer_rounded_cb, diff --git a/AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py b/AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py index b0c438f0..d3bd0c88 100644 --- a/AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py +++ b/AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py @@ -1,7 +1,7 @@ -from PyQt5 import QtWidgets +from PyQt5 import QtWidgets, QtGui from PyQt5.QtCore import QSettings -from AppGUI.GUIElements import FCDoubleSpinner, FCCheckBox, NumericalEvalTupleEntry +from AppGUI.GUIElements import FCDoubleSpinner, FCCheckBox, NumericalEvalTupleEntry, FCComboBox from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext @@ -40,10 +40,53 @@ class ToolsTransformPrefGroupUI(OptionsGroupUI): grid0.setColumnStretch(0, 0) grid0.setColumnStretch(1, 1) - # ## Rotate Angle + # Reference Type + ref_label = QtWidgets.QLabel('%s:' % _("Reference")) + ref_label.setToolTip( + _("The reference point for Rotate, Skew, Scale, Mirror.\n" + "Can be:\n" + "- Origin -> it is the 0, 0 point\n" + "- Selection -> the center of the bounding box of the selected objects\n" + "- Point -> a custom point defined by X,Y coordinates\n" + "- Object -> the center of the bounding box of a specific object") + ) + self.ref_combo = FCComboBox() + self.ref_items = [_("Origin"), _("Selection"), _("Point"), _("Object")] + self.ref_combo.addItems(self.ref_items) + grid0.addWidget(ref_label, 0, 0) + grid0.addWidget(self.ref_combo, 0, 1) + + self.point_label = QtWidgets.QLabel('%s:' % _("Point")) + self.point_label.setToolTip( + _("A point of reference in format X,Y.") + ) + self.point_entry = NumericalEvalTupleEntry() + + grid0.addWidget(self.point_label, 1, 0) + grid0.addWidget(self.point_entry, 1, 1) + + # Type of object to be used as reference + self.type_object_label = QtWidgets.QLabel('%s:' % _("Object")) + self.type_object_label.setToolTip( + _("The type of object used as reference.") + ) + + self.type_obj_combo = FCComboBox() + self.type_obj_combo.addItem(_("Gerber")) + self.type_obj_combo.addItem(_("Excellon")) + self.type_obj_combo.addItem(_("Geometry")) + + self.type_obj_combo.setItemIcon(0, QtGui.QIcon(self.app.resource_location + "/flatcam_icon16.png")) + self.type_obj_combo.setItemIcon(1, QtGui.QIcon(self.app.resource_location + "/drill16.png")) + self.type_obj_combo.setItemIcon(2, QtGui.QIcon(self.app.resource_location + "/geometry16.png")) + + grid0.addWidget(self.type_object_label, 3, 0) + grid0.addWidget(self.type_obj_combo, 3, 1) + + # ## Rotate Angle rotate_title_lbl = QtWidgets.QLabel('%s' % _("Rotate")) - grid0.addWidget(rotate_title_lbl, 0, 0, 1, 2) + grid0.addWidget(rotate_title_lbl, 4, 0, 1, 2) self.rotate_entry = FCDoubleSpinner() self.rotate_entry.set_range(-360.0, 360.0) @@ -57,12 +100,21 @@ class ToolsTransformPrefGroupUI(OptionsGroupUI): "Positive numbers for CW motion.\n" "Negative numbers for CCW motion.") ) - grid0.addWidget(self.rotate_label, 1, 0) - grid0.addWidget(self.rotate_entry, 1, 1) + grid0.addWidget(self.rotate_label, 6, 0) + grid0.addWidget(self.rotate_entry, 6, 1) # ## Skew/Shear Angle on X axis skew_title_lbl = QtWidgets.QLabel('%s' % _("Skew")) - grid0.addWidget(skew_title_lbl, 2, 0, 1, 2) + grid0.addWidget(skew_title_lbl, 8, 0) + + # ## Link Skew factors + self.skew_link_cb = FCCheckBox() + self.skew_link_cb.setText(_("Link")) + self.skew_link_cb.setToolTip( + _("Link the Y entry to X entry and copy it's content.") + ) + + grid0.addWidget(self.skew_link_cb, 8, 1) self.skewx_entry = FCDoubleSpinner() self.skewx_entry.set_range(-360.0, 360.0) @@ -74,8 +126,8 @@ class ToolsTransformPrefGroupUI(OptionsGroupUI): _("Angle for Skew action, in degrees.\n" "Float number between -360 and 359.") ) - grid0.addWidget(self.skewx_label, 3, 0) - grid0.addWidget(self.skewx_entry, 3, 1) + grid0.addWidget(self.skewx_label, 9, 0) + grid0.addWidget(self.skewx_entry, 9, 1) # ## Skew/Shear Angle on Y axis self.skewy_entry = FCDoubleSpinner() @@ -88,12 +140,19 @@ class ToolsTransformPrefGroupUI(OptionsGroupUI): _("Angle for Skew action, in degrees.\n" "Float number between -360 and 359.") ) - grid0.addWidget(self.skewy_label, 4, 0) - grid0.addWidget(self.skewy_entry, 4, 1) + grid0.addWidget(self.skewy_label, 10, 0) + grid0.addWidget(self.skewy_entry, 10, 1) # ## Scale scale_title_lbl = QtWidgets.QLabel('%s' % _("Scale")) - grid0.addWidget(scale_title_lbl, 5, 0, 1, 2) + grid0.addWidget(scale_title_lbl, 12, 0) + + # ## Link Scale factors + self.scale_link_cb = FCCheckBox(_("Link")) + self.scale_link_cb.setToolTip( + _("Link the Y entry to X entry and copy it's content.") + ) + grid0.addWidget(self.scale_link_cb, 12, 1) self.scalex_entry = FCDoubleSpinner() self.scalex_entry.set_range(0, 9999.9999) @@ -104,8 +163,8 @@ class ToolsTransformPrefGroupUI(OptionsGroupUI): self.scalex_label.setToolTip( _("Factor for scaling on X axis.") ) - grid0.addWidget(self.scalex_label, 6, 0) - grid0.addWidget(self.scalex_entry, 6, 1) + grid0.addWidget(self.scalex_label, 14, 0) + grid0.addWidget(self.scalex_entry, 14, 1) # ## Scale factor on X axis self.scaley_entry = FCDoubleSpinner() @@ -117,30 +176,12 @@ class ToolsTransformPrefGroupUI(OptionsGroupUI): self.scaley_label.setToolTip( _("Factor for scaling on Y axis.") ) - grid0.addWidget(self.scaley_label, 7, 0) - grid0.addWidget(self.scaley_entry, 7, 1) - - # ## Link Scale factors - self.link_cb = FCCheckBox(_("Link")) - self.link_cb.setToolTip( - _("Scale the selected object(s)\n" - "using the Scale_X factor for both axis.") - ) - grid0.addWidget(self.link_cb, 8, 0) - - # ## Scale Reference - self.reference_cb = FCCheckBox('%s' % _("Scale Reference")) - self.reference_cb.setToolTip( - _("Scale the selected object(s)\n" - "using the origin reference when checked,\n" - "and the center of the biggest bounding box\n" - "of the selected objects when unchecked.") - ) - grid0.addWidget(self.reference_cb, 8, 1) + grid0.addWidget(self.scaley_label, 16, 0) + grid0.addWidget(self.scaley_entry, 16, 1) # ## Offset offset_title_lbl = QtWidgets.QLabel('%s' % _("Offset")) - grid0.addWidget(offset_title_lbl, 9, 0, 1, 2) + grid0.addWidget(offset_title_lbl, 20, 0, 1, 2) self.offx_entry = FCDoubleSpinner() self.offx_entry.set_range(-9999.9999, 9999.9999) @@ -151,8 +192,8 @@ class ToolsTransformPrefGroupUI(OptionsGroupUI): self.offx_label.setToolTip( _("Distance to offset on X axis. In current units.") ) - grid0.addWidget(self.offx_label, 10, 0) - grid0.addWidget(self.offx_entry, 10, 1) + grid0.addWidget(self.offx_label, 22, 0) + grid0.addWidget(self.offx_entry, 22, 1) # ## Offset distance on Y axis self.offy_entry = FCDoubleSpinner() @@ -164,41 +205,23 @@ class ToolsTransformPrefGroupUI(OptionsGroupUI): self.offy_label.setToolTip( _("Distance to offset on Y axis. In current units.") ) - grid0.addWidget(self.offy_label, 11, 0) - grid0.addWidget(self.offy_entry, 11, 1) - - # ## Mirror - mirror_title_lbl = QtWidgets.QLabel('%s' % _("Mirror")) - grid0.addWidget(mirror_title_lbl, 12, 0, 1, 2) - - # ## Mirror (Flip) Reference Point - self.mirror_reference_cb = FCCheckBox('%s' % _("Mirror Reference")) - self.mirror_reference_cb.setToolTip( - _("Flip the selected object(s)\n" - "around the point in Point Entry Field.\n" - "\n" - "The point coordinates can be captured by\n" - "left click on canvas together with pressing\n" - "SHIFT key. \n" - "Then click Add button to insert coordinates.\n" - "Or enter the coords in format (x, y) in the\n" - "Point Entry field and click Flip on X(Y)")) - grid0.addWidget(self.mirror_reference_cb, 13, 0, 1, 2) - - self.flip_ref_label = QtWidgets.QLabel('%s' % _("Mirror Reference point")) - self.flip_ref_label.setToolTip( - _("Coordinates in format (x, y) used as reference for mirroring.\n" - "The 'x' in (x, y) will be used when using Flip on X and\n" - "the 'y' in (x, y) will be used when using Flip on Y and") - ) - self.flip_ref_entry = NumericalEvalTupleEntry(border_color='#0069A9') - - grid0.addWidget(self.flip_ref_label, 14, 0, 1, 2) - grid0.addWidget(self.flip_ref_entry, 15, 0, 1, 2) + grid0.addWidget(self.offy_label, 24, 0) + grid0.addWidget(self.offy_entry, 24, 1) # ## Buffer buffer_title_lbl = QtWidgets.QLabel('%s' % _("Buffer")) - grid0.addWidget(buffer_title_lbl, 16, 0, 1, 2) + grid0.addWidget(buffer_title_lbl, 26, 0) + + self.buffer_rounded_cb = FCCheckBox() + self.buffer_rounded_cb.setText('%s' % _("Rounded")) + self.buffer_rounded_cb.setToolTip( + _("If checked then the buffer will surround the buffered shape,\n" + "every corner will be rounded.\n" + "If not checked then the buffer will follow the exact geometry\n" + "of the buffered shape.") + ) + + grid0.addWidget(self.buffer_rounded_cb, 26, 1) self.buffer_label = QtWidgets.QLabel('%s:' % _("Distance")) self.buffer_label.setToolTip( @@ -214,8 +237,8 @@ class ToolsTransformPrefGroupUI(OptionsGroupUI): self.buffer_entry.setWrapping(True) self.buffer_entry.set_range(-9999.9999, 9999.9999) - grid0.addWidget(self.buffer_label, 17, 0) - grid0.addWidget(self.buffer_entry, 17, 1) + grid0.addWidget(self.buffer_label, 28, 0) + grid0.addWidget(self.buffer_entry, 28, 1) self.buffer_factor_label = QtWidgets.QLabel('%s:' % _("Value")) self.buffer_factor_label.setToolTip( @@ -232,18 +255,7 @@ class ToolsTransformPrefGroupUI(OptionsGroupUI): self.buffer_factor_entry.setWrapping(True) self.buffer_factor_entry.setSingleStep(1) - grid0.addWidget(self.buffer_factor_label, 18, 0) - grid0.addWidget(self.buffer_factor_entry, 18, 1) - - self.buffer_rounded_cb = FCCheckBox() - self.buffer_rounded_cb.setText('%s' % _("Rounded")) - self.buffer_rounded_cb.setToolTip( - _("If checked then the buffer will surround the buffered shape,\n" - "every corner will be rounded.\n" - "If not checked then the buffer will follow the exact geometry\n" - "of the buffered shape.") - ) - - grid0.addWidget(self.buffer_rounded_cb, 19, 0, 1, 2) + grid0.addWidget(self.buffer_factor_label, 30, 0) + grid0.addWidget(self.buffer_factor_entry, 30, 1) self.layout.addStretch() diff --git a/AppTools/ToolTransform.py b/AppTools/ToolTransform.py index 41e6ff91..9cd28afc 100644 --- a/AppTools/ToolTransform.py +++ b/AppTools/ToolTransform.py @@ -5,9 +5,12 @@ # MIT Licence # # ########################################################## -from PyQt5 import QtWidgets +from PyQt5 import QtWidgets, QtGui, QtCore from AppTool import AppTool -from AppGUI.GUIElements import FCDoubleSpinner, FCCheckBox, FCButton, OptionalInputSection, FCEntry +from AppGUI.GUIElements import FCDoubleSpinner, FCCheckBox, FCButton, OptionalInputSection, FCEntry, FCComboBox, \ + NumericalEvalTupleEntry + +import numpy as np import gettext import AppTranslation as fcTranslate @@ -53,9 +56,76 @@ class ToolTransform(AppTool): grid0.addWidget(QtWidgets.QLabel('')) + # Reference + ref_label = QtWidgets.QLabel('%s:' % _("Reference")) + ref_label.setToolTip( + _("The reference point for Rotate, Skew, Scale, Mirror.\n" + "Can be:\n" + "- Origin -> it is the 0, 0 point\n" + "- Selection -> the center of the bounding box of the selected objects\n" + "- Point -> a custom point defined by X,Y coordinates\n" + "- Object -> the center of the bounding box of a specific object") + ) + self.ref_combo = FCComboBox() + self.ref_items = [_("Origin"), _("Selection"), _("Point"), _("Object")] + self.ref_combo.addItems(self.ref_items) + + grid0.addWidget(ref_label, 0, 0) + grid0.addWidget(self.ref_combo, 0, 1, 1, 2) + + self.point_label = QtWidgets.QLabel('%s:' % _("Value")) + self.point_label.setToolTip( + _("A point of reference in format X,Y.") + ) + self.point_entry = NumericalEvalTupleEntry() + + grid0.addWidget(self.point_label, 1, 0) + grid0.addWidget(self.point_entry, 1, 1, 1, 2) + + self.point_button = FCButton(_("Add")) + self.point_button.setToolTip( + _("Add point coordinates from clipboard.") + ) + grid0.addWidget(self.point_button, 2, 0, 1, 3) + + # Type of object to be used as reference + self.type_object_label = QtWidgets.QLabel('%s:' % _("Type")) + self.type_object_label.setToolTip( + _("The type of object used as reference.") + ) + + self.type_obj_combo = FCComboBox() + self.type_obj_combo.addItem(_("Gerber")) + self.type_obj_combo.addItem(_("Excellon")) + self.type_obj_combo.addItem(_("Geometry")) + + self.type_obj_combo.setItemIcon(0, QtGui.QIcon(self.app.resource_location + "/flatcam_icon16.png")) + self.type_obj_combo.setItemIcon(1, QtGui.QIcon(self.app.resource_location + "/drill16.png")) + self.type_obj_combo.setItemIcon(2, QtGui.QIcon(self.app.resource_location + "/geometry16.png")) + + grid0.addWidget(self.type_object_label, 3, 0) + grid0.addWidget(self.type_obj_combo, 3, 1, 1, 2) + + # Object to be used as reference + self.object_combo = FCComboBox() + self.object_combo.setModel(self.app.collection) + self.object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) + self.object_combo.is_last = True + + self.object_combo.setToolTip( + _("The object used as reference.\n" + "The used point is the center of it's bounding box.") + ) + grid0.addWidget(self.object_combo, 4, 0, 1, 3) + + separator_line = QtWidgets.QFrame() + separator_line.setFrameShape(QtWidgets.QFrame.HLine) + separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) + grid0.addWidget(separator_line, 5, 0, 1, 3) + # ## Rotate Title rotate_title_label = QtWidgets.QLabel("%s" % self.rotateName) - grid0.addWidget(rotate_title_label, 0, 0, 1, 3) + grid0.addWidget(rotate_title_label, 6, 0, 1, 3) self.rotate_label = QtWidgets.QLabel('%s:' % _("Angle")) self.rotate_label.setToolTip( @@ -73,7 +143,7 @@ class ToolTransform(AppTool): # self.rotate_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) - self.rotate_button = FCButton() + self.rotate_button = FCButton(_("Rotate")) self.rotate_button.setToolTip( _("Rotate the selected object(s).\n" "The point of reference is the middle of\n" @@ -81,18 +151,26 @@ class ToolTransform(AppTool): ) self.rotate_button.setMinimumWidth(90) - grid0.addWidget(self.rotate_label, 1, 0) - grid0.addWidget(self.rotate_entry, 1, 1) - grid0.addWidget(self.rotate_button, 1, 2) + grid0.addWidget(self.rotate_label, 7, 0) + grid0.addWidget(self.rotate_entry, 7, 1) + grid0.addWidget(self.rotate_button, 7, 2) separator_line = QtWidgets.QFrame() separator_line.setFrameShape(QtWidgets.QFrame.HLine) separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) - grid0.addWidget(separator_line, 2, 0, 1, 3) + grid0.addWidget(separator_line, 8, 0, 1, 3) # ## Skew Title skew_title_label = QtWidgets.QLabel("%s" % self.skewName) - grid0.addWidget(skew_title_label, 3, 0, 1, 3) + grid0.addWidget(skew_title_label, 9, 0, 1, 2) + + self.skew_link_cb = FCCheckBox() + self.skew_link_cb.setText(_("Link")) + self.skew_link_cb.setToolTip( + _("Link the Y entry to X entry and copy it's content.") + ) + + grid0.addWidget(self.skew_link_cb, 9, 2) self.skewx_label = QtWidgets.QLabel('%s:' % _("X angle")) self.skewx_label.setToolTip( @@ -104,16 +182,16 @@ class ToolTransform(AppTool): self.skewx_entry.set_precision(self.decimals) self.skewx_entry.set_range(-360, 360) - self.skewx_button = FCButton() + self.skewx_button = FCButton(_("Skew X")) self.skewx_button.setToolTip( _("Skew/shear the selected object(s).\n" "The point of reference is the middle of\n" "the bounding box for all selected objects.")) self.skewx_button.setMinimumWidth(90) - grid0.addWidget(self.skewx_label, 4, 0) - grid0.addWidget(self.skewx_entry, 4, 1) - grid0.addWidget(self.skewx_button, 4, 2) + grid0.addWidget(self.skewx_label, 10, 0) + grid0.addWidget(self.skewx_entry, 10, 1) + grid0.addWidget(self.skewx_button, 10, 2) self.skewy_label = QtWidgets.QLabel('%s:' % _("Y angle")) self.skewy_label.setToolTip( @@ -125,25 +203,36 @@ class ToolTransform(AppTool): self.skewy_entry.set_precision(self.decimals) self.skewy_entry.set_range(-360, 360) - self.skewy_button = FCButton() + self.skewy_button = FCButton(_("Skew Y")) self.skewy_button.setToolTip( _("Skew/shear the selected object(s).\n" "The point of reference is the middle of\n" "the bounding box for all selected objects.")) self.skewy_button.setMinimumWidth(90) - grid0.addWidget(self.skewy_label, 5, 0) - grid0.addWidget(self.skewy_entry, 5, 1) - grid0.addWidget(self.skewy_button, 5, 2) + grid0.addWidget(self.skewy_label, 12, 0) + grid0.addWidget(self.skewy_entry, 12, 1) + grid0.addWidget(self.skewy_button, 12, 2) + + self.ois_sk = OptionalInputSection(self.skew_link_cb, [self.skewy_label, self.skewy_entry, self.skewy_button], + logic=False) separator_line = QtWidgets.QFrame() separator_line.setFrameShape(QtWidgets.QFrame.HLine) separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) - grid0.addWidget(separator_line, 6, 0, 1, 3) + grid0.addWidget(separator_line, 14, 0, 1, 3) # ## Scale Title scale_title_label = QtWidgets.QLabel("%s" % self.scaleName) - grid0.addWidget(scale_title_label, 7, 0, 1, 3) + grid0.addWidget(scale_title_label, 15, 0, 1, 2) + + self.scale_link_cb = FCCheckBox() + self.scale_link_cb.setText(_("Link")) + self.scale_link_cb.setToolTip( + _("Link the Y entry to X entry and copy it's content.") + ) + + grid0.addWidget(self.scale_link_cb, 15, 2) self.scalex_label = QtWidgets.QLabel('%s:' % _("X factor")) self.scalex_label.setToolTip( @@ -154,16 +243,16 @@ class ToolTransform(AppTool): self.scalex_entry.set_precision(self.decimals) self.scalex_entry.setMinimum(-1e6) - self.scalex_button = FCButton() + self.scalex_button = FCButton(_("Scale X")) self.scalex_button.setToolTip( _("Scale the selected object(s).\n" "The point of reference depends on \n" "the Scale reference checkbox state.")) self.scalex_button.setMinimumWidth(90) - grid0.addWidget(self.scalex_label, 8, 0) - grid0.addWidget(self.scalex_entry, 8, 1) - grid0.addWidget(self.scalex_button, 8, 2) + grid0.addWidget(self.scalex_label, 17, 0) + grid0.addWidget(self.scalex_entry, 17, 1) + grid0.addWidget(self.scalex_button, 17, 2) self.scaley_label = QtWidgets.QLabel('%s:' % _("Y factor")) self.scaley_label.setToolTip( @@ -174,45 +263,57 @@ class ToolTransform(AppTool): self.scaley_entry.set_precision(self.decimals) self.scaley_entry.setMinimum(-1e6) - self.scaley_button = FCButton() + self.scaley_button = FCButton(_("Scale Y")) self.scaley_button.setToolTip( _("Scale the selected object(s).\n" "The point of reference depends on \n" "the Scale reference checkbox state.")) self.scaley_button.setMinimumWidth(90) - grid0.addWidget(self.scaley_label, 9, 0) - grid0.addWidget(self.scaley_entry, 9, 1) - grid0.addWidget(self.scaley_button, 9, 2) + grid0.addWidget(self.scaley_label, 19, 0) + grid0.addWidget(self.scaley_entry, 19, 1) + grid0.addWidget(self.scaley_button, 19, 2) - self.scale_link_cb = FCCheckBox() - self.scale_link_cb.setText(_("Link")) - self.scale_link_cb.setToolTip( - _("Scale the selected object(s)\n" - "using the Scale_X factor for both axis.") - ) - - self.scale_zero_ref_cb = FCCheckBox() - self.scale_zero_ref_cb.setText('%s' % _("Scale Reference")) - self.scale_zero_ref_cb.setToolTip( - _("Scale the selected object(s)\n" - "using the origin reference when checked,\n" - "and the center of the biggest bounding box\n" - "of the selected objects when unchecked.")) - - self.ois_scale = OptionalInputSection(self.scale_link_cb, [self.scaley_entry, self.scaley_button], logic=False) - - grid0.addWidget(self.scale_link_cb, 10, 0) - grid0.addWidget(self.scale_zero_ref_cb, 10, 1, 1, 2) + self.ois_s = OptionalInputSection(self.scale_link_cb, + [ + self.scaley_label, + self.scaley_entry, + self.scaley_button + ], logic=False) separator_line = QtWidgets.QFrame() separator_line.setFrameShape(QtWidgets.QFrame.HLine) separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) - grid0.addWidget(separator_line, 11, 0, 1, 3) + grid0.addWidget(separator_line, 21, 0, 1, 3) + + # ## Flip Title + flip_title_label = QtWidgets.QLabel("%s" % self.flipName) + grid0.addWidget(flip_title_label, 23, 0, 1, 3) + + self.flipx_button = FCButton(_("Flip on X")) + self.flipx_button.setToolTip( + _("Flip the selected object(s) over the X axis.") + ) + + self.flipy_button = FCButton(_("Flip on Y")) + self.flipy_button.setToolTip( + _("Flip the selected object(s) over the X axis.") + ) + + hlay0 = QtWidgets.QHBoxLayout() + grid0.addLayout(hlay0, 25, 0, 1, 3) + + hlay0.addWidget(self.flipx_button) + hlay0.addWidget(self.flipy_button) + + separator_line = QtWidgets.QFrame() + separator_line.setFrameShape(QtWidgets.QFrame.HLine) + separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) + grid0.addWidget(separator_line, 27, 0, 1, 3) # ## Offset Title offset_title_label = QtWidgets.QLabel("%s" % self.offsetName) - grid0.addWidget(offset_title_label, 12, 0, 1, 3) + grid0.addWidget(offset_title_label, 29, 0, 1, 3) self.offx_label = QtWidgets.QLabel('%s:' % _("X val")) self.offx_label.setToolTip( @@ -223,16 +324,16 @@ class ToolTransform(AppTool): self.offx_entry.set_precision(self.decimals) self.offx_entry.setMinimum(-1e6) - self.offx_button = FCButton() + self.offx_button = FCButton(_("Offset X")) self.offx_button.setToolTip( _("Offset the selected object(s).\n" "The point of reference is the middle of\n" "the bounding box for all selected objects.\n")) self.offx_button.setMinimumWidth(90) - grid0.addWidget(self.offx_label, 13, 0) - grid0.addWidget(self.offx_entry, 13, 1) - grid0.addWidget(self.offx_button, 13, 2) + grid0.addWidget(self.offx_label, 31, 0) + grid0.addWidget(self.offx_entry, 31, 1) + grid0.addWidget(self.offx_button, 31, 2) self.offy_label = QtWidgets.QLabel('%s:' % _("Y val")) self.offy_label.setToolTip( @@ -243,91 +344,35 @@ class ToolTransform(AppTool): self.offy_entry.set_precision(self.decimals) self.offy_entry.setMinimum(-1e6) - self.offy_button = FCButton() + self.offy_button = FCButton(_("Offset Y")) self.offy_button.setToolTip( _("Offset the selected object(s).\n" "The point of reference is the middle of\n" "the bounding box for all selected objects.\n")) self.offy_button.setMinimumWidth(90) - grid0.addWidget(self.offy_label, 14, 0) - grid0.addWidget(self.offy_entry, 14, 1) - grid0.addWidget(self.offy_button, 14, 2) + grid0.addWidget(self.offy_label, 32, 0) + grid0.addWidget(self.offy_entry, 32, 1) + grid0.addWidget(self.offy_button, 32, 2) separator_line = QtWidgets.QFrame() separator_line.setFrameShape(QtWidgets.QFrame.HLine) separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) - grid0.addWidget(separator_line, 15, 0, 1, 3) - - # ## Flip Title - flip_title_label = QtWidgets.QLabel("%s" % self.flipName) - grid0.addWidget(flip_title_label, 16, 0, 1, 3) - - self.flipx_button = FCButton() - self.flipx_button.setToolTip( - _("Flip the selected object(s) over the X axis.") - ) - - self.flipy_button = FCButton() - self.flipy_button.setToolTip( - _("Flip the selected object(s) over the X axis.") - ) - - hlay0 = QtWidgets.QHBoxLayout() - grid0.addLayout(hlay0, 17, 0, 1, 3) - - hlay0.addWidget(self.flipx_button) - hlay0.addWidget(self.flipy_button) - - self.flip_ref_cb = FCCheckBox() - self.flip_ref_cb.setText('%s' % _("Mirror Reference")) - self.flip_ref_cb.setToolTip( - _("Flip the selected object(s)\n" - "around the point in Point Entry Field.\n" - "\n" - "The point coordinates can be captured by\n" - "left click on canvas together with pressing\n" - "SHIFT key. \n" - "Then click Add button to insert coordinates.\n" - "Or enter the coords in format (x, y) in the\n" - "Point Entry field and click Flip on X(Y)")) - - grid0.addWidget(self.flip_ref_cb, 18, 0, 1, 3) - - self.flip_ref_label = QtWidgets.QLabel('%s:' % _("Ref. Point")) - self.flip_ref_label.setToolTip( - _("Coordinates in format (x, y) used as reference for mirroring.\n" - "The 'x' in (x, y) will be used when using Flip on X and\n" - "the 'y' in (x, y) will be used when using Flip on Y.") - ) - self.flip_ref_entry = FCEntry() - # self.flip_ref_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) - # self.flip_ref_entry.setFixedWidth(70) - - self.flip_ref_button = FCButton() - self.flip_ref_button.setToolTip( - _("The point coordinates can be captured by\n" - "left click on canvas together with pressing\n" - "SHIFT key. Then click Add button to insert.")) - - self.ois_flip = OptionalInputSection(self.flip_ref_cb, [self.flip_ref_entry, self.flip_ref_button], logic=True) - - hlay1 = QtWidgets.QHBoxLayout() - grid0.addLayout(hlay1, 19, 0, 1, 3) - - hlay1.addWidget(self.flip_ref_label) - hlay1.addWidget(self.flip_ref_entry) - - grid0.addWidget(self.flip_ref_button, 20, 0, 1, 3) - - separator_line = QtWidgets.QFrame() - separator_line.setFrameShape(QtWidgets.QFrame.HLine) - separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) - grid0.addWidget(separator_line, 21, 0, 1, 3) + grid0.addWidget(separator_line, 34, 0, 1, 3) # ## Buffer Title buffer_title_label = QtWidgets.QLabel("%s" % self.bufferName) - grid0.addWidget(buffer_title_label, 22, 0, 1, 3) + grid0.addWidget(buffer_title_label, 35, 0, 1, 2) + + self.buffer_rounded_cb = FCCheckBox('%s' % _("Rounded")) + self.buffer_rounded_cb.setToolTip( + _("If checked then the buffer will surround the buffered shape,\n" + "every corner will be rounded.\n" + "If not checked then the buffer will follow the exact geometry\n" + "of the buffered shape.") + ) + + grid0.addWidget(self.buffer_rounded_cb, 35, 2) self.buffer_label = QtWidgets.QLabel('%s:' % _("Distance")) self.buffer_label.setToolTip( @@ -343,16 +388,16 @@ class ToolTransform(AppTool): self.buffer_entry.setWrapping(True) self.buffer_entry.set_range(-9999.9999, 9999.9999) - self.buffer_button = FCButton() + self.buffer_button = FCButton(_("Buffer D")) self.buffer_button.setToolTip( _("Create the buffer effect on each geometry,\n" "element from the selected object, using the distance.") ) self.buffer_button.setMinimumWidth(90) - grid0.addWidget(self.buffer_label, 23, 0) - grid0.addWidget(self.buffer_entry, 23, 1) - grid0.addWidget(self.buffer_button, 23, 2) + grid0.addWidget(self.buffer_label, 37, 0) + grid0.addWidget(self.buffer_entry, 37, 1) + grid0.addWidget(self.buffer_button, 37, 2) self.buffer_factor_label = QtWidgets.QLabel('%s:' % _("Value")) self.buffer_factor_label.setToolTip( @@ -369,28 +414,18 @@ class ToolTransform(AppTool): self.buffer_factor_entry.setWrapping(True) self.buffer_factor_entry.setSingleStep(1) - self.buffer_factor_button = FCButton() + self.buffer_factor_button = FCButton(_("Buffer F")) self.buffer_factor_button.setToolTip( _("Create the buffer effect on each geometry,\n" "element from the selected object, using the factor.") ) self.buffer_factor_button.setMinimumWidth(90) - grid0.addWidget(self.buffer_factor_label, 24, 0) - grid0.addWidget(self.buffer_factor_entry, 24, 1) - grid0.addWidget(self.buffer_factor_button, 24, 2) + grid0.addWidget(self.buffer_factor_label, 38, 0) + grid0.addWidget(self.buffer_factor_entry, 38, 1) + grid0.addWidget(self.buffer_factor_button, 38, 2) - self.buffer_rounded_cb = FCCheckBox('%s' % _("Rounded")) - self.buffer_rounded_cb.setToolTip( - _("If checked then the buffer will surround the buffered shape,\n" - "every corner will be rounded.\n" - "If not checked then the buffer will follow the exact geometry\n" - "of the buffered shape.") - ) - - grid0.addWidget(self.buffer_rounded_cb, 25, 0, 1, 3) - - grid0.addWidget(QtWidgets.QLabel(''), 26, 0, 1, 3) + grid0.addWidget(QtWidgets.QLabel(''), 42, 0, 1, 3) self.layout.addStretch() @@ -408,30 +443,29 @@ class ToolTransform(AppTool): self.layout.addWidget(self.reset_button) # ## Signals + self.ref_combo.currentIndexChanged.connect(self.on_reference_changed) + self.type_obj_combo.currentIndexChanged.connect(self.on_type_obj_index_changed) + self.point_button.clicked.connect(self.on_add_coords) + self.rotate_button.clicked.connect(self.on_rotate) + self.skewx_button.clicked.connect(self.on_skewx) self.skewy_button.clicked.connect(self.on_skewy) + self.scalex_button.clicked.connect(self.on_scalex) self.scaley_button.clicked.connect(self.on_scaley) + self.offx_button.clicked.connect(self.on_offx) self.offy_button.clicked.connect(self.on_offy) + self.flipx_button.clicked.connect(self.on_flipx) self.flipy_button.clicked.connect(self.on_flipy) - self.flip_ref_button.clicked.connect(self.on_flip_add_coords) + self.buffer_button.clicked.connect(self.on_buffer_by_distance) self.buffer_factor_button.clicked.connect(self.on_buffer_by_factor) self.reset_button.clicked.connect(self.set_tool_ui) - # self.rotate_entry.returnPressed.connect(self.on_rotate) - # self.skewx_entry.returnPressed.connect(self.on_skewx) - # self.skewy_entry.returnPressed.connect(self.on_skewy) - # self.scalex_entry.returnPressed.connect(self.on_scalex) - # self.scaley_entry.returnPressed.connect(self.on_scaley) - # self.offx_entry.returnPressed.connect(self.on_offx) - # self.offy_entry.returnPressed.connect(self.on_offy) - # self.buffer_entry.returnPressed.connect(self.on_buffer_by_distance) - def run(self, toggle=True): self.app.defaults.report_usage("ToolTransform()") @@ -463,130 +497,160 @@ class ToolTransform(AppTool): AppTool.install(self, icon, separator, shortcut='Alt+T', **kwargs) def set_tool_ui(self): - self.rotate_button.set_value(_("Rotate")) - self.skewx_button.set_value(_("Skew X")) - self.skewy_button.set_value(_("Skew Y")) - self.scalex_button.set_value(_("Scale X")) - self.scaley_button.set_value(_("Scale Y")) - self.scale_link_cb.set_value(True) - self.scale_zero_ref_cb.set_value(True) - self.offx_button.set_value(_("Offset X")) - self.offy_button.set_value(_("Offset Y")) - self.flipx_button.set_value(_("Flip on X")) - self.flipy_button.set_value(_("Flip on Y")) - self.flip_ref_cb.set_value(True) - self.flip_ref_button.set_value(_("Add")) - self.buffer_button.set_value(_("Buffer D")) - self.buffer_factor_button.set_value(_("Buffer F")) # ## Initialize form - if self.app.defaults["tools_transform_rotate"]: - self.rotate_entry.set_value(self.app.defaults["tools_transform_rotate"]) - else: - self.rotate_entry.set_value(0.0) + self.ref_combo.set_value(self.app.defaults["tools_transform_reference"]) + self.type_obj_combo.set_value(self.app.defaults["tools_transform_ref_object"]) + self.point_entry.set_value(self.app.defaults["tools_transform_ref_point"]) + self.rotate_entry.set_value(self.app.defaults["tools_transform_rotate"]) - if self.app.defaults["tools_transform_skew_x"]: - self.skewx_entry.set_value(self.app.defaults["tools_transform_skew_x"]) - else: - self.skewx_entry.set_value(0.0) + self.skewx_entry.set_value(self.app.defaults["tools_transform_skew_x"]) + self.skewy_entry.set_value(self.app.defaults["tools_transform_skew_y"]) + self.skew_link_cb.set_value(self.app.defaults["tools_transform_skew_link"]) - if self.app.defaults["tools_transform_skew_y"]: - self.skewy_entry.set_value(self.app.defaults["tools_transform_skew_y"]) - else: - self.skewy_entry.set_value(0.0) + self.scalex_entry.set_value(self.app.defaults["tools_transform_scale_x"]) + self.scaley_entry.set_value(self.app.defaults["tools_transform_scale_y"]) + self.scale_link_cb.set_value(self.app.defaults["tools_transform_scale_link"]) - if self.app.defaults["tools_transform_scale_x"]: - self.scalex_entry.set_value(self.app.defaults["tools_transform_scale_x"]) - else: - self.scalex_entry.set_value(1.0) + self.offx_entry.set_value(self.app.defaults["tools_transform_offset_x"]) + self.offy_entry.set_value(self.app.defaults["tools_transform_offset_y"]) - if self.app.defaults["tools_transform_scale_y"]: - self.scaley_entry.set_value(self.app.defaults["tools_transform_scale_y"]) - else: - self.scaley_entry.set_value(1.0) + self.buffer_entry.set_value(self.app.defaults["tools_transform_buffer_dis"]) + self.buffer_factor_entry.set_value(self.app.defaults["tools_transform_buffer_factor"]) + self.buffer_rounded_cb.set_value(self.app.defaults["tools_transform_buffer_corner"]) - if self.app.defaults["tools_transform_scale_link"]: - self.scale_link_cb.set_value(self.app.defaults["tools_transform_scale_link"]) - else: - self.scale_link_cb.set_value(True) + # initial state is hidden + self.point_label.hide() + self.point_entry.hide() + self.point_button.hide() - if self.app.defaults["tools_transform_scale_reference"]: - self.scale_zero_ref_cb.set_value(self.app.defaults["tools_transform_scale_reference"]) - else: - self.scale_zero_ref_cb.set_value(True) + self.type_object_label.hide() + self.type_obj_combo.hide() + self.object_combo.hide() - if self.app.defaults["tools_transform_offset_x"]: - self.offx_entry.set_value(self.app.defaults["tools_transform_offset_x"]) - else: - self.offx_entry.set_value(0.0) + def on_type_obj_index_changed(self, index): + self.object_combo.setRootModelIndex(self.app.collection.index(index, 0, QtCore.QModelIndex())) + self.object_combo.setCurrentIndex(0) + self.object_combo.obj_type = { + _("Gerber"): "Gerber", _("Excellon"): "Excellon", _("Geometry"): "Geometry" + }[self.type_obj_combo.get_value()] - if self.app.defaults["tools_transform_offset_y"]: - self.offy_entry.set_value(self.app.defaults["tools_transform_offset_y"]) - else: - self.offy_entry.set_value(0.0) + def on_reference_changed(self, index): + if index == 0 or index == 1: # "Origin" or "Selection" reference + self.point_label.hide() + self.point_entry.hide() + self.point_button.hide() - if self.app.defaults["tools_transform_mirror_reference"]: - self.flip_ref_cb.set_value(self.app.defaults["tools_transform_mirror_reference"]) - else: - self.flip_ref_cb.set_value(False) + self.type_object_label.hide() + self.type_obj_combo.hide() + self.object_combo.hide() + elif index == 2: # "Point" reference + self.point_label.show() + self.point_entry.show() + self.point_button.show() - if self.app.defaults["tools_transform_mirror_point"]: - self.flip_ref_entry.set_value(self.app.defaults["tools_transform_mirror_point"]) - else: - self.flip_ref_entry.set_value("0, 0") + self.type_object_label.hide() + self.type_obj_combo.hide() + self.object_combo.hide() + else: # "Object" reference + self.point_label.hide() + self.point_entry.hide() + self.point_button.hide() - if self.app.defaults["tools_transform_buffer_dis"]: - self.buffer_entry.set_value(self.app.defaults["tools_transform_buffer_dis"]) - else: - self.buffer_entry.set_value(0.0) + self.type_object_label.show() + self.type_obj_combo.show() + self.object_combo.show() - if self.app.defaults["tools_transform_buffer_factor"]: - self.buffer_factor_entry.set_value(self.app.defaults["tools_transform_buffer_factor"]) - else: - self.buffer_factor_entry.set_value(100.0) + def on_calculate_reference(self): + ref_val = self.ref_combo.currentIndex() - if self.app.defaults["tools_transform_buffer_corner"]: - self.buffer_rounded_cb.set_value(self.app.defaults["tools_transform_buffer_corner"]) - else: - self.buffer_rounded_cb.set_value(True) + if ref_val == 0: # "Origin" reference + return 0, 0 + elif ref_val == 1: # "Selection" reference + sel_list = self.app.collection.get_selected() + if sel_list: + xmin, ymin, xmax, ymax = self.alt_bounds(obj_list=sel_list) + px = (xmax + xmin) * 0.5 + py = (ymax + ymin) * 0.5 + return px, py + else: + self.app.inform.emit('[ERROR_NOTCL] %s' % _("No object selected.")) + return "fail" + elif ref_val == 2: # "Point" reference + point_val = self.point_entry.get_value() + try: + px, py = eval('{}'.format(point_val)) + return px, py + except Exception: + self.app.inform.emit('[WARNING_NOTCL] %s' % _("Incorrect format for Point value. Needs format X,Y")) + return "fail" + else: # "Object" reference + obj_name = self.object_combo.get_value() + ref_obj = self.app.collection.get_by_name(obj_name) + xmin, ymin, xmax, ymax = ref_obj.bounds() + px = (xmax + xmin) * 0.5 + py = (ymax + ymin) * 0.5 + return px, py + + def on_add_coords(self): + val = self.app.clipboard.text() + self.point_entry.set_value(val) def on_rotate(self): value = float(self.rotate_entry.get_value()) if value == 0: - self.app.inform.emit('[WARNING_NOTCL] %s' % - _("Rotate transformation can not be done for a value of 0.")) - self.app.worker_task.emit({'fcn': self.on_rotate_action, 'params': [value]}) - return + self.app.inform.emit('[WARNING_NOTCL] %s' % _("Rotate transformation can not be done for a value of 0.")) + return + point = self.on_calculate_reference() + if point == 'fail': + return + self.app.worker_task.emit({'fcn': self.on_rotate_action, 'params': [value, point]}) def on_flipx(self): axis = 'Y' - - self.app.worker_task.emit({'fcn': self.on_flip, 'params': [axis]}) - return + point = self.on_calculate_reference() + if point == 'fail': + return + self.app.worker_task.emit({'fcn': self.on_flip, 'params': [axis, point]}) def on_flipy(self): axis = 'X' - - self.app.worker_task.emit({'fcn': self.on_flip, 'params': [axis]}) - return - - def on_flip_add_coords(self): - val = self.app.clipboard.text() - self.flip_ref_entry.set_value(val) + point = self.on_calculate_reference() + if point == 'fail': + return + self.app.worker_task.emit({'fcn': self.on_flip, 'params': [axis, point]}) def on_skewx(self): - value = float(self.skewx_entry.get_value()) - axis = 'X' + xvalue = float(self.skewx_entry.get_value()) - self.app.worker_task.emit({'fcn': self.on_skew, 'params': [axis, value]}) - return + if xvalue == 0: + return + + if self.skew_link_cb.get_value(): + yvalue = xvalue + else: + yvalue = 0 + + axis = 'X' + point = self.on_calculate_reference() + if point == 'fail': + return + + self.app.worker_task.emit({'fcn': self.on_skew, 'params': [axis, xvalue, yvalue, point]}) def on_skewy(self): - value = float(self.skewy_entry.get_value()) - axis = 'Y' + xvalue = 0 + yvalue = float(self.skewy_entry.get_value()) - self.app.worker_task.emit({'fcn': self.on_skew, 'params': [axis, value]}) - return + if yvalue == 0: + return + + axis = 'Y' + point = self.on_calculate_reference() + if point == 'fail': + return + + self.app.worker_task.emit({'fcn': self.on_skew, 'params': [axis, xvalue, yvalue, point]}) def on_scalex(self): xvalue = float(self.scalex_entry.get_value()) @@ -602,13 +666,11 @@ class ToolTransform(AppTool): yvalue = 1 axis = 'X' - point = (0, 0) - if self.scale_zero_ref_cb.get_value(): - self.app.worker_task.emit({'fcn': self.on_scale, 'params': [axis, xvalue, yvalue, point]}) - else: - self.app.worker_task.emit({'fcn': self.on_scale, 'params': [axis, xvalue, yvalue]}) + point = self.on_calculate_reference() + if point == 'fail': + return - return + self.app.worker_task.emit({'fcn': self.on_scale, 'params': [axis, xvalue, yvalue, point]}) def on_scaley(self): xvalue = 1 @@ -620,13 +682,11 @@ class ToolTransform(AppTool): return axis = 'Y' - point = (0, 0) - if self.scale_zero_ref_cb.get_value(): - self.app.worker_task.emit({'fcn': self.on_scale, 'params': [axis, xvalue, yvalue, point]}) - else: - self.app.worker_task.emit({'fcn': self.on_scale, 'params': [axis, xvalue, yvalue]}) + point = self.on_calculate_reference() + if point == 'fail': + return - return + self.app.worker_task.emit({'fcn': self.on_scale, 'params': [axis, xvalue, yvalue, point]}) def on_offx(self): value = float(self.offx_entry.get_value()) @@ -636,7 +696,6 @@ class ToolTransform(AppTool): axis = 'X' self.app.worker_task.emit({'fcn': self.on_offset, 'params': [axis, value]}) - return def on_offy(self): value = float(self.offy_entry.get_value()) @@ -646,14 +705,12 @@ class ToolTransform(AppTool): axis = 'Y' self.app.worker_task.emit({'fcn': self.on_offset, 'params': [axis, value]}) - return def on_buffer_by_distance(self): value = self.buffer_entry.get_value() join = 1 if self.buffer_rounded_cb.get_value() else 2 self.app.worker_task.emit({'fcn': self.on_buffer_action, 'params': [value, join]}) - return def on_buffer_by_factor(self): value = self.buffer_factor_entry.get_value() / 100.0 @@ -663,14 +720,9 @@ class ToolTransform(AppTool): factor = True self.app.worker_task.emit({'fcn': self.on_buffer_action, 'params': [value, join, factor]}) - return - def on_rotate_action(self, num): + def on_rotate_action(self, num, point): obj_list = self.app.collection.get_selected() - xminlist = [] - yminlist = [] - xmaxlist = [] - ymaxlist = [] if not obj_list: self.app.inform.emit('[WARNING_NOTCL] %s' % _("No object selected. Please Select an object to rotate!")) @@ -678,25 +730,7 @@ class ToolTransform(AppTool): else: with self.app.proc_container.new(_("Appying Rotate")): try: - # first get a bounding box to fit all - for obj in obj_list: - if obj.kind == 'cncjob': - pass - else: - xmin, ymin, xmax, ymax = obj.bounds() - xminlist.append(xmin) - yminlist.append(ymin) - xmaxlist.append(xmax) - ymaxlist.append(ymax) - - # get the minimum x,y and maximum x,y for all objects selected - xminimal = min(xminlist) - yminimal = min(yminlist) - xmaximal = max(xmaxlist) - ymaximal = max(ymaxlist) - - px = 0.5 * (xminimal + xmaximal) - py = 0.5 * (yminimal + ymaximal) + px, py = point for sel_obj in obj_list: if sel_obj.kind == 'cncjob': self.app.inform.emit(_("CNCJob objects can't be rotated.")) @@ -713,44 +747,16 @@ class ToolTransform(AppTool): (_("Due of"), str(e), _("action was not executed."))) return - def on_flip(self, axis): + def on_flip(self, axis, point): obj_list = self.app.collection.get_selected() - xminlist = [] - yminlist = [] - xmaxlist = [] - ymaxlist = [] if not obj_list: - self.app.inform.emit('[WARNING_NOTCL] %s!' % - _("No object selected. Please Select an object to flip")) + self.app.inform.emit('[WARNING_NOTCL] %s!' % _("No object selected. Please Select an object to flip")) return else: with self.app.proc_container.new(_("Applying Flip")): try: - # get mirroring coords from the point entry - if self.flip_ref_cb.isChecked(): - px, py = eval('{}'.format(self.flip_ref_entry.text())) - # get mirroing coords from the center of an all-enclosing bounding box - else: - # first get a bounding box to fit all - for obj in obj_list: - if obj.kind == 'cncjob': - pass - else: - xmin, ymin, xmax, ymax = obj.bounds() - xminlist.append(xmin) - yminlist.append(ymin) - xmaxlist.append(xmax) - ymaxlist.append(ymax) - - # get the minimum x,y and maximum x,y for all objects selected - xminimal = min(xminlist) - yminimal = min(yminlist) - xmaximal = max(xmaxlist) - ymaximal = max(ymaxlist) - - px = 0.5 * (xminimal + xmaximal) - py = 0.5 * (yminimal + ymaximal) + px, py = point # execute mirroring for sel_obj in obj_list: @@ -765,8 +771,7 @@ class ToolTransform(AppTool): sel_obj.options['mirror_y'] = not sel_obj.options['mirror_y'] else: sel_obj.options['mirror_y'] = True - self.app.inform.emit('[success] %s...' % - _('Flip on the Y axis done')) + self.app.inform.emit('[success] %s...' % _('Flip on the Y axis done')) elif axis == 'Y': sel_obj.mirror('Y', (px, py)) # add information to the object that it was changed and how much @@ -783,12 +788,10 @@ class ToolTransform(AppTool): (_("Due of"), str(e), _("action was not executed."))) return - def on_skew(self, axis, num): + def on_skew(self, axis, xvalue, yvalue, point): obj_list = self.app.collection.get_selected() - xminlist = [] - yminlist = [] - if num == 0 or num == 90 or num == 180: + if xvalue in [90, 180] or yvalue in [90, 180] or xvalue == yvalue == 0: self.app.inform.emit('[WARNING_NOTCL] %s' % _("Skew transformation can not be done for 0, 90 and 180 degrees.")) return @@ -800,31 +803,16 @@ class ToolTransform(AppTool): else: with self.app.proc_container.new(_("Applying Skew")): try: - # first get a bounding box to fit all - for obj in obj_list: - if obj.kind == 'cncjob': - pass - else: - xmin, ymin, xmax, ymax = obj.bounds() - xminlist.append(xmin) - yminlist.append(ymin) - - # get the minimum x,y and maximum x,y for all objects selected - xminimal = min(xminlist) - yminimal = min(yminlist) + px, py = point for sel_obj in obj_list: if sel_obj.kind == 'cncjob': self.app.inform.emit(_("CNCJob objects can't be skewed.")) else: - if axis == 'X': - sel_obj.skew(num, 0, point=(xminimal, yminimal)) - # add information to the object that it was changed and how much - sel_obj.options['skew_x'] = num - elif axis == 'Y': - sel_obj.skew(0, num, point=(xminimal, yminimal)) - # add information to the object that it was changed and how much - sel_obj.options['skew_y'] = num + sel_obj.skew(xvalue, yvalue, point=(px, py)) + # add information to the object that it was changed and how much + sel_obj.options['skew_x'] = xvalue + sel_obj.options['skew_y'] = yvalue self.app.app_obj.object_changed.emit(sel_obj) sel_obj.plot() self.app.inform.emit('[success] %s %s %s...' % (_('Skew on the'), str(axis), _("axis done"))) @@ -835,10 +823,6 @@ class ToolTransform(AppTool): def on_scale(self, axis, xfactor, yfactor, point=None): obj_list = self.app.collection.get_selected() - xminlist = [] - yminlist = [] - xmaxlist = [] - ymaxlist = [] if not obj_list: self.app.inform.emit('[WARNING_NOTCL] %s' % _("No object selected. Please Select an object to scale!")) @@ -846,29 +830,7 @@ class ToolTransform(AppTool): else: with self.app.proc_container.new(_("Applying Scale")): try: - # first get a bounding box to fit all - for obj in obj_list: - if obj.kind == 'cncjob': - pass - else: - xmin, ymin, xmax, ymax = obj.bounds() - xminlist.append(xmin) - yminlist.append(ymin) - xmaxlist.append(xmax) - ymaxlist.append(ymax) - - # get the minimum x,y and maximum x,y for all objects selected - xminimal = min(xminlist) - yminimal = min(yminlist) - xmaximal = max(xmaxlist) - ymaximal = max(ymaxlist) - - if point is None: - px = 0.5 * (xminimal + xmaximal) - py = 0.5 * (yminimal + ymaximal) - else: - px = 0 - py = 0 + px, py = point for sel_obj in obj_list: if sel_obj.kind == 'cncjob': @@ -953,4 +915,32 @@ class ToolTransform(AppTool): (_("Due of"), str(e), _("action was not executed."))) return + @staticmethod + def alt_bounds(obj_list): + """ + Returns coordinates of rectangular bounds + of an object with geometry: (xmin, ymin, xmax, ymax). + """ + + def bounds_rec(lst): + minx = np.Inf + miny = np.Inf + maxx = -np.Inf + maxy = -np.Inf + + try: + for obj in lst: + if obj.kind != 'cncjob': + minx_, miny_, maxx_, maxy_ = bounds_rec(obj) + minx = min(minx, minx_) + miny = min(miny, miny_) + maxx = max(maxx, maxx_) + maxy = max(maxy, maxy_) + return minx, miny, maxx, maxy + except TypeError: + # it's an object, return it's bounds + return lst.bounds() + + return bounds_rec(obj_list) + # end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 31d045ac..9a837d1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ CHANGELOG for FlatCAM beta ================================================= +3.06.2020 + +- updated Transform Tool to have a selection of possible references for the transformations that are now selectable in the GUI +- Transform Tool - compacted the UI + 2.06.2020 - Tcl Shell - added a button to delete the content of the active line diff --git a/defaults.py b/defaults.py index 2c99fd77..d35c6c4f 100644 --- a/defaults.py +++ b/defaults.py @@ -499,17 +499,22 @@ class FlatCAMDefaults: "tools_calc_electro_growth": 10.0, # Transform Tool + "tools_transform_reference": _("Selection"), + "tools_transform_ref_object": _("Gerber"), + "tools_transform_ref_point": "0, 0", + "tools_transform_rotate": 90, "tools_transform_skew_x": 0.0, "tools_transform_skew_y": 0.0, + "tools_transform_skew_link": True, + "tools_transform_scale_x": 1.0, "tools_transform_scale_y": 1.0, "tools_transform_scale_link": True, - "tools_transform_scale_reference": True, + "tools_transform_offset_x": 0.0, "tools_transform_offset_y": 0.0, - "tools_transform_mirror_reference": False, - "tools_transform_mirror_point": "0.0, 0.0", + "tools_transform_buffer_dis": 0.0, "tools_transform_buffer_factor": 100.0, "tools_transform_buffer_corner": True,