meerk40t 0.9.3001__py2.py3-none-any.whl → 0.9.7010__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +1195 -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 +1844 -1507
- meerk40t/core/elements/clipboard.py +229 -219
- meerk40t/core/elements/element_treeops.py +4561 -2837
- meerk40t/core/elements/element_types.py +125 -105
- meerk40t/core/elements/elements.py +4329 -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 +933 -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/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 +462 -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 +198 -0
- meerk40t/extra/ezd.py +1165 -1165
- meerk40t/extra/hershey.py +835 -340
- meerk40t/extra/imageactions.py +322 -316
- meerk40t/extra/inkscape.py +630 -622
- meerk40t/extra/lbrn.py +424 -424
- meerk40t/extra/outerworld.py +284 -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 +1081 -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 +170 -133
- meerk40t/gui/choicepropertypanel.py +1673 -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 +1430 -846
- meerk40t/gui/icons.py +3422 -2747
- meerk40t/gui/imagesplitter.py +555 -508
- meerk40t/gui/keymap.py +354 -344
- meerk40t/gui/laserpanel.py +892 -806
- meerk40t/gui/laserrender.py +1470 -1232
- meerk40t/gui/lasertoolpanel.py +805 -793
- meerk40t/gui/magnetoptions.py +436 -0
- meerk40t/gui/materialmanager.py +2917 -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 +494 -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 +2468 -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 +589 -346
- meerk40t/gui/scenewidgets/relocatewidget.py +33 -33
- meerk40t/gui/scenewidgets/reticlewidget.py +83 -83
- meerk40t/gui/scenewidgets/selectionwidget.py +2952 -2756
- meerk40t/gui/simpleui.py +357 -333
- meerk40t/gui/simulation.py +2431 -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 +591 -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 +160 -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 +1444 -1169
- meerk40t/gui/wxmmain.py +5578 -4112
- meerk40t/gui/wxmribbon.py +1591 -1076
- meerk40t/gui/wxmscene.py +1635 -1453
- meerk40t/gui/wxmtree.py +2410 -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 +2778 -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 +3809 -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 +102 -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 +390 -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 +672 -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 +940 -940
- meerk40t/tools/rasterplotter.py +1660 -547
- meerk40t/tools/shxparser.py +989 -901
- meerk40t/tools/ttfparser.py +726 -446
- meerk40t/tools/zinglplotter.py +595 -593
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.dist-info}/LICENSE +21 -21
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.dist-info}/METADATA +150 -139
- meerk40t-0.9.7010.dist-info/RECORD +445 -0
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.dist-info}/WHEEL +1 -1
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.dist-info}/top_level.txt +0 -1
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.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.7010.dist-info}/entry_points.txt +0 -0
meerk40t/core/node/op_image.py
CHANGED
@@ -1,369 +1,755 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
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
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
default_map["
|
82
|
-
|
83
|
-
)
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
if
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
if
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
self.
|
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
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
self.settings
|
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
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
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
|
-
|
1
|
+
"""
|
2
|
+
ImageOpNode defines an operation for processing images in the laser cutting application.
|
3
|
+
|
4
|
+
This class represents a node of type "op image" and manages the settings and behaviors
|
5
|
+
associated with image operations, including image processing, classification, and
|
6
|
+
time estimation for operations. It allows for the manipulation of image attributes,
|
7
|
+
the application of raster cuts, and the handling of drag-and-drop functionality for
|
8
|
+
image nodes.
|
9
|
+
|
10
|
+
Args:
|
11
|
+
settings (dict, optional): Initial settings for the operation.
|
12
|
+
**kwargs: Additional keyword arguments for node initialization.
|
13
|
+
|
14
|
+
Methods:
|
15
|
+
classify(node, fuzzy=False, fuzzydistance=100, usedefault=False): Classifies the given node based on its attributes.
|
16
|
+
load(settings, section): Loads settings from a specified section.
|
17
|
+
save(settings, section): Saves the current settings to a specified section.
|
18
|
+
time_estimate(): Estimates the time required for the operation based on its parameters.
|
19
|
+
preprocess(context, matrix, plan): Preprocesses the operation for execution based on the provided context and matrix.
|
20
|
+
as_cutobjects(closed_distance=15, passes=1): Generates cut objects for the image operation.
|
21
|
+
"""
|
22
|
+
from copy import copy
|
23
|
+
from math import isnan
|
24
|
+
from meerk40t.constants import (
|
25
|
+
RASTER_T2B,
|
26
|
+
RASTER_B2T,
|
27
|
+
RASTER_R2L,
|
28
|
+
RASTER_L2R,
|
29
|
+
RASTER_HATCH,
|
30
|
+
RASTER_GREEDY_H,
|
31
|
+
RASTER_GREEDY_V,
|
32
|
+
RASTER_CROSSOVER,
|
33
|
+
RASTER_SPIRAL,
|
34
|
+
)
|
35
|
+
from meerk40t.core.cutcode.rastercut import RasterCut
|
36
|
+
from meerk40t.core.elements.element_types import *
|
37
|
+
from meerk40t.core.node.node import Node
|
38
|
+
from meerk40t.core.parameters import Parameters
|
39
|
+
from meerk40t.core.units import MM_PER_INCH, UNITS_PER_INCH, UNITS_PER_MM, Length
|
40
|
+
from meerk40t.svgelements import Color, Path, Polygon
|
41
|
+
|
42
|
+
|
43
|
+
class ImageOpNode(Node, Parameters):
|
44
|
+
"""
|
45
|
+
Default object defining any operation done on the laser.
|
46
|
+
|
47
|
+
This is a Node of type "op image".
|
48
|
+
"""
|
49
|
+
|
50
|
+
def __init__(self, settings=None, **kwargs):
|
51
|
+
if settings is not None:
|
52
|
+
settings = dict(settings)
|
53
|
+
Parameters.__init__(self, settings, **kwargs)
|
54
|
+
|
55
|
+
# Is this op out of useful bounds?
|
56
|
+
self.dangerous = False
|
57
|
+
self.stopop = True
|
58
|
+
self.label = "Image"
|
59
|
+
self.overrule_dpi = False
|
60
|
+
self._allowed_elements_dnd = ("elem image",)
|
61
|
+
self._allowed_elements = ("elem image",)
|
62
|
+
self.consider_laserspot = False
|
63
|
+
self._spot_in_device_units = 0
|
64
|
+
self._instructions = {}
|
65
|
+
|
66
|
+
self.allowed_attributes = []
|
67
|
+
super().__init__(type="op image", **kwargs)
|
68
|
+
self._formatter = "{enabled}{pass}{element_type}{direction}{speed}mm/s @{power}"
|
69
|
+
# They might come from a svg read, but shouldnt be in settings
|
70
|
+
for attrib in ("lock", "dangerous", "use_grayscale", "consider_laserspot", "overrule_dpi"):
|
71
|
+
if attrib in self.settings:
|
72
|
+
del self.settings[attrib]
|
73
|
+
|
74
|
+
def __repr__(self):
|
75
|
+
return "ImageOpNode()"
|
76
|
+
|
77
|
+
def default_map(self, default_map=None):
|
78
|
+
default_map = super().default_map(default_map=default_map)
|
79
|
+
default_map["element_type"] = "Image"
|
80
|
+
default_map["dpi"] = str(int(self.dpi)) if self.overrule_dpi else ""
|
81
|
+
default_map["danger"] = "❌" if self.dangerous else ""
|
82
|
+
default_map["defop"] = "✓" if self.default else ""
|
83
|
+
default_map["enabled"] = "(Disabled) " if not self.output else ""
|
84
|
+
default_map["pass"] = (
|
85
|
+
f"{self.passes}X " if self.passes_custom and self.passes != 1 else ""
|
86
|
+
)
|
87
|
+
default_map["penpass"] = f"(p:{self.penbox_pass}) " if self.penbox_pass else ""
|
88
|
+
default_map["penvalue"] = (
|
89
|
+
f"(v:{self.penbox_value}) " if self.penbox_value else ""
|
90
|
+
)
|
91
|
+
if self.bidirectional:
|
92
|
+
raster_swing = "="
|
93
|
+
else:
|
94
|
+
raster_swing = "-"
|
95
|
+
if self.raster_direction == RASTER_T2B:
|
96
|
+
raster_dir = "T2B"
|
97
|
+
elif self.raster_direction == RASTER_B2T:
|
98
|
+
raster_dir = "B2T"
|
99
|
+
elif self.raster_direction == RASTER_R2L:
|
100
|
+
raster_dir = "R2L"
|
101
|
+
elif self.raster_direction == RASTER_L2R:
|
102
|
+
raster_dir = "L2R"
|
103
|
+
elif self.raster_direction == RASTER_HATCH:
|
104
|
+
raster_dir = "X"
|
105
|
+
elif self.raster_direction == RASTER_CROSSOVER:
|
106
|
+
raster_dir = "|-|-"
|
107
|
+
elif self.raster_direction == RASTER_GREEDY_H:
|
108
|
+
raster_dir = "GR-"
|
109
|
+
elif self.raster_direction == RASTER_GREEDY_V:
|
110
|
+
raster_dir = "GR|"
|
111
|
+
elif self.raster_direction == RASTER_SPIRAL:
|
112
|
+
raster_dir = "(.)"
|
113
|
+
else:
|
114
|
+
raster_dir = str(self.raster_direction)
|
115
|
+
default_map["direction"] = f"{raster_swing}{raster_dir} "
|
116
|
+
default_map["speed"] = "default"
|
117
|
+
default_map["power"] = "default"
|
118
|
+
default_map["frequency"] = "default"
|
119
|
+
default_map["opstop"] = "<stop>" if self.stopop else ""
|
120
|
+
default_map.update(self.settings)
|
121
|
+
default_map["color"] = self.color.hexrgb if self.color is not None else ""
|
122
|
+
default_map["colcode"] = self.color.hexrgb if self.color is not None else ""
|
123
|
+
default_map["overscan"] = f"±{self.overscan}"
|
124
|
+
# print(self.dangerous, self.stopop, self.raster_direction)
|
125
|
+
default_map["percent"] = "100%"
|
126
|
+
default_map["ppi"] = "default"
|
127
|
+
if self.power is not None:
|
128
|
+
default_map["percent"] = f"{self.power / 10.0:.0f}%"
|
129
|
+
default_map["ppi"] = f"{self.power:.0f}"
|
130
|
+
default_map["speed_mm_min"] = (
|
131
|
+
"" if self.speed is None else f"{self.speed * 60:.0f}"
|
132
|
+
)
|
133
|
+
return default_map
|
134
|
+
|
135
|
+
def can_drop(self, drag_node):
|
136
|
+
# Default routine for drag + drop for an op node - irrelevant for others...
|
137
|
+
if drag_node.has_ancestor("branch reg"):
|
138
|
+
# Will be dealt with in elements -
|
139
|
+
# we don't implement a more sophisticated routine here
|
140
|
+
return False
|
141
|
+
if hasattr(drag_node, "as_image"):
|
142
|
+
return True
|
143
|
+
elif drag_node.type == "reference" and hasattr(drag_node.node, "as_image"):
|
144
|
+
return True
|
145
|
+
elif drag_node.type in op_nodes:
|
146
|
+
# Move operation to a different position.
|
147
|
+
return True
|
148
|
+
elif drag_node.type in ("file", "group"):
|
149
|
+
return not any(e.has_ancestor("branch reg") for e in drag_node.flat(elem_nodes))
|
150
|
+
return False
|
151
|
+
|
152
|
+
def drop(self, drag_node, modify=True, flag=False):
|
153
|
+
# Default routine for drag + drop for an op node - irrelevant for others...
|
154
|
+
if hasattr(drag_node, "as_image"):
|
155
|
+
if drag_node.has_ancestor("branch reg"):
|
156
|
+
# We do not accept reg nodes.
|
157
|
+
return False
|
158
|
+
# Dragging element onto operation adds that element to the op.
|
159
|
+
if modify:
|
160
|
+
self.add_reference(drag_node, pos=None if flag else 0)
|
161
|
+
return True
|
162
|
+
elif drag_node.type == "reference":
|
163
|
+
# Disallow drop of image refelems onto a Dot op.
|
164
|
+
if not hasattr(drag_node.node, "as_image"):
|
165
|
+
return False
|
166
|
+
# Move a refelem to end of op.
|
167
|
+
if modify:
|
168
|
+
self.append_child(drag_node)
|
169
|
+
return True
|
170
|
+
elif drag_node.type in op_nodes:
|
171
|
+
# Move operation to a different position.
|
172
|
+
if modify:
|
173
|
+
self.insert_sibling(drag_node)
|
174
|
+
return True
|
175
|
+
elif drag_node.type in ("file", "group") and not drag_node.has_ancestor(
|
176
|
+
"branch reg"
|
177
|
+
):
|
178
|
+
some_nodes = False
|
179
|
+
for e in drag_node.flat(elem_nodes):
|
180
|
+
# Add element to operation
|
181
|
+
if hasattr(e, "as_image"):
|
182
|
+
if modify:
|
183
|
+
self.add_reference(e)
|
184
|
+
some_nodes = True
|
185
|
+
return some_nodes
|
186
|
+
return False
|
187
|
+
|
188
|
+
def is_referenced(self, node):
|
189
|
+
for e in self.children:
|
190
|
+
if e is node:
|
191
|
+
return True
|
192
|
+
if hasattr(e, "node") and e.node is node:
|
193
|
+
return True
|
194
|
+
return False
|
195
|
+
|
196
|
+
def valid_node_for_reference(self, node):
|
197
|
+
if hasattr(node, "as_image"):
|
198
|
+
return True
|
199
|
+
else:
|
200
|
+
return False
|
201
|
+
|
202
|
+
def classify(self, node, fuzzy=False, fuzzydistance=100, usedefault=False):
|
203
|
+
if self.is_referenced(node):
|
204
|
+
# No need to add it again...
|
205
|
+
return False, False, None
|
206
|
+
|
207
|
+
feedback = []
|
208
|
+
if hasattr(node, "as_image"):
|
209
|
+
self.add_reference(node)
|
210
|
+
# Have classified and no more classification are needed
|
211
|
+
feedback.append("stroke")
|
212
|
+
feedback.append("fill")
|
213
|
+
return True, self.stopop, feedback
|
214
|
+
return False, False, None
|
215
|
+
|
216
|
+
def load(self, settings, section):
|
217
|
+
settings.read_persistent_attributes(section, self)
|
218
|
+
hexa = self.settings.get("hex_color")
|
219
|
+
if hexa is not None:
|
220
|
+
self.color = Color(hexa)
|
221
|
+
self.updated()
|
222
|
+
|
223
|
+
def save(self, settings, section):
|
224
|
+
# Sync certain properties with self.settings
|
225
|
+
for attr in ("label", "lock", "id"):
|
226
|
+
if hasattr(self, attr) and attr in self.settings:
|
227
|
+
self.settings[attr] = getattr(self, attr)
|
228
|
+
if "hex_color" in self.settings:
|
229
|
+
self.settings["hex_color"] = self.color.hexa
|
230
|
+
|
231
|
+
settings.write_persistent_attributes(section, self)
|
232
|
+
settings.write_persistent(section, "hex_color", self.color.hexa)
|
233
|
+
settings.write_persistent_dict(section, self.settings)
|
234
|
+
|
235
|
+
def time_estimate(self):
|
236
|
+
"""
|
237
|
+
The scanlines would equal "(e.height * 1000) / dpi" but our images are pre-actualized.
|
238
|
+
|
239
|
+
@return:
|
240
|
+
"""
|
241
|
+
estimate = 0
|
242
|
+
for node in self.children:
|
243
|
+
if node.type == "reference":
|
244
|
+
node = node.node
|
245
|
+
try:
|
246
|
+
e = node.image
|
247
|
+
if self.overrule_dpi and self.dpi:
|
248
|
+
dpi = self.dpi
|
249
|
+
else:
|
250
|
+
dpi = node.dpi
|
251
|
+
except AttributeError:
|
252
|
+
continue
|
253
|
+
min_x, min_y, max_x, max_y = node.bounds
|
254
|
+
width_in_inches = (max_x - min_x) / UNITS_PER_INCH
|
255
|
+
height_in_inches = (max_y - min_y) / UNITS_PER_INCH
|
256
|
+
speed_in_per_s = self.speed / MM_PER_INCH
|
257
|
+
if self.raster_direction in (
|
258
|
+
RASTER_T2B, RASTER_B2T, RASTER_HATCH,
|
259
|
+
RASTER_GREEDY_H, RASTER_CROSSOVER, RASTER_SPIRAL,
|
260
|
+
):
|
261
|
+
scanlines = height_in_inches * dpi
|
262
|
+
if not self.bidirectional:
|
263
|
+
scanlines *= 2
|
264
|
+
estimate += (
|
265
|
+
scanlines * width_in_inches / speed_in_per_s
|
266
|
+
+ height_in_inches / speed_in_per_s
|
267
|
+
)
|
268
|
+
this_len = scanlines * width_in_inches + height_in_inches
|
269
|
+
estimate += this_len / speed_in_per_s
|
270
|
+
# print (f"Horizontal scanlines: {scanlines}, Length: {this_len:.1f}")
|
271
|
+
if self.raster_direction in (RASTER_L2R, RASTER_R2L, RASTER_HATCH, RASTER_GREEDY_V):
|
272
|
+
scanlines = width_in_inches * dpi
|
273
|
+
if not self.bidirectional:
|
274
|
+
scanlines *= 2
|
275
|
+
this_len = scanlines * height_in_inches + width_in_inches
|
276
|
+
estimate += this_len / speed_in_per_s
|
277
|
+
# print (f"Vertical scanlines: {scanlines}, Length: {this_len:.1f}")
|
278
|
+
if self.passes_custom and self.passes != 1:
|
279
|
+
estimate *= max(self.passes, 1)
|
280
|
+
|
281
|
+
if isnan(estimate):
|
282
|
+
estimate = 0
|
283
|
+
|
284
|
+
hours, remainder = divmod(estimate, 3600)
|
285
|
+
minutes, seconds = divmod(remainder, 60)
|
286
|
+
return f"{int(hours)}:{str(int(minutes)).zfill(2)}:{str(int(seconds)).zfill(2)}"
|
287
|
+
|
288
|
+
def preprocess(self, context, matrix, plan):
|
289
|
+
"""
|
290
|
+
Process the scale to native resolution done with the given matrix. In the case of image ops we are scaling
|
291
|
+
the overscan length into usable native units.
|
292
|
+
|
293
|
+
@param context:
|
294
|
+
@param matrix:
|
295
|
+
@param plan:
|
296
|
+
@return:
|
297
|
+
"""
|
298
|
+
self._spot_in_device_units = 0
|
299
|
+
self._instructions = {}
|
300
|
+
if hasattr(context, "device"):
|
301
|
+
if hasattr(context.device, "get_raster_instructions"):
|
302
|
+
self._instructions = context.device.get_raster_instructions()
|
303
|
+
|
304
|
+
if self.consider_laserspot:
|
305
|
+
try:
|
306
|
+
laserspot = getattr(context.device, "laserspot", "0.3mm")
|
307
|
+
spot = 2 * float(Length(laserspot)) / ( context.device.view.native_scale_x + context.device.view.native_scale_y)
|
308
|
+
# print (f"Laserpot in device units: {spot:.2f} [{laserspot.length_mm}], scale: {context.device.view.native_scale_x + context.device.view.native_scale_y:.2f}")
|
309
|
+
except (ValueError, AttributeError):
|
310
|
+
spot = 0
|
311
|
+
self._spot_in_device_units = spot
|
312
|
+
|
313
|
+
try:
|
314
|
+
overscan = float(Length(self.overscan))
|
315
|
+
except ValueError:
|
316
|
+
overscan = 0
|
317
|
+
transformed_vector = matrix.transform_vector([0, overscan])
|
318
|
+
self.overscan = abs(complex(transformed_vector[0], transformed_vector[1]))
|
319
|
+
|
320
|
+
native_mm = abs(complex(*matrix.transform_vector([0, UNITS_PER_MM])))
|
321
|
+
self.settings["native_mm"] = native_mm
|
322
|
+
self.settings["native_speed"] = self.speed * native_mm
|
323
|
+
self.settings["native_rapid_speed"] = self.rapid_speed * native_mm
|
324
|
+
for node in self.children:
|
325
|
+
if self.overrule_dpi and self.dpi:
|
326
|
+
node.dpi = self.dpi
|
327
|
+
|
328
|
+
def actual(image_node):
|
329
|
+
def process_images():
|
330
|
+
if hasattr(image_node, "process_image"):
|
331
|
+
if image_node._keyhole_geometry is not None:
|
332
|
+
image_node._keyhole_image = None
|
333
|
+
image_node._cache = None
|
334
|
+
image_node._keyhole_geometry.transform(matrix)
|
335
|
+
image_node._context = context
|
336
|
+
image_node.process_image()
|
337
|
+
|
338
|
+
return process_images
|
339
|
+
|
340
|
+
commands = plan.commands
|
341
|
+
commands.append(actual(node))
|
342
|
+
|
343
|
+
msx = matrix.value_scale_x()
|
344
|
+
msy = matrix.value_scale_y()
|
345
|
+
rotated = False
|
346
|
+
negative_scale_x = msx < 0
|
347
|
+
negative_scale_y = msy < 0
|
348
|
+
if msx == 0 and msy == 0:
|
349
|
+
# Rotated view
|
350
|
+
rotated = True
|
351
|
+
p1a = matrix.point_in_matrix_space((0, 0))
|
352
|
+
p2a = matrix.point_in_matrix_space((1, 0))
|
353
|
+
dx = p1a.x - p2a.x
|
354
|
+
dy = p1a.y - p2a.y
|
355
|
+
negative_scale_x = bool(dy < 0) if dx == 0 else bool(dx < 0)
|
356
|
+
negative_scale_y = False if dx == 0 else bool(dy < 0)
|
357
|
+
if rotated:
|
358
|
+
mapping = {
|
359
|
+
RASTER_T2B: RASTER_L2R,
|
360
|
+
RASTER_B2T: RASTER_R2L,
|
361
|
+
RASTER_R2L: RASTER_B2T,
|
362
|
+
RASTER_L2R: RASTER_T2B,
|
363
|
+
RASTER_GREEDY_H: RASTER_GREEDY_V,
|
364
|
+
RASTER_GREEDY_V: RASTER_GREEDY_H,
|
365
|
+
}
|
366
|
+
if self.raster_direction in mapping:
|
367
|
+
self.raster_direction = mapping[self.raster_direction]
|
368
|
+
if negative_scale_y:
|
369
|
+
# Y is negative scale, flip raster_direction if needed
|
370
|
+
self.raster_preference_top = not self.raster_preference_top
|
371
|
+
if self.raster_direction == RASTER_T2B:
|
372
|
+
self.raster_direction = RASTER_B2T
|
373
|
+
elif self.raster_direction == RASTER_B2T:
|
374
|
+
self.raster_direction = RASTER_T2B
|
375
|
+
if negative_scale_x:
|
376
|
+
# X is negative scale, flip raster_direction if needed
|
377
|
+
self.raster_preference_left = not self.raster_preference_left
|
378
|
+
if self.raster_direction == RASTER_R2L:
|
379
|
+
self.raster_direction = RASTER_L2R
|
380
|
+
elif self.raster_direction == RASTER_L2R:
|
381
|
+
self.raster_direction = RASTER_R2L
|
382
|
+
|
383
|
+
# Look for registered raster (image) preprocessors,
|
384
|
+
# these are routines that take one image as parameter
|
385
|
+
# and deliver a set of (result image, method (aka raster_direction) )
|
386
|
+
# that will be dealt with independently
|
387
|
+
# The registered datastructure is (rasterid, description, method)
|
388
|
+
def call_me(method):
|
389
|
+
def handler():
|
390
|
+
method(self)
|
391
|
+
return handler
|
392
|
+
|
393
|
+
for key, description, method in context.kernel.lookup_all("raster_preprocessor/.*"):
|
394
|
+
if key == self.raster_direction:
|
395
|
+
plan.commands.append(call_me(method))
|
396
|
+
# print (f"Found {description}")
|
397
|
+
break
|
398
|
+
|
399
|
+
def as_cutobjects(self, closed_distance=15, passes=1):
|
400
|
+
"""
|
401
|
+
Generator of cutobjects for the image operation. This takes any image node children
|
402
|
+
and converts them into rastercut cutobjects.
|
403
|
+
"""
|
404
|
+
unsupported = self._instructions.get("unsupported_opt", ())
|
405
|
+
if self.raster_direction in unsupported:
|
406
|
+
self.raster_direction = RASTER_T2B
|
407
|
+
|
408
|
+
for image_node in self.children:
|
409
|
+
cutcodes = []
|
410
|
+
# Process each child. All settings are different for each child.
|
411
|
+
if image_node.type == "reference":
|
412
|
+
image_node = image_node.node
|
413
|
+
if not hasattr(image_node, "as_image"):
|
414
|
+
continue
|
415
|
+
if getattr(image_node, "hidden", False):
|
416
|
+
continue
|
417
|
+
cutsettings = self.derive()
|
418
|
+
settings = dict(cutsettings)
|
419
|
+
# Set overscan
|
420
|
+
overscan = self.overscan
|
421
|
+
if not isinstance(overscan, float):
|
422
|
+
overscan = float(Length(overscan))
|
423
|
+
|
424
|
+
# Set variables by direction
|
425
|
+
if hasattr(image_node, "direction") and image_node.direction is not None:
|
426
|
+
direction = image_node.direction
|
427
|
+
else:
|
428
|
+
direction = self.raster_direction
|
429
|
+
|
430
|
+
horizontal = False
|
431
|
+
bidirectional = self.bidirectional
|
432
|
+
start_on_left = self.raster_preference_left
|
433
|
+
start_on_top = self.raster_preference_top
|
434
|
+
if direction in (RASTER_GREEDY_V, RASTER_L2R, RASTER_R2L):
|
435
|
+
horizontal = False
|
436
|
+
if direction in (RASTER_B2T, RASTER_T2B, RASTER_HATCH, RASTER_CROSSOVER, RASTER_GREEDY_H):
|
437
|
+
horizontal = True
|
438
|
+
if direction in (RASTER_T2B, RASTER_CROSSOVER):
|
439
|
+
start_on_top = True
|
440
|
+
if direction == RASTER_B2T:
|
441
|
+
start_on_top = False
|
442
|
+
if direction == RASTER_R2L:
|
443
|
+
start_on_left = False
|
444
|
+
if direction == RASTER_L2R:
|
445
|
+
start_on_left = True
|
446
|
+
if direction in (RASTER_GREEDY_H, RASTER_GREEDY_V, RASTER_CROSSOVER):
|
447
|
+
bidirectional = True
|
448
|
+
|
449
|
+
# Set variables
|
450
|
+
pil_image, bounds = image_node.as_image()
|
451
|
+
offset_x = bounds[0]
|
452
|
+
offset_y = bounds[1]
|
453
|
+
|
454
|
+
# Get steps from individual images
|
455
|
+
image_width, image_height = pil_image.size
|
456
|
+
expected_width = bounds[2] - bounds[0]
|
457
|
+
expected_height = bounds[3] - bounds[1]
|
458
|
+
step_x = expected_width / image_width
|
459
|
+
step_y = expected_height / image_height
|
460
|
+
|
461
|
+
dotwidth = 2 * self._spot_in_device_units / (step_x + step_y)
|
462
|
+
|
463
|
+
if horizontal:
|
464
|
+
# Raster step is only along y for horizontal raster
|
465
|
+
settings["raster_step_x"] = 0
|
466
|
+
settings["raster_step_y"] = step_y
|
467
|
+
else:
|
468
|
+
# Raster step is only along x for vertical raster
|
469
|
+
settings["raster_step_x"] = step_x
|
470
|
+
settings["raster_step_y"] = 0
|
471
|
+
|
472
|
+
# Establish path
|
473
|
+
min_x = offset_x
|
474
|
+
min_y = offset_y
|
475
|
+
max_x = offset_x + pil_image.width * step_x
|
476
|
+
max_y = offset_y + pil_image.height * step_y
|
477
|
+
path = Path(
|
478
|
+
Polygon(
|
479
|
+
(min_x, min_y),
|
480
|
+
(min_x, max_y),
|
481
|
+
(max_x, max_y),
|
482
|
+
(max_x, min_y),
|
483
|
+
)
|
484
|
+
)
|
485
|
+
inverted = False
|
486
|
+
# Not used!
|
487
|
+
if image_node.is_depthmap:
|
488
|
+
# Make sure it's grayscale...
|
489
|
+
if pil_image.mode != "L":
|
490
|
+
pil_image = pil_image.convert("L")
|
491
|
+
|
492
|
+
gres = image_node.depth_resolution
|
493
|
+
if gres < 0:
|
494
|
+
gres = 0
|
495
|
+
if gres > 255:
|
496
|
+
gres = 255
|
497
|
+
stepsize = 255 / gres
|
498
|
+
|
499
|
+
# no need for the filter as we have already moved every
|
500
|
+
# pixel during preprocessing to either 255 or 0
|
501
|
+
# def image_filter(pixel):
|
502
|
+
# # We ignore grayscale and move it into black-white = always on
|
503
|
+
# # The filter takes a pixel value between 0=black and 255=white
|
504
|
+
# # provides and creates a power value of 1.0 for black
|
505
|
+
# # and 0.0 for white
|
506
|
+
# if pixel == 255:
|
507
|
+
# return 0.0
|
508
|
+
# else:
|
509
|
+
# return 1.0
|
510
|
+
image_filter = None
|
511
|
+
|
512
|
+
if inverted:
|
513
|
+
delta = +1
|
514
|
+
start_pixel = 0
|
515
|
+
else:
|
516
|
+
delta = -1
|
517
|
+
start_pixel = 255
|
518
|
+
for gray in range(image_node.depth_resolution):
|
519
|
+
skip_pixel = int(start_pixel + gray * delta * stepsize)
|
520
|
+
if skip_pixel < 0:
|
521
|
+
skip_pixel = 0
|
522
|
+
if skip_pixel > 255:
|
523
|
+
skip_pixel = 255
|
524
|
+
|
525
|
+
def threshold_filter(pixel):
|
526
|
+
# This threshold filter function is defined to set pixels to white (255) if they are above
|
527
|
+
# or equal to the threshold, and to black (0) if they are below the threshold.
|
528
|
+
return 255 if pixel >= skip_pixel else 0
|
529
|
+
|
530
|
+
cleared_image = pil_image.point(threshold_filter)
|
531
|
+
extrema = cleared_image.getextrema()
|
532
|
+
# print (f"{skip_pixel}: extrema={extrema}")
|
533
|
+
if extrema == (0, 0):
|
534
|
+
# all black
|
535
|
+
# We will burn this
|
536
|
+
pass
|
537
|
+
elif extrema == (255, 255):
|
538
|
+
# all white
|
539
|
+
# we can skip this
|
540
|
+
# print (f"Skipping from {skip_pixel} as image is fully white")
|
541
|
+
continue
|
542
|
+
|
543
|
+
# Create Cut Object
|
544
|
+
rasterimage = copy(cleared_image)
|
545
|
+
cutsettings = dict(settings)
|
546
|
+
cut = RasterCut(
|
547
|
+
image=rasterimage,
|
548
|
+
offset_x=offset_x,
|
549
|
+
offset_y=offset_y,
|
550
|
+
step_x=step_x,
|
551
|
+
step_y=step_y,
|
552
|
+
inverted=inverted,
|
553
|
+
direction=direction,
|
554
|
+
bidirectional=bidirectional,
|
555
|
+
horizontal=horizontal,
|
556
|
+
start_minimum_x=start_on_left,
|
557
|
+
start_minimum_y=start_on_top,
|
558
|
+
overscan=overscan,
|
559
|
+
settings=cutsettings,
|
560
|
+
passes=passes,
|
561
|
+
post_filter=image_filter,
|
562
|
+
label=f"Pass {gray}: cutoff={skip_pixel}",
|
563
|
+
laserspot=dotwidth,
|
564
|
+
special=self._instructions,
|
565
|
+
)
|
566
|
+
cut.path = path
|
567
|
+
cut.original_op = self.type
|
568
|
+
cutcodes.append(cut)
|
569
|
+
|
570
|
+
if direction == RASTER_HATCH:
|
571
|
+
# Create optional crosshatch cut
|
572
|
+
horizontal = not horizontal
|
573
|
+
settings = dict(settings)
|
574
|
+
if horizontal:
|
575
|
+
# Raster step is only along y for horizontal raster
|
576
|
+
settings["raster_step_x"] = 0
|
577
|
+
settings["raster_step_y"] = step_y
|
578
|
+
else:
|
579
|
+
# Raster step is only along x for vertical raster
|
580
|
+
settings["raster_step_x"] = step_x
|
581
|
+
settings["raster_step_y"] = 0
|
582
|
+
rasterimage = copy(cleared_image)
|
583
|
+
cutsettings = dict(settings)
|
584
|
+
cut = RasterCut(
|
585
|
+
image=rasterimage,
|
586
|
+
offset_x=offset_x,
|
587
|
+
offset_y=offset_y,
|
588
|
+
step_x=step_x,
|
589
|
+
step_y=step_y,
|
590
|
+
inverted=inverted,
|
591
|
+
direction=direction,
|
592
|
+
bidirectional=bidirectional,
|
593
|
+
horizontal=horizontal,
|
594
|
+
start_minimum_x=start_on_left,
|
595
|
+
start_minimum_y=start_on_top,
|
596
|
+
overscan=overscan,
|
597
|
+
settings=cutsettings,
|
598
|
+
passes=passes,
|
599
|
+
post_filter=image_filter,
|
600
|
+
label=f"Pass {gray}.2: cutoff={skip_pixel}",
|
601
|
+
laserspot=dotwidth,
|
602
|
+
special=self._instructions,
|
603
|
+
)
|
604
|
+
cut.path = path
|
605
|
+
cut.original_op = self.type
|
606
|
+
cutcodes.append(cut)
|
607
|
+
else:
|
608
|
+
# Create Cut Object for regular image
|
609
|
+
image_filter = None
|
610
|
+
do_optimize = self.raster_direction in (RASTER_GREEDY_H, RASTER_GREEDY_V)
|
611
|
+
if do_optimize:
|
612
|
+
# get some image statistics
|
613
|
+
white_pixels = 0
|
614
|
+
used_colors = pil_image.getcolors()
|
615
|
+
for col_count, col in used_colors:
|
616
|
+
if col==255:
|
617
|
+
white_pixels = col_count
|
618
|
+
break
|
619
|
+
white_pixel_ratio = white_pixels / (pil_image.width * pil_image.height)
|
620
|
+
# print (f"white pixels: {white_pixels}, ratio = {white_pixel_ratio:.3f}")
|
621
|
+
if white_pixel_ratio < 0.3:
|
622
|
+
self.raster_direction = RASTER_T2B if self.raster_direction == RASTER_GREEDY_H else RASTER_L2R
|
623
|
+
|
624
|
+
if self.raster_direction in (RASTER_CROSSOVER, RASTER_SPIRAL): # Crossover - need both
|
625
|
+
settings["raster_step_x"] = step_x
|
626
|
+
settings["raster_step_y"] = step_y
|
627
|
+
if self.raster_direction == RASTER_CROSSOVER and "split_crossover" in self._instructions:
|
628
|
+
self._instructions["mode_filter"] = "ROW"
|
629
|
+
horizontal=True
|
630
|
+
bidirectional=True
|
631
|
+
start_on_top = True
|
632
|
+
start_on_left = True
|
633
|
+
if horizontal:
|
634
|
+
# Raster step is only along y for horizontal raster
|
635
|
+
settings["raster_step_x"] = 0
|
636
|
+
settings["raster_step_y"] = step_y
|
637
|
+
else:
|
638
|
+
# Raster step is only along x for vertical raster
|
639
|
+
settings["raster_step_x"] = step_x
|
640
|
+
settings["raster_step_y"] = 0
|
641
|
+
# Create Cut Object for horizontal lines
|
642
|
+
# The image may be manipulated inside RasterCut, so let's create a fresh copy
|
643
|
+
rasterimage = copy(pil_image)
|
644
|
+
cutsettings = dict(settings)
|
645
|
+
cut = RasterCut(
|
646
|
+
image=rasterimage,
|
647
|
+
offset_x=offset_x,
|
648
|
+
offset_y=offset_y,
|
649
|
+
step_x=step_x,
|
650
|
+
step_y=step_y,
|
651
|
+
inverted=False,
|
652
|
+
direction=direction,
|
653
|
+
bidirectional=bidirectional,
|
654
|
+
horizontal=horizontal,
|
655
|
+
start_minimum_x=start_on_left,
|
656
|
+
start_minimum_y=start_on_top,
|
657
|
+
overscan=overscan,
|
658
|
+
settings=cutsettings,
|
659
|
+
passes=passes,
|
660
|
+
post_filter=image_filter,
|
661
|
+
laserspot=dotwidth,
|
662
|
+
special=dict(self._instructions),
|
663
|
+
)
|
664
|
+
cut.path = path
|
665
|
+
cut.original_op = self.type
|
666
|
+
cutcodes.append(cut)
|
667
|
+
|
668
|
+
# Now set it for the next pass
|
669
|
+
horizontal=False
|
670
|
+
if horizontal:
|
671
|
+
# Raster step is only along y for horizontal raster
|
672
|
+
settings["raster_step_x"] = 0
|
673
|
+
settings["raster_step_y"] = step_y
|
674
|
+
else:
|
675
|
+
# Raster step is only along x for vertical raster
|
676
|
+
settings["raster_step_x"] = step_x
|
677
|
+
settings["raster_step_y"] = 0
|
678
|
+
|
679
|
+
self._instructions["mode_filter"] = "COL"
|
680
|
+
|
681
|
+
# The image may be manipulated inside RasterCut, so let's create a fresh copy
|
682
|
+
rasterimage = copy(pil_image)
|
683
|
+
cutsettings = dict(settings)
|
684
|
+
cut = RasterCut(
|
685
|
+
image=rasterimage,
|
686
|
+
offset_x=offset_x,
|
687
|
+
offset_y=offset_y,
|
688
|
+
step_x=step_x,
|
689
|
+
step_y=step_y,
|
690
|
+
inverted=inverted,
|
691
|
+
bidirectional=bidirectional,
|
692
|
+
direction=direction,
|
693
|
+
horizontal=horizontal,
|
694
|
+
start_minimum_x=start_on_left,
|
695
|
+
start_minimum_y=start_on_top,
|
696
|
+
overscan=overscan,
|
697
|
+
settings=cutsettings,
|
698
|
+
passes=passes,
|
699
|
+
laserspot=dotwidth,
|
700
|
+
special=self._instructions,
|
701
|
+
)
|
702
|
+
cut.path = path
|
703
|
+
cut.original_op = self.type
|
704
|
+
cutcodes.append(cut)
|
705
|
+
if self.raster_direction == RASTER_HATCH:
|
706
|
+
# Create optional crosshatch cut
|
707
|
+
direction = RASTER_L2R if start_on_left else RASTER_R2L
|
708
|
+
horizontal = False
|
709
|
+
settings = dict(settings)
|
710
|
+
if horizontal:
|
711
|
+
# Raster step is only along y for horizontal raster
|
712
|
+
settings["raster_step_x"] = 0
|
713
|
+
settings["raster_step_y"] = step_y
|
714
|
+
else:
|
715
|
+
# Raster step is only along x for vertical raster
|
716
|
+
settings["raster_step_x"] = step_x
|
717
|
+
settings["raster_step_y"] = 0
|
718
|
+
# The image may be manipulated inside RasterCut, so let's create a fresh copy
|
719
|
+
rasterimage = copy(pil_image)
|
720
|
+
cutsettings = dict(settings)
|
721
|
+
cut = RasterCut(
|
722
|
+
image=rasterimage,
|
723
|
+
offset_x=offset_x,
|
724
|
+
offset_y=offset_y,
|
725
|
+
step_x=step_x,
|
726
|
+
step_y=step_y,
|
727
|
+
inverted=inverted,
|
728
|
+
direction=direction,
|
729
|
+
bidirectional=bidirectional,
|
730
|
+
horizontal=horizontal,
|
731
|
+
start_minimum_x=start_on_left,
|
732
|
+
start_minimum_y=start_on_top,
|
733
|
+
overscan=overscan,
|
734
|
+
settings=cutsettings,
|
735
|
+
passes=passes,
|
736
|
+
laserspot=dotwidth,
|
737
|
+
special=self._instructions,
|
738
|
+
)
|
739
|
+
cut.path = path
|
740
|
+
cut.original_op = self.type
|
741
|
+
cutcodes.append(cut)
|
742
|
+
# Yield all generated cutcodes of this image
|
743
|
+
yield from cutcodes
|
744
|
+
|
745
|
+
@property
|
746
|
+
def bounds(self):
|
747
|
+
if not self._bounds_dirty:
|
748
|
+
return self._bounds
|
749
|
+
|
750
|
+
self._bounds = None
|
751
|
+
if self.output:
|
752
|
+
if self._children:
|
753
|
+
self._bounds = Node.union_bounds(self._children, bounds=self._bounds, ignore_locked=False, ignore_hidden=True)
|
754
|
+
self._bounds_dirty = False
|
755
|
+
return self._bounds
|