meerk40t 0.9.3001__py2.py3-none-any.whl → 0.9.7010__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 (445) 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 +1195 -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 +1844 -1507
  50. meerk40t/core/elements/clipboard.py +229 -219
  51. meerk40t/core/elements/element_treeops.py +4561 -2837
  52. meerk40t/core/elements/element_types.py +125 -105
  53. meerk40t/core/elements/elements.py +4329 -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 +933 -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/trace.py +651 -563
  66. meerk40t/core/elements/tree_commands.py +415 -409
  67. meerk40t/core/elements/undo_redo.py +116 -58
  68. meerk40t/core/elements/wordlist.py +319 -200
  69. meerk40t/core/exceptions.py +9 -9
  70. meerk40t/core/laserjob.py +220 -220
  71. meerk40t/core/logging.py +63 -63
  72. meerk40t/core/node/blobnode.py +83 -86
  73. meerk40t/core/node/bootstrap.py +105 -103
  74. meerk40t/core/node/branch_elems.py +40 -31
  75. meerk40t/core/node/branch_ops.py +45 -38
  76. meerk40t/core/node/branch_regmark.py +48 -41
  77. meerk40t/core/node/cutnode.py +29 -32
  78. meerk40t/core/node/effect_hatch.py +375 -257
  79. meerk40t/core/node/effect_warp.py +398 -0
  80. meerk40t/core/node/effect_wobble.py +441 -309
  81. meerk40t/core/node/elem_ellipse.py +404 -309
  82. meerk40t/core/node/elem_image.py +1082 -801
  83. meerk40t/core/node/elem_line.py +358 -292
  84. meerk40t/core/node/elem_path.py +259 -201
  85. meerk40t/core/node/elem_point.py +129 -102
  86. meerk40t/core/node/elem_polyline.py +310 -246
  87. meerk40t/core/node/elem_rect.py +376 -286
  88. meerk40t/core/node/elem_text.py +445 -418
  89. meerk40t/core/node/filenode.py +59 -40
  90. meerk40t/core/node/groupnode.py +138 -74
  91. meerk40t/core/node/image_processed.py +777 -766
  92. meerk40t/core/node/image_raster.py +156 -113
  93. meerk40t/core/node/layernode.py +31 -31
  94. meerk40t/core/node/mixins.py +135 -107
  95. meerk40t/core/node/node.py +1427 -1304
  96. meerk40t/core/node/nutils.py +117 -114
  97. meerk40t/core/node/op_cut.py +462 -335
  98. meerk40t/core/node/op_dots.py +296 -251
  99. meerk40t/core/node/op_engrave.py +414 -311
  100. meerk40t/core/node/op_image.py +755 -369
  101. meerk40t/core/node/op_raster.py +787 -522
  102. meerk40t/core/node/place_current.py +37 -40
  103. meerk40t/core/node/place_point.py +329 -126
  104. meerk40t/core/node/refnode.py +58 -47
  105. meerk40t/core/node/rootnode.py +225 -219
  106. meerk40t/core/node/util_console.py +48 -48
  107. meerk40t/core/node/util_goto.py +84 -65
  108. meerk40t/core/node/util_home.py +61 -61
  109. meerk40t/core/node/util_input.py +102 -102
  110. meerk40t/core/node/util_output.py +102 -102
  111. meerk40t/core/node/util_wait.py +65 -65
  112. meerk40t/core/parameters.py +709 -707
  113. meerk40t/core/planner.py +875 -785
  114. meerk40t/core/plotplanner.py +656 -652
  115. meerk40t/core/space.py +120 -113
  116. meerk40t/core/spoolers.py +706 -705
  117. meerk40t/core/svg_io.py +1836 -1549
  118. meerk40t/core/treeop.py +534 -445
  119. meerk40t/core/undos.py +278 -124
  120. meerk40t/core/units.py +784 -680
  121. meerk40t/core/view.py +393 -322
  122. meerk40t/core/webhelp.py +62 -62
  123. meerk40t/core/wordlist.py +513 -504
  124. meerk40t/cylinder/cylinder.py +247 -0
  125. meerk40t/cylinder/gui/cylindersettings.py +41 -0
  126. meerk40t/cylinder/gui/gui.py +24 -0
  127. meerk40t/device/__init__.py +1 -1
  128. meerk40t/device/basedevice.py +322 -123
  129. meerk40t/device/devicechoices.py +50 -0
  130. meerk40t/device/dummydevice.py +163 -128
  131. meerk40t/device/gui/defaultactions.py +618 -602
  132. meerk40t/device/gui/effectspanel.py +114 -0
  133. meerk40t/device/gui/formatterpanel.py +253 -290
  134. meerk40t/device/gui/warningpanel.py +337 -260
  135. meerk40t/device/mixins.py +13 -13
  136. meerk40t/dxf/__init__.py +1 -1
  137. meerk40t/dxf/dxf_io.py +766 -554
  138. meerk40t/dxf/plugin.py +47 -35
  139. meerk40t/external_plugins.py +79 -79
  140. meerk40t/external_plugins_build.py +28 -28
  141. meerk40t/extra/cag.py +112 -116
  142. meerk40t/extra/coolant.py +403 -0
  143. meerk40t/extra/encode_detect.py +198 -0
  144. meerk40t/extra/ezd.py +1165 -1165
  145. meerk40t/extra/hershey.py +835 -340
  146. meerk40t/extra/imageactions.py +322 -316
  147. meerk40t/extra/inkscape.py +630 -622
  148. meerk40t/extra/lbrn.py +424 -424
  149. meerk40t/extra/outerworld.py +284 -0
  150. meerk40t/extra/param_functions.py +1542 -1556
  151. meerk40t/extra/potrace.py +257 -253
  152. meerk40t/extra/serial_exchange.py +118 -0
  153. meerk40t/extra/updater.py +602 -453
  154. meerk40t/extra/vectrace.py +147 -146
  155. meerk40t/extra/winsleep.py +83 -83
  156. meerk40t/extra/xcs_reader.py +597 -0
  157. meerk40t/fill/fills.py +781 -335
  158. meerk40t/fill/patternfill.py +1061 -1061
  159. meerk40t/fill/patterns.py +614 -567
  160. meerk40t/grbl/control.py +87 -87
  161. meerk40t/grbl/controller.py +990 -903
  162. meerk40t/grbl/device.py +1081 -768
  163. meerk40t/grbl/driver.py +989 -771
  164. meerk40t/grbl/emulator.py +532 -497
  165. meerk40t/grbl/gcodejob.py +783 -767
  166. meerk40t/grbl/gui/grblconfiguration.py +373 -298
  167. meerk40t/grbl/gui/grblcontroller.py +485 -271
  168. meerk40t/grbl/gui/grblhardwareconfig.py +269 -153
  169. meerk40t/grbl/gui/grbloperationconfig.py +105 -0
  170. meerk40t/grbl/gui/gui.py +147 -116
  171. meerk40t/grbl/interpreter.py +44 -44
  172. meerk40t/grbl/loader.py +22 -22
  173. meerk40t/grbl/mock_connection.py +56 -56
  174. meerk40t/grbl/plugin.py +294 -264
  175. meerk40t/grbl/serial_connection.py +93 -88
  176. meerk40t/grbl/tcp_connection.py +81 -79
  177. meerk40t/grbl/ws_connection.py +112 -0
  178. meerk40t/gui/__init__.py +1 -1
  179. meerk40t/gui/about.py +2042 -296
  180. meerk40t/gui/alignment.py +1644 -1608
  181. meerk40t/gui/autoexec.py +199 -0
  182. meerk40t/gui/basicops.py +791 -670
  183. meerk40t/gui/bufferview.py +77 -71
  184. meerk40t/gui/busy.py +170 -133
  185. meerk40t/gui/choicepropertypanel.py +1673 -1469
  186. meerk40t/gui/consolepanel.py +706 -542
  187. meerk40t/gui/devicepanel.py +687 -581
  188. meerk40t/gui/dialogoptions.py +110 -107
  189. meerk40t/gui/executejob.py +316 -306
  190. meerk40t/gui/fonts.py +90 -90
  191. meerk40t/gui/functionwrapper.py +252 -0
  192. meerk40t/gui/gui_mixins.py +729 -0
  193. meerk40t/gui/guicolors.py +205 -182
  194. meerk40t/gui/help_assets/help_assets.py +218 -201
  195. meerk40t/gui/helper.py +154 -0
  196. meerk40t/gui/hersheymanager.py +1430 -846
  197. meerk40t/gui/icons.py +3422 -2747
  198. meerk40t/gui/imagesplitter.py +555 -508
  199. meerk40t/gui/keymap.py +354 -344
  200. meerk40t/gui/laserpanel.py +892 -806
  201. meerk40t/gui/laserrender.py +1470 -1232
  202. meerk40t/gui/lasertoolpanel.py +805 -793
  203. meerk40t/gui/magnetoptions.py +436 -0
  204. meerk40t/gui/materialmanager.py +2917 -0
  205. meerk40t/gui/materialtest.py +1722 -1694
  206. meerk40t/gui/mkdebug.py +646 -359
  207. meerk40t/gui/mwindow.py +163 -140
  208. meerk40t/gui/navigationpanels.py +2605 -2467
  209. meerk40t/gui/notes.py +143 -142
  210. meerk40t/gui/opassignment.py +414 -410
  211. meerk40t/gui/operation_info.py +310 -299
  212. meerk40t/gui/plugin.py +494 -328
  213. meerk40t/gui/position.py +714 -669
  214. meerk40t/gui/preferences.py +901 -650
  215. meerk40t/gui/propertypanels/attributes.py +1461 -1131
  216. meerk40t/gui/propertypanels/blobproperty.py +117 -114
  217. meerk40t/gui/propertypanels/consoleproperty.py +83 -80
  218. meerk40t/gui/propertypanels/gotoproperty.py +77 -0
  219. meerk40t/gui/propertypanels/groupproperties.py +223 -217
  220. meerk40t/gui/propertypanels/hatchproperty.py +489 -469
  221. meerk40t/gui/propertypanels/imageproperty.py +2244 -1384
  222. meerk40t/gui/propertypanels/inputproperty.py +59 -58
  223. meerk40t/gui/propertypanels/opbranchproperties.py +82 -80
  224. meerk40t/gui/propertypanels/operationpropertymain.py +1890 -1638
  225. meerk40t/gui/propertypanels/outputproperty.py +59 -58
  226. meerk40t/gui/propertypanels/pathproperty.py +389 -380
  227. meerk40t/gui/propertypanels/placementproperty.py +1214 -383
  228. meerk40t/gui/propertypanels/pointproperty.py +140 -136
  229. meerk40t/gui/propertypanels/propertywindow.py +313 -181
  230. meerk40t/gui/propertypanels/rasterwizardpanels.py +996 -912
  231. meerk40t/gui/propertypanels/regbranchproperties.py +76 -0
  232. meerk40t/gui/propertypanels/textproperty.py +770 -755
  233. meerk40t/gui/propertypanels/waitproperty.py +56 -55
  234. meerk40t/gui/propertypanels/warpproperty.py +121 -0
  235. meerk40t/gui/propertypanels/wobbleproperty.py +255 -204
  236. meerk40t/gui/ribbon.py +2468 -2210
  237. meerk40t/gui/scene/scene.py +1100 -1051
  238. meerk40t/gui/scene/sceneconst.py +22 -22
  239. meerk40t/gui/scene/scenepanel.py +439 -349
  240. meerk40t/gui/scene/scenespacewidget.py +365 -365
  241. meerk40t/gui/scene/widget.py +518 -505
  242. meerk40t/gui/scenewidgets/affinemover.py +215 -215
  243. meerk40t/gui/scenewidgets/attractionwidget.py +315 -309
  244. meerk40t/gui/scenewidgets/bedwidget.py +120 -97
  245. meerk40t/gui/scenewidgets/elementswidget.py +137 -107
  246. meerk40t/gui/scenewidgets/gridwidget.py +785 -745
  247. meerk40t/gui/scenewidgets/guidewidget.py +765 -765
  248. meerk40t/gui/scenewidgets/laserpathwidget.py +66 -66
  249. meerk40t/gui/scenewidgets/machineoriginwidget.py +86 -86
  250. meerk40t/gui/scenewidgets/nodeselector.py +28 -28
  251. meerk40t/gui/scenewidgets/rectselectwidget.py +589 -346
  252. meerk40t/gui/scenewidgets/relocatewidget.py +33 -33
  253. meerk40t/gui/scenewidgets/reticlewidget.py +83 -83
  254. meerk40t/gui/scenewidgets/selectionwidget.py +2952 -2756
  255. meerk40t/gui/simpleui.py +357 -333
  256. meerk40t/gui/simulation.py +2431 -2094
  257. meerk40t/gui/snapoptions.py +208 -203
  258. meerk40t/gui/spoolerpanel.py +1227 -1180
  259. meerk40t/gui/statusbarwidgets/defaultoperations.py +480 -353
  260. meerk40t/gui/statusbarwidgets/infowidget.py +520 -483
  261. meerk40t/gui/statusbarwidgets/opassignwidget.py +356 -355
  262. meerk40t/gui/statusbarwidgets/selectionwidget.py +172 -171
  263. meerk40t/gui/statusbarwidgets/shapepropwidget.py +754 -236
  264. meerk40t/gui/statusbarwidgets/statusbar.py +272 -260
  265. meerk40t/gui/statusbarwidgets/statusbarwidget.py +268 -270
  266. meerk40t/gui/statusbarwidgets/strokewidget.py +267 -251
  267. meerk40t/gui/themes.py +200 -78
  268. meerk40t/gui/tips.py +591 -0
  269. meerk40t/gui/toolwidgets/circlebrush.py +35 -35
  270. meerk40t/gui/toolwidgets/toolcircle.py +248 -242
  271. meerk40t/gui/toolwidgets/toolcontainer.py +82 -77
  272. meerk40t/gui/toolwidgets/tooldraw.py +97 -90
  273. meerk40t/gui/toolwidgets/toolellipse.py +219 -212
  274. meerk40t/gui/toolwidgets/toolimagecut.py +25 -132
  275. meerk40t/gui/toolwidgets/toolline.py +39 -144
  276. meerk40t/gui/toolwidgets/toollinetext.py +79 -236
  277. meerk40t/gui/toolwidgets/toollinetext_inline.py +296 -0
  278. meerk40t/gui/toolwidgets/toolmeasure.py +160 -216
  279. meerk40t/gui/toolwidgets/toolnodeedit.py +2088 -2074
  280. meerk40t/gui/toolwidgets/toolnodemove.py +92 -94
  281. meerk40t/gui/toolwidgets/toolparameter.py +754 -668
  282. meerk40t/gui/toolwidgets/toolplacement.py +108 -108
  283. meerk40t/gui/toolwidgets/toolpoint.py +68 -59
  284. meerk40t/gui/toolwidgets/toolpointlistbuilder.py +294 -0
  285. meerk40t/gui/toolwidgets/toolpointmove.py +183 -0
  286. meerk40t/gui/toolwidgets/toolpolygon.py +288 -403
  287. meerk40t/gui/toolwidgets/toolpolyline.py +38 -196
  288. meerk40t/gui/toolwidgets/toolrect.py +211 -207
  289. meerk40t/gui/toolwidgets/toolrelocate.py +72 -72
  290. meerk40t/gui/toolwidgets/toolribbon.py +598 -113
  291. meerk40t/gui/toolwidgets/tooltabedit.py +546 -0
  292. meerk40t/gui/toolwidgets/tooltext.py +98 -89
  293. meerk40t/gui/toolwidgets/toolvector.py +213 -204
  294. meerk40t/gui/toolwidgets/toolwidget.py +39 -39
  295. meerk40t/gui/usbconnect.py +98 -91
  296. meerk40t/gui/utilitywidgets/buttonwidget.py +18 -18
  297. meerk40t/gui/utilitywidgets/checkboxwidget.py +90 -90
  298. meerk40t/gui/utilitywidgets/controlwidget.py +14 -14
  299. meerk40t/gui/utilitywidgets/cyclocycloidwidget.py +343 -340
  300. meerk40t/gui/utilitywidgets/debugwidgets.py +148 -0
  301. meerk40t/gui/utilitywidgets/handlewidget.py +27 -27
  302. meerk40t/gui/utilitywidgets/harmonograph.py +450 -447
  303. meerk40t/gui/utilitywidgets/openclosewidget.py +40 -40
  304. meerk40t/gui/utilitywidgets/rotationwidget.py +54 -54
  305. meerk40t/gui/utilitywidgets/scalewidget.py +75 -75
  306. meerk40t/gui/utilitywidgets/seekbarwidget.py +183 -183
  307. meerk40t/gui/utilitywidgets/togglewidget.py +142 -142
  308. meerk40t/gui/utilitywidgets/toolbarwidget.py +8 -8
  309. meerk40t/gui/wordlisteditor.py +985 -931
  310. meerk40t/gui/wxmeerk40t.py +1444 -1169
  311. meerk40t/gui/wxmmain.py +5578 -4112
  312. meerk40t/gui/wxmribbon.py +1591 -1076
  313. meerk40t/gui/wxmscene.py +1635 -1453
  314. meerk40t/gui/wxmtree.py +2410 -2089
  315. meerk40t/gui/wxutils.py +1769 -1099
  316. meerk40t/gui/zmatrix.py +102 -102
  317. meerk40t/image/__init__.py +1 -1
  318. meerk40t/image/dither.py +429 -0
  319. meerk40t/image/imagetools.py +2778 -2269
  320. meerk40t/internal_plugins.py +150 -130
  321. meerk40t/kernel/__init__.py +63 -12
  322. meerk40t/kernel/channel.py +259 -212
  323. meerk40t/kernel/context.py +538 -538
  324. meerk40t/kernel/exceptions.py +41 -41
  325. meerk40t/kernel/functions.py +463 -414
  326. meerk40t/kernel/jobs.py +100 -100
  327. meerk40t/kernel/kernel.py +3809 -3571
  328. meerk40t/kernel/lifecycles.py +71 -71
  329. meerk40t/kernel/module.py +49 -49
  330. meerk40t/kernel/service.py +147 -147
  331. meerk40t/kernel/settings.py +383 -343
  332. meerk40t/lihuiyu/controller.py +883 -876
  333. meerk40t/lihuiyu/device.py +1181 -1069
  334. meerk40t/lihuiyu/driver.py +1466 -1372
  335. meerk40t/lihuiyu/gui/gui.py +127 -106
  336. meerk40t/lihuiyu/gui/lhyaccelgui.py +377 -363
  337. meerk40t/lihuiyu/gui/lhycontrollergui.py +741 -651
  338. meerk40t/lihuiyu/gui/lhydrivergui.py +470 -446
  339. meerk40t/lihuiyu/gui/lhyoperationproperties.py +238 -237
  340. meerk40t/lihuiyu/gui/tcpcontroller.py +226 -190
  341. meerk40t/lihuiyu/interpreter.py +53 -53
  342. meerk40t/lihuiyu/laserspeed.py +450 -450
  343. meerk40t/lihuiyu/loader.py +90 -90
  344. meerk40t/lihuiyu/parser.py +404 -404
  345. meerk40t/lihuiyu/plugin.py +101 -102
  346. meerk40t/lihuiyu/tcp_connection.py +111 -109
  347. meerk40t/main.py +231 -165
  348. meerk40t/moshi/builder.py +788 -781
  349. meerk40t/moshi/controller.py +505 -499
  350. meerk40t/moshi/device.py +495 -442
  351. meerk40t/moshi/driver.py +862 -696
  352. meerk40t/moshi/gui/gui.py +78 -76
  353. meerk40t/moshi/gui/moshicontrollergui.py +538 -522
  354. meerk40t/moshi/gui/moshidrivergui.py +87 -75
  355. meerk40t/moshi/plugin.py +43 -43
  356. meerk40t/network/console_server.py +102 -57
  357. meerk40t/network/kernelserver.py +10 -9
  358. meerk40t/network/tcp_server.py +142 -140
  359. meerk40t/network/udp_server.py +103 -77
  360. meerk40t/network/web_server.py +390 -0
  361. meerk40t/newly/controller.py +1158 -1144
  362. meerk40t/newly/device.py +874 -732
  363. meerk40t/newly/driver.py +540 -412
  364. meerk40t/newly/gui/gui.py +219 -188
  365. meerk40t/newly/gui/newlyconfig.py +116 -101
  366. meerk40t/newly/gui/newlycontroller.py +193 -186
  367. meerk40t/newly/gui/operationproperties.py +51 -51
  368. meerk40t/newly/mock_connection.py +82 -82
  369. meerk40t/newly/newly_params.py +56 -56
  370. meerk40t/newly/plugin.py +1214 -1246
  371. meerk40t/newly/usb_connection.py +322 -322
  372. meerk40t/rotary/gui/gui.py +52 -46
  373. meerk40t/rotary/gui/rotarysettings.py +240 -232
  374. meerk40t/rotary/rotary.py +202 -98
  375. meerk40t/ruida/control.py +291 -91
  376. meerk40t/ruida/controller.py +138 -1088
  377. meerk40t/ruida/device.py +672 -231
  378. meerk40t/ruida/driver.py +534 -472
  379. meerk40t/ruida/emulator.py +1494 -1491
  380. meerk40t/ruida/exceptions.py +4 -4
  381. meerk40t/ruida/gui/gui.py +71 -76
  382. meerk40t/ruida/gui/ruidaconfig.py +239 -72
  383. meerk40t/ruida/gui/ruidacontroller.py +187 -184
  384. meerk40t/ruida/gui/ruidaoperationproperties.py +48 -47
  385. meerk40t/ruida/loader.py +54 -52
  386. meerk40t/ruida/mock_connection.py +57 -109
  387. meerk40t/ruida/plugin.py +124 -87
  388. meerk40t/ruida/rdjob.py +2084 -945
  389. meerk40t/ruida/serial_connection.py +116 -0
  390. meerk40t/ruida/tcp_connection.py +146 -0
  391. meerk40t/ruida/udp_connection.py +73 -0
  392. meerk40t/svgelements.py +9671 -9669
  393. meerk40t/tools/driver_to_path.py +584 -579
  394. meerk40t/tools/geomstr.py +5583 -4680
  395. meerk40t/tools/jhfparser.py +357 -292
  396. meerk40t/tools/kerftest.py +904 -890
  397. meerk40t/tools/livinghinges.py +1168 -1033
  398. meerk40t/tools/pathtools.py +987 -949
  399. meerk40t/tools/pmatrix.py +234 -0
  400. meerk40t/tools/pointfinder.py +942 -942
  401. meerk40t/tools/polybool.py +940 -940
  402. meerk40t/tools/rasterplotter.py +1660 -547
  403. meerk40t/tools/shxparser.py +989 -901
  404. meerk40t/tools/ttfparser.py +726 -446
  405. meerk40t/tools/zinglplotter.py +595 -593
  406. {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.dist-info}/LICENSE +21 -21
  407. {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.dist-info}/METADATA +150 -139
  408. meerk40t-0.9.7010.dist-info/RECORD +445 -0
  409. {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.dist-info}/WHEEL +1 -1
  410. {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.dist-info}/top_level.txt +0 -1
  411. {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.dist-info}/zip-safe +1 -1
  412. meerk40t/balormk/elementlightjob.py +0 -159
  413. meerk40t-0.9.3001.dist-info/RECORD +0 -437
  414. test/bootstrap.py +0 -63
  415. test/test_cli.py +0 -12
  416. test/test_core_cutcode.py +0 -418
  417. test/test_core_elements.py +0 -144
  418. test/test_core_plotplanner.py +0 -397
  419. test/test_core_viewports.py +0 -312
  420. test/test_drivers_grbl.py +0 -108
  421. test/test_drivers_lihuiyu.py +0 -443
  422. test/test_drivers_newly.py +0 -113
  423. test/test_element_degenerate_points.py +0 -43
  424. test/test_elements_classify.py +0 -97
  425. test/test_elements_penbox.py +0 -22
  426. test/test_file_svg.py +0 -176
  427. test/test_fill.py +0 -155
  428. test/test_geomstr.py +0 -1523
  429. test/test_geomstr_nodes.py +0 -18
  430. test/test_imagetools_actualize.py +0 -306
  431. test/test_imagetools_wizard.py +0 -258
  432. test/test_kernel.py +0 -200
  433. test/test_laser_speeds.py +0 -3303
  434. test/test_length.py +0 -57
  435. test/test_lifecycle.py +0 -66
  436. test/test_operations.py +0 -251
  437. test/test_operations_hatch.py +0 -57
  438. test/test_ruida.py +0 -19
  439. test/test_spooler.py +0 -22
  440. test/test_tools_rasterplotter.py +0 -29
  441. test/test_wobble.py +0 -133
  442. test/test_zingl.py +0 -124
  443. {test → meerk40t/cylinder}/__init__.py +0 -0
  444. /meerk40t/{core/element_commands.py → cylinder/gui/__init__.py} +0 -0
  445. {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.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()