From e5dab6f9c3e73f1c2b69f4344d935c42cb6b5141 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Tue, 5 Jun 2018 00:12:09 +0300 Subject: [PATCH 1/6] - added flipx and flipy functions --- FlatCAMApp.py | 84 ++++++++++++++++++++++++++++++++++++++++++++ FlatCAMGUI.py | 9 ++++- share/flipx.png | Bin 0 -> 522 bytes share/flipy.png | Bin 0 -> 514 bytes share/transfer.png | Bin 0 -> 684 bytes share/transform.png | Bin 0 -> 551 bytes 6 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 share/flipx.png create mode 100644 share/flipy.png create mode 100644 share/transfer.png create mode 100644 share/transform.png diff --git a/FlatCAMApp.py b/FlatCAMApp.py index fa2dce9c..8c58d2d2 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -536,6 +536,8 @@ class App(QtCore.QObject): self.ui.menuoptions_transfer_p2a.triggered.connect(self.on_options_project2app) self.ui.menuoptions_transfer_o2p.triggered.connect(self.on_options_object2project) self.ui.menuoptions_transfer_p2o.triggered.connect(self.on_options_project2object) + self.ui.menuoptions_transform_flipx.triggered.connect(self.on_flipx) + self.ui.menuoptions_transform_flipy.triggered.connect(self.on_flipy) self.ui.menuviewdisableall.triggered.connect(self.disable_plots) self.ui.menuviewdisableother.triggered.connect(lambda: self.disable_plots(except_current=True)) self.ui.menuviewenable.triggered.connect(self.enable_all_plots) @@ -1485,6 +1487,88 @@ class App(QtCore.QObject): # self.options2form() + def on_flipx(self): + obj_list = self.collection.get_selected() + xminlist = [] + yminlist = [] + xmaxlist = [] + ymaxlist = [] + + if not obj_list: + self.inform.emit("WARNING: No object selected.") + msg = "Please Select an object to flip!" + warningbox = QtGui.QMessageBox() + warningbox.setText(msg) + warningbox.setWindowTitle("Warning ...") + warningbox.setWindowIcon(QtGui.QIcon('share/warning.png')) + warningbox.setStandardButtons(QtGUi.QMessageBox.Ok) + warningbox.setDefaultButton(QtGui.QMessageBox.Ok) + warningbox.exec_() + else: + # first get a bounding box to fit all + for obj in obj_list: + 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) + + # execute mirroring + for obj in obj_list: + obj.mirror('X', [px, py]) + obj.plot() + self.info('Flipped on the X axis ...') + + def on_flipy(self): + obj_list = self.collection.get_selected() + xminlist = [] + yminlist = [] + xmaxlist = [] + ymaxlist = [] + + if not obj_list: + self.inform.emit("WARNING: No object selected.") + msg = "Please Select an object to flip!" + warningbox = QtGui.QMessageBox() + warningbox.setText(msg) + warningbox.setWindowTitle("Warning ...") + warningbox.setWindowIcon(QtGui.QIcon('share/warning.png')) + warningbox.setStandardButtons(QtGui.QMessageBox.Ok) + warningbox.setDefaultButton(QtGui.QMessageBox.Ok) + warningbox.exec_() + else: + # first get a bounding box to fit all + for obj in obj_list: + 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) + + # execute mirroring + for obj in obj_list: + obj.mirror('Y', [px, py]) + obj.plot() + self.info('Flipped on the Y axis ...') + def on_delete(self): """ Delete the currently selected FlatCAMObjs. diff --git a/FlatCAMGUI.py b/FlatCAMGUI.py index cff4eb12..9f5486ee 100644 --- a/FlatCAMGUI.py +++ b/FlatCAMGUI.py @@ -105,7 +105,7 @@ class FlatCAMGUI(QtGui.QMainWindow): ### Options ### self.menuoptions = self.menu.addMenu('&Options') - self.menuoptions_transfer = self.menuoptions.addMenu('Transfer options') + self.menuoptions_transfer = self.menuoptions.addMenu(QtGui.QIcon('share/transfer.png'), 'Transfer options') self.menuoptions_transfer_a2p = self.menuoptions_transfer.addAction("Application to Project") self.menuoptions_transfer_p2a = self.menuoptions_transfer.addAction("Project to Application") self.menuoptions_transfer_p2o = self.menuoptions_transfer.addAction("Project to Object") @@ -113,6 +113,13 @@ class FlatCAMGUI(QtGui.QMainWindow): self.menuoptions_transfer_a2o = self.menuoptions_transfer.addAction("Application to Object") self.menuoptions_transfer_o2a = self.menuoptions_transfer.addAction("Object to Application") + ### Transform Object ### + self.menuoptions_transform = self.menuoptions.addMenu(QtGui.QIcon('share/transform.png'), '&Transform Object') + self.menuoptions_transform_flipx = self.menuoptions_transform.addAction(QtGui.QIcon('share/flipx.png'), + "Flip Selection on &X axis") + self.menuoptions_transform_flipy = self.menuoptions_transform.addAction(QtGui.QIcon('share/flipy.png'), + "Flip Selection on &Y axis") + ### View ### self.menuview = self.menu.addMenu('&View') self.menuviewdisableall = self.menuview.addAction(QtGui.QIcon('share/clear_plot16.png'), 'Disable all plots') diff --git a/share/flipx.png b/share/flipx.png new file mode 100644 index 0000000000000000000000000000000000000000..84d6a0a6207a7e234f671023c296b0e19c2eb2c7 GIT binary patch literal 522 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^xl_H+M9WCijSl0AZa z85pY67#JE_7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG#NP=YDR+ueoXe|!I#{XiaP zfk$L914Ee%2s17}Q~@%cy~NYkmHiQ`D383t@xb@TfI>GsT^vI+&bMCN=;c%>bM)i+ zw{e<_GCCY?xwIE_v~_4iJF!fhcW9~li`c3LxkZK@3p90BsK!VsdE`#$Q25WQRe6PWSj%mmQX5#JgUfnV$H_By7!- z1GA!+v%S<133;ozK^`gYP2MWX_vR~Yr8_FwvZ!c%o#rdhRnNOL6 zOY51h8JbPkckH+b^rLEtYeY#(Vo9o1a#1RfVlXl=w9qv$)ip8*@ zE`7n}Lc?aq!rjs;Hyq5(g=W{Un<@E@&BUy!$4PK@K2D?NY%?PN}v7CMhd8i!PC{x JWt~$(6957Jvfuy! literal 0 HcmV?d00001 diff --git a/share/transfer.png b/share/transfer.png new file mode 100644 index 0000000000000000000000000000000000000000..2ba13e89dfe94ba280119152935d09a771394128 GIT binary patch literal 684 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM3?#3wJbMaAv7|ftIx;Y9?C1WI$O_~uBzpw; zGB8xBF)%c=FfjZA3N^f7U???UV0e|lz+g3lfkC`r&aOZkpv2MupAc6d&CJL$n9=|L z|La8076BbZa{rm4H4;K8MesDv~X`lg&N#5=*X72ktE&@561s;*b3=DTmL6~t(`{8Dw zAbW|YuPgf_R#6^h-jB7Xe1Jloo-U3d8t0P}7DyNv85$=|Pe@EnKB9K?#JPhfkMd4= z=*YaM=Fc*wCZ`^rLm3V(A!n|H#5helD55=K(y_J7%VjNMS)ZLaVU+ZdU3u}GJxoqa z?~2`ARZ~Snb<$R?%$|{zmATlu$NS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5lweBoc6VX;-`;;_Kaj^> z;_2(k{)knSN0Ili_lk2242=4oE{-7n?-P#BHU}Zr>03I)7?*RX zpO6grRbq07+4UilOrx_4Q?f@u5Cc<8&NaM8wG7N2XFGKKN0*25;bWHB9y%g#SI8Gp~2@ZpT6ZqwIBp$R4vHH=ug)Z;Rh zyF2`!rRXg@*}>7f+N0Nx1Z$!nStaBsv_C}dP?vl=lJ5{bbAM2TU^^@&pNv5OS zlE-;}d=9#qExm42j@t=U$?Ho(KRwrowe|Ly5FwtqN3RtanyMwP5hW>!C8<`)MX5lF z!N|bSLf61l*T^u$(8$Wz*vimY*TBTez+jEEP$-Iq-29Zxv`X9>?z*jd3Dm&g>FVdQ I&MBb@07UfCs{jB1 literal 0 HcmV?d00001 From 41e471d9d524ade3ad61047b77fca3dede35d6ed Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Tue, 5 Jun 2018 03:19:58 +0300 Subject: [PATCH 2/6] - changed the self.info() functions to signal self.inform.emit() and the signal is emitted only once per operation not for each object in selection. --- FlatCAMApp.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 8c58d2d2..5f0af33c 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -1526,7 +1526,7 @@ class App(QtCore.QObject): for obj in obj_list: obj.mirror('X', [px, py]) obj.plot() - self.info('Flipped on the X axis ...') + self.inform.emit('Flipped on the X axis ...') def on_flipy(self): obj_list = self.collection.get_selected() @@ -1567,7 +1567,7 @@ class App(QtCore.QObject): for obj in obj_list: obj.mirror('Y', [px, py]) obj.plot() - self.info('Flipped on the Y axis ...') + self.inform.emit('Flipped on the Y axis ...') def on_delete(self): """ From 74104ec19f8fa2778756361ac467f86fa285464b Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Tue, 5 Jun 2018 13:43:14 +0300 Subject: [PATCH 3/6] - modified the pull request to include along the flipX, flipY commands also the Rotate, SkewX and SkewY commands. Fix for issue #235 All perform the same in regard of multiple object selection. --- FlatCAMApp.py | 126 ++++++++++++++++++++++++++++++++++++++++- FlatCAMGUI.py | 10 ++++ GUIElements.py | 79 ++++++++++++++++++++++---- camlib.py | 142 +++++++++++++++++++++++++++++++++++++++++++++++ share/rotate.png | Bin 0 -> 955 bytes share/skewX.png | Bin 0 -> 1385 bytes share/skewY.png | Bin 0 -> 656 bytes 7 files changed, 346 insertions(+), 11 deletions(-) create mode 100644 share/rotate.png create mode 100644 share/skewX.png create mode 100644 share/skewY.png diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 5f0af33c..309bacdb 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -35,6 +35,7 @@ from FlatCAMCommon import LoudDict from FlatCAMShell import FCShell from FlatCAMDraw import FlatCAMDraw from FlatCAMProcess import * +from GUIElements import FCInputDialog from MeasurementTool import Measurement from DblSidedTool import DblSidedTool import tclCommands @@ -538,6 +539,9 @@ class App(QtCore.QObject): self.ui.menuoptions_transfer_p2o.triggered.connect(self.on_options_project2object) self.ui.menuoptions_transform_flipx.triggered.connect(self.on_flipx) self.ui.menuoptions_transform_flipy.triggered.connect(self.on_flipy) + self.ui.menuoptions_transform_skewx.triggered.connect(self.on_skewx) + self.ui.menuoptions_transform_skewy.triggered.connect(self.on_skewy) + self.ui.menuoptions_transform_rotate.triggered.connect(self.on_rotate) self.ui.menuviewdisableall.triggered.connect(self.disable_plots) self.ui.menuviewdisableother.triggered.connect(lambda: self.disable_plots(except_current=True)) self.ui.menuviewenable.triggered.connect(self.enable_all_plots) @@ -1501,7 +1505,7 @@ class App(QtCore.QObject): warningbox.setText(msg) warningbox.setWindowTitle("Warning ...") warningbox.setWindowIcon(QtGui.QIcon('share/warning.png')) - warningbox.setStandardButtons(QtGUi.QMessageBox.Ok) + warningbox.setStandardButtons(QtGui.QMessageBox.Ok) warningbox.setDefaultButton(QtGui.QMessageBox.Ok) warningbox.exec_() else: @@ -1569,6 +1573,126 @@ class App(QtCore.QObject): obj.plot() self.inform.emit('Flipped on the Y axis ...') + def on_rotate(self, preset=None): + obj_list = self.collection.get_selected() + xminlist = [] + yminlist = [] + xmaxlist = [] + ymaxlist = [] + + if not obj_list: + self.inform.emit("WARNING: No object selected.") + msg = "Please Select an object to rotate!" + warningbox = QtGui.QMessageBox() + warningbox.setText(msg) + warningbox.setWindowTitle("Warning ...") + warningbox.setWindowIcon(QtGui.QIcon('share/warning.png')) + warningbox.setStandardButtons(QtGui.QMessageBox.Ok) + warningbox.setDefaultButton(QtGui.QMessageBox.Ok) + warningbox.exec_() + else: + if preset is not None: + rotatebox = FCInputDialog() + num, ok = rotatebox.get_value(title='Transform', message='Enter the Angle value', + min=-360, max=360, decimals=3) + else: + num = preset + ok = True + + if ok: + for sel_obj in obj_list: + # first get a bounding box to fit all + for obj in obj_list: + 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) + + sel_obj.rotate(-num, point=(px, py)) + sel_obj.plot() + self.inform.emit('Object was rotated ...') + + def on_skewx(self): + obj_list = self.collection.get_selected() + xminlist = [] + yminlist = [] + + if not obj_list: + self.inform.emit("WARNING: No object selected.") + msg = "Please Select an object to skew/shear!" + warningbox = QtGui.QMessageBox() + warningbox.setText(msg) + warningbox.setWindowTitle("Warning ...") + warningbox.setWindowIcon(QtGui.QIcon('share/warning.png')) + warningbox.setStandardButtons(QtGui.QMessageBox.Ok) + warningbox.setDefaultButton(QtGui.QMessageBox.Ok) + warningbox.exec_() + else: + skewxbox = FCInputDialog() + num, ok = skewxbox.get_value(title='Transform', message='Enter the Angle value', + min=-360, max=360, decimals=3) + if ok: + # first get a bounding box to fit all + for obj in obj_list: + 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) + + for obj in obj_list: + obj.skew(num, 0, point=(xminimal, yminimal)) + obj.plot() + self.inform.emit('Object was skewed on X axis ...') + + def on_skewy(self): + obj_list = self.collection.get_selected() + xminlist = [] + yminlist = [] + + + if not obj_list: + self.inform.emit("WARNING: No object selected.") + msg = "Please Select an object to skew/shear!" + warningbox = QtGui.QMessageBox() + warningbox.setText(msg) + warningbox.setWindowTitle("Warning ...") + warningbox.setWindowIcon(QtGui.QIcon('share/warning.png')) + warningbox.setStandardButtons(QtGui.QMessageBox.Ok) + warningbox.setDefaultButton(QtGui.QMessageBox.Ok) + warningbox.exec_() + else: + skewybox = FCInputDialog() + num, ok = skewybox.get_value(title='Transform', message='Enter the Angle value', + min=-360, max=360, decimals=3) + if ok: + # first get a bounding box to fit all + for obj in obj_list: + 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) + + for obj in obj_list: + obj.skew(0, num, point=(xminimal, yminimal)) + obj.plot() + self.inform.emit('Object was skewed on Y axis ...') + def on_delete(self): """ Delete the currently selected FlatCAMObjs. diff --git a/FlatCAMGUI.py b/FlatCAMGUI.py index 9f5486ee..7531a91b 100644 --- a/FlatCAMGUI.py +++ b/FlatCAMGUI.py @@ -119,6 +119,16 @@ class FlatCAMGUI(QtGui.QMainWindow): "Flip Selection on &X axis") self.menuoptions_transform_flipy = self.menuoptions_transform.addAction(QtGui.QIcon('share/flipy.png'), "Flip Selection on &Y axis") + # Separator + self.menuoptions_transform.addSeparator() + self.menuoptions_transform_skewx = self.menuoptions_transform.addAction(QtGui.QIcon('share/skewx.png'), + "&Skew Selection on X axis") + self.menuoptions_transform_skewy = self.menuoptions_transform.addAction(QtGui.QIcon('share/skewy.png'), + "S&kew Selection on Y axis") + # Separator + self.menuoptions_transform.addSeparator() + self.menuoptions_transform_rotate = self.menuoptions_transform.addAction(QtGui.QIcon('share/rotate.png'), + "&Rotate Selection") ### View ### self.menuview = self.menu.addMenu('&View') diff --git a/GUIElements.py b/GUIElements.py index d46aaf24..42fd3270 100644 --- a/GUIElements.py +++ b/GUIElements.py @@ -80,13 +80,15 @@ class LengthEntry(QtGui.QLineEdit): self.readyToEdit = True def mousePressEvent(self, e, Parent=None): - super(LengthEntry, self).mousePressEvent(e) # required to deselect on 2e click + # required to deselect on 2nd click + super(LengthEntry, self).mousePressEvent(e) if self.readyToEdit: self.selectAll() self.readyToEdit = False def focusOutEvent(self, e): - super(LengthEntry, self).focusOutEvent(e) # required to remove cursor on focusOut + # required to remove cursor on focusOut + super(LengthEntry, self).focusOutEvent(e) self.deselect() self.readyToEdit = True @@ -126,13 +128,15 @@ class FloatEntry(QtGui.QLineEdit): self.readyToEdit = True def mousePressEvent(self, e, Parent=None): - super(FloatEntry, self).mousePressEvent(e) # required to deselect on 2e click + # required to deselect on 2nd click + super(FloatEntry, self).mousePressEvent(e) if self.readyToEdit: self.selectAll() self.readyToEdit = False def focusOutEvent(self, e): - super(FloatEntry, self).focusOutEvent(e) # required to remove cursor on focusOut + # required to remove cursor on focusOut + super(FloatEntry, self).focusOutEvent(e) self.deselect() self.readyToEdit = True @@ -166,13 +170,15 @@ class IntEntry(QtGui.QLineEdit): self.readyToEdit = True def mousePressEvent(self, e, Parent=None): - super(IntEntry, self).mousePressEvent(e) # required to deselect on 2e click + # required to deselect on 2nd click + super(IntEntry, self).mousePressEvent(e) if self.readyToEdit: self.selectAll() self.readyToEdit = False def focusOutEvent(self, e): - super(IntEntry, self).focusOutEvent(e) # required to remove cursor on focusOut + # required to remove cursor on focusOut + super(IntEntry, self).focusOutEvent(e) self.deselect() self.readyToEdit = True @@ -199,13 +205,15 @@ class FCEntry(QtGui.QLineEdit): self.readyToEdit = True def mousePressEvent(self, e, Parent=None): - super(FCEntry, self).mousePressEvent(e) # required to deselect on 2e click + # required to deselect on 2nd click + super(FCEntry, self).mousePressEvent(e) if self.readyToEdit: self.selectAll() self.readyToEdit = False def focusOutEvent(self, e): - super(FCEntry, self).focusOutEvent(e) # required to remove cursor on focusOut + # required to remove cursor on focusOut + super(FCEntry, self).focusOutEvent(e) self.deselect() self.readyToEdit = True @@ -222,13 +230,15 @@ class EvalEntry(QtGui.QLineEdit): self.readyToEdit = True def mousePressEvent(self, e, Parent=None): - super(EvalEntry, self).mousePressEvent(e) # required to deselect on 2e click + # required to deselect on 2nd click + super(EvalEntry, self).mousePressEvent(e) if self.readyToEdit: self.selectAll() self.readyToEdit = False def focusOutEvent(self, e): - super(EvalEntry, self).focusOutEvent(e) # required to remove cursor on focusOut + # required to remove cursor on focusOut + super(EvalEntry, self).focusOutEvent(e) self.deselect() self.readyToEdit = True @@ -275,6 +285,55 @@ class FCTextArea(QtGui.QPlainTextEdit): def get_value(self): return str(self.toPlainText()) +class FCInputDialog(QtGui.QInputDialog): + def __init__(self, parent=None, ok=False, val=None): + super(FCInputDialog, self).__init__(parent) + self.allow_empty = ok + self.empty_val = val + self.readyToEdit = True + + def mousePressEvent(self, e, Parent=None): + # required to deselect on 2nd click + super(FCInputDialog, self).mousePressEvent(e) + if self.readyToEdit: + self.selectAll() + self.readyToEdit = False + + def focusOutEvent(self, e): + # required to remove cursor on focusOut + super(FCInputDialog, self).focusOutEvent(e) + self.deselect() + self.readyToEdit = True + + def get_value(self, title=None, message=None, min=None, max=None, decimals=None): + if title is None: + title = "FlatCAM action" + if message is None: + message = "Please enter the value: " + if min is None: + min = 0.0 + if max is None: + max = 100.0 + if decimals is None: + decimals = 1 + self.val,self.ok = self.getDouble(self, title, message, min=min, + max=max, decimals=decimals) + return [self.val,self.ok] + + def set_value(self, val): + pass + + +class FCButton(QtGui.QPushButton): + def __init__(self, parent=None): + super(FCButton, self).__init__(parent) + + def get_value(self): + return self.isChecked() + + def set_value(self, val): + self.setText(str(val)) + class VerticalScrollArea(QtGui.QScrollArea): """ diff --git a/camlib.py b/camlib.py index 55301bb3..52b1c484 100644 --- a/camlib.py +++ b/camlib.py @@ -1051,6 +1051,46 @@ class Geometry(object): self.solid_geometry = mirror_geom(self.solid_geometry) + def skew(self, angle_x=None, angle_y=None, point=None): + """ + Shear/Skew the geometries of an object by angles along x and y dimensions. + + Parameters + ---------- + xs, ys : float, float + The shear angle(s) for the x and y axes respectively. These can be + specified in either degrees (default) or radians by setting + use_radians=True. + + See shapely manual for more information: + http://toblerity.org/shapely/manual.html#affine-transformations + """ + if angle_y is None: + angle_y = 0.0 + if angle_x is None: + angle_x = 0.0 + if point is None: + self.solid_geometry = affinity.skew(self.solid_geometry, angle_x, angle_y, + origin=(0, 0)) + else: + px, py = point + self.solid_geometry = affinity.skew(self.solid_geometry, angle_x, angle_y, + origin=(px, py)) + return + + def rotate(self, angle, point=None): + """ + Rotate an object by a given angle around given coords (point) + :param angle: + :param point: + :return: + """ + if point is None: + self.solid_geometry = affinity.rotate(self.solid_geometry, angle, origin='center') + else: + px, py = point + self.solid_geometry = affinity.rotate(self.solid_geometry, angle, origin=(px, py)) + return class ApertureMacro: """ @@ -2869,6 +2909,60 @@ class Excellon(Geometry): # Recreate geometry self.create_geometry() + def skew(self, angle_x=None, angle_y=None, point=None): + """ + Shear/Skew the geometries of an object by angles along x and y dimensions. + Tool sizes, feedrates an Z-plane dimensions are untouched. + + Parameters + ---------- + angle_x, angle_y: float, float + The shear angle(s) for the x and y axes respectively. These can be + specified in either degrees (default) or radians by setting + use_radians=True. + point: point of origin for skew, tuple of coordinates + + See shapely manual for more information: + http://toblerity.org/shapely/manual.html#affine-transformations + """ + + if angle_y is None: + angle_y = 0.0 + if angle_x is None: + angle_x = 0.0 + if point is None: + # Drills + for drill in self.drills: + drill['point'] = affinity.skew(drill['point'], angle_x, angle_y, + origin=(0, 0)) + else: + # Drills + px, py = point + for drill in self.drills: + drill['point'] = affinity.skew(drill['point'], angle_x, angle_y, + origin=(px, py)) + + self.create_geometry() + + def rotate(self, angle, point=None): + """ + Rotate the geometry of an object by an angle around the 'point' coordinates + :param angle: + :param point: point around which to rotate + :return: + """ + if point is None: + # Drills + for drill in self.drills: + drill['point'] = affinity.rotate(drill['point'], angle, origin='center') + else: + # Drills + px, py = point + for drill in self.drills: + drill['point'] = affinity.rotate(drill['point'], angle, origin=(px, py)) + + self.create_geometry() + def convert_units(self, units): factor = Geometry.convert_units(self, units) @@ -3535,6 +3629,54 @@ class CNCjob(Geometry): self.create_geometry() + def skew(self, angle_x=None, angle_y=None, point=None): + """ + Shear/Skew the geometries of an object by angles along x and y dimensions. + + Parameters + ---------- + angle_x, angle_y : float, float + The shear angle(s) for the x and y axes respectively. These can be + specified in either degrees (default) or radians by setting + use_radians=True. + point: tupple of coordinates . Origin for skew. + + See shapely manual for more information: + http://toblerity.org/shapely/manual.html#affine-transformations + """ + + if angle_y is None: + angle_y = 0.0 + if angle_x is None: + angle_x = 0.0 + if point == None: + for g in self.gcode_parsed: + g['geom'] = affinity.skew(g['geom'], angle_x, angle_y, + origin=(0, 0)) + else: + for g in self.gcode_parsed: + g['geom'] = affinity.skew(g['geom'], angle_x, angle_y, + origin=point) + + self.create_geometry() + + def rotate(self, angle, point=None): + """ + Rotate the geometrys of an object by an given angle around the coordinates of the 'point' + :param angle: + :param point: + :return: + """ + if point is None: + for g in self.gcode_parsed: + g['geom'] = affinity.rotate(g['geom'], angle, origin='center') + else: + px, py = point + for g in self.gcode_parsed: + g['geom'] = affinity.rotate(g['geom'], angle, origin=(px, py)) + + self.create_geometry() + def export_svg(self, scale_factor=0.00): """ Exports the CNC Job as a SVG Element diff --git a/share/rotate.png b/share/rotate.png new file mode 100644 index 0000000000000000000000000000000000000000..1cbdfa42a92b614d0ff5a6916b283a92c491412a GIT binary patch literal 955 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}a}tmUKs7M+SzC{oH>NS%G|oWRD45dJguM!v-tY$DUh!@P+6=(yLU`q0KcVS>y)vIg-4nJa0`PlBg3pY5H=O_D8JzLUOEof{cMSF|G4-aSVw#{PcQ!$COYBwh#B; z?0B7{`8J5hTgzAE(KHcBr&2+|YrY#?>X#^R9I|$tuxMB30x_ngE}ED0Hh)>ht@(XX zlhY(Uo!c>I0=A?k{-1EP=UC3U8E0?1ChvayuE+Ret-zNW^ZS+OtM5JEbH03E@x88 zncRGD3CAY0S%O*n$}e!HD*kz<{Qb38*AvdmhqiOE*}ULAezj$DTybrXNWXR7=6O}w zOV|2wnX=!R#pCF9a1z_;1$*_l+V4&a5erY689t@eInS$m(e2M^t+mt4%St}UZ*e*s zxc8m$3vu^LrxxAsFqn65`hz%oZfl{-sf?S-IEBNOPd{9>f7Q)Na*pRZ3NBna-FD>L z1S_q5N21K*%yeF6{NUo3C^)9LbeY{^txsIXtmogg?c1ReQv2xHvbfd0+~>>W_T3l% ze6H#4uGv3$Ep+!>*y+-I?R<9b1oN#c;%~NxS!|rJ;Ck?>9RQ#mFCGu-fk&wyL5WtgBb;95~@V{wK(T54S$%jcZE_-mT#1Z<@(hei{*AE zoqRS)>uPaakWg3IuGnRHZ?~VeUgzz#;&=~d;-TAelcs;|>9T)&ZTggu?G>kc1k!qM z=J#(tr21a9ob^r7_P2EbxjU?`zL}%#y-v?nyG3%o^@9zCuBq2gJ={~UO_ zQmvAUQh^kMk%6Iwu7Rnpp=pSrg_W^^6_9OUVr5_uy6Ek76b-rgDVb@NxHYK!56uQ@ OVDNPHb6Mw<&;$T!Jf-mf literal 0 HcmV?d00001 diff --git a/share/skewX.png b/share/skewX.png new file mode 100644 index 0000000000000000000000000000000000000000..93f726eda890b361c84119e2d12bf834684ee622 GIT binary patch literal 1385 zcmcgrUuYY39KYJGr0c@K&gzPP&Ks=$Npk;^OYUe^libCIE@_qo7bebK?tX2K_Wtbd z(pAvloc_$2N|mnlj=j+gAX3u z{r=qN^Zk7PzMjoI@7+DH8$l3nYJ|_h^(puDbi(fnI`T4H_S-_y&gr)tazK=4PF?{+2+}Dlg`!o zRpcmkG?XUNMgo+Tky#VuXEOz9c2Z(xZ1@m5SjbmsN zVoye~EvJgGe=@nCg z^%y_UCd1s_U&sYQZ@4j2fkhFgcwMTfK(kYP6oY>PvLbVIl8i8XJSoJ(ae*Mo2*C^S z1kIC4hN0;=*|xD0m*GPs%LJJu9SV`8Ktw2l;lm8W#)9z}MQ?Lcnq`Zc1lqa^)NOG? z_vLa46Nt8M7Ib~8-2vIMZtGTAH_${rgdSBiS+81dakgURfvHRbIce%D+RQGe?4TZw z@idtvg%70X|3mTy*mxcpfCNLUwxu}@!UdJfBzq+-~Q&syB*r8;`GlK zx+733>+JG7YZrGd4>}#o-I4vChcn$@&aw49P)|S7SNkbnyPoGxJ>sc7>Zy@F-|b&YE~H^^eDIrB VzhTWklr?(XqNIcjzZ5@l`fryJrs4nq literal 0 HcmV?d00001 diff --git a/share/skewY.png b/share/skewY.png new file mode 100644 index 0000000000000000000000000000000000000000..a56410de5f8773cb8e96b54227f67f30c088a5da GIT binary patch literal 656 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU3?z3ec*FxKmUKs7M+SzC{oH>NS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5l&A>s332`Z|38pPX8^Qx z+nX8xfVQfY1o;IsFdW`%d8yrodDoV=OT9Ubzh%m8TwS62aGL#!C?Tys>gx`;o(a4W z%}}}%sDm-d+uensgH_f8$hqk0;uvCa`t6m|LQM)1ZHXpFKW}t6eW>rwf7NfzX^R(a zSmA2CS9@yuM_z8hUmtVsZN2EYyxs5ntEBVs6B0!*j-7#VPHsmeZpx zZY~rQx!K~U(7L{aZ{ezY{cQ3TJyQbs^7;-x?A+QdkaW<%!ZADG$)g;j8Tw&Y7e_zN ze)>Fd!%pV`Wg^6wfe0|EIAy4Dg zo3Gc|Ou2JLAw+C_wDYY?yJgn2Cv9k#cl!`+%V@Z+pmTk#&r6`^R7+eVN>UO_QmvAU zQh^kMk%6Iwu7Rnpu|bG|ft9g|m7#?;kYQkOFipJ}MMG|WN@iLmrUqkUh=vc6XF$Ox v39=zLKdq!Zu_%?nF(p4KRlzeiF+DXXH8G{K@MJ0|nJ{>|`njxgN@xNA?bpag literal 0 HcmV?d00001 From 782f784dee4455a26a2a88a3c2db3fffb38147a6 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Tue, 5 Jun 2018 14:49:13 +0300 Subject: [PATCH 4/6] - fixed a bug that created desync on rotate multiple objects that were offseted from origin --- FlatCAMApp.py | 1 + 1 file changed, 1 insertion(+) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 309bacdb..58ea32bf 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -1615,6 +1615,7 @@ class App(QtCore.QObject): xmaximal = max(xmaxlist) ymaximal = max(ymaxlist) + for sel_obj in obj_list: px = 0.5 * (xminimal + xmaximal) py = 0.5 * (yminimal + ymaximal) From 01dcfb69a6944b99965a327e958729fa866ebf89 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Tue, 5 Jun 2018 22:13:33 +0300 Subject: [PATCH 5/6] - remade the functions into a Tool framework - renamed the other current files that holds 'Tools' to start with Tool like Tcl commands start with TclCommand --- FlatCAMApp.py | 221 +--------------- FlatCAMGUI.py | 23 +- DblSidedTool.py => ToolDblSided.py | 0 MeasurementTool.py => ToolMeasurement.py | 0 ToolTransform.py | 319 +++++++++++++++++++++++ 5 files changed, 331 insertions(+), 232 deletions(-) rename DblSidedTool.py => ToolDblSided.py (100%) rename MeasurementTool.py => ToolMeasurement.py (100%) create mode 100644 ToolTransform.py diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 58ea32bf..3fe46be8 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -36,8 +36,9 @@ from FlatCAMShell import FCShell from FlatCAMDraw import FlatCAMDraw from FlatCAMProcess import * from GUIElements import FCInputDialog -from MeasurementTool import Measurement -from DblSidedTool import DblSidedTool +from ToolMeasurement import Measurement +from ToolDblSided import DblSidedTool +from ToolTransform import ToolTransform import tclCommands from camlib import * @@ -537,11 +538,6 @@ class App(QtCore.QObject): self.ui.menuoptions_transfer_p2a.triggered.connect(self.on_options_project2app) self.ui.menuoptions_transfer_o2p.triggered.connect(self.on_options_object2project) self.ui.menuoptions_transfer_p2o.triggered.connect(self.on_options_project2object) - self.ui.menuoptions_transform_flipx.triggered.connect(self.on_flipx) - self.ui.menuoptions_transform_flipy.triggered.connect(self.on_flipy) - self.ui.menuoptions_transform_skewx.triggered.connect(self.on_skewx) - self.ui.menuoptions_transform_skewy.triggered.connect(self.on_skewy) - self.ui.menuoptions_transform_rotate.triggered.connect(self.on_rotate) self.ui.menuviewdisableall.triggered.connect(self.disable_plots) self.ui.menuviewdisableother.triggered.connect(lambda: self.disable_plots(except_current=True)) self.ui.menuviewenable.triggered.connect(self.enable_all_plots) @@ -586,10 +582,12 @@ class App(QtCore.QObject): self.dblsidedtool = DblSidedTool(self) self.dblsidedtool.install(icon=QtGui.QIcon('share/doubleside16.png'), separator=True) - self.measeurement_tool = Measurement(self) - self.measeurement_tool.install(icon=QtGui.QIcon('share/measure16.png')) + self.measurement_tool = Measurement(self) + self.measurement_tool.install(icon=QtGui.QIcon('share/measure16.png')) + self.ui.measure_btn.triggered.connect(self.measurement_tool.run) - self.ui.measure_btn.triggered.connect(self.measeurement_tool.run) + self.transform_tool = ToolTransform(self) + self.transform_tool.install(icon=QtGui.QIcon('share/transform.png'), pos=self.ui.menuedit) self.draw = FlatCAMDraw(self, disabled=True) @@ -1491,209 +1489,6 @@ class App(QtCore.QObject): # self.options2form() - def on_flipx(self): - obj_list = self.collection.get_selected() - xminlist = [] - yminlist = [] - xmaxlist = [] - ymaxlist = [] - - if not obj_list: - self.inform.emit("WARNING: No object selected.") - msg = "Please Select an object to flip!" - warningbox = QtGui.QMessageBox() - warningbox.setText(msg) - warningbox.setWindowTitle("Warning ...") - warningbox.setWindowIcon(QtGui.QIcon('share/warning.png')) - warningbox.setStandardButtons(QtGui.QMessageBox.Ok) - warningbox.setDefaultButton(QtGui.QMessageBox.Ok) - warningbox.exec_() - else: - # first get a bounding box to fit all - for obj in obj_list: - 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) - - # execute mirroring - for obj in obj_list: - obj.mirror('X', [px, py]) - obj.plot() - self.inform.emit('Flipped on the X axis ...') - - def on_flipy(self): - obj_list = self.collection.get_selected() - xminlist = [] - yminlist = [] - xmaxlist = [] - ymaxlist = [] - - if not obj_list: - self.inform.emit("WARNING: No object selected.") - msg = "Please Select an object to flip!" - warningbox = QtGui.QMessageBox() - warningbox.setText(msg) - warningbox.setWindowTitle("Warning ...") - warningbox.setWindowIcon(QtGui.QIcon('share/warning.png')) - warningbox.setStandardButtons(QtGui.QMessageBox.Ok) - warningbox.setDefaultButton(QtGui.QMessageBox.Ok) - warningbox.exec_() - else: - # first get a bounding box to fit all - for obj in obj_list: - 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) - - # execute mirroring - for obj in obj_list: - obj.mirror('Y', [px, py]) - obj.plot() - self.inform.emit('Flipped on the Y axis ...') - - def on_rotate(self, preset=None): - obj_list = self.collection.get_selected() - xminlist = [] - yminlist = [] - xmaxlist = [] - ymaxlist = [] - - if not obj_list: - self.inform.emit("WARNING: No object selected.") - msg = "Please Select an object to rotate!" - warningbox = QtGui.QMessageBox() - warningbox.setText(msg) - warningbox.setWindowTitle("Warning ...") - warningbox.setWindowIcon(QtGui.QIcon('share/warning.png')) - warningbox.setStandardButtons(QtGui.QMessageBox.Ok) - warningbox.setDefaultButton(QtGui.QMessageBox.Ok) - warningbox.exec_() - else: - if preset is not None: - rotatebox = FCInputDialog() - num, ok = rotatebox.get_value(title='Transform', message='Enter the Angle value', - min=-360, max=360, decimals=3) - else: - num = preset - ok = True - - if ok: - for sel_obj in obj_list: - # first get a bounding box to fit all - for obj in obj_list: - 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) - - for sel_obj in obj_list: - px = 0.5 * (xminimal + xmaximal) - py = 0.5 * (yminimal + ymaximal) - - sel_obj.rotate(-num, point=(px, py)) - sel_obj.plot() - self.inform.emit('Object was rotated ...') - - def on_skewx(self): - obj_list = self.collection.get_selected() - xminlist = [] - yminlist = [] - - if not obj_list: - self.inform.emit("WARNING: No object selected.") - msg = "Please Select an object to skew/shear!" - warningbox = QtGui.QMessageBox() - warningbox.setText(msg) - warningbox.setWindowTitle("Warning ...") - warningbox.setWindowIcon(QtGui.QIcon('share/warning.png')) - warningbox.setStandardButtons(QtGui.QMessageBox.Ok) - warningbox.setDefaultButton(QtGui.QMessageBox.Ok) - warningbox.exec_() - else: - skewxbox = FCInputDialog() - num, ok = skewxbox.get_value(title='Transform', message='Enter the Angle value', - min=-360, max=360, decimals=3) - if ok: - # first get a bounding box to fit all - for obj in obj_list: - 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) - - for obj in obj_list: - obj.skew(num, 0, point=(xminimal, yminimal)) - obj.plot() - self.inform.emit('Object was skewed on X axis ...') - - def on_skewy(self): - obj_list = self.collection.get_selected() - xminlist = [] - yminlist = [] - - - if not obj_list: - self.inform.emit("WARNING: No object selected.") - msg = "Please Select an object to skew/shear!" - warningbox = QtGui.QMessageBox() - warningbox.setText(msg) - warningbox.setWindowTitle("Warning ...") - warningbox.setWindowIcon(QtGui.QIcon('share/warning.png')) - warningbox.setStandardButtons(QtGui.QMessageBox.Ok) - warningbox.setDefaultButton(QtGui.QMessageBox.Ok) - warningbox.exec_() - else: - skewybox = FCInputDialog() - num, ok = skewybox.get_value(title='Transform', message='Enter the Angle value', - min=-360, max=360, decimals=3) - if ok: - # first get a bounding box to fit all - for obj in obj_list: - 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) - - for obj in obj_list: - obj.skew(0, num, point=(xminimal, yminimal)) - obj.plot() - self.inform.emit('Object was skewed on Y axis ...') - def on_delete(self): """ Delete the currently selected FlatCAMObjs. diff --git a/FlatCAMGUI.py b/FlatCAMGUI.py index 7531a91b..0c3829d5 100644 --- a/FlatCAMGUI.py +++ b/FlatCAMGUI.py @@ -98,10 +98,12 @@ class FlatCAMGUI(QtGui.QMainWindow): self.menueditnew = self.menuedit.addAction(QtGui.QIcon('share/new_geo16.png'), 'New Geometry') self.menueditedit = self.menuedit.addAction(QtGui.QIcon('share/edit16.png'), 'Edit Geometry') self.menueditok = self.menuedit.addAction(QtGui.QIcon('share/edit_ok16.png'), 'Update Geometry') - #self.menueditok. - #self.menueditcancel = self.menuedit.addAction(QtGui.QIcon('share/cancel_edit16.png'), "Cancel Edit") + # Separator + self.menuedit.addSeparator() self.menueditjoin = self.menuedit.addAction(QtGui.QIcon('share/join16.png'), 'Join Geometry') self.menueditdelete = self.menuedit.addAction(QtGui.QIcon('share/trash16.png'), 'Delete') + self.menuedit.addSeparator() + ### Options ### self.menuoptions = self.menu.addMenu('&Options') @@ -113,23 +115,6 @@ class FlatCAMGUI(QtGui.QMainWindow): self.menuoptions_transfer_a2o = self.menuoptions_transfer.addAction("Application to Object") self.menuoptions_transfer_o2a = self.menuoptions_transfer.addAction("Object to Application") - ### Transform Object ### - self.menuoptions_transform = self.menuoptions.addMenu(QtGui.QIcon('share/transform.png'), '&Transform Object') - self.menuoptions_transform_flipx = self.menuoptions_transform.addAction(QtGui.QIcon('share/flipx.png'), - "Flip Selection on &X axis") - self.menuoptions_transform_flipy = self.menuoptions_transform.addAction(QtGui.QIcon('share/flipy.png'), - "Flip Selection on &Y axis") - # Separator - self.menuoptions_transform.addSeparator() - self.menuoptions_transform_skewx = self.menuoptions_transform.addAction(QtGui.QIcon('share/skewx.png'), - "&Skew Selection on X axis") - self.menuoptions_transform_skewy = self.menuoptions_transform.addAction(QtGui.QIcon('share/skewy.png'), - "S&kew Selection on Y axis") - # Separator - self.menuoptions_transform.addSeparator() - self.menuoptions_transform_rotate = self.menuoptions_transform.addAction(QtGui.QIcon('share/rotate.png'), - "&Rotate Selection") - ### View ### self.menuview = self.menu.addMenu('&View') self.menuviewdisableall = self.menuview.addAction(QtGui.QIcon('share/clear_plot16.png'), 'Disable all plots') diff --git a/DblSidedTool.py b/ToolDblSided.py similarity index 100% rename from DblSidedTool.py rename to ToolDblSided.py diff --git a/MeasurementTool.py b/ToolMeasurement.py similarity index 100% rename from MeasurementTool.py rename to ToolMeasurement.py diff --git a/ToolTransform.py b/ToolTransform.py new file mode 100644 index 00000000..a168421e --- /dev/null +++ b/ToolTransform.py @@ -0,0 +1,319 @@ +from PyQt4 import QtGui, QtCore +from PyQt4 import Qt +from GUIElements import FCEntry, FCButton +from FlatCAMTool import FlatCAMTool +from FlatCAMObj import FlatCAMGerber, FlatCAMExcellon, FlatCAMGeometry + + +class ToolTransform(FlatCAMTool): + + toolName = "Object Transformation" + rotateName = "Rotate Transformation" + skewName = "Skew/Shear Transformation" + flipName = "Flip Transformation" + + def __init__(self, app): + FlatCAMTool.__init__(self, app) + + self.transform_lay = QtGui.QVBoxLayout() + self.layout.addLayout(self.transform_lay) + ## Title + title_label = QtGui.QLabel("%s
" % self.toolName) + self.transform_lay.addWidget(title_label) + + self.empty_label = QtGui.QLabel("") + self.empty_label.setFixedWidth(80) + self.empty_label1 = QtGui.QLabel("") + self.empty_label1.setFixedWidth(80) + self.empty_label2 = QtGui.QLabel("") + self.empty_label2.setFixedWidth(80) + self.transform_lay.addWidget(self.empty_label) + + ## Rotate Title + rotate_title_label = QtGui.QLabel("%s" % self.rotateName) + self.transform_lay.addWidget(rotate_title_label) + + ## Form Layout + form_layout = QtGui.QFormLayout() + self.transform_lay.addLayout(form_layout) + + self.rotate_entry = FCEntry() + self.rotate_entry.setFixedWidth(70) + self.rotate_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) + self.rotate_label = QtGui.QLabel("Angle:") + self.rotate_label.setToolTip( + "Angle for Rotation action, in degrees.\n" + "Float number between -360 and 359.\n" + "Positive numbers for CW motion.\n" + "Negative numbers for CCW motion." + ) + self.rotate_label.setFixedWidth(80) + + self.rotate_button = FCButton() + self.rotate_button.set_value("Rotate") + self.rotate_button.setToolTip( + "Rotate the selected object(s).\n" + "The point of reference is the middle of\n" + "the bounding box for all selected objects.\n" + ) + self.rotate_button.setFixedWidth(70) + + form_layout.addRow(self.rotate_label, self.rotate_entry) + form_layout.addRow(self.empty_label, self.rotate_button) + + self.transform_lay.addWidget(self.empty_label1) + + ## Skew Title + skew_title_label = QtGui.QLabel("%s" % self.skewName) + self.transform_lay.addWidget(skew_title_label) + + ## Form Layout + form1_layout = QtGui.QFormLayout() + self.transform_lay.addLayout(form1_layout) + + self.skewx_entry = FCEntry() + self.skewx_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) + self.skewx_entry.setFixedWidth(70) + self.skewx_label = QtGui.QLabel("Angle:") + self.skewx_label.setToolTip( + "Angle for Skew action, in degrees.\n" + "Float number between -360 and 359." + ) + self.skewx_label.setFixedWidth(80) + + self.skewx_button = FCButton() + self.skewx_button.set_value("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.\n") + self.skewx_button.setFixedWidth(70) + + self.skewy_entry = FCEntry() + self.skewy_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) + self.skewy_entry.setFixedWidth(70) + self.skewy_label = QtGui.QLabel("Angle:") + self.skewy_label.setToolTip( + "Angle for Skew action, in degrees.\n" + "Float number between -360 and 359." + ) + self.skewy_label.setFixedWidth(80) + + self.skewy_button = FCButton() + self.skewy_button.set_value("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.\n") + self.skewy_button.setFixedWidth(70) + + form1_layout.addRow(self.skewx_label, self.skewx_entry) + form1_layout.addRow(self.empty_label, self.skewx_button) + form1_layout.addRow(self.skewy_label, self.skewy_entry) + form1_layout.addRow(self.empty_label, self.skewy_button) + + self.transform_lay.addWidget(self.empty_label2) + + ## Flip Title + flip_title_label = QtGui.QLabel("%s" % self.flipName) + self.transform_lay.addWidget(flip_title_label) + + ## Form Layout + form2_layout = QtGui.QFormLayout() + self.transform_lay.addLayout(form2_layout) + + self.flipx_button = FCButton() + self.flipx_button.set_value("Flip_X") + self.flipx_button.setToolTip( + "Flip the selected object(s) over the X axis.\n" + "Does not create a new object.\n " + ) + self.flipx_button.setFixedWidth(70) + + self.flipy_button = FCButton() + self.flipy_button.set_value("Flip_Y") + self.flipy_button.setToolTip( + "Flip the selected object(s) over the X axis.\n" + "Does not create a new object.\n " + ) + self.flipy_button.setFixedWidth(70) + + form2_layout.setSpacing(16) + form2_layout.addRow(self.flipx_button, self.flipy_button) + + self.transform_lay.addStretch() + + ## Signals + 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.flipx_button.clicked.connect(self.on_flipx) + self.flipy_button.clicked.connect(self.on_flipy) + + self.rotate_entry.returnPressed.connect(self.on_rotate) + self.skewx_entry.returnPressed.connect(self.on_skewx) + self.skewy_entry.returnPressed.connect(self.on_skewy) + + ## Initialize form + self.rotate_entry.set_value('0') + self.skewx_entry.set_value('0') + self.skewy_entry.set_value('0') + + def on_rotate(self): + value = float(self.rotate_entry.get_value()) + self.on_rotate_action(value) + return + + def on_flipx(self): + self.on_flip("Y") + return + + def on_flipy(self): + self.on_flip("X") + return + + def on_skewx(self): + value = float(self.skewx_entry.get_value()) + self.on_skew("X", value) + return + + def on_skewy(self): + value = float(self.skewy_entry.get_value()) + self.on_skew("Y", value) + return + + def on_rotate_action(self, num): + obj_list = self.app.collection.get_selected() + xminlist = [] + yminlist = [] + xmaxlist = [] + ymaxlist = [] + + if not obj_list: + self.app.inform.emit("WARNING: No object selected.") + msg = "Please Select an object to rotate!" + warningbox = QtGui.QMessageBox() + warningbox.setText(msg) + warningbox.setWindowTitle("Warning ...") + warningbox.setWindowIcon(QtGui.QIcon('share/warning.png')) + warningbox.setStandardButtons(QtGui.QMessageBox.Ok) + warningbox.setDefaultButton(QtGui.QMessageBox.Ok) + warningbox.exec_() + else: + try: + # first get a bounding box to fit all + for obj in obj_list: + 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) + + for sel_obj in obj_list: + px = 0.5 * (xminimal + xmaximal) + py = 0.5 * (yminimal + ymaximal) + + sel_obj.rotate(-num, point=(px, py)) + sel_obj.plot() + self.app.inform.emit('Object was rotated ...') + except Exception as e: + self.app.inform.emit("[ERROR] Due of %s, rotation movement was not executed." % str(e)) + return + + def on_flip(self, axis): + obj_list = self.app.collection.get_selected() + xminlist = [] + yminlist = [] + xmaxlist = [] + ymaxlist = [] + + if not obj_list: + self.app.inform.emit("WARNING: No object selected.") + msg = "Please Select an object to flip!" + warningbox = QtGui.QMessageBox() + warningbox.setText(msg) + warningbox.setWindowTitle("Warning ...") + warningbox.setWindowIcon(QtGui.QIcon('share/warning.png')) + warningbox.setStandardButtons(QtGui.QMessageBox.Ok) + warningbox.setDefaultButton(QtGui.QMessageBox.Ok) + warningbox.exec_() + return + else: + try: + # first get a bounding box to fit all + for obj in obj_list: + 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) + + # execute mirroring + for obj in obj_list: + if axis is 'X': + obj.mirror('X', [px, py]) + obj.plot() + self.app.inform.emit('Flipped on the Y axis ...') + elif axis is 'Y': + obj.mirror('Y', [px, py]) + obj.plot() + self.app.inform.emit('Flipped on the X axis ...') + + except Exception as e: + self.app.inform.emit("[ERROR] Due of %s, Flip action was not executed.") + return + + def on_skew(self, axis, num): + obj_list = self.app.collection.get_selected() + xminlist = [] + yminlist = [] + + if not obj_list: + self.app.inform.emit("WARNING: No object selected.") + msg = "Please Select an object to skew/shear!" + warningbox = QtGui.QMessageBox() + warningbox.setText(msg) + warningbox.setWindowTitle("Warning ...") + warningbox.setWindowIcon(QtGui.QIcon('share/warning.png')) + warningbox.setStandardButtons(QtGui.QMessageBox.Ok) + warningbox.setDefaultButton(QtGui.QMessageBox.Ok) + warningbox.exec_() + else: + try: + # first get a bounding box to fit all + for obj in obj_list: + 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) + + for obj in obj_list: + if axis is 'X': + obj.skew(num, 0, point=(xminimal, yminimal)) + elif axis is 'Y': + obj.skew(0, num, point=(xminimal, yminimal)) + obj.plot() + self.app.inform.emit('Object was skewed on %s axis ...' % str(axis)) + except Exception as e: + self.app.inform.emit("[ERROR] Due of %s, Skew action was not executed." % str(e)) + return + +# end of file \ No newline at end of file From 00d22b7963f0cdda94592b26fd1fa7f4c6fab607 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Tue, 5 Jun 2018 22:55:05 +0300 Subject: [PATCH 6/6] - some small changes in labels --- ToolTransform.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ToolTransform.py b/ToolTransform.py index a168421e..033af65c 100644 --- a/ToolTransform.py +++ b/ToolTransform.py @@ -40,7 +40,7 @@ class ToolTransform(FlatCAMTool): self.rotate_entry = FCEntry() self.rotate_entry.setFixedWidth(70) self.rotate_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) - self.rotate_label = QtGui.QLabel("Angle:") + self.rotate_label = QtGui.QLabel("Angle Rotation:") self.rotate_label.setToolTip( "Angle for Rotation action, in degrees.\n" "Float number between -360 and 359.\n" @@ -74,7 +74,7 @@ class ToolTransform(FlatCAMTool): self.skewx_entry = FCEntry() self.skewx_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) self.skewx_entry.setFixedWidth(70) - self.skewx_label = QtGui.QLabel("Angle:") + self.skewx_label = QtGui.QLabel("Angle SkewX:") self.skewx_label.setToolTip( "Angle for Skew action, in degrees.\n" "Float number between -360 and 359." @@ -92,7 +92,7 @@ class ToolTransform(FlatCAMTool): self.skewy_entry = FCEntry() self.skewy_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) self.skewy_entry.setFixedWidth(70) - self.skewy_label = QtGui.QLabel("Angle:") + self.skewy_label = QtGui.QLabel("Angle SkewY:") self.skewy_label.setToolTip( "Angle for Skew action, in degrees.\n" "Float number between -360 and 359."