meerk40t 0.9.3001__py2.py3-none-any.whl → 0.9.7020__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (446) hide show
  1. meerk40t/__init__.py +1 -1
  2. meerk40t/balormk/balor_params.py +167 -167
  3. meerk40t/balormk/clone_loader.py +457 -457
  4. meerk40t/balormk/controller.py +1566 -1512
  5. meerk40t/balormk/cylindermod.py +64 -0
  6. meerk40t/balormk/device.py +966 -1959
  7. meerk40t/balormk/driver.py +778 -591
  8. meerk40t/balormk/galvo_commands.py +1194 -0
  9. meerk40t/balormk/gui/balorconfig.py +237 -111
  10. meerk40t/balormk/gui/balorcontroller.py +191 -184
  11. meerk40t/balormk/gui/baloroperationproperties.py +116 -115
  12. meerk40t/balormk/gui/corscene.py +845 -0
  13. meerk40t/balormk/gui/gui.py +179 -147
  14. meerk40t/balormk/livelightjob.py +466 -382
  15. meerk40t/balormk/mock_connection.py +131 -109
  16. meerk40t/balormk/plugin.py +133 -135
  17. meerk40t/balormk/usb_connection.py +306 -301
  18. meerk40t/camera/__init__.py +1 -1
  19. meerk40t/camera/camera.py +514 -397
  20. meerk40t/camera/gui/camerapanel.py +1241 -1095
  21. meerk40t/camera/gui/gui.py +58 -58
  22. meerk40t/camera/plugin.py +441 -399
  23. meerk40t/ch341/__init__.py +27 -27
  24. meerk40t/ch341/ch341device.py +628 -628
  25. meerk40t/ch341/libusb.py +595 -589
  26. meerk40t/ch341/mock.py +171 -171
  27. meerk40t/ch341/windriver.py +157 -157
  28. meerk40t/constants.py +13 -0
  29. meerk40t/core/__init__.py +1 -1
  30. meerk40t/core/bindalias.py +550 -539
  31. meerk40t/core/core.py +47 -47
  32. meerk40t/core/cutcode/cubiccut.py +73 -73
  33. meerk40t/core/cutcode/cutcode.py +315 -312
  34. meerk40t/core/cutcode/cutgroup.py +141 -137
  35. meerk40t/core/cutcode/cutobject.py +192 -185
  36. meerk40t/core/cutcode/dwellcut.py +37 -37
  37. meerk40t/core/cutcode/gotocut.py +29 -29
  38. meerk40t/core/cutcode/homecut.py +29 -29
  39. meerk40t/core/cutcode/inputcut.py +34 -34
  40. meerk40t/core/cutcode/linecut.py +33 -33
  41. meerk40t/core/cutcode/outputcut.py +34 -34
  42. meerk40t/core/cutcode/plotcut.py +335 -335
  43. meerk40t/core/cutcode/quadcut.py +61 -61
  44. meerk40t/core/cutcode/rastercut.py +168 -148
  45. meerk40t/core/cutcode/waitcut.py +34 -34
  46. meerk40t/core/cutplan.py +1843 -1316
  47. meerk40t/core/drivers.py +330 -329
  48. meerk40t/core/elements/align.py +801 -669
  49. meerk40t/core/elements/branches.py +1858 -1507
  50. meerk40t/core/elements/clipboard.py +229 -219
  51. meerk40t/core/elements/element_treeops.py +4595 -2837
  52. meerk40t/core/elements/element_types.py +125 -105
  53. meerk40t/core/elements/elements.py +4315 -3617
  54. meerk40t/core/elements/files.py +117 -64
  55. meerk40t/core/elements/geometry.py +473 -224
  56. meerk40t/core/elements/grid.py +467 -316
  57. meerk40t/core/elements/materials.py +158 -94
  58. meerk40t/core/elements/notes.py +50 -38
  59. meerk40t/core/elements/offset_clpr.py +934 -912
  60. meerk40t/core/elements/offset_mk.py +963 -955
  61. meerk40t/core/elements/penbox.py +339 -267
  62. meerk40t/core/elements/placements.py +300 -83
  63. meerk40t/core/elements/render.py +785 -687
  64. meerk40t/core/elements/shapes.py +2618 -2092
  65. meerk40t/core/elements/testcases.py +105 -0
  66. meerk40t/core/elements/trace.py +651 -563
  67. meerk40t/core/elements/tree_commands.py +415 -409
  68. meerk40t/core/elements/undo_redo.py +116 -58
  69. meerk40t/core/elements/wordlist.py +319 -200
  70. meerk40t/core/exceptions.py +9 -9
  71. meerk40t/core/laserjob.py +220 -220
  72. meerk40t/core/logging.py +63 -63
  73. meerk40t/core/node/blobnode.py +83 -86
  74. meerk40t/core/node/bootstrap.py +105 -103
  75. meerk40t/core/node/branch_elems.py +40 -31
  76. meerk40t/core/node/branch_ops.py +45 -38
  77. meerk40t/core/node/branch_regmark.py +48 -41
  78. meerk40t/core/node/cutnode.py +29 -32
  79. meerk40t/core/node/effect_hatch.py +375 -257
  80. meerk40t/core/node/effect_warp.py +398 -0
  81. meerk40t/core/node/effect_wobble.py +441 -309
  82. meerk40t/core/node/elem_ellipse.py +404 -309
  83. meerk40t/core/node/elem_image.py +1082 -801
  84. meerk40t/core/node/elem_line.py +358 -292
  85. meerk40t/core/node/elem_path.py +259 -201
  86. meerk40t/core/node/elem_point.py +129 -102
  87. meerk40t/core/node/elem_polyline.py +310 -246
  88. meerk40t/core/node/elem_rect.py +376 -286
  89. meerk40t/core/node/elem_text.py +445 -418
  90. meerk40t/core/node/filenode.py +59 -40
  91. meerk40t/core/node/groupnode.py +138 -74
  92. meerk40t/core/node/image_processed.py +777 -766
  93. meerk40t/core/node/image_raster.py +156 -113
  94. meerk40t/core/node/layernode.py +31 -31
  95. meerk40t/core/node/mixins.py +135 -107
  96. meerk40t/core/node/node.py +1427 -1304
  97. meerk40t/core/node/nutils.py +117 -114
  98. meerk40t/core/node/op_cut.py +463 -335
  99. meerk40t/core/node/op_dots.py +296 -251
  100. meerk40t/core/node/op_engrave.py +414 -311
  101. meerk40t/core/node/op_image.py +755 -369
  102. meerk40t/core/node/op_raster.py +787 -522
  103. meerk40t/core/node/place_current.py +37 -40
  104. meerk40t/core/node/place_point.py +329 -126
  105. meerk40t/core/node/refnode.py +58 -47
  106. meerk40t/core/node/rootnode.py +225 -219
  107. meerk40t/core/node/util_console.py +48 -48
  108. meerk40t/core/node/util_goto.py +84 -65
  109. meerk40t/core/node/util_home.py +61 -61
  110. meerk40t/core/node/util_input.py +102 -102
  111. meerk40t/core/node/util_output.py +102 -102
  112. meerk40t/core/node/util_wait.py +65 -65
  113. meerk40t/core/parameters.py +709 -707
  114. meerk40t/core/planner.py +875 -785
  115. meerk40t/core/plotplanner.py +656 -652
  116. meerk40t/core/space.py +120 -113
  117. meerk40t/core/spoolers.py +706 -705
  118. meerk40t/core/svg_io.py +1836 -1549
  119. meerk40t/core/treeop.py +534 -445
  120. meerk40t/core/undos.py +278 -124
  121. meerk40t/core/units.py +784 -680
  122. meerk40t/core/view.py +393 -322
  123. meerk40t/core/webhelp.py +62 -62
  124. meerk40t/core/wordlist.py +513 -504
  125. meerk40t/cylinder/cylinder.py +247 -0
  126. meerk40t/cylinder/gui/cylindersettings.py +41 -0
  127. meerk40t/cylinder/gui/gui.py +24 -0
  128. meerk40t/device/__init__.py +1 -1
  129. meerk40t/device/basedevice.py +322 -123
  130. meerk40t/device/devicechoices.py +50 -0
  131. meerk40t/device/dummydevice.py +163 -128
  132. meerk40t/device/gui/defaultactions.py +618 -602
  133. meerk40t/device/gui/effectspanel.py +114 -0
  134. meerk40t/device/gui/formatterpanel.py +253 -290
  135. meerk40t/device/gui/warningpanel.py +337 -260
  136. meerk40t/device/mixins.py +13 -13
  137. meerk40t/dxf/__init__.py +1 -1
  138. meerk40t/dxf/dxf_io.py +766 -554
  139. meerk40t/dxf/plugin.py +47 -35
  140. meerk40t/external_plugins.py +79 -79
  141. meerk40t/external_plugins_build.py +28 -28
  142. meerk40t/extra/cag.py +112 -116
  143. meerk40t/extra/coolant.py +403 -0
  144. meerk40t/extra/encode_detect.py +204 -0
  145. meerk40t/extra/ezd.py +1165 -1165
  146. meerk40t/extra/hershey.py +834 -340
  147. meerk40t/extra/imageactions.py +322 -316
  148. meerk40t/extra/inkscape.py +628 -622
  149. meerk40t/extra/lbrn.py +424 -424
  150. meerk40t/extra/outerworld.py +283 -0
  151. meerk40t/extra/param_functions.py +1542 -1556
  152. meerk40t/extra/potrace.py +257 -253
  153. meerk40t/extra/serial_exchange.py +118 -0
  154. meerk40t/extra/updater.py +602 -453
  155. meerk40t/extra/vectrace.py +147 -146
  156. meerk40t/extra/winsleep.py +83 -83
  157. meerk40t/extra/xcs_reader.py +597 -0
  158. meerk40t/fill/fills.py +781 -335
  159. meerk40t/fill/patternfill.py +1061 -1061
  160. meerk40t/fill/patterns.py +614 -567
  161. meerk40t/grbl/control.py +87 -87
  162. meerk40t/grbl/controller.py +990 -903
  163. meerk40t/grbl/device.py +1084 -768
  164. meerk40t/grbl/driver.py +989 -771
  165. meerk40t/grbl/emulator.py +532 -497
  166. meerk40t/grbl/gcodejob.py +783 -767
  167. meerk40t/grbl/gui/grblconfiguration.py +373 -298
  168. meerk40t/grbl/gui/grblcontroller.py +485 -271
  169. meerk40t/grbl/gui/grblhardwareconfig.py +269 -153
  170. meerk40t/grbl/gui/grbloperationconfig.py +105 -0
  171. meerk40t/grbl/gui/gui.py +147 -116
  172. meerk40t/grbl/interpreter.py +44 -44
  173. meerk40t/grbl/loader.py +22 -22
  174. meerk40t/grbl/mock_connection.py +56 -56
  175. meerk40t/grbl/plugin.py +294 -264
  176. meerk40t/grbl/serial_connection.py +93 -88
  177. meerk40t/grbl/tcp_connection.py +81 -79
  178. meerk40t/grbl/ws_connection.py +112 -0
  179. meerk40t/gui/__init__.py +1 -1
  180. meerk40t/gui/about.py +2042 -296
  181. meerk40t/gui/alignment.py +1644 -1608
  182. meerk40t/gui/autoexec.py +199 -0
  183. meerk40t/gui/basicops.py +791 -670
  184. meerk40t/gui/bufferview.py +77 -71
  185. meerk40t/gui/busy.py +232 -133
  186. meerk40t/gui/choicepropertypanel.py +1662 -1469
  187. meerk40t/gui/consolepanel.py +706 -542
  188. meerk40t/gui/devicepanel.py +687 -581
  189. meerk40t/gui/dialogoptions.py +110 -107
  190. meerk40t/gui/executejob.py +316 -306
  191. meerk40t/gui/fonts.py +90 -90
  192. meerk40t/gui/functionwrapper.py +252 -0
  193. meerk40t/gui/gui_mixins.py +729 -0
  194. meerk40t/gui/guicolors.py +205 -182
  195. meerk40t/gui/help_assets/help_assets.py +218 -201
  196. meerk40t/gui/helper.py +154 -0
  197. meerk40t/gui/hersheymanager.py +1440 -846
  198. meerk40t/gui/icons.py +3422 -2747
  199. meerk40t/gui/imagesplitter.py +555 -508
  200. meerk40t/gui/keymap.py +354 -344
  201. meerk40t/gui/laserpanel.py +897 -806
  202. meerk40t/gui/laserrender.py +1470 -1232
  203. meerk40t/gui/lasertoolpanel.py +805 -793
  204. meerk40t/gui/magnetoptions.py +436 -0
  205. meerk40t/gui/materialmanager.py +2944 -0
  206. meerk40t/gui/materialtest.py +1722 -1694
  207. meerk40t/gui/mkdebug.py +646 -359
  208. meerk40t/gui/mwindow.py +163 -140
  209. meerk40t/gui/navigationpanels.py +2605 -2467
  210. meerk40t/gui/notes.py +143 -142
  211. meerk40t/gui/opassignment.py +414 -410
  212. meerk40t/gui/operation_info.py +310 -299
  213. meerk40t/gui/plugin.py +500 -328
  214. meerk40t/gui/position.py +714 -669
  215. meerk40t/gui/preferences.py +901 -650
  216. meerk40t/gui/propertypanels/attributes.py +1461 -1131
  217. meerk40t/gui/propertypanels/blobproperty.py +117 -114
  218. meerk40t/gui/propertypanels/consoleproperty.py +83 -80
  219. meerk40t/gui/propertypanels/gotoproperty.py +77 -0
  220. meerk40t/gui/propertypanels/groupproperties.py +223 -217
  221. meerk40t/gui/propertypanels/hatchproperty.py +489 -469
  222. meerk40t/gui/propertypanels/imageproperty.py +2244 -1384
  223. meerk40t/gui/propertypanels/inputproperty.py +59 -58
  224. meerk40t/gui/propertypanels/opbranchproperties.py +82 -80
  225. meerk40t/gui/propertypanels/operationpropertymain.py +1890 -1638
  226. meerk40t/gui/propertypanels/outputproperty.py +59 -58
  227. meerk40t/gui/propertypanels/pathproperty.py +389 -380
  228. meerk40t/gui/propertypanels/placementproperty.py +1214 -383
  229. meerk40t/gui/propertypanels/pointproperty.py +140 -136
  230. meerk40t/gui/propertypanels/propertywindow.py +313 -181
  231. meerk40t/gui/propertypanels/rasterwizardpanels.py +996 -912
  232. meerk40t/gui/propertypanels/regbranchproperties.py +76 -0
  233. meerk40t/gui/propertypanels/textproperty.py +770 -755
  234. meerk40t/gui/propertypanels/waitproperty.py +56 -55
  235. meerk40t/gui/propertypanels/warpproperty.py +121 -0
  236. meerk40t/gui/propertypanels/wobbleproperty.py +255 -204
  237. meerk40t/gui/ribbon.py +2471 -2210
  238. meerk40t/gui/scene/scene.py +1100 -1051
  239. meerk40t/gui/scene/sceneconst.py +22 -22
  240. meerk40t/gui/scene/scenepanel.py +439 -349
  241. meerk40t/gui/scene/scenespacewidget.py +365 -365
  242. meerk40t/gui/scene/widget.py +518 -505
  243. meerk40t/gui/scenewidgets/affinemover.py +215 -215
  244. meerk40t/gui/scenewidgets/attractionwidget.py +315 -309
  245. meerk40t/gui/scenewidgets/bedwidget.py +120 -97
  246. meerk40t/gui/scenewidgets/elementswidget.py +137 -107
  247. meerk40t/gui/scenewidgets/gridwidget.py +785 -745
  248. meerk40t/gui/scenewidgets/guidewidget.py +765 -765
  249. meerk40t/gui/scenewidgets/laserpathwidget.py +66 -66
  250. meerk40t/gui/scenewidgets/machineoriginwidget.py +86 -86
  251. meerk40t/gui/scenewidgets/nodeselector.py +28 -28
  252. meerk40t/gui/scenewidgets/rectselectwidget.py +592 -346
  253. meerk40t/gui/scenewidgets/relocatewidget.py +33 -33
  254. meerk40t/gui/scenewidgets/reticlewidget.py +83 -83
  255. meerk40t/gui/scenewidgets/selectionwidget.py +2958 -2756
  256. meerk40t/gui/simpleui.py +362 -333
  257. meerk40t/gui/simulation.py +2451 -2094
  258. meerk40t/gui/snapoptions.py +208 -203
  259. meerk40t/gui/spoolerpanel.py +1227 -1180
  260. meerk40t/gui/statusbarwidgets/defaultoperations.py +480 -353
  261. meerk40t/gui/statusbarwidgets/infowidget.py +520 -483
  262. meerk40t/gui/statusbarwidgets/opassignwidget.py +356 -355
  263. meerk40t/gui/statusbarwidgets/selectionwidget.py +172 -171
  264. meerk40t/gui/statusbarwidgets/shapepropwidget.py +754 -236
  265. meerk40t/gui/statusbarwidgets/statusbar.py +272 -260
  266. meerk40t/gui/statusbarwidgets/statusbarwidget.py +268 -270
  267. meerk40t/gui/statusbarwidgets/strokewidget.py +267 -251
  268. meerk40t/gui/themes.py +200 -78
  269. meerk40t/gui/tips.py +590 -0
  270. meerk40t/gui/toolwidgets/circlebrush.py +35 -35
  271. meerk40t/gui/toolwidgets/toolcircle.py +248 -242
  272. meerk40t/gui/toolwidgets/toolcontainer.py +82 -77
  273. meerk40t/gui/toolwidgets/tooldraw.py +97 -90
  274. meerk40t/gui/toolwidgets/toolellipse.py +219 -212
  275. meerk40t/gui/toolwidgets/toolimagecut.py +25 -132
  276. meerk40t/gui/toolwidgets/toolline.py +39 -144
  277. meerk40t/gui/toolwidgets/toollinetext.py +79 -236
  278. meerk40t/gui/toolwidgets/toollinetext_inline.py +296 -0
  279. meerk40t/gui/toolwidgets/toolmeasure.py +163 -216
  280. meerk40t/gui/toolwidgets/toolnodeedit.py +2088 -2074
  281. meerk40t/gui/toolwidgets/toolnodemove.py +92 -94
  282. meerk40t/gui/toolwidgets/toolparameter.py +754 -668
  283. meerk40t/gui/toolwidgets/toolplacement.py +108 -108
  284. meerk40t/gui/toolwidgets/toolpoint.py +68 -59
  285. meerk40t/gui/toolwidgets/toolpointlistbuilder.py +294 -0
  286. meerk40t/gui/toolwidgets/toolpointmove.py +183 -0
  287. meerk40t/gui/toolwidgets/toolpolygon.py +288 -403
  288. meerk40t/gui/toolwidgets/toolpolyline.py +38 -196
  289. meerk40t/gui/toolwidgets/toolrect.py +211 -207
  290. meerk40t/gui/toolwidgets/toolrelocate.py +72 -72
  291. meerk40t/gui/toolwidgets/toolribbon.py +598 -113
  292. meerk40t/gui/toolwidgets/tooltabedit.py +546 -0
  293. meerk40t/gui/toolwidgets/tooltext.py +98 -89
  294. meerk40t/gui/toolwidgets/toolvector.py +213 -204
  295. meerk40t/gui/toolwidgets/toolwidget.py +39 -39
  296. meerk40t/gui/usbconnect.py +98 -91
  297. meerk40t/gui/utilitywidgets/buttonwidget.py +18 -18
  298. meerk40t/gui/utilitywidgets/checkboxwidget.py +90 -90
  299. meerk40t/gui/utilitywidgets/controlwidget.py +14 -14
  300. meerk40t/gui/utilitywidgets/cyclocycloidwidget.py +343 -340
  301. meerk40t/gui/utilitywidgets/debugwidgets.py +148 -0
  302. meerk40t/gui/utilitywidgets/handlewidget.py +27 -27
  303. meerk40t/gui/utilitywidgets/harmonograph.py +450 -447
  304. meerk40t/gui/utilitywidgets/openclosewidget.py +40 -40
  305. meerk40t/gui/utilitywidgets/rotationwidget.py +54 -54
  306. meerk40t/gui/utilitywidgets/scalewidget.py +75 -75
  307. meerk40t/gui/utilitywidgets/seekbarwidget.py +183 -183
  308. meerk40t/gui/utilitywidgets/togglewidget.py +142 -142
  309. meerk40t/gui/utilitywidgets/toolbarwidget.py +8 -8
  310. meerk40t/gui/wordlisteditor.py +985 -931
  311. meerk40t/gui/wxmeerk40t.py +1447 -1169
  312. meerk40t/gui/wxmmain.py +5644 -4112
  313. meerk40t/gui/wxmribbon.py +1591 -1076
  314. meerk40t/gui/wxmscene.py +1631 -1453
  315. meerk40t/gui/wxmtree.py +2416 -2089
  316. meerk40t/gui/wxutils.py +1769 -1099
  317. meerk40t/gui/zmatrix.py +102 -102
  318. meerk40t/image/__init__.py +1 -1
  319. meerk40t/image/dither.py +429 -0
  320. meerk40t/image/imagetools.py +2793 -2269
  321. meerk40t/internal_plugins.py +150 -130
  322. meerk40t/kernel/__init__.py +63 -12
  323. meerk40t/kernel/channel.py +259 -212
  324. meerk40t/kernel/context.py +538 -538
  325. meerk40t/kernel/exceptions.py +41 -41
  326. meerk40t/kernel/functions.py +463 -414
  327. meerk40t/kernel/jobs.py +100 -100
  328. meerk40t/kernel/kernel.py +3828 -3571
  329. meerk40t/kernel/lifecycles.py +71 -71
  330. meerk40t/kernel/module.py +49 -49
  331. meerk40t/kernel/service.py +147 -147
  332. meerk40t/kernel/settings.py +383 -343
  333. meerk40t/lihuiyu/controller.py +883 -876
  334. meerk40t/lihuiyu/device.py +1181 -1069
  335. meerk40t/lihuiyu/driver.py +1466 -1372
  336. meerk40t/lihuiyu/gui/gui.py +127 -106
  337. meerk40t/lihuiyu/gui/lhyaccelgui.py +377 -363
  338. meerk40t/lihuiyu/gui/lhycontrollergui.py +741 -651
  339. meerk40t/lihuiyu/gui/lhydrivergui.py +470 -446
  340. meerk40t/lihuiyu/gui/lhyoperationproperties.py +238 -237
  341. meerk40t/lihuiyu/gui/tcpcontroller.py +226 -190
  342. meerk40t/lihuiyu/interpreter.py +53 -53
  343. meerk40t/lihuiyu/laserspeed.py +450 -450
  344. meerk40t/lihuiyu/loader.py +90 -90
  345. meerk40t/lihuiyu/parser.py +404 -404
  346. meerk40t/lihuiyu/plugin.py +101 -102
  347. meerk40t/lihuiyu/tcp_connection.py +111 -109
  348. meerk40t/main.py +231 -165
  349. meerk40t/moshi/builder.py +788 -781
  350. meerk40t/moshi/controller.py +505 -499
  351. meerk40t/moshi/device.py +495 -442
  352. meerk40t/moshi/driver.py +862 -696
  353. meerk40t/moshi/gui/gui.py +78 -76
  354. meerk40t/moshi/gui/moshicontrollergui.py +538 -522
  355. meerk40t/moshi/gui/moshidrivergui.py +87 -75
  356. meerk40t/moshi/plugin.py +43 -43
  357. meerk40t/network/console_server.py +140 -57
  358. meerk40t/network/kernelserver.py +10 -9
  359. meerk40t/network/tcp_server.py +142 -140
  360. meerk40t/network/udp_server.py +103 -77
  361. meerk40t/network/web_server.py +404 -0
  362. meerk40t/newly/controller.py +1158 -1144
  363. meerk40t/newly/device.py +874 -732
  364. meerk40t/newly/driver.py +540 -412
  365. meerk40t/newly/gui/gui.py +219 -188
  366. meerk40t/newly/gui/newlyconfig.py +116 -101
  367. meerk40t/newly/gui/newlycontroller.py +193 -186
  368. meerk40t/newly/gui/operationproperties.py +51 -51
  369. meerk40t/newly/mock_connection.py +82 -82
  370. meerk40t/newly/newly_params.py +56 -56
  371. meerk40t/newly/plugin.py +1214 -1246
  372. meerk40t/newly/usb_connection.py +322 -322
  373. meerk40t/rotary/gui/gui.py +52 -46
  374. meerk40t/rotary/gui/rotarysettings.py +240 -232
  375. meerk40t/rotary/rotary.py +202 -98
  376. meerk40t/ruida/control.py +291 -91
  377. meerk40t/ruida/controller.py +138 -1088
  378. meerk40t/ruida/device.py +676 -231
  379. meerk40t/ruida/driver.py +534 -472
  380. meerk40t/ruida/emulator.py +1494 -1491
  381. meerk40t/ruida/exceptions.py +4 -4
  382. meerk40t/ruida/gui/gui.py +71 -76
  383. meerk40t/ruida/gui/ruidaconfig.py +239 -72
  384. meerk40t/ruida/gui/ruidacontroller.py +187 -184
  385. meerk40t/ruida/gui/ruidaoperationproperties.py +48 -47
  386. meerk40t/ruida/loader.py +54 -52
  387. meerk40t/ruida/mock_connection.py +57 -109
  388. meerk40t/ruida/plugin.py +124 -87
  389. meerk40t/ruida/rdjob.py +2084 -945
  390. meerk40t/ruida/serial_connection.py +116 -0
  391. meerk40t/ruida/tcp_connection.py +146 -0
  392. meerk40t/ruida/udp_connection.py +73 -0
  393. meerk40t/svgelements.py +9671 -9669
  394. meerk40t/tools/driver_to_path.py +584 -579
  395. meerk40t/tools/geomstr.py +5583 -4680
  396. meerk40t/tools/jhfparser.py +357 -292
  397. meerk40t/tools/kerftest.py +904 -890
  398. meerk40t/tools/livinghinges.py +1168 -1033
  399. meerk40t/tools/pathtools.py +987 -949
  400. meerk40t/tools/pmatrix.py +234 -0
  401. meerk40t/tools/pointfinder.py +942 -942
  402. meerk40t/tools/polybool.py +941 -940
  403. meerk40t/tools/rasterplotter.py +1660 -547
  404. meerk40t/tools/shxparser.py +1047 -901
  405. meerk40t/tools/ttfparser.py +726 -446
  406. meerk40t/tools/zinglplotter.py +595 -593
  407. {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/LICENSE +21 -21
  408. {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/METADATA +150 -139
  409. meerk40t-0.9.7020.dist-info/RECORD +446 -0
  410. {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/WHEEL +1 -1
  411. {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/top_level.txt +0 -1
  412. {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/zip-safe +1 -1
  413. meerk40t/balormk/elementlightjob.py +0 -159
  414. meerk40t-0.9.3001.dist-info/RECORD +0 -437
  415. test/bootstrap.py +0 -63
  416. test/test_cli.py +0 -12
  417. test/test_core_cutcode.py +0 -418
  418. test/test_core_elements.py +0 -144
  419. test/test_core_plotplanner.py +0 -397
  420. test/test_core_viewports.py +0 -312
  421. test/test_drivers_grbl.py +0 -108
  422. test/test_drivers_lihuiyu.py +0 -443
  423. test/test_drivers_newly.py +0 -113
  424. test/test_element_degenerate_points.py +0 -43
  425. test/test_elements_classify.py +0 -97
  426. test/test_elements_penbox.py +0 -22
  427. test/test_file_svg.py +0 -176
  428. test/test_fill.py +0 -155
  429. test/test_geomstr.py +0 -1523
  430. test/test_geomstr_nodes.py +0 -18
  431. test/test_imagetools_actualize.py +0 -306
  432. test/test_imagetools_wizard.py +0 -258
  433. test/test_kernel.py +0 -200
  434. test/test_laser_speeds.py +0 -3303
  435. test/test_length.py +0 -57
  436. test/test_lifecycle.py +0 -66
  437. test/test_operations.py +0 -251
  438. test/test_operations_hatch.py +0 -57
  439. test/test_ruida.py +0 -19
  440. test/test_spooler.py +0 -22
  441. test/test_tools_rasterplotter.py +0 -29
  442. test/test_wobble.py +0 -133
  443. test/test_zingl.py +0 -124
  444. {test → meerk40t/cylinder}/__init__.py +0 -0
  445. /meerk40t/{core/element_commands.py → cylinder/gui/__init__.py} +0 -0
  446. {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/entry_points.txt +0 -0
@@ -1,745 +1,785 @@
1
- """
2
- Grid widget is primarily tasked with drawing the grid in the scene. This is the size and shape of the desired bedsize.
3
- """
4
-
5
- from math import atan2, cos, sin, sqrt, tau
6
- from platform import system
7
-
8
- import wx
9
-
10
- from meerk40t.core.units import Length
11
- from meerk40t.gui.laserrender import DRAW_MODE_GRID, DRAW_MODE_GUIDES
12
- from meerk40t.gui.scene.widget import Widget
13
-
14
-
15
- class GridWidget(Widget):
16
- """
17
- Scene Widget
18
- """
19
-
20
- def __init__(self, scene, name=None, suppress_labels=False):
21
- Widget.__init__(self, scene, all=True)
22
- if name is None:
23
- self.name = "Standard"
24
- else:
25
- self.name = name
26
- self.primary_grid_lines = None
27
- self.secondary_grid_lines = None
28
- self.background = None
29
- self.primary_grid_line_pen = wx.Pen()
30
- self.secondary_grid_line_pen = wx.Pen()
31
- self.circular_grid_line_pen = wx.Pen()
32
- self.last_ticksize = 0
33
- self.last_w = 0
34
- self.last_h = 0
35
- self.last_min_x = float("inf")
36
- self.last_min_y = float("inf")
37
- self.last_max_x = -float("inf")
38
- self.last_max_y = -float("inf")
39
- if suppress_labels is None:
40
- suppress_labels = False
41
- self.suppress_labels_in_all_cases = suppress_labels
42
-
43
- self.draw_grid = True
44
- self.primary_start_x = 0
45
- self.primary_start_y = 0
46
- self.secondary_start_x = 0
47
- self.secondary_start_y = 0
48
- self.circular_grid_center_x = 0
49
- self.circular_grid_center_y = 0
50
- # Min and max coords of the screen estate
51
- self.min_x = 0
52
- self.min_y = 0
53
- self.max_x = 0
54
- self.max_y = 0
55
- self.primary_tick_length_x = 0
56
- self.primary_tick_length_y = 0
57
- self.secondary_tick_length_x = 0
58
- self.secondary_tick_length_y = 0
59
- self.zero_x = 0
60
- self.zero_y = 0
61
- # Circular Grid
62
- self.min_radius = float("inf")
63
- self.max_radius = -float("inf")
64
- self.min_angle = 0
65
- self.max_angle = tau
66
- self.os = system()
67
-
68
- # Stuff related to grids and guides
69
- self.draw_grid_primary = True
70
- # Secondary grid, perpendicular, but with definable center and scaling
71
- self.draw_grid_secondary = False
72
- self.grid_secondary_cx = None
73
- self.grid_secondary_cy = None
74
- self.grid_secondary_scale_x = 1
75
- self.grid_secondary_scale_y = 1
76
- # Circular grid
77
- self.draw_grid_circular = False
78
- self.grid_circular_cx = None
79
- self.grid_circular_cy = None
80
- # self.auto_tick = False # by definition do not auto_tick
81
- # Let the grid resize itself
82
- self.auto_tick = True
83
- self.tick_distance = 0
84
-
85
- self.grid_points = None # Points representing the grid - total of primary + secondary + circular
86
-
87
- self.set_colors()
88
-
89
- @property
90
- def scene_scale(self):
91
- matrix = self.scene.widget_root.scene_widget.matrix
92
- try:
93
- scene_scale = sqrt(abs(matrix.determinant))
94
- if scene_scale < 1e-8:
95
- matrix.reset()
96
- return 1.0
97
- return scene_scale
98
- except (OverflowError, ValueError, ZeroDivisionError):
99
- matrix.reset()
100
- return 1.0
101
-
102
- ###########################
103
- # PEN SETUP
104
- ###########################
105
-
106
- def set_line_width(self, pen, line_width):
107
- # Sets the linewidth of a wx.pen
108
- # establish os-system
109
- if line_width < 1 and self.os == "Darwin":
110
- # Mac
111
- line_width = 1
112
- try:
113
- pen.SetWidth(line_width)
114
- except TypeError:
115
- pen.SetWidth(int(line_width))
116
-
117
- def _set_pen_width_from_matrix(self):
118
- line_width = 1.0 / self.scene_scale
119
- self.set_line_width(self.primary_grid_line_pen, line_width)
120
- self.set_line_width(self.secondary_grid_line_pen, line_width)
121
- self.set_line_width(self.circular_grid_line_pen, line_width)
122
-
123
- def set_colors(self):
124
- self.primary_grid_line_pen.SetColour(self.scene.colors.color_grid)
125
- self.secondary_grid_line_pen.SetColour(self.scene.colors.color_grid2)
126
- self.circular_grid_line_pen.SetColour(self.scene.colors.color_grid3)
127
- self.set_line_width(self.primary_grid_line_pen, 1)
128
- self.set_line_width(self.secondary_grid_line_pen, 1)
129
- self.set_line_width(self.circular_grid_line_pen, 1)
130
-
131
- ###########################
132
- # CALCULATE GRID LINES
133
- ###########################
134
-
135
- def _calc_primary_grid_lines(self):
136
- starts = []
137
- ends = []
138
- # Primary grid
139
- # We could be way too high
140
- start_x = self.zero_x
141
- while start_x - self.primary_tick_length_x > self.min_x:
142
- start_x -= self.primary_tick_length_x
143
- start_y = self.zero_y
144
- while start_y - self.primary_tick_length_y > self.min_y:
145
- start_y -= self.primary_tick_length_y
146
- # But we could be way too low, too
147
- while start_x < self.min_x:
148
- start_x += self.primary_tick_length_x
149
- while start_y < self.min_y:
150
- start_y += self.primary_tick_length_y
151
-
152
- x = start_x
153
- while x <= self.max_x:
154
- starts.append((x, self.min_y))
155
- ends.append((x, self.max_y))
156
- x += self.primary_tick_length_x
157
-
158
- y = start_y
159
- while y <= self.max_y:
160
- starts.append((self.min_x, y))
161
- ends.append((self.max_x, y))
162
- y += self.primary_tick_length_y
163
- self.primary_grid_lines = starts, ends
164
-
165
- def _calc_secondary_grid_lines(self):
166
- starts2 = []
167
- ends2 = []
168
- # Primary grid
169
- # Secondary grid
170
- # We could be way too high
171
- start_x = self.zero_x
172
- while start_x - self.secondary_tick_length_x > self.min_x:
173
- start_x -= self.secondary_tick_length_x
174
- start_y = self.zero_y
175
- while start_y - self.secondary_tick_length_y > self.min_y:
176
- start_y -= self.secondary_tick_length_y
177
- # But we could be way too low, too
178
- while start_x < self.min_x:
179
- start_x += self.secondary_tick_length_x
180
- while start_y < self.min_y:
181
- start_y += self.secondary_tick_length_y
182
-
183
- x = start_x
184
- while x <= self.max_x:
185
- starts2.append((x, self.min_y))
186
- ends2.append((x, self.max_y))
187
- x += self.secondary_tick_length_x
188
-
189
- y = start_y
190
- while y <= self.max_y:
191
- starts2.append((self.min_x, y))
192
- ends2.append((self.max_x, y))
193
- y += self.secondary_tick_length_y
194
- self.secondary_grid_lines = starts2, ends2
195
-
196
- def calculate_grid_lines(self):
197
- """
198
- Based on the current matrix calculate the grid within the bed-space.
199
- """
200
- d = self.scene.context
201
- self.zero_x, self.zero_y = d.space.origin_zero()
202
- self._calc_primary_grid_lines()
203
- self._calc_secondary_grid_lines()
204
-
205
- ###########################
206
- # CALCULATE PROPERTIES
207
- ###########################
208
-
209
- @property
210
- def scaled_conversion(self):
211
- return float(Length(f"1{self.scene.context.units_name}")) * self.scene_scale
212
-
213
- def calculate_tickdistance(self, w, h):
214
- # Establish the delta for about 15 ticks
215
- wpoints = w / 30.0
216
- hpoints = h / 20.0
217
- points = (wpoints + hpoints) / 2
218
- scaled_conversion = self.scaled_conversion
219
- if scaled_conversion == 0:
220
- return
221
- # tweak the scaled points into being useful.
222
- # points = scaled_conversion * round(points / scaled_conversion * 10.0) / 10.0
223
- delta = points / scaled_conversion
224
- # Let's establish a proper delta: we want to understand the log and x.yyy multiplikator
225
- x = delta
226
- factor = 1
227
- if x >= 1:
228
- while x >= 10:
229
- x *= 0.1
230
- factor *= 10
231
- else:
232
- while x < 1:
233
- x *= 10
234
- factor *= 0.1
235
-
236
- l_pref = delta / factor
237
- # Assign 'useful' scale
238
- if l_pref < 3:
239
- l_pref = 1
240
- # elif l_pref < 4:
241
- # l_pref = 2.5
242
- else:
243
- l_pref = 5.0
244
-
245
- delta1 = l_pref * factor
246
- # print("New Delta={delta}".format(delta=delta))
247
- # points = self.scaled_conversion * float("{:.1g}".format(points / self.scaled_conversion))
248
-
249
- self.tick_distance = delta1
250
-
251
- def calculate_center_start(self):
252
- p = self.scene.context
253
- self.primary_start_x, self.primary_start_y = p.space.origin_zero()
254
-
255
- if self.grid_secondary_cx is None:
256
- self.secondary_start_x = self.primary_start_x
257
- else:
258
- self.secondary_start_x = self.grid_secondary_cx
259
-
260
- if self.grid_secondary_cy is None:
261
- self.secondary_start_y = self.primary_start_y
262
- else:
263
- self.secondary_start_y = self.grid_secondary_cy
264
-
265
- if self.grid_circular_cx is None:
266
- self.circular_grid_center_x = self.primary_start_x
267
- else:
268
- self.circular_grid_center_x = self.grid_circular_cx
269
-
270
- if self.grid_circular_cy is None:
271
- self.circular_grid_center_y = self.primary_start_y
272
- else:
273
- self.circular_grid_center_y = self.grid_circular_cy
274
-
275
- def calculate_gridsize(self, w, h):
276
- self.min_x = float("inf")
277
- self.max_x = -float("inf")
278
- self.min_y = float("inf")
279
- self.max_y = -float("inf")
280
- for xx in (0, w):
281
- for yy in (0, h):
282
- x, y = self.scene.convert_window_to_scene([xx, yy])
283
- self.min_x = min(self.min_x, x)
284
- self.min_y = min(self.min_y, y)
285
- self.max_x = max(self.max_x, x)
286
- self.max_y = max(self.max_y, y)
287
-
288
- # self.min_x, self.min_y = self.scene.convert_window_to_scene([0, 0])
289
- # self.max_x, self.max_y = self.scene.convert_window_to_scene([w, h])
290
-
291
- self.min_x = max(0, self.min_x)
292
- self.min_y = max(0, self.min_y)
293
- self.max_x = min(self.scene.context.space.width, self.max_x)
294
- self.max_y = min(self.scene.context.space.height, self.max_y)
295
-
296
- def calculate_tick_length(self):
297
- tick_length = float(
298
- Length(f"{self.tick_distance}{self.scene.context.units_name}")
299
- )
300
- if tick_length == 0:
301
- tick_length = float(Length("10mm"))
302
- self.primary_tick_length_x = tick_length
303
- self.primary_tick_length_y = tick_length
304
- # print (f"x={self.tlenx1} ({Length(amount=self.tlenx1, digits=3).length_mm})")
305
- # print (f"y={self.tleny1} ({Length(amount=self.tleny1, digits=3).length_mm})")
306
- self.secondary_tick_length_x = (
307
- self.primary_tick_length_x * self.grid_secondary_scale_x
308
- )
309
- self.secondary_tick_length_y = (
310
- self.primary_tick_length_y * self.grid_secondary_scale_y
311
- )
312
-
313
- def calculate_radii_angles(self):
314
- # let's establish which circles we really have to draw
315
- self.min_radius = float("inf")
316
- self.max_radius = -float("inf")
317
- test_points = (
318
- # all 4 corners
319
- (self.min_x, self.min_y),
320
- (self.min_x, self.max_y),
321
- (self.max_x, self.min_y),
322
- (self.max_x, self.max_y),
323
- # and the boundary points aligned with the center
324
- (self.circular_grid_center_x, self.max_y),
325
- (self.circular_grid_center_x, self.min_y),
326
- (self.min_x, self.circular_grid_center_y),
327
- (self.max_x, self.circular_grid_center_y),
328
- )
329
- for i, pt in enumerate(test_points):
330
- dx = pt[0] - self.circular_grid_center_x
331
- dy = pt[1] - self.circular_grid_center_y
332
- r = sqrt(dx * dx + dy * dy)
333
- if r < self.min_radius:
334
- self.min_radius = r
335
- if r > self.max_radius:
336
- self.max_radius = r
337
-
338
- # 1 | 2 | 3
339
- # --+---+--
340
- # 4 | 5 | 6
341
- # --+---+--
342
- # 7 | 8 | 9
343
- min_a = float("inf")
344
- max_a = -float("inf")
345
- if self.circular_grid_center_x <= self.min_x:
346
- # left
347
- if self.circular_grid_center_y <= self.min_y:
348
- # below
349
- pt1 = (self.min_x, self.max_y)
350
- pt2 = (self.max_x, self.min_y)
351
- elif self.circular_grid_center_y >= self.max_y:
352
- # above
353
- pt1 = (self.max_x, self.max_y)
354
- pt2 = (self.min_x, self.min_y)
355
- else:
356
- # between
357
- pt1 = (self.min_x, self.max_y)
358
- pt2 = (self.min_x, self.min_y)
359
- elif self.circular_grid_center_x >= self.max_x:
360
- # right
361
- if self.circular_grid_center_y <= self.min_y:
362
- # below
363
- pt1 = (self.min_x, self.min_y)
364
- pt2 = (self.max_x, self.max_y)
365
- elif self.circular_grid_center_y >= self.max_y:
366
- # above
367
- pt1 = (self.max_x, self.min_y)
368
- pt2 = (self.min_x, self.max_y)
369
- else:
370
- # between
371
- pt1 = (self.max_x, self.min_y)
372
- pt2 = (self.max_x, self.max_y)
373
- else:
374
- # between
375
- if self.circular_grid_center_y <= self.min_y:
376
- # below
377
- pt1 = (self.min_x, self.min_y)
378
- pt2 = (self.max_x, self.min_y)
379
- elif self.circular_grid_center_y >= self.max_y:
380
- # above
381
- pt1 = (self.max_x, self.max_y)
382
- pt2 = (self.min_x, self.max_y)
383
- else:
384
- # between
385
- pt1 = None
386
- pt2 = None
387
- min_a = 0
388
- max_a = tau
389
- if pt1 is not None:
390
- dx1 = pt1[0] - self.circular_grid_center_x
391
- dy1 = pt1[1] - self.circular_grid_center_y
392
- dx2 = pt2[0] - self.circular_grid_center_x
393
- dy2 = pt2[1] - self.circular_grid_center_y
394
- max_a = atan2(dy1, dx1)
395
- min_a = atan2(dy2, dx2)
396
-
397
- while max_a < min_a:
398
- max_a += tau
399
- while min_a < 0:
400
- min_a += tau
401
- max_a += tau
402
- self.min_angle = min_a
403
- self.max_angle = max_a
404
- if (
405
- self.min_x < self.circular_grid_center_x < self.max_x
406
- and self.min_y < self.circular_grid_center_y < self.max_y
407
- ):
408
- self.min_radius = 0
409
-
410
- ###########################
411
- # CALCULATE GRID POINTS
412
- ###########################
413
-
414
- def calculate_scene_grid_points(self):
415
- """
416
- Looks at all elements (all_points=True) or at non-selected elements (all_points=False)
417
- and identifies all attraction points (center, corners, sides)
418
- Notabene this calculation generates SCREEN coordinates
419
- """
420
- self.grid_points = [] # Clear all
421
-
422
- # Let's add grid points - set just the visible part of the grid
423
-
424
- if self.draw_grid_primary:
425
- self._calculate_grid_points_primary()
426
- if self.draw_grid_secondary:
427
- self._calculate_grid_points_secondary()
428
- if self.draw_grid_circular:
429
- self._calculate_grid_points_circular()
430
-
431
- def _calculate_grid_points_primary(self):
432
- # That's easy just the rectangular stuff
433
- # We could be way too high
434
- start_x = self.zero_x
435
- while start_x - self.primary_tick_length_x > self.min_x:
436
- start_x -= self.primary_tick_length_x
437
- start_y = self.zero_y
438
- while start_y - self.primary_tick_length_y > self.min_y:
439
- start_y -= self.primary_tick_length_y
440
- # But we could be way too low, too
441
- while start_x < self.min_x:
442
- start_x += self.primary_tick_length_x
443
- while start_y < self.min_y:
444
- start_y += self.primary_tick_length_y
445
- x = start_x
446
- while x <= self.max_x:
447
- y = start_y
448
- while y <= self.max_y:
449
- # mx, my = self.scene.convert_scene_to_window([x, y])
450
- self.grid_points.append([x, y])
451
- y += self.primary_tick_length_y
452
- x += self.primary_tick_length_x
453
-
454
- def _calculate_grid_points_secondary(self):
455
- if (
456
- self.draw_grid_primary
457
- and self.primary_start_x == 0
458
- and self.primary_start_y == 0
459
- and self.grid_secondary_scale_x == 1
460
- and self.grid_secondary_scale_y == 1
461
- ):
462
- return # is it identical to the primary?
463
- # We could be way too high
464
- start_x = self.zero_x
465
- while start_x - self.secondary_tick_length_x > self.min_x:
466
- start_x -= self.secondary_tick_length_x
467
- start_y = self.zero_y
468
- while start_y - self.secondary_tick_length_y > self.min_y:
469
- start_y -= self.secondary_tick_length_y
470
- # But we could be way too low, too
471
- while start_x < self.min_x:
472
- start_x += self.secondary_tick_length_x
473
- while start_y < self.min_y:
474
- start_y += self.secondary_tick_length_y
475
- x = start_x
476
- while x <= self.max_x:
477
- y = start_y
478
- while y <= self.max_y:
479
- # mx, my = self.scene.convert_scene_to_window([x, y])
480
- self.grid_points.append([x, y])
481
- y += self.secondary_tick_length_y
482
- x += self.secondary_tick_length_x
483
-
484
- def _calculate_grid_points_circular(self):
485
- p = self.scene.context
486
- # Okay, we are drawing on 48 segments line, even from center to outline, odd from 1/3rd to outline
487
- start_x = self.circular_grid_center_x
488
- start_y = self.circular_grid_center_y
489
- x = start_x
490
- y = start_y
491
- # mx, my = self.scene.convert_scene_to_window([x, y])
492
- self.grid_points.append([x, y])
493
- max_r = abs(
494
- complex(p.device.view.unit_width, p.device.view.unit_height)
495
- ) # hypot
496
- tick_length = (self.primary_tick_length_x + self.primary_tick_length_y) / 2
497
- r_fourth = max_r // (4 * tick_length) * tick_length
498
- segments = 48
499
- r_angle = 0
500
- i = 0
501
- while r_angle < self.min_angle:
502
- r_angle += tau / segments
503
- i += 1
504
- while r_angle < self.max_angle:
505
- c_angle = r_angle
506
- while c_angle > tau:
507
- c_angle -= tau
508
- if i % 2 == 0:
509
- r = 0
510
- else:
511
- r = r_fourth
512
- while r < self.min_radius:
513
- r += tick_length
514
-
515
- while r <= self.max_radius:
516
- r += tick_length
517
- x = start_x + r * cos(c_angle)
518
- y = start_y + r * sin(c_angle)
519
-
520
- if self.min_x <= x <= self.max_x and self.min_y <= y <= self.max_y:
521
- # mx, my = self.scene.convert_scene_to_window([x, y])
522
- self.grid_points.append([x, y])
523
-
524
- i += 1
525
- r_angle += tau / segments
526
-
527
- ###########################
528
- # WIDGET DRAW AND PROCESS
529
- ###########################
530
-
531
- def process_draw(self, gc):
532
- """
533
- Draw the grid on the scene.
534
- """
535
- # print ("GridWidget %s draw" % self.name)
536
-
537
- # Get proper gridsize
538
- w, h = gc.Size
539
- if w < 50 or h < 50:
540
- # Algorithm is unstable for very low values of w or h.
541
- return
542
- if self.auto_tick:
543
- self.calculate_tickdistance(w, h)
544
- self.calculate_center_start()
545
- self.calculate_gridsize(w, h)
546
- self.calculate_tick_length()
547
- self.calculate_radii_angles()
548
-
549
- # When do we need to redraw?!
550
- if self.last_ticksize != self.tick_distance:
551
- self.last_ticksize = self.tick_distance
552
- self.primary_grid_lines = None
553
- # With the new zoom-algorithm we also need to redraw if the origin
554
- # or the size have changed...
555
- # That's a price I am willing to pay...
556
- if self.last_w != w or self.last_h != h:
557
- self.last_w = w
558
- self.last_h = h
559
- self.primary_grid_lines = None
560
- if self.min_x != self.last_min_x or self.min_y != self.last_min_y:
561
- self.last_min_x = self.min_x
562
- self.last_min_y = self.min_y
563
- self.primary_grid_lines = None
564
- if self.max_x != self.last_max_x or self.max_y != self.last_max_y:
565
- self.last_max_x = self.max_x
566
- self.last_max_y = self.max_y
567
- self.primary_grid_lines = None
568
-
569
- if self.scene.context.draw_mode & DRAW_MODE_GRID != 0:
570
- return # Do not draw grid.
571
-
572
- if self.primary_grid_lines is None or self.secondary_grid_lines is None:
573
- self.calculate_grid_lines()
574
- self.calculate_scene_grid_points()
575
-
576
- self._set_pen_width_from_matrix()
577
-
578
- gc.SetPen(self.primary_grid_line_pen)
579
- brush = wx.Brush(
580
- colour=self.scene.colors.color_bed, style=wx.BRUSHSTYLE_TRANSPARENT
581
- )
582
- gc.SetBrush(brush)
583
- # There is a bug in wxPython v4.1.1 and below that will not allow to apply a LineWidth below a given level:
584
- # At a matrix scale value of about 17.2 and a corresponding line width of 0.058 everything looks good
585
- # but one step more with 18.9 and 0.053 the lines degenerate...
586
- # Interestingly, this does not apply to arcs in a path, they remain at 1 pixel.
587
- if self.draw_grid_circular:
588
- self._draw_grid_circular(gc)
589
- if self.draw_grid_secondary:
590
- self._draw_grid_secondary(gc)
591
- if self.draw_grid_primary:
592
- self._draw_grid_primary(gc)
593
-
594
- def _draw_grid_primary(self, gc):
595
- starts, ends = self.primary_grid_lines
596
- gc.SetPen(self.primary_grid_line_pen)
597
- grid_path = gc.CreatePath()
598
- if starts and ends:
599
- for i in range(len(starts)):
600
- sx = starts[i][0]
601
- sy = starts[i][1]
602
- grid_path.MoveToPoint(sx, sy)
603
- sx = ends[i][0]
604
- sy = ends[i][1]
605
- grid_path.AddLineToPoint(sx, sy)
606
- gc.StrokePath(grid_path)
607
-
608
- def _draw_grid_secondary(self, gc):
609
- starts2, ends2 = self.secondary_grid_lines
610
- gc.SetPen(self.secondary_grid_line_pen)
611
- grid_path = gc.CreatePath()
612
- if starts2 and ends2:
613
- for i in range(len(starts2)):
614
- sx = starts2[i][0]
615
- sy = starts2[i][1]
616
- grid_path.MoveToPoint(sx, sy)
617
- sx = ends2[i][0]
618
- sy = ends2[i][1]
619
- grid_path.AddLineToPoint(sx, sy)
620
- gc.StrokePath(grid_path)
621
-
622
- # gc.StrokeLineSegments(starts2, ends2)
623
-
624
- def _draw_grid_circular(self, gc):
625
- gc.SetPen(self.circular_grid_line_pen)
626
- u_width = float(self.scene.context.device.view.unit_width)
627
- u_height = float(self.scene.context.device.view.unit_height)
628
- gc.Clip(0, 0, u_width, u_height)
629
- siz = sqrt(u_width * u_width + u_height * u_height)
630
- sox = self.circular_grid_center_x / u_width
631
- soy = self.circular_grid_center_y / u_height
632
- step = self.primary_tick_length_x
633
- factor = max(2 * (1 - sox), 2 * (1 - soy))
634
- # Initially I drew a complete circle, which is a waste in most situations,
635
- # so let's create a path
636
- circle_path = gc.CreatePath()
637
- y = 0
638
- while y < 2 * self.min_radius:
639
- y += 2 * step
640
- while y < 2 * self.max_radius:
641
- y += 2 * step
642
- spoint_x = self.circular_grid_center_x + y / 2 * cos(self.min_angle)
643
- spoint_y = self.circular_grid_center_x + y / 2 * sin(self.min_angle)
644
- circle_path.MoveToPoint(spoint_x, spoint_y)
645
- # gc.DrawEllipse(self.cx - y / 2, self.cy - y / 2, y, y)
646
- circle_path.AddArc(
647
- self.circular_grid_center_x,
648
- self.circular_grid_center_y,
649
- y / 2,
650
- self.min_angle,
651
- self.max_angle,
652
- True,
653
- )
654
- gc.StrokePath(circle_path)
655
- # circle_path.AddArc(self.cx, self.cy, y, self.min_angle, self.max_angle)
656
- # (around one fourth of radius)
657
- mid_y = y // (4 * step) * step
658
- # print("Last Y=%.1f (%s), mid_y=%.1f (%s)" % (y, Length(amount=y).length_mm, mid_y, Length(amount=mid_y).length_mm))
659
- radials_start = []
660
- radials_end = []
661
- fsize = 10 / self.scene.widget_root.scene_widget.matrix.value_scale_x()
662
- if fsize < 1.0:
663
- fsize = 1.0 # Mac does not allow values lower than 1.
664
- try:
665
- font = wx.Font(
666
- fsize,
667
- wx.FONTFAMILY_SWISS,
668
- wx.FONTSTYLE_NORMAL,
669
- wx.FONTWEIGHT_BOLD,
670
- )
671
- except TypeError:
672
- font = wx.Font(
673
- int(fsize),
674
- wx.FONTFAMILY_SWISS,
675
- wx.FONTSTYLE_NORMAL,
676
- wx.FONTWEIGHT_BOLD,
677
- )
678
- # gc.SetFont(font, wx.BLACK)
679
- # debugstr = "Angle= %.1f - %.1f (%d)" % (self.min_angle/tau*360, self.max_angle/tau*360, self.sector)
680
- # gc.DrawText(debugstr, (self.min_x + self.max_x)/2, (self.min_y + self.max_y)/2)
681
- gc.SetFont(font, self.scene.colors.color_guide3)
682
- segments = 48
683
- r_angle = 0
684
- i = 0
685
- while r_angle < self.min_angle:
686
- r_angle += tau / segments
687
- i += 1
688
-
689
- # Draw radials...
690
- while r_angle < self.max_angle:
691
- c_angle = r_angle
692
- while c_angle > tau:
693
- c_angle -= tau
694
- if i % 2 == 0:
695
- degang = round(c_angle / tau * 360, 1)
696
- if degang == 360:
697
- degang = 0
698
- a_text = f"{degang:.0f}°"
699
- (t_width, t_height) = gc.GetTextExtent(a_text)
700
- # Make sure text remains legible without breaking your neck... ;-)
701
- if tau * 1 / 4 < c_angle < tau * 3 / 4:
702
- myangle = (-1.0 * c_angle) + tau / 2
703
- dx = t_width
704
- else:
705
- myangle = -1.0 * c_angle
706
- dx = 0
707
- if (
708
- self.scene.context.draw_mode & DRAW_MODE_GUIDES == 0
709
- or self.suppress_labels_in_all_cases
710
- ):
711
- gc.DrawText(
712
- a_text,
713
- self.circular_grid_center_x + cos(c_angle) * (mid_y + dx),
714
- self.circular_grid_center_y + sin(c_angle) * (mid_y + dx),
715
- myangle,
716
- )
717
- s_factor = 0
718
- else:
719
- s_factor = 1
720
- radials_start.append(
721
- (
722
- self.circular_grid_center_x + s_factor * 0.5 * mid_y * cos(c_angle),
723
- self.circular_grid_center_y + s_factor * 0.5 * mid_y * sin(c_angle),
724
- )
725
- )
726
- radials_end.append(
727
- (
728
- self.circular_grid_center_x + 0.5 * y * cos(c_angle),
729
- self.circular_grid_center_y + 0.5 * y * sin(c_angle),
730
- )
731
- )
732
- r_angle += tau / segments
733
- i += 1
734
- if len(radials_start) > 0:
735
- gc.StrokeLineSegments(radials_start, radials_end)
736
- gc.ResetClip()
737
-
738
- def signal(self, signal, *args, **kwargs):
739
- """
740
- Signal commands which draw the background and updates the grid when needed to recalculate the lines
741
- """
742
- if signal == "grid":
743
- self.primary_grid_lines = None
744
- elif signal == "theme":
745
- self.set_colors()
1
+ """
2
+ Grid widget is primarily tasked with drawing the grid in the scene. This is the size and shape of the desired bedsize.
3
+ """
4
+
5
+ from math import atan2, cos, sin, sqrt, tau
6
+ from platform import system
7
+
8
+ import wx
9
+
10
+ from meerk40t.core.units import Length
11
+ from meerk40t.gui.laserrender import DRAW_MODE_GRID, DRAW_MODE_GUIDES
12
+ from meerk40t.gui.scene.widget import Widget
13
+
14
+
15
+ class GridWidget(Widget):
16
+ """
17
+ Scene Widget
18
+ """
19
+
20
+ def __init__(self, scene, name=None, suppress_labels=False):
21
+ Widget.__init__(self, scene, all=True)
22
+ if name is None:
23
+ self.name = "Standard"
24
+ else:
25
+ self.name = name
26
+ self.primary_grid_lines = None
27
+ self.secondary_grid_lines = None
28
+ self.background = None
29
+ self.primary_grid_line_pen = wx.Pen()
30
+ self.secondary_grid_line_pen = wx.Pen()
31
+ self.circular_grid_line_pen = wx.Pen()
32
+ self.offset_line_pen = wx.Pen()
33
+ self.last_ticksize = 0
34
+ self.last_w = 0
35
+ self.last_h = 0
36
+ self.last_min_x = float("inf")
37
+ self.last_min_y = float("inf")
38
+ self.last_max_x = -float("inf")
39
+ self.last_max_y = -float("inf")
40
+ if suppress_labels is None:
41
+ suppress_labels = False
42
+ self.suppress_labels_in_all_cases = suppress_labels
43
+
44
+ self.draw_grid = True
45
+ self.primary_start_x = 0
46
+ self.primary_start_y = 0
47
+ self.secondary_start_x = 0
48
+ self.secondary_start_y = 0
49
+ self.circular_grid_center_x = 0
50
+ self.circular_grid_center_y = 0
51
+ # Min and max coords of the screen estate
52
+ self.min_x = 0
53
+ self.min_y = 0
54
+ self.max_x = 0
55
+ self.max_y = 0
56
+ self.primary_tick_length_x = 0
57
+ self.primary_tick_length_y = 0
58
+ self.secondary_tick_length_x = 0
59
+ self.secondary_tick_length_y = 0
60
+ self.zero_x = 0
61
+ self.zero_y = 0
62
+ # Circular Grid
63
+ self.min_radius = float("inf")
64
+ self.max_radius = -float("inf")
65
+ self.min_angle = 0
66
+ self.max_angle = tau
67
+ self.os = system()
68
+
69
+ # If there is a user margin then display the physical dimensions
70
+ self.draw_offset_lines = False
71
+ # Stuff related to grids and guides
72
+ self.draw_grid_primary = True
73
+ # Secondary grid, perpendicular, but with definable center and scaling
74
+ self.draw_grid_secondary = False
75
+ self.grid_secondary_cx = None
76
+ self.grid_secondary_cy = None
77
+ self.grid_secondary_scale_x = 1
78
+ self.grid_secondary_scale_y = 1
79
+ self.set_secondary_axis_scales()
80
+ # Circular grid
81
+ self.draw_grid_circular = False
82
+ self.grid_circular_cx = None
83
+ self.grid_circular_cy = None
84
+ # self.auto_tick = False # by definition do not auto_tick
85
+ # Let the grid resize itself
86
+ self.auto_tick = True
87
+ self.tick_distance = 0
88
+
89
+ self.grid_points = None # Points representing the grid - total of primary + secondary + circular
90
+
91
+ self.set_colors()
92
+
93
+ def set_secondary_axis_scales(self):
94
+ sx = 1.0
95
+ sy = 1.0
96
+ if hasattr(self.scene.context.device, "rotary"):
97
+ if self.scene.context.device.rotary.scale_x is not None:
98
+ sx = self.scene.context.device.rotary.scale_x
99
+ if self.scene.context.device.rotary.scale_y is not None:
100
+ sy = self.scene.context.device.rotary.scale_y
101
+
102
+ self.grid_secondary_scale_x = sx
103
+ self.grid_secondary_scale_y = sy
104
+
105
+ @property
106
+ def scene_scale(self):
107
+ matrix = self.scene.widget_root.scene_widget.matrix
108
+ try:
109
+ scene_scale = sqrt(abs(matrix.determinant))
110
+ if scene_scale < 1e-8:
111
+ matrix.reset()
112
+ return 1.0
113
+ return scene_scale
114
+ except (OverflowError, ValueError, ZeroDivisionError):
115
+ matrix.reset()
116
+ return 1.0
117
+
118
+ ###########################
119
+ # PEN SETUP
120
+ ###########################
121
+
122
+ def set_line_width(self, pen, line_width):
123
+ # Sets the linewidth of a wx.pen
124
+ # establish os-system
125
+ if line_width < 1 and self.os == "Darwin":
126
+ # Mac
127
+ line_width = 1
128
+ try:
129
+ pen.SetWidth(line_width)
130
+ except TypeError:
131
+ pen.SetWidth(int(line_width))
132
+
133
+ def _set_pen_width_from_matrix(self):
134
+ line_width = 1.0 / self.scene_scale
135
+ self.set_line_width(self.primary_grid_line_pen, line_width)
136
+ self.set_line_width(self.secondary_grid_line_pen, line_width)
137
+ self.set_line_width(self.circular_grid_line_pen, line_width)
138
+ self.set_line_width(self.offset_line_pen, line_width)
139
+
140
+ def set_colors(self):
141
+ self.primary_grid_line_pen.SetColour(self.scene.colors.color_grid)
142
+ self.secondary_grid_line_pen.SetColour(self.scene.colors.color_grid2)
143
+ self.circular_grid_line_pen.SetColour(self.scene.colors.color_grid3)
144
+ self.offset_line_pen.SetColour(wx.GREEN)
145
+ self.set_line_width(self.primary_grid_line_pen, 1)
146
+ self.set_line_width(self.secondary_grid_line_pen, 1)
147
+ self.set_line_width(self.circular_grid_line_pen, 1)
148
+ self.set_line_width(self.offset_line_pen, 1)
149
+
150
+ ###########################
151
+ # CALCULATE GRID LINES
152
+ ###########################
153
+
154
+ def _calc_primary_grid_lines(self):
155
+ starts = []
156
+ ends = []
157
+ # Primary grid
158
+ # We could be way too high
159
+ start_x = self.zero_x
160
+ while start_x - self.primary_tick_length_x > self.min_x:
161
+ start_x -= self.primary_tick_length_x
162
+ start_y = self.zero_y
163
+ while start_y - self.primary_tick_length_y > self.min_y:
164
+ start_y -= self.primary_tick_length_y
165
+ # But we could be way too low, too
166
+ while start_x < self.min_x:
167
+ start_x += self.primary_tick_length_x
168
+ while start_y < self.min_y:
169
+ start_y += self.primary_tick_length_y
170
+
171
+ x = start_x
172
+ while x <= self.max_x:
173
+ starts.append((x, self.min_y))
174
+ ends.append((x, self.max_y))
175
+ x += self.primary_tick_length_x
176
+
177
+ y = start_y
178
+ while y <= self.max_y:
179
+ starts.append((self.min_x, y))
180
+ ends.append((self.max_x, y))
181
+ y += self.primary_tick_length_y
182
+ self.primary_grid_lines = starts, ends
183
+
184
+ def _calc_secondary_grid_lines(self):
185
+ starts2 = []
186
+ ends2 = []
187
+ # Primary grid
188
+ # Secondary grid
189
+ # We could be way too high
190
+ start_x = self.zero_x
191
+ while start_x - self.secondary_tick_length_x > self.min_x:
192
+ start_x -= self.secondary_tick_length_x
193
+ start_y = self.zero_y
194
+ while start_y - self.secondary_tick_length_y > self.min_y:
195
+ start_y -= self.secondary_tick_length_y
196
+ # But we could be way too low, too
197
+ while start_x < self.min_x:
198
+ start_x += self.secondary_tick_length_x
199
+ while start_y < self.min_y:
200
+ start_y += self.secondary_tick_length_y
201
+
202
+ x = start_x
203
+ while x <= self.max_x:
204
+ starts2.append((x, self.min_y))
205
+ ends2.append((x, self.max_y))
206
+ x += self.secondary_tick_length_x
207
+
208
+ y = start_y
209
+ while y <= self.max_y:
210
+ starts2.append((self.min_x, y))
211
+ ends2.append((self.max_x, y))
212
+ y += self.secondary_tick_length_y
213
+ self.secondary_grid_lines = starts2, ends2
214
+
215
+ def calculate_grid_lines(self):
216
+ """
217
+ Based on the current matrix calculate the grid within the bed-space.
218
+ """
219
+ d = self.scene.context
220
+ self.set_secondary_axis_scales()
221
+ self.zero_x, self.zero_y = d.space.origin_zero()
222
+ self._calc_primary_grid_lines()
223
+ self._calc_secondary_grid_lines()
224
+
225
+ ###########################
226
+ # CALCULATE PROPERTIES
227
+ ###########################
228
+
229
+ @property
230
+ def scaled_conversion(self):
231
+ return float(Length(f"1{self.scene.context.units_name}")) * self.scene_scale
232
+
233
+ def calculate_tickdistance(self, w, h):
234
+ # Establish the delta for about 15 ticks
235
+ wpoints = w / 30.0
236
+ hpoints = h / 20.0
237
+ points = (wpoints + hpoints) / 2
238
+ scaled_conversion = self.scaled_conversion
239
+ if scaled_conversion == 0:
240
+ return
241
+ # tweak the scaled points into being useful.
242
+ # points = scaled_conversion * round(points / scaled_conversion * 10.0) / 10.0
243
+ delta = points / scaled_conversion
244
+ # Let's establish a proper delta: we want to understand the log and x.yyy multiplikator
245
+ x = delta
246
+ factor = 1
247
+ if x >= 1:
248
+ while x >= 10:
249
+ x *= 0.1
250
+ factor *= 10
251
+ else:
252
+ while x < 1:
253
+ x *= 10
254
+ factor *= 0.1
255
+
256
+ l_pref = delta / factor
257
+ # Assign 'useful' scale
258
+ if l_pref < 3:
259
+ l_pref = 1
260
+ # elif l_pref < 4:
261
+ # l_pref = 2.5
262
+ else:
263
+ l_pref = 5.0
264
+
265
+ delta1 = l_pref * factor
266
+ # print("New Delta={delta}".format(delta=delta))
267
+ # points = self.scaled_conversion * float("{:.1g}".format(points / self.scaled_conversion))
268
+
269
+ self.tick_distance = delta1
270
+
271
+ def calculate_center_start(self):
272
+ p = self.scene.context
273
+ self.primary_start_x, self.primary_start_y = p.space.origin_zero()
274
+
275
+ if self.grid_secondary_cx is None:
276
+ self.secondary_start_x = self.primary_start_x
277
+ else:
278
+ self.secondary_start_x = self.grid_secondary_cx
279
+
280
+ if self.grid_secondary_cy is None:
281
+ self.secondary_start_y = self.primary_start_y
282
+ else:
283
+ self.secondary_start_y = self.grid_secondary_cy
284
+
285
+ if self.grid_circular_cx is None:
286
+ self.circular_grid_center_x = self.primary_start_x
287
+ else:
288
+ self.circular_grid_center_x = self.grid_circular_cx
289
+
290
+ if self.grid_circular_cy is None:
291
+ self.circular_grid_center_y = self.primary_start_y
292
+ else:
293
+ self.circular_grid_center_y = self.grid_circular_cy
294
+
295
+ def calculate_gridsize(self, w, h):
296
+ self.min_x = float("inf")
297
+ self.max_x = -float("inf")
298
+ self.min_y = float("inf")
299
+ self.max_y = -float("inf")
300
+ for xx in (0, w):
301
+ for yy in (0, h):
302
+ x, y = self.scene.convert_window_to_scene([xx, yy])
303
+ self.min_x = min(self.min_x, x)
304
+ self.min_y = min(self.min_y, y)
305
+ self.max_x = max(self.max_x, x)
306
+ self.max_y = max(self.max_y, y)
307
+
308
+ # self.min_x, self.min_y = self.scene.convert_window_to_scene([0, 0])
309
+ # self.max_x, self.max_y = self.scene.convert_window_to_scene([w, h])
310
+
311
+ self.min_x = max(0, self.min_x)
312
+ self.min_y = max(0, self.min_y)
313
+ self.max_x = min(self.scene.context.space.width, self.max_x)
314
+ self.max_y = min(self.scene.context.space.height, self.max_y)
315
+
316
+ def calculate_tick_length(self):
317
+ tick_length = float(
318
+ Length(f"{self.tick_distance}{self.scene.context.units_name}")
319
+ )
320
+ if tick_length == 0:
321
+ tick_length = float(Length("10mm"))
322
+ self.primary_tick_length_x = tick_length
323
+ self.primary_tick_length_y = tick_length
324
+ # print (f"x={self.tlenx1} ({Length(amount=self.tlenx1, digits=3).length_mm})")
325
+ # print (f"y={self.tleny1} ({Length(amount=self.tleny1, digits=3).length_mm})")
326
+ self.secondary_tick_length_x = (
327
+ self.primary_tick_length_x * self.grid_secondary_scale_x
328
+ )
329
+ self.secondary_tick_length_y = (
330
+ self.primary_tick_length_y * self.grid_secondary_scale_y
331
+ )
332
+
333
+ def calculate_radii_angles(self):
334
+ # let's establish which circles we really have to draw
335
+ self.min_radius = float("inf")
336
+ self.max_radius = -float("inf")
337
+ test_points = (
338
+ # all 4 corners
339
+ (self.min_x, self.min_y),
340
+ (self.min_x, self.max_y),
341
+ (self.max_x, self.min_y),
342
+ (self.max_x, self.max_y),
343
+ # and the boundary points aligned with the center
344
+ (self.circular_grid_center_x, self.max_y),
345
+ (self.circular_grid_center_x, self.min_y),
346
+ (self.min_x, self.circular_grid_center_y),
347
+ (self.max_x, self.circular_grid_center_y),
348
+ )
349
+ for i, pt in enumerate(test_points):
350
+ dx = pt[0] - self.circular_grid_center_x
351
+ dy = pt[1] - self.circular_grid_center_y
352
+ r = sqrt(dx * dx + dy * dy)
353
+ if r < self.min_radius:
354
+ self.min_radius = r
355
+ if r > self.max_radius:
356
+ self.max_radius = r
357
+
358
+ # 1 | 2 | 3
359
+ # --+---+--
360
+ # 4 | 5 | 6
361
+ # --+---+--
362
+ # 7 | 8 | 9
363
+ min_a = float("inf")
364
+ max_a = -float("inf")
365
+ if self.circular_grid_center_x <= self.min_x:
366
+ # left
367
+ if self.circular_grid_center_y <= self.min_y:
368
+ # below
369
+ pt1 = (self.min_x, self.max_y)
370
+ pt2 = (self.max_x, self.min_y)
371
+ elif self.circular_grid_center_y >= self.max_y:
372
+ # above
373
+ pt1 = (self.max_x, self.max_y)
374
+ pt2 = (self.min_x, self.min_y)
375
+ else:
376
+ # between
377
+ pt1 = (self.min_x, self.max_y)
378
+ pt2 = (self.min_x, self.min_y)
379
+ elif self.circular_grid_center_x >= self.max_x:
380
+ # right
381
+ if self.circular_grid_center_y <= self.min_y:
382
+ # below
383
+ pt1 = (self.min_x, self.min_y)
384
+ pt2 = (self.max_x, self.max_y)
385
+ elif self.circular_grid_center_y >= self.max_y:
386
+ # above
387
+ pt1 = (self.max_x, self.min_y)
388
+ pt2 = (self.min_x, self.max_y)
389
+ else:
390
+ # between
391
+ pt1 = (self.max_x, self.min_y)
392
+ pt2 = (self.max_x, self.max_y)
393
+ else:
394
+ # between
395
+ if self.circular_grid_center_y <= self.min_y:
396
+ # below
397
+ pt1 = (self.min_x, self.min_y)
398
+ pt2 = (self.max_x, self.min_y)
399
+ elif self.circular_grid_center_y >= self.max_y:
400
+ # above
401
+ pt1 = (self.max_x, self.max_y)
402
+ pt2 = (self.min_x, self.max_y)
403
+ else:
404
+ # between
405
+ pt1 = None
406
+ pt2 = None
407
+ min_a = 0
408
+ max_a = tau
409
+ if pt1 is not None:
410
+ dx1 = pt1[0] - self.circular_grid_center_x
411
+ dy1 = pt1[1] - self.circular_grid_center_y
412
+ dx2 = pt2[0] - self.circular_grid_center_x
413
+ dy2 = pt2[1] - self.circular_grid_center_y
414
+ max_a = atan2(dy1, dx1)
415
+ min_a = atan2(dy2, dx2)
416
+
417
+ while max_a < min_a:
418
+ max_a += tau
419
+ while min_a < 0:
420
+ min_a += tau
421
+ max_a += tau
422
+ self.min_angle = min_a
423
+ self.max_angle = max_a
424
+ if (
425
+ self.min_x < self.circular_grid_center_x < self.max_x
426
+ and self.min_y < self.circular_grid_center_y < self.max_y
427
+ ):
428
+ self.min_radius = 0
429
+
430
+ ###########################
431
+ # CALCULATE GRID POINTS
432
+ ###########################
433
+
434
+ def calculate_scene_grid_points(self):
435
+ """
436
+ Looks at all elements (all_points=True) or at non-selected elements (all_points=False)
437
+ and identifies all attraction points (center, corners, sides)
438
+ Notabene this calculation generates SCREEN coordinates
439
+ """
440
+ self.grid_points = [] # Clear all
441
+
442
+ # Let's add grid points - set just the visible part of the grid
443
+
444
+ if self.draw_grid_primary:
445
+ self._calculate_grid_points_primary()
446
+ if self.draw_grid_secondary:
447
+ self._calculate_grid_points_secondary()
448
+ if self.draw_grid_circular:
449
+ self._calculate_grid_points_circular()
450
+
451
+ def _calculate_grid_points_primary(self):
452
+ # That's easy just the rectangular stuff
453
+ # We could be way too high
454
+ start_x = self.zero_x
455
+ while start_x - self.primary_tick_length_x > self.min_x:
456
+ start_x -= self.primary_tick_length_x
457
+ start_y = self.zero_y
458
+ while start_y - self.primary_tick_length_y > self.min_y:
459
+ start_y -= self.primary_tick_length_y
460
+ # But we could be way too low, too
461
+ while start_x < self.min_x:
462
+ start_x += self.primary_tick_length_x
463
+ while start_y < self.min_y:
464
+ start_y += self.primary_tick_length_y
465
+ x = start_x
466
+ while x <= self.max_x:
467
+ y = start_y
468
+ while y <= self.max_y:
469
+ # mx, my = self.scene.convert_scene_to_window([x, y])
470
+ self.grid_points.append([x, y])
471
+ y += self.primary_tick_length_y
472
+ x += self.primary_tick_length_x
473
+
474
+ def _calculate_grid_points_secondary(self):
475
+ if (
476
+ self.draw_grid_primary
477
+ and self.primary_start_x == 0
478
+ and self.primary_start_y == 0
479
+ and self.grid_secondary_scale_x == 1
480
+ and self.grid_secondary_scale_y == 1
481
+ ):
482
+ return # is it identical to the primary?
483
+ # We could be way too high
484
+ start_x = self.zero_x
485
+ while start_x - self.secondary_tick_length_x > self.min_x:
486
+ start_x -= self.secondary_tick_length_x
487
+ start_y = self.zero_y
488
+ while start_y - self.secondary_tick_length_y > self.min_y:
489
+ start_y -= self.secondary_tick_length_y
490
+ # But we could be way too low, too
491
+ while start_x < self.min_x:
492
+ start_x += self.secondary_tick_length_x
493
+ while start_y < self.min_y:
494
+ start_y += self.secondary_tick_length_y
495
+ x = start_x
496
+ while x <= self.max_x:
497
+ y = start_y
498
+ while y <= self.max_y:
499
+ # mx, my = self.scene.convert_scene_to_window([x, y])
500
+ self.grid_points.append([x, y])
501
+ y += self.secondary_tick_length_y
502
+ x += self.secondary_tick_length_x
503
+
504
+ def _calculate_grid_points_circular(self):
505
+ p = self.scene.context
506
+ # Okay, we are drawing on 48 segments line, even from center to outline, odd from 1/3rd to outline
507
+ start_x = self.circular_grid_center_x
508
+ start_y = self.circular_grid_center_y
509
+ x = start_x
510
+ y = start_y
511
+ # mx, my = self.scene.convert_scene_to_window([x, y])
512
+ self.grid_points.append([x, y])
513
+ max_r = abs(
514
+ complex(p.device.view.unit_width, p.device.view.unit_height)
515
+ ) # hypot
516
+ tick_length = (self.primary_tick_length_x + self.primary_tick_length_y) / 2
517
+ r_fourth = max_r // (4 * tick_length) * tick_length
518
+ segments = 48
519
+ r_angle = 0
520
+ i = 0
521
+ while r_angle < self.min_angle:
522
+ r_angle += tau / segments
523
+ i += 1
524
+ while r_angle < self.max_angle:
525
+ c_angle = r_angle
526
+ while c_angle > tau:
527
+ c_angle -= tau
528
+ if i % 2 == 0:
529
+ r = 0
530
+ else:
531
+ r = r_fourth
532
+ while r < self.min_radius:
533
+ r += tick_length
534
+
535
+ while r <= self.max_radius:
536
+ r += tick_length
537
+ x = start_x + r * cos(c_angle)
538
+ y = start_y + r * sin(c_angle)
539
+
540
+ if self.min_x <= x <= self.max_x and self.min_y <= y <= self.max_y:
541
+ # mx, my = self.scene.convert_scene_to_window([x, y])
542
+ self.grid_points.append([x, y])
543
+
544
+ i += 1
545
+ r_angle += tau / segments
546
+
547
+ ###########################
548
+ # WIDGET DRAW AND PROCESS
549
+ ###########################
550
+
551
+ def process_draw(self, gc):
552
+ """
553
+ Draw the grid on the scene.
554
+ """
555
+ # print ("GridWidget %s draw" % self.name)
556
+
557
+ # Get proper gridsize
558
+ w, h = gc.Size
559
+ if w < 50 or h < 50:
560
+ # Algorithm is unstable for very low values of w or h.
561
+ return
562
+ if self.auto_tick:
563
+ self.calculate_tickdistance(w, h)
564
+ self.calculate_center_start()
565
+ self.calculate_gridsize(w, h)
566
+ self.calculate_tick_length()
567
+ self.calculate_radii_angles()
568
+
569
+ # When do we need to redraw?!
570
+ if self.last_ticksize != self.tick_distance:
571
+ self.last_ticksize = self.tick_distance
572
+ self.primary_grid_lines = None
573
+ # With the new zoom-algorithm we also need to redraw if the origin
574
+ # or the size have changed...
575
+ # That's a price I am willing to pay...
576
+ if self.last_w != w or self.last_h != h:
577
+ self.last_w = w
578
+ self.last_h = h
579
+ self.primary_grid_lines = None
580
+ if self.min_x != self.last_min_x or self.min_y != self.last_min_y:
581
+ self.last_min_x = self.min_x
582
+ self.last_min_y = self.min_y
583
+ self.primary_grid_lines = None
584
+ if self.max_x != self.last_max_x or self.max_y != self.last_max_y:
585
+ self.last_max_x = self.max_x
586
+ self.last_max_y = self.max_y
587
+ self.primary_grid_lines = None
588
+
589
+ if self.scene.context.draw_mode & DRAW_MODE_GRID != 0:
590
+ return # Do not draw grid.
591
+
592
+ if self.primary_grid_lines is None or self.secondary_grid_lines is None:
593
+ self.calculate_grid_lines()
594
+ self.calculate_scene_grid_points()
595
+
596
+ self._set_pen_width_from_matrix()
597
+
598
+ gc.SetPen(self.primary_grid_line_pen)
599
+ brush = wx.Brush(
600
+ colour=self.scene.colors.color_bed, style=wx.BRUSHSTYLE_TRANSPARENT
601
+ )
602
+ gc.SetBrush(brush)
603
+ # There is a bug in wxPython v4.1.1 and below that will not allow to apply a LineWidth below a given level:
604
+ # At a matrix scale value of about 17.2 and a corresponding line width of 0.058 everything looks good
605
+ # but one step more with 18.9 and 0.053 the lines degenerate...
606
+ # Interestingly, this does not apply to arcs in a path, they remain at 1 pixel.
607
+ if self.draw_offset_lines:
608
+ self._draw_boundary(gc)
609
+ if self.draw_grid_circular:
610
+ self._draw_grid_circular(gc)
611
+ if self.draw_grid_secondary:
612
+ self._draw_grid_secondary(gc)
613
+ if self.draw_grid_primary:
614
+ self._draw_grid_primary(gc)
615
+
616
+ def _draw_boundary(self, gc):
617
+ gc.SetPen(self.offset_line_pen)
618
+ vw = self.scene.context.device.view
619
+ margin_x = -1 * float(Length(vw.margin_x))
620
+ margin_y = -1 * float(Length(vw.margin_y))
621
+ mx = vw.unit_width
622
+ my = vw.unit_height
623
+ ox = margin_x
624
+ oy = margin_y
625
+
626
+ grid_path = gc.CreatePath()
627
+ grid_path.MoveToPoint(ox, oy)
628
+ grid_path.AddLineToPoint(ox, my)
629
+ grid_path.AddLineToPoint(mx, my)
630
+ grid_path.AddLineToPoint(mx, oy)
631
+ grid_path.AddLineToPoint(ox, oy)
632
+ gc.StrokePath(grid_path)
633
+
634
+ def _draw_grid_primary(self, gc):
635
+ starts, ends = self.primary_grid_lines
636
+ gc.SetPen(self.primary_grid_line_pen)
637
+ grid_path = gc.CreatePath()
638
+ if starts and ends:
639
+ for i in range(len(starts)):
640
+ sx = starts[i][0]
641
+ sy = starts[i][1]
642
+ grid_path.MoveToPoint(sx, sy)
643
+ sx = ends[i][0]
644
+ sy = ends[i][1]
645
+ grid_path.AddLineToPoint(sx, sy)
646
+ gc.StrokePath(grid_path)
647
+
648
+ def _draw_grid_secondary(self, gc):
649
+ starts2, ends2 = self.secondary_grid_lines
650
+ gc.SetPen(self.secondary_grid_line_pen)
651
+ grid_path = gc.CreatePath()
652
+ if starts2 and ends2:
653
+ for i in range(len(starts2)):
654
+ sx = starts2[i][0]
655
+ sy = starts2[i][1]
656
+ grid_path.MoveToPoint(sx, sy)
657
+ sx = ends2[i][0]
658
+ sy = ends2[i][1]
659
+ grid_path.AddLineToPoint(sx, sy)
660
+ gc.StrokePath(grid_path)
661
+
662
+ # gc.StrokeLineSegments(starts2, ends2)
663
+
664
+ def _draw_grid_circular(self, gc):
665
+ gc.SetPen(self.circular_grid_line_pen)
666
+ u_width = float(self.scene.context.device.view.unit_width)
667
+ u_height = float(self.scene.context.device.view.unit_height)
668
+ gc.Clip(0, 0, u_width, u_height)
669
+ # siz = sqrt(u_width * u_width + u_height * u_height)
670
+ sox = self.circular_grid_center_x / u_width
671
+ soy = self.circular_grid_center_y / u_height
672
+ step = self.primary_tick_length_x
673
+ # factor = max(2 * (1 - sox), 2 * (1 - soy))
674
+ # Initially I drew a complete circle, which is a waste in most situations,
675
+ # so let's create a path
676
+ circle_path = gc.CreatePath()
677
+ y = 0
678
+ while y < 2 * self.min_radius:
679
+ y += 2 * step
680
+ while y < 2 * self.max_radius:
681
+ y += 2 * step
682
+ spoint_x = self.circular_grid_center_x + y / 2 * cos(self.min_angle)
683
+ spoint_y = self.circular_grid_center_x + y / 2 * sin(self.min_angle)
684
+ circle_path.MoveToPoint(spoint_x, spoint_y)
685
+ # gc.DrawEllipse(self.cx - y / 2, self.cy - y / 2, y, y)
686
+ circle_path.AddArc(
687
+ self.circular_grid_center_x,
688
+ self.circular_grid_center_y,
689
+ y / 2,
690
+ self.min_angle,
691
+ self.max_angle,
692
+ True,
693
+ )
694
+ gc.StrokePath(circle_path)
695
+ # circle_path.AddArc(self.cx, self.cy, y, self.min_angle, self.max_angle)
696
+ # (around one fourth of radius)
697
+ mid_y = y // (4 * step) * step
698
+ # print("Last Y=%.1f (%s), mid_y=%.1f (%s)" % (y, Length(amount=y).length_mm, mid_y, Length(amount=mid_y).length_mm))
699
+ radials_start = []
700
+ radials_end = []
701
+ fsize = 10 / self.scene.widget_root.scene_widget.matrix.value_scale_x()
702
+ if fsize < 1.0:
703
+ fsize = 1.0 # Mac does not allow values lower than 1.
704
+ try:
705
+ font = wx.Font(
706
+ fsize,
707
+ wx.FONTFAMILY_SWISS,
708
+ wx.FONTSTYLE_NORMAL,
709
+ wx.FONTWEIGHT_BOLD,
710
+ )
711
+ except TypeError:
712
+ font = wx.Font(
713
+ int(fsize),
714
+ wx.FONTFAMILY_SWISS,
715
+ wx.FONTSTYLE_NORMAL,
716
+ wx.FONTWEIGHT_BOLD,
717
+ )
718
+ # gc.SetFont(font, wx.BLACK)
719
+ # debugstr = "Angle= %.1f - %.1f (%d)" % (self.min_angle/tau*360, self.max_angle/tau*360, self.sector)
720
+ # gc.DrawText(debugstr, (self.min_x + self.max_x)/2, (self.min_y + self.max_y)/2)
721
+ gc.SetFont(font, self.scene.colors.color_guide3)
722
+ segments = 48
723
+ r_angle = 0
724
+ i = 0
725
+ while r_angle < self.min_angle:
726
+ r_angle += tau / segments
727
+ i += 1
728
+
729
+ # Draw radials...
730
+ while r_angle < self.max_angle:
731
+ c_angle = r_angle
732
+ while c_angle > tau:
733
+ c_angle -= tau
734
+ if i % 2 == 0:
735
+ degang = round(c_angle / tau * 360, 1)
736
+ if degang == 360:
737
+ degang = 0
738
+ a_text = f"{degang:.0f}°"
739
+ (t_width, t_height) = gc.GetTextExtent(a_text)
740
+ # Make sure text remains legible without breaking your neck... ;-)
741
+ if tau * 1 / 4 < c_angle < tau * 3 / 4:
742
+ myangle = (-1.0 * c_angle) + tau / 2
743
+ dx = t_width
744
+ else:
745
+ myangle = -1.0 * c_angle
746
+ dx = 0
747
+ if (
748
+ self.scene.context.draw_mode & DRAW_MODE_GUIDES == 0
749
+ or self.suppress_labels_in_all_cases
750
+ ):
751
+ gc.DrawText(
752
+ a_text,
753
+ self.circular_grid_center_x + cos(c_angle) * (mid_y + dx),
754
+ self.circular_grid_center_y + sin(c_angle) * (mid_y + dx),
755
+ myangle,
756
+ )
757
+ s_factor = 0
758
+ else:
759
+ s_factor = 1
760
+ radials_start.append(
761
+ (
762
+ self.circular_grid_center_x + s_factor * 0.5 * mid_y * cos(c_angle),
763
+ self.circular_grid_center_y + s_factor * 0.5 * mid_y * sin(c_angle),
764
+ )
765
+ )
766
+ radials_end.append(
767
+ (
768
+ self.circular_grid_center_x + 0.5 * y * cos(c_angle),
769
+ self.circular_grid_center_y + 0.5 * y * sin(c_angle),
770
+ )
771
+ )
772
+ r_angle += tau / segments
773
+ i += 1
774
+ if len(radials_start) > 0:
775
+ gc.StrokeLineSegments(radials_start, radials_end)
776
+ gc.ResetClip()
777
+
778
+ def signal(self, signal, *args, **kwargs):
779
+ """
780
+ Signal commands which draw the background and updates the grid when needed to recalculate the lines
781
+ """
782
+ if signal == "grid":
783
+ self.primary_grid_lines = None
784
+ elif signal == "theme":
785
+ self.set_colors()