ToolTransform.py 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810
  1. # ########################################################## ##
  2. # FlatCAM: 2D Post-processing for Manufacturing #
  3. # http://flatcam.org #
  4. # File Author: Marius Adrian Stanciu (c) #
  5. # Date: 3/10/2019 #
  6. # MIT Licence #
  7. # ########################################################## ##
  8. from FlatCAMTool import FlatCAMTool
  9. from FlatCAMObj import *
  10. import gettext
  11. import FlatCAMTranslation as fcTranslate
  12. import builtins
  13. fcTranslate.apply_language('strings')
  14. if '_' not in builtins.__dict__:
  15. _ = gettext.gettext
  16. class ToolTransform(FlatCAMTool):
  17. toolName = _("Object Transform")
  18. rotateName = _("Rotate")
  19. skewName = _("Skew/Shear")
  20. scaleName = _("Scale")
  21. flipName = _("Mirror (Flip)")
  22. offsetName = _("Offset")
  23. def __init__(self, app):
  24. FlatCAMTool.__init__(self, app)
  25. self.decimals = 4
  26. self.transform_lay = QtWidgets.QVBoxLayout()
  27. self.layout.addLayout(self.transform_lay)
  28. # ## Title
  29. title_label = QtWidgets.QLabel("%s" % self.toolName)
  30. title_label.setStyleSheet("""
  31. QLabel
  32. {
  33. font-size: 16px;
  34. font-weight: bold;
  35. }
  36. """)
  37. self.transform_lay.addWidget(title_label)
  38. self.transform_lay.addWidget(QtWidgets.QLabel(''))
  39. # ## Layout
  40. grid0 = QtWidgets.QGridLayout()
  41. self.transform_lay.addLayout(grid0)
  42. grid0.setColumnStretch(0, 0)
  43. grid0.setColumnStretch(1, 1)
  44. grid0.setColumnStretch(2, 0)
  45. grid0.addWidget(QtWidgets.QLabel(''))
  46. # ## Rotate Title
  47. rotate_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.rotateName)
  48. grid0.addWidget(rotate_title_label, 0, 0, 1, 3)
  49. self.rotate_label = QtWidgets.QLabel('%s:' % _("Angle"))
  50. self.rotate_label.setToolTip(
  51. _("Angle for Rotation action, in degrees.\n"
  52. "Float number between -360 and 359.\n"
  53. "Positive numbers for CW motion.\n"
  54. "Negative numbers for CCW motion.")
  55. )
  56. self.rotate_entry = FCDoubleSpinner()
  57. self.rotate_entry.set_precision(self.decimals)
  58. self.rotate_entry.setSingleStep(45)
  59. self.rotate_entry.setWrapping(True)
  60. self.rotate_entry.set_range(-360, 360)
  61. # self.rotate_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
  62. self.rotate_button = FCButton()
  63. self.rotate_button.set_value(_("Rotate"))
  64. self.rotate_button.setToolTip(
  65. _("Rotate the selected object(s).\n"
  66. "The point of reference is the middle of\n"
  67. "the bounding box for all selected objects.")
  68. )
  69. self.rotate_button.setMinimumWidth(90)
  70. grid0.addWidget(self.rotate_label, 1, 0)
  71. grid0.addWidget(self.rotate_entry, 1, 1)
  72. grid0.addWidget(self.rotate_button, 1, 2)
  73. grid0.addWidget(QtWidgets.QLabel(''), 2, 0)
  74. # ## Skew Title
  75. skew_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.skewName)
  76. grid0.addWidget(skew_title_label, 3, 0, 1, 3)
  77. self.skewx_label = QtWidgets.QLabel('%s:' % _("X angle"))
  78. self.skewx_label.setToolTip(
  79. _("Angle for Skew action, in degrees.\n"
  80. "Float number between -360 and 360.")
  81. )
  82. self.skewx_entry = FCDoubleSpinner()
  83. # self.skewx_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
  84. self.skewx_entry.set_precision(self.decimals)
  85. self.skewx_entry.set_range(-360, 360)
  86. self.skewx_button = FCButton()
  87. self.skewx_button.set_value(_("Skew X"))
  88. self.skewx_button.setToolTip(
  89. _("Skew/shear the selected object(s).\n"
  90. "The point of reference is the middle of\n"
  91. "the bounding box for all selected objects."))
  92. self.skewx_button.setMinimumWidth(90)
  93. grid0.addWidget(self.skewx_label, 4, 0)
  94. grid0.addWidget(self.skewx_entry, 4, 1)
  95. grid0.addWidget(self.skewx_button, 4, 2)
  96. self.skewy_label = QtWidgets.QLabel('%s:' % _("Y angle"))
  97. self.skewy_label.setToolTip(
  98. _("Angle for Skew action, in degrees.\n"
  99. "Float number between -360 and 360.")
  100. )
  101. self.skewy_entry = FCDoubleSpinner()
  102. # self.skewy_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
  103. self.skewy_entry.set_precision(self.decimals)
  104. self.skewy_entry.set_range(-360, 360)
  105. self.skewy_button = FCButton()
  106. self.skewy_button.set_value(_("Skew Y"))
  107. self.skewy_button.setToolTip(
  108. _("Skew/shear the selected object(s).\n"
  109. "The point of reference is the middle of\n"
  110. "the bounding box for all selected objects."))
  111. self.skewy_button.setMinimumWidth(90)
  112. grid0.addWidget(self.skewy_label, 5, 0)
  113. grid0.addWidget(self.skewy_entry, 5, 1)
  114. grid0.addWidget(self.skewy_button, 5, 2)
  115. grid0.addWidget(QtWidgets.QLabel(''), 6, 0)
  116. # ## Scale Title
  117. scale_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.scaleName)
  118. grid0.addWidget(scale_title_label, 7, 0, 1, 3)
  119. self.scalex_label = QtWidgets.QLabel('%s:' % _("X factor"))
  120. self.scalex_label.setToolTip(
  121. _("Factor for scaling on X axis.")
  122. )
  123. self.scalex_entry = FCDoubleSpinner()
  124. # self.scalex_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
  125. self.scalex_entry.set_precision(self.decimals)
  126. self.scalex_entry.setMinimum(-1e6)
  127. self.scalex_button = FCButton()
  128. self.scalex_button.set_value(_("Scale X"))
  129. self.scalex_button.setToolTip(
  130. _("Scale the selected object(s).\n"
  131. "The point of reference depends on \n"
  132. "the Scale reference checkbox state."))
  133. self.scalex_button.setMinimumWidth(90)
  134. grid0.addWidget(self.scalex_label, 8, 0)
  135. grid0.addWidget(self.scalex_entry, 8, 1)
  136. grid0.addWidget(self.scalex_button, 8, 2)
  137. self.scaley_label = QtWidgets.QLabel('%s:' % _("Y factor"))
  138. self.scaley_label.setToolTip(
  139. _("Factor for scaling on Y axis.")
  140. )
  141. self.scaley_entry = FCDoubleSpinner()
  142. # self.scaley_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
  143. self.scaley_entry.set_precision(self.decimals)
  144. self.scaley_entry.setMinimum(-1e6)
  145. self.scaley_button = FCButton()
  146. self.scaley_button.set_value(_("Scale Y"))
  147. self.scaley_button.setToolTip(
  148. _("Scale the selected object(s).\n"
  149. "The point of reference depends on \n"
  150. "the Scale reference checkbox state."))
  151. self.scaley_button.setMinimumWidth(90)
  152. grid0.addWidget(self.scaley_label, 9, 0)
  153. grid0.addWidget(self.scaley_entry, 9, 1)
  154. grid0.addWidget(self.scaley_button, 9, 2)
  155. self.scale_link_cb = FCCheckBox()
  156. self.scale_link_cb.set_value(True)
  157. self.scale_link_cb.setText(_("Link"))
  158. self.scale_link_cb.setToolTip(
  159. _("Scale the selected object(s)\n"
  160. "using the Scale_X factor for both axis.")
  161. )
  162. self.scale_zero_ref_cb = FCCheckBox()
  163. self.scale_zero_ref_cb.set_value(True)
  164. self.scale_zero_ref_cb.setText('%s' % _("Scale Reference"))
  165. self.scale_zero_ref_cb.setToolTip(
  166. _("Scale the selected object(s)\n"
  167. "using the origin reference when checked,\n"
  168. "and the center of the biggest bounding box\n"
  169. "of the selected objects when unchecked."))
  170. self.ois_scale = OptionalInputSection(self.scale_link_cb, [self.scaley_entry, self.scaley_button], logic=False)
  171. grid0.addWidget(self.scale_link_cb, 10, 0)
  172. grid0.addWidget(self.scale_zero_ref_cb, 10, 1)
  173. grid0.addWidget(QtWidgets.QLabel(''), 11, 0)
  174. # ## Offset Title
  175. offset_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.offsetName)
  176. grid0.addWidget(offset_title_label, 12, 0, 1, 3)
  177. self.offx_label = QtWidgets.QLabel('%s:' % _("X val"))
  178. self.offx_label.setToolTip(
  179. _("Distance to offset on X axis. In current units.")
  180. )
  181. self.offx_entry = FCDoubleSpinner()
  182. # self.offx_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
  183. self.offx_entry.set_precision(self.decimals)
  184. self.offx_entry.setMinimum(-1e6)
  185. self.offx_button = FCButton()
  186. self.offx_button.set_value(_("Offset X"))
  187. self.offx_button.setToolTip(
  188. _("Offset the selected object(s).\n"
  189. "The point of reference is the middle of\n"
  190. "the bounding box for all selected objects.\n"))
  191. self.offx_button.setMinimumWidth(90)
  192. grid0.addWidget(self.offx_label, 13, 0)
  193. grid0.addWidget(self.offx_entry, 13, 1)
  194. grid0.addWidget(self.offx_button, 13, 2)
  195. self.offy_label = QtWidgets.QLabel('%s:' % _("Y val"))
  196. self.offy_label.setToolTip(
  197. _("Distance to offset on Y axis. In current units.")
  198. )
  199. self.offy_entry = FCDoubleSpinner()
  200. # self.offy_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
  201. self.offy_entry.set_precision(self.decimals)
  202. self.offy_entry.setMinimum(-1e6)
  203. self.offy_button = FCButton()
  204. self.offy_button.set_value(_("Offset Y"))
  205. self.offy_button.setToolTip(
  206. _("Offset the selected object(s).\n"
  207. "The point of reference is the middle of\n"
  208. "the bounding box for all selected objects.\n"))
  209. self.offy_button.setMinimumWidth(90)
  210. grid0.addWidget(self.offy_label, 14, 0)
  211. grid0.addWidget(self.offy_entry, 14, 1)
  212. grid0.addWidget(self.offy_button, 14, 2)
  213. grid0.addWidget(QtWidgets.QLabel(''))
  214. # ## Flip Title
  215. flip_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.flipName)
  216. self.transform_lay.addWidget(flip_title_label)
  217. self.flipx_button = FCButton()
  218. self.flipx_button.set_value(_("Flip on X"))
  219. self.flipx_button.setToolTip(
  220. _("Flip the selected object(s) over the X axis.")
  221. )
  222. self.flipy_button = FCButton()
  223. self.flipy_button.set_value(_("Flip on Y"))
  224. self.flipy_button.setToolTip(
  225. _("Flip the selected object(s) over the X axis.")
  226. )
  227. hlay0= QtWidgets.QHBoxLayout()
  228. self.transform_lay.addLayout(hlay0)
  229. hlay0.addWidget(self.flipx_button)
  230. hlay0.addWidget(self.flipy_button)
  231. self.flip_ref_cb = FCCheckBox()
  232. self.flip_ref_cb.set_value(True)
  233. self.flip_ref_cb.setText('%s' % _("Mirror Reference"))
  234. self.flip_ref_cb.setToolTip(
  235. _("Flip the selected object(s)\n"
  236. "around the point in Point Entry Field.\n"
  237. "\n"
  238. "The point coordinates can be captured by\n"
  239. "left click on canvas together with pressing\n"
  240. "SHIFT key. \n"
  241. "Then click Add button to insert coordinates.\n"
  242. "Or enter the coords in format (x, y) in the\n"
  243. "Point Entry field and click Flip on X(Y)"))
  244. self.transform_lay.addWidget(self.flip_ref_cb)
  245. self.flip_ref_label = QtWidgets.QLabel('%s:' % _("Ref. Point"))
  246. self.flip_ref_label.setToolTip(
  247. _("Coordinates in format (x, y) used as reference for mirroring.\n"
  248. "The 'x' in (x, y) will be used when using Flip on X and\n"
  249. "the 'y' in (x, y) will be used when using Flip on Y.")
  250. )
  251. self.flip_ref_entry = EvalEntry2("(0, 0)")
  252. # self.flip_ref_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
  253. # self.flip_ref_entry.setFixedWidth(70)
  254. self.flip_ref_button = FCButton()
  255. self.flip_ref_button.set_value(_("Add"))
  256. self.flip_ref_button.setToolTip(
  257. _("The point coordinates can be captured by\n"
  258. "left click on canvas together with pressing\n"
  259. "SHIFT key. Then click Add button to insert."))
  260. self.ois_flip = OptionalInputSection(self.flip_ref_cb, [self.flip_ref_entry, self.flip_ref_button], logic=True)
  261. hlay1= QtWidgets.QHBoxLayout()
  262. self.transform_lay.addLayout(hlay1)
  263. hlay1.addWidget(self.flip_ref_label)
  264. hlay1.addWidget(self.flip_ref_entry)
  265. self.transform_lay.addWidget(self.flip_ref_button)
  266. self.transform_lay.addStretch()
  267. # ## Signals
  268. self.rotate_button.clicked.connect(self.on_rotate)
  269. self.skewx_button.clicked.connect(self.on_skewx)
  270. self.skewy_button.clicked.connect(self.on_skewy)
  271. self.scalex_button.clicked.connect(self.on_scalex)
  272. self.scaley_button.clicked.connect(self.on_scaley)
  273. self.offx_button.clicked.connect(self.on_offx)
  274. self.offy_button.clicked.connect(self.on_offy)
  275. self.flipx_button.clicked.connect(self.on_flipx)
  276. self.flipy_button.clicked.connect(self.on_flipy)
  277. self.flip_ref_button.clicked.connect(self.on_flip_add_coords)
  278. self.rotate_entry.editingFinished.connect(self.on_rotate)
  279. self.skewx_entry.editingFinished.connect(self.on_skewx)
  280. self.skewy_entry.editingFinished.connect(self.on_skewy)
  281. self.scalex_entry.editingFinished.connect(self.on_scalex)
  282. self.scaley_entry.editingFinished.connect(self.on_scaley)
  283. self.offx_entry.editingFinished.connect(self.on_offx)
  284. self.offy_entry.editingFinished.connect(self.on_offy)
  285. def run(self, toggle=True):
  286. self.app.report_usage("ToolTransform()")
  287. if toggle:
  288. # if the splitter is hidden, display it, else hide it but only if the current widget is the same
  289. if self.app.ui.splitter.sizes()[0] == 0:
  290. self.app.ui.splitter.setSizes([1, 1])
  291. else:
  292. try:
  293. if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName:
  294. # if tab is populated with the tool but it does not have the focus, focus on it
  295. if not self.app.ui.notebook.currentWidget() is self.app.ui.tool_tab:
  296. # focus on Tool Tab
  297. self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
  298. else:
  299. self.app.ui.splitter.setSizes([0, 1])
  300. except AttributeError:
  301. pass
  302. else:
  303. if self.app.ui.splitter.sizes()[0] == 0:
  304. self.app.ui.splitter.setSizes([1, 1])
  305. FlatCAMTool.run(self)
  306. self.set_tool_ui()
  307. self.app.ui.notebook.setTabText(2, _("Transform Tool"))
  308. def install(self, icon=None, separator=None, **kwargs):
  309. FlatCAMTool.install(self, icon, separator, shortcut='ALT+E', **kwargs)
  310. def set_tool_ui(self):
  311. # ## Initialize form
  312. if self.app.defaults["tools_transform_rotate"]:
  313. self.rotate_entry.set_value(self.app.defaults["tools_transform_rotate"])
  314. else:
  315. self.rotate_entry.set_value(0.0)
  316. if self.app.defaults["tools_transform_skew_x"]:
  317. self.skewx_entry.set_value(self.app.defaults["tools_transform_skew_x"])
  318. else:
  319. self.skewx_entry.set_value(0.0)
  320. if self.app.defaults["tools_transform_skew_y"]:
  321. self.skewy_entry.set_value(self.app.defaults["tools_transform_skew_y"])
  322. else:
  323. self.skewy_entry.set_value(0.0)
  324. if self.app.defaults["tools_transform_scale_x"]:
  325. self.scalex_entry.set_value(self.app.defaults["tools_transform_scale_x"])
  326. else:
  327. self.scalex_entry.set_value(1.0)
  328. if self.app.defaults["tools_transform_scale_y"]:
  329. self.scaley_entry.set_value(self.app.defaults["tools_transform_scale_y"])
  330. else:
  331. self.scaley_entry.set_value(1.0)
  332. if self.app.defaults["tools_transform_scale_link"]:
  333. self.scale_link_cb.set_value(self.app.defaults["tools_transform_scale_link"])
  334. else:
  335. self.scale_link_cb.set_value(True)
  336. if self.app.defaults["tools_transform_scale_reference"]:
  337. self.scale_zero_ref_cb.set_value(self.app.defaults["tools_transform_scale_reference"])
  338. else:
  339. self.scale_zero_ref_cb.set_value(True)
  340. if self.app.defaults["tools_transform_offset_x"]:
  341. self.offx_entry.set_value(self.app.defaults["tools_transform_offset_x"])
  342. else:
  343. self.offx_entry.set_value(0.0)
  344. if self.app.defaults["tools_transform_offset_y"]:
  345. self.offy_entry.set_value(self.app.defaults["tools_transform_offset_y"])
  346. else:
  347. self.offy_entry.set_value(0.0)
  348. if self.app.defaults["tools_transform_mirror_reference"]:
  349. self.flip_ref_cb.set_value(self.app.defaults["tools_transform_mirror_reference"])
  350. else:
  351. self.flip_ref_cb.set_value(False)
  352. if self.app.defaults["tools_transform_mirror_point"]:
  353. self.flip_ref_entry.set_value(self.app.defaults["tools_transform_mirror_point"])
  354. else:
  355. self.flip_ref_entry.set_value((0, 0))
  356. def on_rotate(self):
  357. value = float(self.rotate_entry.get_value())
  358. if value == 0:
  359. self.app.inform.emit('[WARNING_NOTCL] %s' %
  360. _("Rotate transformation can not be done for a value of 0."))
  361. self.app.worker_task.emit({'fcn': self.on_rotate_action, 'params': [value]})
  362. return
  363. def on_flipx(self):
  364. axis = 'Y'
  365. self.app.worker_task.emit({'fcn': self.on_flip, 'params': [axis]})
  366. return
  367. def on_flipy(self):
  368. axis = 'X'
  369. self.app.worker_task.emit({'fcn': self.on_flip, 'params': [axis]})
  370. return
  371. def on_flip_add_coords(self):
  372. val = self.app.clipboard.text()
  373. self.flip_ref_entry.set_value(val)
  374. def on_skewx(self):
  375. value = float(self.skewx_entry.get_value())
  376. axis = 'X'
  377. self.app.worker_task.emit({'fcn': self.on_skew, 'params': [axis, value]})
  378. return
  379. def on_skewy(self):
  380. value = float(self.skewy_entry.get_value())
  381. axis = 'Y'
  382. self.app.worker_task.emit({'fcn': self.on_skew, 'params': [axis, value]})
  383. return
  384. def on_scalex(self):
  385. xvalue = float(self.scalex_entry.get_value())
  386. if xvalue == 0 or xvalue == 1:
  387. self.app.inform.emit('[WARNING_NOTCL] %s' %
  388. _("Scale transformation can not be done for a factor of 0 or 1."))
  389. return
  390. if self.scale_link_cb.get_value():
  391. yvalue = xvalue
  392. else:
  393. yvalue = 1
  394. axis = 'X'
  395. point = (0, 0)
  396. if self.scale_zero_ref_cb.get_value():
  397. self.app.worker_task.emit({'fcn': self.on_scale, 'params': [axis, xvalue, yvalue, point]})
  398. else:
  399. self.app.worker_task.emit({'fcn': self.on_scale, 'params': [axis, xvalue, yvalue]})
  400. return
  401. def on_scaley(self):
  402. xvalue = 1
  403. yvalue = float(self.scaley_entry.get_value())
  404. if yvalue == 0 or yvalue == 1:
  405. self.app.inform.emit('[WARNING_NOTCL] %s' %
  406. _("Scale transformation can not be done for a factor of 0 or 1."))
  407. return
  408. axis = 'Y'
  409. point = (0, 0)
  410. if self.scale_zero_ref_cb.get_value():
  411. self.app.worker_task.emit({'fcn': self.on_scale, 'params': [axis, xvalue, yvalue, point]})
  412. else:
  413. self.app.worker_task.emit({'fcn': self.on_scale, 'params': [axis, xvalue, yvalue]})
  414. return
  415. def on_offx(self):
  416. value = float(self.offx_entry.get_value())
  417. if value == 0:
  418. self.app.inform.emit('[WARNING_NOTCL] %s' %
  419. _("Offset transformation can not be done for a value of 0."))
  420. return
  421. axis = 'X'
  422. self.app.worker_task.emit({'fcn': self.on_offset, 'params': [axis, value]})
  423. return
  424. def on_offy(self):
  425. value = float(self.offy_entry.get_value())
  426. if value == 0:
  427. self.app.inform.emit('[WARNING_NOTCL] %s' %
  428. _("Offset transformation can not be done for a value of 0."))
  429. return
  430. axis = 'Y'
  431. self.app.worker_task.emit({'fcn': self.on_offset, 'params': [axis, value]})
  432. return
  433. def on_rotate_action(self, num):
  434. obj_list = self.app.collection.get_selected()
  435. xminlist = []
  436. yminlist = []
  437. xmaxlist = []
  438. ymaxlist = []
  439. if not obj_list:
  440. self.app.inform.emit('[WARNING_NOTCL] %s' %
  441. _("No object selected. Please Select an object to rotate!"))
  442. return
  443. else:
  444. with self.app.proc_container.new(_("Appying Rotate")):
  445. try:
  446. # first get a bounding box to fit all
  447. for obj in obj_list:
  448. if isinstance(obj, FlatCAMCNCjob):
  449. pass
  450. else:
  451. xmin, ymin, xmax, ymax = obj.bounds()
  452. xminlist.append(xmin)
  453. yminlist.append(ymin)
  454. xmaxlist.append(xmax)
  455. ymaxlist.append(ymax)
  456. # get the minimum x,y and maximum x,y for all objects selected
  457. xminimal = min(xminlist)
  458. yminimal = min(yminlist)
  459. xmaximal = max(xmaxlist)
  460. ymaximal = max(ymaxlist)
  461. self.app.progress.emit(20)
  462. px = 0.5 * (xminimal + xmaximal)
  463. py = 0.5 * (yminimal + ymaximal)
  464. for sel_obj in obj_list:
  465. if isinstance(sel_obj, FlatCAMCNCjob):
  466. self.app.inform.emit(_("CNCJob objects can't be rotated."))
  467. else:
  468. sel_obj.rotate(-num, point=(px, py))
  469. self.app.object_changed.emit(sel_obj)
  470. # add information to the object that it was changed and how much
  471. sel_obj.options['rotate'] = num
  472. sel_obj.plot()
  473. self.app.inform.emit('[success] %s...' % _('Rotate done'))
  474. self.app.progress.emit(100)
  475. except Exception as e:
  476. self.app.inform.emit('[ERROR_NOTCL] %s %s, %s.' %
  477. (_("Due of"), str(e), _("action was not executed.")))
  478. return
  479. def on_flip(self, axis):
  480. obj_list = self.app.collection.get_selected()
  481. xminlist = []
  482. yminlist = []
  483. xmaxlist = []
  484. ymaxlist = []
  485. if not obj_list:
  486. self.app.inform.emit('[WARNING_NOTCL] %s!' %
  487. _("No object selected. Please Select an object to flip"))
  488. return
  489. else:
  490. with self.app.proc_container.new(_("Applying Flip")):
  491. try:
  492. # get mirroring coords from the point entry
  493. if self.flip_ref_cb.isChecked():
  494. px, py = eval('{}'.format(self.flip_ref_entry.text()))
  495. # get mirroing coords from the center of an all-enclosing bounding box
  496. else:
  497. # first get a bounding box to fit all
  498. for obj in obj_list:
  499. if isinstance(obj, FlatCAMCNCjob):
  500. pass
  501. else:
  502. xmin, ymin, xmax, ymax = obj.bounds()
  503. xminlist.append(xmin)
  504. yminlist.append(ymin)
  505. xmaxlist.append(xmax)
  506. ymaxlist.append(ymax)
  507. # get the minimum x,y and maximum x,y for all objects selected
  508. xminimal = min(xminlist)
  509. yminimal = min(yminlist)
  510. xmaximal = max(xmaxlist)
  511. ymaximal = max(ymaxlist)
  512. px = 0.5 * (xminimal + xmaximal)
  513. py = 0.5 * (yminimal + ymaximal)
  514. self.app.progress.emit(20)
  515. # execute mirroring
  516. for sel_obj in obj_list:
  517. if isinstance(sel_obj, FlatCAMCNCjob):
  518. self.app.inform.emit(_("CNCJob objects can't be mirrored/flipped."))
  519. else:
  520. if axis is 'X':
  521. sel_obj.mirror('X', (px, py))
  522. # add information to the object that it was changed and how much
  523. # the axis is reversed because of the reference
  524. if 'mirror_y' in sel_obj.options:
  525. sel_obj.options['mirror_y'] = not sel_obj.options['mirror_y']
  526. else:
  527. sel_obj.options['mirror_y'] = True
  528. self.app.inform.emit('[success] %s...' %
  529. _('Flip on the Y axis done'))
  530. elif axis is 'Y':
  531. sel_obj.mirror('Y', (px, py))
  532. # add information to the object that it was changed and how much
  533. # the axis is reversed because of the reference
  534. if 'mirror_x' in sel_obj.options:
  535. sel_obj.options['mirror_x'] = not sel_obj.options['mirror_x']
  536. else:
  537. sel_obj.options['mirror_x'] = True
  538. self.app.inform.emit('[success] %s...' %
  539. _('Flip on the X axis done'))
  540. self.app.object_changed.emit(sel_obj)
  541. sel_obj.plot()
  542. self.app.progress.emit(100)
  543. except Exception as e:
  544. self.app.inform.emit('[ERROR_NOTCL] %s %s, %s.' %
  545. (_("Due of"), str(e), _("action was not executed.")))
  546. return
  547. def on_skew(self, axis, num):
  548. obj_list = self.app.collection.get_selected()
  549. xminlist = []
  550. yminlist = []
  551. if num == 0 or num == 90 or num == 180:
  552. self.app.inform.emit('[WARNING_NOTCL] %s' %
  553. _("Skew transformation can not be done for 0, 90 and 180 degrees."))
  554. return
  555. if not obj_list:
  556. self.app.inform.emit('[WARNING_NOTCL] %s' %
  557. _("No object selected. Please Select an object to shear/skew!"))
  558. return
  559. else:
  560. with self.app.proc_container.new(_("Applying Skew")):
  561. try:
  562. # first get a bounding box to fit all
  563. for obj in obj_list:
  564. if isinstance(obj, FlatCAMCNCjob):
  565. pass
  566. else:
  567. xmin, ymin, xmax, ymax = obj.bounds()
  568. xminlist.append(xmin)
  569. yminlist.append(ymin)
  570. # get the minimum x,y and maximum x,y for all objects selected
  571. xminimal = min(xminlist)
  572. yminimal = min(yminlist)
  573. self.app.progress.emit(20)
  574. for sel_obj in obj_list:
  575. if isinstance(sel_obj, FlatCAMCNCjob):
  576. self.app.inform.emit(_("CNCJob objects can't be skewed."))
  577. else:
  578. if axis is 'X':
  579. sel_obj.skew(num, 0, point=(xminimal, yminimal))
  580. # add information to the object that it was changed and how much
  581. sel_obj.options['skew_x'] = num
  582. elif axis is 'Y':
  583. sel_obj.skew(0, num, point=(xminimal, yminimal))
  584. # add information to the object that it was changed and how much
  585. sel_obj.options['skew_y'] = num
  586. self.app.object_changed.emit(sel_obj)
  587. sel_obj.plot()
  588. self.app.inform.emit('[success] %s %s %s...' %
  589. (_('Skew on the'), str(axis), _("axis done")))
  590. self.app.progress.emit(100)
  591. except Exception as e:
  592. self.app.inform.emit('[ERROR_NOTCL] %s %s, %s.' %
  593. (_("Due of"), str(e), _("action was not executed.")))
  594. return
  595. def on_scale(self, axis, xfactor, yfactor, point=None):
  596. obj_list = self.app.collection.get_selected()
  597. xminlist = []
  598. yminlist = []
  599. xmaxlist = []
  600. ymaxlist = []
  601. if not obj_list:
  602. self.app.inform.emit('[WARNING_NOTCL] %s' %
  603. _("No object selected. Please Select an object to scale!"))
  604. return
  605. else:
  606. with self.app.proc_container.new(_("Applying Scale")):
  607. try:
  608. # first get a bounding box to fit all
  609. for obj in obj_list:
  610. if isinstance(obj, FlatCAMCNCjob):
  611. pass
  612. else:
  613. xmin, ymin, xmax, ymax = obj.bounds()
  614. xminlist.append(xmin)
  615. yminlist.append(ymin)
  616. xmaxlist.append(xmax)
  617. ymaxlist.append(ymax)
  618. # get the minimum x,y and maximum x,y for all objects selected
  619. xminimal = min(xminlist)
  620. yminimal = min(yminlist)
  621. xmaximal = max(xmaxlist)
  622. ymaximal = max(ymaxlist)
  623. self.app.progress.emit(20)
  624. if point is None:
  625. px = 0.5 * (xminimal + xmaximal)
  626. py = 0.5 * (yminimal + ymaximal)
  627. else:
  628. px = 0
  629. py = 0
  630. for sel_obj in obj_list:
  631. if isinstance(sel_obj, FlatCAMCNCjob):
  632. self.app.inform.emit(_("CNCJob objects can't be scaled."))
  633. else:
  634. sel_obj.scale(xfactor, yfactor, point=(px, py))
  635. # add information to the object that it was changed and how much
  636. sel_obj.options['scale_x'] = xfactor
  637. sel_obj.options['scale_y'] = yfactor
  638. self.app.object_changed.emit(sel_obj)
  639. sel_obj.plot()
  640. self.app.inform.emit('[success] %s %s %s...' %
  641. (_('Scale on the'), str(axis), _('axis done')))
  642. self.app.progress.emit(100)
  643. except Exception as e:
  644. self.app.inform.emit('[ERROR_NOTCL] %s %s, %s.' %
  645. (_("Due of"), str(e), _("action was not executed.")))
  646. return
  647. def on_offset(self, axis, num):
  648. obj_list = self.app.collection.get_selected()
  649. if not obj_list:
  650. self.app.inform.emit('[WARNING_NOTCL] %s' %
  651. _("No object selected. Please Select an object to offset!"))
  652. return
  653. else:
  654. with self.app.proc_container.new(_("Applying Offset")):
  655. try:
  656. self.app.progress.emit(20)
  657. for sel_obj in obj_list:
  658. if isinstance(sel_obj, FlatCAMCNCjob):
  659. self.app.inform.emit(_("CNCJob objects can't be offset."))
  660. else:
  661. if axis is 'X':
  662. sel_obj.offset((num, 0))
  663. # add information to the object that it was changed and how much
  664. sel_obj.options['offset_x'] = num
  665. elif axis is 'Y':
  666. sel_obj.offset((0, num))
  667. # add information to the object that it was changed and how much
  668. sel_obj.options['offset_y'] = num
  669. self.app.object_changed.emit(sel_obj)
  670. sel_obj.plot()
  671. self.app.inform.emit('[success] %s %s %s...' %
  672. (_('Offset on the'), str(axis), _('axis done')))
  673. self.app.progress.emit(100)
  674. except Exception as e:
  675. self.app.inform.emit('[ERROR_NOTCL] %s %s, %s.' %
  676. (_("Due of"), str(e), _("action was not executed.")))
  677. return
  678. # end of file