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/core/node/op_raster.py
CHANGED
@@ -1,522 +1,787 @@
|
|
1
|
-
from copy import copy
|
2
|
-
from math import isnan
|
3
|
-
|
4
|
-
from meerk40t.
|
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
|
-
self.
|
37
|
-
"elem ellipse",
|
38
|
-
"elem path",
|
39
|
-
"elem polyline",
|
40
|
-
"elem rect",
|
41
|
-
"elem line",
|
42
|
-
"elem text",
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
#
|
47
|
-
self.
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
self.
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
if self.
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
default_map["
|
94
|
-
default_map["
|
95
|
-
default_map["
|
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
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
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
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
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
|
-
|
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
|
-
self.
|
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
|
-
if
|
390
|
-
self.
|
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
|
-
|
487
|
-
|
488
|
-
|
489
|
-
)
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
1
|
+
from copy import copy
|
2
|
+
from math import isnan
|
3
|
+
|
4
|
+
from meerk40t.constants import (
|
5
|
+
RASTER_T2B,
|
6
|
+
RASTER_B2T,
|
7
|
+
RASTER_R2L,
|
8
|
+
RASTER_L2R,
|
9
|
+
RASTER_HATCH,
|
10
|
+
RASTER_GREEDY_H,
|
11
|
+
RASTER_GREEDY_V,
|
12
|
+
RASTER_CROSSOVER,
|
13
|
+
RASTER_SPIRAL,
|
14
|
+
)
|
15
|
+
from meerk40t.core.cutcode.rastercut import RasterCut
|
16
|
+
from meerk40t.core.cutplan import CutPlanningFailedError
|
17
|
+
from meerk40t.core.elements.element_types import *
|
18
|
+
from meerk40t.core.node.elem_image import ImageNode
|
19
|
+
from meerk40t.core.node.node import Node
|
20
|
+
from meerk40t.core.parameters import Parameters
|
21
|
+
from meerk40t.core.units import MM_PER_INCH, UNITS_PER_INCH, UNITS_PER_MM, Length
|
22
|
+
from meerk40t.svgelements import Color, Matrix, Path, Polygon
|
23
|
+
|
24
|
+
class RasterOpNode(Node, Parameters):
|
25
|
+
"""
|
26
|
+
Default object defining any raster operation done on the laser.
|
27
|
+
|
28
|
+
This is a Node of type "op raster".
|
29
|
+
"""
|
30
|
+
|
31
|
+
def __init__(self, settings=None, **kwargs):
|
32
|
+
if settings is not None:
|
33
|
+
settings = dict(settings)
|
34
|
+
Parameters.__init__(self, settings, **kwargs)
|
35
|
+
|
36
|
+
self._allowed_elements_dnd = (
|
37
|
+
"elem ellipse",
|
38
|
+
"elem path",
|
39
|
+
"elem polyline",
|
40
|
+
"elem rect",
|
41
|
+
"elem line",
|
42
|
+
"elem text",
|
43
|
+
"elem image",
|
44
|
+
"image raster",
|
45
|
+
)
|
46
|
+
# Which elements do we consider for automatic classification?
|
47
|
+
self._allowed_elements = (
|
48
|
+
"elem ellipse",
|
49
|
+
"elem path",
|
50
|
+
"elem polyline",
|
51
|
+
"elem rect",
|
52
|
+
"elem line",
|
53
|
+
"elem text",
|
54
|
+
)
|
55
|
+
|
56
|
+
# self.allowed_attributes.append("fill")
|
57
|
+
# Is this op out of useful bounds?
|
58
|
+
self.dangerous = False
|
59
|
+
self.stopop = False
|
60
|
+
self.label = "Raster"
|
61
|
+
self.use_grayscale = True
|
62
|
+
self.consider_laserspot = False
|
63
|
+
self._spot_in_device_units = 0
|
64
|
+
self._instructions = {}
|
65
|
+
|
66
|
+
# To which attributes do the classification color check respond
|
67
|
+
# Can be extended / reduced by add_color_attribute / remove_color_attribute
|
68
|
+
# An empty set indicates all nodes will be allowed
|
69
|
+
self.allowed_attributes = []
|
70
|
+
super().__init__(type="op raster", **kwargs)
|
71
|
+
self._formatter = (
|
72
|
+
"{enabled}{pass}{element_type}{direction}{speed}mm/s @{power} {color}"
|
73
|
+
)
|
74
|
+
if isinstance(self.use_grayscale, str):
|
75
|
+
s = self.use_grayscale.lower()
|
76
|
+
self.use_grayscale = s in ("true", "1")
|
77
|
+
if self.use_grayscale is None:
|
78
|
+
self.use_grayscale = True
|
79
|
+
# They might come from a svg read, but shouldnt be in settings
|
80
|
+
for attrib in ("lock", "dangerous", "use_grayscale", "consider_laserspot"):
|
81
|
+
if attrib in self.settings:
|
82
|
+
del self.settings[attrib]
|
83
|
+
|
84
|
+
|
85
|
+
def __repr__(self):
|
86
|
+
return "RasterOp()"
|
87
|
+
|
88
|
+
def default_map(self, default_map=None):
|
89
|
+
default_map = super().default_map(default_map=default_map)
|
90
|
+
default_map["element_type"] = "Raster"
|
91
|
+
default_map["dpi"] = str(int(self.dpi))
|
92
|
+
default_map["danger"] = "❌" if self.dangerous else ""
|
93
|
+
default_map["defop"] = "✓" if self.default else ""
|
94
|
+
default_map["enabled"] = "(Disabled) " if not self.output else ""
|
95
|
+
default_map["pass"] = (
|
96
|
+
f"{self.passes}X " if self.passes_custom and self.passes != 1 else ""
|
97
|
+
)
|
98
|
+
default_map["penpass"] = f"(p:{self.penbox_pass}) " if self.penbox_pass else ""
|
99
|
+
default_map["penvalue"] = (
|
100
|
+
f"(v:{self.penbox_value}) " if self.penbox_value else ""
|
101
|
+
)
|
102
|
+
if self.bidirectional:
|
103
|
+
raster_swing = "="
|
104
|
+
else:
|
105
|
+
raster_swing = "-"
|
106
|
+
if self.raster_direction == RASTER_T2B:
|
107
|
+
raster_dir = "T2B"
|
108
|
+
elif self.raster_direction == RASTER_B2T:
|
109
|
+
raster_dir = "B2T"
|
110
|
+
elif self.raster_direction == RASTER_R2L:
|
111
|
+
raster_dir = "R2L"
|
112
|
+
elif self.raster_direction == RASTER_L2R:
|
113
|
+
raster_dir = "L2R"
|
114
|
+
elif self.raster_direction == RASTER_HATCH:
|
115
|
+
raster_dir = "X"
|
116
|
+
elif self.raster_direction == RASTER_CROSSOVER:
|
117
|
+
raster_dir = "|-|-"
|
118
|
+
elif self.raster_direction == RASTER_GREEDY_H:
|
119
|
+
raster_dir = "GR-"
|
120
|
+
elif self.raster_direction == RASTER_GREEDY_V:
|
121
|
+
raster_dir = "GR|"
|
122
|
+
elif self.raster_direction == RASTER_SPIRAL:
|
123
|
+
raster_dir = "(.)"
|
124
|
+
else:
|
125
|
+
raster_dir = str(self.raster_direction)
|
126
|
+
default_map["direction"] = f"{raster_swing}{raster_dir} "
|
127
|
+
default_map["speed"] = "default"
|
128
|
+
default_map["power"] = "default"
|
129
|
+
default_map["frequency"] = "default"
|
130
|
+
ct = 0
|
131
|
+
t = ""
|
132
|
+
s = ""
|
133
|
+
for cc in self.allowed_attributes:
|
134
|
+
if len(cc) > 0:
|
135
|
+
t += cc[0].upper()
|
136
|
+
ct += 1
|
137
|
+
if ct > 0:
|
138
|
+
s = self.color.hex + "-" + t
|
139
|
+
default_map["colcode"] = s
|
140
|
+
default_map["opstop"] = "(stop)" if self.stopop else ""
|
141
|
+
default_map.update(self.settings)
|
142
|
+
default_map["color"] = self.color.hexrgb if self.color is not None else ""
|
143
|
+
default_map["overscan"] = f"±{self.overscan}"
|
144
|
+
default_map["percent"] = "100%"
|
145
|
+
default_map["ppi"] = "default"
|
146
|
+
default_map["grayscale"] = "GS" if self.use_grayscale else "BW"
|
147
|
+
if self.power is not None:
|
148
|
+
default_map["percent"] = f"{self.power / 10.0:.0f}%"
|
149
|
+
default_map["ppi"] = f"{self.power:.0f}"
|
150
|
+
default_map["speed_mm_min"] = (
|
151
|
+
"" if self.speed is None else f"{self.speed * 60:.0f}"
|
152
|
+
)
|
153
|
+
return default_map
|
154
|
+
|
155
|
+
def can_drop(self, drag_node):
|
156
|
+
# Default routine for drag + drop for an op node - irrelevant for others...
|
157
|
+
if drag_node.has_ancestor("branch reg"):
|
158
|
+
# Will be dealt with in elements -
|
159
|
+
# we don't implement a more sophisticated routine here
|
160
|
+
return False
|
161
|
+
if drag_node.type.startswith("elem ") and drag_node.type in self._allowed_elements_dnd:
|
162
|
+
return True
|
163
|
+
elif drag_node.type == "reference" and drag_node.node.type in self._allowed_elements_dnd:
|
164
|
+
return True
|
165
|
+
elif drag_node.type in op_nodes:
|
166
|
+
# Move operation to a different position.
|
167
|
+
return True
|
168
|
+
elif drag_node.type in ("file", "group"):
|
169
|
+
return not any(e.has_ancestor("branch reg") for e in drag_node.flat(elem_nodes))
|
170
|
+
return False
|
171
|
+
|
172
|
+
def drop(self, drag_node, modify=True, flag=False):
|
173
|
+
count = 0
|
174
|
+
existing = 0
|
175
|
+
result = False
|
176
|
+
if drag_node.type.startswith("elem") and not drag_node.has_ancestor(
|
177
|
+
"branch reg"
|
178
|
+
):
|
179
|
+
existing += 1
|
180
|
+
# if drag_node.type == "elem image":
|
181
|
+
# return False
|
182
|
+
# Dragging element onto operation adds that element to the op.
|
183
|
+
if modify:
|
184
|
+
count += 1
|
185
|
+
self.add_reference(drag_node, pos=None if flag else 0)
|
186
|
+
result = True
|
187
|
+
elif drag_node.type == "reference":
|
188
|
+
# # Disallow drop of image refelems onto a Dot op.
|
189
|
+
# if drag_node.type == "elem image":
|
190
|
+
# return False
|
191
|
+
# Move a refelem to end of op.
|
192
|
+
existing += 1
|
193
|
+
if modify:
|
194
|
+
count += 1
|
195
|
+
self.append_child(drag_node)
|
196
|
+
result = True
|
197
|
+
elif drag_node.type in op_nodes:
|
198
|
+
# Move operation to a different position.
|
199
|
+
if modify:
|
200
|
+
self.insert_sibling(drag_node)
|
201
|
+
result = True
|
202
|
+
elif drag_node.type in ("file", "group") and not drag_node.has_ancestor(
|
203
|
+
"branch reg"
|
204
|
+
):
|
205
|
+
some_nodes = False
|
206
|
+
for e in drag_node.flat(types=elem_nodes):
|
207
|
+
existing += 1
|
208
|
+
# Disallow drop of image elems onto a Dot op.
|
209
|
+
# if drag_node.type == "elem image":
|
210
|
+
# continue
|
211
|
+
# Add element to operation
|
212
|
+
if modify:
|
213
|
+
count += 1
|
214
|
+
self.add_reference(e)
|
215
|
+
some_nodes = True
|
216
|
+
result = some_nodes
|
217
|
+
return result
|
218
|
+
|
219
|
+
def has_color_attribute(self, attribute):
|
220
|
+
return attribute in self.allowed_attributes
|
221
|
+
|
222
|
+
def add_color_attribute(self, attribute):
|
223
|
+
if not attribute in self.allowed_attributes:
|
224
|
+
self.allowed_attributes.append(attribute)
|
225
|
+
|
226
|
+
def remove_color_attribute(self, attribute):
|
227
|
+
if attribute in self.allowed_attributes:
|
228
|
+
self.allowed_attributes.remove(attribute)
|
229
|
+
|
230
|
+
def has_attributes(self):
|
231
|
+
return "stroke" in self.allowed_attributes or "fill" in self.allowed_attributes
|
232
|
+
|
233
|
+
def is_referenced(self, node):
|
234
|
+
for e in self.children:
|
235
|
+
if e is node:
|
236
|
+
return True
|
237
|
+
if hasattr(e, "node") and e.node is node:
|
238
|
+
return True
|
239
|
+
return False
|
240
|
+
|
241
|
+
def valid_node_for_reference(self, node):
|
242
|
+
if node.type in self._allowed_elements_dnd:
|
243
|
+
return True
|
244
|
+
else:
|
245
|
+
return False
|
246
|
+
|
247
|
+
def classify(self, node, fuzzy=False, fuzzydistance=100, usedefault=False):
|
248
|
+
def matching_color(col1, col2):
|
249
|
+
_result = False
|
250
|
+
if col1 is None and col2 is None:
|
251
|
+
_result = True
|
252
|
+
elif (
|
253
|
+
col1 is not None
|
254
|
+
and col1.argb is not None
|
255
|
+
and col2 is not None
|
256
|
+
and col2.argb is not None
|
257
|
+
):
|
258
|
+
if fuzzy:
|
259
|
+
distance = Color.distance(col1, col2)
|
260
|
+
_result = distance < fuzzydistance
|
261
|
+
else:
|
262
|
+
_result = col1 == col2
|
263
|
+
return _result
|
264
|
+
|
265
|
+
if self.is_referenced(node):
|
266
|
+
# No need to add it again...
|
267
|
+
return False, False, None
|
268
|
+
|
269
|
+
feedback = []
|
270
|
+
if node.type in self._allowed_elements:
|
271
|
+
if self.default and usedefault:
|
272
|
+
# Have classified but more classification might be needed
|
273
|
+
if self.valid_node_for_reference(node):
|
274
|
+
self.add_reference(node)
|
275
|
+
feedback.append("stroke")
|
276
|
+
feedback.append("fill")
|
277
|
+
return True, self.stopop, feedback
|
278
|
+
else:
|
279
|
+
if self.has_attributes():
|
280
|
+
result = False
|
281
|
+
for attribute in self.allowed_attributes:
|
282
|
+
if (
|
283
|
+
hasattr(node, attribute)
|
284
|
+
and getattr(node, attribute) is not None
|
285
|
+
):
|
286
|
+
plain_color_op = abs(self.color)
|
287
|
+
plain_color_node = abs(getattr(node, attribute))
|
288
|
+
if matching_color(plain_color_op, plain_color_node):
|
289
|
+
if self.valid_node_for_reference(node):
|
290
|
+
result = True
|
291
|
+
self.add_reference(node)
|
292
|
+
# Have classified but more classification might be needed
|
293
|
+
feedback.append(attribute)
|
294
|
+
if result:
|
295
|
+
return True, self.stopop, feedback
|
296
|
+
else: # empty ? Anything with either a solid fill or a plain white stroke goes
|
297
|
+
if self.valid_node_for_reference(node):
|
298
|
+
addit = False
|
299
|
+
if node.type in ("elem image", "elem text"):
|
300
|
+
addit = True
|
301
|
+
if hasattr(node, "fill"):
|
302
|
+
if node.fill is not None and node.fill.argb is not None:
|
303
|
+
# if matching_color(node.fill, Color("white")):
|
304
|
+
# addit = True
|
305
|
+
# if matching_color(node.fill, Color("black")):
|
306
|
+
# addit = True
|
307
|
+
addit = True
|
308
|
+
feedback.append("fill")
|
309
|
+
if hasattr(node, "stroke"):
|
310
|
+
if node.stroke is not None and node.stroke.argb is not None:
|
311
|
+
if matching_color(node.stroke, Color("white")):
|
312
|
+
addit = True
|
313
|
+
feedback.append("stroke")
|
314
|
+
if matching_color(node.stroke, Color("black")):
|
315
|
+
addit = True
|
316
|
+
feedback.append("stroke")
|
317
|
+
if addit:
|
318
|
+
self.add_reference(node)
|
319
|
+
# Have classified but more classification might be needed
|
320
|
+
return True, self.stopop, feedback
|
321
|
+
return False, False, None
|
322
|
+
|
323
|
+
def load(self, settings, section):
|
324
|
+
settings.read_persistent_attributes(section, self)
|
325
|
+
hexa = self.settings.get("hex_color")
|
326
|
+
if hexa is not None:
|
327
|
+
self.color = Color(hexa)
|
328
|
+
self.updated()
|
329
|
+
|
330
|
+
def save(self, settings, section):
|
331
|
+
# Sync certain properties with self.settings
|
332
|
+
for attr in ("label", "lock", "id"):
|
333
|
+
if hasattr(self, attr) and attr in self.settings:
|
334
|
+
self.settings[attr] = getattr(self, attr)
|
335
|
+
if "hex_color" in self.settings:
|
336
|
+
self.settings["hex_color"] = self.color.hexa
|
337
|
+
|
338
|
+
settings.write_persistent_attributes(section, self)
|
339
|
+
settings.write_persistent(section, "hex_color", self.color.hexa)
|
340
|
+
settings.write_persistent_dict(section, self.settings)
|
341
|
+
|
342
|
+
def time_estimate(self):
|
343
|
+
estimate = 0
|
344
|
+
dpi = self.dpi
|
345
|
+
# Get fresh union bounds, may not have been marked dirty on additions or removals.
|
346
|
+
min_x, min_y, max_x, max_y = Node.union_bounds(self.flat(types=elem_ref_nodes))
|
347
|
+
width_in_inches = (max_x - min_x) / UNITS_PER_INCH
|
348
|
+
height_in_inches = (max_y - min_y) / UNITS_PER_INCH
|
349
|
+
speed_in_per_s = self.speed / MM_PER_INCH
|
350
|
+
if self.raster_direction in (
|
351
|
+
RASTER_T2B, RASTER_B2T, RASTER_HATCH,
|
352
|
+
RASTER_GREEDY_H, RASTER_CROSSOVER, RASTER_SPIRAL,
|
353
|
+
):
|
354
|
+
scanlines = height_in_inches * dpi
|
355
|
+
if not self.bidirectional:
|
356
|
+
scanlines *= 2
|
357
|
+
this_len = scanlines * width_in_inches + height_in_inches
|
358
|
+
estimate += this_len / speed_in_per_s
|
359
|
+
# print (f"Horizontal scanlines: {scanlines}, Length: {this_len:.1f}")
|
360
|
+
if self.raster_direction in (RASTER_L2R, RASTER_R2L, RASTER_HATCH, RASTER_GREEDY_V):
|
361
|
+
scanlines = width_in_inches * dpi
|
362
|
+
if not self.bidirectional:
|
363
|
+
scanlines *= 2
|
364
|
+
this_len = scanlines * height_in_inches + width_in_inches
|
365
|
+
estimate += this_len / speed_in_per_s
|
366
|
+
# print (f"Vertical scanlines: {scanlines}, Length: {this_len:.1f}")
|
367
|
+
if self.passes_custom and self.passes != 1:
|
368
|
+
estimate *= max(self.passes, 1)
|
369
|
+
|
370
|
+
if isnan(estimate):
|
371
|
+
estimate = 0
|
372
|
+
hours, remainder = divmod(estimate, 3600)
|
373
|
+
minutes, seconds = divmod(remainder, 60)
|
374
|
+
return f"{int(hours)}:{str(int(minutes)).zfill(2)}:{str(int(seconds)).zfill(2)}"
|
375
|
+
|
376
|
+
def preprocess(self, context, matrix, plan):
|
377
|
+
"""
|
378
|
+
Preprocess is called during job planning. This should be called with
|
379
|
+
the native matrix.
|
380
|
+
|
381
|
+
@param context:
|
382
|
+
@param matrix:
|
383
|
+
@param plan:
|
384
|
+
@return:
|
385
|
+
"""
|
386
|
+
self._spot_in_device_units = 0
|
387
|
+
self._instructions = {}
|
388
|
+
if hasattr(context, "device"):
|
389
|
+
if hasattr(context.device, "get_raster_instructions"):
|
390
|
+
self._instructions = context.device.get_raster_instructions()
|
391
|
+
|
392
|
+
if self.consider_laserspot:
|
393
|
+
try:
|
394
|
+
laserspot = getattr(context.device, "laserspot", "0.3mm")
|
395
|
+
spot = 2 * float(Length(laserspot)) / ( context.device.view.native_scale_x + context.device.view.native_scale_y)
|
396
|
+
# 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}")
|
397
|
+
except (ValueError, AttributeError):
|
398
|
+
spot = 0
|
399
|
+
self._spot_in_device_units = spot
|
400
|
+
|
401
|
+
if isinstance(self.speed, str):
|
402
|
+
try:
|
403
|
+
self.speed = float(self.speed)
|
404
|
+
except ValueError:
|
405
|
+
pass
|
406
|
+
commands = plan.commands
|
407
|
+
native_mm = abs(complex(*matrix.transform_vector([0, UNITS_PER_MM])))
|
408
|
+
self.settings["native_mm"] = native_mm
|
409
|
+
self.settings["native_speed"] = self.speed * native_mm
|
410
|
+
self.settings["native_rapid_speed"] = self.rapid_speed * native_mm
|
411
|
+
try:
|
412
|
+
overscan = float(Length(self.overscan))
|
413
|
+
except ValueError:
|
414
|
+
overscan = 0
|
415
|
+
transformed_vector = matrix.transform_vector([0, overscan])
|
416
|
+
self.overscan = abs(complex(transformed_vector[0], transformed_vector[1]))
|
417
|
+
|
418
|
+
if len(self.children) == 0:
|
419
|
+
return
|
420
|
+
|
421
|
+
make_raster = context.lookup("render-op/make_raster")
|
422
|
+
if make_raster is None:
|
423
|
+
|
424
|
+
def strip_rasters():
|
425
|
+
self.remove_all_children()
|
426
|
+
|
427
|
+
commands.append(strip_rasters)
|
428
|
+
return
|
429
|
+
|
430
|
+
def make_image():
|
431
|
+
"""
|
432
|
+
Nested function to be added to commands and to call make_raster on the given elements.
|
433
|
+
@return:
|
434
|
+
"""
|
435
|
+
# Calculate raster steps from DPI device context
|
436
|
+
self.set_dirty_bounds()
|
437
|
+
|
438
|
+
step_x, step_y = context.device.view.dpi_to_steps(self.dpi)
|
439
|
+
img_mx = Matrix.scale(step_x, step_y)
|
440
|
+
data = []
|
441
|
+
for node in self.flat():
|
442
|
+
if node.type not in self._allowed_elements_dnd:
|
443
|
+
continue
|
444
|
+
if node.type == "reference":
|
445
|
+
node = node.node
|
446
|
+
if getattr(node, 'hidden', False):
|
447
|
+
continue
|
448
|
+
data.append(node)
|
449
|
+
if not data:
|
450
|
+
self.children.clear()
|
451
|
+
return
|
452
|
+
bounds = Node.union_bounds(data, attr="paint_bounds")
|
453
|
+
reverse = context.elements.classify_reverse
|
454
|
+
if reverse:
|
455
|
+
data = list(reversed(data))
|
456
|
+
try:
|
457
|
+
image = make_raster(data, bounds=bounds, step_x=step_x, step_y=step_y)
|
458
|
+
if step_x > 0:
|
459
|
+
img_mx.post_translate(bounds[0], 0)
|
460
|
+
else:
|
461
|
+
img_mx.post_translate(bounds[2], 0)
|
462
|
+
if step_y > 0:
|
463
|
+
img_mx.post_translate(0, bounds[1])
|
464
|
+
else:
|
465
|
+
img_mx.post_translate(0, bounds[3])
|
466
|
+
|
467
|
+
except (AssertionError, MemoryError) as e:
|
468
|
+
raise CutPlanningFailedError("Raster too large.") from e
|
469
|
+
image = image.convert("L")
|
470
|
+
image_node = ImageNode(image=image, matrix=img_mx)
|
471
|
+
self.children.clear()
|
472
|
+
self.add_node(image_node)
|
473
|
+
image_node.step_x = step_x
|
474
|
+
image_node.step_y = step_y
|
475
|
+
image_node.process_image()
|
476
|
+
|
477
|
+
msx = matrix.value_scale_x()
|
478
|
+
msy = matrix.value_scale_y()
|
479
|
+
rotated = False
|
480
|
+
negative_scale_x = msx < 0
|
481
|
+
negative_scale_y = msy < 0
|
482
|
+
if msx == 0 and msy == 0:
|
483
|
+
# Rotated view
|
484
|
+
rotated = True
|
485
|
+
p1a = matrix.point_in_matrix_space((0, 0))
|
486
|
+
p2a = matrix.point_in_matrix_space((1, 0))
|
487
|
+
dx = p1a.x - p2a.x
|
488
|
+
dy = p1a.y - p2a.y
|
489
|
+
negative_scale_x = bool(dy < 0) if dx == 0 else bool(dx < 0)
|
490
|
+
negative_scale_y = False if dx == 0 else bool(dy < 0)
|
491
|
+
|
492
|
+
if rotated:
|
493
|
+
mapping = {
|
494
|
+
RASTER_T2B: RASTER_L2R,
|
495
|
+
RASTER_B2T: RASTER_R2L,
|
496
|
+
RASTER_R2L: RASTER_B2T,
|
497
|
+
RASTER_L2R: RASTER_T2B,
|
498
|
+
RASTER_GREEDY_H: RASTER_GREEDY_V,
|
499
|
+
RASTER_GREEDY_V: RASTER_GREEDY_H,
|
500
|
+
}
|
501
|
+
if self.raster_direction in mapping:
|
502
|
+
self.raster_direction = mapping[self.raster_direction]
|
503
|
+
if negative_scale_y:
|
504
|
+
# Y is negative scale, flip raster_direction if needed
|
505
|
+
self.raster_preference_top = not self.raster_preference_top
|
506
|
+
if self.raster_direction == RASTER_T2B:
|
507
|
+
self.raster_direction = RASTER_B2T
|
508
|
+
elif self.raster_direction == RASTER_B2T:
|
509
|
+
self.raster_direction = RASTER_T2B
|
510
|
+
if negative_scale_x:
|
511
|
+
# X is negative scale, flip raster_direction if needed
|
512
|
+
self.raster_preference_left = not self.raster_preference_left
|
513
|
+
if self.raster_direction == RASTER_R2L:
|
514
|
+
self.raster_direction = RASTER_L2R
|
515
|
+
elif self.raster_direction == RASTER_L2R:
|
516
|
+
self.raster_direction = RASTER_R2L
|
517
|
+
|
518
|
+
commands.append(make_image)
|
519
|
+
# Look for registered raster (image) preprocessors,
|
520
|
+
# these are routines that take one image as parameter
|
521
|
+
# and deliver a set of (result image, method (aka raster_direction) )
|
522
|
+
# that will be dealt with independently
|
523
|
+
# The registered datastructure is (rasterid, description, method)
|
524
|
+
def call_me(method):
|
525
|
+
def handler():
|
526
|
+
method(self)
|
527
|
+
return handler
|
528
|
+
|
529
|
+
for key, description, method in context.kernel.lookup_all("raster_preprocessor/.*"):
|
530
|
+
if key == self.raster_direction:
|
531
|
+
plan.commands.append(call_me(method))
|
532
|
+
# print (f"Found {description}")
|
533
|
+
break
|
534
|
+
|
535
|
+
|
536
|
+
def as_cutobjects(self, closed_distance=15, passes=1):
|
537
|
+
"""
|
538
|
+
Generator of cutobjects for a raster operation. This takes any image node children
|
539
|
+
and converts them into rastercut objects. These objects should have already been converted
|
540
|
+
from vector shapes.
|
541
|
+
|
542
|
+
The preference for raster shapes is to use the settings set on this operation rather than on the image-node.
|
543
|
+
"""
|
544
|
+
if len(self.children) == 0:
|
545
|
+
return
|
546
|
+
settings = self.derive()
|
547
|
+
# make an independent copy
|
548
|
+
unsupported = self._instructions.get("unsupported_opt", ())
|
549
|
+
if self.raster_direction in unsupported:
|
550
|
+
self.raster_direction = RASTER_T2B
|
551
|
+
|
552
|
+
# Set overscan
|
553
|
+
overscan = self.overscan
|
554
|
+
if not isinstance(overscan, float):
|
555
|
+
overscan = float(Length(overscan))
|
556
|
+
settings["overscan"] = overscan
|
557
|
+
|
558
|
+
for image_node in self.children:
|
559
|
+
if hasattr(image_node, "direction") and image_node.direction is not None:
|
560
|
+
direction = image_node.direction
|
561
|
+
else:
|
562
|
+
direction = self.raster_direction
|
563
|
+
horizontal = False
|
564
|
+
bidirectional = self.bidirectional
|
565
|
+
start_on_left = self.raster_preference_left
|
566
|
+
start_on_top = self.raster_preference_top
|
567
|
+
# Set variables by direction
|
568
|
+
if direction in (RASTER_GREEDY_V, RASTER_L2R, RASTER_R2L):
|
569
|
+
horizontal = False
|
570
|
+
if direction in (RASTER_B2T, RASTER_T2B, RASTER_HATCH, RASTER_CROSSOVER, RASTER_GREEDY_H):
|
571
|
+
horizontal = True
|
572
|
+
if direction in (RASTER_T2B, RASTER_CROSSOVER):
|
573
|
+
start_on_top = True
|
574
|
+
if direction == RASTER_B2T:
|
575
|
+
start_on_top = False
|
576
|
+
if direction == RASTER_R2L:
|
577
|
+
start_on_left = False
|
578
|
+
if direction == RASTER_L2R:
|
579
|
+
start_on_left = True
|
580
|
+
if direction in (RASTER_GREEDY_H, RASTER_GREEDY_V, RASTER_CROSSOVER):
|
581
|
+
bidirectional = True
|
582
|
+
|
583
|
+
cutcodes = []
|
584
|
+
# Process each child. Some core settings are the same for each child.
|
585
|
+
|
586
|
+
if image_node.type == "reference":
|
587
|
+
image_node = image_node.node
|
588
|
+
if getattr(image_node, "hidden", False):
|
589
|
+
continue
|
590
|
+
if image_node.type != "elem image":
|
591
|
+
continue
|
592
|
+
|
593
|
+
step_x = image_node.step_x
|
594
|
+
step_y = image_node.step_y
|
595
|
+
|
596
|
+
dotwidth = 2 * self._spot_in_device_units / (step_x + step_y)
|
597
|
+
# print (f"Laserspot in device units: {self._spot_in_device_units:.2f}, step: {step_x:.2f} + {step_y:.2f} -> {dotwidth:.2f}")
|
598
|
+
|
599
|
+
if horizontal:
|
600
|
+
# Raster step is only along y for horizontal raster
|
601
|
+
settings["raster_step_x"] = 0
|
602
|
+
settings["raster_step_y"] = step_y
|
603
|
+
else:
|
604
|
+
# Raster step is only along x for vertical raster
|
605
|
+
settings["raster_step_x"] = step_x
|
606
|
+
settings["raster_step_y"] = 0
|
607
|
+
|
608
|
+
# Perform correct actualization
|
609
|
+
image_node.process_image()
|
610
|
+
|
611
|
+
# Set variables
|
612
|
+
matrix = image_node.matrix
|
613
|
+
pil_image = image_node.image
|
614
|
+
offset_x = matrix.value_trans_x()
|
615
|
+
offset_y = matrix.value_trans_y()
|
616
|
+
|
617
|
+
# Establish path
|
618
|
+
min_x = offset_x
|
619
|
+
min_y = offset_y
|
620
|
+
max_x = offset_x + pil_image.width * step_x
|
621
|
+
max_y = offset_y + pil_image.height * step_y
|
622
|
+
path = Path(
|
623
|
+
Polygon(
|
624
|
+
(min_x, min_y),
|
625
|
+
(min_x, max_y),
|
626
|
+
(max_x, max_y),
|
627
|
+
(max_x, min_y),
|
628
|
+
)
|
629
|
+
)
|
630
|
+
image_filter = None
|
631
|
+
if not self.use_grayscale: # By default a bw picture
|
632
|
+
threshold = 200
|
633
|
+
pil_image = pil_image.point(lambda x: 255 if x > threshold else 0, mode="1")
|
634
|
+
# pil_image = pil_image.convert("1")
|
635
|
+
if self.raster_direction in (RASTER_GREEDY_H, RASTER_GREEDY_V): # Greedy nearest neighbour
|
636
|
+
# get some image statistics
|
637
|
+
white_pixels = 0
|
638
|
+
used_colors = pil_image.getcolors()
|
639
|
+
for col_count, col in used_colors:
|
640
|
+
if col==255:
|
641
|
+
white_pixels = col_count
|
642
|
+
break
|
643
|
+
white_pixel_ratio = white_pixels / (pil_image.width * pil_image.height)
|
644
|
+
# print (f"white pixels: {white_pixels}, ratio = {white_pixel_ratio:.3f}")
|
645
|
+
if white_pixel_ratio < 0.3:
|
646
|
+
self.raster_direction = RASTER_T2B if self.raster_direction == RASTER_GREEDY_H else RASTER_L2R
|
647
|
+
|
648
|
+
if self.raster_direction in (RASTER_CROSSOVER, RASTER_SPIRAL): # Crossover - need both
|
649
|
+
settings["raster_step_x"] = step_x
|
650
|
+
settings["raster_step_y"] = step_y
|
651
|
+
horizontal = True
|
652
|
+
start_on_top = True
|
653
|
+
start_on_left = True
|
654
|
+
if self.raster_direction == RASTER_CROSSOVER and "split_crossover" in self._instructions:
|
655
|
+
self._instructions["mode_filter"] = "ROW"
|
656
|
+
horizontal=True
|
657
|
+
bidirectional=True
|
658
|
+
start_on_top = True
|
659
|
+
start_on_left = True
|
660
|
+
if horizontal:
|
661
|
+
# Raster step is only along y for horizontal raster
|
662
|
+
settings["raster_step_x"] = 0
|
663
|
+
settings["raster_step_y"] = step_y
|
664
|
+
else:
|
665
|
+
# Raster step is only along x for vertical raster
|
666
|
+
settings["raster_step_x"] = step_x
|
667
|
+
settings["raster_step_y"] = 0
|
668
|
+
# Create Cut Object for horizontal lines
|
669
|
+
# The image may be manipulated inside RasterCut, so let's create a fresh copy
|
670
|
+
rasterimage = copy(pil_image)
|
671
|
+
cutsettings = dict(settings)
|
672
|
+
cut = RasterCut(
|
673
|
+
image=rasterimage,
|
674
|
+
offset_x=offset_x,
|
675
|
+
offset_y=offset_y,
|
676
|
+
step_x=step_x,
|
677
|
+
step_y=step_y,
|
678
|
+
inverted=False,
|
679
|
+
bidirectional=bidirectional,
|
680
|
+
direction=direction,
|
681
|
+
horizontal=horizontal,
|
682
|
+
start_minimum_x=start_on_left,
|
683
|
+
start_minimum_y=start_on_top,
|
684
|
+
overscan=overscan,
|
685
|
+
settings=cutsettings,
|
686
|
+
passes=passes,
|
687
|
+
post_filter=image_filter,
|
688
|
+
laserspot=dotwidth,
|
689
|
+
special=dict(self._instructions),
|
690
|
+
)
|
691
|
+
cut.path = path
|
692
|
+
cut.original_op = self.type
|
693
|
+
cutcodes.append(cut)
|
694
|
+
|
695
|
+
# Now set it for the next pass
|
696
|
+
horizontal=False
|
697
|
+
if horizontal:
|
698
|
+
# Raster step is only along y for horizontal raster
|
699
|
+
settings["raster_step_x"] = 0
|
700
|
+
settings["raster_step_y"] = step_y
|
701
|
+
else:
|
702
|
+
# Raster step is only along x for vertical raster
|
703
|
+
settings["raster_step_x"] = step_x
|
704
|
+
settings["raster_step_y"] = 0
|
705
|
+
|
706
|
+
self._instructions["mode_filter"] = "COL"
|
707
|
+
|
708
|
+
# The image may be manipulated inside RasterCut, so let's create a fresh copy
|
709
|
+
rasterimage = copy(pil_image)
|
710
|
+
cutsettings = dict(settings)
|
711
|
+
|
712
|
+
# Create Cut Object
|
713
|
+
cut = RasterCut(
|
714
|
+
image=rasterimage,
|
715
|
+
offset_x=offset_x,
|
716
|
+
offset_y=offset_y,
|
717
|
+
step_x=step_x,
|
718
|
+
step_y=step_y,
|
719
|
+
inverted=False,
|
720
|
+
bidirectional=bidirectional,
|
721
|
+
direction=direction,
|
722
|
+
horizontal=horizontal,
|
723
|
+
start_minimum_x=start_on_left,
|
724
|
+
start_minimum_y=start_on_top,
|
725
|
+
overscan=overscan,
|
726
|
+
settings=cutsettings,
|
727
|
+
passes=passes,
|
728
|
+
post_filter=image_filter,
|
729
|
+
laserspot=dotwidth,
|
730
|
+
special=self._instructions,
|
731
|
+
)
|
732
|
+
cut.path = path
|
733
|
+
cut.original_op = self.type
|
734
|
+
cutcodes.append(cut)
|
735
|
+
if self.raster_direction == RASTER_HATCH:
|
736
|
+
# Create optional crosshatch cut
|
737
|
+
|
738
|
+
direction = RASTER_L2R if start_on_left else RASTER_R2L
|
739
|
+
|
740
|
+
horizontal = False
|
741
|
+
settings = dict(settings)
|
742
|
+
if horizontal:
|
743
|
+
# Raster step is only along y for horizontal raster
|
744
|
+
settings["raster_step_x"] = 0
|
745
|
+
settings["raster_step_y"] = step_y
|
746
|
+
else:
|
747
|
+
# Raster step is only along x for vertical raster
|
748
|
+
settings["raster_step_x"] = step_x
|
749
|
+
settings["raster_step_y"] = 0
|
750
|
+
# The image may be manipulated inside RasterCut, so let's create a fresh copy
|
751
|
+
rasterimage = copy(pil_image)
|
752
|
+
cutsettings = dict(settings)
|
753
|
+
cut = RasterCut(
|
754
|
+
image=rasterimage,
|
755
|
+
offset_x=offset_x,
|
756
|
+
offset_y=offset_y,
|
757
|
+
step_x=step_x,
|
758
|
+
step_y=step_y,
|
759
|
+
inverted=False,
|
760
|
+
bidirectional=bidirectional,
|
761
|
+
direction=direction,
|
762
|
+
horizontal=horizontal,
|
763
|
+
start_minimum_x=start_on_left,
|
764
|
+
start_minimum_y=start_on_top,
|
765
|
+
overscan=overscan,
|
766
|
+
settings=cutsettings,
|
767
|
+
passes=passes,
|
768
|
+
laserspot=dotwidth,
|
769
|
+
special=self._instructions,
|
770
|
+
)
|
771
|
+
cut.path = path
|
772
|
+
cut.original_op = self.type
|
773
|
+
cutcodes.append(cut)
|
774
|
+
# Yield all generated cutcodes of this image
|
775
|
+
yield from cutcodes
|
776
|
+
|
777
|
+
@property
|
778
|
+
def bounds(self):
|
779
|
+
if not self._bounds_dirty:
|
780
|
+
return self._bounds
|
781
|
+
|
782
|
+
self._bounds = None
|
783
|
+
if self.output:
|
784
|
+
if self._children:
|
785
|
+
self._bounds = Node.union_bounds(self._children, bounds=self._bounds, ignore_locked=False, ignore_hidden=True)
|
786
|
+
self._bounds_dirty = False
|
787
|
+
return self._bounds
|