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.
- meerk40t/__init__.py +1 -1
- meerk40t/balormk/balor_params.py +167 -167
- meerk40t/balormk/clone_loader.py +457 -457
- meerk40t/balormk/controller.py +1566 -1512
- meerk40t/balormk/cylindermod.py +64 -0
- meerk40t/balormk/device.py +966 -1959
- meerk40t/balormk/driver.py +778 -591
- meerk40t/balormk/galvo_commands.py +1194 -0
- meerk40t/balormk/gui/balorconfig.py +237 -111
- meerk40t/balormk/gui/balorcontroller.py +191 -184
- meerk40t/balormk/gui/baloroperationproperties.py +116 -115
- meerk40t/balormk/gui/corscene.py +845 -0
- meerk40t/balormk/gui/gui.py +179 -147
- meerk40t/balormk/livelightjob.py +466 -382
- meerk40t/balormk/mock_connection.py +131 -109
- meerk40t/balormk/plugin.py +133 -135
- meerk40t/balormk/usb_connection.py +306 -301
- meerk40t/camera/__init__.py +1 -1
- meerk40t/camera/camera.py +514 -397
- meerk40t/camera/gui/camerapanel.py +1241 -1095
- meerk40t/camera/gui/gui.py +58 -58
- meerk40t/camera/plugin.py +441 -399
- meerk40t/ch341/__init__.py +27 -27
- meerk40t/ch341/ch341device.py +628 -628
- meerk40t/ch341/libusb.py +595 -589
- meerk40t/ch341/mock.py +171 -171
- meerk40t/ch341/windriver.py +157 -157
- meerk40t/constants.py +13 -0
- meerk40t/core/__init__.py +1 -1
- meerk40t/core/bindalias.py +550 -539
- meerk40t/core/core.py +47 -47
- meerk40t/core/cutcode/cubiccut.py +73 -73
- meerk40t/core/cutcode/cutcode.py +315 -312
- meerk40t/core/cutcode/cutgroup.py +141 -137
- meerk40t/core/cutcode/cutobject.py +192 -185
- meerk40t/core/cutcode/dwellcut.py +37 -37
- meerk40t/core/cutcode/gotocut.py +29 -29
- meerk40t/core/cutcode/homecut.py +29 -29
- meerk40t/core/cutcode/inputcut.py +34 -34
- meerk40t/core/cutcode/linecut.py +33 -33
- meerk40t/core/cutcode/outputcut.py +34 -34
- meerk40t/core/cutcode/plotcut.py +335 -335
- meerk40t/core/cutcode/quadcut.py +61 -61
- meerk40t/core/cutcode/rastercut.py +168 -148
- meerk40t/core/cutcode/waitcut.py +34 -34
- meerk40t/core/cutplan.py +1843 -1316
- meerk40t/core/drivers.py +330 -329
- meerk40t/core/elements/align.py +801 -669
- meerk40t/core/elements/branches.py +1858 -1507
- meerk40t/core/elements/clipboard.py +229 -219
- meerk40t/core/elements/element_treeops.py +4595 -2837
- meerk40t/core/elements/element_types.py +125 -105
- meerk40t/core/elements/elements.py +4315 -3617
- meerk40t/core/elements/files.py +117 -64
- meerk40t/core/elements/geometry.py +473 -224
- meerk40t/core/elements/grid.py +467 -316
- meerk40t/core/elements/materials.py +158 -94
- meerk40t/core/elements/notes.py +50 -38
- meerk40t/core/elements/offset_clpr.py +934 -912
- meerk40t/core/elements/offset_mk.py +963 -955
- meerk40t/core/elements/penbox.py +339 -267
- meerk40t/core/elements/placements.py +300 -83
- meerk40t/core/elements/render.py +785 -687
- meerk40t/core/elements/shapes.py +2618 -2092
- meerk40t/core/elements/testcases.py +105 -0
- meerk40t/core/elements/trace.py +651 -563
- meerk40t/core/elements/tree_commands.py +415 -409
- meerk40t/core/elements/undo_redo.py +116 -58
- meerk40t/core/elements/wordlist.py +319 -200
- meerk40t/core/exceptions.py +9 -9
- meerk40t/core/laserjob.py +220 -220
- meerk40t/core/logging.py +63 -63
- meerk40t/core/node/blobnode.py +83 -86
- meerk40t/core/node/bootstrap.py +105 -103
- meerk40t/core/node/branch_elems.py +40 -31
- meerk40t/core/node/branch_ops.py +45 -38
- meerk40t/core/node/branch_regmark.py +48 -41
- meerk40t/core/node/cutnode.py +29 -32
- meerk40t/core/node/effect_hatch.py +375 -257
- meerk40t/core/node/effect_warp.py +398 -0
- meerk40t/core/node/effect_wobble.py +441 -309
- meerk40t/core/node/elem_ellipse.py +404 -309
- meerk40t/core/node/elem_image.py +1082 -801
- meerk40t/core/node/elem_line.py +358 -292
- meerk40t/core/node/elem_path.py +259 -201
- meerk40t/core/node/elem_point.py +129 -102
- meerk40t/core/node/elem_polyline.py +310 -246
- meerk40t/core/node/elem_rect.py +376 -286
- meerk40t/core/node/elem_text.py +445 -418
- meerk40t/core/node/filenode.py +59 -40
- meerk40t/core/node/groupnode.py +138 -74
- meerk40t/core/node/image_processed.py +777 -766
- meerk40t/core/node/image_raster.py +156 -113
- meerk40t/core/node/layernode.py +31 -31
- meerk40t/core/node/mixins.py +135 -107
- meerk40t/core/node/node.py +1427 -1304
- meerk40t/core/node/nutils.py +117 -114
- meerk40t/core/node/op_cut.py +463 -335
- meerk40t/core/node/op_dots.py +296 -251
- meerk40t/core/node/op_engrave.py +414 -311
- meerk40t/core/node/op_image.py +755 -369
- meerk40t/core/node/op_raster.py +787 -522
- meerk40t/core/node/place_current.py +37 -40
- meerk40t/core/node/place_point.py +329 -126
- meerk40t/core/node/refnode.py +58 -47
- meerk40t/core/node/rootnode.py +225 -219
- meerk40t/core/node/util_console.py +48 -48
- meerk40t/core/node/util_goto.py +84 -65
- meerk40t/core/node/util_home.py +61 -61
- meerk40t/core/node/util_input.py +102 -102
- meerk40t/core/node/util_output.py +102 -102
- meerk40t/core/node/util_wait.py +65 -65
- meerk40t/core/parameters.py +709 -707
- meerk40t/core/planner.py +875 -785
- meerk40t/core/plotplanner.py +656 -652
- meerk40t/core/space.py +120 -113
- meerk40t/core/spoolers.py +706 -705
- meerk40t/core/svg_io.py +1836 -1549
- meerk40t/core/treeop.py +534 -445
- meerk40t/core/undos.py +278 -124
- meerk40t/core/units.py +784 -680
- meerk40t/core/view.py +393 -322
- meerk40t/core/webhelp.py +62 -62
- meerk40t/core/wordlist.py +513 -504
- meerk40t/cylinder/cylinder.py +247 -0
- meerk40t/cylinder/gui/cylindersettings.py +41 -0
- meerk40t/cylinder/gui/gui.py +24 -0
- meerk40t/device/__init__.py +1 -1
- meerk40t/device/basedevice.py +322 -123
- meerk40t/device/devicechoices.py +50 -0
- meerk40t/device/dummydevice.py +163 -128
- meerk40t/device/gui/defaultactions.py +618 -602
- meerk40t/device/gui/effectspanel.py +114 -0
- meerk40t/device/gui/formatterpanel.py +253 -290
- meerk40t/device/gui/warningpanel.py +337 -260
- meerk40t/device/mixins.py +13 -13
- meerk40t/dxf/__init__.py +1 -1
- meerk40t/dxf/dxf_io.py +766 -554
- meerk40t/dxf/plugin.py +47 -35
- meerk40t/external_plugins.py +79 -79
- meerk40t/external_plugins_build.py +28 -28
- meerk40t/extra/cag.py +112 -116
- meerk40t/extra/coolant.py +403 -0
- meerk40t/extra/encode_detect.py +204 -0
- meerk40t/extra/ezd.py +1165 -1165
- meerk40t/extra/hershey.py +834 -340
- meerk40t/extra/imageactions.py +322 -316
- meerk40t/extra/inkscape.py +628 -622
- meerk40t/extra/lbrn.py +424 -424
- meerk40t/extra/outerworld.py +283 -0
- meerk40t/extra/param_functions.py +1542 -1556
- meerk40t/extra/potrace.py +257 -253
- meerk40t/extra/serial_exchange.py +118 -0
- meerk40t/extra/updater.py +602 -453
- meerk40t/extra/vectrace.py +147 -146
- meerk40t/extra/winsleep.py +83 -83
- meerk40t/extra/xcs_reader.py +597 -0
- meerk40t/fill/fills.py +781 -335
- meerk40t/fill/patternfill.py +1061 -1061
- meerk40t/fill/patterns.py +614 -567
- meerk40t/grbl/control.py +87 -87
- meerk40t/grbl/controller.py +990 -903
- meerk40t/grbl/device.py +1084 -768
- meerk40t/grbl/driver.py +989 -771
- meerk40t/grbl/emulator.py +532 -497
- meerk40t/grbl/gcodejob.py +783 -767
- meerk40t/grbl/gui/grblconfiguration.py +373 -298
- meerk40t/grbl/gui/grblcontroller.py +485 -271
- meerk40t/grbl/gui/grblhardwareconfig.py +269 -153
- meerk40t/grbl/gui/grbloperationconfig.py +105 -0
- meerk40t/grbl/gui/gui.py +147 -116
- meerk40t/grbl/interpreter.py +44 -44
- meerk40t/grbl/loader.py +22 -22
- meerk40t/grbl/mock_connection.py +56 -56
- meerk40t/grbl/plugin.py +294 -264
- meerk40t/grbl/serial_connection.py +93 -88
- meerk40t/grbl/tcp_connection.py +81 -79
- meerk40t/grbl/ws_connection.py +112 -0
- meerk40t/gui/__init__.py +1 -1
- meerk40t/gui/about.py +2042 -296
- meerk40t/gui/alignment.py +1644 -1608
- meerk40t/gui/autoexec.py +199 -0
- meerk40t/gui/basicops.py +791 -670
- meerk40t/gui/bufferview.py +77 -71
- meerk40t/gui/busy.py +232 -133
- meerk40t/gui/choicepropertypanel.py +1662 -1469
- meerk40t/gui/consolepanel.py +706 -542
- meerk40t/gui/devicepanel.py +687 -581
- meerk40t/gui/dialogoptions.py +110 -107
- meerk40t/gui/executejob.py +316 -306
- meerk40t/gui/fonts.py +90 -90
- meerk40t/gui/functionwrapper.py +252 -0
- meerk40t/gui/gui_mixins.py +729 -0
- meerk40t/gui/guicolors.py +205 -182
- meerk40t/gui/help_assets/help_assets.py +218 -201
- meerk40t/gui/helper.py +154 -0
- meerk40t/gui/hersheymanager.py +1440 -846
- meerk40t/gui/icons.py +3422 -2747
- meerk40t/gui/imagesplitter.py +555 -508
- meerk40t/gui/keymap.py +354 -344
- meerk40t/gui/laserpanel.py +897 -806
- meerk40t/gui/laserrender.py +1470 -1232
- meerk40t/gui/lasertoolpanel.py +805 -793
- meerk40t/gui/magnetoptions.py +436 -0
- meerk40t/gui/materialmanager.py +2944 -0
- meerk40t/gui/materialtest.py +1722 -1694
- meerk40t/gui/mkdebug.py +646 -359
- meerk40t/gui/mwindow.py +163 -140
- meerk40t/gui/navigationpanels.py +2605 -2467
- meerk40t/gui/notes.py +143 -142
- meerk40t/gui/opassignment.py +414 -410
- meerk40t/gui/operation_info.py +310 -299
- meerk40t/gui/plugin.py +500 -328
- meerk40t/gui/position.py +714 -669
- meerk40t/gui/preferences.py +901 -650
- meerk40t/gui/propertypanels/attributes.py +1461 -1131
- meerk40t/gui/propertypanels/blobproperty.py +117 -114
- meerk40t/gui/propertypanels/consoleproperty.py +83 -80
- meerk40t/gui/propertypanels/gotoproperty.py +77 -0
- meerk40t/gui/propertypanels/groupproperties.py +223 -217
- meerk40t/gui/propertypanels/hatchproperty.py +489 -469
- meerk40t/gui/propertypanels/imageproperty.py +2244 -1384
- meerk40t/gui/propertypanels/inputproperty.py +59 -58
- meerk40t/gui/propertypanels/opbranchproperties.py +82 -80
- meerk40t/gui/propertypanels/operationpropertymain.py +1890 -1638
- meerk40t/gui/propertypanels/outputproperty.py +59 -58
- meerk40t/gui/propertypanels/pathproperty.py +389 -380
- meerk40t/gui/propertypanels/placementproperty.py +1214 -383
- meerk40t/gui/propertypanels/pointproperty.py +140 -136
- meerk40t/gui/propertypanels/propertywindow.py +313 -181
- meerk40t/gui/propertypanels/rasterwizardpanels.py +996 -912
- meerk40t/gui/propertypanels/regbranchproperties.py +76 -0
- meerk40t/gui/propertypanels/textproperty.py +770 -755
- meerk40t/gui/propertypanels/waitproperty.py +56 -55
- meerk40t/gui/propertypanels/warpproperty.py +121 -0
- meerk40t/gui/propertypanels/wobbleproperty.py +255 -204
- meerk40t/gui/ribbon.py +2471 -2210
- meerk40t/gui/scene/scene.py +1100 -1051
- meerk40t/gui/scene/sceneconst.py +22 -22
- meerk40t/gui/scene/scenepanel.py +439 -349
- meerk40t/gui/scene/scenespacewidget.py +365 -365
- meerk40t/gui/scene/widget.py +518 -505
- meerk40t/gui/scenewidgets/affinemover.py +215 -215
- meerk40t/gui/scenewidgets/attractionwidget.py +315 -309
- meerk40t/gui/scenewidgets/bedwidget.py +120 -97
- meerk40t/gui/scenewidgets/elementswidget.py +137 -107
- meerk40t/gui/scenewidgets/gridwidget.py +785 -745
- meerk40t/gui/scenewidgets/guidewidget.py +765 -765
- meerk40t/gui/scenewidgets/laserpathwidget.py +66 -66
- meerk40t/gui/scenewidgets/machineoriginwidget.py +86 -86
- meerk40t/gui/scenewidgets/nodeselector.py +28 -28
- meerk40t/gui/scenewidgets/rectselectwidget.py +592 -346
- meerk40t/gui/scenewidgets/relocatewidget.py +33 -33
- meerk40t/gui/scenewidgets/reticlewidget.py +83 -83
- meerk40t/gui/scenewidgets/selectionwidget.py +2958 -2756
- meerk40t/gui/simpleui.py +362 -333
- meerk40t/gui/simulation.py +2451 -2094
- meerk40t/gui/snapoptions.py +208 -203
- meerk40t/gui/spoolerpanel.py +1227 -1180
- meerk40t/gui/statusbarwidgets/defaultoperations.py +480 -353
- meerk40t/gui/statusbarwidgets/infowidget.py +520 -483
- meerk40t/gui/statusbarwidgets/opassignwidget.py +356 -355
- meerk40t/gui/statusbarwidgets/selectionwidget.py +172 -171
- meerk40t/gui/statusbarwidgets/shapepropwidget.py +754 -236
- meerk40t/gui/statusbarwidgets/statusbar.py +272 -260
- meerk40t/gui/statusbarwidgets/statusbarwidget.py +268 -270
- meerk40t/gui/statusbarwidgets/strokewidget.py +267 -251
- meerk40t/gui/themes.py +200 -78
- meerk40t/gui/tips.py +590 -0
- meerk40t/gui/toolwidgets/circlebrush.py +35 -35
- meerk40t/gui/toolwidgets/toolcircle.py +248 -242
- meerk40t/gui/toolwidgets/toolcontainer.py +82 -77
- meerk40t/gui/toolwidgets/tooldraw.py +97 -90
- meerk40t/gui/toolwidgets/toolellipse.py +219 -212
- meerk40t/gui/toolwidgets/toolimagecut.py +25 -132
- meerk40t/gui/toolwidgets/toolline.py +39 -144
- meerk40t/gui/toolwidgets/toollinetext.py +79 -236
- meerk40t/gui/toolwidgets/toollinetext_inline.py +296 -0
- meerk40t/gui/toolwidgets/toolmeasure.py +163 -216
- meerk40t/gui/toolwidgets/toolnodeedit.py +2088 -2074
- meerk40t/gui/toolwidgets/toolnodemove.py +92 -94
- meerk40t/gui/toolwidgets/toolparameter.py +754 -668
- meerk40t/gui/toolwidgets/toolplacement.py +108 -108
- meerk40t/gui/toolwidgets/toolpoint.py +68 -59
- meerk40t/gui/toolwidgets/toolpointlistbuilder.py +294 -0
- meerk40t/gui/toolwidgets/toolpointmove.py +183 -0
- meerk40t/gui/toolwidgets/toolpolygon.py +288 -403
- meerk40t/gui/toolwidgets/toolpolyline.py +38 -196
- meerk40t/gui/toolwidgets/toolrect.py +211 -207
- meerk40t/gui/toolwidgets/toolrelocate.py +72 -72
- meerk40t/gui/toolwidgets/toolribbon.py +598 -113
- meerk40t/gui/toolwidgets/tooltabedit.py +546 -0
- meerk40t/gui/toolwidgets/tooltext.py +98 -89
- meerk40t/gui/toolwidgets/toolvector.py +213 -204
- meerk40t/gui/toolwidgets/toolwidget.py +39 -39
- meerk40t/gui/usbconnect.py +98 -91
- meerk40t/gui/utilitywidgets/buttonwidget.py +18 -18
- meerk40t/gui/utilitywidgets/checkboxwidget.py +90 -90
- meerk40t/gui/utilitywidgets/controlwidget.py +14 -14
- meerk40t/gui/utilitywidgets/cyclocycloidwidget.py +343 -340
- meerk40t/gui/utilitywidgets/debugwidgets.py +148 -0
- meerk40t/gui/utilitywidgets/handlewidget.py +27 -27
- meerk40t/gui/utilitywidgets/harmonograph.py +450 -447
- meerk40t/gui/utilitywidgets/openclosewidget.py +40 -40
- meerk40t/gui/utilitywidgets/rotationwidget.py +54 -54
- meerk40t/gui/utilitywidgets/scalewidget.py +75 -75
- meerk40t/gui/utilitywidgets/seekbarwidget.py +183 -183
- meerk40t/gui/utilitywidgets/togglewidget.py +142 -142
- meerk40t/gui/utilitywidgets/toolbarwidget.py +8 -8
- meerk40t/gui/wordlisteditor.py +985 -931
- meerk40t/gui/wxmeerk40t.py +1447 -1169
- meerk40t/gui/wxmmain.py +5644 -4112
- meerk40t/gui/wxmribbon.py +1591 -1076
- meerk40t/gui/wxmscene.py +1631 -1453
- meerk40t/gui/wxmtree.py +2416 -2089
- meerk40t/gui/wxutils.py +1769 -1099
- meerk40t/gui/zmatrix.py +102 -102
- meerk40t/image/__init__.py +1 -1
- meerk40t/image/dither.py +429 -0
- meerk40t/image/imagetools.py +2793 -2269
- meerk40t/internal_plugins.py +150 -130
- meerk40t/kernel/__init__.py +63 -12
- meerk40t/kernel/channel.py +259 -212
- meerk40t/kernel/context.py +538 -538
- meerk40t/kernel/exceptions.py +41 -41
- meerk40t/kernel/functions.py +463 -414
- meerk40t/kernel/jobs.py +100 -100
- meerk40t/kernel/kernel.py +3828 -3571
- meerk40t/kernel/lifecycles.py +71 -71
- meerk40t/kernel/module.py +49 -49
- meerk40t/kernel/service.py +147 -147
- meerk40t/kernel/settings.py +383 -343
- meerk40t/lihuiyu/controller.py +883 -876
- meerk40t/lihuiyu/device.py +1181 -1069
- meerk40t/lihuiyu/driver.py +1466 -1372
- meerk40t/lihuiyu/gui/gui.py +127 -106
- meerk40t/lihuiyu/gui/lhyaccelgui.py +377 -363
- meerk40t/lihuiyu/gui/lhycontrollergui.py +741 -651
- meerk40t/lihuiyu/gui/lhydrivergui.py +470 -446
- meerk40t/lihuiyu/gui/lhyoperationproperties.py +238 -237
- meerk40t/lihuiyu/gui/tcpcontroller.py +226 -190
- meerk40t/lihuiyu/interpreter.py +53 -53
- meerk40t/lihuiyu/laserspeed.py +450 -450
- meerk40t/lihuiyu/loader.py +90 -90
- meerk40t/lihuiyu/parser.py +404 -404
- meerk40t/lihuiyu/plugin.py +101 -102
- meerk40t/lihuiyu/tcp_connection.py +111 -109
- meerk40t/main.py +231 -165
- meerk40t/moshi/builder.py +788 -781
- meerk40t/moshi/controller.py +505 -499
- meerk40t/moshi/device.py +495 -442
- meerk40t/moshi/driver.py +862 -696
- meerk40t/moshi/gui/gui.py +78 -76
- meerk40t/moshi/gui/moshicontrollergui.py +538 -522
- meerk40t/moshi/gui/moshidrivergui.py +87 -75
- meerk40t/moshi/plugin.py +43 -43
- meerk40t/network/console_server.py +140 -57
- meerk40t/network/kernelserver.py +10 -9
- meerk40t/network/tcp_server.py +142 -140
- meerk40t/network/udp_server.py +103 -77
- meerk40t/network/web_server.py +404 -0
- meerk40t/newly/controller.py +1158 -1144
- meerk40t/newly/device.py +874 -732
- meerk40t/newly/driver.py +540 -412
- meerk40t/newly/gui/gui.py +219 -188
- meerk40t/newly/gui/newlyconfig.py +116 -101
- meerk40t/newly/gui/newlycontroller.py +193 -186
- meerk40t/newly/gui/operationproperties.py +51 -51
- meerk40t/newly/mock_connection.py +82 -82
- meerk40t/newly/newly_params.py +56 -56
- meerk40t/newly/plugin.py +1214 -1246
- meerk40t/newly/usb_connection.py +322 -322
- meerk40t/rotary/gui/gui.py +52 -46
- meerk40t/rotary/gui/rotarysettings.py +240 -232
- meerk40t/rotary/rotary.py +202 -98
- meerk40t/ruida/control.py +291 -91
- meerk40t/ruida/controller.py +138 -1088
- meerk40t/ruida/device.py +676 -231
- meerk40t/ruida/driver.py +534 -472
- meerk40t/ruida/emulator.py +1494 -1491
- meerk40t/ruida/exceptions.py +4 -4
- meerk40t/ruida/gui/gui.py +71 -76
- meerk40t/ruida/gui/ruidaconfig.py +239 -72
- meerk40t/ruida/gui/ruidacontroller.py +187 -184
- meerk40t/ruida/gui/ruidaoperationproperties.py +48 -47
- meerk40t/ruida/loader.py +54 -52
- meerk40t/ruida/mock_connection.py +57 -109
- meerk40t/ruida/plugin.py +124 -87
- meerk40t/ruida/rdjob.py +2084 -945
- meerk40t/ruida/serial_connection.py +116 -0
- meerk40t/ruida/tcp_connection.py +146 -0
- meerk40t/ruida/udp_connection.py +73 -0
- meerk40t/svgelements.py +9671 -9669
- meerk40t/tools/driver_to_path.py +584 -579
- meerk40t/tools/geomstr.py +5583 -4680
- meerk40t/tools/jhfparser.py +357 -292
- meerk40t/tools/kerftest.py +904 -890
- meerk40t/tools/livinghinges.py +1168 -1033
- meerk40t/tools/pathtools.py +987 -949
- meerk40t/tools/pmatrix.py +234 -0
- meerk40t/tools/pointfinder.py +942 -942
- meerk40t/tools/polybool.py +941 -940
- meerk40t/tools/rasterplotter.py +1660 -547
- meerk40t/tools/shxparser.py +1047 -901
- meerk40t/tools/ttfparser.py +726 -446
- meerk40t/tools/zinglplotter.py +595 -593
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/LICENSE +21 -21
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/METADATA +150 -139
- meerk40t-0.9.7020.dist-info/RECORD +446 -0
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/WHEEL +1 -1
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/top_level.txt +0 -1
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/zip-safe +1 -1
- meerk40t/balormk/elementlightjob.py +0 -159
- meerk40t-0.9.3001.dist-info/RECORD +0 -437
- test/bootstrap.py +0 -63
- test/test_cli.py +0 -12
- test/test_core_cutcode.py +0 -418
- test/test_core_elements.py +0 -144
- test/test_core_plotplanner.py +0 -397
- test/test_core_viewports.py +0 -312
- test/test_drivers_grbl.py +0 -108
- test/test_drivers_lihuiyu.py +0 -443
- test/test_drivers_newly.py +0 -113
- test/test_element_degenerate_points.py +0 -43
- test/test_elements_classify.py +0 -97
- test/test_elements_penbox.py +0 -22
- test/test_file_svg.py +0 -176
- test/test_fill.py +0 -155
- test/test_geomstr.py +0 -1523
- test/test_geomstr_nodes.py +0 -18
- test/test_imagetools_actualize.py +0 -306
- test/test_imagetools_wizard.py +0 -258
- test/test_kernel.py +0 -200
- test/test_laser_speeds.py +0 -3303
- test/test_length.py +0 -57
- test/test_lifecycle.py +0 -66
- test/test_operations.py +0 -251
- test/test_operations_hatch.py +0 -57
- test/test_ruida.py +0 -19
- test/test_spooler.py +0 -22
- test/test_tools_rasterplotter.py +0 -29
- test/test_wobble.py +0 -133
- test/test_zingl.py +0 -124
- {test → meerk40t/cylinder}/__init__.py +0 -0
- /meerk40t/{core/element_commands.py → cylinder/gui/__init__.py} +0 -0
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/entry_points.txt +0 -0
meerk40t/gui/simulation.py
CHANGED
@@ -1,2094 +1,2451 @@
|
|
1
|
-
import math
|
2
|
-
import platform
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
from ..core.cutcode.
|
10
|
-
from ..core.cutcode.
|
11
|
-
from ..core.cutcode.
|
12
|
-
from ..core.cutcode.
|
13
|
-
from ..core.cutcode.
|
14
|
-
from ..core.cutcode.
|
15
|
-
from ..core.cutcode.
|
16
|
-
from ..core.cutcode.
|
17
|
-
from ..core.cutcode.
|
18
|
-
from ..core.cutcode.
|
19
|
-
from ..core.cutcode.
|
20
|
-
from ..core.
|
21
|
-
from ..core.node.
|
22
|
-
from ..core.node.
|
23
|
-
from ..core.node.
|
24
|
-
from ..core.
|
25
|
-
from ..
|
26
|
-
from
|
27
|
-
from .
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
from .
|
47
|
-
from .
|
48
|
-
from .scene.
|
49
|
-
from .
|
50
|
-
from .scenewidgets.
|
51
|
-
from .
|
52
|
-
from .
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
)
|
77
|
-
self.
|
78
|
-
self.
|
79
|
-
|
80
|
-
|
81
|
-
self.
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
)
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
self.check_decompile
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
self.
|
101
|
-
|
102
|
-
|
103
|
-
self.
|
104
|
-
|
105
|
-
|
106
|
-
self.
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
self.
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
self.
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
newop =
|
196
|
-
self.cutplan.plan.insert(myidx, newop)
|
197
|
-
myidx += 1
|
198
|
-
cut.pop(0)
|
199
|
-
|
200
|
-
#
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
entry
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
except ValueError:
|
297
|
-
return
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
op =
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
(
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
#
|
400
|
-
#
|
401
|
-
#
|
402
|
-
#
|
403
|
-
#
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
)
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
_(
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
#
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
self.
|
487
|
-
self.
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
self.
|
494
|
-
self.
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
res = f"
|
516
|
-
elif isinstance(e,
|
517
|
-
res = f"
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
res
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
self.
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
selected
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
elif optype == "
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
self.
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
self
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
self.
|
804
|
-
self.
|
805
|
-
self.
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
self.
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
self.
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
self.
|
830
|
-
self.
|
831
|
-
self.
|
832
|
-
self.
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
self.
|
838
|
-
self.
|
839
|
-
|
840
|
-
|
841
|
-
self.
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
self.
|
849
|
-
|
850
|
-
)
|
851
|
-
self.
|
852
|
-
self.
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
)
|
858
|
-
self.
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
)
|
864
|
-
self.
|
865
|
-
|
866
|
-
)
|
867
|
-
|
868
|
-
|
869
|
-
)
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
self.
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
self.
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
self.
|
890
|
-
|
891
|
-
)
|
892
|
-
self.
|
893
|
-
|
894
|
-
)
|
895
|
-
|
896
|
-
self.
|
897
|
-
self.
|
898
|
-
|
899
|
-
self.
|
900
|
-
self.
|
901
|
-
|
902
|
-
self.
|
903
|
-
|
904
|
-
self.
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
self.
|
911
|
-
|
912
|
-
|
913
|
-
self.
|
914
|
-
self.
|
915
|
-
self.
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
self.
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
self.
|
939
|
-
|
940
|
-
|
941
|
-
|
942
|
-
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
|
947
|
-
self.
|
948
|
-
self.
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
self.
|
954
|
-
|
955
|
-
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
|
960
|
-
|
961
|
-
|
962
|
-
self.
|
963
|
-
|
964
|
-
self.
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
self.
|
970
|
-
self.
|
971
|
-
self.
|
972
|
-
|
973
|
-
self.
|
974
|
-
self.
|
975
|
-
|
976
|
-
)
|
977
|
-
self.
|
978
|
-
self.
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
-
|
983
|
-
self.
|
984
|
-
wx.
|
985
|
-
|
986
|
-
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
|
991
|
-
|
992
|
-
)
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
self.
|
1001
|
-
self.
|
1002
|
-
self.
|
1003
|
-
self.
|
1004
|
-
self.
|
1005
|
-
|
1006
|
-
self.
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
self.
|
1011
|
-
self.
|
1012
|
-
|
1013
|
-
self.
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
)
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
self
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1034
|
-
)
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
)
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
1053
|
-
|
1054
|
-
|
1055
|
-
|
1056
|
-
|
1057
|
-
self.
|
1058
|
-
|
1059
|
-
self.
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
self.
|
1064
|
-
self.
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1071
|
-
|
1072
|
-
|
1073
|
-
|
1074
|
-
|
1075
|
-
|
1076
|
-
self.
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1102
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
1109
|
-
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
1115
|
-
|
1116
|
-
|
1117
|
-
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1122
|
-
|
1123
|
-
|
1124
|
-
|
1125
|
-
|
1126
|
-
|
1127
|
-
|
1128
|
-
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
1136
|
-
|
1137
|
-
|
1138
|
-
|
1139
|
-
|
1140
|
-
|
1141
|
-
|
1142
|
-
|
1143
|
-
|
1144
|
-
|
1145
|
-
#
|
1146
|
-
|
1147
|
-
|
1148
|
-
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1153
|
-
|
1154
|
-
self.
|
1155
|
-
|
1156
|
-
|
1157
|
-
|
1158
|
-
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1162
|
-
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
1166
|
-
|
1167
|
-
|
1168
|
-
|
1169
|
-
|
1170
|
-
|
1171
|
-
|
1172
|
-
|
1173
|
-
|
1174
|
-
self.
|
1175
|
-
self.
|
1176
|
-
|
1177
|
-
|
1178
|
-
|
1179
|
-
|
1180
|
-
|
1181
|
-
|
1182
|
-
|
1183
|
-
|
1184
|
-
self.
|
1185
|
-
|
1186
|
-
|
1187
|
-
|
1188
|
-
|
1189
|
-
|
1190
|
-
|
1191
|
-
|
1192
|
-
|
1193
|
-
self.
|
1194
|
-
|
1195
|
-
|
1196
|
-
self.
|
1197
|
-
|
1198
|
-
)
|
1199
|
-
|
1200
|
-
|
1201
|
-
|
1202
|
-
|
1203
|
-
|
1204
|
-
|
1205
|
-
)
|
1206
|
-
|
1207
|
-
|
1208
|
-
|
1209
|
-
self.
|
1210
|
-
|
1211
|
-
|
1212
|
-
|
1213
|
-
|
1214
|
-
|
1215
|
-
|
1216
|
-
|
1217
|
-
|
1218
|
-
|
1219
|
-
|
1220
|
-
|
1221
|
-
|
1222
|
-
|
1223
|
-
|
1224
|
-
|
1225
|
-
|
1226
|
-
|
1227
|
-
|
1228
|
-
|
1229
|
-
|
1230
|
-
|
1231
|
-
|
1232
|
-
|
1233
|
-
|
1234
|
-
|
1235
|
-
|
1236
|
-
|
1237
|
-
|
1238
|
-
|
1239
|
-
|
1240
|
-
|
1241
|
-
|
1242
|
-
|
1243
|
-
|
1244
|
-
|
1245
|
-
|
1246
|
-
|
1247
|
-
|
1248
|
-
|
1249
|
-
|
1250
|
-
|
1251
|
-
|
1252
|
-
|
1253
|
-
|
1254
|
-
|
1255
|
-
|
1256
|
-
|
1257
|
-
|
1258
|
-
|
1259
|
-
|
1260
|
-
|
1261
|
-
|
1262
|
-
|
1263
|
-
|
1264
|
-
|
1265
|
-
|
1266
|
-
|
1267
|
-
|
1268
|
-
|
1269
|
-
|
1270
|
-
|
1271
|
-
|
1272
|
-
|
1273
|
-
|
1274
|
-
|
1275
|
-
|
1276
|
-
|
1277
|
-
|
1278
|
-
|
1279
|
-
|
1280
|
-
|
1281
|
-
|
1282
|
-
|
1283
|
-
|
1284
|
-
|
1285
|
-
|
1286
|
-
|
1287
|
-
|
1288
|
-
|
1289
|
-
|
1290
|
-
|
1291
|
-
|
1292
|
-
)
|
1293
|
-
|
1294
|
-
|
1295
|
-
|
1296
|
-
|
1297
|
-
|
1298
|
-
|
1299
|
-
|
1300
|
-
|
1301
|
-
self.
|
1302
|
-
|
1303
|
-
|
1304
|
-
|
1305
|
-
|
1306
|
-
|
1307
|
-
|
1308
|
-
|
1309
|
-
|
1310
|
-
|
1311
|
-
|
1312
|
-
|
1313
|
-
|
1314
|
-
|
1315
|
-
|
1316
|
-
self.
|
1317
|
-
|
1318
|
-
|
1319
|
-
|
1320
|
-
|
1321
|
-
|
1322
|
-
|
1323
|
-
|
1324
|
-
|
1325
|
-
|
1326
|
-
|
1327
|
-
|
1328
|
-
|
1329
|
-
|
1330
|
-
|
1331
|
-
|
1332
|
-
|
1333
|
-
|
1334
|
-
|
1335
|
-
|
1336
|
-
|
1337
|
-
|
1338
|
-
|
1339
|
-
|
1340
|
-
|
1341
|
-
|
1342
|
-
|
1343
|
-
|
1344
|
-
|
1345
|
-
|
1346
|
-
self.
|
1347
|
-
|
1348
|
-
|
1349
|
-
|
1350
|
-
|
1351
|
-
|
1352
|
-
|
1353
|
-
|
1354
|
-
|
1355
|
-
|
1356
|
-
|
1357
|
-
if
|
1358
|
-
self.
|
1359
|
-
|
1360
|
-
|
1361
|
-
|
1362
|
-
|
1363
|
-
|
1364
|
-
|
1365
|
-
|
1366
|
-
|
1367
|
-
|
1368
|
-
|
1369
|
-
|
1370
|
-
|
1371
|
-
|
1372
|
-
|
1373
|
-
|
1374
|
-
|
1375
|
-
|
1376
|
-
|
1377
|
-
|
1378
|
-
|
1379
|
-
|
1380
|
-
|
1381
|
-
|
1382
|
-
|
1383
|
-
|
1384
|
-
|
1385
|
-
|
1386
|
-
|
1387
|
-
|
1388
|
-
|
1389
|
-
self.
|
1390
|
-
|
1391
|
-
|
1392
|
-
|
1393
|
-
|
1394
|
-
|
1395
|
-
|
1396
|
-
|
1397
|
-
|
1398
|
-
|
1399
|
-
|
1400
|
-
|
1401
|
-
|
1402
|
-
|
1403
|
-
|
1404
|
-
|
1405
|
-
|
1406
|
-
|
1407
|
-
|
1408
|
-
|
1409
|
-
|
1410
|
-
|
1411
|
-
|
1412
|
-
|
1413
|
-
|
1414
|
-
|
1415
|
-
|
1416
|
-
|
1417
|
-
|
1418
|
-
|
1419
|
-
|
1420
|
-
|
1421
|
-
|
1422
|
-
|
1423
|
-
|
1424
|
-
|
1425
|
-
|
1426
|
-
|
1427
|
-
|
1428
|
-
|
1429
|
-
|
1430
|
-
|
1431
|
-
|
1432
|
-
|
1433
|
-
|
1434
|
-
|
1435
|
-
|
1436
|
-
|
1437
|
-
|
1438
|
-
|
1439
|
-
|
1440
|
-
|
1441
|
-
|
1442
|
-
|
1443
|
-
|
1444
|
-
|
1445
|
-
|
1446
|
-
|
1447
|
-
|
1448
|
-
|
1449
|
-
|
1450
|
-
|
1451
|
-
|
1452
|
-
|
1453
|
-
|
1454
|
-
|
1455
|
-
|
1456
|
-
|
1457
|
-
|
1458
|
-
|
1459
|
-
|
1460
|
-
|
1461
|
-
|
1462
|
-
|
1463
|
-
|
1464
|
-
|
1465
|
-
|
1466
|
-
)
|
1467
|
-
except
|
1468
|
-
|
1469
|
-
|
1470
|
-
|
1471
|
-
|
1472
|
-
|
1473
|
-
|
1474
|
-
|
1475
|
-
|
1476
|
-
|
1477
|
-
|
1478
|
-
|
1479
|
-
|
1480
|
-
|
1481
|
-
|
1482
|
-
|
1483
|
-
|
1484
|
-
self.
|
1485
|
-
|
1486
|
-
|
1487
|
-
|
1488
|
-
|
1489
|
-
|
1490
|
-
|
1491
|
-
|
1492
|
-
|
1493
|
-
|
1494
|
-
|
1495
|
-
|
1496
|
-
|
1497
|
-
)
|
1498
|
-
|
1499
|
-
|
1500
|
-
|
1501
|
-
|
1502
|
-
|
1503
|
-
|
1504
|
-
|
1505
|
-
|
1506
|
-
|
1507
|
-
|
1508
|
-
self.
|
1509
|
-
|
1510
|
-
|
1511
|
-
|
1512
|
-
|
1513
|
-
|
1514
|
-
|
1515
|
-
|
1516
|
-
|
1517
|
-
|
1518
|
-
|
1519
|
-
|
1520
|
-
|
1521
|
-
|
1522
|
-
|
1523
|
-
|
1524
|
-
|
1525
|
-
|
1526
|
-
|
1527
|
-
|
1528
|
-
|
1529
|
-
|
1530
|
-
|
1531
|
-
|
1532
|
-
self.
|
1533
|
-
|
1534
|
-
|
1535
|
-
|
1536
|
-
|
1537
|
-
|
1538
|
-
|
1539
|
-
|
1540
|
-
|
1541
|
-
|
1542
|
-
|
1543
|
-
|
1544
|
-
|
1545
|
-
|
1546
|
-
|
1547
|
-
|
1548
|
-
|
1549
|
-
|
1550
|
-
|
1551
|
-
|
1552
|
-
|
1553
|
-
|
1554
|
-
|
1555
|
-
|
1556
|
-
|
1557
|
-
|
1558
|
-
)
|
1559
|
-
|
1560
|
-
self.
|
1561
|
-
|
1562
|
-
|
1563
|
-
self.
|
1564
|
-
|
1565
|
-
|
1566
|
-
|
1567
|
-
|
1568
|
-
|
1569
|
-
|
1570
|
-
|
1571
|
-
|
1572
|
-
|
1573
|
-
|
1574
|
-
|
1575
|
-
|
1576
|
-
self.
|
1577
|
-
|
1578
|
-
self.
|
1579
|
-
|
1580
|
-
|
1581
|
-
self.
|
1582
|
-
|
1583
|
-
|
1584
|
-
|
1585
|
-
|
1586
|
-
|
1587
|
-
""
|
1588
|
-
|
1589
|
-
|
1590
|
-
|
1591
|
-
|
1592
|
-
|
1593
|
-
|
1594
|
-
|
1595
|
-
|
1596
|
-
|
1597
|
-
|
1598
|
-
self.
|
1599
|
-
|
1600
|
-
|
1601
|
-
self.
|
1602
|
-
|
1603
|
-
self.
|
1604
|
-
|
1605
|
-
|
1606
|
-
|
1607
|
-
|
1608
|
-
)
|
1609
|
-
|
1610
|
-
|
1611
|
-
|
1612
|
-
|
1613
|
-
|
1614
|
-
|
1615
|
-
|
1616
|
-
|
1617
|
-
|
1618
|
-
|
1619
|
-
|
1620
|
-
|
1621
|
-
|
1622
|
-
|
1623
|
-
|
1624
|
-
|
1625
|
-
|
1626
|
-
|
1627
|
-
|
1628
|
-
|
1629
|
-
|
1630
|
-
|
1631
|
-
|
1632
|
-
|
1633
|
-
self.
|
1634
|
-
|
1635
|
-
|
1636
|
-
|
1637
|
-
|
1638
|
-
|
1639
|
-
|
1640
|
-
|
1641
|
-
|
1642
|
-
|
1643
|
-
|
1644
|
-
|
1645
|
-
|
1646
|
-
|
1647
|
-
|
1648
|
-
|
1649
|
-
|
1650
|
-
|
1651
|
-
|
1652
|
-
|
1653
|
-
|
1654
|
-
|
1655
|
-
|
1656
|
-
|
1657
|
-
|
1658
|
-
|
1659
|
-
|
1660
|
-
|
1661
|
-
|
1662
|
-
|
1663
|
-
|
1664
|
-
|
1665
|
-
|
1666
|
-
|
1667
|
-
|
1668
|
-
|
1669
|
-
|
1670
|
-
|
1671
|
-
self.
|
1672
|
-
|
1673
|
-
|
1674
|
-
self.
|
1675
|
-
|
1676
|
-
|
1677
|
-
|
1678
|
-
|
1679
|
-
|
1680
|
-
|
1681
|
-
|
1682
|
-
|
1683
|
-
|
1684
|
-
|
1685
|
-
|
1686
|
-
|
1687
|
-
|
1688
|
-
|
1689
|
-
|
1690
|
-
|
1691
|
-
|
1692
|
-
|
1693
|
-
|
1694
|
-
|
1695
|
-
|
1696
|
-
|
1697
|
-
|
1698
|
-
|
1699
|
-
|
1700
|
-
|
1701
|
-
|
1702
|
-
|
1703
|
-
|
1704
|
-
|
1705
|
-
|
1706
|
-
)
|
1707
|
-
|
1708
|
-
|
1709
|
-
|
1710
|
-
|
1711
|
-
|
1712
|
-
|
1713
|
-
|
1714
|
-
|
1715
|
-
|
1716
|
-
|
1717
|
-
|
1718
|
-
|
1719
|
-
|
1720
|
-
|
1721
|
-
|
1722
|
-
|
1723
|
-
|
1724
|
-
|
1725
|
-
|
1726
|
-
|
1727
|
-
|
1728
|
-
|
1729
|
-
|
1730
|
-
|
1731
|
-
|
1732
|
-
|
1733
|
-
|
1734
|
-
|
1735
|
-
|
1736
|
-
|
1737
|
-
|
1738
|
-
|
1739
|
-
|
1740
|
-
|
1741
|
-
|
1742
|
-
|
1743
|
-
|
1744
|
-
|
1745
|
-
|
1746
|
-
|
1747
|
-
|
1748
|
-
|
1749
|
-
|
1750
|
-
|
1751
|
-
|
1752
|
-
|
1753
|
-
|
1754
|
-
|
1755
|
-
|
1756
|
-
|
1757
|
-
|
1758
|
-
|
1759
|
-
|
1760
|
-
|
1761
|
-
|
1762
|
-
|
1763
|
-
|
1764
|
-
|
1765
|
-
|
1766
|
-
|
1767
|
-
|
1768
|
-
|
1769
|
-
|
1770
|
-
|
1771
|
-
|
1772
|
-
|
1773
|
-
|
1774
|
-
|
1775
|
-
|
1776
|
-
|
1777
|
-
|
1778
|
-
|
1779
|
-
|
1780
|
-
|
1781
|
-
|
1782
|
-
|
1783
|
-
|
1784
|
-
|
1785
|
-
|
1786
|
-
|
1787
|
-
|
1788
|
-
|
1789
|
-
|
1790
|
-
|
1791
|
-
|
1792
|
-
|
1793
|
-
|
1794
|
-
|
1795
|
-
|
1796
|
-
|
1797
|
-
|
1798
|
-
|
1799
|
-
|
1800
|
-
|
1801
|
-
|
1802
|
-
|
1803
|
-
|
1804
|
-
|
1805
|
-
|
1806
|
-
|
1807
|
-
|
1808
|
-
|
1809
|
-
|
1810
|
-
|
1811
|
-
|
1812
|
-
|
1813
|
-
|
1814
|
-
|
1815
|
-
|
1816
|
-
|
1817
|
-
|
1818
|
-
|
1819
|
-
|
1820
|
-
|
1821
|
-
|
1822
|
-
|
1823
|
-
|
1824
|
-
|
1825
|
-
|
1826
|
-
|
1827
|
-
|
1828
|
-
|
1829
|
-
|
1830
|
-
|
1831
|
-
|
1832
|
-
|
1833
|
-
|
1834
|
-
|
1835
|
-
|
1836
|
-
|
1837
|
-
|
1838
|
-
|
1839
|
-
|
1840
|
-
|
1841
|
-
|
1842
|
-
|
1843
|
-
|
1844
|
-
|
1845
|
-
|
1846
|
-
|
1847
|
-
|
1848
|
-
|
1849
|
-
|
1850
|
-
|
1851
|
-
|
1852
|
-
|
1853
|
-
|
1854
|
-
|
1855
|
-
|
1856
|
-
|
1857
|
-
|
1858
|
-
|
1859
|
-
|
1860
|
-
|
1861
|
-
|
1862
|
-
|
1863
|
-
|
1864
|
-
|
1865
|
-
|
1866
|
-
|
1867
|
-
|
1868
|
-
|
1869
|
-
|
1870
|
-
|
1871
|
-
|
1872
|
-
|
1873
|
-
|
1874
|
-
|
1875
|
-
|
1876
|
-
|
1877
|
-
|
1878
|
-
|
1879
|
-
|
1880
|
-
|
1881
|
-
|
1882
|
-
|
1883
|
-
|
1884
|
-
|
1885
|
-
|
1886
|
-
|
1887
|
-
|
1888
|
-
|
1889
|
-
|
1890
|
-
|
1891
|
-
|
1892
|
-
|
1893
|
-
|
1894
|
-
|
1895
|
-
|
1896
|
-
|
1897
|
-
|
1898
|
-
|
1899
|
-
|
1900
|
-
|
1901
|
-
|
1902
|
-
|
1903
|
-
|
1904
|
-
|
1905
|
-
|
1906
|
-
|
1907
|
-
|
1908
|
-
|
1909
|
-
|
1910
|
-
|
1911
|
-
|
1912
|
-
|
1913
|
-
|
1914
|
-
|
1915
|
-
|
1916
|
-
|
1917
|
-
|
1918
|
-
|
1919
|
-
|
1920
|
-
|
1921
|
-
|
1922
|
-
|
1923
|
-
|
1924
|
-
|
1925
|
-
|
1926
|
-
|
1927
|
-
|
1928
|
-
|
1929
|
-
|
1930
|
-
|
1931
|
-
|
1932
|
-
|
1933
|
-
|
1934
|
-
|
1935
|
-
|
1936
|
-
|
1937
|
-
|
1938
|
-
|
1939
|
-
|
1940
|
-
|
1941
|
-
|
1942
|
-
|
1943
|
-
|
1944
|
-
|
1945
|
-
|
1946
|
-
|
1947
|
-
|
1948
|
-
|
1949
|
-
|
1950
|
-
|
1951
|
-
|
1952
|
-
|
1953
|
-
|
1954
|
-
|
1955
|
-
|
1956
|
-
|
1957
|
-
|
1958
|
-
|
1959
|
-
|
1960
|
-
|
1961
|
-
|
1962
|
-
|
1963
|
-
|
1964
|
-
)
|
1965
|
-
|
1966
|
-
|
1967
|
-
|
1968
|
-
|
1969
|
-
|
1970
|
-
|
1971
|
-
|
1972
|
-
|
1973
|
-
|
1974
|
-
|
1975
|
-
|
1976
|
-
|
1977
|
-
|
1978
|
-
|
1979
|
-
|
1980
|
-
|
1981
|
-
|
1982
|
-
|
1983
|
-
|
1984
|
-
|
1985
|
-
|
1986
|
-
|
1987
|
-
|
1988
|
-
|
1989
|
-
|
1990
|
-
|
1991
|
-
|
1992
|
-
|
1993
|
-
|
1994
|
-
|
1995
|
-
|
1996
|
-
|
1997
|
-
|
1998
|
-
|
1999
|
-
|
2000
|
-
|
2001
|
-
|
2002
|
-
|
2003
|
-
|
2004
|
-
|
2005
|
-
|
2006
|
-
|
2007
|
-
|
2008
|
-
|
2009
|
-
|
2010
|
-
|
2011
|
-
|
2012
|
-
|
2013
|
-
|
2014
|
-
|
2015
|
-
|
2016
|
-
|
2017
|
-
|
2018
|
-
|
2019
|
-
|
2020
|
-
|
2021
|
-
|
2022
|
-
|
2023
|
-
|
2024
|
-
|
2025
|
-
|
2026
|
-
|
2027
|
-
|
2028
|
-
|
2029
|
-
|
2030
|
-
|
2031
|
-
|
2032
|
-
|
2033
|
-
|
2034
|
-
|
2035
|
-
|
2036
|
-
|
2037
|
-
|
2038
|
-
|
2039
|
-
|
2040
|
-
|
2041
|
-
|
2042
|
-
|
2043
|
-
|
2044
|
-
|
2045
|
-
|
2046
|
-
|
2047
|
-
|
2048
|
-
|
2049
|
-
|
2050
|
-
|
2051
|
-
|
2052
|
-
|
2053
|
-
|
2054
|
-
|
2055
|
-
|
2056
|
-
|
2057
|
-
|
2058
|
-
|
2059
|
-
|
2060
|
-
|
2061
|
-
|
2062
|
-
|
2063
|
-
|
2064
|
-
|
2065
|
-
|
2066
|
-
|
2067
|
-
|
2068
|
-
|
2069
|
-
|
2070
|
-
|
2071
|
-
|
2072
|
-
|
2073
|
-
|
2074
|
-
|
2075
|
-
|
2076
|
-
|
2077
|
-
|
2078
|
-
|
2079
|
-
|
2080
|
-
|
2081
|
-
|
2082
|
-
|
2083
|
-
|
2084
|
-
|
2085
|
-
|
2086
|
-
|
2087
|
-
|
2088
|
-
|
2089
|
-
|
2090
|
-
|
2091
|
-
|
2092
|
-
|
2093
|
-
|
2094
|
-
|
1
|
+
import math
|
2
|
+
import platform
|
3
|
+
from time import perf_counter, sleep
|
4
|
+
|
5
|
+
import wx
|
6
|
+
|
7
|
+
from meerk40t.kernel import Job, signal_listener
|
8
|
+
|
9
|
+
from ..core.cutcode.cubiccut import CubicCut
|
10
|
+
from ..core.cutcode.cutcode import CutCode
|
11
|
+
from ..core.cutcode.dwellcut import DwellCut
|
12
|
+
from ..core.cutcode.gotocut import GotoCut
|
13
|
+
from ..core.cutcode.homecut import HomeCut
|
14
|
+
from ..core.cutcode.inputcut import InputCut
|
15
|
+
from ..core.cutcode.linecut import LineCut
|
16
|
+
from ..core.cutcode.outputcut import OutputCut
|
17
|
+
from ..core.cutcode.plotcut import PlotCut
|
18
|
+
from ..core.cutcode.quadcut import QuadCut
|
19
|
+
from ..core.cutcode.rastercut import RasterCut
|
20
|
+
from ..core.cutcode.waitcut import WaitCut
|
21
|
+
from ..core.node.util_console import ConsoleOperation
|
22
|
+
from ..core.node.util_goto import GotoOperation
|
23
|
+
from ..core.node.util_home import HomeOperation
|
24
|
+
from ..core.node.util_wait import WaitOperation
|
25
|
+
from ..core.units import Length
|
26
|
+
from ..svgelements import Matrix
|
27
|
+
from .choicepropertypanel import ChoicePropertyPanel
|
28
|
+
from .icons import (
|
29
|
+
STD_ICON_SIZE,
|
30
|
+
get_default_icon_size,
|
31
|
+
icon_bell,
|
32
|
+
icon_close_window,
|
33
|
+
icon_console,
|
34
|
+
icon_external,
|
35
|
+
icon_internal,
|
36
|
+
icon_return,
|
37
|
+
icon_round_stop,
|
38
|
+
icon_timer,
|
39
|
+
icons8_circled_play,
|
40
|
+
icons8_home_filled,
|
41
|
+
icons8_image,
|
42
|
+
icons8_laser_beam_hazard,
|
43
|
+
icons8_pause,
|
44
|
+
icons8_route,
|
45
|
+
)
|
46
|
+
from .laserrender import DRAW_MODE_BACKGROUND, LaserRender
|
47
|
+
from .mwindow import MWindow
|
48
|
+
from .scene.scenepanel import ScenePanel
|
49
|
+
from .scene.widget import Widget
|
50
|
+
from .scenewidgets.bedwidget import BedWidget
|
51
|
+
from .scenewidgets.gridwidget import GridWidget
|
52
|
+
from .wxutils import (
|
53
|
+
StaticBoxSizer,
|
54
|
+
TextCtrl,
|
55
|
+
dip_size,
|
56
|
+
get_gc_scale,
|
57
|
+
wxButton,
|
58
|
+
wxCheckBox,
|
59
|
+
wxListBox,
|
60
|
+
wxListCtrl,
|
61
|
+
wxStaticText,
|
62
|
+
)
|
63
|
+
from .zmatrix import ZMatrix
|
64
|
+
|
65
|
+
_ = wx.GetTranslation
|
66
|
+
|
67
|
+
|
68
|
+
class OperationsPanel(wx.Panel):
|
69
|
+
"""
|
70
|
+
OperationsPanel is a panel that display cutplan operations and allows to edit them
|
71
|
+
"""
|
72
|
+
|
73
|
+
def __init__(self, *args, context=None, cutplan=None, **kwds):
|
74
|
+
# begin wxGlade: PassesPanel.__init__
|
75
|
+
kwds["style"] = kwds.get("style", 0)
|
76
|
+
wx.Panel.__init__(self, *args, **kwds)
|
77
|
+
self.parent = args[0]
|
78
|
+
self.context = context
|
79
|
+
self.context.themes.set_window_colors(self)
|
80
|
+
|
81
|
+
self.cutplan = cutplan
|
82
|
+
if self.cutplan is None:
|
83
|
+
self.plan_name = ""
|
84
|
+
else:
|
85
|
+
self.plan_name = self.cutplan.name
|
86
|
+
self.list_operations = wxListCtrl(self, wx.ID_ANY, style=wx.LC_LIST)
|
87
|
+
self.context.themes.set_window_colors(self.list_operations)
|
88
|
+
|
89
|
+
self.text_operation_param = TextCtrl(
|
90
|
+
self, wx.ID_ANY, "", style=wx.TE_PROCESS_ENTER
|
91
|
+
)
|
92
|
+
self.check_decompile = wxCheckBox(self, wx.ID_ANY, "D")
|
93
|
+
self.check_decompile.SetToolTip(
|
94
|
+
_("Decompile cutplan = make operations visible and editable again")
|
95
|
+
)
|
96
|
+
self.text_operation_param.SetToolTip(
|
97
|
+
_("Modify operation parameter, press Enter to apply")
|
98
|
+
)
|
99
|
+
|
100
|
+
self.list_operations.Bind(
|
101
|
+
wx.EVT_RIGHT_DOWN, self.on_listbox_operation_rightclick
|
102
|
+
)
|
103
|
+
self.list_operations.Bind(
|
104
|
+
wx.EVT_LIST_ITEM_SELECTED, self.on_listbox_operation_select
|
105
|
+
)
|
106
|
+
self.text_operation_param.Bind(wx.EVT_TEXT_ENTER, self.on_text_operation_param)
|
107
|
+
self.check_decompile.Bind(wx.EVT_CHECKBOX, self.on_check_decompile)
|
108
|
+
|
109
|
+
ops_sizer = wx.BoxSizer(wx.VERTICAL)
|
110
|
+
ops_sizer.Add(self.list_operations, 1, wx.EXPAND, 0)
|
111
|
+
hsizer = wx.BoxSizer(wx.HORIZONTAL)
|
112
|
+
hsizer.Add(self.text_operation_param, 1, wx.EXPAND, 0)
|
113
|
+
hsizer.Add(self.check_decompile, 0, wx.ALIGN_CENTER_VERTICAL, 0)
|
114
|
+
ops_sizer.Add(hsizer, 0, wx.EXPAND, 0)
|
115
|
+
self.setup_state_images()
|
116
|
+
self.SetSizer(ops_sizer)
|
117
|
+
self.Layout()
|
118
|
+
self.context.setting(bool, "cutplan_decompile", False)
|
119
|
+
decompile = self.context.cutplan_decompile
|
120
|
+
self.check_decompile.SetValue(decompile)
|
121
|
+
self.set_cut_plan(self.cutplan)
|
122
|
+
|
123
|
+
def setup_state_images(self):
|
124
|
+
iconsize = 20
|
125
|
+
self.default_images = [
|
126
|
+
["beep", icon_bell],
|
127
|
+
["interrupt", icon_round_stop],
|
128
|
+
["quit", icon_close_window],
|
129
|
+
["wait", icon_timer],
|
130
|
+
["home", icons8_home_filled],
|
131
|
+
["goto", icon_return],
|
132
|
+
["origin", icon_return],
|
133
|
+
["output", icon_external],
|
134
|
+
["input", icon_internal],
|
135
|
+
["cutcode", icons8_laser_beam_hazard],
|
136
|
+
# Intentionally the last...
|
137
|
+
["console", icon_console],
|
138
|
+
]
|
139
|
+
self.options_images = wx.ImageList()
|
140
|
+
self.options_images.Create(width=iconsize, height=iconsize)
|
141
|
+
for entry in self.default_images:
|
142
|
+
image = entry[1].GetBitmap(resize=(iconsize, iconsize), noadjustment=True)
|
143
|
+
image_id1 = self.options_images.Add(bitmap=image)
|
144
|
+
self.list_operations.AssignImageList(self.options_images, wx.IMAGE_LIST_SMALL)
|
145
|
+
|
146
|
+
def establish_state(self, typename, opname):
|
147
|
+
# Establish icon
|
148
|
+
stateidx = -1
|
149
|
+
tofind = opname.lower()
|
150
|
+
for idx, entry in enumerate(self.default_images):
|
151
|
+
if tofind.startswith(entry[0]):
|
152
|
+
stateidx = idx
|
153
|
+
break
|
154
|
+
if stateidx < 0 and typename == "ConsoleOperation":
|
155
|
+
stateidx = len(self.default_images) - 1
|
156
|
+
# print(f"opname={opname}, parameter={parameter}, state={stateidx}")
|
157
|
+
return stateidx
|
158
|
+
|
159
|
+
def on_check_decompile(self, event):
|
160
|
+
flag = self.check_decompile.GetValue()
|
161
|
+
if self.context.cutplan_decompile != flag:
|
162
|
+
self.context.cutplan_decompile = flag
|
163
|
+
if flag:
|
164
|
+
self.set_cut_plan(self.cutplan)
|
165
|
+
|
166
|
+
def set_cut_plan(self, cutplan):
|
167
|
+
def name_str(e):
|
168
|
+
res1 = type(e).__name__
|
169
|
+
try:
|
170
|
+
res2 = e.__name__
|
171
|
+
except AttributeError:
|
172
|
+
res2 = str(e)
|
173
|
+
# print(f"{res1} -> {res2}")
|
174
|
+
return res1, res2
|
175
|
+
|
176
|
+
decompile = self.context.cutplan_decompile
|
177
|
+
oldidx = self.list_operations.GetFirstSelected()
|
178
|
+
self.cutplan = cutplan
|
179
|
+
self.plan_name = self.cutplan.name
|
180
|
+
if decompile:
|
181
|
+
changes = True
|
182
|
+
while changes:
|
183
|
+
changes = False # Prove me wrong...
|
184
|
+
for idx, cut in enumerate(self.cutplan.plan):
|
185
|
+
if isinstance(cut, CutCode):
|
186
|
+
# Let's have a look at the beginning
|
187
|
+
myidx = idx
|
188
|
+
while len(cut) > 0:
|
189
|
+
entry = cut[0]
|
190
|
+
if isinstance(entry, GotoCut):
|
191
|
+
# reverse engineer
|
192
|
+
changes = True
|
193
|
+
x = entry._start_x
|
194
|
+
y = entry._start_y
|
195
|
+
newop = GotoOperation(x=x, y=y)
|
196
|
+
self.cutplan.plan.insert(myidx, newop)
|
197
|
+
myidx += 1
|
198
|
+
cut.pop(0)
|
199
|
+
elif isinstance(entry, HomeCut):
|
200
|
+
# reverse engineer
|
201
|
+
changes = True
|
202
|
+
newop = HomeOperation()
|
203
|
+
self.cutplan.plan.insert(myidx, newop)
|
204
|
+
myidx += 1
|
205
|
+
cut.pop(0)
|
206
|
+
elif isinstance(entry, WaitCut):
|
207
|
+
# reverse engineer
|
208
|
+
changes = True
|
209
|
+
wt = entry.dwell_time
|
210
|
+
newop = WaitOperation(wait=wt)
|
211
|
+
self.cutplan.plan.insert(myidx, newop)
|
212
|
+
myidx += 1
|
213
|
+
cut.pop(0)
|
214
|
+
else:
|
215
|
+
# 'Real ' stuff starts that's enough...
|
216
|
+
break
|
217
|
+
|
218
|
+
# And now look to the end
|
219
|
+
while len(cut) > 0:
|
220
|
+
last = len(cut) - 1
|
221
|
+
entry = cut[last]
|
222
|
+
if isinstance(entry, GotoCut):
|
223
|
+
# reverse engineer
|
224
|
+
changes = True
|
225
|
+
x = entry._start_x
|
226
|
+
y = entry._start_y
|
227
|
+
newop = GotoOperation(x=x, y=y)
|
228
|
+
self.cutplan.plan.insert(myidx + 1, newop)
|
229
|
+
cut.pop(last)
|
230
|
+
elif isinstance(entry, HomeCut):
|
231
|
+
# reverse engineer
|
232
|
+
changes = True
|
233
|
+
newop = HomeOperation()
|
234
|
+
self.cutplan.plan.insert(myidx + 1, newop)
|
235
|
+
cut.pop(last)
|
236
|
+
elif isinstance(entry, WaitCut):
|
237
|
+
# reverse engineer
|
238
|
+
changes = True
|
239
|
+
wt = entry.dwell_time
|
240
|
+
newop = WaitOperation(wait=wt)
|
241
|
+
self.cutplan.plan.insert(myidx + 1, newop)
|
242
|
+
cut.pop(last)
|
243
|
+
else:
|
244
|
+
# 'Real ' stuff starts that's enough...
|
245
|
+
break
|
246
|
+
if len(cut) == 0:
|
247
|
+
idx = self.cutplan.plan.index(cut)
|
248
|
+
self.cutplan.plan.pop(idx)
|
249
|
+
changes = True
|
250
|
+
|
251
|
+
if changes:
|
252
|
+
# Break from inner loop and try again...
|
253
|
+
break
|
254
|
+
|
255
|
+
self.list_operations.DeleteAllItems()
|
256
|
+
if self.cutplan.plan is not None and len(self.cutplan.plan) != 0:
|
257
|
+
for idx, entry in enumerate(self.cutplan.plan):
|
258
|
+
tname, info = name_str(entry)
|
259
|
+
item = self.list_operations.InsertItem(
|
260
|
+
self.list_operations.GetItemCount(), f"{idx:02d}# - {info}"
|
261
|
+
)
|
262
|
+
|
263
|
+
state = self.establish_state(tname, info)
|
264
|
+
if state >= 0:
|
265
|
+
self.list_operations.SetItemImage(item, state)
|
266
|
+
|
267
|
+
if 0 <= oldidx < len(self.cutplan.plan):
|
268
|
+
self.list_operations.SetItemState(
|
269
|
+
oldidx, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED
|
270
|
+
)
|
271
|
+
self.on_listbox_operation_select(None)
|
272
|
+
|
273
|
+
# def on_listbox_operation_drag_drop(self, event):
|
274
|
+
# idx1 = 0
|
275
|
+
# idx2 = 0
|
276
|
+
# temp = self.cutplan.plan[idx1]
|
277
|
+
# self.cutplan.plan[idx1] = self.cutplan.plan[idx2]
|
278
|
+
# self.cutplan.plan[idx2] = temp
|
279
|
+
|
280
|
+
def on_text_operation_param(self, event):
|
281
|
+
content = self.text_operation_param.GetValue()
|
282
|
+
idx = self.list_operations.GetFirstSelected()
|
283
|
+
flag = False
|
284
|
+
if idx < 0:
|
285
|
+
return
|
286
|
+
op = self.cutplan.plan[idx]
|
287
|
+
if isinstance(op, ConsoleOperation):
|
288
|
+
op.command = content
|
289
|
+
elif isinstance(op, GotoOperation):
|
290
|
+
params = content.split(",")
|
291
|
+
x = 0
|
292
|
+
y = 0
|
293
|
+
if len(params) > 0:
|
294
|
+
try:
|
295
|
+
x = float(Length(params[0]))
|
296
|
+
except ValueError:
|
297
|
+
return
|
298
|
+
if len(params) > 1:
|
299
|
+
try:
|
300
|
+
y = float(Length(params[1]))
|
301
|
+
except ValueError:
|
302
|
+
return
|
303
|
+
op.x = x
|
304
|
+
op.y = y
|
305
|
+
elif isinstance(op, WaitOperation):
|
306
|
+
try:
|
307
|
+
duration = float(content)
|
308
|
+
op.wait = duration
|
309
|
+
except ValueError:
|
310
|
+
return
|
311
|
+
op.x = None
|
312
|
+
op.y = None
|
313
|
+
else:
|
314
|
+
return
|
315
|
+
self.context.signal("plan", self.plan_name, 1)
|
316
|
+
|
317
|
+
def on_listbox_operation_select(self, event):
|
318
|
+
flag = False
|
319
|
+
content = ""
|
320
|
+
idx = self.list_operations.GetFirstSelected()
|
321
|
+
cutcode = None
|
322
|
+
if idx >= 0:
|
323
|
+
op = self.cutplan.plan[idx]
|
324
|
+
if isinstance(op, ConsoleOperation):
|
325
|
+
content = op.command
|
326
|
+
flag = True
|
327
|
+
elif isinstance(op, GotoOperation):
|
328
|
+
content = str(op.x) + "," + str(op.y)
|
329
|
+
flag = True
|
330
|
+
elif isinstance(op, WaitOperation):
|
331
|
+
content = str(op.wait)
|
332
|
+
flag = True
|
333
|
+
elif isinstance(op, CutCode):
|
334
|
+
cutcode = op
|
335
|
+
self.parent.set_cutcode_entry(cutcode)
|
336
|
+
self.text_operation_param.SetValue(content)
|
337
|
+
self.text_operation_param.Enable(flag)
|
338
|
+
|
339
|
+
def on_listbox_operation_rightclick(self, event):
|
340
|
+
def remove_operation(event):
|
341
|
+
idx = self.list_operations.GetFirstSelected()
|
342
|
+
self.cutplan.plan.pop(idx)
|
343
|
+
self.context.signal("plan", self.plan_name, 1)
|
344
|
+
# self._refresh_simulated_plan()
|
345
|
+
|
346
|
+
def append_operation(operation):
|
347
|
+
def check(event):
|
348
|
+
self.cutplan.plan.append(my_operation)
|
349
|
+
self.context.signal("plan", self.plan_name, 1)
|
350
|
+
|
351
|
+
my_operation = operation
|
352
|
+
return check
|
353
|
+
|
354
|
+
def insert_operation(operation):
|
355
|
+
def check(event):
|
356
|
+
self.cutplan.plan.insert(
|
357
|
+
self.list_operations.GetFirstSelected(), my_operation
|
358
|
+
)
|
359
|
+
self.context.signal("plan", self.plan_name, 1)
|
360
|
+
|
361
|
+
my_operation = operation
|
362
|
+
return check
|
363
|
+
|
364
|
+
if self.list_operations.GetFirstSelected() < 0:
|
365
|
+
return
|
366
|
+
|
367
|
+
gui = self
|
368
|
+
|
369
|
+
menu = wx.Menu()
|
370
|
+
self.Bind(
|
371
|
+
wx.EVT_MENU,
|
372
|
+
remove_operation,
|
373
|
+
menu.Append(
|
374
|
+
wx.ID_ANY,
|
375
|
+
_("Remove operation"),
|
376
|
+
_("Removes the current operation from the active cutplan"),
|
377
|
+
),
|
378
|
+
)
|
379
|
+
standards = (
|
380
|
+
("Home", "util home", ""),
|
381
|
+
("Goto Origin", "util goto", "0,0"),
|
382
|
+
("Beep", "util console", "beep"),
|
383
|
+
("Interrupt", "util console", 'interrupt "Spooling was interrupted"'),
|
384
|
+
("Console", "util console", "echo 'Still burning'"),
|
385
|
+
("Wait", "util wait", "5"),
|
386
|
+
)
|
387
|
+
pre_items = []
|
388
|
+
for elem in standards:
|
389
|
+
desc = elem[0]
|
390
|
+
optype = elem[1]
|
391
|
+
opparam = elem[2]
|
392
|
+
|
393
|
+
if optype is not None:
|
394
|
+
addop = None
|
395
|
+
if optype == "util console":
|
396
|
+
addop = ConsoleOperation(command=opparam)
|
397
|
+
elif optype == "util home":
|
398
|
+
addop = HomeOperation()
|
399
|
+
# elif optype == "util output":
|
400
|
+
# if opparam is not None:
|
401
|
+
# params = opparam.split(",")
|
402
|
+
# mask = 0
|
403
|
+
# setvalue = 0
|
404
|
+
# if len(params) > 0:
|
405
|
+
# try:
|
406
|
+
# mask = int(params[0])
|
407
|
+
# except ValueError:
|
408
|
+
# mask = 0
|
409
|
+
# if len(params) > 1:
|
410
|
+
# try:
|
411
|
+
# setvalue = int(params[1])
|
412
|
+
# except ValueError:
|
413
|
+
# setvalue = 0
|
414
|
+
# if mask != 0 or setvalue != 0:
|
415
|
+
# addop = OutputOperation(mask, setvalue)
|
416
|
+
elif optype == "util goto":
|
417
|
+
if opparam is not None:
|
418
|
+
params = opparam.split(",")
|
419
|
+
x = 0
|
420
|
+
y = 0
|
421
|
+
if len(params) > 0:
|
422
|
+
try:
|
423
|
+
x = float(Length(params[0]))
|
424
|
+
except ValueError:
|
425
|
+
x = 0
|
426
|
+
if len(params) > 1:
|
427
|
+
try:
|
428
|
+
y = float(Length(params[1]))
|
429
|
+
except ValueError:
|
430
|
+
y = 0
|
431
|
+
addop = GotoOperation(x=x, y=y)
|
432
|
+
elif optype == "util wait":
|
433
|
+
if opparam is not None:
|
434
|
+
try:
|
435
|
+
opparam = float(opparam)
|
436
|
+
except ValueError:
|
437
|
+
opparam = None
|
438
|
+
if opparam is not None:
|
439
|
+
addop = WaitOperation(wait=opparam)
|
440
|
+
if addop is not None:
|
441
|
+
pre_items.append([desc, addop])
|
442
|
+
|
443
|
+
menu.AppendSeparator()
|
444
|
+
for entry in pre_items:
|
445
|
+
self.Bind(
|
446
|
+
wx.EVT_MENU,
|
447
|
+
insert_operation(entry[1]),
|
448
|
+
menu.Append(
|
449
|
+
wx.ID_ANY,
|
450
|
+
_("Insert '{operation}' before").format(operation=entry[0]),
|
451
|
+
_(
|
452
|
+
"Inserts this special operation before the current cutplan entry"
|
453
|
+
),
|
454
|
+
),
|
455
|
+
)
|
456
|
+
menu.AppendSeparator()
|
457
|
+
for entry in pre_items:
|
458
|
+
self.Bind(
|
459
|
+
wx.EVT_MENU,
|
460
|
+
append_operation(entry[1]),
|
461
|
+
menu.Append(
|
462
|
+
wx.ID_ANY,
|
463
|
+
_("Appends '{operation}' at end").format(operation=entry[0]),
|
464
|
+
_("Appends this special operation at the end of the cutplan"),
|
465
|
+
),
|
466
|
+
)
|
467
|
+
|
468
|
+
if menu.MenuItemCount != 0:
|
469
|
+
gui.PopupMenu(menu)
|
470
|
+
menu.Destroy()
|
471
|
+
|
472
|
+
|
473
|
+
class CutcodePanel(wx.Panel):
|
474
|
+
"""
|
475
|
+
CutcodePanel is a panel that display cutplan Cutcode and allows to edit them
|
476
|
+
"""
|
477
|
+
|
478
|
+
def __init__(self, *args, context=None, cutcode=None, plan_name=None, **kwds):
|
479
|
+
# begin wxGlade: PassesPanel.__init__
|
480
|
+
kwds["style"] = kwds.get("style", 0)
|
481
|
+
self.parent = args[0]
|
482
|
+
wx.Panel.__init__(self, *args, **kwds)
|
483
|
+
self.context = context
|
484
|
+
self.context.themes.set_window_colors(self)
|
485
|
+
|
486
|
+
self.cutcode = cutcode
|
487
|
+
self.plan_name = plan_name
|
488
|
+
self.list_cutcode = wxListBox(
|
489
|
+
self, wx.ID_ANY, choices=[], style=wx.LB_MULTIPLE
|
490
|
+
)
|
491
|
+
self.last_selected = []
|
492
|
+
self.display_highlighted_only = False
|
493
|
+
# self.text_operation_param = TextCtrl(
|
494
|
+
# self, wx.ID_ANY, "", style=wx.TE_PROCESS_ENTER
|
495
|
+
# )
|
496
|
+
# self.text_operation_param.SetToolTip(
|
497
|
+
# _("Modify operation parameter, press Enter to apply")
|
498
|
+
# )
|
499
|
+
|
500
|
+
self.list_cutcode.Bind(wx.EVT_RIGHT_DOWN, self.on_listbox_operation_rightclick)
|
501
|
+
# self.list_cutcode.Bind(wx.EVT_LISTBOX_DCLICK, self.on_listbox_operation_dclick)
|
502
|
+
self.list_cutcode.Bind(wx.EVT_LISTBOX, self.on_listbox_operation_select)
|
503
|
+
# self.text_operation_param.Bind(wx.EVT_TEXT_ENTER, self.on_text_operation_param)
|
504
|
+
|
505
|
+
ops_sizer = wx.BoxSizer(wx.VERTICAL)
|
506
|
+
ops_sizer.Add(self.list_cutcode, 1, wx.EXPAND, 0)
|
507
|
+
# ops_sizer.Add(self.text_operation_param, 0, wx.EXPAND, 0)
|
508
|
+
self.SetSizer(ops_sizer)
|
509
|
+
self.Layout()
|
510
|
+
self.set_cutcode_entry(self.cutcode, self.plan_name)
|
511
|
+
|
512
|
+
def set_cutcode_entry(self, cutcode, plan_name):
|
513
|
+
def name_str(e):
|
514
|
+
if isinstance(e, RasterCut):
|
515
|
+
res = f"Raster: {e.width} x {e.height}"
|
516
|
+
elif isinstance(e, CubicCut):
|
517
|
+
res = f"Cubic: ({e.start[0]:.0f}, {e.start[1]:.0f}) - ({e.end[0]:.0f}, {e.end[1]:.0f})"
|
518
|
+
res += f" (c1=({e.c1()[0]:.0f}, {e.c1()[1]:.0f}), c2=({e.c2()[0]:.0f}, {e.c2()[1]:.0f}) )"
|
519
|
+
elif isinstance(e, LineCut):
|
520
|
+
res = f"Line: ({e.start[0]:.0f}, {e.start[1]:.0f}) - ({e.end[0]:.0f}, {e.end[1]:.0f})"
|
521
|
+
elif isinstance(e, DwellCut):
|
522
|
+
res = f"Dwell: {e.dwell_time}"
|
523
|
+
elif isinstance(e, WaitCut):
|
524
|
+
res = f"Wait: {e.dwell_time}"
|
525
|
+
elif isinstance(e, HomeCut):
|
526
|
+
res = "Home"
|
527
|
+
elif isinstance(e, GotoCut):
|
528
|
+
coord = f"({e._start_x:.0f}, {e._start_y:.0f})"
|
529
|
+
res = f"Goto: ({e.start[0]:.0f}, {e.start[1]:.0f}) {coord}"
|
530
|
+
elif isinstance(e, InputCut):
|
531
|
+
res = f"Input: {e.input_value:b} (mask: {e.input_mask:b})"
|
532
|
+
elif isinstance(e, OutputCut):
|
533
|
+
res = f"Output: {e.output_value:b} (mask: {e.output_mask:b})"
|
534
|
+
elif isinstance(e, PlotCut):
|
535
|
+
res = f"Plot: {len(e)} points"
|
536
|
+
elif isinstance(e, QuadCut):
|
537
|
+
res = f"Quad: {e.start[0]:.0f}, {e.start[1]:.0f} - {e.end[0]:.0f}, {e.end[1]:.0f}"
|
538
|
+
res += f" (c={e.c()[0]:.0f}, {e.c()[1]:.0f})"
|
539
|
+
else:
|
540
|
+
try:
|
541
|
+
res = e.__name__
|
542
|
+
except AttributeError:
|
543
|
+
res = str(e)
|
544
|
+
if hasattr(e, "label") and getattr(e, "label") is not None:
|
545
|
+
res += f" ({getattr(e, 'label')})"
|
546
|
+
return res
|
547
|
+
|
548
|
+
self.cutcode = cutcode
|
549
|
+
# Reset highlighted flags
|
550
|
+
if cutcode is not None:
|
551
|
+
for cut in self.cutcode:
|
552
|
+
cut.highlighted = False
|
553
|
+
cut.visible = True
|
554
|
+
self.plan_name = plan_name
|
555
|
+
self.list_cutcode.Clear()
|
556
|
+
self.list_cutcode.Enable(True)
|
557
|
+
if self.cutcode is None:
|
558
|
+
self.list_cutcode.InsertItems(
|
559
|
+
[_("Please select a cutcode entry"), _("from the operations panel")], 0
|
560
|
+
)
|
561
|
+
self.list_cutcode.Enable(False)
|
562
|
+
elif len(self.cutcode) != 0:
|
563
|
+
self.list_cutcode.InsertItems([name_str(e) for e in self.cutcode], 0)
|
564
|
+
|
565
|
+
def on_listbox_operation_select(self, event):
|
566
|
+
if self.display_highlighted_only:
|
567
|
+
for cut in self.cutcode:
|
568
|
+
cut.visible = False
|
569
|
+
for cut in self.last_selected:
|
570
|
+
if cut < len(self.cutcode):
|
571
|
+
self.cutcode[cut].highlighted = False
|
572
|
+
self.last_selected = self.list_cutcode.GetSelections()
|
573
|
+
if self.last_selected is None:
|
574
|
+
self.last_selected = []
|
575
|
+
for cut in self.last_selected:
|
576
|
+
self.cutcode[cut].highlighted = True
|
577
|
+
self.cutcode[cut].visible = True
|
578
|
+
self.context.signal("refresh_simulation")
|
579
|
+
|
580
|
+
def on_listbox_operation_dclick(self, event):
|
581
|
+
# No useful logic yet, the old logic fails as it is opening a wrong node
|
582
|
+
# There are no property panels for CutObjects yet
|
583
|
+
return
|
584
|
+
|
585
|
+
def on_listbox_operation_rightclick(self, event):
|
586
|
+
def remove_operation(event):
|
587
|
+
selected = self.list_cutcode.GetSelections()
|
588
|
+
if selected is None or len(selected) == 0:
|
589
|
+
return
|
590
|
+
#
|
591
|
+
idx = len(selected) - 1
|
592
|
+
while idx >= 0:
|
593
|
+
entry = selected[idx]
|
594
|
+
self.cutcode.pop(entry)
|
595
|
+
idx -= 1
|
596
|
+
self.context.signal("plan", self.plan_name, 1)
|
597
|
+
|
598
|
+
def display_selected(event):
|
599
|
+
self.display_highlighted_only = not self.display_highlighted_only
|
600
|
+
if self.display_highlighted_only:
|
601
|
+
for cut in self.cutcode:
|
602
|
+
cut.visible = False
|
603
|
+
for cut in self.last_selected:
|
604
|
+
self.cutcode[cut].visible = True
|
605
|
+
else:
|
606
|
+
for cut in self.cutcode:
|
607
|
+
cut.visible = True
|
608
|
+
self.context.signal("refresh_simulation")
|
609
|
+
|
610
|
+
def remove_before(event):
|
611
|
+
selected = self.list_cutcode.GetSelections()
|
612
|
+
if selected is None or len(selected) == 0:
|
613
|
+
return
|
614
|
+
#
|
615
|
+
entry = selected[0]
|
616
|
+
if entry > 0:
|
617
|
+
del self.cutcode[:entry]
|
618
|
+
self.context.signal("plan", self.plan_name, 1)
|
619
|
+
|
620
|
+
def remove_after(event):
|
621
|
+
selected = self.list_cutcode.GetSelections()
|
622
|
+
if selected is None or len(selected) == 0:
|
623
|
+
return
|
624
|
+
#
|
625
|
+
entry = selected[-1]
|
626
|
+
if entry < len(self.cutcode) - 1:
|
627
|
+
del self.cutcode[entry + 1 :]
|
628
|
+
self.context.signal("plan", self.plan_name, 1)
|
629
|
+
|
630
|
+
def append_operation(cutcode):
|
631
|
+
def check(event):
|
632
|
+
self.cutcode.append(my_cutcode)
|
633
|
+
self.context.signal("plan", self.plan_name, 1)
|
634
|
+
|
635
|
+
my_cutcode = cutcode
|
636
|
+
return check
|
637
|
+
|
638
|
+
def insert_operation(cutcode):
|
639
|
+
def check(event):
|
640
|
+
selected = self.list_cutcode.GetSelections()
|
641
|
+
if selected is None or len(selected) == 0:
|
642
|
+
return
|
643
|
+
idx = selected[0]
|
644
|
+
self.cutcode.insert(idx, my_cutcode)
|
645
|
+
self.context.signal("plan", self.plan_name, 1)
|
646
|
+
|
647
|
+
my_cutcode = cutcode
|
648
|
+
return check
|
649
|
+
|
650
|
+
selected = self.list_cutcode.GetSelections()
|
651
|
+
if selected is None or len(selected) == 0:
|
652
|
+
return
|
653
|
+
sel_min = selected[0]
|
654
|
+
sel_max = selected[-1]
|
655
|
+
gui = self
|
656
|
+
|
657
|
+
menu = wx.Menu()
|
658
|
+
self.Bind(
|
659
|
+
wx.EVT_MENU,
|
660
|
+
remove_operation,
|
661
|
+
menu.Append(
|
662
|
+
wx.ID_ANY,
|
663
|
+
_("Remove cutcode"),
|
664
|
+
_("Removes the selected cutcode-entries from the active cutplan"),
|
665
|
+
),
|
666
|
+
)
|
667
|
+
if sel_min > 0:
|
668
|
+
self.Bind(
|
669
|
+
wx.EVT_MENU,
|
670
|
+
remove_before,
|
671
|
+
menu.Append(
|
672
|
+
wx.ID_ANY,
|
673
|
+
_("Delete cuts before"),
|
674
|
+
_("Delete all cuts before the first selected"),
|
675
|
+
),
|
676
|
+
)
|
677
|
+
if sel_max < len(self.cutcode) - 1:
|
678
|
+
self.Bind(
|
679
|
+
wx.EVT_MENU,
|
680
|
+
remove_after,
|
681
|
+
menu.Append(
|
682
|
+
wx.ID_ANY,
|
683
|
+
_("Delete cuts after"),
|
684
|
+
_("Delete all cuts after the last selected"),
|
685
|
+
),
|
686
|
+
)
|
687
|
+
standards = (
|
688
|
+
("Home", "home", ""),
|
689
|
+
("Goto Origin", "goto", "0,0"),
|
690
|
+
# ("Info", "info", "Still burning"),
|
691
|
+
("Wait", "wait", "5"),
|
692
|
+
)
|
693
|
+
pre_items = []
|
694
|
+
for elem in standards:
|
695
|
+
desc = elem[0]
|
696
|
+
optype = elem[1]
|
697
|
+
opparam = elem[2]
|
698
|
+
|
699
|
+
if optype is not None:
|
700
|
+
addop = None
|
701
|
+
if optype == "console":
|
702
|
+
# addop = ConsoleOperation(command=opparam)
|
703
|
+
pass
|
704
|
+
# elif optype == "info":
|
705
|
+
# addop = InfoCut(message=opparam)
|
706
|
+
elif optype == "home":
|
707
|
+
addop = HomeCut()
|
708
|
+
# elif optype == "util output":
|
709
|
+
# if opparam is not None:
|
710
|
+
# params = opparam.split(",")
|
711
|
+
# mask = 0
|
712
|
+
# setvalue = 0
|
713
|
+
# if len(params) > 0:
|
714
|
+
# try:
|
715
|
+
# mask = int(params[0])
|
716
|
+
# except ValueError:
|
717
|
+
# mask = 0
|
718
|
+
# if len(params) > 1:
|
719
|
+
# try:
|
720
|
+
# setvalue = int(params[1])
|
721
|
+
# except ValueError:
|
722
|
+
# setvalue = 0
|
723
|
+
# if mask != 0 or setvalue != 0:
|
724
|
+
# addop = OutputOperation(mask, setvalue)
|
725
|
+
elif optype == "goto":
|
726
|
+
if opparam is not None:
|
727
|
+
params = opparam.split(",")
|
728
|
+
x = 0
|
729
|
+
y = 0
|
730
|
+
if len(params) > 0:
|
731
|
+
try:
|
732
|
+
x = float(Length(params[0]))
|
733
|
+
except ValueError:
|
734
|
+
x = 0
|
735
|
+
if len(params) > 1:
|
736
|
+
try:
|
737
|
+
y = float(Length(params[1]))
|
738
|
+
except ValueError:
|
739
|
+
y = 0
|
740
|
+
addop = GotoCut((x, y))
|
741
|
+
elif optype == "wait":
|
742
|
+
if opparam is not None:
|
743
|
+
try:
|
744
|
+
opparam = float(opparam)
|
745
|
+
except ValueError:
|
746
|
+
opparam = None
|
747
|
+
if opparam is not None:
|
748
|
+
addop = WaitCut(wait=1000 * opparam)
|
749
|
+
if addop is not None:
|
750
|
+
pre_items.append([desc, addop])
|
751
|
+
|
752
|
+
menu.AppendSeparator()
|
753
|
+
for entry in pre_items:
|
754
|
+
self.Bind(
|
755
|
+
wx.EVT_MENU,
|
756
|
+
insert_operation(entry[1]),
|
757
|
+
menu.Append(
|
758
|
+
wx.ID_ANY,
|
759
|
+
_("Insert '{operation}' before").format(operation=entry[0]),
|
760
|
+
_(
|
761
|
+
"Inserts this special operation before the current cutplan entry"
|
762
|
+
),
|
763
|
+
),
|
764
|
+
)
|
765
|
+
menu.AppendSeparator()
|
766
|
+
for entry in pre_items:
|
767
|
+
self.Bind(
|
768
|
+
wx.EVT_MENU,
|
769
|
+
append_operation(entry[1]),
|
770
|
+
menu.Append(
|
771
|
+
wx.ID_ANY,
|
772
|
+
_("Appends '{operation}' at end").format(operation=entry[0]),
|
773
|
+
_("Appends this special operation at the end of the cutplan"),
|
774
|
+
),
|
775
|
+
)
|
776
|
+
|
777
|
+
menu.AppendSeparator()
|
778
|
+
item = menu.AppendCheckItem(
|
779
|
+
wx.ID_ANY,
|
780
|
+
_("Only show highlighted cutcode items"),
|
781
|
+
)
|
782
|
+
self.Bind(wx.EVT_MENU, display_selected, item)
|
783
|
+
item.Check(self.display_highlighted_only)
|
784
|
+
|
785
|
+
if menu.MenuItemCount != 0:
|
786
|
+
gui.PopupMenu(menu)
|
787
|
+
menu.Destroy()
|
788
|
+
|
789
|
+
|
790
|
+
class SimulationPanel(wx.Panel, Job):
|
791
|
+
def __init__(
|
792
|
+
self,
|
793
|
+
*args,
|
794
|
+
context=None,
|
795
|
+
plan_name=None,
|
796
|
+
auto_clear=True,
|
797
|
+
optimise_at_start=True,
|
798
|
+
**kwds,
|
799
|
+
):
|
800
|
+
kwds["style"] = kwds.get("style", 0) | wx.TAB_TRAVERSAL
|
801
|
+
wx.Panel.__init__(self, *args, **kwds)
|
802
|
+
self.parent = args[0]
|
803
|
+
self.context = context
|
804
|
+
self.context.themes.set_window_colors(self)
|
805
|
+
self.SetHelpText("simulate")
|
806
|
+
|
807
|
+
self.retries = 0
|
808
|
+
self.plan_name = plan_name
|
809
|
+
self.auto_clear = auto_clear
|
810
|
+
# Display travel paths?
|
811
|
+
self.display_travel = self.context.setting(bool, "display_travel", True)
|
812
|
+
self.raster_as_image = self.context.setting(bool, "raster_as_image", True)
|
813
|
+
self.laserspot_display = self.context.setting(bool, "laserspot_display", True)
|
814
|
+
self.laserspot_width = None
|
815
|
+
self.calc_laser_spot_width()
|
816
|
+
|
817
|
+
Job.__init__(self)
|
818
|
+
self._playback_cuts = True
|
819
|
+
self._cut_end_time = []
|
820
|
+
|
821
|
+
self.update_job = Job(
|
822
|
+
process=self.cache_updater,
|
823
|
+
job_name="cache_updater",
|
824
|
+
interval=0.25,
|
825
|
+
times=1,
|
826
|
+
run_main=True,
|
827
|
+
)
|
828
|
+
|
829
|
+
self.job_name = "simulate"
|
830
|
+
self.run_main = True
|
831
|
+
self.process = self.animate_sim
|
832
|
+
self.interval = 0.1
|
833
|
+
if plan_name:
|
834
|
+
self.cutplan = self.context.planner.get_or_make_plan(plan_name)
|
835
|
+
else:
|
836
|
+
self.cutplan = self.context.planner.default_plan
|
837
|
+
self.plan_name = self.cutplan.name
|
838
|
+
self.operations = self.cutplan.plan
|
839
|
+
# for e in self.operations:
|
840
|
+
# print(f"Init: {type(e).__name__} {e}")
|
841
|
+
self.cutcode = CutCode()
|
842
|
+
|
843
|
+
for c in self.operations:
|
844
|
+
if isinstance(c, CutCode):
|
845
|
+
self.cutcode.extend(c)
|
846
|
+
self.cutcode = CutCode(self.cutcode.flat())
|
847
|
+
|
848
|
+
self.statistics = self.cutcode.provide_statistics()
|
849
|
+
|
850
|
+
self.max = max(len(self.cutcode), 0) + 1
|
851
|
+
self.progress = self.max
|
852
|
+
self.view_pane = ScenePanel(
|
853
|
+
self.context,
|
854
|
+
self,
|
855
|
+
scene_name="SimScene",
|
856
|
+
style=wx.EXPAND,
|
857
|
+
)
|
858
|
+
self.view_pane.start_scene()
|
859
|
+
self.view_pane.SetCanFocus(False)
|
860
|
+
self.widget_scene = self.view_pane.scene
|
861
|
+
# poor mans slide out
|
862
|
+
self.btn_slide_options = wxButton(self, wx.ID_ANY, "<")
|
863
|
+
self.btn_slide_options.Bind(wx.EVT_BUTTON, self.slide_out)
|
864
|
+
self.btn_slide_options.SetToolTip(
|
865
|
+
_("Show/Hide optimization options for this job.")
|
866
|
+
)
|
867
|
+
from copy import copy
|
868
|
+
|
869
|
+
prechoices = copy(context.lookup("choices/optimize"))
|
870
|
+
choices = list(map(copy, prechoices))
|
871
|
+
# Clear the page-entry
|
872
|
+
for entry in choices:
|
873
|
+
entry["page"] = ""
|
874
|
+
self.subpanel_optimize = wx.Panel(self, wx.ID_ANY)
|
875
|
+
self.options_optimize = ChoicePropertyPanel(
|
876
|
+
self, wx.ID_ANY, context=self.context, choices=choices, scrolling=False
|
877
|
+
)
|
878
|
+
self.options_optimize.SetupScrolling()
|
879
|
+
self.subpanel_operations = OperationsPanel(
|
880
|
+
self, wx.ID_ANY, context=self.context, cutplan=self.cutplan
|
881
|
+
)
|
882
|
+
self.subpanel_cutcode = CutcodePanel(
|
883
|
+
self, wx.ID_ANY, context=self.context, cutcode=None, plan_name=None
|
884
|
+
)
|
885
|
+
|
886
|
+
self.panel_optimize = wx.Notebook(self, wx.ID_ANY)
|
887
|
+
self.context.themes.set_window_colors(self.panel_optimize)
|
888
|
+
|
889
|
+
self.subpanel_optimize.Reparent(self.panel_optimize)
|
890
|
+
self.subpanel_operations.Reparent(self.panel_optimize)
|
891
|
+
self.subpanel_cutcode.Reparent(self.panel_optimize)
|
892
|
+
self.panel_optimize.AddPage(self.subpanel_optimize, _("Optimizations"))
|
893
|
+
self.panel_optimize.AddPage(self.subpanel_operations, _("Operations"))
|
894
|
+
self.panel_optimize.AddPage(self.subpanel_cutcode, _("Cutcode"))
|
895
|
+
self.checkbox_optimize = wxCheckBox(self, wx.ID_ANY, _("Optimize"))
|
896
|
+
self.checkbox_optimize.SetToolTip(_("Enable/Disable Optimize"))
|
897
|
+
self.checkbox_optimize.SetValue(self.context.planner.do_optimization)
|
898
|
+
self.btn_redo_it = wxButton(self, wx.ID_ANY, _("Recalculate"))
|
899
|
+
self.btn_redo_it.Bind(wx.EVT_BUTTON, self.on_redo_it)
|
900
|
+
self.btn_redo_it.SetToolTip(_("Apply the settings and recalculate the cutplan"))
|
901
|
+
|
902
|
+
self.slider_progress = wx.Slider(self, wx.ID_ANY, self.max, 0, self.max)
|
903
|
+
self.slider_progress.SetFocus()
|
904
|
+
self.text_distance_laser = TextCtrl(
|
905
|
+
self, wx.ID_ANY, "", style=wx.TE_READONLY
|
906
|
+
)
|
907
|
+
self.text_distance_travel = TextCtrl(
|
908
|
+
self, wx.ID_ANY, "", style=wx.TE_READONLY
|
909
|
+
)
|
910
|
+
self.text_distance_total = TextCtrl(
|
911
|
+
self, wx.ID_ANY, "", style=wx.TE_READONLY
|
912
|
+
)
|
913
|
+
self.text_time_laser = TextCtrl(self, wx.ID_ANY, "", style=wx.TE_READONLY)
|
914
|
+
self.text_time_travel = TextCtrl(self, wx.ID_ANY, "", style=wx.TE_READONLY)
|
915
|
+
self.text_time_extra = TextCtrl(self, wx.ID_ANY, "", style=wx.TE_READONLY)
|
916
|
+
self.text_time_total = TextCtrl(self, wx.ID_ANY, "", style=wx.TE_READONLY)
|
917
|
+
self.text_distance_laser_step = TextCtrl(
|
918
|
+
self, wx.ID_ANY, "", style=wx.TE_READONLY
|
919
|
+
)
|
920
|
+
self.text_distance_travel_step = TextCtrl(
|
921
|
+
self, wx.ID_ANY, "", style=wx.TE_READONLY
|
922
|
+
)
|
923
|
+
self.text_distance_total_step = TextCtrl(
|
924
|
+
self, wx.ID_ANY, "", style=wx.TE_READONLY
|
925
|
+
)
|
926
|
+
self.text_time_laser_step = TextCtrl(
|
927
|
+
self, wx.ID_ANY, "", style=wx.TE_READONLY
|
928
|
+
)
|
929
|
+
self.text_time_travel_step = TextCtrl(
|
930
|
+
self, wx.ID_ANY, "", style=wx.TE_READONLY
|
931
|
+
)
|
932
|
+
self.text_time_extra_step = TextCtrl(
|
933
|
+
self, wx.ID_ANY, "", style=wx.TE_READONLY
|
934
|
+
)
|
935
|
+
self.text_time_total_step = TextCtrl(
|
936
|
+
self, wx.ID_ANY, "", style=wx.TE_READONLY
|
937
|
+
)
|
938
|
+
self.button_play = wxButton(self, wx.ID_ANY, "")
|
939
|
+
self.button_play.SetToolTip(_("Start the simulation replay"))
|
940
|
+
self.slider_playbackspeed = wx.Slider(self, wx.ID_ANY, 180, 0, 310)
|
941
|
+
self.slider_playbackspeed.SetToolTip(_("Set the speed for the simulation"))
|
942
|
+
self.text_playback_speed = TextCtrl(
|
943
|
+
self, wx.ID_ANY, "100%", style=wx.TE_READONLY
|
944
|
+
)
|
945
|
+
self.radio_cut = wx.RadioButton(self, wx.ID_ANY, _("Steps"))
|
946
|
+
self.radio_time_seconds = wx.RadioButton(self, wx.ID_ANY, _("Time (sec.)"))
|
947
|
+
self.radio_time_minutes = wx.RadioButton(self, wx.ID_ANY, _("Time (min)"))
|
948
|
+
self.radio_cut.SetToolTip(
|
949
|
+
_(
|
950
|
+
"Cut operations Playback-Mode: play will jump from one completed operations to next"
|
951
|
+
)
|
952
|
+
)
|
953
|
+
self.radio_time_seconds.SetToolTip(
|
954
|
+
_("Timed Playback-Mode: play will jump from one second to next")
|
955
|
+
)
|
956
|
+
self.radio_time_minutes.SetToolTip(
|
957
|
+
_("Timed Playback-Mode: play will jump from one minute to next")
|
958
|
+
)
|
959
|
+
|
960
|
+
self.button_spool = wxButton(self, wx.ID_ANY, _("Send to Laser"))
|
961
|
+
self.button_spool.SetToolTip(_("Send the current cutplan to the laser."))
|
962
|
+
self._slided_in = None
|
963
|
+
|
964
|
+
self.__set_properties()
|
965
|
+
self.__do_layout()
|
966
|
+
|
967
|
+
self.matrix = Matrix()
|
968
|
+
|
969
|
+
self.previous_window_position = None
|
970
|
+
self.previous_scene_position = None
|
971
|
+
self._Buffer = None
|
972
|
+
|
973
|
+
self.Bind(wx.EVT_SLIDER, self.on_slider_progress, self.slider_progress)
|
974
|
+
self.Bind(wx.EVT_BUTTON, self.on_button_play, self.button_play)
|
975
|
+
self.Bind(wx.EVT_SLIDER, self.on_slider_playback, self.slider_playbackspeed)
|
976
|
+
# self.Bind(wx.EVT_COMBOBOX, self.on_combo_device, self.combo_device)
|
977
|
+
self.Bind(wx.EVT_BUTTON, self.on_button_spool, self.button_spool)
|
978
|
+
self.Bind(wx.EVT_RIGHT_DOWN, self.on_mouse_right_down)
|
979
|
+
self.Bind(wx.EVT_RADIOBUTTON, self.on_radio_playback_mode, self.radio_cut)
|
980
|
+
self.Bind(
|
981
|
+
wx.EVT_RADIOBUTTON, self.on_radio_playback_mode, self.radio_time_seconds
|
982
|
+
)
|
983
|
+
self.Bind(
|
984
|
+
wx.EVT_RADIOBUTTON, self.on_radio_playback_mode, self.radio_time_minutes
|
985
|
+
)
|
986
|
+
self.view_pane.scene_panel.Bind(wx.EVT_RIGHT_DOWN, self.on_mouse_right_down)
|
987
|
+
|
988
|
+
# end wxGlade
|
989
|
+
self.Bind(wx.EVT_CHECKBOX, self.on_checkbox_optimize, self.checkbox_optimize)
|
990
|
+
self.on_checkbox_optimize(None)
|
991
|
+
|
992
|
+
self.Bind(wx.EVT_SIZE, self.on_size)
|
993
|
+
|
994
|
+
##############
|
995
|
+
# BUILD SCENE
|
996
|
+
##############
|
997
|
+
|
998
|
+
self.sim_cutcode = SimulationWidget(self.widget_scene, self)
|
999
|
+
self.sim_cutcode.raster_as_image = self.raster_as_image
|
1000
|
+
self.sim_cutcode.laserspot_width = self.laserspot_width
|
1001
|
+
self.widget_scene.add_scenewidget(self.sim_cutcode)
|
1002
|
+
self.sim_travel = SimulationTravelWidget(self.widget_scene, self)
|
1003
|
+
self.sim_travel.display = self.display_travel
|
1004
|
+
self.widget_scene.add_scenewidget(self.sim_travel)
|
1005
|
+
|
1006
|
+
self.grid = GridWidget(
|
1007
|
+
self.widget_scene, name="Simulation", suppress_labels=True
|
1008
|
+
)
|
1009
|
+
# Don't let grid resize itself
|
1010
|
+
self.grid.auto_tick = False
|
1011
|
+
if self.context.units_name == "mm":
|
1012
|
+
self.grid.tick_distance = 10 # mm
|
1013
|
+
elif self.context.units_name == "cm":
|
1014
|
+
self.grid.tick_distance = 1
|
1015
|
+
elif self.context.units_name == "inch":
|
1016
|
+
self.grid.tick_distance = 0.5
|
1017
|
+
elif self.context.units_name == "mil":
|
1018
|
+
self.grid.tick_distance = 500
|
1019
|
+
self.widget_scene.add_scenewidget(self.grid)
|
1020
|
+
self.widget_scene.add_scenewidget(
|
1021
|
+
BedWidget(self.widget_scene, name="Simulation")
|
1022
|
+
)
|
1023
|
+
self.widget_scene.add_interfacewidget(SimReticleWidget(self.widget_scene, self))
|
1024
|
+
self.parent.add_module_delegate(self.options_optimize)
|
1025
|
+
self.context.setting(int, "simulation_mode", 0)
|
1026
|
+
default = self.context.simulation_mode
|
1027
|
+
if default == 0:
|
1028
|
+
self.radio_cut.SetValue(True)
|
1029
|
+
elif default == 1:
|
1030
|
+
self.radio_time_seconds.SetValue(True)
|
1031
|
+
elif default == 2:
|
1032
|
+
self.radio_time_minutes.SetValue(True)
|
1033
|
+
self.on_radio_playback_mode(None)
|
1034
|
+
# Allow Scene update from now on (are suppressed by default during startup phase)
|
1035
|
+
self.widget_scene.suppress_changes = False
|
1036
|
+
# self.Show()
|
1037
|
+
self.running = False
|
1038
|
+
self.slided_in = True
|
1039
|
+
self.start_time = perf_counter()
|
1040
|
+
self.debug(f"Init done: {perf_counter()-self.start_time}")
|
1041
|
+
|
1042
|
+
def reload_statistics(self):
|
1043
|
+
try:
|
1044
|
+
self.statistics = self.cutcode.provide_statistics()
|
1045
|
+
self._set_slider_dimensions()
|
1046
|
+
self.sim_travel.initvars()
|
1047
|
+
self.update_fields()
|
1048
|
+
except RuntimeError:
|
1049
|
+
# Was already deleted
|
1050
|
+
pass
|
1051
|
+
|
1052
|
+
def debug(self, message):
|
1053
|
+
# print (message)
|
1054
|
+
return
|
1055
|
+
|
1056
|
+
def _startup(self):
|
1057
|
+
self.debug(f"Startup: {perf_counter()-self.start_time}")
|
1058
|
+
self.slided_in = True
|
1059
|
+
self.fit_scene_to_panel()
|
1060
|
+
|
1061
|
+
def __set_properties(self):
|
1062
|
+
self.text_distance_laser.SetToolTip(_("Distance Estimate: while Lasering"))
|
1063
|
+
self.text_distance_travel.SetToolTip(_("Distance Estimate: Traveling"))
|
1064
|
+
self.text_distance_total.SetToolTip(_("Distance Estimate: Total"))
|
1065
|
+
self.text_time_laser.SetToolTip(_("Time Estimate: Lasering Time"))
|
1066
|
+
self.text_time_travel.SetToolTip(_("Time Estimate: Traveling Time"))
|
1067
|
+
self.text_time_extra.SetToolTip(
|
1068
|
+
_("Time Estimate: Extra Time (i.e. to swing around)")
|
1069
|
+
)
|
1070
|
+
self.text_time_total.SetToolTip(_("Time Estimate: Total Time"))
|
1071
|
+
self.button_play.SetBitmap(
|
1072
|
+
icons8_circled_play.GetBitmap(resize=get_default_icon_size(self.context))
|
1073
|
+
)
|
1074
|
+
self.text_playback_speed.SetMinSize(dip_size(self, 55, 23))
|
1075
|
+
# self.combo_device.SetToolTip(_("Select the device"))
|
1076
|
+
self.button_spool.SetFont(
|
1077
|
+
wx.Font(
|
1078
|
+
18,
|
1079
|
+
wx.FONTFAMILY_DEFAULT,
|
1080
|
+
wx.FONTSTYLE_NORMAL,
|
1081
|
+
wx.FONTWEIGHT_NORMAL,
|
1082
|
+
0,
|
1083
|
+
"Segoe UI",
|
1084
|
+
)
|
1085
|
+
)
|
1086
|
+
self.button_spool.SetBitmap(
|
1087
|
+
icons8_route.GetBitmap(resize=1.5 * get_default_icon_size(self.context))
|
1088
|
+
)
|
1089
|
+
# end wxGlade
|
1090
|
+
|
1091
|
+
def __do_layout(self):
|
1092
|
+
# begin wxGlade: Simulation.__do_layout
|
1093
|
+
self.text_distance_laser.SetMinSize(dip_size(self, 35, -1))
|
1094
|
+
self.text_distance_laser_step.SetMinSize(dip_size(self, 35, -1))
|
1095
|
+
self.text_distance_total.SetMinSize(dip_size(self, 35, -1))
|
1096
|
+
self.text_distance_total_step.SetMinSize(dip_size(self, 35, -1))
|
1097
|
+
self.text_distance_travel.SetMinSize(dip_size(self, 35, -1))
|
1098
|
+
self.text_distance_travel_step.SetMinSize(dip_size(self, 35, -1))
|
1099
|
+
self.text_time_laser.SetMinSize(dip_size(self, 35, -1))
|
1100
|
+
self.text_time_laser_step.SetMinSize(dip_size(self, 35, -1))
|
1101
|
+
self.text_time_total.SetMinSize(dip_size(self, 35, -1))
|
1102
|
+
self.text_time_total_step.SetMinSize(dip_size(self, 35, -1))
|
1103
|
+
self.text_time_travel.SetMinSize(dip_size(self, 35, -1))
|
1104
|
+
self.text_time_travel_step.SetMinSize(dip_size(self, 35, -1))
|
1105
|
+
self.text_time_extra.SetMinSize(dip_size(self, 35, -1))
|
1106
|
+
self.text_time_extra_step.SetMinSize(dip_size(self, 35, -1))
|
1107
|
+
v_sizer_main = wx.BoxSizer(wx.VERTICAL)
|
1108
|
+
h_sizer_scroll = wx.BoxSizer(wx.HORIZONTAL)
|
1109
|
+
h_sizer_text_1 = wx.BoxSizer(wx.HORIZONTAL)
|
1110
|
+
h_sizer_text_2 = wx.BoxSizer(wx.HORIZONTAL)
|
1111
|
+
h_sizer_buttons = wx.BoxSizer(wx.HORIZONTAL)
|
1112
|
+
|
1113
|
+
sizer_execute = wx.BoxSizer(wx.VERTICAL)
|
1114
|
+
sizer_speed_options = wx.BoxSizer(wx.VERTICAL)
|
1115
|
+
sizer_pb_speed = wx.BoxSizer(wx.HORIZONTAL)
|
1116
|
+
sizer_total_time = StaticBoxSizer(
|
1117
|
+
self, wx.ID_ANY, _("Total Time"), wx.HORIZONTAL
|
1118
|
+
)
|
1119
|
+
sizer_travel_time = StaticBoxSizer(
|
1120
|
+
self, wx.ID_ANY, _("Travel Time"), wx.HORIZONTAL
|
1121
|
+
)
|
1122
|
+
sizer_laser_time = StaticBoxSizer(
|
1123
|
+
self, wx.ID_ANY, _("Laser Time"), wx.HORIZONTAL
|
1124
|
+
)
|
1125
|
+
sizer_extra_time = StaticBoxSizer(
|
1126
|
+
self, wx.ID_ANY, _("Extra Time"), wx.HORIZONTAL
|
1127
|
+
)
|
1128
|
+
sizer_total_distance = StaticBoxSizer(
|
1129
|
+
self, wx.ID_ANY, _("Total Distance"), wx.HORIZONTAL
|
1130
|
+
)
|
1131
|
+
sizer_travel_distance = StaticBoxSizer(
|
1132
|
+
self, wx.ID_ANY, _("Travel Distance"), wx.HORIZONTAL
|
1133
|
+
)
|
1134
|
+
sizer_laser_distance = StaticBoxSizer(
|
1135
|
+
self, wx.ID_ANY, _("Laser Distance"), wx.HORIZONTAL
|
1136
|
+
)
|
1137
|
+
# +--------+---+-------+
|
1138
|
+
# | P | | Optim |
|
1139
|
+
# | R | | |
|
1140
|
+
# | E | |Options|
|
1141
|
+
# | V | > | |
|
1142
|
+
# | I | | |
|
1143
|
+
# | E | +-------+
|
1144
|
+
# | W | |Refresh|
|
1145
|
+
# +--------+---+-------+
|
1146
|
+
|
1147
|
+
opt_sizer = wx.BoxSizer(wx.VERTICAL)
|
1148
|
+
self.options_optimize.Reparent(self.subpanel_optimize)
|
1149
|
+
self.checkbox_optimize.Reparent(self.subpanel_optimize)
|
1150
|
+
self.btn_redo_it.Reparent(self.subpanel_optimize)
|
1151
|
+
|
1152
|
+
self.checkbox_optimize.SetMinSize(dip_size(self, -1, 23))
|
1153
|
+
opt_sizer.Add(self.options_optimize, 1, wx.EXPAND, 0)
|
1154
|
+
opt_sizer.Add(self.checkbox_optimize, 0, wx.EXPAND, 0)
|
1155
|
+
opt_sizer.Add(self.btn_redo_it, 0, wx.EXPAND, 0)
|
1156
|
+
self.subpanel_optimize.SetSizer(opt_sizer)
|
1157
|
+
self.subpanel_optimize.Layout()
|
1158
|
+
|
1159
|
+
# Linux requires a minimum height / width to display a text inside a button
|
1160
|
+
system = platform.system()
|
1161
|
+
if system == "Darwin":
|
1162
|
+
mysize = 40
|
1163
|
+
elif system == "Windows":
|
1164
|
+
mysize = 23
|
1165
|
+
elif system == "Linux":
|
1166
|
+
mysize = 40
|
1167
|
+
else:
|
1168
|
+
mysize = 20
|
1169
|
+
self.btn_slide_options.SetMinSize(dip_size(self, mysize, -1))
|
1170
|
+
self.voption_sizer = wx.BoxSizer(wx.VERTICAL)
|
1171
|
+
self.voption_sizer.Add(self.panel_optimize, 1, wx.EXPAND, 0)
|
1172
|
+
|
1173
|
+
self.hscene_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
1174
|
+
self.hscene_sizer.Add(self.view_pane, 2, wx.EXPAND, 0)
|
1175
|
+
self.hscene_sizer.Add(self.btn_slide_options, 0, wx.EXPAND, 0)
|
1176
|
+
self.hscene_sizer.Add(self.voption_sizer, 1, wx.EXPAND, 0)
|
1177
|
+
|
1178
|
+
h_sizer_scroll.Add(self.slider_progress, 1, wx.EXPAND, 0)
|
1179
|
+
|
1180
|
+
sizer_laser_distance.Add(self.text_distance_laser_step, 1, wx.EXPAND, 0)
|
1181
|
+
sizer_laser_distance.Add(self.text_distance_laser, 1, wx.EXPAND, 0)
|
1182
|
+
h_sizer_text_1.Add(sizer_laser_distance, 1, wx.EXPAND, 0)
|
1183
|
+
|
1184
|
+
sizer_travel_distance.Add(self.text_distance_travel_step, 1, wx.EXPAND, 0)
|
1185
|
+
sizer_travel_distance.Add(self.text_distance_travel, 1, wx.EXPAND, 0)
|
1186
|
+
h_sizer_text_1.Add(sizer_travel_distance, 1, wx.EXPAND, 0)
|
1187
|
+
|
1188
|
+
sizer_total_distance.Add(self.text_distance_total_step, 1, wx.EXPAND, 0)
|
1189
|
+
sizer_total_distance.Add(self.text_distance_total, 1, wx.EXPAND, 0)
|
1190
|
+
h_sizer_text_1.Add(sizer_total_distance, 1, wx.EXPAND, 0)
|
1191
|
+
|
1192
|
+
sizer_laser_time.Add(self.text_time_laser_step, 1, wx.EXPAND, 0)
|
1193
|
+
sizer_laser_time.Add(self.text_time_laser, 1, wx.EXPAND, 0)
|
1194
|
+
h_sizer_text_2.Add(sizer_laser_time, 1, wx.EXPAND, 0)
|
1195
|
+
|
1196
|
+
sizer_travel_time.Add(self.text_time_travel_step, 1, wx.EXPAND, 0)
|
1197
|
+
sizer_travel_time.Add(self.text_time_travel, 1, wx.EXPAND, 0)
|
1198
|
+
h_sizer_text_2.Add(sizer_travel_time, 1, wx.EXPAND, 0)
|
1199
|
+
|
1200
|
+
sizer_total_time.Add(self.text_time_total_step, 1, wx.EXPAND, 0)
|
1201
|
+
sizer_total_time.Add(self.text_time_total, 1, wx.EXPAND, 0)
|
1202
|
+
h_sizer_text_2.Add(sizer_total_time, 1, wx.EXPAND, 0)
|
1203
|
+
|
1204
|
+
sizer_extra_time.Add(self.text_time_extra_step, 1, wx.EXPAND, 0)
|
1205
|
+
sizer_extra_time.Add(self.text_time_extra, 1, wx.EXPAND, 0)
|
1206
|
+
h_sizer_text_2.Add(sizer_extra_time, 1, wx.EXPAND, 0)
|
1207
|
+
|
1208
|
+
h_sizer_buttons.Add(self.button_play, 0, 0, 0)
|
1209
|
+
sizer_speed_options.Add(self.slider_playbackspeed, 0, wx.EXPAND, 0)
|
1210
|
+
|
1211
|
+
label_playback_speed = wxStaticText(self, wx.ID_ANY, _("Playback Speed") + " ")
|
1212
|
+
sizer_pb_speed.Add(label_playback_speed, 2, wx.ALIGN_CENTER_VERTICAL, 0)
|
1213
|
+
sizer_pb_speed.Add(self.text_playback_speed, 1, wx.EXPAND, 0)
|
1214
|
+
|
1215
|
+
sizer_display = wx.BoxSizer(wx.HORIZONTAL)
|
1216
|
+
label_playback_mode = wxStaticText(self, wx.ID_ANY, _("Mode") + " ")
|
1217
|
+
sizer_display.Add(label_playback_mode, 1, wx.ALIGN_CENTER_VERTICAL, 0)
|
1218
|
+
# Make sure it has about textbox size, otherwise too narrow
|
1219
|
+
self.radio_cut.SetMinSize(dip_size(self, -1, 23))
|
1220
|
+
sizer_display.Add(self.radio_cut, 1, wx.ALIGN_CENTER_VERTICAL, 0)
|
1221
|
+
sizer_display.Add(self.radio_time_seconds, 1, wx.ALIGN_CENTER_VERTICAL, 0)
|
1222
|
+
sizer_display.Add(self.radio_time_minutes, 1, wx.ALIGN_CENTER_VERTICAL, 0)
|
1223
|
+
sizer_speed_options.Add(sizer_pb_speed, 0, wx.EXPAND, 0)
|
1224
|
+
sizer_speed_options.Add(sizer_display, 0, wx.EXPAND, 0)
|
1225
|
+
h_sizer_buttons.Add(sizer_speed_options, 1, wx.EXPAND, 0)
|
1226
|
+
# sizer_execute.Add(self.combo_device, 0, wx.EXPAND, 0)
|
1227
|
+
sizer_execute.Add(self.button_spool, 1, wx.EXPAND, 0)
|
1228
|
+
h_sizer_buttons.Add(sizer_execute, 1, wx.EXPAND, 0)
|
1229
|
+
v_sizer_main.Add(self.hscene_sizer, 1, wx.EXPAND, 0)
|
1230
|
+
v_sizer_main.Add(h_sizer_scroll, 0, wx.EXPAND, 0)
|
1231
|
+
v_sizer_main.Add(h_sizer_text_1, 0, wx.EXPAND, 0)
|
1232
|
+
v_sizer_main.Add(h_sizer_text_2, 0, wx.EXPAND, 0)
|
1233
|
+
v_sizer_main.Add(h_sizer_buttons, 0, wx.EXPAND, 0)
|
1234
|
+
self.SetSizer(v_sizer_main)
|
1235
|
+
self.slided_in = True # Hide initially
|
1236
|
+
self.Layout()
|
1237
|
+
# end wxGlade
|
1238
|
+
|
1239
|
+
def on_size(self, event):
|
1240
|
+
sz = event.GetSize()
|
1241
|
+
event.Skip()
|
1242
|
+
self.debug(f"Manually forwarding the size: {sz}")
|
1243
|
+
self.view_pane.SetSize(wx.Size(sz[0], int(2 / 3 * sz[1])))
|
1244
|
+
self.Layout()
|
1245
|
+
sz = self.view_pane.GetSize()
|
1246
|
+
self.debug(f"Now pane has: {sz}")
|
1247
|
+
self.fit_scene_to_panel()
|
1248
|
+
|
1249
|
+
# Manages the display / non-display of the optimisation-options
|
1250
|
+
@property
|
1251
|
+
def slided_in(self):
|
1252
|
+
return self._slided_in
|
1253
|
+
|
1254
|
+
@slided_in.setter
|
1255
|
+
def slided_in(self, newvalue):
|
1256
|
+
self._slided_in = newvalue
|
1257
|
+
try:
|
1258
|
+
if newvalue:
|
1259
|
+
# Slided in ->
|
1260
|
+
self.hscene_sizer.Show(sizer=self.voption_sizer, show=False, recursive=True)
|
1261
|
+
self.voption_sizer.Layout()
|
1262
|
+
self.btn_slide_options.SetLabel("<")
|
1263
|
+
else:
|
1264
|
+
# Slided out ->
|
1265
|
+
self.hscene_sizer.Show(sizer=self.voption_sizer, show=True, recursive=True)
|
1266
|
+
self.voption_sizer.Layout()
|
1267
|
+
self.btn_slide_options.SetLabel(">")
|
1268
|
+
self.hscene_sizer.Layout()
|
1269
|
+
self.Layout()
|
1270
|
+
except RuntimeError:
|
1271
|
+
return
|
1272
|
+
|
1273
|
+
def toggle_background(self, event):
|
1274
|
+
"""
|
1275
|
+
Toggle the draw mode for the background
|
1276
|
+
"""
|
1277
|
+
self.widget_scene.context.draw_mode ^= DRAW_MODE_BACKGROUND
|
1278
|
+
self.widget_scene.request_refresh()
|
1279
|
+
|
1280
|
+
def toggle_grid(self, gridtype):
|
1281
|
+
if gridtype == "primary":
|
1282
|
+
self.grid.draw_grid_primary = not self.grid.draw_grid_primary
|
1283
|
+
elif gridtype == "secondary":
|
1284
|
+
self.grid.draw_grid_secondary = not self.grid.draw_grid_secondary
|
1285
|
+
elif gridtype == "circular":
|
1286
|
+
self.grid.draw_grid_circular = not self.grid.draw_grid_circular
|
1287
|
+
elif gridtype == "offset":
|
1288
|
+
self.grid.draw_offset_lines = not self.grid.draw_offset_lines
|
1289
|
+
self.widget_scene.request_refresh()
|
1290
|
+
|
1291
|
+
def toggle_grid_p(self, event):
|
1292
|
+
self.toggle_grid("primary")
|
1293
|
+
|
1294
|
+
def toggle_grid_s(self, event):
|
1295
|
+
self.toggle_grid("secondary")
|
1296
|
+
|
1297
|
+
def toggle_grid_c(self, event):
|
1298
|
+
self.toggle_grid("circular")
|
1299
|
+
|
1300
|
+
def toggle_grid_o(self, event):
|
1301
|
+
self.toggle_grid("offset")
|
1302
|
+
|
1303
|
+
def toggle_travel_display(self, event):
|
1304
|
+
self.display_travel = not self.display_travel
|
1305
|
+
self.context.display_travel = self.display_travel
|
1306
|
+
self.sim_travel.display = self.display_travel
|
1307
|
+
self.widget_scene.request_refresh()
|
1308
|
+
|
1309
|
+
def toggle_raster_display(self, event):
|
1310
|
+
self.raster_as_image = not self.raster_as_image
|
1311
|
+
self.context.raster_as_image = self.raster_as_image
|
1312
|
+
self.sim_cutcode.raster_as_image = self.raster_as_image
|
1313
|
+
self.widget_scene.request_refresh()
|
1314
|
+
|
1315
|
+
def calc_laser_spot_width(self):
|
1316
|
+
if self.laserspot_display:
|
1317
|
+
spot_value = getattr(self.context.device, "laserspot", "0.3mm")
|
1318
|
+
try:
|
1319
|
+
scale = 0.5 * (self.context.device.view.native_scale_x + self.context.device.view.native_scale_y)
|
1320
|
+
spotwidth_in_scene = float(Length(spot_value))
|
1321
|
+
spot_width = spotwidth_in_scene / scale
|
1322
|
+
# print (f"Scale for device: {scale}, spot in scene: {spot_value} = {spotwidth_in_scene} -> {spot_width}")
|
1323
|
+
except ValueError:
|
1324
|
+
spot_width = None
|
1325
|
+
self.laserspot_width = spot_width
|
1326
|
+
else:
|
1327
|
+
self.laserspot_width = None
|
1328
|
+
|
1329
|
+
def toggle_laserspot(self, event):
|
1330
|
+
self.laserspot_display = not self.laserspot_display
|
1331
|
+
self.context.laserspot_display = self.laserspot_display
|
1332
|
+
self.calc_laser_spot_width()
|
1333
|
+
self.sim_cutcode.laserspot_width = self.laserspot_width
|
1334
|
+
self.widget_scene.request_refresh()
|
1335
|
+
|
1336
|
+
def remove_background(self, event):
|
1337
|
+
self.widget_scene._signal_widget(
|
1338
|
+
self.widget_scene.widget_root, "background", None
|
1339
|
+
)
|
1340
|
+
self.widget_scene.request_refresh()
|
1341
|
+
|
1342
|
+
def zoom_in(self):
|
1343
|
+
matrix = self.widget_scene.widget_root.matrix
|
1344
|
+
zoomfactor = 1.5 / 1.0
|
1345
|
+
matrix.post_scale(zoomfactor)
|
1346
|
+
self.widget_scene.request_refresh()
|
1347
|
+
|
1348
|
+
def zoom_out(self):
|
1349
|
+
matrix = self.widget_scene.widget_root.matrix
|
1350
|
+
zoomfactor = 1.0 / 1.5
|
1351
|
+
matrix.post_scale(zoomfactor)
|
1352
|
+
self.widget_scene.request_refresh()
|
1353
|
+
|
1354
|
+
def fit_scene_to_panel(self):
|
1355
|
+
bbox = self.context.device.view.source_bbox()
|
1356
|
+
winsize = self.view_pane.GetSize()
|
1357
|
+
if winsize[0] != 0 and winsize[1] != 0:
|
1358
|
+
self.widget_scene.widget_root.focus_viewport_scene(bbox, winsize, 0.1)
|
1359
|
+
self.widget_scene.request_refresh()
|
1360
|
+
|
1361
|
+
def set_cutcode_entry(self, cutcode):
|
1362
|
+
self.subpanel_cutcode.set_cutcode_entry(cutcode, self.plan_name)
|
1363
|
+
|
1364
|
+
def progress_to_idx(self, progress):
|
1365
|
+
residual = 0
|
1366
|
+
idx = progress
|
1367
|
+
if not self._playback_cuts:
|
1368
|
+
# progress is the time indicator
|
1369
|
+
idx = len(self.statistics) - 1
|
1370
|
+
prev_time = None
|
1371
|
+
while idx >= 0:
|
1372
|
+
item = self.statistics[idx]
|
1373
|
+
this_time = item["time_at_end_of_burn"]
|
1374
|
+
# print (f"{idx} {this_time} vs {progress} - {item}")
|
1375
|
+
if this_time <= progress:
|
1376
|
+
if prev_time is not None:
|
1377
|
+
# We compute a 0 to 1 ratio of the progress
|
1378
|
+
residual = (progress - this_time) / (prev_time - this_time)
|
1379
|
+
break
|
1380
|
+
prev_time = this_time
|
1381
|
+
idx -= 1
|
1382
|
+
idx += 1
|
1383
|
+
if idx == 0:
|
1384
|
+
item = self.statistics[idx]
|
1385
|
+
start_time = item["time_at_start"]
|
1386
|
+
this_time = item["time_at_end_of_burn"]
|
1387
|
+
residual = (progress - start_time) / (this_time - start_time)
|
1388
|
+
|
1389
|
+
if idx >= len(self.statistics):
|
1390
|
+
idx = len(self.statistics) - 1
|
1391
|
+
if idx < 0:
|
1392
|
+
idx = 0
|
1393
|
+
# print(
|
1394
|
+
# f"Cut-Mode={self._playback_cuts}, prog={progress}, idx={idx}, stats={len(self.statistics)}"
|
1395
|
+
# )
|
1396
|
+
return idx, residual
|
1397
|
+
|
1398
|
+
def on_mouse_right_down(self, event=None):
|
1399
|
+
def cut_before(event):
|
1400
|
+
step, residual = self.progress_to_idx(self.progress)
|
1401
|
+
self.context(f"plan{self.plan_name} sublist {step} -1\n")
|
1402
|
+
|
1403
|
+
def cut_after(event):
|
1404
|
+
step, residual = self.progress_to_idx(self.progress)
|
1405
|
+
self.context(f"plan{self.plan_name} sublist 0 {step}\n")
|
1406
|
+
|
1407
|
+
gui = self
|
1408
|
+
menu = wx.Menu()
|
1409
|
+
if self.radio_cut.GetValue():
|
1410
|
+
self.Bind(
|
1411
|
+
wx.EVT_MENU,
|
1412
|
+
cut_before,
|
1413
|
+
menu.Append(
|
1414
|
+
wx.ID_ANY,
|
1415
|
+
_("Delete cuts before"),
|
1416
|
+
_("Delete all cuts before the current position in Simulation"),
|
1417
|
+
),
|
1418
|
+
)
|
1419
|
+
self.Bind(
|
1420
|
+
wx.EVT_MENU,
|
1421
|
+
cut_after,
|
1422
|
+
menu.Append(
|
1423
|
+
wx.ID_ANY,
|
1424
|
+
_("Delete cuts after"),
|
1425
|
+
_("Delete all cuts after the current position in Simulation"),
|
1426
|
+
),
|
1427
|
+
)
|
1428
|
+
menu.AppendSeparator()
|
1429
|
+
id1 = menu.Append(
|
1430
|
+
wx.ID_ANY,
|
1431
|
+
_("Show Background"),
|
1432
|
+
_("Display the background picture in the Simulation pane"),
|
1433
|
+
wx.ITEM_CHECK,
|
1434
|
+
)
|
1435
|
+
self.Bind(wx.EVT_MENU, self.toggle_background, id=id1.GetId())
|
1436
|
+
menu.Check(
|
1437
|
+
id1.GetId(),
|
1438
|
+
(self.widget_scene.context.draw_mode & DRAW_MODE_BACKGROUND == 0),
|
1439
|
+
)
|
1440
|
+
id2 = menu.Append(
|
1441
|
+
wx.ID_ANY,
|
1442
|
+
_("Show Primary Grid"),
|
1443
|
+
_("Display the primary grid in the Simulation pane"),
|
1444
|
+
wx.ITEM_CHECK,
|
1445
|
+
)
|
1446
|
+
self.Bind(wx.EVT_MENU, self.toggle_grid_p, id=id2.GetId())
|
1447
|
+
menu.Check(id2.GetId(), self.grid.draw_grid_primary)
|
1448
|
+
id3 = menu.Append(
|
1449
|
+
wx.ID_ANY,
|
1450
|
+
_("Show Secondary Grid"),
|
1451
|
+
_("Display the secondary grid in the Simulation pane"),
|
1452
|
+
wx.ITEM_CHECK,
|
1453
|
+
)
|
1454
|
+
self.Bind(wx.EVT_MENU, self.toggle_grid_s, id=id3.GetId())
|
1455
|
+
menu.Check(id3.GetId(), self.grid.draw_grid_secondary)
|
1456
|
+
id4 = menu.Append(
|
1457
|
+
wx.ID_ANY,
|
1458
|
+
_("Show Circular Grid"),
|
1459
|
+
_("Display the circular grid in the Simulation pane"),
|
1460
|
+
wx.ITEM_CHECK,
|
1461
|
+
)
|
1462
|
+
self.Bind(wx.EVT_MENU, self.toggle_grid_c, id=id4.GetId())
|
1463
|
+
menu.Check(id4.GetId(), self.grid.draw_grid_circular)
|
1464
|
+
try:
|
1465
|
+
mx = float(Length(self.context.device.view.margin_x))
|
1466
|
+
my = float(Length(self.context.device.view.margin_y))
|
1467
|
+
except ValueError:
|
1468
|
+
mx = 0
|
1469
|
+
my = 0
|
1470
|
+
# print(self.context.device.view.margin_x, self.context.device.view.margin_y)
|
1471
|
+
if mx != 0.0 or my != 0.0:
|
1472
|
+
menu.AppendSeparator()
|
1473
|
+
id4b = menu.Append(
|
1474
|
+
wx.ID_ANY,
|
1475
|
+
_("Show physical dimensions"),
|
1476
|
+
_("Display the physical dimensions in the Simulation pane"),
|
1477
|
+
wx.ITEM_CHECK,
|
1478
|
+
)
|
1479
|
+
self.Bind(wx.EVT_MENU, self.toggle_grid_o, id=id4b.GetId())
|
1480
|
+
menu.Check(id4b.GetId(), self.grid.draw_offset_lines)
|
1481
|
+
if self.widget_scene.has_background:
|
1482
|
+
menu.AppendSeparator()
|
1483
|
+
id5 = menu.Append(wx.ID_ANY, _("Remove Background"), "")
|
1484
|
+
self.Bind(wx.EVT_MENU, self.remove_background, id=id5.GetId())
|
1485
|
+
menu.AppendSeparator()
|
1486
|
+
id6 = menu.Append(
|
1487
|
+
wx.ID_ANY,
|
1488
|
+
_("Show travel path"),
|
1489
|
+
_("Displays the laser travel when not burning"),
|
1490
|
+
wx.ITEM_CHECK,
|
1491
|
+
)
|
1492
|
+
self.Bind(wx.EVT_MENU, self.toggle_travel_display, id=id6.GetId())
|
1493
|
+
menu.Check(id6.GetId(), self.display_travel)
|
1494
|
+
id7 = menu.Append(
|
1495
|
+
wx.ID_ANY,
|
1496
|
+
_("Raster as Image"),
|
1497
|
+
_("Show picture as image / as all the lines needed"),
|
1498
|
+
wx.ITEM_CHECK,
|
1499
|
+
)
|
1500
|
+
self.Bind(wx.EVT_MENU, self.toggle_raster_display, id=id7.GetId())
|
1501
|
+
menu.Check(id7.GetId(), self.raster_as_image)
|
1502
|
+
id8 = menu.Append(
|
1503
|
+
wx.ID_ANY,
|
1504
|
+
_("Simulate laser width"),
|
1505
|
+
_("Show laser path as wide as laserspot width / as simple line"),
|
1506
|
+
wx.ITEM_CHECK,
|
1507
|
+
)
|
1508
|
+
self.Bind(wx.EVT_MENU, self.toggle_laserspot, id=id8.GetId())
|
1509
|
+
menu.Check(id8.GetId(), self.laserspot_display)
|
1510
|
+
|
1511
|
+
menu.AppendSeparator()
|
1512
|
+
self.Bind(
|
1513
|
+
wx.EVT_MENU,
|
1514
|
+
lambda e: self.zoom_out(),
|
1515
|
+
menu.Append(
|
1516
|
+
wx.ID_ANY,
|
1517
|
+
_("Zoom Out"),
|
1518
|
+
_("Make the scene smaller"),
|
1519
|
+
),
|
1520
|
+
)
|
1521
|
+
self.Bind(
|
1522
|
+
wx.EVT_MENU,
|
1523
|
+
lambda e: self.zoom_in(),
|
1524
|
+
menu.Append(
|
1525
|
+
wx.ID_ANY,
|
1526
|
+
_("Zoom In"),
|
1527
|
+
_("Make the scene larger"),
|
1528
|
+
),
|
1529
|
+
)
|
1530
|
+
self.Bind(
|
1531
|
+
wx.EVT_MENU,
|
1532
|
+
lambda e: self.fit_scene_to_panel(),
|
1533
|
+
menu.Append(
|
1534
|
+
wx.ID_ANY,
|
1535
|
+
_("Zoom to Bed"),
|
1536
|
+
_("View the whole laser bed"),
|
1537
|
+
),
|
1538
|
+
)
|
1539
|
+
|
1540
|
+
if menu.MenuItemCount != 0:
|
1541
|
+
gui.PopupMenu(menu)
|
1542
|
+
menu.Destroy()
|
1543
|
+
|
1544
|
+
def _set_slider_dimensions(self):
|
1545
|
+
if self._playback_cuts:
|
1546
|
+
self.max = max(len(self.cutcode), 1)
|
1547
|
+
else:
|
1548
|
+
totalduration = 0
|
1549
|
+
maxidx = len(self.statistics)
|
1550
|
+
if maxidx > 0:
|
1551
|
+
totalduration = int(
|
1552
|
+
self.statistics[-1]["total_time_extra"]
|
1553
|
+
+ self.statistics[-1]["total_time_travel"]
|
1554
|
+
+ self.statistics[-1]["total_time_cut"]
|
1555
|
+
)
|
1556
|
+
self.max = max(totalduration, 1)
|
1557
|
+
self.progress = self.max
|
1558
|
+
self.slider_progress.SetMin(0)
|
1559
|
+
self.slider_progress.SetMax(self.max)
|
1560
|
+
self.slider_progress.SetValue(self.max)
|
1561
|
+
value = self.slider_playbackspeed.GetValue()
|
1562
|
+
value = int((10.0 ** (value // 90)) * (1.0 + float(value % 90) / 10.0))
|
1563
|
+
if self.radio_cut.GetValue():
|
1564
|
+
factor = 0.1 # steps
|
1565
|
+
else:
|
1566
|
+
factor = 1 # seconds
|
1567
|
+
self.interval = factor * 100.0 / float(value)
|
1568
|
+
|
1569
|
+
def _refresh_simulated_plan(self):
|
1570
|
+
self.debug(f"Refresh simulated: {perf_counter()-self.start_time}")
|
1571
|
+
# Stop animation
|
1572
|
+
if self.running:
|
1573
|
+
self._stop()
|
1574
|
+
return
|
1575
|
+
# Refresh cutcode
|
1576
|
+
self.SetCursor(wx.Cursor(wx.CURSOR_WAIT))
|
1577
|
+
|
1578
|
+
if self.plan_name:
|
1579
|
+
self.cutplan = self.context.planner.get_or_make_plan(self.plan_name)
|
1580
|
+
else:
|
1581
|
+
self.cutplan = self.context.planner.default_plan
|
1582
|
+
self.plan_name = self.cutplan.name
|
1583
|
+
self.operations = self.cutplan.plan
|
1584
|
+
self.subpanel_cutcode.set_cutcode_entry(None, self.plan_name)
|
1585
|
+
self.subpanel_operations.set_cut_plan(self.cutplan)
|
1586
|
+
# for e in self.operations:
|
1587
|
+
# print(f"Refresh: {type(e).__name__} {e}")
|
1588
|
+
# try:
|
1589
|
+
# idx = 0
|
1590
|
+
# for n in e:
|
1591
|
+
# print(f"Entry {idx}#: {name_str(n)}")
|
1592
|
+
# idx += 1
|
1593
|
+
# except:
|
1594
|
+
# pass
|
1595
|
+
|
1596
|
+
self.cutcode = CutCode()
|
1597
|
+
|
1598
|
+
for c in self.operations:
|
1599
|
+
if isinstance(c, CutCode):
|
1600
|
+
self.cutcode.extend(c)
|
1601
|
+
self.cutcode = CutCode(self.cutcode.flat())
|
1602
|
+
|
1603
|
+
# self.reload_statistics()
|
1604
|
+
|
1605
|
+
# for idx, stat in enumerate(self.statistics):
|
1606
|
+
# print(f"#{idx}: {stat}")
|
1607
|
+
bb = self.cutplan._previous_bounds
|
1608
|
+
if bb is None or math.isinf(bb[0]):
|
1609
|
+
self.parent.SetTitle(_("Simulation"))
|
1610
|
+
else:
|
1611
|
+
wd = bb[2] - bb[0]
|
1612
|
+
ht = bb[3] - bb[1]
|
1613
|
+
sdimx = Length(
|
1614
|
+
wd, preferred_units=self.context.units_name, digits=2
|
1615
|
+
).preferred_length
|
1616
|
+
sdimy = Length(
|
1617
|
+
ht, preferred_units=self.context.units_name, digits=2
|
1618
|
+
).preferred_length
|
1619
|
+
self.parent.SetTitle(_("Simulation") + f" ({sdimx}x{sdimy})")
|
1620
|
+
|
1621
|
+
self.update_job.cancel()
|
1622
|
+
self.context.schedule(self.update_job)
|
1623
|
+
|
1624
|
+
self._startup()
|
1625
|
+
self.request_refresh()
|
1626
|
+
self.SetCursor(wx.Cursor(wx.CURSOR_ARROW))
|
1627
|
+
|
1628
|
+
|
1629
|
+
@signal_listener("device;modified")
|
1630
|
+
@signal_listener("plan")
|
1631
|
+
def on_plan_change(self, origin, plan_name=None, status=None):
|
1632
|
+
def resend_signal():
|
1633
|
+
self.debug(f"Resending signal: {perf_counter()-self.start_time}")
|
1634
|
+
self.context.signal("plan", plan_name=self.plan_name, status=1)
|
1635
|
+
|
1636
|
+
winsize = self.view_pane.GetSize()
|
1637
|
+
winsize1 = self.hscene_sizer.GetSize()
|
1638
|
+
winsize2 = self.GetSize()
|
1639
|
+
self.debug(
|
1640
|
+
f"Plan called : {perf_counter()-self.start_time} (Pane: {winsize}, Sizer: {winsize1}, Window: {winsize2})"
|
1641
|
+
)
|
1642
|
+
if plan_name == self.plan_name:
|
1643
|
+
# This may come too early before all things have been done
|
1644
|
+
if (
|
1645
|
+
winsize[0] == 0 or winsize[1] == 0
|
1646
|
+
) and self.retries > 3: # Still initialising
|
1647
|
+
self.Fit()
|
1648
|
+
self.hscene_sizer.Layout()
|
1649
|
+
self.view_pane.Show()
|
1650
|
+
interval = 0.25
|
1651
|
+
self.retries += 1
|
1652
|
+
self.debug(
|
1653
|
+
f"Need to resend signal due to invalid window-size, attempt {self.retries}/10, will wait for {interval:.2f} sec"
|
1654
|
+
)
|
1655
|
+
|
1656
|
+
_job = Job(
|
1657
|
+
process=resend_signal,
|
1658
|
+
job_name="resender",
|
1659
|
+
interval=interval,
|
1660
|
+
times=1,
|
1661
|
+
run_main=True,
|
1662
|
+
)
|
1663
|
+
self.context.schedule(_job)
|
1664
|
+
return
|
1665
|
+
|
1666
|
+
self.retries = 0
|
1667
|
+
self._refresh_simulated_plan()
|
1668
|
+
|
1669
|
+
@signal_listener("refresh_simulation")
|
1670
|
+
def on_request_refresh(self, origin, *args):
|
1671
|
+
self.widget_scene.request_refresh()
|
1672
|
+
|
1673
|
+
def on_radio_playback_mode(self, event):
|
1674
|
+
self._playback_cuts = self.radio_cut.GetValue()
|
1675
|
+
default = 0
|
1676
|
+
if self.radio_cut.GetValue():
|
1677
|
+
default = 0
|
1678
|
+
elif self.radio_time_seconds.GetValue():
|
1679
|
+
default = 1
|
1680
|
+
elif self.radio_time_minutes.GetValue():
|
1681
|
+
default = 2
|
1682
|
+
self.context.simulation_mode = default
|
1683
|
+
self._set_slider_dimensions()
|
1684
|
+
|
1685
|
+
def on_checkbox_optimize(self, event):
|
1686
|
+
if self.checkbox_optimize.GetValue():
|
1687
|
+
self.options_optimize.Enable(True)
|
1688
|
+
else:
|
1689
|
+
self.options_optimize.Enable(False)
|
1690
|
+
|
1691
|
+
def cache_updater(self):
|
1692
|
+
try:
|
1693
|
+
self.button_spool.Enable(False)
|
1694
|
+
except RuntimeError:
|
1695
|
+
# Control no longer existant
|
1696
|
+
return
|
1697
|
+
msg = self.button_spool.GetLabel()
|
1698
|
+
self.button_spool.SetLabel(_("Calculating"))
|
1699
|
+
for cut in self.cutcode:
|
1700
|
+
if isinstance(cut, (RasterCut, PlotCut)):
|
1701
|
+
if hasattr(cut, "_plotcache") and cut._plotcache is not None:
|
1702
|
+
continue
|
1703
|
+
if isinstance(cut, RasterCut):
|
1704
|
+
cut._plotcache = list(cut.plot.plot())
|
1705
|
+
elif isinstance(cut, PlotCut):
|
1706
|
+
cut._plotcache = list(cut.plot)
|
1707
|
+
self.context.signal("refresh_scene", self.widget_scene.name)
|
1708
|
+
self.reload_statistics()
|
1709
|
+
try:
|
1710
|
+
self.button_spool.SetLabel(msg)
|
1711
|
+
self.button_spool.Enable(True)
|
1712
|
+
except RuntimeError:
|
1713
|
+
# No longer existing
|
1714
|
+
pass
|
1715
|
+
|
1716
|
+
def update_fields(self):
|
1717
|
+
def len_str(value):
|
1718
|
+
if abs(value) >= 1000000:
|
1719
|
+
result = f"{value / 1000000:.2f}km"
|
1720
|
+
elif abs(value) >= 1000:
|
1721
|
+
result = f"{value / 1000:.2f}m"
|
1722
|
+
else:
|
1723
|
+
result = f"{value:.0f}mm"
|
1724
|
+
return result
|
1725
|
+
|
1726
|
+
step, residual = self.progress_to_idx(self.progress)
|
1727
|
+
item = self.statistics[step - 1]
|
1728
|
+
partials = {
|
1729
|
+
"total_distance_travel": 0,
|
1730
|
+
"total_distance_cut": 0,
|
1731
|
+
"total_time_travel": 0,
|
1732
|
+
"total_internal_travel": 0,
|
1733
|
+
"total_time_cut": 0,
|
1734
|
+
"total_time_extra": 0,
|
1735
|
+
}
|
1736
|
+
if residual != 0 and step < len(self.statistics):
|
1737
|
+
itemnext = self.statistics[step]
|
1738
|
+
for entry in partials:
|
1739
|
+
partials[entry] = residual * (itemnext[entry] - item[entry])
|
1740
|
+
|
1741
|
+
###################
|
1742
|
+
# UPDATE POSITIONAL
|
1743
|
+
###################
|
1744
|
+
|
1745
|
+
mm = self.cutcode.settings.get("native_mm", 39.3701)
|
1746
|
+
# item = (i, distance_travel, distance_cut, extra, duration_travel, duration_cut)
|
1747
|
+
travel_mm = (
|
1748
|
+
item["total_distance_travel"] + partials["total_distance_travel"]
|
1749
|
+
) / mm
|
1750
|
+
internal_mm = (
|
1751
|
+
item["total_internal_travel"] + partials["total_internal_travel"]
|
1752
|
+
) / mm
|
1753
|
+
cuts_mm = (item["total_distance_cut"] + partials["total_distance_cut"]) / mm
|
1754
|
+
# travel_mm = self.cutcode.length_travel(stop_at=step) / mm
|
1755
|
+
# cuts_mm = self.cutcode.length_cut(stop_at=step) / mm
|
1756
|
+
info = len_str(travel_mm)
|
1757
|
+
if internal_mm != 0:
|
1758
|
+
info += f" ({len_str(internal_mm)})"
|
1759
|
+
self.text_distance_travel_step.SetValue(info)
|
1760
|
+
self.text_distance_laser_step.SetValue(len_str(cuts_mm))
|
1761
|
+
self.text_distance_total_step.SetValue(len_str(travel_mm + cuts_mm))
|
1762
|
+
try:
|
1763
|
+
time_travel = item["total_time_travel"] + partials["total_time_travel"]
|
1764
|
+
t_hours = int(time_travel // 3600)
|
1765
|
+
t_mins = int((time_travel % 3600) // 60)
|
1766
|
+
t_seconds = int(time_travel % 60)
|
1767
|
+
self.text_time_travel_step.SetValue(
|
1768
|
+
f"{int(t_hours)}:{int(t_mins):02d}:{int(t_seconds):02d}"
|
1769
|
+
)
|
1770
|
+
except ZeroDivisionError:
|
1771
|
+
time_travel = 0
|
1772
|
+
try:
|
1773
|
+
time_cuts = item["total_time_cut"] + partials["total_time_cut"]
|
1774
|
+
t_hours = int(time_cuts // 3600)
|
1775
|
+
t_mins = int((time_cuts % 3600) // 60)
|
1776
|
+
t_seconds = int(time_cuts % 60)
|
1777
|
+
self.text_time_laser_step.SetValue(
|
1778
|
+
f"{int(t_hours)}:{int(t_mins):02d}:{int(t_seconds):02d}"
|
1779
|
+
)
|
1780
|
+
except ZeroDivisionError:
|
1781
|
+
time_cuts = 0
|
1782
|
+
try:
|
1783
|
+
extra = item["total_time_extra"] + partials["total_time_extra"]
|
1784
|
+
t_hours = int(extra // 3600)
|
1785
|
+
t_mins = int((extra % 3600) // 60)
|
1786
|
+
t_seconds = int(extra % 60)
|
1787
|
+
self.text_time_extra_step.SetValue(
|
1788
|
+
f"{int(t_hours)}:{int(t_mins):02d}:{int(t_seconds):02d}"
|
1789
|
+
)
|
1790
|
+
if self._playback_cuts:
|
1791
|
+
time_total = time_travel + time_cuts + extra
|
1792
|
+
else:
|
1793
|
+
time_total = self.progress
|
1794
|
+
t_hours = int(time_total // 3600)
|
1795
|
+
t_mins = int((time_total % 3600) // 60)
|
1796
|
+
t_seconds = int(time_total % 60)
|
1797
|
+
self.text_time_total_step.SetValue(
|
1798
|
+
f"{int(t_hours)}:{int(t_mins):02d}:{int(t_seconds):02d}"
|
1799
|
+
)
|
1800
|
+
except ZeroDivisionError:
|
1801
|
+
pass
|
1802
|
+
|
1803
|
+
###################
|
1804
|
+
# UPDATE TOTAL
|
1805
|
+
###################
|
1806
|
+
|
1807
|
+
travel_mm = self.statistics[-1]["total_distance_travel"] / mm
|
1808
|
+
cuts_mm = self.statistics[-1]["total_distance_cut"] / mm
|
1809
|
+
self.text_distance_travel.SetValue(len_str(travel_mm))
|
1810
|
+
self.text_distance_laser.SetValue(len_str(cuts_mm))
|
1811
|
+
self.text_distance_total.SetValue(len_str(travel_mm + cuts_mm))
|
1812
|
+
|
1813
|
+
try:
|
1814
|
+
time_travel = self.statistics[-1]["total_time_travel"]
|
1815
|
+
t_hours = int(time_travel // 3600)
|
1816
|
+
t_mins = int((time_travel % 3600) // 60)
|
1817
|
+
t_seconds = int(time_travel % 60)
|
1818
|
+
self.text_time_travel.SetValue(f"{t_hours}:{t_mins:02d}:{t_seconds:02d}")
|
1819
|
+
except ZeroDivisionError:
|
1820
|
+
time_travel = 0
|
1821
|
+
try:
|
1822
|
+
time_cuts = self.statistics[-1]["total_time_cut"]
|
1823
|
+
t_hours = int(time_cuts // 3600)
|
1824
|
+
t_mins = int((time_cuts % 3600) // 60)
|
1825
|
+
t_seconds = int(time_cuts % 60)
|
1826
|
+
self.text_time_laser.SetValue(f"{t_hours}:{t_mins:02d}:{t_seconds:02d}")
|
1827
|
+
except ZeroDivisionError:
|
1828
|
+
time_cuts = 0
|
1829
|
+
try:
|
1830
|
+
extra = self.statistics[-1]["total_time_extra"]
|
1831
|
+
t_hours = int(extra // 3600)
|
1832
|
+
t_mins = int((extra % 3600) // 60)
|
1833
|
+
t_seconds = int(extra % 60)
|
1834
|
+
self.text_time_extra.SetValue(f"{t_hours}:{t_mins:02d}:{t_seconds:02d}")
|
1835
|
+
time_total = time_travel + time_cuts + extra
|
1836
|
+
t_hours = int(time_total // 3600)
|
1837
|
+
t_mins = int((time_total % 3600) // 60)
|
1838
|
+
t_seconds = int(time_total % 60)
|
1839
|
+
self.text_time_total.SetValue(f"{t_hours}:{t_mins:02d}:{t_seconds:02d}")
|
1840
|
+
except ZeroDivisionError:
|
1841
|
+
pass
|
1842
|
+
|
1843
|
+
def slide_out(self, event):
|
1844
|
+
self.slided_in = not self.slided_in
|
1845
|
+
event.Skip()
|
1846
|
+
|
1847
|
+
def on_redo_it(self, event):
|
1848
|
+
# Dont occupy gui event handling too long
|
1849
|
+
wx.CallAfter(self.redo_action)
|
1850
|
+
|
1851
|
+
def redo_action(self):
|
1852
|
+
self.SetCursor(wx.Cursor(wx.CURSOR_WAIT))
|
1853
|
+
self.btn_redo_it.SetLabel(_("Preparing simulation..."))
|
1854
|
+
self.btn_redo_it.Enable(False)
|
1855
|
+
self.btn_redo_it.Refresh()
|
1856
|
+
self.btn_redo_it.Update()
|
1857
|
+
busy = self.context.kernel.busyinfo
|
1858
|
+
busy.start(msg=_("Preparing simulation..."))
|
1859
|
+
|
1860
|
+
plan = self.plan_name
|
1861
|
+
if self.checkbox_optimize.GetValue():
|
1862
|
+
opt = " preopt optimize"
|
1863
|
+
self.context.planner.do_optimization = True
|
1864
|
+
else:
|
1865
|
+
opt = ""
|
1866
|
+
self.context.planner.do_optimization = False
|
1867
|
+
self.context.signal("optimize", self.context.planner.do_optimization)
|
1868
|
+
self.context(
|
1869
|
+
f"plan{plan} clear\nplan{plan} copy preprocess validate blob{opt}\n"
|
1870
|
+
)
|
1871
|
+
busy.end()
|
1872
|
+
self._refresh_simulated_plan()
|
1873
|
+
self.btn_redo_it.Enable(True)
|
1874
|
+
self.btn_redo_it.SetLabel(_("Recalculate"))
|
1875
|
+
self.SetCursor(wx.Cursor(wx.CURSOR_ARROW))
|
1876
|
+
|
1877
|
+
def pane_show(self):
|
1878
|
+
self.Layout()
|
1879
|
+
self.context.setting(str, "units_name", "mm")
|
1880
|
+
|
1881
|
+
bbox = self.context.device.view.source_bbox()
|
1882
|
+
self.widget_scene.widget_root.focus_viewport_scene(
|
1883
|
+
bbox, self.view_pane.Size, 0.1
|
1884
|
+
)
|
1885
|
+
self.update_fields()
|
1886
|
+
# self.panel_optimize.pane_show()
|
1887
|
+
self.panel_optimize.Show()
|
1888
|
+
|
1889
|
+
def pane_hide(self):
|
1890
|
+
if self.auto_clear:
|
1891
|
+
self.context(f"plan{self.plan_name} clear\n")
|
1892
|
+
self.context.close("SimScene")
|
1893
|
+
self.context.unschedule(self)
|
1894
|
+
self.running = False
|
1895
|
+
# self.panel_optimize.pane_hide()
|
1896
|
+
try:
|
1897
|
+
self.panel_optimize.Hide()
|
1898
|
+
except RuntimeError:
|
1899
|
+
pass
|
1900
|
+
|
1901
|
+
@signal_listener("refresh_scene")
|
1902
|
+
def on_refresh_scene(self, origin, scene_name=None, *args):
|
1903
|
+
"""
|
1904
|
+
Called by 'refresh_scene' change. To refresh tree.
|
1905
|
+
@param origin: the path of the originating signal
|
1906
|
+
@param scene_name: Scene to refresh on if matching
|
1907
|
+
@param args:
|
1908
|
+
@return:
|
1909
|
+
"""
|
1910
|
+
if scene_name == "SimScene":
|
1911
|
+
self.request_refresh()
|
1912
|
+
|
1913
|
+
def request_refresh(self, *args):
|
1914
|
+
self.widget_scene.request_refresh(*args)
|
1915
|
+
|
1916
|
+
def on_slider_progress(self, event=None): # wxGlade: Simulation.<event_handler>
|
1917
|
+
self.progress = min(self.slider_progress.GetValue(), self.max)
|
1918
|
+
self.update_fields()
|
1919
|
+
self.context.signal("refresh_scene", self.widget_scene.name)
|
1920
|
+
|
1921
|
+
def _start(self):
|
1922
|
+
self.button_play.SetBitmap(
|
1923
|
+
icons8_pause.GetBitmap(resize=get_default_icon_size(self.context))
|
1924
|
+
)
|
1925
|
+
self.button_play.SetToolTip(_("Stop the simulation replay"))
|
1926
|
+
self.context.schedule(self)
|
1927
|
+
self.running = True
|
1928
|
+
|
1929
|
+
def _stop(self):
|
1930
|
+
self.button_play.SetBitmap(
|
1931
|
+
icons8_circled_play.GetBitmap(resize=get_default_icon_size(self.context))
|
1932
|
+
)
|
1933
|
+
self.button_play.SetToolTip(_("Start the simulation replay"))
|
1934
|
+
self.context.unschedule(self)
|
1935
|
+
self.running = False
|
1936
|
+
|
1937
|
+
def on_button_play(self, event=None): # wxGlade: Simulation.<event_handler>
|
1938
|
+
if self.running:
|
1939
|
+
self._stop()
|
1940
|
+
return
|
1941
|
+
if self.progress >= self.max:
|
1942
|
+
self.progress = 0
|
1943
|
+
self.slider_progress.SetValue(self.progress)
|
1944
|
+
self.update_fields()
|
1945
|
+
self._start()
|
1946
|
+
|
1947
|
+
def animate_sim(self, event=None):
|
1948
|
+
if self.radio_time_minutes.GetValue():
|
1949
|
+
self.progress += 60
|
1950
|
+
else:
|
1951
|
+
self.progress += 1
|
1952
|
+
if self.progress >= self.max:
|
1953
|
+
self.progress = self.max
|
1954
|
+
self.slider_progress.SetValue(self.progress)
|
1955
|
+
self._stop()
|
1956
|
+
else:
|
1957
|
+
self.slider_progress.SetValue(self.progress)
|
1958
|
+
self.update_fields()
|
1959
|
+
self.context.signal("refresh_scene", self.widget_scene.name)
|
1960
|
+
|
1961
|
+
def on_slider_playback(self, event=None): # wxGlade: Simulation.<event_handler>
|
1962
|
+
# Slider is now pseudo logarithmic in scale varying from 1% to 5,000%.
|
1963
|
+
|
1964
|
+
value = self.slider_playbackspeed.GetValue()
|
1965
|
+
value = int((10.0 ** (value // 90)) * (1.0 + float(value % 90) / 10.0))
|
1966
|
+
if self.radio_cut.GetValue():
|
1967
|
+
factor = 0.1 # steps
|
1968
|
+
else:
|
1969
|
+
factor = 1 # seconds
|
1970
|
+
self.interval = factor * 100.0 / float(value)
|
1971
|
+
|
1972
|
+
self.text_playback_speed.SetValue(f"{value}%")
|
1973
|
+
|
1974
|
+
def on_button_spool(self, event=None): # wxGlade: Simulation.<event_handler>
|
1975
|
+
self.context(f"plan{self.plan_name} spool\n")
|
1976
|
+
self.context("window close Simulation\n")
|
1977
|
+
if self.context.auto_spooler:
|
1978
|
+
self.context("window open JobSpooler\n")
|
1979
|
+
|
1980
|
+
|
1981
|
+
class SimulationWidget(Widget):
|
1982
|
+
"""
|
1983
|
+
The simulation widget is responsible for rendering the cutcode to the scene. This should be
|
1984
|
+
done such that both progress of 0 and 1 render nothing and items begin to draw at 2.
|
1985
|
+
"""
|
1986
|
+
|
1987
|
+
def __init__(self, scene, sim):
|
1988
|
+
Widget.__init__(self, scene, all=False)
|
1989
|
+
self.renderer = LaserRender(self.scene.context)
|
1990
|
+
self.sim = sim
|
1991
|
+
self.matrix.post_cat(~scene.context.device.view.matrix)
|
1992
|
+
self.last_msg = None
|
1993
|
+
self.raster_as_image = True
|
1994
|
+
self.laserspot_width = None # 1 Pixel
|
1995
|
+
|
1996
|
+
def process_draw(self, gc: wx.GraphicsContext):
|
1997
|
+
if self.sim.progress < 0:
|
1998
|
+
return
|
1999
|
+
spot_width = self.laserspot_width
|
2000
|
+
residual = 0
|
2001
|
+
idx = 0
|
2002
|
+
if self.sim.progress < self.sim.max:
|
2003
|
+
idx, residual = self.sim.progress_to_idx(self.sim.progress)
|
2004
|
+
# print(f"SimWidget, idx={idx}, residual={residual:.3f}")
|
2005
|
+
sim_cut = self.sim.cutcode[:idx]
|
2006
|
+
else:
|
2007
|
+
sim_cut = self.sim.cutcode
|
2008
|
+
self.renderer.draw_cutcode(sim_cut, gc, 0, 0, self.raster_as_image, laserspot_width=spot_width)
|
2009
|
+
if residual <= 0:
|
2010
|
+
return
|
2011
|
+
# We draw interpolated lines to acknowledge we are in the middle of a cut operation
|
2012
|
+
starts = []
|
2013
|
+
ends = []
|
2014
|
+
cutstart = wx.Point2D(*self.sim.cutcode[idx].start)
|
2015
|
+
cutend = wx.Point2D(*self.sim.cutcode[idx].end)
|
2016
|
+
if self.sim.statistics[idx]["type"] == "RasterCut":
|
2017
|
+
if self.raster_as_image:
|
2018
|
+
# Rastercut object.
|
2019
|
+
x = 0
|
2020
|
+
y = 0
|
2021
|
+
cut = self.sim.cutcode[idx]
|
2022
|
+
image = cut.image
|
2023
|
+
gc.PushState()
|
2024
|
+
matrix = Matrix.scale(cut.step_x, cut.step_y)
|
2025
|
+
matrix.post_translate(cut.offset_x + x, cut.offset_y + y) # Adjust image xy
|
2026
|
+
gc.ConcatTransform(wx.GraphicsContext.CreateMatrix(gc, ZMatrix(matrix)))
|
2027
|
+
try:
|
2028
|
+
cache = cut._cache
|
2029
|
+
cache_id = cut._cache_id
|
2030
|
+
except AttributeError:
|
2031
|
+
cache = None
|
2032
|
+
cache_id = -1
|
2033
|
+
if cache_id != id(image):
|
2034
|
+
# Cached image is invalid.
|
2035
|
+
cache = None
|
2036
|
+
if cache is None:
|
2037
|
+
# No valid cache. Generate.
|
2038
|
+
cut._cache_width, cut._cache_height = image.size
|
2039
|
+
try:
|
2040
|
+
cut._cache = self.renderer.make_thumbnail(image, maximum=5000)
|
2041
|
+
except (MemoryError, RuntimeError):
|
2042
|
+
cut._cache = None
|
2043
|
+
cut._cache_id = id(image)
|
2044
|
+
# Set draw - constraint
|
2045
|
+
if cut.horizontal:
|
2046
|
+
if cut.start_minimum_y:
|
2047
|
+
# mode = "T2B"
|
2048
|
+
clip_w = cut._cache_width
|
2049
|
+
clip_h = int(residual * cut._cache_height)
|
2050
|
+
clip_x = 0
|
2051
|
+
clip_y = 0
|
2052
|
+
else:
|
2053
|
+
# mode = "B2T"
|
2054
|
+
clip_w = cut._cache_width
|
2055
|
+
clip_h = int(residual * cut._cache_height)
|
2056
|
+
clip_x = 0
|
2057
|
+
clip_y = cut._cache_height - clip_h
|
2058
|
+
else:
|
2059
|
+
if cut.start_minimum_x:
|
2060
|
+
# mode = "L2R"
|
2061
|
+
clip_w = int(residual * cut._cache_width)
|
2062
|
+
clip_h = cut._cache_height
|
2063
|
+
clip_x = 0
|
2064
|
+
clip_y = 0
|
2065
|
+
else:
|
2066
|
+
# mode = "R2L"
|
2067
|
+
clip_w = int(residual * cut._cache_width)
|
2068
|
+
clip_h = cut._cache_height
|
2069
|
+
clip_x = cut._cache_width - clip_w
|
2070
|
+
clip_y = 0
|
2071
|
+
|
2072
|
+
# msg = f"Mode: {mode}, Horiz: {cut.horizontal}, from left: {cut.start_on_left}, from top: {cut.start_on_top}"
|
2073
|
+
# if msg != self.last_msg:
|
2074
|
+
# print (msg)
|
2075
|
+
# self.last_msg = msg
|
2076
|
+
gc.Clip(clip_x, clip_y, clip_w, clip_h)
|
2077
|
+
if cut._cache is not None:
|
2078
|
+
# Cache exists and is valid.
|
2079
|
+
gc.DrawBitmap(cut._cache, 0, 0, cut._cache_width, cut._cache_height)
|
2080
|
+
# gc.SetBrush(wx.RED_BRUSH)
|
2081
|
+
# gc.DrawRectangle(0, 0, cut._cache_width, cut._cache_height)
|
2082
|
+
else:
|
2083
|
+
# Image was too large to cache, draw a red rectangle instead.
|
2084
|
+
gc.SetBrush(wx.RED_BRUSH)
|
2085
|
+
gc.DrawRectangle(0, 0, cut._cache_width, cut._cache_height)
|
2086
|
+
gc.DrawBitmap(
|
2087
|
+
icons8_image.GetBitmap(),
|
2088
|
+
0,
|
2089
|
+
0,
|
2090
|
+
cut._cache_width,
|
2091
|
+
cut._cache_height,
|
2092
|
+
)
|
2093
|
+
gc.ResetClip()
|
2094
|
+
gc.PopState()
|
2095
|
+
else:
|
2096
|
+
# We draw the cutcode up to a certain percentage
|
2097
|
+
simcut = (self.sim.cutcode[idx], )
|
2098
|
+
self.renderer.draw_cutcode(simcut, gc, 0, 0, self.raster_as_image, residual=residual, laserspot_width=spot_width)
|
2099
|
+
|
2100
|
+
return
|
2101
|
+
# # We draw a rectangle covering the raster area
|
2102
|
+
# spath = str(self.sim.cutcode[idx].path)
|
2103
|
+
# sparse = re.compile(" ([0-9,\.]*) ")
|
2104
|
+
# min_x = None
|
2105
|
+
# max_x = None
|
2106
|
+
# path_width = 0
|
2107
|
+
# for numpair in sparse.findall(spath):
|
2108
|
+
# comma_idx = numpair.find(",")
|
2109
|
+
# if comma_idx >= 0:
|
2110
|
+
# left_num = numpair[:comma_idx]
|
2111
|
+
# right_num = numpair[comma_idx + 1 :]
|
2112
|
+
# # print (f"'{numpair}' -> '{left_num}', '{right_num}'")
|
2113
|
+
# try:
|
2114
|
+
# c_x = float(left_num)
|
2115
|
+
# c_y = float(right_num)
|
2116
|
+
# if min_x is None:
|
2117
|
+
# min_x = c_x
|
2118
|
+
# max_x = c_x
|
2119
|
+
# else:
|
2120
|
+
# if c_x < min_x:
|
2121
|
+
# min_x = c_x
|
2122
|
+
# if c_x > max_x:
|
2123
|
+
# max_x = c_x
|
2124
|
+
# path_width = max_x - min_x
|
2125
|
+
# except ValueError:
|
2126
|
+
# pass
|
2127
|
+
# print(f"path={self.sim.cutcode[idx].path}")
|
2128
|
+
# print(f"Raster: ({cutstart[0]}, {cutstart[1]}) - ({cutend[0]}, {cutend[1]})")
|
2129
|
+
# print(f"w={abs(cutend[0] - cutstart[0])}, w-cutop = {2*self.sim.cutcode[idx].width}, w_path={path_width}")
|
2130
|
+
# c_vars = vars(self.sim.cutcode[idx])
|
2131
|
+
# for cv in c_vars:
|
2132
|
+
# print(f"{cv}={c_vars[cv]}")
|
2133
|
+
# rect_y = cutstart[1]
|
2134
|
+
# rect_x = self.sim.cutcode[idx].offset_x
|
2135
|
+
# rect_w = max(2 * self.sim.cutcode[idx].width, path_width)
|
2136
|
+
# rect_h = residual * (cutend[1] - cutstart[1])
|
2137
|
+
# interim_pen = wx.Pen(wx.GREEN, 1, wx.PENSTYLE_SOLID)
|
2138
|
+
# gc.SetPen(interim_pen)
|
2139
|
+
# gc.DrawRectangle(rect_x, rect_y, rect_w, rect_h)
|
2140
|
+
end = wx.Point2D(
|
2141
|
+
cutstart[0] + residual * (cutend[0] - cutstart[0]),
|
2142
|
+
cutstart[1] + residual * (cutend[1] - cutstart[1]),
|
2143
|
+
)
|
2144
|
+
starts.append(cutstart)
|
2145
|
+
ends.append(end)
|
2146
|
+
interim_pen = wx.Pen(wx.GREEN, 1, wx.PENSTYLE_SOLID)
|
2147
|
+
gc.SetPen(interim_pen)
|
2148
|
+
gc.StrokeLineSegments(starts, ends)
|
2149
|
+
|
2150
|
+
|
2151
|
+
class SimulationTravelWidget(Widget):
|
2152
|
+
"""
|
2153
|
+
The simulation Travel Widget is responsible for the background of dotted lines and arrows
|
2154
|
+
within the simulation scene.
|
2155
|
+
"""
|
2156
|
+
|
2157
|
+
def __init__(self, scene, sim):
|
2158
|
+
Widget.__init__(self, scene, all=False)
|
2159
|
+
self.sim_matrix = ~scene.context.device.view.matrix
|
2160
|
+
self.sim = sim
|
2161
|
+
self.matrix.post_cat(~scene.context.device.view.matrix)
|
2162
|
+
self.display = True
|
2163
|
+
self.initvars()
|
2164
|
+
|
2165
|
+
def initvars(self):
|
2166
|
+
self.starts = list()
|
2167
|
+
self.ends = list()
|
2168
|
+
self.pos = list()
|
2169
|
+
self.starts.append(wx.Point2D(0, 0))
|
2170
|
+
self.ends.append(wx.Point2D(0, 0))
|
2171
|
+
prev = None
|
2172
|
+
for i, curr in enumerate(list(self.sim.cutcode)):
|
2173
|
+
if prev is not None:
|
2174
|
+
if prev.end != curr.start:
|
2175
|
+
# This is a travel
|
2176
|
+
start = wx.Point2D(*prev.end)
|
2177
|
+
end = wx.Point2D(*curr.start)
|
2178
|
+
self.starts.append(start)
|
2179
|
+
self.ends.append(end)
|
2180
|
+
# print (f"Travel found at idx {i}, {start}->{end}")
|
2181
|
+
s = complex(start[0], start[1])
|
2182
|
+
e = complex(end[0], end[1])
|
2183
|
+
d = abs(s - e)
|
2184
|
+
if d >= 127:
|
2185
|
+
for p in [0.75]:
|
2186
|
+
m = p * (e - s) + s
|
2187
|
+
ang = math.atan2((s - e).imag, (s - e).real)
|
2188
|
+
# arrow_size = d / 10.0
|
2189
|
+
arrow_size = 50
|
2190
|
+
m0 = m + complex(
|
2191
|
+
math.cos(ang + math.tau / 10) * arrow_size,
|
2192
|
+
math.sin(ang + math.tau / 10) * arrow_size,
|
2193
|
+
)
|
2194
|
+
m1 = m + complex(
|
2195
|
+
math.cos(ang - math.tau / 10) * arrow_size,
|
2196
|
+
math.sin(ang - math.tau / 10) * arrow_size,
|
2197
|
+
)
|
2198
|
+
m = wx.Point2D(m.real, m.imag)
|
2199
|
+
self.starts.append(m)
|
2200
|
+
self.ends.append(wx.Point2D(m0.real, m0.imag))
|
2201
|
+
self.starts.append(m)
|
2202
|
+
self.ends.append(wx.Point2D(m1.real, m1.imag))
|
2203
|
+
else:
|
2204
|
+
end = wx.Point2D(*curr.start)
|
2205
|
+
self.starts = list()
|
2206
|
+
self.ends = list()
|
2207
|
+
self.starts.append(wx.Point2D(0, 0))
|
2208
|
+
self.ends.append(end)
|
2209
|
+
self.pos.append(len(self.starts))
|
2210
|
+
prev = curr
|
2211
|
+
|
2212
|
+
def process_draw(self, gc: wx.GraphicsContext):
|
2213
|
+
if not self.display:
|
2214
|
+
return
|
2215
|
+
if not len(self.pos):
|
2216
|
+
return
|
2217
|
+
residual = 0
|
2218
|
+
idx = 0
|
2219
|
+
if self.sim.progress < self.sim.max:
|
2220
|
+
idx, residual = self.sim.progress_to_idx(self.sim.progress)
|
2221
|
+
pos = self.pos[idx]
|
2222
|
+
# print(f"TravelWidget, idx={idx}, residual={residual:.3f}, pos={pos}")
|
2223
|
+
else:
|
2224
|
+
pos = self.pos[-1]
|
2225
|
+
if pos < 0:
|
2226
|
+
return
|
2227
|
+
gcscale = get_gc_scale(gc)
|
2228
|
+
if gcscale == 0:
|
2229
|
+
gcscale = 1
|
2230
|
+
linewidth = 1 / gcscale
|
2231
|
+
|
2232
|
+
starts = self.starts[:pos]
|
2233
|
+
ends = self.ends[:pos]
|
2234
|
+
if residual > 0 and idx > 0:
|
2235
|
+
p1 = self.sim.cutcode[idx - 1].end
|
2236
|
+
p2 = self.sim.cutcode[idx - 1].start
|
2237
|
+
# progress = time
|
2238
|
+
t1 = self.sim.statistics[idx - 1]
|
2239
|
+
t2 = self.sim.statistics[idx]
|
2240
|
+
end_time = t1["time_at_end_of_travel"]
|
2241
|
+
# Time after travel.
|
2242
|
+
new_time = t2["time_at_end_of_travel"]
|
2243
|
+
if t1["total_time_travel"] != t2["total_time_travel"]: # Travel time
|
2244
|
+
fact = (min(self.sim.progress, new_time) - end_time) / (
|
2245
|
+
new_time - end_time
|
2246
|
+
)
|
2247
|
+
newstart = wx.Point2D(p1[0], p1[1])
|
2248
|
+
newend = wx.Point2D(
|
2249
|
+
p1[0] + fact * (p2[0] - p1[0]),
|
2250
|
+
p1[1] + fact * (p2[1] - p1[1]),
|
2251
|
+
)
|
2252
|
+
mystarts = list()
|
2253
|
+
myends = list()
|
2254
|
+
mystarts.append(newstart)
|
2255
|
+
myends.append(newend)
|
2256
|
+
interim_pen = wx.Pen(wx.GREEN, 1, wx.PENSTYLE_DOT)
|
2257
|
+
try:
|
2258
|
+
interim_pen.SetWidth(linewidth)
|
2259
|
+
except TypeError:
|
2260
|
+
interim_pen.SetWidth(int(linewidth))
|
2261
|
+
gc.SetPen(interim_pen)
|
2262
|
+
gc.StrokeLineSegments(mystarts, myends)
|
2263
|
+
mypen = wx.Pen(wx.BLACK, 1, wx.PENSTYLE_LONG_DASH)
|
2264
|
+
try:
|
2265
|
+
mypen.SetWidth(linewidth)
|
2266
|
+
except TypeError:
|
2267
|
+
mypen.SetWidth(int(linewidth))
|
2268
|
+
gc.SetPen(mypen)
|
2269
|
+
gc.StrokeLineSegments(starts, ends)
|
2270
|
+
# for idx, pt_start in enumerate(starts):
|
2271
|
+
# pt_end = ends[idx]
|
2272
|
+
# print (f"#{idx}: ({pt_start[0]:.0f}, {pt_start[1]:.0f}) - ({pt_end[0]:.0f}, {pt_end[1]:.0f})")
|
2273
|
+
# starts = list()
|
2274
|
+
# ends = list()
|
2275
|
+
# starts.append(wx.Point2D(0, 0))
|
2276
|
+
# ends.append(wx.Point2D(10000, 10000))
|
2277
|
+
# starts.append(wx.Point2D(0, 10000))
|
2278
|
+
# ends.append(wx.Point2D(10000, 0))
|
2279
|
+
# gc.SetPen(wx.CYAN_PEN)
|
2280
|
+
# gc.StrokeLineSegments(starts, ends)
|
2281
|
+
|
2282
|
+
|
2283
|
+
class SimReticleWidget(Widget):
|
2284
|
+
"""
|
2285
|
+
The simulation Reticle widget is responsible for rendering the three green circles.
|
2286
|
+
The position at 0 should be 0,0. At 1 the start position. And at all other positions
|
2287
|
+
the end of the current cut object.
|
2288
|
+
"""
|
2289
|
+
|
2290
|
+
def __init__(self, scene, sim):
|
2291
|
+
Widget.__init__(self, scene, all=False)
|
2292
|
+
self.sim_matrix = ~scene.context.device.view.matrix
|
2293
|
+
self.sim = sim
|
2294
|
+
|
2295
|
+
def process_draw(self, gc):
|
2296
|
+
x = 0
|
2297
|
+
y = 0
|
2298
|
+
if (
|
2299
|
+
# self.sim.progress > 0 and
|
2300
|
+
self.sim.cutcode is not None
|
2301
|
+
and len(self.sim.cutcode)
|
2302
|
+
):
|
2303
|
+
idx, residual = self.sim.progress_to_idx(self.sim.progress)
|
2304
|
+
dx = 0
|
2305
|
+
dy = 0
|
2306
|
+
if self.sim.progress != self.sim.max:
|
2307
|
+
if idx > 0:
|
2308
|
+
pos = self.sim.cutcode[idx - 1].end
|
2309
|
+
else:
|
2310
|
+
pos = self.sim.cutcode[idx].start
|
2311
|
+
if residual > 0:
|
2312
|
+
# We could still be traversing or already burning...
|
2313
|
+
# We have two time stamps one after travel,
|
2314
|
+
# one after burn
|
2315
|
+
item = self.sim.statistics[idx]
|
2316
|
+
# print(
|
2317
|
+
# f"Time stamp: {self.sim.progress}, "
|
2318
|
+
# + f"at start: {item['time_at_start']}, "
|
2319
|
+
# + f"after travel: {item['time_at_end_of_travel']}, "
|
2320
|
+
# + f"after burn: {item['time_at_end_of_burn']}"
|
2321
|
+
# )
|
2322
|
+
if self.sim.progress < item["time_at_end_of_travel"]:
|
2323
|
+
# All travel done...
|
2324
|
+
fraction = (self.sim.progress - item["time_at_start"]) / (
|
2325
|
+
item["time_at_end_of_travel"] - item["time_at_start"]
|
2326
|
+
)
|
2327
|
+
pos = self.sim.cutcode[idx - 1].end
|
2328
|
+
npos = self.sim.cutcode[idx].start
|
2329
|
+
else:
|
2330
|
+
# Still travelling, duration
|
2331
|
+
fraction = (
|
2332
|
+
self.sim.progress - item["time_at_end_of_travel"]
|
2333
|
+
) / (
|
2334
|
+
item["time_at_end_of_burn"] - item["time_at_end_of_travel"]
|
2335
|
+
)
|
2336
|
+
pos = self.sim.cutcode[idx].start
|
2337
|
+
npos = self.sim.cutcode[idx].end
|
2338
|
+
|
2339
|
+
dx = fraction * (npos[0] - pos[0])
|
2340
|
+
dy = fraction * (npos[1] - pos[1])
|
2341
|
+
else:
|
2342
|
+
pos = self.sim.cutcode[idx].end
|
2343
|
+
x = pos[0] + dx
|
2344
|
+
y = pos[1] + dy
|
2345
|
+
x, y = self.sim_matrix.point_in_matrix_space((x, y))
|
2346
|
+
|
2347
|
+
try:
|
2348
|
+
# Draw Reticle
|
2349
|
+
gc.SetPen(wx.Pen(wx.Colour(0, 255, 0, alpha=127)))
|
2350
|
+
gc.SetBrush(wx.TRANSPARENT_BRUSH)
|
2351
|
+
x, y = self.scene.convert_scene_to_window([x, y])
|
2352
|
+
gc.DrawEllipse(x - 5, y - 5, 10, 10)
|
2353
|
+
gc.DrawEllipse(x - 10, y - 10, 20, 20)
|
2354
|
+
gc.DrawEllipse(x - 20, y - 20, 40, 40)
|
2355
|
+
except AttributeError:
|
2356
|
+
pass
|
2357
|
+
|
2358
|
+
|
2359
|
+
class Simulation(MWindow):
|
2360
|
+
def __init__(self, *args, **kwds):
|
2361
|
+
super().__init__(706, 755, *args, **kwds)
|
2362
|
+
# We do this very early to allow resizing events to do their thing...
|
2363
|
+
self.restore_aspect(honor_initial_values=True)
|
2364
|
+
if len(args) > 3:
|
2365
|
+
plan_name = args[3]
|
2366
|
+
else:
|
2367
|
+
plan_name = None
|
2368
|
+
if len(args) > 4:
|
2369
|
+
auto_clear = bool(int(args[4]))
|
2370
|
+
else:
|
2371
|
+
auto_clear = True
|
2372
|
+
if len(args) > 5:
|
2373
|
+
optimise = bool(int(args[5]))
|
2374
|
+
else:
|
2375
|
+
optimise = True
|
2376
|
+
|
2377
|
+
self.panel = SimulationPanel(
|
2378
|
+
self,
|
2379
|
+
wx.ID_ANY,
|
2380
|
+
context=self.context,
|
2381
|
+
plan_name=plan_name,
|
2382
|
+
auto_clear=auto_clear,
|
2383
|
+
optimise_at_start=optimise,
|
2384
|
+
)
|
2385
|
+
self.sizer.Add(self.panel, 1, wx.EXPAND, 0)
|
2386
|
+
_icon = wx.NullIcon
|
2387
|
+
_icon.CopyFromBitmap(icons8_laser_beam_hazard.GetBitmap())
|
2388
|
+
self.SetIcon(_icon)
|
2389
|
+
self.SetTitle(_("Simulation"))
|
2390
|
+
self.Layout()
|
2391
|
+
|
2392
|
+
@staticmethod
|
2393
|
+
def sub_register(kernel):
|
2394
|
+
def handler(opt):
|
2395
|
+
optpart = " preopt optimize" if opt else ""
|
2396
|
+
|
2397
|
+
busy = kernel.busyinfo
|
2398
|
+
busy.change(msg=_("Preparing simulation..."))
|
2399
|
+
busy.start()
|
2400
|
+
|
2401
|
+
kernel.console(
|
2402
|
+
f"planz copy preprocess validate blob{optpart}\nwindow toggle Simulation z\n"
|
2403
|
+
)
|
2404
|
+
busy.end()
|
2405
|
+
|
2406
|
+
def open_simulator(*args):
|
2407
|
+
opt = kernel.planner.do_optimization
|
2408
|
+
handler(opt)
|
2409
|
+
|
2410
|
+
def open_simulator_simple(*args):
|
2411
|
+
handler(False)
|
2412
|
+
|
2413
|
+
kernel.register(
|
2414
|
+
"button/jobstart/Simulation",
|
2415
|
+
{
|
2416
|
+
"label": _("Simulate"),
|
2417
|
+
"icon": icons8_laser_beam_hazard,
|
2418
|
+
"tip": _("Simulate the current laser job") + "\n" + _("(Right click: no optimisation)"),
|
2419
|
+
"action": open_simulator,
|
2420
|
+
"action_right": open_simulator_simple,
|
2421
|
+
"rule_enabled": lambda cond: kernel.elements.have_burnable_elements(),
|
2422
|
+
"size": STD_ICON_SIZE,
|
2423
|
+
"priority": 1,
|
2424
|
+
},
|
2425
|
+
)
|
2426
|
+
|
2427
|
+
def window_open(self):
|
2428
|
+
self.panel.pane_show()
|
2429
|
+
|
2430
|
+
def window_close(self):
|
2431
|
+
self.panel.pane_hide()
|
2432
|
+
|
2433
|
+
def delegates(self):
|
2434
|
+
yield self.panel
|
2435
|
+
|
2436
|
+
@signal_listener("background")
|
2437
|
+
def on_background_signal(self, origin, background):
|
2438
|
+
if background is not None:
|
2439
|
+
background = wx.Bitmap.FromBuffer(*background)
|
2440
|
+
self.panel.widget_scene._signal_widget(
|
2441
|
+
self.panel.widget_scene.widget_root, "background", background
|
2442
|
+
)
|
2443
|
+
self.panel.widget_scene.request_refresh()
|
2444
|
+
|
2445
|
+
@staticmethod
|
2446
|
+
def submenu():
|
2447
|
+
return "Burning", "Simulation"
|
2448
|
+
|
2449
|
+
@staticmethod
|
2450
|
+
def helptext():
|
2451
|
+
return _("Display the job simulation window to see what will happen...")
|