ToolTransform.py 31 KB

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