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/tools/driver_to_path.py
CHANGED
@@ -1,579 +1,584 @@
|
|
1
|
-
"""
|
2
|
-
Driver to Path is code adapted from jpirnay's modifications of GRBL parser to read parsed data. When the GRBLParser
|
3
|
-
was converted to a driverlike emulator, this functionality was spun off.
|
4
|
-
"""
|
5
|
-
from math import isnan
|
6
|
-
|
7
|
-
from meerk40t.core.cutcode.cubiccut import CubicCut
|
8
|
-
from meerk40t.core.cutcode.dwellcut import DwellCut
|
9
|
-
from meerk40t.core.cutcode.gotocut import GotoCut
|
10
|
-
from meerk40t.core.cutcode.homecut import HomeCut
|
11
|
-
from meerk40t.core.cutcode.inputcut import InputCut
|
12
|
-
from meerk40t.core.cutcode.linecut import LineCut
|
13
|
-
from meerk40t.core.cutcode.outputcut import OutputCut
|
14
|
-
from meerk40t.core.cutcode.plotcut import PlotCut
|
15
|
-
from meerk40t.core.cutcode.quadcut import QuadCut
|
16
|
-
from meerk40t.core.cutcode.waitcut import WaitCut
|
17
|
-
from meerk40t.core.node.node import Linecap, Linejoin
|
18
|
-
from meerk40t.core.node.op_engrave import EngraveOpNode
|
19
|
-
from meerk40t.core.units import UNITS_PER_PIXEL, Length
|
20
|
-
from meerk40t.svgelements import Arc, Color, Move, Path
|
21
|
-
|
22
|
-
|
23
|
-
class PlotterDriver:
|
24
|
-
"""
|
25
|
-
This is a driverlike plotter that produces path and operations.
|
26
|
-
"""
|
27
|
-
|
28
|
-
def __init__(self):
|
29
|
-
self.last_x = 0
|
30
|
-
self.last_y = 0
|
31
|
-
self.power = 0
|
32
|
-
self.speed = 0
|
33
|
-
self.depth = 0
|
34
|
-
self.paths = list()
|
35
|
-
self.path = Path()
|
36
|
-
self.operations = {}
|
37
|
-
self.paths.append(self.path)
|
38
|
-
self.split_path = True
|
39
|
-
self.ignore_travel = True
|
40
|
-
self.treat_z_as_power = True
|
41
|
-
self.z_only_negative = True
|
42
|
-
|
43
|
-
def _check_operation_need(self):
|
44
|
-
"""
|
45
|
-
Do we need a new operation for this path?
|
46
|
-
|
47
|
-
@return:
|
48
|
-
"""
|
49
|
-
has_power = bool(self.power != 0)
|
50
|
-
if self.treat_z_as_power:
|
51
|
-
if self.z_only_negative and self.depth < 0:
|
52
|
-
has_power = True
|
53
|
-
elif not self.z_only_negative and self.depth != 0:
|
54
|
-
has_power = True
|
55
|
-
|
56
|
-
if self.speed != 0 and has_power:
|
57
|
-
# Do we have this operation already?!
|
58
|
-
id_string = f"{self.speed}|{self.power}|{self.depth}"
|
59
|
-
if id_string not in self.operations:
|
60
|
-
self.operations[id_string] = list()
|
61
|
-
index = len(self.paths) - 1
|
62
|
-
self.operations[id_string].append(index)
|
63
|
-
|
64
|
-
def _remove_trailing_moves(self):
|
65
|
-
"""
|
66
|
-
Removes a trailing move from the current active path.
|
67
|
-
|
68
|
-
@return:
|
69
|
-
"""
|
70
|
-
# Is the trailing segment a move ?
|
71
|
-
while len(self.path) > 0 and isinstance(self.path[-1], Move):
|
72
|
-
del self.path[-1]
|
73
|
-
if len(self.path) == 0:
|
74
|
-
# Degenerate...
|
75
|
-
index = len(self.paths) - 1
|
76
|
-
for op in self.operations:
|
77
|
-
if index in self.operations[op]:
|
78
|
-
opindex = self.operations[op].index(index)
|
79
|
-
self.operations[op].pop(opindex)
|
80
|
-
if len(self.paths) > 0:
|
81
|
-
self.paths.pop(-1)
|
82
|
-
|
83
|
-
def _proper_z(self):
|
84
|
-
"""
|
85
|
-
Is the z treated as power and positive.
|
86
|
-
@return:
|
87
|
-
"""
|
88
|
-
res = False
|
89
|
-
if self.treat_z_as_power:
|
90
|
-
if self.depth < 0 and self.z_only_negative:
|
91
|
-
res = True
|
92
|
-
elif self.depth != 0 and not self.z_only_negative:
|
93
|
-
res = True
|
94
|
-
# print (f"only negative: {self.z_only_negative}, depth={self.depth}, res={res}")
|
95
|
-
return res
|
96
|
-
|
97
|
-
def _needs_adding(self, power):
|
98
|
-
"""
|
99
|
-
Does this power equal zero while we ignore travels or do we require adding to the path.
|
100
|
-
@param power:
|
101
|
-
@return:
|
102
|
-
"""
|
103
|
-
result = False
|
104
|
-
if power is None:
|
105
|
-
power = 0
|
106
|
-
if self.ignore_travel:
|
107
|
-
if power != 0 or self._proper_z():
|
108
|
-
result = True
|
109
|
-
else:
|
110
|
-
result = True
|
111
|
-
return result
|
112
|
-
|
113
|
-
def plot(self, q):
|
114
|
-
"""
|
115
|
-
Driver like command, plot sends cutcode to the driver.
|
116
|
-
@param q:
|
117
|
-
@return:
|
118
|
-
"""
|
119
|
-
if self._needs_adding(q.settings.get("power", 0)):
|
120
|
-
self._new()
|
121
|
-
if isinstance(q, LineCut):
|
122
|
-
self.path.move((q.start, q.end))
|
123
|
-
elif isinstance(q, QuadCut):
|
124
|
-
self.path.quad((q.start, q.c, q.end))
|
125
|
-
elif isinstance(q, CubicCut):
|
126
|
-
self.path.cubic(q.start, q.c1(), q.c2(), q.end)
|
127
|
-
elif isinstance(q, WaitCut):
|
128
|
-
pass
|
129
|
-
elif isinstance(q, HomeCut):
|
130
|
-
self.path.move((0, 0))
|
131
|
-
elif isinstance(q, GotoCut):
|
132
|
-
pass
|
133
|
-
elif isinstance(q, DwellCut):
|
134
|
-
pass
|
135
|
-
elif isinstance(q, (InputCut, OutputCut)):
|
136
|
-
pass
|
137
|
-
elif isinstance(q, PlotCut):
|
138
|
-
started = False
|
139
|
-
for x0, y0, power, x1, y1 in q.plot:
|
140
|
-
if not started:
|
141
|
-
self.path.move((x0, y0))
|
142
|
-
started = True
|
143
|
-
if power == 0:
|
144
|
-
self.path.move((x1, y1))
|
145
|
-
else:
|
146
|
-
self.path.line((x1, y1))
|
147
|
-
|
148
|
-
def _new(self, *args):
|
149
|
-
"""
|
150
|
-
Original code for plotter.new
|
151
|
-
@param args:
|
152
|
-
@return:
|
153
|
-
"""
|
154
|
-
splitter = self.split_path
|
155
|
-
if splitter and len(self.path):
|
156
|
-
self._remove_trailing_moves()
|
157
|
-
self.path = Path()
|
158
|
-
self.paths.append(self.path)
|
159
|
-
# print (f"Z at time of path start: {self.depth}")
|
160
|
-
if len(args) > 1:
|
161
|
-
feed = args[0]
|
162
|
-
power = args[1]
|
163
|
-
if feed != 0:
|
164
|
-
self.speed = feed
|
165
|
-
if power != 0:
|
166
|
-
self.power = power
|
167
|
-
self._check_operation_need()
|
168
|
-
|
169
|
-
def _move(self, x, y):
|
170
|
-
"""
|
171
|
-
original code for plotter.move
|
172
|
-
|
173
|
-
@param x:
|
174
|
-
@param y:
|
175
|
-
@return:
|
176
|
-
"""
|
177
|
-
self.path.move((x, y))
|
178
|
-
|
179
|
-
def _line(self, x0, y0, x1, y1, power):
|
180
|
-
"""
|
181
|
-
Original code for plotter.line
|
182
|
-
|
183
|
-
@param x0:
|
184
|
-
@param y0:
|
185
|
-
@param x1:
|
186
|
-
@param y1:
|
187
|
-
@param power:
|
188
|
-
@return:
|
189
|
-
"""
|
190
|
-
# Do we need it added?
|
191
|
-
if self._needs_adding(power):
|
192
|
-
if not self.path:
|
193
|
-
self.path.move((x0, y0))
|
194
|
-
self.path.line((x1, y1))
|
195
|
-
else:
|
196
|
-
self.path.move((x1, y1))
|
197
|
-
|
198
|
-
def _cw_arc(self, x0, y0, cx, cy, x1, y1, power):
|
199
|
-
"""
|
200
|
-
Original code for plotter clockwise arc. (Will never be used).
|
201
|
-
@param x0:
|
202
|
-
@param y0:
|
203
|
-
@param cx:
|
204
|
-
@param cy:
|
205
|
-
@param x1:
|
206
|
-
@param y1:
|
207
|
-
@param power:
|
208
|
-
@return:
|
209
|
-
"""
|
210
|
-
# Do we need it added?
|
211
|
-
if self._needs_adding(power):
|
212
|
-
arc = Arc(start=(x0, y0), center=(cx, cy), end=(x1, y1), ccw=False)
|
213
|
-
if isnan(arc.sweep):
|
214
|
-
# This arc is not valid.
|
215
|
-
self.path.line((x1, y1))
|
216
|
-
else:
|
217
|
-
self.path.append(arc)
|
218
|
-
else:
|
219
|
-
self.path.move((x1, y1))
|
220
|
-
|
221
|
-
def _ccw_arc(self, x0, y0, cx, cy, x1, y1, power):
|
222
|
-
"""
|
223
|
-
Original code for counter-clockwise arc.
|
224
|
-
|
225
|
-
This will never be used.
|
226
|
-
|
227
|
-
@param x0:
|
228
|
-
@param y0:
|
229
|
-
@param cx:
|
230
|
-
@param cy:
|
231
|
-
@param x1:
|
232
|
-
@param y1:
|
233
|
-
@param power:
|
234
|
-
@return:
|
235
|
-
"""
|
236
|
-
# Do we need it added?
|
237
|
-
if self._needs_adding(power):
|
238
|
-
arc = Arc(start=(x0, y0), center=(cx, cy), end=(x1, y1), ccw=True)
|
239
|
-
if isnan(arc.sweep):
|
240
|
-
# This arc is not valid.
|
241
|
-
self.path.line((x1, y1))
|
242
|
-
else:
|
243
|
-
self.path.append(arc)
|
244
|
-
else:
|
245
|
-
self.path.move((x1, y1))
|
246
|
-
|
247
|
-
def _end(self):
|
248
|
-
"""
|
249
|
-
Original code for plotter.end.
|
250
|
-
|
251
|
-
@return:
|
252
|
-
"""
|
253
|
-
# Is the trailing segment a move ?
|
254
|
-
self._remove_trailing_moves()
|
255
|
-
|
256
|
-
def _zaxis(self, depth):
|
257
|
-
"""
|
258
|
-
Original code for plotter.zaxis
|
259
|
-
|
260
|
-
@param depth:
|
261
|
-
@return:
|
262
|
-
"""
|
263
|
-
splitter = self.split_path
|
264
|
-
if splitter and len(self.path):
|
265
|
-
self._remove_trailing_moves()
|
266
|
-
self.path = Path()
|
267
|
-
self.paths.append(self.path)
|
268
|
-
# print (f"Z at time of path start: {self.depth}")
|
269
|
-
self.depth = depth
|
270
|
-
self._check_operation_need()
|
271
|
-
|
272
|
-
|
273
|
-
class DriverToPath:
|
274
|
-
def __init__(self):
|
275
|
-
self.origin = 1 # 0 top left, 1 bottom left, 2 center
|
276
|
-
self.split_path = True
|
277
|
-
self.ignore_travel = True
|
278
|
-
self.treat_z_as_power = False
|
279
|
-
self.z_only_negative = True
|
280
|
-
self.no_duplicates = False
|
281
|
-
self.create_operations = True
|
282
|
-
|
283
|
-
self.scale_speed = False
|
284
|
-
self.scale_speed_lower = 2
|
285
|
-
self.scale_speed_higher = 200
|
286
|
-
self.scale_power = False
|
287
|
-
self.scale_power_lower = 200
|
288
|
-
self.scale_power_higher = 1000
|
289
|
-
|
290
|
-
self.options = [
|
291
|
-
{
|
292
|
-
"attr": "ignore_travel",
|
293
|
-
"object": self,
|
294
|
-
"default": True,
|
295
|
-
"type": bool,
|
296
|
-
"label": "Ignore travel",
|
297
|
-
"tip": "Try to take only 'valid' movements",
|
298
|
-
"section": "_10_Path",
|
299
|
-
},
|
300
|
-
{
|
301
|
-
"attr": "split_path",
|
302
|
-
"object": self,
|
303
|
-
"default": True,
|
304
|
-
"type": bool,
|
305
|
-
"label": "Split paths",
|
306
|
-
"tip": "Split path into smaller chunks",
|
307
|
-
"section": "_10_Path",
|
308
|
-
},
|
309
|
-
{
|
310
|
-
"attr": "no_duplicates",
|
311
|
-
"object": self,
|
312
|
-
"default": True,
|
313
|
-
"type": bool,
|
314
|
-
"label": "Single occurrence",
|
315
|
-
"tip": "Prevent duplicate creation of segments (like in a multipass operation)",
|
316
|
-
"section": "_10_Path",
|
317
|
-
},
|
318
|
-
{
|
319
|
-
"attr": "treat_z_as_power",
|
320
|
-
"object": self,
|
321
|
-
"default": False,
|
322
|
-
"type": bool,
|
323
|
-
"label": "Treat Z-Movement as On/Off",
|
324
|
-
"tip": "Use negative Z-Values as a Power-On indicator, positive values as travel",
|
325
|
-
"conditional": (self, "ignore_travel"),
|
326
|
-
"section": "_10_Path",
|
327
|
-
"subsection": "_10_Z-Axis",
|
328
|
-
},
|
329
|
-
{
|
330
|
-
"attr": "z_only_negative",
|
331
|
-
"object": self,
|
332
|
-
"default": False,
|
333
|
-
"type": bool,
|
334
|
-
"label": "Only negative",
|
335
|
-
"tip": "Active: use positive values as travel\nInactive: use all non-zero values",
|
336
|
-
"conditional": (self, "treat_z_as_power"),
|
337
|
-
"section": "_10_Path",
|
338
|
-
"subsection": "_10_Z-Axis",
|
339
|
-
},
|
340
|
-
{
|
341
|
-
"attr": "origin",
|
342
|
-
"object": self,
|
343
|
-
"default": 1,
|
344
|
-
"type": int,
|
345
|
-
"style": "option",
|
346
|
-
"choices": (0, 1, 2, 3),
|
347
|
-
"display": (
|
348
|
-
"Top Left",
|
349
|
-
"Bottom Left",
|
350
|
-
"Center",
|
351
|
-
"Center (Y mirrored)",
|
352
|
-
),
|
353
|
-
"label": "Bed-Origin",
|
354
|
-
"tip": "Correct starting point",
|
355
|
-
"section": "_20_Correct Orientation",
|
356
|
-
},
|
357
|
-
{
|
358
|
-
"attr": "create_operations",
|
359
|
-
"object": self,
|
360
|
-
"default": False,
|
361
|
-
"type": bool,
|
362
|
-
"label": "Create operations",
|
363
|
-
"tip": "Create corresponding operations for Power and Speed pairs",
|
364
|
-
"section": "_30_Operation",
|
365
|
-
},
|
366
|
-
{
|
367
|
-
"attr": "scale_speed",
|
368
|
-
"object": self,
|
369
|
-
"default": False,
|
370
|
-
"type": bool,
|
371
|
-
"label": "Scale Speed",
|
372
|
-
"tip": "Set lower and higher level to scale the speed\n"
|
373
|
-
+ "Minimum speed used will be mapped to lower level\n"
|
374
|
-
+ "Maximum speed used will be mapped to upper level",
|
375
|
-
"conditional": (self, "create_operations"),
|
376
|
-
"section": "_30_Operation",
|
377
|
-
"subsection": "_20_Speed",
|
378
|
-
},
|
379
|
-
{
|
380
|
-
"attr": "scale_speed_lower",
|
381
|
-
"object": self,
|
382
|
-
"default": 2,
|
383
|
-
"type": float,
|
384
|
-
"label": "Lowest speed",
|
385
|
-
"trailer": "mm/sec",
|
386
|
-
"tip": "Minimum speed used will be mapped to lower level",
|
387
|
-
"conditional": (self, "scale_speed"),
|
388
|
-
"section": "_30_Operation",
|
389
|
-
"subsection": "_20_Speed",
|
390
|
-
},
|
391
|
-
{
|
392
|
-
"attr": "scale_speed_higher",
|
393
|
-
"object": self,
|
394
|
-
"default": 200,
|
395
|
-
"type": float,
|
396
|
-
"label": "Highest speed",
|
397
|
-
"trailer": "mm/sec",
|
398
|
-
"tip": "Maximum speed used will be mapped to upper level",
|
399
|
-
"conditional": (self, "scale_speed"),
|
400
|
-
"section": "_30_Operation",
|
401
|
-
"subsection": "_20_Speed",
|
402
|
-
},
|
403
|
-
{
|
404
|
-
"attr": "scale_power",
|
405
|
-
"object": self,
|
406
|
-
"default": False,
|
407
|
-
"type": bool,
|
408
|
-
"label": "Scale Power",
|
409
|
-
"tip": "Set lower and higher level to scale the power\n"
|
410
|
-
+ "Minimum power used will be mapped to lower level\n"
|
411
|
-
+ "Maximum power used will be mapped to upper level",
|
412
|
-
"section": "_30_Operation",
|
413
|
-
"subsection": "_20_Power",
|
414
|
-
},
|
415
|
-
{
|
416
|
-
"attr": "scale_power_lower",
|
417
|
-
"object": self,
|
418
|
-
"default": 200,
|
419
|
-
"type": float,
|
420
|
-
"label": "Lowest Power",
|
421
|
-
"trailer": "ppi",
|
422
|
-
"tip": "Minimum power used will be mapped to lower level",
|
423
|
-
"conditional": (self, "scale_power"),
|
424
|
-
"section": "_30_Operation",
|
425
|
-
"subsection": "_20_Power",
|
426
|
-
},
|
427
|
-
{
|
428
|
-
"attr": "scale_power_higher",
|
429
|
-
"object": self,
|
430
|
-
"default": 1000,
|
431
|
-
"type": float,
|
432
|
-
"label": "Highest power",
|
433
|
-
"trailer": "",
|
434
|
-
"tip": "Maximum power used will be mapped to upper level",
|
435
|
-
"conditional": (self, "scale_power"),
|
436
|
-
"section": "_30_Operation",
|
437
|
-
"subsection": "_20_Power",
|
438
|
-
},
|
439
|
-
]
|
440
|
-
|
441
|
-
def parse(self, blob_type, data, elements, options=None):
|
442
|
-
"""Parse the data with the given emulator and create and add the relevant paths.
|
443
|
-
|
444
|
-
Args:
|
445
|
-
blob_type: the emulator to use to parse the data, suffix entry value.
|
446
|
-
data (bytes): the grbl code to parse
|
447
|
-
elements (class): context for elements
|
448
|
-
options (disctionary, optional): A dictionary with settings. Defaults to None.
|
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
|
-
if
|
509
|
-
|
510
|
-
else:
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
opnode
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
1
|
+
"""
|
2
|
+
Driver to Path is code adapted from jpirnay's modifications of GRBL parser to read parsed data. When the GRBLParser
|
3
|
+
was converted to a driverlike emulator, this functionality was spun off.
|
4
|
+
"""
|
5
|
+
from math import isnan
|
6
|
+
|
7
|
+
from meerk40t.core.cutcode.cubiccut import CubicCut
|
8
|
+
from meerk40t.core.cutcode.dwellcut import DwellCut
|
9
|
+
from meerk40t.core.cutcode.gotocut import GotoCut
|
10
|
+
from meerk40t.core.cutcode.homecut import HomeCut
|
11
|
+
from meerk40t.core.cutcode.inputcut import InputCut
|
12
|
+
from meerk40t.core.cutcode.linecut import LineCut
|
13
|
+
from meerk40t.core.cutcode.outputcut import OutputCut
|
14
|
+
from meerk40t.core.cutcode.plotcut import PlotCut
|
15
|
+
from meerk40t.core.cutcode.quadcut import QuadCut
|
16
|
+
from meerk40t.core.cutcode.waitcut import WaitCut
|
17
|
+
from meerk40t.core.node.node import Linecap, Linejoin
|
18
|
+
from meerk40t.core.node.op_engrave import EngraveOpNode
|
19
|
+
from meerk40t.core.units import UNITS_PER_PIXEL, Length
|
20
|
+
from meerk40t.svgelements import Arc, Color, Move, Path
|
21
|
+
|
22
|
+
|
23
|
+
class PlotterDriver:
|
24
|
+
"""
|
25
|
+
This is a driverlike plotter that produces path and operations.
|
26
|
+
"""
|
27
|
+
|
28
|
+
def __init__(self):
|
29
|
+
self.last_x = 0
|
30
|
+
self.last_y = 0
|
31
|
+
self.power = 0
|
32
|
+
self.speed = 0
|
33
|
+
self.depth = 0
|
34
|
+
self.paths = list()
|
35
|
+
self.path = Path()
|
36
|
+
self.operations = {}
|
37
|
+
self.paths.append(self.path)
|
38
|
+
self.split_path = True
|
39
|
+
self.ignore_travel = True
|
40
|
+
self.treat_z_as_power = True
|
41
|
+
self.z_only_negative = True
|
42
|
+
|
43
|
+
def _check_operation_need(self):
|
44
|
+
"""
|
45
|
+
Do we need a new operation for this path?
|
46
|
+
|
47
|
+
@return:
|
48
|
+
"""
|
49
|
+
has_power = bool(self.power != 0)
|
50
|
+
if self.treat_z_as_power:
|
51
|
+
if self.z_only_negative and self.depth < 0:
|
52
|
+
has_power = True
|
53
|
+
elif not self.z_only_negative and self.depth != 0:
|
54
|
+
has_power = True
|
55
|
+
|
56
|
+
if self.speed != 0 and has_power:
|
57
|
+
# Do we have this operation already?!
|
58
|
+
id_string = f"{self.speed}|{self.power}|{self.depth}"
|
59
|
+
if id_string not in self.operations:
|
60
|
+
self.operations[id_string] = list()
|
61
|
+
index = len(self.paths) - 1
|
62
|
+
self.operations[id_string].append(index)
|
63
|
+
|
64
|
+
def _remove_trailing_moves(self):
|
65
|
+
"""
|
66
|
+
Removes a trailing move from the current active path.
|
67
|
+
|
68
|
+
@return:
|
69
|
+
"""
|
70
|
+
# Is the trailing segment a move ?
|
71
|
+
while len(self.path) > 0 and isinstance(self.path[-1], Move):
|
72
|
+
del self.path[-1]
|
73
|
+
if len(self.path) == 0:
|
74
|
+
# Degenerate...
|
75
|
+
index = len(self.paths) - 1
|
76
|
+
for op in self.operations:
|
77
|
+
if index in self.operations[op]:
|
78
|
+
opindex = self.operations[op].index(index)
|
79
|
+
self.operations[op].pop(opindex)
|
80
|
+
if len(self.paths) > 0:
|
81
|
+
self.paths.pop(-1)
|
82
|
+
|
83
|
+
def _proper_z(self):
|
84
|
+
"""
|
85
|
+
Is the z treated as power and positive.
|
86
|
+
@return:
|
87
|
+
"""
|
88
|
+
res = False
|
89
|
+
if self.treat_z_as_power:
|
90
|
+
if self.depth < 0 and self.z_only_negative:
|
91
|
+
res = True
|
92
|
+
elif self.depth != 0 and not self.z_only_negative:
|
93
|
+
res = True
|
94
|
+
# print (f"only negative: {self.z_only_negative}, depth={self.depth}, res={res}")
|
95
|
+
return res
|
96
|
+
|
97
|
+
def _needs_adding(self, power):
|
98
|
+
"""
|
99
|
+
Does this power equal zero while we ignore travels or do we require adding to the path.
|
100
|
+
@param power:
|
101
|
+
@return:
|
102
|
+
"""
|
103
|
+
result = False
|
104
|
+
if power is None:
|
105
|
+
power = 0
|
106
|
+
if self.ignore_travel:
|
107
|
+
if power != 0 or self._proper_z():
|
108
|
+
result = True
|
109
|
+
else:
|
110
|
+
result = True
|
111
|
+
return result
|
112
|
+
|
113
|
+
def plot(self, q):
|
114
|
+
"""
|
115
|
+
Driver like command, plot sends cutcode to the driver.
|
116
|
+
@param q:
|
117
|
+
@return:
|
118
|
+
"""
|
119
|
+
if self._needs_adding(q.settings.get("power", 0)):
|
120
|
+
self._new()
|
121
|
+
if isinstance(q, LineCut):
|
122
|
+
self.path.move((q.start, q.end))
|
123
|
+
elif isinstance(q, QuadCut):
|
124
|
+
self.path.quad((q.start, q.c, q.end))
|
125
|
+
elif isinstance(q, CubicCut):
|
126
|
+
self.path.cubic(q.start, q.c1(), q.c2(), q.end)
|
127
|
+
elif isinstance(q, WaitCut):
|
128
|
+
pass
|
129
|
+
elif isinstance(q, HomeCut):
|
130
|
+
self.path.move((0, 0))
|
131
|
+
elif isinstance(q, GotoCut):
|
132
|
+
pass
|
133
|
+
elif isinstance(q, DwellCut):
|
134
|
+
pass
|
135
|
+
elif isinstance(q, (InputCut, OutputCut)):
|
136
|
+
pass
|
137
|
+
elif isinstance(q, PlotCut):
|
138
|
+
started = False
|
139
|
+
for x0, y0, power, x1, y1 in q.plot:
|
140
|
+
if not started:
|
141
|
+
self.path.move((x0, y0))
|
142
|
+
started = True
|
143
|
+
if power == 0:
|
144
|
+
self.path.move((x1, y1))
|
145
|
+
else:
|
146
|
+
self.path.line((x1, y1))
|
147
|
+
|
148
|
+
def _new(self, *args):
|
149
|
+
"""
|
150
|
+
Original code for plotter.new
|
151
|
+
@param args:
|
152
|
+
@return:
|
153
|
+
"""
|
154
|
+
splitter = self.split_path
|
155
|
+
if splitter and len(self.path):
|
156
|
+
self._remove_trailing_moves()
|
157
|
+
self.path = Path()
|
158
|
+
self.paths.append(self.path)
|
159
|
+
# print (f"Z at time of path start: {self.depth}")
|
160
|
+
if len(args) > 1:
|
161
|
+
feed = args[0]
|
162
|
+
power = args[1]
|
163
|
+
if feed != 0:
|
164
|
+
self.speed = feed
|
165
|
+
if power != 0:
|
166
|
+
self.power = power
|
167
|
+
self._check_operation_need()
|
168
|
+
|
169
|
+
def _move(self, x, y):
|
170
|
+
"""
|
171
|
+
original code for plotter.move
|
172
|
+
|
173
|
+
@param x:
|
174
|
+
@param y:
|
175
|
+
@return:
|
176
|
+
"""
|
177
|
+
self.path.move((x, y))
|
178
|
+
|
179
|
+
def _line(self, x0, y0, x1, y1, power):
|
180
|
+
"""
|
181
|
+
Original code for plotter.line
|
182
|
+
|
183
|
+
@param x0:
|
184
|
+
@param y0:
|
185
|
+
@param x1:
|
186
|
+
@param y1:
|
187
|
+
@param power:
|
188
|
+
@return:
|
189
|
+
"""
|
190
|
+
# Do we need it added?
|
191
|
+
if self._needs_adding(power):
|
192
|
+
if not self.path:
|
193
|
+
self.path.move((x0, y0))
|
194
|
+
self.path.line((x1, y1))
|
195
|
+
else:
|
196
|
+
self.path.move((x1, y1))
|
197
|
+
|
198
|
+
def _cw_arc(self, x0, y0, cx, cy, x1, y1, power):
|
199
|
+
"""
|
200
|
+
Original code for plotter clockwise arc. (Will never be used).
|
201
|
+
@param x0:
|
202
|
+
@param y0:
|
203
|
+
@param cx:
|
204
|
+
@param cy:
|
205
|
+
@param x1:
|
206
|
+
@param y1:
|
207
|
+
@param power:
|
208
|
+
@return:
|
209
|
+
"""
|
210
|
+
# Do we need it added?
|
211
|
+
if self._needs_adding(power):
|
212
|
+
arc = Arc(start=(x0, y0), center=(cx, cy), end=(x1, y1), ccw=False)
|
213
|
+
if isnan(arc.sweep):
|
214
|
+
# This arc is not valid.
|
215
|
+
self.path.line((x1, y1))
|
216
|
+
else:
|
217
|
+
self.path.append(arc)
|
218
|
+
else:
|
219
|
+
self.path.move((x1, y1))
|
220
|
+
|
221
|
+
def _ccw_arc(self, x0, y0, cx, cy, x1, y1, power):
|
222
|
+
"""
|
223
|
+
Original code for counter-clockwise arc.
|
224
|
+
|
225
|
+
This will never be used.
|
226
|
+
|
227
|
+
@param x0:
|
228
|
+
@param y0:
|
229
|
+
@param cx:
|
230
|
+
@param cy:
|
231
|
+
@param x1:
|
232
|
+
@param y1:
|
233
|
+
@param power:
|
234
|
+
@return:
|
235
|
+
"""
|
236
|
+
# Do we need it added?
|
237
|
+
if self._needs_adding(power):
|
238
|
+
arc = Arc(start=(x0, y0), center=(cx, cy), end=(x1, y1), ccw=True)
|
239
|
+
if isnan(arc.sweep):
|
240
|
+
# This arc is not valid.
|
241
|
+
self.path.line((x1, y1))
|
242
|
+
else:
|
243
|
+
self.path.append(arc)
|
244
|
+
else:
|
245
|
+
self.path.move((x1, y1))
|
246
|
+
|
247
|
+
def _end(self):
|
248
|
+
"""
|
249
|
+
Original code for plotter.end.
|
250
|
+
|
251
|
+
@return:
|
252
|
+
"""
|
253
|
+
# Is the trailing segment a move ?
|
254
|
+
self._remove_trailing_moves()
|
255
|
+
|
256
|
+
def _zaxis(self, depth):
|
257
|
+
"""
|
258
|
+
Original code for plotter.zaxis
|
259
|
+
|
260
|
+
@param depth:
|
261
|
+
@return:
|
262
|
+
"""
|
263
|
+
splitter = self.split_path
|
264
|
+
if splitter and len(self.path):
|
265
|
+
self._remove_trailing_moves()
|
266
|
+
self.path = Path()
|
267
|
+
self.paths.append(self.path)
|
268
|
+
# print (f"Z at time of path start: {self.depth}")
|
269
|
+
self.depth = depth
|
270
|
+
self._check_operation_need()
|
271
|
+
|
272
|
+
|
273
|
+
class DriverToPath:
|
274
|
+
def __init__(self):
|
275
|
+
self.origin = 1 # 0 top left, 1 bottom left, 2 center
|
276
|
+
self.split_path = True
|
277
|
+
self.ignore_travel = True
|
278
|
+
self.treat_z_as_power = False
|
279
|
+
self.z_only_negative = True
|
280
|
+
self.no_duplicates = False
|
281
|
+
self.create_operations = True
|
282
|
+
|
283
|
+
self.scale_speed = False
|
284
|
+
self.scale_speed_lower = 2
|
285
|
+
self.scale_speed_higher = 200
|
286
|
+
self.scale_power = False
|
287
|
+
self.scale_power_lower = 200
|
288
|
+
self.scale_power_higher = 1000
|
289
|
+
|
290
|
+
self.options = [
|
291
|
+
{
|
292
|
+
"attr": "ignore_travel",
|
293
|
+
"object": self,
|
294
|
+
"default": True,
|
295
|
+
"type": bool,
|
296
|
+
"label": "Ignore travel",
|
297
|
+
"tip": "Try to take only 'valid' movements",
|
298
|
+
"section": "_10_Path",
|
299
|
+
},
|
300
|
+
{
|
301
|
+
"attr": "split_path",
|
302
|
+
"object": self,
|
303
|
+
"default": True,
|
304
|
+
"type": bool,
|
305
|
+
"label": "Split paths",
|
306
|
+
"tip": "Split path into smaller chunks",
|
307
|
+
"section": "_10_Path",
|
308
|
+
},
|
309
|
+
{
|
310
|
+
"attr": "no_duplicates",
|
311
|
+
"object": self,
|
312
|
+
"default": True,
|
313
|
+
"type": bool,
|
314
|
+
"label": "Single occurrence",
|
315
|
+
"tip": "Prevent duplicate creation of segments (like in a multipass operation)",
|
316
|
+
"section": "_10_Path",
|
317
|
+
},
|
318
|
+
{
|
319
|
+
"attr": "treat_z_as_power",
|
320
|
+
"object": self,
|
321
|
+
"default": False,
|
322
|
+
"type": bool,
|
323
|
+
"label": "Treat Z-Movement as On/Off",
|
324
|
+
"tip": "Use negative Z-Values as a Power-On indicator, positive values as travel",
|
325
|
+
"conditional": (self, "ignore_travel"),
|
326
|
+
"section": "_10_Path",
|
327
|
+
"subsection": "_10_Z-Axis",
|
328
|
+
},
|
329
|
+
{
|
330
|
+
"attr": "z_only_negative",
|
331
|
+
"object": self,
|
332
|
+
"default": False,
|
333
|
+
"type": bool,
|
334
|
+
"label": "Only negative",
|
335
|
+
"tip": "Active: use positive values as travel\nInactive: use all non-zero values",
|
336
|
+
"conditional": (self, "treat_z_as_power"),
|
337
|
+
"section": "_10_Path",
|
338
|
+
"subsection": "_10_Z-Axis",
|
339
|
+
},
|
340
|
+
{
|
341
|
+
"attr": "origin",
|
342
|
+
"object": self,
|
343
|
+
"default": 1,
|
344
|
+
"type": int,
|
345
|
+
"style": "option",
|
346
|
+
"choices": (0, 1, 2, 3),
|
347
|
+
"display": (
|
348
|
+
"Top Left",
|
349
|
+
"Bottom Left",
|
350
|
+
"Center",
|
351
|
+
"Center (Y mirrored)",
|
352
|
+
),
|
353
|
+
"label": "Bed-Origin",
|
354
|
+
"tip": "Correct starting point",
|
355
|
+
"section": "_20_Correct Orientation",
|
356
|
+
},
|
357
|
+
{
|
358
|
+
"attr": "create_operations",
|
359
|
+
"object": self,
|
360
|
+
"default": False,
|
361
|
+
"type": bool,
|
362
|
+
"label": "Create operations",
|
363
|
+
"tip": "Create corresponding operations for Power and Speed pairs",
|
364
|
+
"section": "_30_Operation",
|
365
|
+
},
|
366
|
+
{
|
367
|
+
"attr": "scale_speed",
|
368
|
+
"object": self,
|
369
|
+
"default": False,
|
370
|
+
"type": bool,
|
371
|
+
"label": "Scale Speed",
|
372
|
+
"tip": "Set lower and higher level to scale the speed\n"
|
373
|
+
+ "Minimum speed used will be mapped to lower level\n"
|
374
|
+
+ "Maximum speed used will be mapped to upper level",
|
375
|
+
"conditional": (self, "create_operations"),
|
376
|
+
"section": "_30_Operation",
|
377
|
+
"subsection": "_20_Speed",
|
378
|
+
},
|
379
|
+
{
|
380
|
+
"attr": "scale_speed_lower",
|
381
|
+
"object": self,
|
382
|
+
"default": 2,
|
383
|
+
"type": float,
|
384
|
+
"label": "Lowest speed",
|
385
|
+
"trailer": "mm/sec",
|
386
|
+
"tip": "Minimum speed used will be mapped to lower level",
|
387
|
+
"conditional": (self, "scale_speed"),
|
388
|
+
"section": "_30_Operation",
|
389
|
+
"subsection": "_20_Speed",
|
390
|
+
},
|
391
|
+
{
|
392
|
+
"attr": "scale_speed_higher",
|
393
|
+
"object": self,
|
394
|
+
"default": 200,
|
395
|
+
"type": float,
|
396
|
+
"label": "Highest speed",
|
397
|
+
"trailer": "mm/sec",
|
398
|
+
"tip": "Maximum speed used will be mapped to upper level",
|
399
|
+
"conditional": (self, "scale_speed"),
|
400
|
+
"section": "_30_Operation",
|
401
|
+
"subsection": "_20_Speed",
|
402
|
+
},
|
403
|
+
{
|
404
|
+
"attr": "scale_power",
|
405
|
+
"object": self,
|
406
|
+
"default": False,
|
407
|
+
"type": bool,
|
408
|
+
"label": "Scale Power",
|
409
|
+
"tip": "Set lower and higher level to scale the power\n"
|
410
|
+
+ "Minimum power used will be mapped to lower level\n"
|
411
|
+
+ "Maximum power used will be mapped to upper level",
|
412
|
+
"section": "_30_Operation",
|
413
|
+
"subsection": "_20_Power",
|
414
|
+
},
|
415
|
+
{
|
416
|
+
"attr": "scale_power_lower",
|
417
|
+
"object": self,
|
418
|
+
"default": 200,
|
419
|
+
"type": float,
|
420
|
+
"label": "Lowest Power",
|
421
|
+
"trailer": "ppi",
|
422
|
+
"tip": "Minimum power used will be mapped to lower level",
|
423
|
+
"conditional": (self, "scale_power"),
|
424
|
+
"section": "_30_Operation",
|
425
|
+
"subsection": "_20_Power",
|
426
|
+
},
|
427
|
+
{
|
428
|
+
"attr": "scale_power_higher",
|
429
|
+
"object": self,
|
430
|
+
"default": 1000,
|
431
|
+
"type": float,
|
432
|
+
"label": "Highest power",
|
433
|
+
"trailer": "",
|
434
|
+
"tip": "Maximum power used will be mapped to upper level",
|
435
|
+
"conditional": (self, "scale_power"),
|
436
|
+
"section": "_30_Operation",
|
437
|
+
"subsection": "_20_Power",
|
438
|
+
},
|
439
|
+
]
|
440
|
+
|
441
|
+
def parse(self, blob_type, data, elements, options=None):
|
442
|
+
"""Parse the data with the given emulator and create and add the relevant paths.
|
443
|
+
|
444
|
+
Args:
|
445
|
+
blob_type: the emulator to use to parse the data, suffix entry value.
|
446
|
+
data (bytes): the grbl code to parse
|
447
|
+
elements (class): context for elements
|
448
|
+
options (disctionary, optional): A dictionary with settings. Defaults to None.
|
449
|
+
"""
|
450
|
+
# _("Driver to path")
|
451
|
+
with elements.undoscope("Driver to path"):
|
452
|
+
plotter = PlotterDriver()
|
453
|
+
for opt in self.options:
|
454
|
+
if hasattr(plotter, opt["attr"]):
|
455
|
+
setattr(plotter, opt["attr"], getattr(self, opt["attr"]))
|
456
|
+
|
457
|
+
spooler_job = elements.lookup(f"spoolerjob/{blob_type}")
|
458
|
+
if spooler_job is None:
|
459
|
+
return
|
460
|
+
job_object = spooler_job(plotter, elements.space.display.matrix)
|
461
|
+
if job_object is None:
|
462
|
+
return
|
463
|
+
job_object.write_blob(data)
|
464
|
+
while not job_object.execute():
|
465
|
+
# Still more to execute.
|
466
|
+
pass
|
467
|
+
|
468
|
+
op_nodes = {}
|
469
|
+
color_index = 0
|
470
|
+
color_array = (
|
471
|
+
"blue",
|
472
|
+
"lime",
|
473
|
+
"red",
|
474
|
+
"black",
|
475
|
+
"magenta",
|
476
|
+
"cyan",
|
477
|
+
"yellow",
|
478
|
+
"teal",
|
479
|
+
"orange",
|
480
|
+
"aqua",
|
481
|
+
"fuchsia",
|
482
|
+
"navy",
|
483
|
+
"olive",
|
484
|
+
"springgreen",
|
485
|
+
)
|
486
|
+
if self.no_duplicates:
|
487
|
+
for idx1 in range(0, len(plotter.paths) - 1):
|
488
|
+
path1 = plotter.paths[idx1]
|
489
|
+
for idx2 in range(idx1 + 1, len(plotter.paths)):
|
490
|
+
path2 = plotter.paths[idx2]
|
491
|
+
if path1 == path2:
|
492
|
+
plotter.paths[idx2] = None
|
493
|
+
minspeed = None
|
494
|
+
maxspeed = None
|
495
|
+
minpower = None
|
496
|
+
maxpower = None
|
497
|
+
if self.scale_power or self.scale_speed:
|
498
|
+
for op in plotter.operations:
|
499
|
+
values = op.split("|")
|
500
|
+
if len(values) > 1:
|
501
|
+
speed = float(values[0])
|
502
|
+
power = float(values[1])
|
503
|
+
if speed != 0:
|
504
|
+
if minspeed is None:
|
505
|
+
minspeed = speed
|
506
|
+
else:
|
507
|
+
minspeed = min(minspeed, speed)
|
508
|
+
if maxspeed is None:
|
509
|
+
maxspeed = speed
|
510
|
+
else:
|
511
|
+
maxspeed = max(maxspeed, speed)
|
512
|
+
if power != 0:
|
513
|
+
if minpower is None:
|
514
|
+
minpower = power
|
515
|
+
else:
|
516
|
+
minpower = min(minpower, power)
|
517
|
+
if maxpower is None:
|
518
|
+
maxpower = power
|
519
|
+
else:
|
520
|
+
maxpower = max(maxpower, power)
|
521
|
+
if minpower is None or maxpower is None:
|
522
|
+
self.scale_power = False
|
523
|
+
elif minpower == maxpower:
|
524
|
+
maxpower = minpower + 1
|
525
|
+
if minspeed is None or maxspeed is None:
|
526
|
+
self.scale_speed = False
|
527
|
+
elif minspeed == maxspeed:
|
528
|
+
maxspeed = minspeed + 1
|
529
|
+
|
530
|
+
for index, path in enumerate(plotter.paths):
|
531
|
+
if path is None or len(path) == 0:
|
532
|
+
continue
|
533
|
+
color = Color("blue")
|
534
|
+
opnode = None
|
535
|
+
if self.create_operations:
|
536
|
+
for op in plotter.operations:
|
537
|
+
values = op.split("|")
|
538
|
+
speed = float(values[0])
|
539
|
+
power = float(values[1])
|
540
|
+
zvalue = float(values[2])
|
541
|
+
if index in plotter.operations[op]:
|
542
|
+
if op in op_nodes:
|
543
|
+
opnode = op_nodes[op]
|
544
|
+
else:
|
545
|
+
if self.scale_power and power != 0:
|
546
|
+
power = self.scale_power_lower + (
|
547
|
+
power - minpower
|
548
|
+
) / (maxpower - minpower) * (
|
549
|
+
self.scale_power_higher - self.scale_power_lower
|
550
|
+
)
|
551
|
+
if self.scale_speed and speed != 0:
|
552
|
+
speed = self.scale_speed_lower + (
|
553
|
+
speed - minspeed
|
554
|
+
) / (maxspeed - minspeed) * (
|
555
|
+
self.scale_speed_higher - self.scale_speed_lower
|
556
|
+
)
|
557
|
+
lbl = f"Grbl - P={power}, S={speed}"
|
558
|
+
if zvalue != 0:
|
559
|
+
# convert into a length
|
560
|
+
zlen = Length(amount=zvalue, digits=4).length_mm
|
561
|
+
lbl += f", Z={zlen}"
|
562
|
+
opnode = EngraveOpNode(label=lbl)
|
563
|
+
opnode.speed = speed
|
564
|
+
if power == 0:
|
565
|
+
power = 1000
|
566
|
+
opnode.power = power
|
567
|
+
opnode.color = Color(color_array[color_index])
|
568
|
+
color_index = (color_index + 1) % len(color_array)
|
569
|
+
|
570
|
+
elements.op_branch.add_node(opnode)
|
571
|
+
op_nodes[op] = opnode
|
572
|
+
break
|
573
|
+
if opnode is not None:
|
574
|
+
color = opnode.color
|
575
|
+
node = elements.elem_branch.add(
|
576
|
+
type="elem path",
|
577
|
+
path=abs(path),
|
578
|
+
stroke=color,
|
579
|
+
stroke_width=UNITS_PER_PIXEL,
|
580
|
+
linejoin=Linejoin.JOIN_BEVEL,
|
581
|
+
linecap=Linecap.CAP_SQUARE,
|
582
|
+
)
|
583
|
+
if opnode is not None:
|
584
|
+
opnode.add_reference(node)
|