meerk40t 0.9.3001__py2.py3-none-any.whl → 0.9.7010__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- meerk40t/__init__.py +1 -1
- meerk40t/balormk/balor_params.py +167 -167
- meerk40t/balormk/clone_loader.py +457 -457
- meerk40t/balormk/controller.py +1566 -1512
- meerk40t/balormk/cylindermod.py +64 -0
- meerk40t/balormk/device.py +966 -1959
- meerk40t/balormk/driver.py +778 -591
- meerk40t/balormk/galvo_commands.py +1195 -0
- meerk40t/balormk/gui/balorconfig.py +237 -111
- meerk40t/balormk/gui/balorcontroller.py +191 -184
- meerk40t/balormk/gui/baloroperationproperties.py +116 -115
- meerk40t/balormk/gui/corscene.py +845 -0
- meerk40t/balormk/gui/gui.py +179 -147
- meerk40t/balormk/livelightjob.py +466 -382
- meerk40t/balormk/mock_connection.py +131 -109
- meerk40t/balormk/plugin.py +133 -135
- meerk40t/balormk/usb_connection.py +306 -301
- meerk40t/camera/__init__.py +1 -1
- meerk40t/camera/camera.py +514 -397
- meerk40t/camera/gui/camerapanel.py +1241 -1095
- meerk40t/camera/gui/gui.py +58 -58
- meerk40t/camera/plugin.py +441 -399
- meerk40t/ch341/__init__.py +27 -27
- meerk40t/ch341/ch341device.py +628 -628
- meerk40t/ch341/libusb.py +595 -589
- meerk40t/ch341/mock.py +171 -171
- meerk40t/ch341/windriver.py +157 -157
- meerk40t/constants.py +13 -0
- meerk40t/core/__init__.py +1 -1
- meerk40t/core/bindalias.py +550 -539
- meerk40t/core/core.py +47 -47
- meerk40t/core/cutcode/cubiccut.py +73 -73
- meerk40t/core/cutcode/cutcode.py +315 -312
- meerk40t/core/cutcode/cutgroup.py +141 -137
- meerk40t/core/cutcode/cutobject.py +192 -185
- meerk40t/core/cutcode/dwellcut.py +37 -37
- meerk40t/core/cutcode/gotocut.py +29 -29
- meerk40t/core/cutcode/homecut.py +29 -29
- meerk40t/core/cutcode/inputcut.py +34 -34
- meerk40t/core/cutcode/linecut.py +33 -33
- meerk40t/core/cutcode/outputcut.py +34 -34
- meerk40t/core/cutcode/plotcut.py +335 -335
- meerk40t/core/cutcode/quadcut.py +61 -61
- meerk40t/core/cutcode/rastercut.py +168 -148
- meerk40t/core/cutcode/waitcut.py +34 -34
- meerk40t/core/cutplan.py +1843 -1316
- meerk40t/core/drivers.py +330 -329
- meerk40t/core/elements/align.py +801 -669
- meerk40t/core/elements/branches.py +1844 -1507
- meerk40t/core/elements/clipboard.py +229 -219
- meerk40t/core/elements/element_treeops.py +4561 -2837
- meerk40t/core/elements/element_types.py +125 -105
- meerk40t/core/elements/elements.py +4329 -3617
- meerk40t/core/elements/files.py +117 -64
- meerk40t/core/elements/geometry.py +473 -224
- meerk40t/core/elements/grid.py +467 -316
- meerk40t/core/elements/materials.py +158 -94
- meerk40t/core/elements/notes.py +50 -38
- meerk40t/core/elements/offset_clpr.py +933 -912
- meerk40t/core/elements/offset_mk.py +963 -955
- meerk40t/core/elements/penbox.py +339 -267
- meerk40t/core/elements/placements.py +300 -83
- meerk40t/core/elements/render.py +785 -687
- meerk40t/core/elements/shapes.py +2618 -2092
- meerk40t/core/elements/trace.py +651 -563
- meerk40t/core/elements/tree_commands.py +415 -409
- meerk40t/core/elements/undo_redo.py +116 -58
- meerk40t/core/elements/wordlist.py +319 -200
- meerk40t/core/exceptions.py +9 -9
- meerk40t/core/laserjob.py +220 -220
- meerk40t/core/logging.py +63 -63
- meerk40t/core/node/blobnode.py +83 -86
- meerk40t/core/node/bootstrap.py +105 -103
- meerk40t/core/node/branch_elems.py +40 -31
- meerk40t/core/node/branch_ops.py +45 -38
- meerk40t/core/node/branch_regmark.py +48 -41
- meerk40t/core/node/cutnode.py +29 -32
- meerk40t/core/node/effect_hatch.py +375 -257
- meerk40t/core/node/effect_warp.py +398 -0
- meerk40t/core/node/effect_wobble.py +441 -309
- meerk40t/core/node/elem_ellipse.py +404 -309
- meerk40t/core/node/elem_image.py +1082 -801
- meerk40t/core/node/elem_line.py +358 -292
- meerk40t/core/node/elem_path.py +259 -201
- meerk40t/core/node/elem_point.py +129 -102
- meerk40t/core/node/elem_polyline.py +310 -246
- meerk40t/core/node/elem_rect.py +376 -286
- meerk40t/core/node/elem_text.py +445 -418
- meerk40t/core/node/filenode.py +59 -40
- meerk40t/core/node/groupnode.py +138 -74
- meerk40t/core/node/image_processed.py +777 -766
- meerk40t/core/node/image_raster.py +156 -113
- meerk40t/core/node/layernode.py +31 -31
- meerk40t/core/node/mixins.py +135 -107
- meerk40t/core/node/node.py +1427 -1304
- meerk40t/core/node/nutils.py +117 -114
- meerk40t/core/node/op_cut.py +462 -335
- meerk40t/core/node/op_dots.py +296 -251
- meerk40t/core/node/op_engrave.py +414 -311
- meerk40t/core/node/op_image.py +755 -369
- meerk40t/core/node/op_raster.py +787 -522
- meerk40t/core/node/place_current.py +37 -40
- meerk40t/core/node/place_point.py +329 -126
- meerk40t/core/node/refnode.py +58 -47
- meerk40t/core/node/rootnode.py +225 -219
- meerk40t/core/node/util_console.py +48 -48
- meerk40t/core/node/util_goto.py +84 -65
- meerk40t/core/node/util_home.py +61 -61
- meerk40t/core/node/util_input.py +102 -102
- meerk40t/core/node/util_output.py +102 -102
- meerk40t/core/node/util_wait.py +65 -65
- meerk40t/core/parameters.py +709 -707
- meerk40t/core/planner.py +875 -785
- meerk40t/core/plotplanner.py +656 -652
- meerk40t/core/space.py +120 -113
- meerk40t/core/spoolers.py +706 -705
- meerk40t/core/svg_io.py +1836 -1549
- meerk40t/core/treeop.py +534 -445
- meerk40t/core/undos.py +278 -124
- meerk40t/core/units.py +784 -680
- meerk40t/core/view.py +393 -322
- meerk40t/core/webhelp.py +62 -62
- meerk40t/core/wordlist.py +513 -504
- meerk40t/cylinder/cylinder.py +247 -0
- meerk40t/cylinder/gui/cylindersettings.py +41 -0
- meerk40t/cylinder/gui/gui.py +24 -0
- meerk40t/device/__init__.py +1 -1
- meerk40t/device/basedevice.py +322 -123
- meerk40t/device/devicechoices.py +50 -0
- meerk40t/device/dummydevice.py +163 -128
- meerk40t/device/gui/defaultactions.py +618 -602
- meerk40t/device/gui/effectspanel.py +114 -0
- meerk40t/device/gui/formatterpanel.py +253 -290
- meerk40t/device/gui/warningpanel.py +337 -260
- meerk40t/device/mixins.py +13 -13
- meerk40t/dxf/__init__.py +1 -1
- meerk40t/dxf/dxf_io.py +766 -554
- meerk40t/dxf/plugin.py +47 -35
- meerk40t/external_plugins.py +79 -79
- meerk40t/external_plugins_build.py +28 -28
- meerk40t/extra/cag.py +112 -116
- meerk40t/extra/coolant.py +403 -0
- meerk40t/extra/encode_detect.py +198 -0
- meerk40t/extra/ezd.py +1165 -1165
- meerk40t/extra/hershey.py +835 -340
- meerk40t/extra/imageactions.py +322 -316
- meerk40t/extra/inkscape.py +630 -622
- meerk40t/extra/lbrn.py +424 -424
- meerk40t/extra/outerworld.py +284 -0
- meerk40t/extra/param_functions.py +1542 -1556
- meerk40t/extra/potrace.py +257 -253
- meerk40t/extra/serial_exchange.py +118 -0
- meerk40t/extra/updater.py +602 -453
- meerk40t/extra/vectrace.py +147 -146
- meerk40t/extra/winsleep.py +83 -83
- meerk40t/extra/xcs_reader.py +597 -0
- meerk40t/fill/fills.py +781 -335
- meerk40t/fill/patternfill.py +1061 -1061
- meerk40t/fill/patterns.py +614 -567
- meerk40t/grbl/control.py +87 -87
- meerk40t/grbl/controller.py +990 -903
- meerk40t/grbl/device.py +1081 -768
- meerk40t/grbl/driver.py +989 -771
- meerk40t/grbl/emulator.py +532 -497
- meerk40t/grbl/gcodejob.py +783 -767
- meerk40t/grbl/gui/grblconfiguration.py +373 -298
- meerk40t/grbl/gui/grblcontroller.py +485 -271
- meerk40t/grbl/gui/grblhardwareconfig.py +269 -153
- meerk40t/grbl/gui/grbloperationconfig.py +105 -0
- meerk40t/grbl/gui/gui.py +147 -116
- meerk40t/grbl/interpreter.py +44 -44
- meerk40t/grbl/loader.py +22 -22
- meerk40t/grbl/mock_connection.py +56 -56
- meerk40t/grbl/plugin.py +294 -264
- meerk40t/grbl/serial_connection.py +93 -88
- meerk40t/grbl/tcp_connection.py +81 -79
- meerk40t/grbl/ws_connection.py +112 -0
- meerk40t/gui/__init__.py +1 -1
- meerk40t/gui/about.py +2042 -296
- meerk40t/gui/alignment.py +1644 -1608
- meerk40t/gui/autoexec.py +199 -0
- meerk40t/gui/basicops.py +791 -670
- meerk40t/gui/bufferview.py +77 -71
- meerk40t/gui/busy.py +170 -133
- meerk40t/gui/choicepropertypanel.py +1673 -1469
- meerk40t/gui/consolepanel.py +706 -542
- meerk40t/gui/devicepanel.py +687 -581
- meerk40t/gui/dialogoptions.py +110 -107
- meerk40t/gui/executejob.py +316 -306
- meerk40t/gui/fonts.py +90 -90
- meerk40t/gui/functionwrapper.py +252 -0
- meerk40t/gui/gui_mixins.py +729 -0
- meerk40t/gui/guicolors.py +205 -182
- meerk40t/gui/help_assets/help_assets.py +218 -201
- meerk40t/gui/helper.py +154 -0
- meerk40t/gui/hersheymanager.py +1430 -846
- meerk40t/gui/icons.py +3422 -2747
- meerk40t/gui/imagesplitter.py +555 -508
- meerk40t/gui/keymap.py +354 -344
- meerk40t/gui/laserpanel.py +892 -806
- meerk40t/gui/laserrender.py +1470 -1232
- meerk40t/gui/lasertoolpanel.py +805 -793
- meerk40t/gui/magnetoptions.py +436 -0
- meerk40t/gui/materialmanager.py +2917 -0
- meerk40t/gui/materialtest.py +1722 -1694
- meerk40t/gui/mkdebug.py +646 -359
- meerk40t/gui/mwindow.py +163 -140
- meerk40t/gui/navigationpanels.py +2605 -2467
- meerk40t/gui/notes.py +143 -142
- meerk40t/gui/opassignment.py +414 -410
- meerk40t/gui/operation_info.py +310 -299
- meerk40t/gui/plugin.py +494 -328
- meerk40t/gui/position.py +714 -669
- meerk40t/gui/preferences.py +901 -650
- meerk40t/gui/propertypanels/attributes.py +1461 -1131
- meerk40t/gui/propertypanels/blobproperty.py +117 -114
- meerk40t/gui/propertypanels/consoleproperty.py +83 -80
- meerk40t/gui/propertypanels/gotoproperty.py +77 -0
- meerk40t/gui/propertypanels/groupproperties.py +223 -217
- meerk40t/gui/propertypanels/hatchproperty.py +489 -469
- meerk40t/gui/propertypanels/imageproperty.py +2244 -1384
- meerk40t/gui/propertypanels/inputproperty.py +59 -58
- meerk40t/gui/propertypanels/opbranchproperties.py +82 -80
- meerk40t/gui/propertypanels/operationpropertymain.py +1890 -1638
- meerk40t/gui/propertypanels/outputproperty.py +59 -58
- meerk40t/gui/propertypanels/pathproperty.py +389 -380
- meerk40t/gui/propertypanels/placementproperty.py +1214 -383
- meerk40t/gui/propertypanels/pointproperty.py +140 -136
- meerk40t/gui/propertypanels/propertywindow.py +313 -181
- meerk40t/gui/propertypanels/rasterwizardpanels.py +996 -912
- meerk40t/gui/propertypanels/regbranchproperties.py +76 -0
- meerk40t/gui/propertypanels/textproperty.py +770 -755
- meerk40t/gui/propertypanels/waitproperty.py +56 -55
- meerk40t/gui/propertypanels/warpproperty.py +121 -0
- meerk40t/gui/propertypanels/wobbleproperty.py +255 -204
- meerk40t/gui/ribbon.py +2468 -2210
- meerk40t/gui/scene/scene.py +1100 -1051
- meerk40t/gui/scene/sceneconst.py +22 -22
- meerk40t/gui/scene/scenepanel.py +439 -349
- meerk40t/gui/scene/scenespacewidget.py +365 -365
- meerk40t/gui/scene/widget.py +518 -505
- meerk40t/gui/scenewidgets/affinemover.py +215 -215
- meerk40t/gui/scenewidgets/attractionwidget.py +315 -309
- meerk40t/gui/scenewidgets/bedwidget.py +120 -97
- meerk40t/gui/scenewidgets/elementswidget.py +137 -107
- meerk40t/gui/scenewidgets/gridwidget.py +785 -745
- meerk40t/gui/scenewidgets/guidewidget.py +765 -765
- meerk40t/gui/scenewidgets/laserpathwidget.py +66 -66
- meerk40t/gui/scenewidgets/machineoriginwidget.py +86 -86
- meerk40t/gui/scenewidgets/nodeselector.py +28 -28
- meerk40t/gui/scenewidgets/rectselectwidget.py +589 -346
- meerk40t/gui/scenewidgets/relocatewidget.py +33 -33
- meerk40t/gui/scenewidgets/reticlewidget.py +83 -83
- meerk40t/gui/scenewidgets/selectionwidget.py +2952 -2756
- meerk40t/gui/simpleui.py +357 -333
- meerk40t/gui/simulation.py +2431 -2094
- meerk40t/gui/snapoptions.py +208 -203
- meerk40t/gui/spoolerpanel.py +1227 -1180
- meerk40t/gui/statusbarwidgets/defaultoperations.py +480 -353
- meerk40t/gui/statusbarwidgets/infowidget.py +520 -483
- meerk40t/gui/statusbarwidgets/opassignwidget.py +356 -355
- meerk40t/gui/statusbarwidgets/selectionwidget.py +172 -171
- meerk40t/gui/statusbarwidgets/shapepropwidget.py +754 -236
- meerk40t/gui/statusbarwidgets/statusbar.py +272 -260
- meerk40t/gui/statusbarwidgets/statusbarwidget.py +268 -270
- meerk40t/gui/statusbarwidgets/strokewidget.py +267 -251
- meerk40t/gui/themes.py +200 -78
- meerk40t/gui/tips.py +591 -0
- meerk40t/gui/toolwidgets/circlebrush.py +35 -35
- meerk40t/gui/toolwidgets/toolcircle.py +248 -242
- meerk40t/gui/toolwidgets/toolcontainer.py +82 -77
- meerk40t/gui/toolwidgets/tooldraw.py +97 -90
- meerk40t/gui/toolwidgets/toolellipse.py +219 -212
- meerk40t/gui/toolwidgets/toolimagecut.py +25 -132
- meerk40t/gui/toolwidgets/toolline.py +39 -144
- meerk40t/gui/toolwidgets/toollinetext.py +79 -236
- meerk40t/gui/toolwidgets/toollinetext_inline.py +296 -0
- meerk40t/gui/toolwidgets/toolmeasure.py +160 -216
- meerk40t/gui/toolwidgets/toolnodeedit.py +2088 -2074
- meerk40t/gui/toolwidgets/toolnodemove.py +92 -94
- meerk40t/gui/toolwidgets/toolparameter.py +754 -668
- meerk40t/gui/toolwidgets/toolplacement.py +108 -108
- meerk40t/gui/toolwidgets/toolpoint.py +68 -59
- meerk40t/gui/toolwidgets/toolpointlistbuilder.py +294 -0
- meerk40t/gui/toolwidgets/toolpointmove.py +183 -0
- meerk40t/gui/toolwidgets/toolpolygon.py +288 -403
- meerk40t/gui/toolwidgets/toolpolyline.py +38 -196
- meerk40t/gui/toolwidgets/toolrect.py +211 -207
- meerk40t/gui/toolwidgets/toolrelocate.py +72 -72
- meerk40t/gui/toolwidgets/toolribbon.py +598 -113
- meerk40t/gui/toolwidgets/tooltabedit.py +546 -0
- meerk40t/gui/toolwidgets/tooltext.py +98 -89
- meerk40t/gui/toolwidgets/toolvector.py +213 -204
- meerk40t/gui/toolwidgets/toolwidget.py +39 -39
- meerk40t/gui/usbconnect.py +98 -91
- meerk40t/gui/utilitywidgets/buttonwidget.py +18 -18
- meerk40t/gui/utilitywidgets/checkboxwidget.py +90 -90
- meerk40t/gui/utilitywidgets/controlwidget.py +14 -14
- meerk40t/gui/utilitywidgets/cyclocycloidwidget.py +343 -340
- meerk40t/gui/utilitywidgets/debugwidgets.py +148 -0
- meerk40t/gui/utilitywidgets/handlewidget.py +27 -27
- meerk40t/gui/utilitywidgets/harmonograph.py +450 -447
- meerk40t/gui/utilitywidgets/openclosewidget.py +40 -40
- meerk40t/gui/utilitywidgets/rotationwidget.py +54 -54
- meerk40t/gui/utilitywidgets/scalewidget.py +75 -75
- meerk40t/gui/utilitywidgets/seekbarwidget.py +183 -183
- meerk40t/gui/utilitywidgets/togglewidget.py +142 -142
- meerk40t/gui/utilitywidgets/toolbarwidget.py +8 -8
- meerk40t/gui/wordlisteditor.py +985 -931
- meerk40t/gui/wxmeerk40t.py +1444 -1169
- meerk40t/gui/wxmmain.py +5578 -4112
- meerk40t/gui/wxmribbon.py +1591 -1076
- meerk40t/gui/wxmscene.py +1635 -1453
- meerk40t/gui/wxmtree.py +2410 -2089
- meerk40t/gui/wxutils.py +1769 -1099
- meerk40t/gui/zmatrix.py +102 -102
- meerk40t/image/__init__.py +1 -1
- meerk40t/image/dither.py +429 -0
- meerk40t/image/imagetools.py +2778 -2269
- meerk40t/internal_plugins.py +150 -130
- meerk40t/kernel/__init__.py +63 -12
- meerk40t/kernel/channel.py +259 -212
- meerk40t/kernel/context.py +538 -538
- meerk40t/kernel/exceptions.py +41 -41
- meerk40t/kernel/functions.py +463 -414
- meerk40t/kernel/jobs.py +100 -100
- meerk40t/kernel/kernel.py +3809 -3571
- meerk40t/kernel/lifecycles.py +71 -71
- meerk40t/kernel/module.py +49 -49
- meerk40t/kernel/service.py +147 -147
- meerk40t/kernel/settings.py +383 -343
- meerk40t/lihuiyu/controller.py +883 -876
- meerk40t/lihuiyu/device.py +1181 -1069
- meerk40t/lihuiyu/driver.py +1466 -1372
- meerk40t/lihuiyu/gui/gui.py +127 -106
- meerk40t/lihuiyu/gui/lhyaccelgui.py +377 -363
- meerk40t/lihuiyu/gui/lhycontrollergui.py +741 -651
- meerk40t/lihuiyu/gui/lhydrivergui.py +470 -446
- meerk40t/lihuiyu/gui/lhyoperationproperties.py +238 -237
- meerk40t/lihuiyu/gui/tcpcontroller.py +226 -190
- meerk40t/lihuiyu/interpreter.py +53 -53
- meerk40t/lihuiyu/laserspeed.py +450 -450
- meerk40t/lihuiyu/loader.py +90 -90
- meerk40t/lihuiyu/parser.py +404 -404
- meerk40t/lihuiyu/plugin.py +101 -102
- meerk40t/lihuiyu/tcp_connection.py +111 -109
- meerk40t/main.py +231 -165
- meerk40t/moshi/builder.py +788 -781
- meerk40t/moshi/controller.py +505 -499
- meerk40t/moshi/device.py +495 -442
- meerk40t/moshi/driver.py +862 -696
- meerk40t/moshi/gui/gui.py +78 -76
- meerk40t/moshi/gui/moshicontrollergui.py +538 -522
- meerk40t/moshi/gui/moshidrivergui.py +87 -75
- meerk40t/moshi/plugin.py +43 -43
- meerk40t/network/console_server.py +102 -57
- meerk40t/network/kernelserver.py +10 -9
- meerk40t/network/tcp_server.py +142 -140
- meerk40t/network/udp_server.py +103 -77
- meerk40t/network/web_server.py +390 -0
- meerk40t/newly/controller.py +1158 -1144
- meerk40t/newly/device.py +874 -732
- meerk40t/newly/driver.py +540 -412
- meerk40t/newly/gui/gui.py +219 -188
- meerk40t/newly/gui/newlyconfig.py +116 -101
- meerk40t/newly/gui/newlycontroller.py +193 -186
- meerk40t/newly/gui/operationproperties.py +51 -51
- meerk40t/newly/mock_connection.py +82 -82
- meerk40t/newly/newly_params.py +56 -56
- meerk40t/newly/plugin.py +1214 -1246
- meerk40t/newly/usb_connection.py +322 -322
- meerk40t/rotary/gui/gui.py +52 -46
- meerk40t/rotary/gui/rotarysettings.py +240 -232
- meerk40t/rotary/rotary.py +202 -98
- meerk40t/ruida/control.py +291 -91
- meerk40t/ruida/controller.py +138 -1088
- meerk40t/ruida/device.py +672 -231
- meerk40t/ruida/driver.py +534 -472
- meerk40t/ruida/emulator.py +1494 -1491
- meerk40t/ruida/exceptions.py +4 -4
- meerk40t/ruida/gui/gui.py +71 -76
- meerk40t/ruida/gui/ruidaconfig.py +239 -72
- meerk40t/ruida/gui/ruidacontroller.py +187 -184
- meerk40t/ruida/gui/ruidaoperationproperties.py +48 -47
- meerk40t/ruida/loader.py +54 -52
- meerk40t/ruida/mock_connection.py +57 -109
- meerk40t/ruida/plugin.py +124 -87
- meerk40t/ruida/rdjob.py +2084 -945
- meerk40t/ruida/serial_connection.py +116 -0
- meerk40t/ruida/tcp_connection.py +146 -0
- meerk40t/ruida/udp_connection.py +73 -0
- meerk40t/svgelements.py +9671 -9669
- meerk40t/tools/driver_to_path.py +584 -579
- meerk40t/tools/geomstr.py +5583 -4680
- meerk40t/tools/jhfparser.py +357 -292
- meerk40t/tools/kerftest.py +904 -890
- meerk40t/tools/livinghinges.py +1168 -1033
- meerk40t/tools/pathtools.py +987 -949
- meerk40t/tools/pmatrix.py +234 -0
- meerk40t/tools/pointfinder.py +942 -942
- meerk40t/tools/polybool.py +940 -940
- meerk40t/tools/rasterplotter.py +1660 -547
- meerk40t/tools/shxparser.py +989 -901
- meerk40t/tools/ttfparser.py +726 -446
- meerk40t/tools/zinglplotter.py +595 -593
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.dist-info}/LICENSE +21 -21
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.dist-info}/METADATA +150 -139
- meerk40t-0.9.7010.dist-info/RECORD +445 -0
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.dist-info}/WHEEL +1 -1
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.dist-info}/top_level.txt +0 -1
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.dist-info}/zip-safe +1 -1
- meerk40t/balormk/elementlightjob.py +0 -159
- meerk40t-0.9.3001.dist-info/RECORD +0 -437
- test/bootstrap.py +0 -63
- test/test_cli.py +0 -12
- test/test_core_cutcode.py +0 -418
- test/test_core_elements.py +0 -144
- test/test_core_plotplanner.py +0 -397
- test/test_core_viewports.py +0 -312
- test/test_drivers_grbl.py +0 -108
- test/test_drivers_lihuiyu.py +0 -443
- test/test_drivers_newly.py +0 -113
- test/test_element_degenerate_points.py +0 -43
- test/test_elements_classify.py +0 -97
- test/test_elements_penbox.py +0 -22
- test/test_file_svg.py +0 -176
- test/test_fill.py +0 -155
- test/test_geomstr.py +0 -1523
- test/test_geomstr_nodes.py +0 -18
- test/test_imagetools_actualize.py +0 -306
- test/test_imagetools_wizard.py +0 -258
- test/test_kernel.py +0 -200
- test/test_laser_speeds.py +0 -3303
- test/test_length.py +0 -57
- test/test_lifecycle.py +0 -66
- test/test_operations.py +0 -251
- test/test_operations_hatch.py +0 -57
- test/test_ruida.py +0 -19
- test/test_spooler.py +0 -22
- test/test_tools_rasterplotter.py +0 -29
- test/test_wobble.py +0 -133
- test/test_zingl.py +0 -124
- {test → meerk40t/cylinder}/__init__.py +0 -0
- /meerk40t/{core/element_commands.py → cylinder/gui/__init__.py} +0 -0
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.dist-info}/entry_points.txt +0 -0
meerk40t/extra/ezd.py
CHANGED
@@ -1,1165 +1,1165 @@
|
|
1
|
-
"""
|
2
|
-
Parser for .ezd files.
|
3
|
-
|
4
|
-
These are a working type file produced by EZCad2™. They contain several pens and different settings that were used by
|
5
|
-
the program when the file was saved. The vector objects consist of a series of laser-ready commands/shapes which refer
|
6
|
-
to the required pen. Some modification objects like hatch and spiral work like a group containing other sub-elements
|
7
|
-
and also contain the cached curve/path data. The image objects contain a standard 24 bit bitmap image. All elements
|
8
|
-
are coordinated relative to the center of the working area and, it is much more common to be given the center point than
|
9
|
-
a specific corner. Nearly all coordinates are in mm, and denote the deviation from the center point.
|
10
|
-
|
11
|
-
"""
|
12
|
-
import math
|
13
|
-
import struct
|
14
|
-
from io import BytesIO
|
15
|
-
|
16
|
-
from meerk40t.core.exceptions import BadFileError
|
17
|
-
from meerk40t.core.units import UNITS_PER_INCH, UNITS_PER_MM
|
18
|
-
from meerk40t.svgelements import Color, Matrix, Path, Polygon
|
19
|
-
|
20
|
-
|
21
|
-
def plugin(kernel, lifecycle):
|
22
|
-
if lifecycle == "
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
""
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
"""
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
length
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
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
|
-
self.
|
178
|
-
self.
|
179
|
-
self.
|
180
|
-
|
181
|
-
|
182
|
-
self.
|
183
|
-
self.
|
184
|
-
self.
|
185
|
-
self.
|
186
|
-
self.
|
187
|
-
self.
|
188
|
-
self.
|
189
|
-
self.
|
190
|
-
self.
|
191
|
-
self.
|
192
|
-
self.
|
193
|
-
self.
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
self.
|
202
|
-
self.
|
203
|
-
|
204
|
-
self.
|
205
|
-
self.
|
206
|
-
self.
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
self.
|
223
|
-
self.
|
224
|
-
self.
|
225
|
-
self.
|
226
|
-
self.
|
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
|
-
self._locations["
|
262
|
-
self._locations["
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
"""
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
"""
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
self.
|
286
|
-
self.
|
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
|
-
file.
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
"""
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
""
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
"""
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
""
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
"""
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
"""
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
)
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
self.
|
427
|
-
self.
|
428
|
-
self.
|
429
|
-
|
430
|
-
|
431
|
-
self.
|
432
|
-
self.
|
433
|
-
self.
|
434
|
-
|
435
|
-
self.
|
436
|
-
self.
|
437
|
-
self.
|
438
|
-
self.
|
439
|
-
self.
|
440
|
-
self.
|
441
|
-
self.
|
442
|
-
self.
|
443
|
-
self.
|
444
|
-
self
|
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
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
self.
|
530
|
-
self.
|
531
|
-
self.
|
532
|
-
self.
|
533
|
-
self.
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
self.
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
self.
|
587
|
-
self.
|
588
|
-
self.
|
589
|
-
self.
|
590
|
-
self.
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
self.
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
self.
|
679
|
-
self.
|
680
|
-
self.
|
681
|
-
self.
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
args
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
self.
|
703
|
-
self.
|
704
|
-
self.
|
705
|
-
self.
|
706
|
-
self.
|
707
|
-
self.
|
708
|
-
self.
|
709
|
-
self.
|
710
|
-
self.
|
711
|
-
self.
|
712
|
-
self.
|
713
|
-
self.
|
714
|
-
self.
|
715
|
-
self.
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
(
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
self.
|
755
|
-
self.
|
756
|
-
self.
|
757
|
-
self.
|
758
|
-
self.
|
759
|
-
self.
|
760
|
-
self.
|
761
|
-
self.
|
762
|
-
self.
|
763
|
-
self.
|
764
|
-
self.
|
765
|
-
self.
|
766
|
-
self.
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
self.
|
790
|
-
self.
|
791
|
-
|
792
|
-
|
793
|
-
self.
|
794
|
-
self.
|
795
|
-
self.
|
796
|
-
self.
|
797
|
-
self.
|
798
|
-
self.
|
799
|
-
self.
|
800
|
-
self.
|
801
|
-
|
802
|
-
self.
|
803
|
-
self.
|
804
|
-
self.
|
805
|
-
self.
|
806
|
-
self.
|
807
|
-
|
808
|
-
self.
|
809
|
-
self.
|
810
|
-
self.
|
811
|
-
self.
|
812
|
-
self.
|
813
|
-
self.
|
814
|
-
|
815
|
-
self.
|
816
|
-
self.
|
817
|
-
self.
|
818
|
-
self.
|
819
|
-
self.
|
820
|
-
self.
|
821
|
-
|
822
|
-
self.
|
823
|
-
self.
|
824
|
-
self.
|
825
|
-
self.
|
826
|
-
self.
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
object_type
|
867
|
-
|
868
|
-
|
869
|
-
ez_class
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
"
|
890
|
-
"
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
ez_processor = EZProcessor(elements_service)
|
895
|
-
ez_processor.process(ezfile, pathname)
|
896
|
-
return True
|
897
|
-
|
898
|
-
|
899
|
-
class EZProcessor:
|
900
|
-
def __init__(self, elements):
|
901
|
-
self.elements = elements
|
902
|
-
self.element_list = list()
|
903
|
-
self.regmark_list = list()
|
904
|
-
self.pathname = None
|
905
|
-
self.regmark = self.elements.reg_branch
|
906
|
-
self.op_branch = elements.op_branch
|
907
|
-
self.elem_branch = elements.elem_branch
|
908
|
-
|
909
|
-
self.width = elements.device.view.unit_width
|
910
|
-
self.height = elements.device.view.unit_height
|
911
|
-
self.cx = self.width / 2.0
|
912
|
-
self.cy = self.height / 2.0
|
913
|
-
self.matrix = Matrix.scale(UNITS_PER_MM, -UNITS_PER_MM)
|
914
|
-
self.matrix.post_translate(self.cx, self.cy)
|
915
|
-
|
916
|
-
def process(self, ez, pathname):
|
917
|
-
self.op_branch.remove_all_children()
|
918
|
-
self.elem_branch.remove_all_children()
|
919
|
-
self.pathname = pathname
|
920
|
-
file_node = self.elem_branch.add(type="file", filepath=pathname)
|
921
|
-
file_node.focus()
|
922
|
-
for f in ez.objects:
|
923
|
-
self.parse(ez, f, file_node, self.op_branch)
|
924
|
-
|
925
|
-
def parse(self, ez, element, elem, op, op_add=None, path=None):
|
926
|
-
"""
|
927
|
-
Parse ez structure into MK specific tree structure and objects.
|
928
|
-
|
929
|
-
@param ez: EZFile object
|
930
|
-
@param element: ezobject being parsed.
|
931
|
-
@param elem: element context.
|
932
|
-
@param op: operation context
|
933
|
-
@param op_add: Operation we should add to rather than create.
|
934
|
-
@param path: Path we should append to rather than create.
|
935
|
-
@return:
|
936
|
-
"""
|
937
|
-
if isinstance(element, EZText):
|
938
|
-
node = elem.add(type="elem text", text=element.text, transform=self.matrix)
|
939
|
-
p = ez.pens[element.pen]
|
940
|
-
if op_add is None:
|
941
|
-
op_add = op.add(type="op engrave", **p.__dict__)
|
942
|
-
op_add.add_reference(node)
|
943
|
-
elif isinstance(element, EZCurve):
|
944
|
-
points = element.points
|
945
|
-
if len(points) == 0:
|
946
|
-
return
|
947
|
-
if path is None:
|
948
|
-
append_path = False
|
949
|
-
path = Path(stroke="black", transform=self.matrix)
|
950
|
-
else:
|
951
|
-
append_path = True
|
952
|
-
|
953
|
-
last_end = None
|
954
|
-
for t, closed, contour in points:
|
955
|
-
cpt = [
|
956
|
-
complex(contour[i], contour[i + 1])
|
957
|
-
for i in range(0, len(contour), 2)
|
958
|
-
]
|
959
|
-
if last_end != cpt[0]:
|
960
|
-
path.move(cpt[0])
|
961
|
-
if t == 1:
|
962
|
-
path.line(*cpt[1:])
|
963
|
-
elif t == 2:
|
964
|
-
path.quad(*cpt[1:])
|
965
|
-
elif t == 3:
|
966
|
-
path.cubic(*cpt[1:])
|
967
|
-
last_end = cpt[-1]
|
968
|
-
if points[-1][1]:
|
969
|
-
# Path is closed.
|
970
|
-
path.closed()
|
971
|
-
if append_path:
|
972
|
-
return
|
973
|
-
node = elem.add(
|
974
|
-
type="elem path",
|
975
|
-
path=path,
|
976
|
-
stroke_width=self.elements.default_strokewidth,
|
977
|
-
)
|
978
|
-
p = ez.pens[element.pen]
|
979
|
-
if op_add is None:
|
980
|
-
op_add = op.add(type="op engrave", **p.__dict__)
|
981
|
-
op_add.add_reference(node)
|
982
|
-
elif isinstance(element, EZPolygon):
|
983
|
-
m = element.matrix
|
984
|
-
mx = Matrix(m[0], m[1], m[3], m[4], m[6], m[7])
|
985
|
-
mx *= self.matrix
|
986
|
-
x0, y0 = element.corner_upper_left
|
987
|
-
x1, y1 = element.corner_bottom_right
|
988
|
-
step = math.tau / element.sides
|
989
|
-
cx, cy = (x0 + x1) / 2.0, (y0 + y1) / 2.0
|
990
|
-
rx = (x1 - x0) / 2.0
|
991
|
-
ry = (y1 - y0) / 2.0
|
992
|
-
pts = []
|
993
|
-
theta = step / 2.0
|
994
|
-
for i in range(element.sides):
|
995
|
-
pts.append((cx + math.cos(theta) * rx, cy + math.sin(theta) * ry))
|
996
|
-
theta += step
|
997
|
-
polyline = Polygon(points=pts, transform=mx, stroke="black")
|
998
|
-
node = elem.add(
|
999
|
-
type="elem polyline",
|
1000
|
-
shape=polyline,
|
1001
|
-
stroke_width=self.elements.default_strokewidth,
|
1002
|
-
)
|
1003
|
-
p = ez.pens[element.pen]
|
1004
|
-
if op_add is None:
|
1005
|
-
op_add = op.add(type="op engrave", **p.__dict__)
|
1006
|
-
op_add.add_reference(node)
|
1007
|
-
elif isinstance(element, EZCircle):
|
1008
|
-
m = element.matrix
|
1009
|
-
mx = Matrix(m[0], m[1], m[3], m[4], m[6], m[7])
|
1010
|
-
mx *= self.matrix
|
1011
|
-
node = elem.add(
|
1012
|
-
cx=element.center[0],
|
1013
|
-
cy=element.center[1],
|
1014
|
-
rx=element.radius,
|
1015
|
-
ry=element.radius,
|
1016
|
-
stroke=Color("black"),
|
1017
|
-
matrix=mx,
|
1018
|
-
stroke_width=self.elements.default_strokewidth,
|
1019
|
-
type="elem ellipse",
|
1020
|
-
)
|
1021
|
-
p = ez.pens[element.pen]
|
1022
|
-
if op_add is None:
|
1023
|
-
op_add = op.add(type="op engrave", **p.__dict__)
|
1024
|
-
op_add.add_reference(node)
|
1025
|
-
elif isinstance(element, EZEllipse):
|
1026
|
-
m = element.matrix
|
1027
|
-
mx = Matrix(m[0], m[1], m[3], m[4], m[6], m[7])
|
1028
|
-
mx *= self.matrix
|
1029
|
-
x0, y0 = element.corner_upper_left
|
1030
|
-
x1, y1 = element.corner_bottom_right
|
1031
|
-
node = elem.add(
|
1032
|
-
cx=(x0 + x1) / 2.0,
|
1033
|
-
cy=(y0 + y1) / 2.0,
|
1034
|
-
rx=(x1 - x0) / 2.0,
|
1035
|
-
ry=(y1 - y0) / 2.0,
|
1036
|
-
matrix=mx,
|
1037
|
-
stroke=Color("black"),
|
1038
|
-
stroke_width=self.elements.default_strokewidth,
|
1039
|
-
type="elem ellipse",
|
1040
|
-
)
|
1041
|
-
p = ez.pens[element.pen]
|
1042
|
-
if op_add is None:
|
1043
|
-
op_add = op.add(type="op engrave", **p.__dict__)
|
1044
|
-
op_add.add_reference(node)
|
1045
|
-
elif isinstance(element, EZRect):
|
1046
|
-
m = element.matrix
|
1047
|
-
mx = Matrix(m[0], m[1], m[3], m[4], m[6], m[7])
|
1048
|
-
mx *= self.matrix
|
1049
|
-
x0, y0 = element.corner_upper_left
|
1050
|
-
x1, y1 = element.corner_bottom_right
|
1051
|
-
node = elem.add(
|
1052
|
-
x=x0,
|
1053
|
-
y=y0,
|
1054
|
-
width=x1 - x0,
|
1055
|
-
height=y1 - y0,
|
1056
|
-
matrix=mx,
|
1057
|
-
stroke=Color("black"),
|
1058
|
-
stroke_width=self.elements.default_strokewidth,
|
1059
|
-
type="elem rect",
|
1060
|
-
)
|
1061
|
-
p = ez.pens[element.pen]
|
1062
|
-
if op_add is None:
|
1063
|
-
op_add = op.add(type="op engrave", **p.__dict__)
|
1064
|
-
op_add.add_reference(node)
|
1065
|
-
elif isinstance(element, EZTimer):
|
1066
|
-
op.add(type="util wait", wait=element.wait_time / 1000.0)
|
1067
|
-
elif isinstance(element, EZOutput):
|
1068
|
-
mask = 1 << element.output_bit
|
1069
|
-
bits = mask if element.low_to_high else 0
|
1070
|
-
|
1071
|
-
op.add(
|
1072
|
-
type="util output",
|
1073
|
-
output_value=bits,
|
1074
|
-
output_mask=mask,
|
1075
|
-
)
|
1076
|
-
if element.timed_high:
|
1077
|
-
op.add(type="util wait", wait=element.wait_time / 1000.0)
|
1078
|
-
op.add(
|
1079
|
-
type="util output",
|
1080
|
-
output_value=~bits,
|
1081
|
-
output_mask=mask,
|
1082
|
-
)
|
1083
|
-
elif isinstance(element, EZInput):
|
1084
|
-
op.add(
|
1085
|
-
type="util input",
|
1086
|
-
input_message=element.message,
|
1087
|
-
input_value=element.input_port_bits,
|
1088
|
-
input_mask=element.input_port_bits,
|
1089
|
-
)
|
1090
|
-
elif isinstance(element, EZImage):
|
1091
|
-
image = element.image
|
1092
|
-
left, top = self.matrix.point_in_matrix_space(
|
1093
|
-
(
|
1094
|
-
element.position[0] - (element.width / 2.0),
|
1095
|
-
element.position[1] + element.height / 2.0,
|
1096
|
-
)
|
1097
|
-
)
|
1098
|
-
w, h = image.size
|
1099
|
-
unit_width = element.width * UNITS_PER_MM
|
1100
|
-
unit_height = element.height * UNITS_PER_MM
|
1101
|
-
matrix = Matrix.scale(
|
1102
|
-
(unit_width / w),
|
1103
|
-
(unit_height / h),
|
1104
|
-
)
|
1105
|
-
_dpi = int(
|
1106
|
-
round(
|
1107
|
-
(
|
1108
|
-
float((w * UNITS_PER_INCH) / unit_width)
|
1109
|
-
+ float((h * UNITS_PER_INCH) / unit_height)
|
1110
|
-
)
|
1111
|
-
/ 2.0,
|
1112
|
-
)
|
1113
|
-
)
|
1114
|
-
matrix.post_translate(left, top)
|
1115
|
-
node = elem.add(type="elem image", image=image, matrix=matrix, dpi=_dpi)
|
1116
|
-
p = ez.pens[element.pen]
|
1117
|
-
if op_add is None:
|
1118
|
-
op_add = op.add(type="op image", **p.__dict__)
|
1119
|
-
op_add.add_reference(node)
|
1120
|
-
elif isinstance(element, EZVectorFile):
|
1121
|
-
elem = elem.add(type="group", label=element.label)
|
1122
|
-
for child in element:
|
1123
|
-
# (self, ez, element, elem, op)
|
1124
|
-
self.parse(ez, child, elem, op, op_add=op_add, path=path)
|
1125
|
-
elif isinstance(element, EZHatch):
|
1126
|
-
p = dict(ez.pens[element.pen].__dict__)
|
1127
|
-
|
1128
|
-
op_add = op.add(type="op engrave", **p)
|
1129
|
-
if "label" in p:
|
1130
|
-
# Both pen and hatch have a label, we shall use the hatch-label for hatch; pen for op.
|
1131
|
-
del p["label"]
|
1132
|
-
op_add.add(type="effect hatch", **p, label=element.label)
|
1133
|
-
for child in element:
|
1134
|
-
# Operands for the hatch.
|
1135
|
-
self.parse(ez, child, elem, op, op_add=op_add)
|
1136
|
-
|
1137
|
-
op_add = op.add(type="op engrave", **p)
|
1138
|
-
if element.group:
|
1139
|
-
path = Path(stroke="black", transform=self.matrix)
|
1140
|
-
for child in element.group:
|
1141
|
-
# Per-completed hatch elements.
|
1142
|
-
self.parse(ez, child, elem, op, op_add=op_add, path=path)
|
1143
|
-
|
1144
|
-
# All path elements are added, should add it to the tree.
|
1145
|
-
node = elem.add(
|
1146
|
-
type="elem path",
|
1147
|
-
path=path,
|
1148
|
-
stroke_width=self.elements.default_strokewidth,
|
1149
|
-
)
|
1150
|
-
p = ez.pens[element.pen]
|
1151
|
-
if op_add is None:
|
1152
|
-
op_add = op.add(type="op engrave", **p.__dict__)
|
1153
|
-
op_add.add_reference(node)
|
1154
|
-
elif isinstance(element, (EZGroup, EZCombine)):
|
1155
|
-
elem = elem.add(type="group", label=element.label)
|
1156
|
-
# recurse to children
|
1157
|
-
for child in element:
|
1158
|
-
self.parse(ez, child, elem, op, op_add=op_add, path=path)
|
1159
|
-
elif isinstance(element, EZSpiral):
|
1160
|
-
elem = elem.add(type="group", label=element.label)
|
1161
|
-
# recurse to children
|
1162
|
-
for child in element:
|
1163
|
-
self.parse(ez, child, elem, op)
|
1164
|
-
for child in element.group:
|
1165
|
-
self.parse(ez, child, elem, op)
|
1
|
+
"""
|
2
|
+
Parser for .ezd files.
|
3
|
+
|
4
|
+
These are a working type file produced by EZCad2™. They contain several pens and different settings that were used by
|
5
|
+
the program when the file was saved. The vector objects consist of a series of laser-ready commands/shapes which refer
|
6
|
+
to the required pen. Some modification objects like hatch and spiral work like a group containing other sub-elements
|
7
|
+
and also contain the cached curve/path data. The image objects contain a standard 24 bit bitmap image. All elements
|
8
|
+
are coordinated relative to the center of the working area and, it is much more common to be given the center point than
|
9
|
+
a specific corner. Nearly all coordinates are in mm, and denote the deviation from the center point.
|
10
|
+
|
11
|
+
"""
|
12
|
+
import math
|
13
|
+
import struct
|
14
|
+
from io import BytesIO
|
15
|
+
|
16
|
+
from meerk40t.core.exceptions import BadFileError
|
17
|
+
from meerk40t.core.units import UNITS_PER_INCH, UNITS_PER_MM
|
18
|
+
from meerk40t.svgelements import Color, Matrix, Path, Polygon
|
19
|
+
|
20
|
+
|
21
|
+
def plugin(kernel, lifecycle):
|
22
|
+
if lifecycle == "register":
|
23
|
+
kernel.register("load/EZDLoader", EZDLoader)
|
24
|
+
|
25
|
+
|
26
|
+
def _parse_struct(file):
|
27
|
+
"""
|
28
|
+
Parses a generic structure for ezd files. These are a count of objects. Then for each data entry int32le:length
|
29
|
+
followed by data of that length.
|
30
|
+
|
31
|
+
@param file:
|
32
|
+
@return:
|
33
|
+
"""
|
34
|
+
p = list()
|
35
|
+
count = struct.unpack("<i", file.read(4))[0]
|
36
|
+
for i in range(count):
|
37
|
+
b = file.read(4)
|
38
|
+
if len(b) != 4:
|
39
|
+
return p
|
40
|
+
(length,) = struct.unpack("<i", b)
|
41
|
+
if length == -1:
|
42
|
+
return p
|
43
|
+
b = file.read(length)
|
44
|
+
if len(b) != length:
|
45
|
+
return p
|
46
|
+
p.append(b)
|
47
|
+
return p
|
48
|
+
|
49
|
+
|
50
|
+
def _interpret(data, index, type):
|
51
|
+
"""
|
52
|
+
Provide a specific hint for how to interpret a chunk of bytes. There are cases where 16 bytes could be a point,
|
53
|
+
consisting of two floating points, but could also be a string. This is used to force the typing to use the correct
|
54
|
+
method.
|
55
|
+
|
56
|
+
@param data:
|
57
|
+
@param index:
|
58
|
+
@param type:
|
59
|
+
@return:
|
60
|
+
"""
|
61
|
+
if type == str:
|
62
|
+
data[index] = data[index].decode("utf_16").strip("\x00")
|
63
|
+
elif type == "point":
|
64
|
+
data[index] = struct.unpack("2d", data[index])
|
65
|
+
elif type == "short":
|
66
|
+
(data[index],) = struct.unpack("<H", data[index])
|
67
|
+
elif type == int:
|
68
|
+
(data[index],) = struct.unpack("<i", data[index])
|
69
|
+
elif type == float:
|
70
|
+
(data[index],) = struct.unpack("d", data[index])
|
71
|
+
elif type == "matrix":
|
72
|
+
data[index] = struct.unpack("9d", data[index])
|
73
|
+
|
74
|
+
|
75
|
+
def _construct(data):
|
76
|
+
"""
|
77
|
+
For each element of data (that is a bytes object), interpret them as their most common type.
|
78
|
+
|
79
|
+
@param data:
|
80
|
+
@return:
|
81
|
+
"""
|
82
|
+
for i in range(len(data)):
|
83
|
+
b = data[i]
|
84
|
+
length = len(b)
|
85
|
+
if not isinstance(b, (bytes, bytearray)):
|
86
|
+
continue
|
87
|
+
if length == 2:
|
88
|
+
_interpret(data, i, "short")
|
89
|
+
elif length == 4:
|
90
|
+
_interpret(data, i, int)
|
91
|
+
elif length == 8:
|
92
|
+
_interpret(data, i, float)
|
93
|
+
elif length == 16:
|
94
|
+
_interpret(data, i, "point")
|
95
|
+
elif length == 60:
|
96
|
+
_interpret(data, i, str)
|
97
|
+
elif length == 72:
|
98
|
+
_interpret(data, i, "matrix")
|
99
|
+
elif length == 0:
|
100
|
+
data[i] = None
|
101
|
+
return data
|
102
|
+
|
103
|
+
|
104
|
+
def _huffman_decode_python(file, uncompressed_length):
|
105
|
+
"""
|
106
|
+
Python fallback for huffman decoding of the vector table.
|
107
|
+
|
108
|
+
@param file:
|
109
|
+
@param uncompressed_length:
|
110
|
+
@return:
|
111
|
+
"""
|
112
|
+
huffman_dict = {}
|
113
|
+
table_length = struct.unpack("<H", file.read(2))[0]
|
114
|
+
for i in range(table_length):
|
115
|
+
character, bb, length = struct.unpack("<BIH", file.read(7))
|
116
|
+
bits = "{:032b}".format(bb)[-length:]
|
117
|
+
huffman_dict[bits] = character
|
118
|
+
data = file.read()
|
119
|
+
|
120
|
+
def bit_generator():
|
121
|
+
for d in data:
|
122
|
+
yield from "{:08b}".format(d)
|
123
|
+
|
124
|
+
q = bytearray()
|
125
|
+
c = ""
|
126
|
+
for b in bit_generator():
|
127
|
+
c += b
|
128
|
+
m = huffman_dict.get(c)
|
129
|
+
if m is not None:
|
130
|
+
q.append(m)
|
131
|
+
c = ""
|
132
|
+
if len(q) >= uncompressed_length:
|
133
|
+
return q
|
134
|
+
return q
|
135
|
+
|
136
|
+
|
137
|
+
def _huffman_decode_bitarray(file, uncompressed_length):
|
138
|
+
"""
|
139
|
+
Bitarray decoding of huffman table found in the vector table section.
|
140
|
+
|
141
|
+
@param file:
|
142
|
+
@param uncompressed_length:
|
143
|
+
@return:
|
144
|
+
"""
|
145
|
+
from bitarray import bitarray
|
146
|
+
|
147
|
+
huffman_dict = {}
|
148
|
+
table_length = struct.unpack("<H", file.read(2))[0]
|
149
|
+
for i in range(table_length):
|
150
|
+
character, bb, length = struct.unpack("<BIH", file.read(7))
|
151
|
+
bits = bitarray("{:032b}".format(bb)[-length:])
|
152
|
+
huffman_dict[character] = bits
|
153
|
+
a = bitarray()
|
154
|
+
a.frombytes(file.read())
|
155
|
+
while True:
|
156
|
+
try:
|
157
|
+
return bytearray(a.decode(huffman_dict))
|
158
|
+
except ValueError:
|
159
|
+
a = a[:-1]
|
160
|
+
|
161
|
+
|
162
|
+
class Pen:
|
163
|
+
def __init__(self, file):
|
164
|
+
"""
|
165
|
+
Parse pen with the given file.
|
166
|
+
"""
|
167
|
+
args = _parse_struct(file)
|
168
|
+
_interpret(args, 1, str)
|
169
|
+
_construct(args)
|
170
|
+
|
171
|
+
self.color = Color(bgr=args[0])
|
172
|
+
self.label = args[1]
|
173
|
+
self.mark_enable = args[2]
|
174
|
+
self.passes = args[4] # Loop Count
|
175
|
+
if self.passes >= 1:
|
176
|
+
self.passes_custom = True
|
177
|
+
self.speed = args[5]
|
178
|
+
self.power = args[6] * 10.0
|
179
|
+
self.frequency = args[7] / 1000.0
|
180
|
+
self.start_tc = args[9]
|
181
|
+
self.end_tc = args[10]
|
182
|
+
self.polygon_tc = args[11]
|
183
|
+
self.jump_speed = args[12]
|
184
|
+
self.jump_min_delay = args[13]
|
185
|
+
self.jump_max_delay = args[14]
|
186
|
+
self.opt_start_length = args[16]
|
187
|
+
self.opt_end_length = args[15]
|
188
|
+
self.time_per_point = args[17]
|
189
|
+
self.pulse_per_point = args[21]
|
190
|
+
self.laser_off_tc = args[23]
|
191
|
+
self.wobble_enable = args[26]
|
192
|
+
self.wobble_diameter = args[27]
|
193
|
+
self.wobble_distance = args[28]
|
194
|
+
|
195
|
+
try:
|
196
|
+
self.add_endpoints = args[29]
|
197
|
+
self.add_endpoint_distance = args[30]
|
198
|
+
self.add_endpoint_time_per_point = args[32]
|
199
|
+
self.add_endpoint_point_distance = args[31]
|
200
|
+
self.add_endpoints_point_cycles = args[33]
|
201
|
+
self.opt_enable = args[40]
|
202
|
+
self.break_angle = args[41]
|
203
|
+
|
204
|
+
self.jump_min_jump_delay2 = args[37]
|
205
|
+
self.jump_max_delay2 = args[38]
|
206
|
+
self.jump_speed_max_limit = args[39]
|
207
|
+
except IndexError:
|
208
|
+
pass
|
209
|
+
|
210
|
+
|
211
|
+
class EZCFile:
|
212
|
+
"""
|
213
|
+
Parse the EZCFile given file as a stream.
|
214
|
+
"""
|
215
|
+
|
216
|
+
def __init__(self, file):
|
217
|
+
self._locations = {}
|
218
|
+
self.pens = []
|
219
|
+
self.objects = []
|
220
|
+
self.fonts = []
|
221
|
+
self._preview_bitmap = list()
|
222
|
+
self._prevector = None
|
223
|
+
self.parse_header(file)
|
224
|
+
self.parse_seektable(file)
|
225
|
+
self.parse_unknown_nontable(file)
|
226
|
+
self.parse_tables(file)
|
227
|
+
|
228
|
+
def parse_header(self, file):
|
229
|
+
"""
|
230
|
+
Parse file header.
|
231
|
+
|
232
|
+
@param file:
|
233
|
+
@return:
|
234
|
+
"""
|
235
|
+
magic_number = file.read(16)
|
236
|
+
header = magic_number.decode("utf_16")
|
237
|
+
if header != "EZCADUNI":
|
238
|
+
return False
|
239
|
+
v0 = struct.unpack("<i", file.read(4)) # 0
|
240
|
+
v1 = struct.unpack("<i", file.read(4)) # 2001
|
241
|
+
s1 = file.read(60)
|
242
|
+
s1 = s1.decode("utf-16")
|
243
|
+
s2 = file.read(60)
|
244
|
+
s2 = s2.decode("utf-16")
|
245
|
+
s3 = file.read(60)
|
246
|
+
s3 = s3.decode("utf-16")
|
247
|
+
s4 = file.read(140)
|
248
|
+
|
249
|
+
def parse_seektable(self, file):
|
250
|
+
"""
|
251
|
+
The second item in the file after the header is the seek table lookup. This provides the location in absolute
|
252
|
+
position in the file of the table locations.
|
253
|
+
@param file:
|
254
|
+
@return:
|
255
|
+
"""
|
256
|
+
self._locations["preview"] = struct.unpack("<i", file.read(4))[0]
|
257
|
+
self._locations["v1"] = struct.unpack("<i", file.read(4))[0]
|
258
|
+
self._locations["pens"] = struct.unpack("<i", file.read(4))[0]
|
259
|
+
self._locations["font"] = struct.unpack("<i", file.read(4))[0]
|
260
|
+
self._locations["v4"] = struct.unpack("<i", file.read(4))[0]
|
261
|
+
self._locations["vectors"] = struct.unpack("<i", file.read(4))[0]
|
262
|
+
self._locations["prevectors"] = struct.unpack("<i", file.read(4))[0]
|
263
|
+
|
264
|
+
def parse_unknown_nontable(self, file):
|
265
|
+
"""
|
266
|
+
This is a non-table section. It could be padding for future seek tables entries or have some unknown meaning.
|
267
|
+
|
268
|
+
@param file:
|
269
|
+
@return:
|
270
|
+
"""
|
271
|
+
unknown_table = struct.unpack("<24I", file.read(96))
|
272
|
+
|
273
|
+
def parse_tables(self, file):
|
274
|
+
"""
|
275
|
+
Parses all the different tables found in the file.
|
276
|
+
|
277
|
+
@param file:
|
278
|
+
@return:
|
279
|
+
"""
|
280
|
+
self.parse_preview(file)
|
281
|
+
self.parse_v1(file)
|
282
|
+
self.parse_pens(file)
|
283
|
+
self.parse_font(file)
|
284
|
+
self.parse_v4(file)
|
285
|
+
self.parse_vectors(file)
|
286
|
+
self.parse_prevectors(file)
|
287
|
+
|
288
|
+
def parse_v1(self, file):
|
289
|
+
"""
|
290
|
+
Unknown table location. Usually absent.
|
291
|
+
|
292
|
+
@param file:
|
293
|
+
@return:
|
294
|
+
"""
|
295
|
+
seek = self._locations.get("v1", 0)
|
296
|
+
if seek == 0:
|
297
|
+
return
|
298
|
+
file.seek(seek, 0)
|
299
|
+
|
300
|
+
def parse_v4(self, file):
|
301
|
+
"""
|
302
|
+
Unknown table location usually contains 96 bytes.
|
303
|
+
|
304
|
+
@param file:
|
305
|
+
@return:
|
306
|
+
"""
|
307
|
+
seek = self._locations.get("v4", 0)
|
308
|
+
if seek == 0:
|
309
|
+
return
|
310
|
+
file.seek(seek, 0)
|
311
|
+
unknown_table = struct.unpack("<24I", file.read(96))
|
312
|
+
|
313
|
+
def parse_preview(self, file):
|
314
|
+
"""
|
315
|
+
Contains a preview image of the file.
|
316
|
+
|
317
|
+
@param file:
|
318
|
+
@return:
|
319
|
+
"""
|
320
|
+
seek = self._locations.get("preview", 0)
|
321
|
+
if seek == 0:
|
322
|
+
return
|
323
|
+
file.seek(seek, 0)
|
324
|
+
unknown = struct.unpack("<i", file.read(4))[0]
|
325
|
+
width = struct.unpack("<i", file.read(4))[0]
|
326
|
+
height = struct.unpack("<i", file.read(4))[0]
|
327
|
+
v3 = struct.unpack("<3i", file.read(12))
|
328
|
+
# 800, 0x200002
|
329
|
+
|
330
|
+
# RGB0
|
331
|
+
self._preview_bitmap.extend(
|
332
|
+
struct.unpack(f"<{int(width*height)}I", file.read(4 * width * height))
|
333
|
+
)
|
334
|
+
|
335
|
+
def parse_font(self, file):
|
336
|
+
"""
|
337
|
+
Font table. This usually consists of "Arial" with no other data and only exists if a font is used.
|
338
|
+
|
339
|
+
@param file:
|
340
|
+
@return:
|
341
|
+
"""
|
342
|
+
seek = self._locations.get("font", 0)
|
343
|
+
if seek == 0:
|
344
|
+
return
|
345
|
+
file.seek(seek, 0)
|
346
|
+
font_count = struct.unpack("<i", file.read(4))[0]
|
347
|
+
|
348
|
+
for i in range(font_count):
|
349
|
+
f = file.read(100)
|
350
|
+
self.fonts.append(f.decode("utf_16").strip("\x00"))
|
351
|
+
|
352
|
+
def parse_pens(self, file):
|
353
|
+
"""
|
354
|
+
Contains all the pens used at the time of the saving of the file. This is 256 pens.
|
355
|
+
|
356
|
+
@param file:
|
357
|
+
@return:
|
358
|
+
"""
|
359
|
+
seek = self._locations.get("pens", 0)
|
360
|
+
if seek == 0:
|
361
|
+
return
|
362
|
+
file.seek(seek, 0)
|
363
|
+
|
364
|
+
parameter_count = struct.unpack("<i", file.read(4))[0]
|
365
|
+
seek = struct.unpack("<i", file.read(4))[0]
|
366
|
+
file.seek(seek, 0)
|
367
|
+
for c in range(parameter_count):
|
368
|
+
self.pens.append(Pen(file))
|
369
|
+
|
370
|
+
def parse_prevectors(self, file):
|
371
|
+
"""
|
372
|
+
Pre-vectors are usually 400 bytes with no values.
|
373
|
+
|
374
|
+
@param file:
|
375
|
+
@return:
|
376
|
+
"""
|
377
|
+
seek = self._locations.get("prevectors", 0)
|
378
|
+
if seek == 0:
|
379
|
+
return
|
380
|
+
file.seek(seek, 0)
|
381
|
+
|
382
|
+
# 400 bytes of 00, 100 bytes of int
|
383
|
+
self._prevector = struct.unpack("<400B", file.read(400))
|
384
|
+
|
385
|
+
def parse_vectors(self, file):
|
386
|
+
"""
|
387
|
+
Vectors contain the bulk of the files. This is a compressed file of huffman encoded data. The first section
|
388
|
+
contains the huffman table, followed by the compressed data.
|
389
|
+
|
390
|
+
@param file:
|
391
|
+
@return:
|
392
|
+
"""
|
393
|
+
seek = self._locations.get("vectors", 0)
|
394
|
+
if seek == 0:
|
395
|
+
return
|
396
|
+
file.seek(seek, 0)
|
397
|
+
uncompressed_length, unknown2, unknown3, data_start, unknown5 = struct.unpack(
|
398
|
+
"<IIIII", file.read(20)
|
399
|
+
)
|
400
|
+
try:
|
401
|
+
q = _huffman_decode_bitarray(file, uncompressed_length)
|
402
|
+
except ImportError:
|
403
|
+
q = _huffman_decode_python(file, uncompressed_length)
|
404
|
+
data = BytesIO(q)
|
405
|
+
while parse_object(data, self.objects):
|
406
|
+
pass
|
407
|
+
|
408
|
+
|
409
|
+
class EZObject:
|
410
|
+
"""
|
411
|
+
Every object contains the same 15 pieces of data.
|
412
|
+
If this object type contains children, the count of children and the children are given exactly following the
|
413
|
+
header. Any information specific to the class of object is read after the header and children.
|
414
|
+
"""
|
415
|
+
|
416
|
+
def __init__(self, file):
|
417
|
+
header = _parse_struct(file)
|
418
|
+
_interpret(header, 3, str)
|
419
|
+
_construct(header)
|
420
|
+
|
421
|
+
self.pen = header[0]
|
422
|
+
self.type = header[1]
|
423
|
+
self.state = header[2]
|
424
|
+
|
425
|
+
# Selected 0x02, Hidden 0x01, Locked 0x10
|
426
|
+
self.selected = bool(self.state & 0x02)
|
427
|
+
self.hidden = bool(self.state & 0x01)
|
428
|
+
self.locked = bool(self.state & 0x10)
|
429
|
+
|
430
|
+
self.label = header[3]
|
431
|
+
self.unknown2 = header[4]
|
432
|
+
self.unknown3 = header[5]
|
433
|
+
self.unknown4 = header[6]
|
434
|
+
self.input_port_bits = header[7]
|
435
|
+
self.array_state = header[8]
|
436
|
+
self.array_bidirectional = bool(self.array_state & 0x2)
|
437
|
+
self.array_vertical = bool(self.array_state & 0x1)
|
438
|
+
self.array_count_x = header[9]
|
439
|
+
self.array_count_y = header[10]
|
440
|
+
self.array_step_x = header[11]
|
441
|
+
self.array_step_y = header[12]
|
442
|
+
self.position = header[13]
|
443
|
+
self.z_pos = header[14]
|
444
|
+
if isinstance(self, list):
|
445
|
+
(count,) = struct.unpack("<i", file.read(4))
|
446
|
+
for c in range(count):
|
447
|
+
parse_object(file, self)
|
448
|
+
|
449
|
+
|
450
|
+
class EZCombine(list, EZObject):
|
451
|
+
"""
|
452
|
+
This is a series of related contours.
|
453
|
+
"""
|
454
|
+
|
455
|
+
def __init__(self, file):
|
456
|
+
list.__init__(self)
|
457
|
+
EZObject.__init__(self, file)
|
458
|
+
|
459
|
+
|
460
|
+
class EZGroup(list, EZObject):
|
461
|
+
"""
|
462
|
+
Grouped data appears both when objects are grouped but also in groups within vector file objects like svgs.
|
463
|
+
"""
|
464
|
+
|
465
|
+
def __init__(self, file):
|
466
|
+
list.__init__(self)
|
467
|
+
EZObject.__init__(self, file)
|
468
|
+
|
469
|
+
|
470
|
+
class EZVectorFile(list, EZObject):
|
471
|
+
"""
|
472
|
+
Vector file object.
|
473
|
+
"""
|
474
|
+
|
475
|
+
def __init__(self, file):
|
476
|
+
list.__init__(self)
|
477
|
+
EZObject.__init__(self, file)
|
478
|
+
data1 = _parse_struct(file)
|
479
|
+
_interpret(data1, 0, str)
|
480
|
+
_construct(data1)
|
481
|
+
|
482
|
+
self.path = data1[0]
|
483
|
+
self.args = data1
|
484
|
+
|
485
|
+
|
486
|
+
class EZCurve(EZObject):
|
487
|
+
"""
|
488
|
+
Curves are some number of curve-type (usually 1 or 3) contours.
|
489
|
+
"""
|
490
|
+
|
491
|
+
def __init__(self, file):
|
492
|
+
super().__init__(file)
|
493
|
+
pts = []
|
494
|
+
(count, closed) = struct.unpack("<2I", file.read(8))
|
495
|
+
for i in range(count):
|
496
|
+
(unk1, curve_type, unk2, unk3) = struct.unpack("<BB2H", file.read(6))
|
497
|
+
# Unk1 is 2 for a weird node. with t equal 0.
|
498
|
+
if curve_type == 0:
|
499
|
+
d = struct.unpack(f"<5d", file.read(40))
|
500
|
+
# print(d)
|
501
|
+
continue
|
502
|
+
(pt_count,) = struct.unpack("<i", file.read(4))
|
503
|
+
# print(unk1, curve_type, unk2, unk2, pt_count)
|
504
|
+
pts.append(
|
505
|
+
(
|
506
|
+
curve_type,
|
507
|
+
closed,
|
508
|
+
struct.unpack(f"<{pt_count * 2}d", file.read(16 * pt_count)),
|
509
|
+
)
|
510
|
+
)
|
511
|
+
|
512
|
+
self.points = pts
|
513
|
+
|
514
|
+
|
515
|
+
class EZRect(EZObject):
|
516
|
+
"""
|
517
|
+
Rectangles have optional each corner curved edges.
|
518
|
+
"""
|
519
|
+
|
520
|
+
def __init__(self, file):
|
521
|
+
EZObject.__init__(self, file)
|
522
|
+
args = _parse_struct(file)
|
523
|
+
_construct(args)
|
524
|
+
self.min_pos = args[0]
|
525
|
+
self.max_pos = args[1]
|
526
|
+
self.corner_upper_left = args[0]
|
527
|
+
self.corner_bottom_right = args[1]
|
528
|
+
self.round_c1 = args[2]
|
529
|
+
self.round_c2 = args[3]
|
530
|
+
self.round_c3 = args[4]
|
531
|
+
self.round_c4 = args[5]
|
532
|
+
self.unknown5 = args[6]
|
533
|
+
self.matrix = args[7]
|
534
|
+
|
535
|
+
|
536
|
+
class EZCircle(EZObject):
|
537
|
+
"""
|
538
|
+
Circles are center followed by their radius. The angles are given in radians.
|
539
|
+
"""
|
540
|
+
|
541
|
+
def __init__(self, file):
|
542
|
+
EZObject.__init__(self, file)
|
543
|
+
args = _parse_struct(file)
|
544
|
+
_construct(args)
|
545
|
+
self.center = args[0]
|
546
|
+
self.radius = args[1]
|
547
|
+
self.start_angle = args[2]
|
548
|
+
self.cw = args[3]
|
549
|
+
self.circle_prop0 = args[4]
|
550
|
+
self.matrix = args[5]
|
551
|
+
|
552
|
+
|
553
|
+
class EZEllipse(EZObject):
|
554
|
+
"""
|
555
|
+
Ellipses are a rectangle like structures, the start and end angles create a pie-slice like geometric shape when
|
556
|
+
these are set.
|
557
|
+
"""
|
558
|
+
|
559
|
+
def __init__(self, file):
|
560
|
+
EZObject.__init__(self, file)
|
561
|
+
args = _parse_struct(file)
|
562
|
+
_construct(args)
|
563
|
+
self.corner_upper_left = args[1]
|
564
|
+
self.corner_bottom_right = args[2]
|
565
|
+
self.start_angle = args[3]
|
566
|
+
self.end_angle = args[4]
|
567
|
+
self.matrix = args[6]
|
568
|
+
|
569
|
+
|
570
|
+
class EZSpiral(list, EZObject):
|
571
|
+
"""
|
572
|
+
Spirals are a modification group of the items contained by the spiral. These also contain a cached-group of the
|
573
|
+
output produced by the spiral.
|
574
|
+
"""
|
575
|
+
|
576
|
+
def __init__(self, file):
|
577
|
+
list.__init__(self)
|
578
|
+
EZObject.__init__(self, file)
|
579
|
+
args = _parse_struct(file)
|
580
|
+
_construct(args)
|
581
|
+
self.spiral_pen = args[0]
|
582
|
+
self.spiral_type = args[1]
|
583
|
+
self.min_radius = args[5]
|
584
|
+
self.min_spiral_pitch = args[2]
|
585
|
+
self.max_spiral_pitch = args[3]
|
586
|
+
self.max_spiral_increment = args[4]
|
587
|
+
self.outer_edge_loops = args[6]
|
588
|
+
self.inner_edge_loops = args[7]
|
589
|
+
self.spiral_out = args[8]
|
590
|
+
self.group = EZGroup(file)
|
591
|
+
|
592
|
+
|
593
|
+
class EZPolygon(EZObject):
|
594
|
+
"""
|
595
|
+
Polygons are either regular or star-like. No control is given over the minor or major phase.
|
596
|
+
"""
|
597
|
+
|
598
|
+
def __init__(self, file):
|
599
|
+
EZObject.__init__(self, file)
|
600
|
+
args = _parse_struct(file)
|
601
|
+
_construct(args)
|
602
|
+
self.polygon_type = args[0]
|
603
|
+
self.corner_upper_left = args[1]
|
604
|
+
self.corner_bottom_right = args[2]
|
605
|
+
self.sides = args[7]
|
606
|
+
self.matrix = args[9]
|
607
|
+
|
608
|
+
|
609
|
+
class EZTimer(EZObject):
|
610
|
+
"""
|
611
|
+
Timers are wait commands. These are given a time and simply send the wait command to the laser.
|
612
|
+
"""
|
613
|
+
|
614
|
+
def __init__(self, file):
|
615
|
+
EZObject.__init__(self, file)
|
616
|
+
args = _parse_struct(file)
|
617
|
+
_construct(args)
|
618
|
+
self.wait_time = args[1]
|
619
|
+
|
620
|
+
|
621
|
+
class EZInput(EZObject):
|
622
|
+
"""
|
623
|
+
Input commands wait on the IO of the laser to trigger to the next item within the operations list.
|
624
|
+
"""
|
625
|
+
|
626
|
+
def __init__(self, file):
|
627
|
+
EZObject.__init__(self, file)
|
628
|
+
args = _parse_struct(file)
|
629
|
+
_interpret(args, 1, str)
|
630
|
+
_construct(args)
|
631
|
+
self.message_enabled = bool(args[0])
|
632
|
+
self.message = args[1]
|
633
|
+
|
634
|
+
|
635
|
+
class EZOutput(EZObject):
|
636
|
+
"""
|
637
|
+
Output list sends IO out to the laser, this is used to trigger things like rotary, GPIO, or light.
|
638
|
+
"""
|
639
|
+
|
640
|
+
def __init__(self, file):
|
641
|
+
EZObject.__init__(self, file)
|
642
|
+
args = _parse_struct(file)
|
643
|
+
_construct(args)
|
644
|
+
self.output_bit = args[0]
|
645
|
+
self.low_to_high = bool(args[1]) # 1
|
646
|
+
self.timed_high = bool(args[2]) # 0
|
647
|
+
self.wait_time = args[4] # args[18] is int value
|
648
|
+
self.all_out_mode = bool(args[5])
|
649
|
+
self.all_out_bits = args[6]
|
650
|
+
|
651
|
+
|
652
|
+
class EZEncoderDistance(EZObject):
|
653
|
+
"""
|
654
|
+
This is for testing on-the-fly movement.
|
655
|
+
"""
|
656
|
+
|
657
|
+
def __init__(self, file):
|
658
|
+
EZObject.__init__(self, file)
|
659
|
+
args = _parse_struct(file)
|
660
|
+
_construct(args)
|
661
|
+
self.distance = args[0]
|
662
|
+
|
663
|
+
|
664
|
+
class EZExtendAxis(EZObject):
|
665
|
+
"""
|
666
|
+
This is for testing on-the-fly movement.
|
667
|
+
"""
|
668
|
+
|
669
|
+
def __init__(self, file):
|
670
|
+
EZObject.__init__(self, file)
|
671
|
+
args = _parse_struct(file)
|
672
|
+
_construct(args)
|
673
|
+
self.axis_go_zero = bool(args[0])
|
674
|
+
self.only_once_origin = bool(args[1])
|
675
|
+
self.relative = bool(args[2])
|
676
|
+
self.unit_type = args[3] # Pulse (0), MM (1), Degree(2).
|
677
|
+
self.pulse_per_mm = args[4]
|
678
|
+
self.move_pulse = args[5]
|
679
|
+
self.max_speed = args[6]
|
680
|
+
self.min_speed = args[7]
|
681
|
+
self.acceleration_time = args[8]
|
682
|
+
|
683
|
+
|
684
|
+
class EZText(EZObject):
|
685
|
+
"""
|
686
|
+
Text objects.
|
687
|
+
"""
|
688
|
+
|
689
|
+
def __init__(self, file):
|
690
|
+
EZObject.__init__(self, file)
|
691
|
+
args = _parse_struct(file)
|
692
|
+
_interpret(args, 10, str)
|
693
|
+
_interpret(args, 18, str)
|
694
|
+
_interpret(args, 44, str)
|
695
|
+
_interpret(args, 54, str)
|
696
|
+
_construct(args)
|
697
|
+
self.font_angle = args[0] # Font angle in Text.
|
698
|
+
self.height = args[1] # Height in MM
|
699
|
+
self.text_space_setting = args[5] # 0 auto, 1 between, 2 center
|
700
|
+
self.text_space = args[12]
|
701
|
+
self.char_space = args[13]
|
702
|
+
self.line_space = args[14]
|
703
|
+
self.font = args[18] # Arial, JSF Font, etc
|
704
|
+
self.font2 = args[44]
|
705
|
+
self.x, self.y = args[7]
|
706
|
+
self.text = args[10]
|
707
|
+
self.hatch_loop_distance = args[21]
|
708
|
+
self.circle_text_enable = args[48]
|
709
|
+
self.circle_text_diameter = args[49]
|
710
|
+
self.circle_text_base_angle = args[50]
|
711
|
+
self.circle_text_range_limit_enable = args[51]
|
712
|
+
self.circle_text_range_limit_angle = args[52]
|
713
|
+
self.save_options = args[53] # 3 boolean values
|
714
|
+
self.save_filename = args[54]
|
715
|
+
self.circle_text_button_flags = args[
|
716
|
+
85
|
717
|
+
] # 2 is first button, 1 is right to left.
|
718
|
+
(count,) = struct.unpack("<i", file.read(4))
|
719
|
+
for i in range(count):
|
720
|
+
(_type,) = struct.unpack("<H", file.read(2))
|
721
|
+
# type, 7 file. 1 Text. 2 Serial
|
722
|
+
extradata = _parse_struct(file)
|
723
|
+
_construct(extradata)
|
724
|
+
extradata2 = _parse_struct(file)
|
725
|
+
_construct(extradata2)
|
726
|
+
(unk,) = struct.unpack("<i", file.read(4))
|
727
|
+
|
728
|
+
|
729
|
+
class EZImage(EZObject):
|
730
|
+
"""
|
731
|
+
Image objects consist of a lot of properties to control the encoding of the image and a 24-bit bitmap.
|
732
|
+
"""
|
733
|
+
|
734
|
+
def __init__(self, file):
|
735
|
+
EZObject.__init__(self, file)
|
736
|
+
args = _parse_struct(file)
|
737
|
+
_construct(args)
|
738
|
+
|
739
|
+
image_bytes = bytearray(file.read(2)) # BM
|
740
|
+
image_length = file.read(4) # int32le
|
741
|
+
(size,) = struct.unpack("<i", image_length)
|
742
|
+
image_bytes += image_length
|
743
|
+
image_bytes += file.read(size - 6)
|
744
|
+
|
745
|
+
from PIL import Image
|
746
|
+
|
747
|
+
image = Image.open(BytesIO(image_bytes))
|
748
|
+
|
749
|
+
self.image_path = args[0]
|
750
|
+
self.width = args[5]
|
751
|
+
self.height = args[4]
|
752
|
+
self.fixed_dpi_x = args[9]
|
753
|
+
self.fixed_dpi_y = args[333 - 15]
|
754
|
+
self.image = image
|
755
|
+
self.powermap = args[74 - 15 : 330 - 15]
|
756
|
+
self.scan_line_increment = args[29 - 15]
|
757
|
+
self.scan_line_increment_value = args[30 - 15]
|
758
|
+
self.disable_mark_low_gray_point = args[31 - 15]
|
759
|
+
self.disable_mark_low_gray_point_value = args[32 - 15]
|
760
|
+
self.acc_distance_mm = args[331 - 15]
|
761
|
+
self.dec_distance_mm = args[332 - 15]
|
762
|
+
self.all_offset_mm = args[334 - 15]
|
763
|
+
self.bidirectional_offset = args[330 - 15]
|
764
|
+
self.status_bits = args[25 - 15]
|
765
|
+
self.mirror_x = bool(self.status_bits & 0x20)
|
766
|
+
self.mirror_y = bool(self.status_bits & 0x40)
|
767
|
+
|
768
|
+
|
769
|
+
class EZHatch(list, EZObject):
|
770
|
+
"""
|
771
|
+
Hatch is a modification group. All three hatch elements are given properties for each hatch. The hatch contains
|
772
|
+
the actual elements that were to be given a hatch. As well as a cache-group of curve items that actually are the
|
773
|
+
given hatch properly rendered.
|
774
|
+
"""
|
775
|
+
|
776
|
+
def __init__(self, file):
|
777
|
+
list.__init__(self)
|
778
|
+
EZObject.__init__(self, file)
|
779
|
+
args = _parse_struct(file)
|
780
|
+
_construct(args)
|
781
|
+
self.mark_contours = args[0]
|
782
|
+
self.mark_contours_type = args[41]
|
783
|
+
|
784
|
+
self.hatch1_enabled = args[1]
|
785
|
+
self.hatch1_type = args[3]
|
786
|
+
# Includes average distribute line, allcalc, follow edge, crosshatch
|
787
|
+
# spiral = 0x50
|
788
|
+
self.hatch1_type_all_calc = self.hatch1_type & 0x1
|
789
|
+
self.hatch1_type_follow_edge = self.hatch1_type & 0x2
|
790
|
+
self.hatch1_type_crosshatch = self.hatch1_type & 0x400
|
791
|
+
self.hatch1_angle = args[8]
|
792
|
+
self.hatch1_pen = args[2]
|
793
|
+
self.hatch1_line_space = args[5]
|
794
|
+
self.hatch1_edge_offset = args[4]
|
795
|
+
self.hatch1_start_offset = args[6]
|
796
|
+
self.hatch1_end_offset = args[7]
|
797
|
+
self.hatch1_line_reduction = args[29]
|
798
|
+
self.hatch1_number_of_loops = args[32]
|
799
|
+
self.hatch1_loop_distance = args[35]
|
800
|
+
self.hatch1_angle_inc = args[18]
|
801
|
+
|
802
|
+
self.hatch2_enabled = args[9]
|
803
|
+
self.hatch2_type = args[11]
|
804
|
+
self.hatch2_angle = args[16]
|
805
|
+
self.hatch2_pen = args[10]
|
806
|
+
self.hatch2_line_space = args[13]
|
807
|
+
self.hatch2_edge_offset = args[12]
|
808
|
+
self.hatch2_start_offset = args[14]
|
809
|
+
self.hatch2_end_offset = args[15]
|
810
|
+
self.hatch2_line_reduction = args[30]
|
811
|
+
self.hatch2_number_of_loops = args[33]
|
812
|
+
self.hatch2_loop_distance = args[36]
|
813
|
+
self.hatch2_angle_inc = args[19]
|
814
|
+
|
815
|
+
self.hatch3_enabled = args[20]
|
816
|
+
self.hatch3_type = args[22]
|
817
|
+
self.hatch3_angle = args[27]
|
818
|
+
self.hatch3_pen = args[21]
|
819
|
+
self.hatch3_line_space = args[24]
|
820
|
+
self.hatch3_edge_offset = args[23]
|
821
|
+
self.hatch3_start_offset = args[25]
|
822
|
+
self.hatch3_end_offset = args[26]
|
823
|
+
self.hatch3_line_reduction = args[31]
|
824
|
+
self.hatch3_number_of_loops = args[34]
|
825
|
+
self.hatch3_loop_distance = args[37]
|
826
|
+
self.hatch3_angle_inc = args[28]
|
827
|
+
try:
|
828
|
+
self.hatch1_count = args[42]
|
829
|
+
self.hatch2_count = args[43]
|
830
|
+
self.hatch3_count = args[44]
|
831
|
+
except IndexError:
|
832
|
+
# Older Version without count values.
|
833
|
+
pass
|
834
|
+
tell = file.tell()
|
835
|
+
(check,) = struct.unpack("<i", file.read(4))
|
836
|
+
file.seek(tell, 0)
|
837
|
+
if check == 15:
|
838
|
+
self.group = EZGroup(file)
|
839
|
+
else:
|
840
|
+
self.group = None
|
841
|
+
|
842
|
+
|
843
|
+
object_map = {
|
844
|
+
1: EZCurve,
|
845
|
+
3: EZRect,
|
846
|
+
4: EZCircle,
|
847
|
+
5: EZEllipse,
|
848
|
+
6: EZPolygon,
|
849
|
+
0x30: EZCombine,
|
850
|
+
0x40: EZImage,
|
851
|
+
0x60: EZSpiral,
|
852
|
+
0x6000: EZEncoderDistance,
|
853
|
+
0x5000: EZExtendAxis,
|
854
|
+
0x4000: EZOutput,
|
855
|
+
0x3000: EZInput,
|
856
|
+
0x2000: EZTimer,
|
857
|
+
0x800: EZText,
|
858
|
+
0x10: EZGroup,
|
859
|
+
0x50: EZVectorFile,
|
860
|
+
0x20: EZHatch,
|
861
|
+
}
|
862
|
+
|
863
|
+
|
864
|
+
def parse_object(file, objects):
|
865
|
+
object_type = struct.unpack("<i", file.read(4))[0] # 0
|
866
|
+
if object_type == 0:
|
867
|
+
return False
|
868
|
+
ez_class = object_map.get(object_type)
|
869
|
+
assert ez_class
|
870
|
+
objects.append(ez_class(file))
|
871
|
+
return True
|
872
|
+
|
873
|
+
|
874
|
+
class EZDLoader:
|
875
|
+
@staticmethod
|
876
|
+
def load_types():
|
877
|
+
yield "EZCad2 Files", ("ezd",), "application/x-ezd"
|
878
|
+
|
879
|
+
@staticmethod
|
880
|
+
def load(context, elements_service, pathname, **kwargs):
|
881
|
+
try:
|
882
|
+
with open(pathname, "br") as file:
|
883
|
+
ezfile = EZCFile(file)
|
884
|
+
except (IOError, IndexError) as e:
|
885
|
+
raise BadFileError(str(e)) from e
|
886
|
+
except struct.error:
|
887
|
+
raise BadFileError(
|
888
|
+
"Unseen sequence, object, or formatting.\n"
|
889
|
+
"File format was only partially unrecognized.\n"
|
890
|
+
"Please raise an github issue and submit this file for review.\n"
|
891
|
+
)
|
892
|
+
elements_service._loading_cleared = True
|
893
|
+
|
894
|
+
ez_processor = EZProcessor(elements_service)
|
895
|
+
ez_processor.process(ezfile, pathname)
|
896
|
+
return True
|
897
|
+
|
898
|
+
|
899
|
+
class EZProcessor:
|
900
|
+
def __init__(self, elements):
|
901
|
+
self.elements = elements
|
902
|
+
self.element_list = list()
|
903
|
+
self.regmark_list = list()
|
904
|
+
self.pathname = None
|
905
|
+
self.regmark = self.elements.reg_branch
|
906
|
+
self.op_branch = elements.op_branch
|
907
|
+
self.elem_branch = elements.elem_branch
|
908
|
+
|
909
|
+
self.width = elements.device.view.unit_width
|
910
|
+
self.height = elements.device.view.unit_height
|
911
|
+
self.cx = self.width / 2.0
|
912
|
+
self.cy = self.height / 2.0
|
913
|
+
self.matrix = Matrix.scale(UNITS_PER_MM, -UNITS_PER_MM)
|
914
|
+
self.matrix.post_translate(self.cx, self.cy)
|
915
|
+
|
916
|
+
def process(self, ez, pathname):
|
917
|
+
self.op_branch.remove_all_children()
|
918
|
+
self.elem_branch.remove_all_children()
|
919
|
+
self.pathname = pathname
|
920
|
+
file_node = self.elem_branch.add(type="file", filepath=pathname)
|
921
|
+
file_node.focus()
|
922
|
+
for f in ez.objects:
|
923
|
+
self.parse(ez, f, file_node, self.op_branch)
|
924
|
+
|
925
|
+
def parse(self, ez, element, elem, op, op_add=None, path=None):
|
926
|
+
"""
|
927
|
+
Parse ez structure into MK specific tree structure and objects.
|
928
|
+
|
929
|
+
@param ez: EZFile object
|
930
|
+
@param element: ezobject being parsed.
|
931
|
+
@param elem: element context.
|
932
|
+
@param op: operation context
|
933
|
+
@param op_add: Operation we should add to rather than create.
|
934
|
+
@param path: Path we should append to rather than create.
|
935
|
+
@return:
|
936
|
+
"""
|
937
|
+
if isinstance(element, EZText):
|
938
|
+
node = elem.add(type="elem text", text=element.text, transform=self.matrix)
|
939
|
+
p = ez.pens[element.pen]
|
940
|
+
if op_add is None:
|
941
|
+
op_add = op.add(type="op engrave", **p.__dict__)
|
942
|
+
op_add.add_reference(node)
|
943
|
+
elif isinstance(element, EZCurve):
|
944
|
+
points = element.points
|
945
|
+
if len(points) == 0:
|
946
|
+
return
|
947
|
+
if path is None:
|
948
|
+
append_path = False
|
949
|
+
path = Path(stroke="black", transform=self.matrix)
|
950
|
+
else:
|
951
|
+
append_path = True
|
952
|
+
|
953
|
+
last_end = None
|
954
|
+
for t, closed, contour in points:
|
955
|
+
cpt = [
|
956
|
+
complex(contour[i], contour[i + 1])
|
957
|
+
for i in range(0, len(contour), 2)
|
958
|
+
]
|
959
|
+
if last_end != cpt[0]:
|
960
|
+
path.move(cpt[0])
|
961
|
+
if t == 1:
|
962
|
+
path.line(*cpt[1:])
|
963
|
+
elif t == 2:
|
964
|
+
path.quad(*cpt[1:])
|
965
|
+
elif t == 3:
|
966
|
+
path.cubic(*cpt[1:])
|
967
|
+
last_end = cpt[-1]
|
968
|
+
if points[-1][1]:
|
969
|
+
# Path is closed.
|
970
|
+
path.closed()
|
971
|
+
if append_path:
|
972
|
+
return
|
973
|
+
node = elem.add(
|
974
|
+
type="elem path",
|
975
|
+
path=path,
|
976
|
+
stroke_width=self.elements.default_strokewidth,
|
977
|
+
)
|
978
|
+
p = ez.pens[element.pen]
|
979
|
+
if op_add is None:
|
980
|
+
op_add = op.add(type="op engrave", **p.__dict__)
|
981
|
+
op_add.add_reference(node)
|
982
|
+
elif isinstance(element, EZPolygon):
|
983
|
+
m = element.matrix
|
984
|
+
mx = Matrix(m[0], m[1], m[3], m[4], m[6], m[7])
|
985
|
+
mx *= self.matrix
|
986
|
+
x0, y0 = element.corner_upper_left
|
987
|
+
x1, y1 = element.corner_bottom_right
|
988
|
+
step = math.tau / element.sides
|
989
|
+
cx, cy = (x0 + x1) / 2.0, (y0 + y1) / 2.0
|
990
|
+
rx = (x1 - x0) / 2.0
|
991
|
+
ry = (y1 - y0) / 2.0
|
992
|
+
pts = []
|
993
|
+
theta = step / 2.0
|
994
|
+
for i in range(element.sides):
|
995
|
+
pts.append((cx + math.cos(theta) * rx, cy + math.sin(theta) * ry))
|
996
|
+
theta += step
|
997
|
+
polyline = Polygon(points=pts, transform=mx, stroke="black")
|
998
|
+
node = elem.add(
|
999
|
+
type="elem polyline",
|
1000
|
+
shape=polyline,
|
1001
|
+
stroke_width=self.elements.default_strokewidth,
|
1002
|
+
)
|
1003
|
+
p = ez.pens[element.pen]
|
1004
|
+
if op_add is None:
|
1005
|
+
op_add = op.add(type="op engrave", **p.__dict__)
|
1006
|
+
op_add.add_reference(node)
|
1007
|
+
elif isinstance(element, EZCircle):
|
1008
|
+
m = element.matrix
|
1009
|
+
mx = Matrix(m[0], m[1], m[3], m[4], m[6], m[7])
|
1010
|
+
mx *= self.matrix
|
1011
|
+
node = elem.add(
|
1012
|
+
cx=element.center[0],
|
1013
|
+
cy=element.center[1],
|
1014
|
+
rx=element.radius,
|
1015
|
+
ry=element.radius,
|
1016
|
+
stroke=Color("black"),
|
1017
|
+
matrix=mx,
|
1018
|
+
stroke_width=self.elements.default_strokewidth,
|
1019
|
+
type="elem ellipse",
|
1020
|
+
)
|
1021
|
+
p = ez.pens[element.pen]
|
1022
|
+
if op_add is None:
|
1023
|
+
op_add = op.add(type="op engrave", **p.__dict__)
|
1024
|
+
op_add.add_reference(node)
|
1025
|
+
elif isinstance(element, EZEllipse):
|
1026
|
+
m = element.matrix
|
1027
|
+
mx = Matrix(m[0], m[1], m[3], m[4], m[6], m[7])
|
1028
|
+
mx *= self.matrix
|
1029
|
+
x0, y0 = element.corner_upper_left
|
1030
|
+
x1, y1 = element.corner_bottom_right
|
1031
|
+
node = elem.add(
|
1032
|
+
cx=(x0 + x1) / 2.0,
|
1033
|
+
cy=(y0 + y1) / 2.0,
|
1034
|
+
rx=(x1 - x0) / 2.0,
|
1035
|
+
ry=(y1 - y0) / 2.0,
|
1036
|
+
matrix=mx,
|
1037
|
+
stroke=Color("black"),
|
1038
|
+
stroke_width=self.elements.default_strokewidth,
|
1039
|
+
type="elem ellipse",
|
1040
|
+
)
|
1041
|
+
p = ez.pens[element.pen]
|
1042
|
+
if op_add is None:
|
1043
|
+
op_add = op.add(type="op engrave", **p.__dict__)
|
1044
|
+
op_add.add_reference(node)
|
1045
|
+
elif isinstance(element, EZRect):
|
1046
|
+
m = element.matrix
|
1047
|
+
mx = Matrix(m[0], m[1], m[3], m[4], m[6], m[7])
|
1048
|
+
mx *= self.matrix
|
1049
|
+
x0, y0 = element.corner_upper_left
|
1050
|
+
x1, y1 = element.corner_bottom_right
|
1051
|
+
node = elem.add(
|
1052
|
+
x=x0,
|
1053
|
+
y=y0,
|
1054
|
+
width=x1 - x0,
|
1055
|
+
height=y1 - y0,
|
1056
|
+
matrix=mx,
|
1057
|
+
stroke=Color("black"),
|
1058
|
+
stroke_width=self.elements.default_strokewidth,
|
1059
|
+
type="elem rect",
|
1060
|
+
)
|
1061
|
+
p = ez.pens[element.pen]
|
1062
|
+
if op_add is None:
|
1063
|
+
op_add = op.add(type="op engrave", **p.__dict__)
|
1064
|
+
op_add.add_reference(node)
|
1065
|
+
elif isinstance(element, EZTimer):
|
1066
|
+
op.add(type="util wait", wait=element.wait_time / 1000.0)
|
1067
|
+
elif isinstance(element, EZOutput):
|
1068
|
+
mask = 1 << element.output_bit
|
1069
|
+
bits = mask if element.low_to_high else 0
|
1070
|
+
|
1071
|
+
op.add(
|
1072
|
+
type="util output",
|
1073
|
+
output_value=bits,
|
1074
|
+
output_mask=mask,
|
1075
|
+
)
|
1076
|
+
if element.timed_high:
|
1077
|
+
op.add(type="util wait", wait=element.wait_time / 1000.0)
|
1078
|
+
op.add(
|
1079
|
+
type="util output",
|
1080
|
+
output_value=~bits,
|
1081
|
+
output_mask=mask,
|
1082
|
+
)
|
1083
|
+
elif isinstance(element, EZInput):
|
1084
|
+
op.add(
|
1085
|
+
type="util input",
|
1086
|
+
input_message=element.message,
|
1087
|
+
input_value=element.input_port_bits,
|
1088
|
+
input_mask=element.input_port_bits,
|
1089
|
+
)
|
1090
|
+
elif isinstance(element, EZImage):
|
1091
|
+
image = element.image
|
1092
|
+
left, top = self.matrix.point_in_matrix_space(
|
1093
|
+
(
|
1094
|
+
element.position[0] - (element.width / 2.0),
|
1095
|
+
element.position[1] + element.height / 2.0,
|
1096
|
+
)
|
1097
|
+
)
|
1098
|
+
w, h = image.size
|
1099
|
+
unit_width = element.width * UNITS_PER_MM
|
1100
|
+
unit_height = element.height * UNITS_PER_MM
|
1101
|
+
matrix = Matrix.scale(
|
1102
|
+
(unit_width / w),
|
1103
|
+
(unit_height / h),
|
1104
|
+
)
|
1105
|
+
_dpi = int(
|
1106
|
+
round(
|
1107
|
+
(
|
1108
|
+
float((w * UNITS_PER_INCH) / unit_width)
|
1109
|
+
+ float((h * UNITS_PER_INCH) / unit_height)
|
1110
|
+
)
|
1111
|
+
/ 2.0,
|
1112
|
+
)
|
1113
|
+
)
|
1114
|
+
matrix.post_translate(left, top)
|
1115
|
+
node = elem.add(type="elem image", image=image, matrix=matrix, dpi=_dpi)
|
1116
|
+
p = ez.pens[element.pen]
|
1117
|
+
if op_add is None:
|
1118
|
+
op_add = op.add(type="op image", **p.__dict__)
|
1119
|
+
op_add.add_reference(node)
|
1120
|
+
elif isinstance(element, EZVectorFile):
|
1121
|
+
elem = elem.add(type="group", label=element.label)
|
1122
|
+
for child in element:
|
1123
|
+
# (self, ez, element, elem, op)
|
1124
|
+
self.parse(ez, child, elem, op, op_add=op_add, path=path)
|
1125
|
+
elif isinstance(element, EZHatch):
|
1126
|
+
p = dict(ez.pens[element.pen].__dict__)
|
1127
|
+
|
1128
|
+
op_add = op.add(type="op engrave", **p)
|
1129
|
+
if "label" in p:
|
1130
|
+
# Both pen and hatch have a label, we shall use the hatch-label for hatch; pen for op.
|
1131
|
+
del p["label"]
|
1132
|
+
op_add.add(type="effect hatch", **p, label=element.label)
|
1133
|
+
for child in element:
|
1134
|
+
# Operands for the hatch.
|
1135
|
+
self.parse(ez, child, elem, op, op_add=op_add)
|
1136
|
+
|
1137
|
+
op_add = op.add(type="op engrave", **p)
|
1138
|
+
if element.group:
|
1139
|
+
path = Path(stroke="black", transform=self.matrix)
|
1140
|
+
for child in element.group:
|
1141
|
+
# Per-completed hatch elements.
|
1142
|
+
self.parse(ez, child, elem, op, op_add=op_add, path=path)
|
1143
|
+
|
1144
|
+
# All path elements are added, should add it to the tree.
|
1145
|
+
node = elem.add(
|
1146
|
+
type="elem path",
|
1147
|
+
path=path,
|
1148
|
+
stroke_width=self.elements.default_strokewidth,
|
1149
|
+
)
|
1150
|
+
p = ez.pens[element.pen]
|
1151
|
+
if op_add is None:
|
1152
|
+
op_add = op.add(type="op engrave", **p.__dict__)
|
1153
|
+
op_add.add_reference(node)
|
1154
|
+
elif isinstance(element, (EZGroup, EZCombine)):
|
1155
|
+
elem = elem.add(type="group", label=element.label)
|
1156
|
+
# recurse to children
|
1157
|
+
for child in element:
|
1158
|
+
self.parse(ez, child, elem, op, op_add=op_add, path=path)
|
1159
|
+
elif isinstance(element, EZSpiral):
|
1160
|
+
elem = elem.add(type="group", label=element.label)
|
1161
|
+
# recurse to children
|
1162
|
+
for child in element:
|
1163
|
+
self.parse(ez, child, elem, op)
|
1164
|
+
for child in element.group:
|
1165
|
+
self.parse(ez, child, elem, op)
|