meerk40t 0.9.3001__py2.py3-none-any.whl → 0.9.7020__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- meerk40t/__init__.py +1 -1
- meerk40t/balormk/balor_params.py +167 -167
- meerk40t/balormk/clone_loader.py +457 -457
- meerk40t/balormk/controller.py +1566 -1512
- meerk40t/balormk/cylindermod.py +64 -0
- meerk40t/balormk/device.py +966 -1959
- meerk40t/balormk/driver.py +778 -591
- meerk40t/balormk/galvo_commands.py +1194 -0
- meerk40t/balormk/gui/balorconfig.py +237 -111
- meerk40t/balormk/gui/balorcontroller.py +191 -184
- meerk40t/balormk/gui/baloroperationproperties.py +116 -115
- meerk40t/balormk/gui/corscene.py +845 -0
- meerk40t/balormk/gui/gui.py +179 -147
- meerk40t/balormk/livelightjob.py +466 -382
- meerk40t/balormk/mock_connection.py +131 -109
- meerk40t/balormk/plugin.py +133 -135
- meerk40t/balormk/usb_connection.py +306 -301
- meerk40t/camera/__init__.py +1 -1
- meerk40t/camera/camera.py +514 -397
- meerk40t/camera/gui/camerapanel.py +1241 -1095
- meerk40t/camera/gui/gui.py +58 -58
- meerk40t/camera/plugin.py +441 -399
- meerk40t/ch341/__init__.py +27 -27
- meerk40t/ch341/ch341device.py +628 -628
- meerk40t/ch341/libusb.py +595 -589
- meerk40t/ch341/mock.py +171 -171
- meerk40t/ch341/windriver.py +157 -157
- meerk40t/constants.py +13 -0
- meerk40t/core/__init__.py +1 -1
- meerk40t/core/bindalias.py +550 -539
- meerk40t/core/core.py +47 -47
- meerk40t/core/cutcode/cubiccut.py +73 -73
- meerk40t/core/cutcode/cutcode.py +315 -312
- meerk40t/core/cutcode/cutgroup.py +141 -137
- meerk40t/core/cutcode/cutobject.py +192 -185
- meerk40t/core/cutcode/dwellcut.py +37 -37
- meerk40t/core/cutcode/gotocut.py +29 -29
- meerk40t/core/cutcode/homecut.py +29 -29
- meerk40t/core/cutcode/inputcut.py +34 -34
- meerk40t/core/cutcode/linecut.py +33 -33
- meerk40t/core/cutcode/outputcut.py +34 -34
- meerk40t/core/cutcode/plotcut.py +335 -335
- meerk40t/core/cutcode/quadcut.py +61 -61
- meerk40t/core/cutcode/rastercut.py +168 -148
- meerk40t/core/cutcode/waitcut.py +34 -34
- meerk40t/core/cutplan.py +1843 -1316
- meerk40t/core/drivers.py +330 -329
- meerk40t/core/elements/align.py +801 -669
- meerk40t/core/elements/branches.py +1858 -1507
- meerk40t/core/elements/clipboard.py +229 -219
- meerk40t/core/elements/element_treeops.py +4595 -2837
- meerk40t/core/elements/element_types.py +125 -105
- meerk40t/core/elements/elements.py +4315 -3617
- meerk40t/core/elements/files.py +117 -64
- meerk40t/core/elements/geometry.py +473 -224
- meerk40t/core/elements/grid.py +467 -316
- meerk40t/core/elements/materials.py +158 -94
- meerk40t/core/elements/notes.py +50 -38
- meerk40t/core/elements/offset_clpr.py +934 -912
- meerk40t/core/elements/offset_mk.py +963 -955
- meerk40t/core/elements/penbox.py +339 -267
- meerk40t/core/elements/placements.py +300 -83
- meerk40t/core/elements/render.py +785 -687
- meerk40t/core/elements/shapes.py +2618 -2092
- meerk40t/core/elements/testcases.py +105 -0
- meerk40t/core/elements/trace.py +651 -563
- meerk40t/core/elements/tree_commands.py +415 -409
- meerk40t/core/elements/undo_redo.py +116 -58
- meerk40t/core/elements/wordlist.py +319 -200
- meerk40t/core/exceptions.py +9 -9
- meerk40t/core/laserjob.py +220 -220
- meerk40t/core/logging.py +63 -63
- meerk40t/core/node/blobnode.py +83 -86
- meerk40t/core/node/bootstrap.py +105 -103
- meerk40t/core/node/branch_elems.py +40 -31
- meerk40t/core/node/branch_ops.py +45 -38
- meerk40t/core/node/branch_regmark.py +48 -41
- meerk40t/core/node/cutnode.py +29 -32
- meerk40t/core/node/effect_hatch.py +375 -257
- meerk40t/core/node/effect_warp.py +398 -0
- meerk40t/core/node/effect_wobble.py +441 -309
- meerk40t/core/node/elem_ellipse.py +404 -309
- meerk40t/core/node/elem_image.py +1082 -801
- meerk40t/core/node/elem_line.py +358 -292
- meerk40t/core/node/elem_path.py +259 -201
- meerk40t/core/node/elem_point.py +129 -102
- meerk40t/core/node/elem_polyline.py +310 -246
- meerk40t/core/node/elem_rect.py +376 -286
- meerk40t/core/node/elem_text.py +445 -418
- meerk40t/core/node/filenode.py +59 -40
- meerk40t/core/node/groupnode.py +138 -74
- meerk40t/core/node/image_processed.py +777 -766
- meerk40t/core/node/image_raster.py +156 -113
- meerk40t/core/node/layernode.py +31 -31
- meerk40t/core/node/mixins.py +135 -107
- meerk40t/core/node/node.py +1427 -1304
- meerk40t/core/node/nutils.py +117 -114
- meerk40t/core/node/op_cut.py +463 -335
- meerk40t/core/node/op_dots.py +296 -251
- meerk40t/core/node/op_engrave.py +414 -311
- meerk40t/core/node/op_image.py +755 -369
- meerk40t/core/node/op_raster.py +787 -522
- meerk40t/core/node/place_current.py +37 -40
- meerk40t/core/node/place_point.py +329 -126
- meerk40t/core/node/refnode.py +58 -47
- meerk40t/core/node/rootnode.py +225 -219
- meerk40t/core/node/util_console.py +48 -48
- meerk40t/core/node/util_goto.py +84 -65
- meerk40t/core/node/util_home.py +61 -61
- meerk40t/core/node/util_input.py +102 -102
- meerk40t/core/node/util_output.py +102 -102
- meerk40t/core/node/util_wait.py +65 -65
- meerk40t/core/parameters.py +709 -707
- meerk40t/core/planner.py +875 -785
- meerk40t/core/plotplanner.py +656 -652
- meerk40t/core/space.py +120 -113
- meerk40t/core/spoolers.py +706 -705
- meerk40t/core/svg_io.py +1836 -1549
- meerk40t/core/treeop.py +534 -445
- meerk40t/core/undos.py +278 -124
- meerk40t/core/units.py +784 -680
- meerk40t/core/view.py +393 -322
- meerk40t/core/webhelp.py +62 -62
- meerk40t/core/wordlist.py +513 -504
- meerk40t/cylinder/cylinder.py +247 -0
- meerk40t/cylinder/gui/cylindersettings.py +41 -0
- meerk40t/cylinder/gui/gui.py +24 -0
- meerk40t/device/__init__.py +1 -1
- meerk40t/device/basedevice.py +322 -123
- meerk40t/device/devicechoices.py +50 -0
- meerk40t/device/dummydevice.py +163 -128
- meerk40t/device/gui/defaultactions.py +618 -602
- meerk40t/device/gui/effectspanel.py +114 -0
- meerk40t/device/gui/formatterpanel.py +253 -290
- meerk40t/device/gui/warningpanel.py +337 -260
- meerk40t/device/mixins.py +13 -13
- meerk40t/dxf/__init__.py +1 -1
- meerk40t/dxf/dxf_io.py +766 -554
- meerk40t/dxf/plugin.py +47 -35
- meerk40t/external_plugins.py +79 -79
- meerk40t/external_plugins_build.py +28 -28
- meerk40t/extra/cag.py +112 -116
- meerk40t/extra/coolant.py +403 -0
- meerk40t/extra/encode_detect.py +204 -0
- meerk40t/extra/ezd.py +1165 -1165
- meerk40t/extra/hershey.py +834 -340
- meerk40t/extra/imageactions.py +322 -316
- meerk40t/extra/inkscape.py +628 -622
- meerk40t/extra/lbrn.py +424 -424
- meerk40t/extra/outerworld.py +283 -0
- meerk40t/extra/param_functions.py +1542 -1556
- meerk40t/extra/potrace.py +257 -253
- meerk40t/extra/serial_exchange.py +118 -0
- meerk40t/extra/updater.py +602 -453
- meerk40t/extra/vectrace.py +147 -146
- meerk40t/extra/winsleep.py +83 -83
- meerk40t/extra/xcs_reader.py +597 -0
- meerk40t/fill/fills.py +781 -335
- meerk40t/fill/patternfill.py +1061 -1061
- meerk40t/fill/patterns.py +614 -567
- meerk40t/grbl/control.py +87 -87
- meerk40t/grbl/controller.py +990 -903
- meerk40t/grbl/device.py +1084 -768
- meerk40t/grbl/driver.py +989 -771
- meerk40t/grbl/emulator.py +532 -497
- meerk40t/grbl/gcodejob.py +783 -767
- meerk40t/grbl/gui/grblconfiguration.py +373 -298
- meerk40t/grbl/gui/grblcontroller.py +485 -271
- meerk40t/grbl/gui/grblhardwareconfig.py +269 -153
- meerk40t/grbl/gui/grbloperationconfig.py +105 -0
- meerk40t/grbl/gui/gui.py +147 -116
- meerk40t/grbl/interpreter.py +44 -44
- meerk40t/grbl/loader.py +22 -22
- meerk40t/grbl/mock_connection.py +56 -56
- meerk40t/grbl/plugin.py +294 -264
- meerk40t/grbl/serial_connection.py +93 -88
- meerk40t/grbl/tcp_connection.py +81 -79
- meerk40t/grbl/ws_connection.py +112 -0
- meerk40t/gui/__init__.py +1 -1
- meerk40t/gui/about.py +2042 -296
- meerk40t/gui/alignment.py +1644 -1608
- meerk40t/gui/autoexec.py +199 -0
- meerk40t/gui/basicops.py +791 -670
- meerk40t/gui/bufferview.py +77 -71
- meerk40t/gui/busy.py +232 -133
- meerk40t/gui/choicepropertypanel.py +1662 -1469
- meerk40t/gui/consolepanel.py +706 -542
- meerk40t/gui/devicepanel.py +687 -581
- meerk40t/gui/dialogoptions.py +110 -107
- meerk40t/gui/executejob.py +316 -306
- meerk40t/gui/fonts.py +90 -90
- meerk40t/gui/functionwrapper.py +252 -0
- meerk40t/gui/gui_mixins.py +729 -0
- meerk40t/gui/guicolors.py +205 -182
- meerk40t/gui/help_assets/help_assets.py +218 -201
- meerk40t/gui/helper.py +154 -0
- meerk40t/gui/hersheymanager.py +1440 -846
- meerk40t/gui/icons.py +3422 -2747
- meerk40t/gui/imagesplitter.py +555 -508
- meerk40t/gui/keymap.py +354 -344
- meerk40t/gui/laserpanel.py +897 -806
- meerk40t/gui/laserrender.py +1470 -1232
- meerk40t/gui/lasertoolpanel.py +805 -793
- meerk40t/gui/magnetoptions.py +436 -0
- meerk40t/gui/materialmanager.py +2944 -0
- meerk40t/gui/materialtest.py +1722 -1694
- meerk40t/gui/mkdebug.py +646 -359
- meerk40t/gui/mwindow.py +163 -140
- meerk40t/gui/navigationpanels.py +2605 -2467
- meerk40t/gui/notes.py +143 -142
- meerk40t/gui/opassignment.py +414 -410
- meerk40t/gui/operation_info.py +310 -299
- meerk40t/gui/plugin.py +500 -328
- meerk40t/gui/position.py +714 -669
- meerk40t/gui/preferences.py +901 -650
- meerk40t/gui/propertypanels/attributes.py +1461 -1131
- meerk40t/gui/propertypanels/blobproperty.py +117 -114
- meerk40t/gui/propertypanels/consoleproperty.py +83 -80
- meerk40t/gui/propertypanels/gotoproperty.py +77 -0
- meerk40t/gui/propertypanels/groupproperties.py +223 -217
- meerk40t/gui/propertypanels/hatchproperty.py +489 -469
- meerk40t/gui/propertypanels/imageproperty.py +2244 -1384
- meerk40t/gui/propertypanels/inputproperty.py +59 -58
- meerk40t/gui/propertypanels/opbranchproperties.py +82 -80
- meerk40t/gui/propertypanels/operationpropertymain.py +1890 -1638
- meerk40t/gui/propertypanels/outputproperty.py +59 -58
- meerk40t/gui/propertypanels/pathproperty.py +389 -380
- meerk40t/gui/propertypanels/placementproperty.py +1214 -383
- meerk40t/gui/propertypanels/pointproperty.py +140 -136
- meerk40t/gui/propertypanels/propertywindow.py +313 -181
- meerk40t/gui/propertypanels/rasterwizardpanels.py +996 -912
- meerk40t/gui/propertypanels/regbranchproperties.py +76 -0
- meerk40t/gui/propertypanels/textproperty.py +770 -755
- meerk40t/gui/propertypanels/waitproperty.py +56 -55
- meerk40t/gui/propertypanels/warpproperty.py +121 -0
- meerk40t/gui/propertypanels/wobbleproperty.py +255 -204
- meerk40t/gui/ribbon.py +2471 -2210
- meerk40t/gui/scene/scene.py +1100 -1051
- meerk40t/gui/scene/sceneconst.py +22 -22
- meerk40t/gui/scene/scenepanel.py +439 -349
- meerk40t/gui/scene/scenespacewidget.py +365 -365
- meerk40t/gui/scene/widget.py +518 -505
- meerk40t/gui/scenewidgets/affinemover.py +215 -215
- meerk40t/gui/scenewidgets/attractionwidget.py +315 -309
- meerk40t/gui/scenewidgets/bedwidget.py +120 -97
- meerk40t/gui/scenewidgets/elementswidget.py +137 -107
- meerk40t/gui/scenewidgets/gridwidget.py +785 -745
- meerk40t/gui/scenewidgets/guidewidget.py +765 -765
- meerk40t/gui/scenewidgets/laserpathwidget.py +66 -66
- meerk40t/gui/scenewidgets/machineoriginwidget.py +86 -86
- meerk40t/gui/scenewidgets/nodeselector.py +28 -28
- meerk40t/gui/scenewidgets/rectselectwidget.py +592 -346
- meerk40t/gui/scenewidgets/relocatewidget.py +33 -33
- meerk40t/gui/scenewidgets/reticlewidget.py +83 -83
- meerk40t/gui/scenewidgets/selectionwidget.py +2958 -2756
- meerk40t/gui/simpleui.py +362 -333
- meerk40t/gui/simulation.py +2451 -2094
- meerk40t/gui/snapoptions.py +208 -203
- meerk40t/gui/spoolerpanel.py +1227 -1180
- meerk40t/gui/statusbarwidgets/defaultoperations.py +480 -353
- meerk40t/gui/statusbarwidgets/infowidget.py +520 -483
- meerk40t/gui/statusbarwidgets/opassignwidget.py +356 -355
- meerk40t/gui/statusbarwidgets/selectionwidget.py +172 -171
- meerk40t/gui/statusbarwidgets/shapepropwidget.py +754 -236
- meerk40t/gui/statusbarwidgets/statusbar.py +272 -260
- meerk40t/gui/statusbarwidgets/statusbarwidget.py +268 -270
- meerk40t/gui/statusbarwidgets/strokewidget.py +267 -251
- meerk40t/gui/themes.py +200 -78
- meerk40t/gui/tips.py +590 -0
- meerk40t/gui/toolwidgets/circlebrush.py +35 -35
- meerk40t/gui/toolwidgets/toolcircle.py +248 -242
- meerk40t/gui/toolwidgets/toolcontainer.py +82 -77
- meerk40t/gui/toolwidgets/tooldraw.py +97 -90
- meerk40t/gui/toolwidgets/toolellipse.py +219 -212
- meerk40t/gui/toolwidgets/toolimagecut.py +25 -132
- meerk40t/gui/toolwidgets/toolline.py +39 -144
- meerk40t/gui/toolwidgets/toollinetext.py +79 -236
- meerk40t/gui/toolwidgets/toollinetext_inline.py +296 -0
- meerk40t/gui/toolwidgets/toolmeasure.py +163 -216
- meerk40t/gui/toolwidgets/toolnodeedit.py +2088 -2074
- meerk40t/gui/toolwidgets/toolnodemove.py +92 -94
- meerk40t/gui/toolwidgets/toolparameter.py +754 -668
- meerk40t/gui/toolwidgets/toolplacement.py +108 -108
- meerk40t/gui/toolwidgets/toolpoint.py +68 -59
- meerk40t/gui/toolwidgets/toolpointlistbuilder.py +294 -0
- meerk40t/gui/toolwidgets/toolpointmove.py +183 -0
- meerk40t/gui/toolwidgets/toolpolygon.py +288 -403
- meerk40t/gui/toolwidgets/toolpolyline.py +38 -196
- meerk40t/gui/toolwidgets/toolrect.py +211 -207
- meerk40t/gui/toolwidgets/toolrelocate.py +72 -72
- meerk40t/gui/toolwidgets/toolribbon.py +598 -113
- meerk40t/gui/toolwidgets/tooltabedit.py +546 -0
- meerk40t/gui/toolwidgets/tooltext.py +98 -89
- meerk40t/gui/toolwidgets/toolvector.py +213 -204
- meerk40t/gui/toolwidgets/toolwidget.py +39 -39
- meerk40t/gui/usbconnect.py +98 -91
- meerk40t/gui/utilitywidgets/buttonwidget.py +18 -18
- meerk40t/gui/utilitywidgets/checkboxwidget.py +90 -90
- meerk40t/gui/utilitywidgets/controlwidget.py +14 -14
- meerk40t/gui/utilitywidgets/cyclocycloidwidget.py +343 -340
- meerk40t/gui/utilitywidgets/debugwidgets.py +148 -0
- meerk40t/gui/utilitywidgets/handlewidget.py +27 -27
- meerk40t/gui/utilitywidgets/harmonograph.py +450 -447
- meerk40t/gui/utilitywidgets/openclosewidget.py +40 -40
- meerk40t/gui/utilitywidgets/rotationwidget.py +54 -54
- meerk40t/gui/utilitywidgets/scalewidget.py +75 -75
- meerk40t/gui/utilitywidgets/seekbarwidget.py +183 -183
- meerk40t/gui/utilitywidgets/togglewidget.py +142 -142
- meerk40t/gui/utilitywidgets/toolbarwidget.py +8 -8
- meerk40t/gui/wordlisteditor.py +985 -931
- meerk40t/gui/wxmeerk40t.py +1447 -1169
- meerk40t/gui/wxmmain.py +5644 -4112
- meerk40t/gui/wxmribbon.py +1591 -1076
- meerk40t/gui/wxmscene.py +1631 -1453
- meerk40t/gui/wxmtree.py +2416 -2089
- meerk40t/gui/wxutils.py +1769 -1099
- meerk40t/gui/zmatrix.py +102 -102
- meerk40t/image/__init__.py +1 -1
- meerk40t/image/dither.py +429 -0
- meerk40t/image/imagetools.py +2793 -2269
- meerk40t/internal_plugins.py +150 -130
- meerk40t/kernel/__init__.py +63 -12
- meerk40t/kernel/channel.py +259 -212
- meerk40t/kernel/context.py +538 -538
- meerk40t/kernel/exceptions.py +41 -41
- meerk40t/kernel/functions.py +463 -414
- meerk40t/kernel/jobs.py +100 -100
- meerk40t/kernel/kernel.py +3828 -3571
- meerk40t/kernel/lifecycles.py +71 -71
- meerk40t/kernel/module.py +49 -49
- meerk40t/kernel/service.py +147 -147
- meerk40t/kernel/settings.py +383 -343
- meerk40t/lihuiyu/controller.py +883 -876
- meerk40t/lihuiyu/device.py +1181 -1069
- meerk40t/lihuiyu/driver.py +1466 -1372
- meerk40t/lihuiyu/gui/gui.py +127 -106
- meerk40t/lihuiyu/gui/lhyaccelgui.py +377 -363
- meerk40t/lihuiyu/gui/lhycontrollergui.py +741 -651
- meerk40t/lihuiyu/gui/lhydrivergui.py +470 -446
- meerk40t/lihuiyu/gui/lhyoperationproperties.py +238 -237
- meerk40t/lihuiyu/gui/tcpcontroller.py +226 -190
- meerk40t/lihuiyu/interpreter.py +53 -53
- meerk40t/lihuiyu/laserspeed.py +450 -450
- meerk40t/lihuiyu/loader.py +90 -90
- meerk40t/lihuiyu/parser.py +404 -404
- meerk40t/lihuiyu/plugin.py +101 -102
- meerk40t/lihuiyu/tcp_connection.py +111 -109
- meerk40t/main.py +231 -165
- meerk40t/moshi/builder.py +788 -781
- meerk40t/moshi/controller.py +505 -499
- meerk40t/moshi/device.py +495 -442
- meerk40t/moshi/driver.py +862 -696
- meerk40t/moshi/gui/gui.py +78 -76
- meerk40t/moshi/gui/moshicontrollergui.py +538 -522
- meerk40t/moshi/gui/moshidrivergui.py +87 -75
- meerk40t/moshi/plugin.py +43 -43
- meerk40t/network/console_server.py +140 -57
- meerk40t/network/kernelserver.py +10 -9
- meerk40t/network/tcp_server.py +142 -140
- meerk40t/network/udp_server.py +103 -77
- meerk40t/network/web_server.py +404 -0
- meerk40t/newly/controller.py +1158 -1144
- meerk40t/newly/device.py +874 -732
- meerk40t/newly/driver.py +540 -412
- meerk40t/newly/gui/gui.py +219 -188
- meerk40t/newly/gui/newlyconfig.py +116 -101
- meerk40t/newly/gui/newlycontroller.py +193 -186
- meerk40t/newly/gui/operationproperties.py +51 -51
- meerk40t/newly/mock_connection.py +82 -82
- meerk40t/newly/newly_params.py +56 -56
- meerk40t/newly/plugin.py +1214 -1246
- meerk40t/newly/usb_connection.py +322 -322
- meerk40t/rotary/gui/gui.py +52 -46
- meerk40t/rotary/gui/rotarysettings.py +240 -232
- meerk40t/rotary/rotary.py +202 -98
- meerk40t/ruida/control.py +291 -91
- meerk40t/ruida/controller.py +138 -1088
- meerk40t/ruida/device.py +676 -231
- meerk40t/ruida/driver.py +534 -472
- meerk40t/ruida/emulator.py +1494 -1491
- meerk40t/ruida/exceptions.py +4 -4
- meerk40t/ruida/gui/gui.py +71 -76
- meerk40t/ruida/gui/ruidaconfig.py +239 -72
- meerk40t/ruida/gui/ruidacontroller.py +187 -184
- meerk40t/ruida/gui/ruidaoperationproperties.py +48 -47
- meerk40t/ruida/loader.py +54 -52
- meerk40t/ruida/mock_connection.py +57 -109
- meerk40t/ruida/plugin.py +124 -87
- meerk40t/ruida/rdjob.py +2084 -945
- meerk40t/ruida/serial_connection.py +116 -0
- meerk40t/ruida/tcp_connection.py +146 -0
- meerk40t/ruida/udp_connection.py +73 -0
- meerk40t/svgelements.py +9671 -9669
- meerk40t/tools/driver_to_path.py +584 -579
- meerk40t/tools/geomstr.py +5583 -4680
- meerk40t/tools/jhfparser.py +357 -292
- meerk40t/tools/kerftest.py +904 -890
- meerk40t/tools/livinghinges.py +1168 -1033
- meerk40t/tools/pathtools.py +987 -949
- meerk40t/tools/pmatrix.py +234 -0
- meerk40t/tools/pointfinder.py +942 -942
- meerk40t/tools/polybool.py +941 -940
- meerk40t/tools/rasterplotter.py +1660 -547
- meerk40t/tools/shxparser.py +1047 -901
- meerk40t/tools/ttfparser.py +726 -446
- meerk40t/tools/zinglplotter.py +595 -593
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/LICENSE +21 -21
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/METADATA +150 -139
- meerk40t-0.9.7020.dist-info/RECORD +446 -0
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/WHEEL +1 -1
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/top_level.txt +0 -1
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/zip-safe +1 -1
- meerk40t/balormk/elementlightjob.py +0 -159
- meerk40t-0.9.3001.dist-info/RECORD +0 -437
- test/bootstrap.py +0 -63
- test/test_cli.py +0 -12
- test/test_core_cutcode.py +0 -418
- test/test_core_elements.py +0 -144
- test/test_core_plotplanner.py +0 -397
- test/test_core_viewports.py +0 -312
- test/test_drivers_grbl.py +0 -108
- test/test_drivers_lihuiyu.py +0 -443
- test/test_drivers_newly.py +0 -113
- test/test_element_degenerate_points.py +0 -43
- test/test_elements_classify.py +0 -97
- test/test_elements_penbox.py +0 -22
- test/test_file_svg.py +0 -176
- test/test_fill.py +0 -155
- test/test_geomstr.py +0 -1523
- test/test_geomstr_nodes.py +0 -18
- test/test_imagetools_actualize.py +0 -306
- test/test_imagetools_wizard.py +0 -258
- test/test_kernel.py +0 -200
- test/test_laser_speeds.py +0 -3303
- test/test_length.py +0 -57
- test/test_lifecycle.py +0 -66
- test/test_operations.py +0 -251
- test/test_operations_hatch.py +0 -57
- test/test_ruida.py +0 -19
- test/test_spooler.py +0 -22
- test/test_tools_rasterplotter.py +0 -29
- test/test_wobble.py +0 -133
- test/test_zingl.py +0 -124
- {test → meerk40t/cylinder}/__init__.py +0 -0
- /meerk40t/{core/element_commands.py → cylinder/gui/__init__.py} +0 -0
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/entry_points.txt +0 -0
meerk40t/core/elements/shapes.py
CHANGED
@@ -1,2092 +1,2618 @@
|
|
1
|
-
"""
|
2
|
-
This
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
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
|
-
@self.
|
149
|
-
|
150
|
-
)
|
151
|
-
@self.
|
152
|
-
"
|
153
|
-
|
154
|
-
|
155
|
-
"
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
)
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
)
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
)
|
222
|
-
node.
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
""
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
self.set_emphasis([node])
|
299
|
-
node.focus()
|
300
|
-
if data is None:
|
301
|
-
data = list()
|
302
|
-
data.append(node)
|
303
|
-
# Newly created! Classification needed?
|
304
|
-
post.append(classify_new(data))
|
305
|
-
return "elements", data
|
306
|
-
|
307
|
-
@self.console_argument(
|
308
|
-
|
309
|
-
)
|
310
|
-
@self.
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
)
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
return "elements", data
|
340
|
-
|
341
|
-
@self.
|
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
|
-
if
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
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
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
if
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
)
|
610
|
-
def
|
611
|
-
if data is None:
|
612
|
-
data = list(self.elems(emphasized=True))
|
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
|
-
return
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
node
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
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
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
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
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
if
|
936
|
-
channel("
|
937
|
-
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
|
942
|
-
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
|
977
|
-
|
978
|
-
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
-
|
983
|
-
|
984
|
-
|
985
|
-
|
986
|
-
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
|
991
|
-
|
992
|
-
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
except
|
1011
|
-
channel(
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
1053
|
-
|
1054
|
-
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1067
|
-
"
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1071
|
-
|
1072
|
-
|
1073
|
-
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
if data is None:
|
1084
|
-
data = list(self.elems(emphasized=True))
|
1085
|
-
|
1086
|
-
if
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1102
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
1109
|
-
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
1115
|
-
|
1116
|
-
|
1117
|
-
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1122
|
-
|
1123
|
-
|
1124
|
-
|
1125
|
-
|
1126
|
-
|
1127
|
-
|
1128
|
-
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
1136
|
-
|
1137
|
-
|
1138
|
-
|
1139
|
-
|
1140
|
-
|
1141
|
-
|
1142
|
-
|
1143
|
-
|
1144
|
-
|
1145
|
-
|
1146
|
-
|
1147
|
-
|
1148
|
-
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1153
|
-
|
1154
|
-
|
1155
|
-
|
1156
|
-
|
1157
|
-
|
1158
|
-
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1162
|
-
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
1166
|
-
|
1167
|
-
|
1168
|
-
|
1169
|
-
|
1170
|
-
|
1171
|
-
|
1172
|
-
|
1173
|
-
|
1174
|
-
|
1175
|
-
|
1176
|
-
|
1177
|
-
|
1178
|
-
|
1179
|
-
|
1180
|
-
|
1181
|
-
|
1182
|
-
|
1183
|
-
|
1184
|
-
|
1185
|
-
|
1186
|
-
|
1187
|
-
|
1188
|
-
|
1189
|
-
|
1190
|
-
|
1191
|
-
|
1192
|
-
|
1193
|
-
|
1194
|
-
|
1195
|
-
|
1196
|
-
|
1197
|
-
|
1198
|
-
|
1199
|
-
|
1200
|
-
|
1201
|
-
|
1202
|
-
|
1203
|
-
|
1204
|
-
|
1205
|
-
|
1206
|
-
|
1207
|
-
|
1208
|
-
|
1209
|
-
|
1210
|
-
|
1211
|
-
|
1212
|
-
|
1213
|
-
|
1214
|
-
|
1215
|
-
|
1216
|
-
|
1217
|
-
|
1218
|
-
|
1219
|
-
|
1220
|
-
|
1221
|
-
|
1222
|
-
|
1223
|
-
|
1224
|
-
|
1225
|
-
|
1226
|
-
|
1227
|
-
|
1228
|
-
|
1229
|
-
|
1230
|
-
|
1231
|
-
|
1232
|
-
|
1233
|
-
|
1234
|
-
|
1235
|
-
|
1236
|
-
|
1237
|
-
|
1238
|
-
|
1239
|
-
|
1240
|
-
|
1241
|
-
|
1242
|
-
"
|
1243
|
-
|
1244
|
-
|
1245
|
-
|
1246
|
-
|
1247
|
-
|
1248
|
-
|
1249
|
-
|
1250
|
-
|
1251
|
-
|
1252
|
-
|
1253
|
-
|
1254
|
-
|
1255
|
-
|
1256
|
-
|
1257
|
-
|
1258
|
-
|
1259
|
-
|
1260
|
-
|
1261
|
-
|
1262
|
-
|
1263
|
-
|
1264
|
-
|
1265
|
-
|
1266
|
-
|
1267
|
-
|
1268
|
-
|
1269
|
-
|
1270
|
-
|
1271
|
-
|
1272
|
-
|
1273
|
-
|
1274
|
-
|
1275
|
-
|
1276
|
-
|
1277
|
-
|
1278
|
-
|
1279
|
-
|
1280
|
-
|
1281
|
-
|
1282
|
-
|
1283
|
-
|
1284
|
-
|
1285
|
-
|
1286
|
-
|
1287
|
-
|
1288
|
-
|
1289
|
-
|
1290
|
-
|
1291
|
-
|
1292
|
-
|
1293
|
-
|
1294
|
-
|
1295
|
-
|
1296
|
-
|
1297
|
-
|
1298
|
-
|
1299
|
-
|
1300
|
-
|
1301
|
-
self.
|
1302
|
-
|
1303
|
-
|
1304
|
-
|
1305
|
-
|
1306
|
-
|
1307
|
-
|
1308
|
-
|
1309
|
-
|
1310
|
-
|
1311
|
-
|
1312
|
-
|
1313
|
-
|
1314
|
-
|
1315
|
-
if hasattr(e, "
|
1316
|
-
|
1317
|
-
|
1318
|
-
|
1319
|
-
|
1320
|
-
|
1321
|
-
|
1322
|
-
|
1323
|
-
|
1324
|
-
|
1325
|
-
|
1326
|
-
|
1327
|
-
|
1328
|
-
|
1329
|
-
|
1330
|
-
|
1331
|
-
|
1332
|
-
|
1333
|
-
|
1334
|
-
|
1335
|
-
|
1336
|
-
|
1337
|
-
|
1338
|
-
|
1339
|
-
|
1340
|
-
|
1341
|
-
|
1342
|
-
|
1343
|
-
|
1344
|
-
|
1345
|
-
|
1346
|
-
|
1347
|
-
|
1348
|
-
|
1349
|
-
|
1350
|
-
|
1351
|
-
|
1352
|
-
|
1353
|
-
|
1354
|
-
|
1355
|
-
|
1356
|
-
|
1357
|
-
|
1358
|
-
|
1359
|
-
|
1360
|
-
|
1361
|
-
|
1362
|
-
|
1363
|
-
|
1364
|
-
|
1365
|
-
|
1366
|
-
|
1367
|
-
|
1368
|
-
data
|
1369
|
-
|
1370
|
-
|
1371
|
-
|
1372
|
-
|
1373
|
-
|
1374
|
-
""
|
1375
|
-
|
1376
|
-
|
1377
|
-
|
1378
|
-
|
1379
|
-
|
1380
|
-
|
1381
|
-
|
1382
|
-
|
1383
|
-
|
1384
|
-
|
1385
|
-
|
1386
|
-
|
1387
|
-
|
1388
|
-
|
1389
|
-
|
1390
|
-
|
1391
|
-
|
1392
|
-
|
1393
|
-
|
1394
|
-
)
|
1395
|
-
self.
|
1396
|
-
|
1397
|
-
|
1398
|
-
|
1399
|
-
|
1400
|
-
|
1401
|
-
|
1402
|
-
|
1403
|
-
|
1404
|
-
@self.
|
1405
|
-
|
1406
|
-
|
1407
|
-
|
1408
|
-
|
1409
|
-
|
1410
|
-
|
1411
|
-
|
1412
|
-
|
1413
|
-
)
|
1414
|
-
|
1415
|
-
|
1416
|
-
|
1417
|
-
|
1418
|
-
|
1419
|
-
"
|
1420
|
-
|
1421
|
-
|
1422
|
-
|
1423
|
-
|
1424
|
-
|
1425
|
-
|
1426
|
-
|
1427
|
-
|
1428
|
-
|
1429
|
-
|
1430
|
-
|
1431
|
-
|
1432
|
-
|
1433
|
-
|
1434
|
-
|
1435
|
-
|
1436
|
-
|
1437
|
-
|
1438
|
-
|
1439
|
-
|
1440
|
-
|
1441
|
-
|
1442
|
-
|
1443
|
-
|
1444
|
-
index
|
1445
|
-
|
1446
|
-
|
1447
|
-
|
1448
|
-
|
1449
|
-
|
1450
|
-
|
1451
|
-
|
1452
|
-
|
1453
|
-
|
1454
|
-
|
1455
|
-
|
1456
|
-
|
1457
|
-
|
1458
|
-
|
1459
|
-
|
1460
|
-
|
1461
|
-
|
1462
|
-
|
1463
|
-
|
1464
|
-
|
1465
|
-
|
1466
|
-
|
1467
|
-
|
1468
|
-
|
1469
|
-
|
1470
|
-
|
1471
|
-
|
1472
|
-
|
1473
|
-
|
1474
|
-
|
1475
|
-
|
1476
|
-
|
1477
|
-
|
1478
|
-
|
1479
|
-
|
1480
|
-
|
1481
|
-
|
1482
|
-
|
1483
|
-
|
1484
|
-
|
1485
|
-
|
1486
|
-
|
1487
|
-
|
1488
|
-
|
1489
|
-
|
1490
|
-
|
1491
|
-
|
1492
|
-
|
1493
|
-
|
1494
|
-
|
1495
|
-
|
1496
|
-
|
1497
|
-
|
1498
|
-
|
1499
|
-
|
1500
|
-
|
1501
|
-
|
1502
|
-
|
1503
|
-
|
1504
|
-
|
1505
|
-
|
1506
|
-
|
1507
|
-
|
1508
|
-
|
1509
|
-
|
1510
|
-
|
1511
|
-
|
1512
|
-
|
1513
|
-
|
1514
|
-
|
1515
|
-
|
1516
|
-
|
1517
|
-
|
1518
|
-
|
1519
|
-
|
1520
|
-
|
1521
|
-
|
1522
|
-
|
1523
|
-
|
1524
|
-
|
1525
|
-
|
1526
|
-
|
1527
|
-
|
1528
|
-
|
1529
|
-
|
1530
|
-
|
1531
|
-
|
1532
|
-
|
1533
|
-
|
1534
|
-
|
1535
|
-
|
1536
|
-
|
1537
|
-
|
1538
|
-
|
1539
|
-
|
1540
|
-
|
1541
|
-
|
1542
|
-
|
1543
|
-
|
1544
|
-
|
1545
|
-
|
1546
|
-
|
1547
|
-
|
1548
|
-
|
1549
|
-
|
1550
|
-
|
1551
|
-
|
1552
|
-
|
1553
|
-
|
1554
|
-
|
1555
|
-
|
1556
|
-
|
1557
|
-
|
1558
|
-
|
1559
|
-
|
1560
|
-
|
1561
|
-
|
1562
|
-
|
1563
|
-
|
1564
|
-
|
1565
|
-
|
1566
|
-
|
1567
|
-
|
1568
|
-
|
1569
|
-
|
1570
|
-
|
1571
|
-
|
1572
|
-
|
1573
|
-
|
1574
|
-
|
1575
|
-
|
1576
|
-
|
1577
|
-
|
1578
|
-
|
1579
|
-
|
1580
|
-
|
1581
|
-
|
1582
|
-
|
1583
|
-
|
1584
|
-
|
1585
|
-
|
1586
|
-
|
1587
|
-
|
1588
|
-
|
1589
|
-
|
1590
|
-
|
1591
|
-
|
1592
|
-
|
1593
|
-
|
1594
|
-
|
1595
|
-
|
1596
|
-
|
1597
|
-
|
1598
|
-
|
1599
|
-
|
1600
|
-
|
1601
|
-
|
1602
|
-
|
1603
|
-
|
1604
|
-
|
1605
|
-
|
1606
|
-
|
1607
|
-
|
1608
|
-
|
1609
|
-
|
1610
|
-
|
1611
|
-
|
1612
|
-
|
1613
|
-
|
1614
|
-
|
1615
|
-
|
1616
|
-
|
1617
|
-
|
1618
|
-
|
1619
|
-
|
1620
|
-
|
1621
|
-
|
1622
|
-
|
1623
|
-
|
1624
|
-
|
1625
|
-
|
1626
|
-
|
1627
|
-
|
1628
|
-
|
1629
|
-
|
1630
|
-
|
1631
|
-
|
1632
|
-
|
1633
|
-
|
1634
|
-
|
1635
|
-
|
1636
|
-
|
1637
|
-
|
1638
|
-
|
1639
|
-
|
1640
|
-
|
1641
|
-
|
1642
|
-
|
1643
|
-
|
1644
|
-
|
1645
|
-
|
1646
|
-
|
1647
|
-
|
1648
|
-
|
1649
|
-
|
1650
|
-
|
1651
|
-
|
1652
|
-
|
1653
|
-
|
1654
|
-
|
1655
|
-
|
1656
|
-
|
1657
|
-
|
1658
|
-
|
1659
|
-
|
1660
|
-
|
1661
|
-
|
1662
|
-
|
1663
|
-
|
1664
|
-
|
1665
|
-
|
1666
|
-
)
|
1667
|
-
|
1668
|
-
|
1669
|
-
if
|
1670
|
-
channel("----------")
|
1671
|
-
|
1672
|
-
|
1673
|
-
|
1674
|
-
|
1675
|
-
|
1676
|
-
|
1677
|
-
|
1678
|
-
|
1679
|
-
|
1680
|
-
|
1681
|
-
|
1682
|
-
|
1683
|
-
|
1684
|
-
|
1685
|
-
|
1686
|
-
"
|
1687
|
-
|
1688
|
-
|
1689
|
-
|
1690
|
-
|
1691
|
-
|
1692
|
-
|
1693
|
-
|
1694
|
-
|
1695
|
-
|
1696
|
-
|
1697
|
-
|
1698
|
-
|
1699
|
-
|
1700
|
-
|
1701
|
-
|
1702
|
-
|
1703
|
-
|
1704
|
-
|
1705
|
-
|
1706
|
-
|
1707
|
-
|
1708
|
-
|
1709
|
-
|
1710
|
-
|
1711
|
-
|
1712
|
-
|
1713
|
-
|
1714
|
-
|
1715
|
-
|
1716
|
-
|
1717
|
-
|
1718
|
-
|
1719
|
-
|
1720
|
-
|
1721
|
-
|
1722
|
-
|
1723
|
-
|
1724
|
-
|
1725
|
-
|
1726
|
-
|
1727
|
-
|
1728
|
-
|
1729
|
-
|
1730
|
-
|
1731
|
-
|
1732
|
-
|
1733
|
-
|
1734
|
-
|
1735
|
-
|
1736
|
-
|
1737
|
-
|
1738
|
-
|
1739
|
-
|
1740
|
-
|
1741
|
-
|
1742
|
-
|
1743
|
-
|
1744
|
-
|
1745
|
-
|
1746
|
-
|
1747
|
-
|
1748
|
-
|
1749
|
-
|
1750
|
-
|
1751
|
-
|
1752
|
-
|
1753
|
-
|
1754
|
-
|
1755
|
-
|
1756
|
-
|
1757
|
-
|
1758
|
-
|
1759
|
-
|
1760
|
-
|
1761
|
-
|
1762
|
-
|
1763
|
-
|
1764
|
-
|
1765
|
-
|
1766
|
-
|
1767
|
-
|
1768
|
-
|
1769
|
-
|
1770
|
-
|
1771
|
-
|
1772
|
-
|
1773
|
-
|
1774
|
-
|
1775
|
-
|
1776
|
-
|
1777
|
-
|
1778
|
-
|
1779
|
-
|
1780
|
-
|
1781
|
-
|
1782
|
-
|
1783
|
-
|
1784
|
-
|
1785
|
-
|
1786
|
-
|
1787
|
-
|
1788
|
-
|
1789
|
-
|
1790
|
-
|
1791
|
-
|
1792
|
-
|
1793
|
-
|
1794
|
-
|
1795
|
-
|
1796
|
-
|
1797
|
-
|
1798
|
-
|
1799
|
-
|
1800
|
-
|
1801
|
-
|
1802
|
-
|
1803
|
-
|
1804
|
-
|
1805
|
-
|
1806
|
-
|
1807
|
-
|
1808
|
-
|
1809
|
-
|
1810
|
-
|
1811
|
-
|
1812
|
-
|
1813
|
-
|
1814
|
-
|
1815
|
-
|
1816
|
-
|
1817
|
-
|
1818
|
-
|
1819
|
-
|
1820
|
-
|
1821
|
-
|
1822
|
-
|
1823
|
-
|
1824
|
-
|
1825
|
-
|
1826
|
-
|
1827
|
-
|
1828
|
-
|
1829
|
-
|
1830
|
-
|
1831
|
-
|
1832
|
-
|
1833
|
-
|
1834
|
-
|
1835
|
-
|
1836
|
-
|
1837
|
-
|
1838
|
-
|
1839
|
-
|
1840
|
-
|
1841
|
-
|
1842
|
-
|
1843
|
-
|
1844
|
-
|
1845
|
-
|
1846
|
-
|
1847
|
-
|
1848
|
-
|
1849
|
-
|
1850
|
-
|
1851
|
-
|
1852
|
-
|
1853
|
-
|
1854
|
-
|
1855
|
-
|
1856
|
-
|
1857
|
-
|
1858
|
-
|
1859
|
-
|
1860
|
-
|
1861
|
-
|
1862
|
-
|
1863
|
-
|
1864
|
-
|
1865
|
-
|
1866
|
-
|
1867
|
-
|
1868
|
-
|
1869
|
-
|
1870
|
-
|
1871
|
-
|
1872
|
-
|
1873
|
-
|
1874
|
-
|
1875
|
-
|
1876
|
-
|
1877
|
-
|
1878
|
-
|
1879
|
-
|
1880
|
-
|
1881
|
-
|
1882
|
-
|
1883
|
-
|
1884
|
-
|
1885
|
-
|
1886
|
-
|
1887
|
-
|
1888
|
-
|
1889
|
-
|
1890
|
-
|
1891
|
-
|
1892
|
-
|
1893
|
-
|
1894
|
-
|
1895
|
-
|
1896
|
-
|
1897
|
-
|
1898
|
-
|
1899
|
-
|
1900
|
-
|
1901
|
-
|
1902
|
-
|
1903
|
-
|
1904
|
-
|
1905
|
-
|
1906
|
-
|
1907
|
-
|
1908
|
-
|
1909
|
-
|
1910
|
-
|
1911
|
-
|
1912
|
-
|
1913
|
-
|
1914
|
-
|
1915
|
-
|
1916
|
-
|
1917
|
-
|
1918
|
-
|
1919
|
-
|
1920
|
-
|
1921
|
-
|
1922
|
-
|
1923
|
-
|
1924
|
-
|
1925
|
-
|
1926
|
-
|
1927
|
-
|
1928
|
-
|
1929
|
-
|
1930
|
-
|
1931
|
-
|
1932
|
-
|
1933
|
-
|
1934
|
-
|
1935
|
-
|
1936
|
-
|
1937
|
-
|
1938
|
-
|
1939
|
-
|
1940
|
-
|
1941
|
-
|
1942
|
-
|
1943
|
-
|
1944
|
-
|
1945
|
-
|
1946
|
-
|
1947
|
-
|
1948
|
-
|
1949
|
-
|
1950
|
-
|
1951
|
-
|
1952
|
-
|
1953
|
-
|
1954
|
-
|
1955
|
-
|
1956
|
-
|
1957
|
-
|
1958
|
-
channel(_("
|
1959
|
-
|
1960
|
-
|
1961
|
-
|
1962
|
-
|
1963
|
-
|
1964
|
-
|
1965
|
-
|
1966
|
-
|
1967
|
-
|
1968
|
-
|
1969
|
-
|
1970
|
-
|
1971
|
-
|
1972
|
-
|
1973
|
-
|
1974
|
-
|
1975
|
-
|
1976
|
-
|
1977
|
-
|
1978
|
-
|
1979
|
-
|
1980
|
-
|
1981
|
-
|
1982
|
-
|
1983
|
-
|
1984
|
-
|
1985
|
-
|
1986
|
-
|
1987
|
-
|
1988
|
-
|
1989
|
-
|
1990
|
-
|
1991
|
-
|
1992
|
-
|
1993
|
-
|
1994
|
-
|
1995
|
-
|
1996
|
-
|
1997
|
-
|
1998
|
-
|
1999
|
-
|
2000
|
-
|
2001
|
-
|
2002
|
-
|
2003
|
-
|
2004
|
-
|
2005
|
-
|
2006
|
-
|
2007
|
-
|
2008
|
-
|
2009
|
-
|
2010
|
-
|
2011
|
-
|
2012
|
-
|
2013
|
-
|
2014
|
-
|
2015
|
-
|
2016
|
-
|
2017
|
-
|
2018
|
-
|
2019
|
-
|
2020
|
-
|
2021
|
-
|
2022
|
-
|
2023
|
-
|
2024
|
-
|
2025
|
-
|
2026
|
-
|
2027
|
-
|
2028
|
-
|
2029
|
-
|
2030
|
-
|
2031
|
-
|
2032
|
-
|
2033
|
-
|
2034
|
-
|
2035
|
-
|
2036
|
-
|
2037
|
-
|
2038
|
-
|
2039
|
-
|
2040
|
-
|
2041
|
-
|
2042
|
-
|
2043
|
-
|
2044
|
-
|
2045
|
-
|
2046
|
-
|
2047
|
-
|
2048
|
-
|
2049
|
-
|
2050
|
-
|
2051
|
-
|
2052
|
-
|
2053
|
-
if
|
2054
|
-
|
2055
|
-
|
2056
|
-
|
2057
|
-
|
2058
|
-
|
2059
|
-
|
2060
|
-
|
2061
|
-
|
2062
|
-
|
2063
|
-
|
2064
|
-
|
2065
|
-
|
2066
|
-
|
2067
|
-
|
2068
|
-
|
2069
|
-
|
2070
|
-
|
2071
|
-
|
2072
|
-
|
2073
|
-
|
2074
|
-
|
2075
|
-
|
2076
|
-
|
2077
|
-
|
2078
|
-
|
2079
|
-
|
2080
|
-
|
2081
|
-
|
2082
|
-
|
2083
|
-
|
2084
|
-
|
2085
|
-
|
2086
|
-
|
2087
|
-
|
2088
|
-
|
2089
|
-
|
2090
|
-
|
2091
|
-
|
2092
|
-
|
1
|
+
"""
|
2
|
+
This module contains a collection of console commands that manage and implement the elements system within the application.
|
3
|
+
It provides functionalities for creating, modifying, and classifying various geometric shapes and elements.
|
4
|
+
|
5
|
+
Functions:
|
6
|
+
- plugin: Initializes the console commands for the elements system.
|
7
|
+
- init_commands: Sets up the console commands related to shapes and elements.
|
8
|
+
- element_circle: Creates a circle element at specified coordinates with a given radius.
|
9
|
+
- element_circle_r: Creates a circle element at the origin with a specified radius.
|
10
|
+
- element_ellipse: Creates an ellipse element with specified center and radii.
|
11
|
+
- element_rect: Draws a rectangle with optional rounded corners.
|
12
|
+
- element_line: Draws a line between two specified points.
|
13
|
+
- effect_remove: Removes effects from selected elements.
|
14
|
+
- effect_hatch: Adds a hatch effect to selected elements.
|
15
|
+
- effect_wobble: Adds a wobble effect to selected elements.
|
16
|
+
- element_text: Creates a text element with specified content and font size.
|
17
|
+
- element_text_anchor: Sets the text anchor for a text element.
|
18
|
+
- element_text_edit: Edits the text content of a text element.
|
19
|
+
- element_property_set: Sets a specified property to a new value for selected elements.
|
20
|
+
- recalc: Recalculates the bounds of selected elements.
|
21
|
+
- simplify_path: Simplifies the geometry of selected paths.
|
22
|
+
- create_pattern: Creates a pattern from selected elements.
|
23
|
+
- element_poly: Creates a polygon or polyline from specified points.
|
24
|
+
- element_pathd_info: Lists the path data of recognized paths.
|
25
|
+
- element_path: Creates a path element from SVG path syntax.
|
26
|
+
- element_stroke_width: Adjusts the stroke width of selected elements.
|
27
|
+
- element_cap: Sets the line cap style for selected paths.
|
28
|
+
- element_join: Sets the line join style for selected paths.
|
29
|
+
- element_rule: Sets the fill rule for selected paths.
|
30
|
+
- element_stroke: Sets the stroke color for selected elements.
|
31
|
+
- element_fill: Sets the fill color for selected elements.
|
32
|
+
- element_frame: Draws a frame around the currently selected elements.
|
33
|
+
- element_rotate: Rotates selected elements by a specified angle.
|
34
|
+
- element_scale: Scales selected elements by specified factors.
|
35
|
+
- element_area: Provides information about or changes the area of selected elements.
|
36
|
+
- element_translate: Translates selected elements by specified offsets.
|
37
|
+
- element_position: Sets the position of selected elements to specified coordinates.
|
38
|
+
- element_move_to_laser: Moves selected elements to the current position of the laser head.
|
39
|
+
- element_resize: Resizes selected elements to specified dimensions.
|
40
|
+
- element_matrix: Sets the transformation matrix for selected elements.
|
41
|
+
- reset: Resets affine transformations for selected elements.
|
42
|
+
- element_reify: Reifies affine transformations for selected elements.
|
43
|
+
- element_circ_arc_path: Converts paths to use circular arcs.
|
44
|
+
- element_classify: Classifies selected elements into operations.
|
45
|
+
- declassify: Declassifies selected elements.
|
46
|
+
|
47
|
+
"""
|
48
|
+
|
49
|
+
from math import sqrt
|
50
|
+
|
51
|
+
from meerk40t.core.node.node import Fillrule, Linecap, Linejoin, Node
|
52
|
+
from meerk40t.core.units import (
|
53
|
+
UNITS_PER_MM,
|
54
|
+
UNITS_PER_PIXEL,
|
55
|
+
UNITS_PER_POINT,
|
56
|
+
Angle,
|
57
|
+
Length,
|
58
|
+
)
|
59
|
+
from meerk40t.kernel import CommandSyntaxError
|
60
|
+
from meerk40t.svgelements import (
|
61
|
+
SVG_RULE_EVENODD,
|
62
|
+
SVG_RULE_NONZERO,
|
63
|
+
Color,
|
64
|
+
Matrix,
|
65
|
+
Path,
|
66
|
+
Polygon,
|
67
|
+
Polyline,
|
68
|
+
)
|
69
|
+
from meerk40t.tools.geomstr import Geomstr
|
70
|
+
|
71
|
+
|
72
|
+
def plugin(kernel, lifecycle=None):
|
73
|
+
_ = kernel.translation
|
74
|
+
if lifecycle == "postboot":
|
75
|
+
init_commands(kernel)
|
76
|
+
|
77
|
+
|
78
|
+
def init_commands(kernel):
|
79
|
+
self = kernel.elements
|
80
|
+
|
81
|
+
_ = kernel.translation
|
82
|
+
|
83
|
+
classify_new = self.post_classify
|
84
|
+
|
85
|
+
# ==========
|
86
|
+
# ELEMENT/SHAPE COMMANDS
|
87
|
+
# ==========
|
88
|
+
@self.console_argument("x_pos", type=Length)
|
89
|
+
@self.console_argument("y_pos", type=Length)
|
90
|
+
@self.console_argument("r_pos", type=Length)
|
91
|
+
@self.console_command(
|
92
|
+
"circle",
|
93
|
+
help=_("circle <x> <y> <r>"),
|
94
|
+
input_type=("elements", None),
|
95
|
+
output_type="elements",
|
96
|
+
all_arguments_required=True,
|
97
|
+
)
|
98
|
+
def element_circle(channel, _, x_pos, y_pos, r_pos, data=None, post=None, **kwargs):
|
99
|
+
node = self.elem_branch.add(
|
100
|
+
cx=float(x_pos),
|
101
|
+
cy=float(y_pos),
|
102
|
+
rx=float(r_pos),
|
103
|
+
ry=float(r_pos),
|
104
|
+
stroke=self.default_stroke,
|
105
|
+
stroke_width=self.default_strokewidth,
|
106
|
+
fill=self.default_fill,
|
107
|
+
type="elem ellipse",
|
108
|
+
)
|
109
|
+
self.set_emphasis([node])
|
110
|
+
node.focus()
|
111
|
+
if data is None:
|
112
|
+
data = list()
|
113
|
+
data.append(node)
|
114
|
+
# Newly created! Classification needed?
|
115
|
+
post.append(classify_new(data))
|
116
|
+
return "elements", data
|
117
|
+
|
118
|
+
@self.console_argument("r_pos", type=Length)
|
119
|
+
@self.console_command(
|
120
|
+
"circle_r",
|
121
|
+
help=_("circle_r <r>"),
|
122
|
+
input_type=("elements", None),
|
123
|
+
output_type="elements",
|
124
|
+
all_arguments_required=True,
|
125
|
+
)
|
126
|
+
def element_circle_r(channel, _, r_pos, data=None, post=None, **kwargs):
|
127
|
+
node = self.elem_branch.add(
|
128
|
+
cx=0,
|
129
|
+
cy=0,
|
130
|
+
rx=float(r_pos),
|
131
|
+
ry=float(r_pos),
|
132
|
+
stroke=self.default_stroke,
|
133
|
+
stroke_width=self.default_strokewidth,
|
134
|
+
fill=self.default_fill,
|
135
|
+
type="elem ellipse",
|
136
|
+
)
|
137
|
+
node.altered()
|
138
|
+
self.set_emphasis([node])
|
139
|
+
node.focus()
|
140
|
+
if data is None:
|
141
|
+
data = list()
|
142
|
+
data.append(node)
|
143
|
+
# Newly created! Classification needed?
|
144
|
+
post.append(classify_new(data))
|
145
|
+
return "elements", data
|
146
|
+
|
147
|
+
@self.console_argument("x_pos", type=Length)
|
148
|
+
@self.console_argument("y_pos", type=Length)
|
149
|
+
@self.console_argument("rx", type=Length)
|
150
|
+
@self.console_argument("ry", type=Length)
|
151
|
+
@self.console_command(
|
152
|
+
"ellipse",
|
153
|
+
help=_("ellipse <cx> <cy> <rx> <ry>"),
|
154
|
+
input_type=("elements", None),
|
155
|
+
output_type="elements",
|
156
|
+
all_arguments_required=True,
|
157
|
+
)
|
158
|
+
def element_ellipse(
|
159
|
+
channel, _, x_pos, y_pos, rx, ry, data=None, post=None, **kwargs
|
160
|
+
):
|
161
|
+
node = self.elem_branch.add(
|
162
|
+
cx=float(x_pos),
|
163
|
+
cy=float(y_pos),
|
164
|
+
rx=float(rx),
|
165
|
+
ry=float(ry),
|
166
|
+
stroke=self.default_stroke,
|
167
|
+
stroke_width=self.default_strokewidth,
|
168
|
+
fill=self.default_fill,
|
169
|
+
type="elem ellipse",
|
170
|
+
)
|
171
|
+
node.altered()
|
172
|
+
self.set_emphasis([node])
|
173
|
+
node.focus()
|
174
|
+
if data is None:
|
175
|
+
data = list()
|
176
|
+
data.append(node)
|
177
|
+
# Newly created! Classification needed?
|
178
|
+
post.append(classify_new(data))
|
179
|
+
return "elements", data
|
180
|
+
|
181
|
+
@self.console_argument("x_pos", type=Length, help=_("X-coordinate of center"))
|
182
|
+
@self.console_argument("y_pos", type=Length, help=_("Y-coordinate of center"))
|
183
|
+
@self.console_argument("rx", type=Length, help=_("Primary radius of ellipse"))
|
184
|
+
@self.console_argument("ry", type=Length, help=_("Secondary radius of ellipse (default equal to primary radius=circle)"))
|
185
|
+
@self.console_argument("start_angle", type=Angle, help=_("Start angle of arc (default 0°)"))
|
186
|
+
@self.console_argument("end_angle", type=Angle, help=_("End angle of arc (default 360°)"))
|
187
|
+
@self.console_option(
|
188
|
+
"rotation", "r", type=Angle, help=_("Rotation of arc")
|
189
|
+
)
|
190
|
+
@self.console_command(
|
191
|
+
"arc",
|
192
|
+
help=_("arc <cx> <cy> <rx> <ry> <start> <end>"),
|
193
|
+
input_type=("elements", None),
|
194
|
+
output_type="elements",
|
195
|
+
all_arguments_required=True,
|
196
|
+
)
|
197
|
+
def element_arc(
|
198
|
+
channel, _, x_pos, y_pos, rx, ry=None, start_angle=None, end_angle=None, rotation=None, data=None, post=None, **kwargs
|
199
|
+
):
|
200
|
+
if start_angle is None:
|
201
|
+
start_angle = Angle("0deg")
|
202
|
+
if end_angle is None:
|
203
|
+
end_angle = Angle("360deg")
|
204
|
+
if rotation is None:
|
205
|
+
rotation = Angle("0deg")
|
206
|
+
if ry is None:
|
207
|
+
ry = rx
|
208
|
+
rx_val = float(rx)
|
209
|
+
ry_val = float(ry)
|
210
|
+
cx = float(x_pos)
|
211
|
+
cy = float(y_pos)
|
212
|
+
geom = Geomstr()
|
213
|
+
geom.arc_as_cubics(
|
214
|
+
start_t=start_angle.radians,
|
215
|
+
end_t=end_angle.radians,
|
216
|
+
rx=rx_val,
|
217
|
+
ry=ry_val,
|
218
|
+
cx=cx,
|
219
|
+
cy=cy,
|
220
|
+
rotation=rotation.radians,
|
221
|
+
)
|
222
|
+
node = self.elem_branch.add(
|
223
|
+
label="Arc",
|
224
|
+
geometry=geom,
|
225
|
+
stroke=self.default_stroke,
|
226
|
+
stroke_width=self.default_strokewidth,
|
227
|
+
fill=self.default_fill,
|
228
|
+
type="elem path",
|
229
|
+
)
|
230
|
+
node.altered()
|
231
|
+
self.set_emphasis([node])
|
232
|
+
node.focus()
|
233
|
+
if data is None:
|
234
|
+
data = list()
|
235
|
+
data.append(node)
|
236
|
+
|
237
|
+
# Newly created! Classification needed?
|
238
|
+
post.append(classify_new(data))
|
239
|
+
return "elements", data
|
240
|
+
|
241
|
+
@self.console_argument(
|
242
|
+
"x_pos",
|
243
|
+
type=self.length_x,
|
244
|
+
help=_("x position for top left corner of rectangle."),
|
245
|
+
)
|
246
|
+
@self.console_argument(
|
247
|
+
"y_pos",
|
248
|
+
type=self.length_y,
|
249
|
+
help=_("y position for top left corner of rectangle."),
|
250
|
+
)
|
251
|
+
@self.console_argument(
|
252
|
+
"width", type=self.length_x, help=_("width of the rectangle.")
|
253
|
+
)
|
254
|
+
@self.console_argument(
|
255
|
+
"height", type=self.length_y, help=_("height of the rectangle.")
|
256
|
+
)
|
257
|
+
@self.console_option(
|
258
|
+
"rx", "x", type=self.length_x, help=_("rounded rx corner value.")
|
259
|
+
)
|
260
|
+
@self.console_option(
|
261
|
+
"ry", "y", type=self.length_y, help=_("rounded ry corner value.")
|
262
|
+
)
|
263
|
+
@self.console_command(
|
264
|
+
"rect",
|
265
|
+
help=_("adds rectangle to scene"),
|
266
|
+
input_type=("elements", None),
|
267
|
+
output_type="elements",
|
268
|
+
all_arguments_required=True,
|
269
|
+
)
|
270
|
+
def element_rect(
|
271
|
+
channel,
|
272
|
+
_,
|
273
|
+
x_pos,
|
274
|
+
y_pos,
|
275
|
+
width,
|
276
|
+
height,
|
277
|
+
rx=None,
|
278
|
+
ry=None,
|
279
|
+
data=None,
|
280
|
+
post=None,
|
281
|
+
**kwargs,
|
282
|
+
):
|
283
|
+
"""
|
284
|
+
Draws a svg rectangle with optional rounded corners.
|
285
|
+
"""
|
286
|
+
node = self.elem_branch.add(
|
287
|
+
x=x_pos,
|
288
|
+
y=y_pos,
|
289
|
+
width=width,
|
290
|
+
height=height,
|
291
|
+
rx=rx,
|
292
|
+
ry=ry,
|
293
|
+
stroke=self.default_stroke,
|
294
|
+
stroke_width=self.default_strokewidth,
|
295
|
+
fill=self.default_fill,
|
296
|
+
type="elem rect",
|
297
|
+
)
|
298
|
+
self.set_emphasis([node])
|
299
|
+
node.focus()
|
300
|
+
if data is None:
|
301
|
+
data = list()
|
302
|
+
data.append(node)
|
303
|
+
# Newly created! Classification needed?
|
304
|
+
post.append(classify_new(data))
|
305
|
+
return "elements", data
|
306
|
+
|
307
|
+
@self.console_argument("x0", type=self.length_x, help=_("start x position"))
|
308
|
+
@self.console_argument("y0", type=self.length_y, help=_("start y position"))
|
309
|
+
@self.console_argument("x1", type=self.length_x, help=_("end x position"))
|
310
|
+
@self.console_argument("y1", type=self.length_y, help=_("end y position"))
|
311
|
+
@self.console_command(
|
312
|
+
"line",
|
313
|
+
help=_("adds line to scene"),
|
314
|
+
input_type=("elements", None),
|
315
|
+
output_type="elements",
|
316
|
+
all_arguments_required=True,
|
317
|
+
)
|
318
|
+
def element_line(command, x0, y0, x1, y1, data=None, post=None, **kwargs):
|
319
|
+
"""
|
320
|
+
Draws a svg line in the scene.
|
321
|
+
"""
|
322
|
+
node = self.elem_branch.add(
|
323
|
+
x1=x0,
|
324
|
+
y1=y0,
|
325
|
+
x2=x1,
|
326
|
+
y2=y1,
|
327
|
+
stroke=self.default_stroke,
|
328
|
+
stroke_width=self.default_strokewidth,
|
329
|
+
type="elem line",
|
330
|
+
)
|
331
|
+
node.altered()
|
332
|
+
self.set_emphasis([node])
|
333
|
+
node.focus()
|
334
|
+
if data is None:
|
335
|
+
data = list()
|
336
|
+
data.append(node)
|
337
|
+
# Newly created! Classification needed?
|
338
|
+
post.append(classify_new(data))
|
339
|
+
return "elements", data
|
340
|
+
|
341
|
+
@self.console_command(
|
342
|
+
"effect-remove",
|
343
|
+
help=_("remove effects from element"),
|
344
|
+
input_type=(None, "elements"),
|
345
|
+
)
|
346
|
+
def effect_remove(
|
347
|
+
command,
|
348
|
+
channel,
|
349
|
+
_,
|
350
|
+
data=None,
|
351
|
+
post=None,
|
352
|
+
**kwargs,
|
353
|
+
):
|
354
|
+
if data is None:
|
355
|
+
data = list(self.elems(emphasized=True))
|
356
|
+
|
357
|
+
if len(data) == 0:
|
358
|
+
return
|
359
|
+
for node in data:
|
360
|
+
eparent = node.parent
|
361
|
+
nparent = eparent
|
362
|
+
while True:
|
363
|
+
if nparent.type.startswith("effect"):
|
364
|
+
break
|
365
|
+
if nparent.parent is None:
|
366
|
+
nparent = None
|
367
|
+
break
|
368
|
+
if nparent.parent is self.elem_branch:
|
369
|
+
nparent = None
|
370
|
+
break
|
371
|
+
nparent = nparent.parent
|
372
|
+
if nparent is None:
|
373
|
+
continue
|
374
|
+
was_emphasized = node.emphasized
|
375
|
+
node._parent = None # Otherwise add_node will fail below
|
376
|
+
try:
|
377
|
+
idx = eparent._children.index(node)
|
378
|
+
if idx >= 0:
|
379
|
+
eparent._children.pop(idx)
|
380
|
+
except IndexError:
|
381
|
+
pass
|
382
|
+
nparent.parent.add_node(node)
|
383
|
+
if len(nparent.children) == 0:
|
384
|
+
nparent.remove_node()
|
385
|
+
else:
|
386
|
+
nparent.altered()
|
387
|
+
node.emphasized = was_emphasized
|
388
|
+
self.signal("refresh_scene", "Scene")
|
389
|
+
|
390
|
+
@self.console_option("etype", "e", type=str, default="scanline")
|
391
|
+
@self.console_option("distance", "d", type=Length, default=None)
|
392
|
+
@self.console_option("angle", "a", type=Angle, default=None)
|
393
|
+
@self.console_option("angle_delta", "b", type=Angle, default=None)
|
394
|
+
@self.console_command(
|
395
|
+
"effect-hatch",
|
396
|
+
help=_("adds hatch-effect to scene"),
|
397
|
+
input_type=(None, "elements"),
|
398
|
+
)
|
399
|
+
def effect_hatch(
|
400
|
+
command,
|
401
|
+
channel,
|
402
|
+
_,
|
403
|
+
data=None,
|
404
|
+
etype=None,
|
405
|
+
angle=None,
|
406
|
+
angle_delta=None,
|
407
|
+
distance=None,
|
408
|
+
post=None,
|
409
|
+
**kwargs,
|
410
|
+
):
|
411
|
+
"""
|
412
|
+
Add an effect hatch object
|
413
|
+
"""
|
414
|
+
|
415
|
+
if data is None:
|
416
|
+
data = list(self.elems(emphasized=True))
|
417
|
+
if len(data) == 0:
|
418
|
+
channel(_("No selected elements."))
|
419
|
+
return
|
420
|
+
|
421
|
+
if distance is None and hasattr(self.device, "effect_hatch_default_distance"):
|
422
|
+
distance = getattr(self.device, "effect_hatch_default_distance")
|
423
|
+
elif distance is None:
|
424
|
+
distance = "1mm"
|
425
|
+
|
426
|
+
if angle is None and hasattr(self.device, "effect_hatch_default_angle"):
|
427
|
+
angle = Angle(getattr(self.device, "effect_hatch_default_angle"))
|
428
|
+
elif angle is None:
|
429
|
+
angle = Angle("0deg")
|
430
|
+
|
431
|
+
if angle_delta is None and hasattr(
|
432
|
+
self.device, "effect_hatch_default_angle_delta"
|
433
|
+
):
|
434
|
+
angle_delta = Angle(
|
435
|
+
getattr(self.device, "effect_hatch_default_angle_delta")
|
436
|
+
)
|
437
|
+
elif angle_delta is None:
|
438
|
+
angle_delta = Angle("0deg")
|
439
|
+
|
440
|
+
if etype is None:
|
441
|
+
etype = "scanline"
|
442
|
+
first_node = data[0]
|
443
|
+
|
444
|
+
node = first_node.parent.add(
|
445
|
+
type="effect hatch",
|
446
|
+
label="Hatch Effect",
|
447
|
+
hatch_type=etype,
|
448
|
+
hatch_angle=angle.radians,
|
449
|
+
hatch_angle_delta=angle_delta.radians,
|
450
|
+
hatch_distance=distance,
|
451
|
+
)
|
452
|
+
for n in data:
|
453
|
+
node.append_child(n)
|
454
|
+
|
455
|
+
# Newly created! Classification needed?
|
456
|
+
post.append(classify_new([node]))
|
457
|
+
|
458
|
+
self.set_emphasis([node])
|
459
|
+
node.focus()
|
460
|
+
|
461
|
+
@self.console_option("wtype", "w", type=str, default="circle")
|
462
|
+
@self.console_option("radius", "r", type=Length, default=None)
|
463
|
+
@self.console_option("interval", "i", type=Length, default=None)
|
464
|
+
@self.console_command(
|
465
|
+
"effect-wobble",
|
466
|
+
help=_("adds wobble-effect to selected elements"),
|
467
|
+
input_type=(None, "elements"),
|
468
|
+
)
|
469
|
+
def effect_wobble(
|
470
|
+
command,
|
471
|
+
channel,
|
472
|
+
_,
|
473
|
+
data=None,
|
474
|
+
wtype=None,
|
475
|
+
radius=None,
|
476
|
+
interval=None,
|
477
|
+
post=None,
|
478
|
+
**kwargs,
|
479
|
+
):
|
480
|
+
"""
|
481
|
+
Add an effect hatch object
|
482
|
+
"""
|
483
|
+
if data is None:
|
484
|
+
data = list(self.elems(emphasized=True))
|
485
|
+
if len(data) == 0:
|
486
|
+
return
|
487
|
+
if wtype is None:
|
488
|
+
wtype = "circle"
|
489
|
+
|
490
|
+
if radius is None and hasattr(self.device, "effect_wobble_default_radius"):
|
491
|
+
radius = getattr(self.device, "effect_wobble_default_radius")
|
492
|
+
elif radius is None:
|
493
|
+
radius = "0.5mm"
|
494
|
+
|
495
|
+
if interval is None and hasattr(self.device, "effect_wobble_default_interval"):
|
496
|
+
interval = getattr(self.device, "effect_wobble_default_interval")
|
497
|
+
elif interval is None:
|
498
|
+
interval = "0.5mm"
|
499
|
+
|
500
|
+
wtype = wtype.lower()
|
501
|
+
allowed = list(self.kernel.root.match("wobble", suffix=True))
|
502
|
+
if wtype not in allowed:
|
503
|
+
channel(f"Invalid wobble type, allowed: {','.join(allowed)}")
|
504
|
+
return
|
505
|
+
try:
|
506
|
+
rlen = Length(radius)
|
507
|
+
except ValueError:
|
508
|
+
channel("Invalid value for radius")
|
509
|
+
return
|
510
|
+
try:
|
511
|
+
ilen = Length(interval)
|
512
|
+
except ValueError:
|
513
|
+
channel("Invalid value for interval")
|
514
|
+
return
|
515
|
+
first_node = data[0]
|
516
|
+
node = first_node.parent.add(
|
517
|
+
type="effect wobble",
|
518
|
+
label="Wobble Effect",
|
519
|
+
wobble_type=wtype,
|
520
|
+
wobble_radius=rlen.length_mm,
|
521
|
+
wobble_interval=ilen.length_mm,
|
522
|
+
)
|
523
|
+
for n in data:
|
524
|
+
node.append_child(n)
|
525
|
+
|
526
|
+
# Newly created! Classification needed?
|
527
|
+
post.append(classify_new([node]))
|
528
|
+
|
529
|
+
self.set_emphasis([node])
|
530
|
+
node.focus()
|
531
|
+
|
532
|
+
@self.console_option(
|
533
|
+
"size", "s", type=float, default=16, help=_("font size to for object")
|
534
|
+
)
|
535
|
+
@self.console_argument("text", type=str, help=_("quoted string of text"))
|
536
|
+
@self.console_command(
|
537
|
+
"text",
|
538
|
+
help=_("text <text>"),
|
539
|
+
input_type=(None, "elements"),
|
540
|
+
output_type="elements",
|
541
|
+
)
|
542
|
+
def element_text(
|
543
|
+
command, channel, _, data=None, text=None, size=None, post=None, **kwargs
|
544
|
+
):
|
545
|
+
if text is None:
|
546
|
+
channel(_("No text specified"))
|
547
|
+
return
|
548
|
+
node = self.elem_branch.add(
|
549
|
+
text=text, matrix=Matrix(f"scale({UNITS_PER_PIXEL})"), type="elem text"
|
550
|
+
)
|
551
|
+
node.font_size = size
|
552
|
+
node.stroke = self.default_stroke
|
553
|
+
node.stroke_width = self.default_strokewidth
|
554
|
+
node.fill = self.default_fill
|
555
|
+
node.altered()
|
556
|
+
self.set_emphasis([node])
|
557
|
+
node.focus()
|
558
|
+
if data is None:
|
559
|
+
data = list()
|
560
|
+
data.append(node)
|
561
|
+
# Newly created! Classification needed?
|
562
|
+
post.append(classify_new(data))
|
563
|
+
return "elements", data
|
564
|
+
|
565
|
+
@self.console_argument(
|
566
|
+
"anchor", type=str, default="start", help=_("set text anchor")
|
567
|
+
)
|
568
|
+
@self.console_command(
|
569
|
+
"text-anchor",
|
570
|
+
help=_("set text object text-anchor; start, middle, end"),
|
571
|
+
input_type=(
|
572
|
+
None,
|
573
|
+
"elements",
|
574
|
+
),
|
575
|
+
output_type="elements",
|
576
|
+
)
|
577
|
+
def element_text_anchor(command, channel, _, data, anchor=None, **kwargs):
|
578
|
+
if anchor not in ("start", "middle", "end"):
|
579
|
+
raise CommandSyntaxError(
|
580
|
+
_("Only 'start', 'middle', and 'end' are valid anchors.")
|
581
|
+
)
|
582
|
+
if data is None:
|
583
|
+
data = list(self.elems(emphasized=True))
|
584
|
+
if len(data) == 0:
|
585
|
+
channel(_("No selected elements."))
|
586
|
+
return
|
587
|
+
for e in data:
|
588
|
+
if hasattr(e, "can_modify") and not e.can_modify:
|
589
|
+
channel(_("Can't modify a locked element: {name}").format(name=str(e)))
|
590
|
+
continue
|
591
|
+
if e.type == "elem text":
|
592
|
+
old_anchor = e.anchor
|
593
|
+
e.anchor = anchor
|
594
|
+
channel(f"Node {e} anchor changed from {old_anchor} to {anchor}")
|
595
|
+
|
596
|
+
e.altered()
|
597
|
+
return "elements", data
|
598
|
+
|
599
|
+
@self.console_argument("new_text", type=str, help=_("set new text contents"))
|
600
|
+
@self.console_command(
|
601
|
+
"text-edit",
|
602
|
+
help=_("set text object text to new text"),
|
603
|
+
input_type=(
|
604
|
+
None,
|
605
|
+
"elements",
|
606
|
+
),
|
607
|
+
output_type="elements",
|
608
|
+
all_arguments_required=True,
|
609
|
+
)
|
610
|
+
def element_text_edit(command, channel, _, data, new_text=None, **kwargs):
|
611
|
+
if data is None:
|
612
|
+
data = list(self.elems(emphasized=True))
|
613
|
+
if len(data) == 0:
|
614
|
+
channel(_("No selected elements."))
|
615
|
+
return
|
616
|
+
for e in data:
|
617
|
+
if hasattr(e, "can_modify") and not e.can_modify:
|
618
|
+
channel(_("Can't modify a locked element: {name}").format(name=str(e)))
|
619
|
+
continue
|
620
|
+
if e.type == "elem text":
|
621
|
+
old_text = e.text
|
622
|
+
e.text = new_text
|
623
|
+
elif hasattr(e, "mktext"):
|
624
|
+
old_text = e.mktext
|
625
|
+
e.mktext = new_text
|
626
|
+
for property_op in self.kernel.lookup_all("path_updater/.*"):
|
627
|
+
property_op(self.kernel.root, e)
|
628
|
+
else:
|
629
|
+
continue
|
630
|
+
channel(f"Node {e} anchor changed from {old_text} to {new_text}")
|
631
|
+
e.altered()
|
632
|
+
|
633
|
+
return "elements", data
|
634
|
+
|
635
|
+
def calculate_text_bounds(data):
|
636
|
+
"""
|
637
|
+
A render operation will use the LaserRender class
|
638
|
+
and will re-calculate the element bounds
|
639
|
+
@param data:
|
640
|
+
@return:
|
641
|
+
"""
|
642
|
+
make_raster = self.lookup("render-op/make_raster")
|
643
|
+
if not make_raster:
|
644
|
+
# No renderer is registered to perform render.
|
645
|
+
return
|
646
|
+
for e in data:
|
647
|
+
e.set_dirty_bounds()
|
648
|
+
# arbitrary bounds...
|
649
|
+
bounds = (0, 0, float(Length("5cm")), float(Length("5cm")))
|
650
|
+
try:
|
651
|
+
image = make_raster(
|
652
|
+
data,
|
653
|
+
bounds=bounds,
|
654
|
+
width=500,
|
655
|
+
height=500,
|
656
|
+
)
|
657
|
+
except Exception:
|
658
|
+
pass # Not relevant...
|
659
|
+
|
660
|
+
@self.console_argument("prop", type=str, help=_("property to set"))
|
661
|
+
@self.console_argument("new_value", type=str, help=_("new property value"))
|
662
|
+
@self.console_command(
|
663
|
+
"property-set",
|
664
|
+
help=_("set property to new value"),
|
665
|
+
input_type=(
|
666
|
+
None,
|
667
|
+
"elements",
|
668
|
+
),
|
669
|
+
output_type="elements",
|
670
|
+
all_arguments_required=True,
|
671
|
+
)
|
672
|
+
def element_property_set(
|
673
|
+
command, channel, _, data, post=None, prop=None, new_value=None, **kwargs
|
674
|
+
):
|
675
|
+
"""
|
676
|
+
Generic node manipulation routine, use with care
|
677
|
+
"""
|
678
|
+
if data is None:
|
679
|
+
data = list(self.elems(emphasized=True))
|
680
|
+
if len(data) == 0:
|
681
|
+
channel(_("No selected elements."))
|
682
|
+
return
|
683
|
+
if prop is None or (prop == "?" and new_value=="?"):
|
684
|
+
channel(_("You need to provide the property to set."))
|
685
|
+
if prop == "?":
|
686
|
+
identified = []
|
687
|
+
for op in data:
|
688
|
+
if op.type in identified:
|
689
|
+
continue
|
690
|
+
identified.append(op.type)
|
691
|
+
prop_str = f"{op.type} has the following properties: "
|
692
|
+
for d in op.__dict__:
|
693
|
+
if d.startswith("_"):
|
694
|
+
continue
|
695
|
+
prop_str = f"{prop_str}, {d}"
|
696
|
+
channel(prop_str)
|
697
|
+
channel ("Be careful what you do - this is a failsafe method to crash MeerK40t, burn down your house or whatever...")
|
698
|
+
return
|
699
|
+
classify_required = False
|
700
|
+
prop = prop.lower()
|
701
|
+
if len(new_value) == 0:
|
702
|
+
new_value = None
|
703
|
+
if prop in ("fill", "stroke") and self.classify_on_color:
|
704
|
+
classify_required = True
|
705
|
+
# Let's distinguish a couple of special cases...
|
706
|
+
prevalidated = False
|
707
|
+
if prop in ("fill", "stroke", "color"):
|
708
|
+
if new_value is not None:
|
709
|
+
if new_value.lower() == "none":
|
710
|
+
# The text...
|
711
|
+
new_value = None
|
712
|
+
try:
|
713
|
+
new_value = Color(new_value)
|
714
|
+
prevalidated = True
|
715
|
+
except ValueError:
|
716
|
+
channel(_("Invalid color value: {value}").format(value=new_value))
|
717
|
+
return
|
718
|
+
elif prop in ("x", "y", "width", "height", "stroke_width"):
|
719
|
+
if new_value is None:
|
720
|
+
channel(_("Invalid length: {value}").format(value=new_value))
|
721
|
+
return
|
722
|
+
else:
|
723
|
+
try:
|
724
|
+
new_value = float(Length(new_value))
|
725
|
+
prevalidated = True
|
726
|
+
except ValueError:
|
727
|
+
channel(_("Invalid length: {value}").format(value=new_value))
|
728
|
+
return
|
729
|
+
|
730
|
+
changed = []
|
731
|
+
text_elems = []
|
732
|
+
|
733
|
+
if prop == "lock":
|
734
|
+
if new_value.lower() in ("1", "true"):
|
735
|
+
setval = True
|
736
|
+
elif new_value.lower() in ("0", "false"):
|
737
|
+
setval = False
|
738
|
+
else:
|
739
|
+
try:
|
740
|
+
setval = bool(new_value)
|
741
|
+
except ValueError:
|
742
|
+
channel(
|
743
|
+
_("Can't set '{val}' for {field}.").format(
|
744
|
+
val=new_value, field=prop
|
745
|
+
)
|
746
|
+
)
|
747
|
+
return
|
748
|
+
# print (f"Will set lock to {setval} ({new_value})")
|
749
|
+
for e in data:
|
750
|
+
if hasattr(e, "lock"):
|
751
|
+
e.lock = setval
|
752
|
+
changed.append(e)
|
753
|
+
else:
|
754
|
+
for e in data:
|
755
|
+
# dbg = ""
|
756
|
+
# if hasattr(e, "bounds"):
|
757
|
+
# bb = e.bounds
|
758
|
+
# dbg += (
|
759
|
+
# f"x:{Length(bb[0], digits=2).length_mm}, "
|
760
|
+
# + f"y:{Length(bb[1], digits=2).length_mm}, "
|
761
|
+
# + f"w:{Length(bb[2]-bb[0], digits=2).length_mm}, "
|
762
|
+
# + f"h:{Length(bb[3]-bb[1], digits=2).length_mm}, "
|
763
|
+
# )
|
764
|
+
# dbg += f"{prop}:{str(getattr(e, prop)) if hasattr(e, prop) else '--'}"
|
765
|
+
# print (f"Before: {dbg}")
|
766
|
+
if prop in ("x", "y"):
|
767
|
+
if not e.can_move(self.lock_allows_move):
|
768
|
+
channel(
|
769
|
+
_("Element can not be moved: {name}").format(name=str(e))
|
770
|
+
)
|
771
|
+
continue
|
772
|
+
# We need to adjust the matrix
|
773
|
+
if hasattr(e, "bounds") and hasattr(e, "matrix"):
|
774
|
+
dx = 0
|
775
|
+
dy = 0
|
776
|
+
bb = e.bounds
|
777
|
+
if prop == "x":
|
778
|
+
dx = new_value - bb[0]
|
779
|
+
else:
|
780
|
+
dy = new_value - bb[1]
|
781
|
+
e.matrix.post_translate(dx, dy)
|
782
|
+
else:
|
783
|
+
channel(
|
784
|
+
_("Element has no matrix to modify: {name}").format(
|
785
|
+
name=str(e)
|
786
|
+
)
|
787
|
+
)
|
788
|
+
continue
|
789
|
+
elif prop in ("width", "height"):
|
790
|
+
if new_value == 0:
|
791
|
+
channel(_("Can't set {field} to zero").format(field=prop))
|
792
|
+
continue
|
793
|
+
if hasattr(e, "can_scale") and not e.can_scale:
|
794
|
+
channel(
|
795
|
+
_("Element can not be scaled: {name}").format(name=str(e))
|
796
|
+
)
|
797
|
+
continue
|
798
|
+
if hasattr(e, "matrix") and hasattr(e, "bounds"):
|
799
|
+
bb = e.bounds
|
800
|
+
sx = 1.0
|
801
|
+
sy = 1.0
|
802
|
+
wd = bb[2] - bb[0]
|
803
|
+
ht = bb[3] - bb[1]
|
804
|
+
if prop == "width":
|
805
|
+
sx = new_value / wd
|
806
|
+
else:
|
807
|
+
sy = new_value / ht
|
808
|
+
e.matrix.post_scale(sx, sy)
|
809
|
+
else:
|
810
|
+
channel(
|
811
|
+
_("Element has no matrix to modify: {name}").format(
|
812
|
+
name=str(e)
|
813
|
+
)
|
814
|
+
)
|
815
|
+
continue
|
816
|
+
elif hasattr(e, prop):
|
817
|
+
if hasattr(e, "can_modify") and not e.can_modify:
|
818
|
+
channel(
|
819
|
+
_("Can't modify a locked element: {name}").format(
|
820
|
+
name=str(e)
|
821
|
+
)
|
822
|
+
)
|
823
|
+
continue
|
824
|
+
try:
|
825
|
+
oldval = getattr(e, prop)
|
826
|
+
if prevalidated:
|
827
|
+
setval = new_value
|
828
|
+
else:
|
829
|
+
if oldval is not None:
|
830
|
+
proptype = type(oldval)
|
831
|
+
setval = proptype(new_value)
|
832
|
+
if isinstance(oldval, bool):
|
833
|
+
if new_value.lower() in ("1", "true"):
|
834
|
+
setval = True
|
835
|
+
elif new_value.lower() in ("0", "false"):
|
836
|
+
setval = False
|
837
|
+
else:
|
838
|
+
setval = new_value
|
839
|
+
setattr(e, prop, setval)
|
840
|
+
except TypeError:
|
841
|
+
channel(
|
842
|
+
_(
|
843
|
+
"Can't set '{val}' for {field} (invalid type, old={oldval})."
|
844
|
+
).format(val=new_value, field=prop, oldval=oldval)
|
845
|
+
)
|
846
|
+
except ValueError:
|
847
|
+
channel(
|
848
|
+
_(
|
849
|
+
"Can't set '{val}' for {field} (invalid value, old={oldval})."
|
850
|
+
).format(val=new_value, field=prop, oldval=oldval)
|
851
|
+
)
|
852
|
+
except AttributeError:
|
853
|
+
channel(
|
854
|
+
_(
|
855
|
+
"Can't set '{val}' for {field} (incompatible attribute, old={oldval})."
|
856
|
+
).format(val=new_value, field=prop, oldval=oldval)
|
857
|
+
)
|
858
|
+
|
859
|
+
if "font" in prop:
|
860
|
+
# We need to force a recalculation of the underlying wxfont property
|
861
|
+
if hasattr(e, "wxfont"):
|
862
|
+
delattr(e, "wxfont")
|
863
|
+
text_elems.append(e)
|
864
|
+
if prop in ("mktext", "mkfont"):
|
865
|
+
for property_op in self.kernel.lookup_all("path_updater/.*"):
|
866
|
+
property_op(self.kernel.root, e)
|
867
|
+
if prop in (
|
868
|
+
"dpi",
|
869
|
+
"dither",
|
870
|
+
"dither_type",
|
871
|
+
"invert",
|
872
|
+
"red",
|
873
|
+
"green",
|
874
|
+
"blue",
|
875
|
+
"lightness",
|
876
|
+
):
|
877
|
+
# Images require some recalculation too
|
878
|
+
self.do_image_update(e)
|
879
|
+
|
880
|
+
else:
|
881
|
+
channel(
|
882
|
+
_("Element {name} has no property {field}").format(
|
883
|
+
name=str(e), field=prop
|
884
|
+
)
|
885
|
+
)
|
886
|
+
continue
|
887
|
+
e.altered()
|
888
|
+
# dbg = ""
|
889
|
+
# if hasattr(e, "bounds"):
|
890
|
+
# bb = e.bounds
|
891
|
+
# dbg += (
|
892
|
+
# f"x:{Length(bb[0], digits=2).length_mm}, "
|
893
|
+
# + f"y:{Length(bb[1], digits=2).length_mm}, "
|
894
|
+
# + f"w:{Length(bb[2]-bb[0], digits=2).length_mm}, "
|
895
|
+
# + f"h:{Length(bb[3]-bb[1], digits=2).length_mm}, "
|
896
|
+
# )
|
897
|
+
# dbg += f"{prop}:{str(getattr(e, prop)) if hasattr(e, prop) else '--'}"
|
898
|
+
# print (f"After: {dbg}")
|
899
|
+
changed.append(e)
|
900
|
+
if len(changed) > 0:
|
901
|
+
if len(text_elems) > 0:
|
902
|
+
# Recalculate bounds
|
903
|
+
calculate_text_bounds(text_elems)
|
904
|
+
self.signal("refresh_scene", "Scene")
|
905
|
+
self.signal("element_property_update", changed)
|
906
|
+
self.validate_selected_area()
|
907
|
+
if classify_required:
|
908
|
+
post.append(classify_new(changed))
|
909
|
+
|
910
|
+
return "elements", data
|
911
|
+
|
912
|
+
@self.console_argument("prop", type=str, help=_("property to set"))
|
913
|
+
@self.console_argument("new_value", type=str, help=_("new property value"))
|
914
|
+
@self.console_command(
|
915
|
+
"op-property-set",
|
916
|
+
help=_("set operation property to new value"),
|
917
|
+
input_type=(
|
918
|
+
None,
|
919
|
+
"ops",
|
920
|
+
),
|
921
|
+
output_type="ops",
|
922
|
+
all_arguments_required=True,
|
923
|
+
)
|
924
|
+
def operation_property_set(
|
925
|
+
command, channel, _, data, post=None, prop=None, new_value=None, **kwargs
|
926
|
+
):
|
927
|
+
"""
|
928
|
+
Generic node manipulation routine, use with care
|
929
|
+
"""
|
930
|
+
if data is None:
|
931
|
+
data = list(self.ops(selected=True))
|
932
|
+
if not data:
|
933
|
+
channel(_("No selected operations."))
|
934
|
+
return
|
935
|
+
if prop is None or (prop == "?" and new_value=="?"):
|
936
|
+
channel(_("You need to provide the property to set."))
|
937
|
+
if prop == "?":
|
938
|
+
identified = []
|
939
|
+
for op in data:
|
940
|
+
if op.type in identified:
|
941
|
+
continue
|
942
|
+
identified.append(op.type)
|
943
|
+
prop_str = f"{op.type} has the following properties: "
|
944
|
+
for d in op.__dict__:
|
945
|
+
if d.startswith("_"):
|
946
|
+
continue
|
947
|
+
prop_str = f"{prop_str}, {d}"
|
948
|
+
channel(prop_str)
|
949
|
+
channel ("Be careful what you do - this is a failsafe method to crash MeerK40t, burn down your house or whatever...")
|
950
|
+
return
|
951
|
+
prop = prop.lower()
|
952
|
+
if len(new_value) == 0:
|
953
|
+
new_value = None
|
954
|
+
# Let's distinguish a couple of special cases...
|
955
|
+
prevalidated = False
|
956
|
+
if prop == "color":
|
957
|
+
if new_value is not None:
|
958
|
+
if new_value.lower() == "none":
|
959
|
+
# The text...
|
960
|
+
new_value = None
|
961
|
+
try:
|
962
|
+
new_value = Color(new_value)
|
963
|
+
prevalidated = True
|
964
|
+
except ValueError:
|
965
|
+
channel(_("Invalid color value: {value}").format(value=new_value))
|
966
|
+
return
|
967
|
+
if prop in ("power", "speed", "dpi"):
|
968
|
+
try:
|
969
|
+
testval = float(new_value)
|
970
|
+
except ValueError:
|
971
|
+
channel(f"Invalid value: {new_value}")
|
972
|
+
return
|
973
|
+
if testval < 0:
|
974
|
+
channel(f"Invalid value: {new_value}")
|
975
|
+
return
|
976
|
+
if prop == "power" and testval > 1000:
|
977
|
+
channel(f"Invalid value: {new_value}")
|
978
|
+
return
|
979
|
+
new_value = testval
|
980
|
+
prevalidated = True
|
981
|
+
|
982
|
+
|
983
|
+
changed = []
|
984
|
+
|
985
|
+
for e in data:
|
986
|
+
if hasattr(e, prop):
|
987
|
+
if hasattr(e, "can_modify") and not e.can_modify:
|
988
|
+
channel(
|
989
|
+
_("Can't modify a locked element: {name}").format(
|
990
|
+
name=str(e)
|
991
|
+
)
|
992
|
+
)
|
993
|
+
continue
|
994
|
+
try:
|
995
|
+
oldval = getattr(e, prop)
|
996
|
+
if prevalidated:
|
997
|
+
setval = new_value
|
998
|
+
else:
|
999
|
+
if oldval is not None:
|
1000
|
+
proptype = type(oldval)
|
1001
|
+
setval = proptype(new_value)
|
1002
|
+
if isinstance(oldval, bool):
|
1003
|
+
if new_value.lower() in ("1", "true"):
|
1004
|
+
setval = True
|
1005
|
+
elif new_value.lower() in ("0", "false"):
|
1006
|
+
setval = False
|
1007
|
+
else:
|
1008
|
+
setval = new_value
|
1009
|
+
setattr(e, prop, setval)
|
1010
|
+
except TypeError:
|
1011
|
+
channel(
|
1012
|
+
_(
|
1013
|
+
"Can't set '{val}' for {field} (invalid type, old={oldval})."
|
1014
|
+
).format(val=new_value, field=prop, oldval=oldval)
|
1015
|
+
)
|
1016
|
+
except ValueError:
|
1017
|
+
channel(
|
1018
|
+
_(
|
1019
|
+
"Can't set '{val}' for {field} (invalid value, old={oldval})."
|
1020
|
+
).format(val=new_value, field=prop, oldval=oldval)
|
1021
|
+
)
|
1022
|
+
except AttributeError:
|
1023
|
+
channel(
|
1024
|
+
_(
|
1025
|
+
"Can't set '{val}' for {field} (incompatible attribute, old={oldval})."
|
1026
|
+
).format(val=new_value, field=prop, oldval=oldval)
|
1027
|
+
)
|
1028
|
+
|
1029
|
+
|
1030
|
+
else:
|
1031
|
+
channel(
|
1032
|
+
_("Operation {name} has no property {field}").format(
|
1033
|
+
name=str(e), field=prop
|
1034
|
+
)
|
1035
|
+
)
|
1036
|
+
continue
|
1037
|
+
e.altered()
|
1038
|
+
changed.append(e)
|
1039
|
+
if len(changed) > 0:
|
1040
|
+
self.signal("refresh_scene", "Scene")
|
1041
|
+
self.signal("element_property_update", changed)
|
1042
|
+
|
1043
|
+
return "ops", data
|
1044
|
+
|
1045
|
+
@self.console_command(
|
1046
|
+
"recalc", input_type=("elements", None), output_type="elements"
|
1047
|
+
)
|
1048
|
+
def recalc(command, channel, _, data=None, post=None, **kwargs):
|
1049
|
+
if data is None:
|
1050
|
+
data = list(self.elems(emphasized=True))
|
1051
|
+
if len(data) == 0:
|
1052
|
+
channel(_("No selected elements."))
|
1053
|
+
return
|
1054
|
+
for e in data:
|
1055
|
+
e.set_dirty_bounds()
|
1056
|
+
self.signal("refresh_scene", "Scene")
|
1057
|
+
self.validate_selected_area()
|
1058
|
+
|
1059
|
+
@self.console_option("douglas", "d", type=bool, action="store_true", default=False)
|
1060
|
+
@self.console_option(
|
1061
|
+
"visvalingam", "v", type=bool, action="store_true", default=False
|
1062
|
+
)
|
1063
|
+
@self.console_option(
|
1064
|
+
"tolerance",
|
1065
|
+
"t",
|
1066
|
+
type=float,
|
1067
|
+
help=_("simplification tolerance"),
|
1068
|
+
)
|
1069
|
+
@self.console_command(
|
1070
|
+
"simplify", input_type=("elements", None), output_type="elements"
|
1071
|
+
)
|
1072
|
+
def simplify_path(
|
1073
|
+
command,
|
1074
|
+
channel,
|
1075
|
+
_,
|
1076
|
+
data=None,
|
1077
|
+
tolerance=None,
|
1078
|
+
douglas=None,
|
1079
|
+
visvalingam=None,
|
1080
|
+
post=None,
|
1081
|
+
**kwargs,
|
1082
|
+
):
|
1083
|
+
if data is None:
|
1084
|
+
data = list(self.elems(emphasized=True))
|
1085
|
+
data_changed = list()
|
1086
|
+
if len(data) == 0:
|
1087
|
+
channel("Requires a selected polygon")
|
1088
|
+
return None
|
1089
|
+
method = "douglaspeucker"
|
1090
|
+
if douglas:
|
1091
|
+
method = "douglaspeucker"
|
1092
|
+
if visvalingam:
|
1093
|
+
method = "visvalingam"
|
1094
|
+
if tolerance is None:
|
1095
|
+
tolerance = 25 # About 1/1000 mil
|
1096
|
+
for node in data:
|
1097
|
+
try:
|
1098
|
+
sub_before = len(list(node.as_geometry().as_subpaths()))
|
1099
|
+
except AttributeError:
|
1100
|
+
sub_before = 0
|
1101
|
+
if hasattr(node, "geometry"):
|
1102
|
+
geom = node.geometry
|
1103
|
+
seg_before = node.geometry.index
|
1104
|
+
if method == "douglaspeucker":
|
1105
|
+
node.geometry = geom.simplify(tolerance)
|
1106
|
+
else:
|
1107
|
+
# Let's try Visvalingam line simplification
|
1108
|
+
node.geometry = geom.simplify_geometry(threshold=tolerance)
|
1109
|
+
node.altered()
|
1110
|
+
seg_after = node.geometry.index
|
1111
|
+
try:
|
1112
|
+
sub_after = len(list(node.as_geometry().as_subpaths()))
|
1113
|
+
except AttributeError:
|
1114
|
+
sub_after = 0
|
1115
|
+
channel(
|
1116
|
+
f"Simplified {node.type} ({node.display_label()}), tolerance: {tolerance}={Length(tolerance, digits=4).length_mm})"
|
1117
|
+
)
|
1118
|
+
if seg_before:
|
1119
|
+
saving = f"({(seg_before - seg_after)/seg_before*100:.1f}%)"
|
1120
|
+
else:
|
1121
|
+
saving = ""
|
1122
|
+
channel(f"Subpaths before: {sub_before} to {sub_after}")
|
1123
|
+
channel(f"Segments before: {seg_before} to {seg_after} {saving}")
|
1124
|
+
data_changed.append(node)
|
1125
|
+
else:
|
1126
|
+
channel(
|
1127
|
+
f"Invalid node for simplify {node.type} ({node.display_label()})"
|
1128
|
+
)
|
1129
|
+
if len(data_changed) > 0:
|
1130
|
+
self.signal("element_property_update", data_changed)
|
1131
|
+
self.signal("refresh_scene", "Scene")
|
1132
|
+
return "elements", data
|
1133
|
+
|
1134
|
+
@self.console_command(
|
1135
|
+
"polycut", input_type=("elements", None), output_type="elements"
|
1136
|
+
)
|
1137
|
+
def create_pattern(command, channel, _, data=None, post=None, **kwargs):
|
1138
|
+
if data is None:
|
1139
|
+
data = list(self.elems(emphasized=True))
|
1140
|
+
if len(data) <= 1:
|
1141
|
+
channel("Requires a selected cutter polygon")
|
1142
|
+
return None
|
1143
|
+
data.sort(key=lambda n: n.emphasized_time)
|
1144
|
+
try:
|
1145
|
+
outer_path = data[0].as_path()
|
1146
|
+
inner_path = data[1].as_path()
|
1147
|
+
except AttributeError:
|
1148
|
+
# elem text does not have an as_path() object
|
1149
|
+
return "elements", data
|
1150
|
+
data[1].remove_node()
|
1151
|
+
|
1152
|
+
from meerk40t.tools.pathtools import VectorMontonizer
|
1153
|
+
|
1154
|
+
vm = VectorMontonizer()
|
1155
|
+
outer_path = Polygon(
|
1156
|
+
[outer_path.point(i / 1000.0, error=1e4) for i in range(1001)]
|
1157
|
+
)
|
1158
|
+
vm.add_polyline(outer_path)
|
1159
|
+
path = Path()
|
1160
|
+
for sub_inner in inner_path.as_subpaths():
|
1161
|
+
sub_inner = Path(sub_inner)
|
1162
|
+
pts_sub = [sub_inner.point(i / 1000.0, error=1e4) for i in range(1001)]
|
1163
|
+
for i in range(len(pts_sub) - 1, -1, -1):
|
1164
|
+
pt = pts_sub[i]
|
1165
|
+
if not vm.is_point_inside(pt[0], pt[1]):
|
1166
|
+
del pts_sub[i]
|
1167
|
+
path += Path(Polyline(pts_sub))
|
1168
|
+
node = self.elem_branch.add(path=path, type="elem path")
|
1169
|
+
data.append(node)
|
1170
|
+
node.stroke = self.default_stroke
|
1171
|
+
node.stroke_width = self.default_strokewidth
|
1172
|
+
node.fill = self.default_fill
|
1173
|
+
node.altered()
|
1174
|
+
self.set_emphasis([node])
|
1175
|
+
node.focus()
|
1176
|
+
post.append(classify_new(data))
|
1177
|
+
return "elements", data
|
1178
|
+
|
1179
|
+
@self.console_argument("mlist", type=Length, help=_("list of positions"), nargs="*")
|
1180
|
+
@self.console_command(
|
1181
|
+
("polygon", "polyline"),
|
1182
|
+
help=_("poly(gon|line) (Length Length)*"),
|
1183
|
+
input_type=("elements", None),
|
1184
|
+
output_type="elements",
|
1185
|
+
all_arguments_required=True,
|
1186
|
+
)
|
1187
|
+
def element_poly(command, channel, _, mlist, data=None, post=None, **kwargs):
|
1188
|
+
try:
|
1189
|
+
pts = [float(Length(p)) for p in mlist]
|
1190
|
+
if command == "polygon":
|
1191
|
+
shape = Polygon(pts)
|
1192
|
+
else:
|
1193
|
+
shape = Polyline(pts)
|
1194
|
+
except ValueError:
|
1195
|
+
raise CommandSyntaxError(
|
1196
|
+
_("Must be a list of spaced delimited length pairs.")
|
1197
|
+
)
|
1198
|
+
if shape.is_degenerate():
|
1199
|
+
channel(_("Shape is degenerate."))
|
1200
|
+
return "elements", data
|
1201
|
+
node = self.elem_branch.add(shape=shape, type="elem polyline")
|
1202
|
+
node.stroke = self.default_stroke
|
1203
|
+
node.stroke_width = self.default_strokewidth
|
1204
|
+
node.fill = self.default_fill
|
1205
|
+
node.altered()
|
1206
|
+
self.set_emphasis([node])
|
1207
|
+
node.focus()
|
1208
|
+
if data is None:
|
1209
|
+
data = list()
|
1210
|
+
data.append(node)
|
1211
|
+
# Newly created! Classification needed?
|
1212
|
+
post.append(classify_new(data))
|
1213
|
+
return "elements", data
|
1214
|
+
|
1215
|
+
@self.console_option(
|
1216
|
+
"real",
|
1217
|
+
"r",
|
1218
|
+
action="store_true",
|
1219
|
+
type=bool,
|
1220
|
+
help="Display non-transformed path",
|
1221
|
+
)
|
1222
|
+
@self.console_command(
|
1223
|
+
"path_d_info",
|
1224
|
+
help=_("List the path_d of any recognized paths"),
|
1225
|
+
input_type="elements",
|
1226
|
+
)
|
1227
|
+
def element_pathd_info(command, channel, _, data, real=True, **kwargs):
|
1228
|
+
for node in data:
|
1229
|
+
try:
|
1230
|
+
g = node.as_geometry()
|
1231
|
+
path = g.as_path()
|
1232
|
+
ident = " (Identity)" if node.matrix.is_identity() else ""
|
1233
|
+
channel(f"{str(node)}{ident}: {path.d(transformed=not real)}")
|
1234
|
+
except AttributeError:
|
1235
|
+
channel(f"{str(node)}: Invalid")
|
1236
|
+
|
1237
|
+
@self.console_argument(
|
1238
|
+
"path_d", type=str, help=_("svg path syntax command (quoted).")
|
1239
|
+
)
|
1240
|
+
@self.console_command(
|
1241
|
+
"path",
|
1242
|
+
help=_("path <svg path>"),
|
1243
|
+
output_type="elements",
|
1244
|
+
)
|
1245
|
+
def element_path(path_d, data, post=None, **kwargs):
|
1246
|
+
if path_d is None:
|
1247
|
+
raise CommandSyntaxError(_("Not a valid path_d string"))
|
1248
|
+
try:
|
1249
|
+
path = Path(path_d)
|
1250
|
+
path *= f"Scale({UNITS_PER_PIXEL})"
|
1251
|
+
except (ValueError, AttributeError):
|
1252
|
+
raise CommandSyntaxError(_("Not a valid path_d string (try quotes)"))
|
1253
|
+
|
1254
|
+
node = self.elem_branch.add(path=path, type="elem path")
|
1255
|
+
node.stroke = self.default_stroke
|
1256
|
+
node.stroke_width = self.default_strokewidth
|
1257
|
+
node.fill = self.default_fill
|
1258
|
+
node.altered()
|
1259
|
+
self.set_emphasis([node])
|
1260
|
+
node.focus()
|
1261
|
+
if data is None:
|
1262
|
+
data = list()
|
1263
|
+
data.append(node)
|
1264
|
+
# Newly created! Classification needed?
|
1265
|
+
post.append(classify_new(data))
|
1266
|
+
return "elements", data
|
1267
|
+
|
1268
|
+
@self.console_argument(
|
1269
|
+
"stroke_width",
|
1270
|
+
type=self.length,
|
1271
|
+
help=_("Stroke-width for the given stroke"),
|
1272
|
+
)
|
1273
|
+
@self.console_command(
|
1274
|
+
"stroke-width",
|
1275
|
+
help=_("stroke-width <length>"),
|
1276
|
+
input_type=(
|
1277
|
+
None,
|
1278
|
+
"elements",
|
1279
|
+
),
|
1280
|
+
output_type="elements",
|
1281
|
+
)
|
1282
|
+
def element_stroke_width(command, channel, _, stroke_width, data=None, **kwargs):
|
1283
|
+
def width_string(value):
|
1284
|
+
if value is None:
|
1285
|
+
return "-"
|
1286
|
+
res = ""
|
1287
|
+
display_units = (
|
1288
|
+
(1, ""),
|
1289
|
+
(UNITS_PER_PIXEL, "px"),
|
1290
|
+
(UNITS_PER_POINT, "pt"),
|
1291
|
+
(UNITS_PER_MM, "mm"),
|
1292
|
+
)
|
1293
|
+
for unit in display_units:
|
1294
|
+
unit_value = value / unit[0]
|
1295
|
+
if res != "":
|
1296
|
+
res += ", "
|
1297
|
+
res += f"{unit_value:.3f}{unit[1]}"
|
1298
|
+
return res
|
1299
|
+
|
1300
|
+
if data is None:
|
1301
|
+
data = list(self.elems(emphasized=True))
|
1302
|
+
if stroke_width is None:
|
1303
|
+
# Display data about stroke widths.
|
1304
|
+
channel("----------")
|
1305
|
+
channel(_("Stroke-Width Values:"))
|
1306
|
+
for i, e in enumerate(self.elems()):
|
1307
|
+
name = str(e)
|
1308
|
+
if len(name) > 50:
|
1309
|
+
name = name[:50] + "…"
|
1310
|
+
try:
|
1311
|
+
stroke_width = e.stroke_width
|
1312
|
+
except AttributeError:
|
1313
|
+
# Has no stroke width.
|
1314
|
+
continue
|
1315
|
+
if not hasattr(e, "stroke_scaled"):
|
1316
|
+
# Can't have a scaled stroke.
|
1317
|
+
channel(
|
1318
|
+
_(
|
1319
|
+
"{index}: {name} - {typename}\n stroke-width = {stroke_width}\n scaled-width = {scaled_stroke_width}"
|
1320
|
+
).format(
|
1321
|
+
index=i,
|
1322
|
+
typename="scaled-stroke",
|
1323
|
+
stroke_width=width_string(stroke_width),
|
1324
|
+
scaled_stroke_width=width_string(None),
|
1325
|
+
name=name,
|
1326
|
+
)
|
1327
|
+
)
|
1328
|
+
continue
|
1329
|
+
factor = 1.0
|
1330
|
+
if e.stroke_scaled:
|
1331
|
+
typename = "scaled-stroke"
|
1332
|
+
try:
|
1333
|
+
factor = e.stroke_factor
|
1334
|
+
except AttributeError:
|
1335
|
+
pass
|
1336
|
+
else:
|
1337
|
+
typename = "non-scaling-stroke"
|
1338
|
+
implied_value = factor * stroke_width
|
1339
|
+
channel(
|
1340
|
+
_(
|
1341
|
+
"{index}: {name} - {typename}\n stroke-width = {stroke_width}\n scaled-width = {scaled_stroke_width}"
|
1342
|
+
).format(
|
1343
|
+
index=i,
|
1344
|
+
typename=typename,
|
1345
|
+
stroke_width=width_string(stroke_width),
|
1346
|
+
scaled_stroke_width=width_string(implied_value),
|
1347
|
+
name=name,
|
1348
|
+
)
|
1349
|
+
)
|
1350
|
+
channel("----------")
|
1351
|
+
return
|
1352
|
+
|
1353
|
+
if len(data) == 0:
|
1354
|
+
channel(_("No selected elements."))
|
1355
|
+
return
|
1356
|
+
for e in data:
|
1357
|
+
if hasattr(e, "lock") and e.lock:
|
1358
|
+
channel(_("Can't modify a locked element: {name}").format(name=str(e)))
|
1359
|
+
continue
|
1360
|
+
e.stroke_width = stroke_width
|
1361
|
+
try:
|
1362
|
+
e.stroke_width_zero()
|
1363
|
+
except AttributeError:
|
1364
|
+
pass
|
1365
|
+
# No full modified required, we are effectively only adjusting
|
1366
|
+
# the painted_bounds
|
1367
|
+
e.translated(0, 0)
|
1368
|
+
self.signal("element_property_update", data)
|
1369
|
+
self.signal("refresh_scene", "Scene")
|
1370
|
+
return "elements", data
|
1371
|
+
|
1372
|
+
@self.console_command(
|
1373
|
+
("enable_stroke_scale", "disable_stroke_scale"),
|
1374
|
+
help=_("stroke-width <length>"),
|
1375
|
+
input_type=(
|
1376
|
+
None,
|
1377
|
+
"elements",
|
1378
|
+
),
|
1379
|
+
hidden=True,
|
1380
|
+
output_type="elements",
|
1381
|
+
)
|
1382
|
+
def element_stroke_scale_enable(command, channel, _, data=None, **kwargs):
|
1383
|
+
if data is None:
|
1384
|
+
data = list(self.elems(emphasized=True))
|
1385
|
+
if len(data) == 0:
|
1386
|
+
channel(_("No selected elements."))
|
1387
|
+
return
|
1388
|
+
for e in data:
|
1389
|
+
if hasattr(e, "lock") and e.lock:
|
1390
|
+
channel(_("Can't modify a locked element: {name}").format(name=str(e)))
|
1391
|
+
continue
|
1392
|
+
e.stroke_scaled = command == "enable_stroke_scale"
|
1393
|
+
e.altered()
|
1394
|
+
self.signal("element_property_update", data)
|
1395
|
+
self.signal("refresh_scene", "Scene")
|
1396
|
+
return "elements", data
|
1397
|
+
|
1398
|
+
@self.console_option("filter", "f", type=str, help="Filter indexes")
|
1399
|
+
@self.console_argument(
|
1400
|
+
"cap",
|
1401
|
+
type=str,
|
1402
|
+
help=_("Linecap to apply to the path (one of butt, round, square)"),
|
1403
|
+
)
|
1404
|
+
@self.console_command(
|
1405
|
+
"linecap",
|
1406
|
+
help=_("linecap <cap>"),
|
1407
|
+
input_type=(
|
1408
|
+
None,
|
1409
|
+
"elements",
|
1410
|
+
),
|
1411
|
+
output_type="elements",
|
1412
|
+
)
|
1413
|
+
def element_cap(command, channel, _, cap=None, data=None, filter=None, **kwargs):
|
1414
|
+
if data is None:
|
1415
|
+
data = list(self.elems(emphasized=True))
|
1416
|
+
apply = data
|
1417
|
+
if filter is not None:
|
1418
|
+
apply = list()
|
1419
|
+
for value in filter.split(","):
|
1420
|
+
try:
|
1421
|
+
value = int(value)
|
1422
|
+
except ValueError:
|
1423
|
+
continue
|
1424
|
+
try:
|
1425
|
+
apply.append(data[value])
|
1426
|
+
except IndexError:
|
1427
|
+
channel(_("index {index} out of range").format(index=value))
|
1428
|
+
if cap is None:
|
1429
|
+
channel("----------")
|
1430
|
+
channel(_("Linecaps:"))
|
1431
|
+
i = 0
|
1432
|
+
for e in self.elems():
|
1433
|
+
name = str(e)
|
1434
|
+
if len(name) > 50:
|
1435
|
+
name = name[:50] + "…"
|
1436
|
+
if hasattr(e, "linecap"):
|
1437
|
+
if e.linecap == Linecap.CAP_SQUARE:
|
1438
|
+
capname = "square"
|
1439
|
+
elif e.linecap == Linecap.CAP_BUTT:
|
1440
|
+
capname = "butt"
|
1441
|
+
else:
|
1442
|
+
capname = "round"
|
1443
|
+
channel(
|
1444
|
+
_("{index}: linecap = {linecap} - {name}").format(
|
1445
|
+
index=i, linecap=capname, name=name
|
1446
|
+
)
|
1447
|
+
)
|
1448
|
+
i += 1
|
1449
|
+
channel("----------")
|
1450
|
+
return
|
1451
|
+
else:
|
1452
|
+
capvalue = None
|
1453
|
+
if cap.lower() == "butt":
|
1454
|
+
capvalue = Linecap.CAP_BUTT
|
1455
|
+
elif cap.lower() == "round":
|
1456
|
+
capvalue = Linecap.CAP_ROUND
|
1457
|
+
elif cap.lower() == "square":
|
1458
|
+
capvalue = Linecap.CAP_SQUARE
|
1459
|
+
if capvalue is not None:
|
1460
|
+
for e in apply:
|
1461
|
+
if hasattr(e, "linecap"):
|
1462
|
+
if hasattr(e, "lock") and e.lock:
|
1463
|
+
channel(
|
1464
|
+
_("Can't modify a locked element: {name}").format(
|
1465
|
+
name=str(e)
|
1466
|
+
)
|
1467
|
+
)
|
1468
|
+
continue
|
1469
|
+
e.linecap = capvalue
|
1470
|
+
e.altered()
|
1471
|
+
return "elements", data
|
1472
|
+
|
1473
|
+
@self.console_option("filter", "f", type=str, help="Filter indexes")
|
1474
|
+
@self.console_argument(
|
1475
|
+
"join",
|
1476
|
+
type=str,
|
1477
|
+
help=_(
|
1478
|
+
"jointype to apply to the path (one of arcs, bevel, miter, miter-clip, round)"
|
1479
|
+
),
|
1480
|
+
)
|
1481
|
+
@self.console_command(
|
1482
|
+
"linejoin",
|
1483
|
+
help=_("linejoin <join>"),
|
1484
|
+
input_type=(
|
1485
|
+
None,
|
1486
|
+
"elements",
|
1487
|
+
),
|
1488
|
+
output_type="elements",
|
1489
|
+
)
|
1490
|
+
def element_join(command, channel, _, join=None, data=None, filter=None, **kwargs):
|
1491
|
+
if data is None:
|
1492
|
+
data = list(self.elems(emphasized=True))
|
1493
|
+
apply = data
|
1494
|
+
if filter is not None:
|
1495
|
+
apply = list()
|
1496
|
+
for value in filter.split(","):
|
1497
|
+
try:
|
1498
|
+
value = int(value)
|
1499
|
+
except ValueError:
|
1500
|
+
continue
|
1501
|
+
try:
|
1502
|
+
apply.append(data[value])
|
1503
|
+
except IndexError:
|
1504
|
+
channel(_("index {index} out of range").format(index=value))
|
1505
|
+
if join is None:
|
1506
|
+
channel("----------")
|
1507
|
+
channel(_("Linejoins:"))
|
1508
|
+
i = 0
|
1509
|
+
for e in self.elems():
|
1510
|
+
name = str(e)
|
1511
|
+
if len(name) > 50:
|
1512
|
+
name = name[:50] + "…"
|
1513
|
+
if hasattr(e, "linejoin"):
|
1514
|
+
if e.linejoin == Linejoin.JOIN_ARCS:
|
1515
|
+
joinname = "arcs"
|
1516
|
+
elif e.linejoin == Linejoin.JOIN_BEVEL:
|
1517
|
+
joinname = "bevel"
|
1518
|
+
elif e.linejoin == Linejoin.JOIN_MITER_CLIP:
|
1519
|
+
joinname = "miter-clip"
|
1520
|
+
elif e.linejoin == Linejoin.JOIN_MITER:
|
1521
|
+
joinname = "miter"
|
1522
|
+
elif e.linejoin == Linejoin.JOIN_ROUND:
|
1523
|
+
joinname = "round"
|
1524
|
+
channel(
|
1525
|
+
_("{index}: linejoin = {linejoin} - {name}").format(
|
1526
|
+
index=i, linejoin=joinname, name=name
|
1527
|
+
)
|
1528
|
+
)
|
1529
|
+
i += 1
|
1530
|
+
channel("----------")
|
1531
|
+
return
|
1532
|
+
else:
|
1533
|
+
joinvalue = None
|
1534
|
+
if join.lower() == "arcs":
|
1535
|
+
joinvalue = Linejoin.JOIN_ARCS
|
1536
|
+
elif join.lower() == "bevel":
|
1537
|
+
joinvalue = Linejoin.JOIN_BEVEL
|
1538
|
+
elif join.lower() == "miter":
|
1539
|
+
joinvalue = Linejoin.JOIN_MITER
|
1540
|
+
elif join.lower() == "miter-clip":
|
1541
|
+
joinvalue = Linejoin.JOIN_MITER_CLIP
|
1542
|
+
elif join.lower() == "round":
|
1543
|
+
joinvalue = Linejoin.JOIN_ROUND
|
1544
|
+
if joinvalue is not None:
|
1545
|
+
for e in apply:
|
1546
|
+
if hasattr(e, "linejoin"):
|
1547
|
+
if hasattr(e, "lock") and e.lock:
|
1548
|
+
channel(
|
1549
|
+
_("Can't modify a locked element: {name}").format(
|
1550
|
+
name=str(e)
|
1551
|
+
)
|
1552
|
+
)
|
1553
|
+
continue
|
1554
|
+
e.linejoin = joinvalue
|
1555
|
+
e.altered()
|
1556
|
+
return "elements", data
|
1557
|
+
|
1558
|
+
@self.console_option("filter", "f", type=str, help="Filter indexes")
|
1559
|
+
@self.console_argument(
|
1560
|
+
"rule",
|
1561
|
+
type=str,
|
1562
|
+
help=_("rule to apply to fill the path (one of {nonzero}, {evenodd})").format(
|
1563
|
+
nonzero=SVG_RULE_NONZERO, evenodd=SVG_RULE_EVENODD
|
1564
|
+
),
|
1565
|
+
)
|
1566
|
+
@self.console_command(
|
1567
|
+
"fillrule",
|
1568
|
+
help=_("fillrule <rule>"),
|
1569
|
+
input_type=(
|
1570
|
+
None,
|
1571
|
+
"elements",
|
1572
|
+
),
|
1573
|
+
output_type="elements",
|
1574
|
+
)
|
1575
|
+
def element_rule(command, channel, _, rule=None, data=None, filter=None, **kwargs):
|
1576
|
+
if data is None:
|
1577
|
+
data = list(self.elems(emphasized=True))
|
1578
|
+
apply = data
|
1579
|
+
if filter is not None:
|
1580
|
+
apply = list()
|
1581
|
+
for value in filter.split(","):
|
1582
|
+
try:
|
1583
|
+
value = int(value)
|
1584
|
+
except ValueError:
|
1585
|
+
continue
|
1586
|
+
try:
|
1587
|
+
apply.append(data[value])
|
1588
|
+
except IndexError:
|
1589
|
+
channel(_("index {index} out of range").format(index=value))
|
1590
|
+
if rule is None:
|
1591
|
+
channel("----------")
|
1592
|
+
channel(_("fillrules:"))
|
1593
|
+
i = 0
|
1594
|
+
for e in self.elems():
|
1595
|
+
name = str(e)
|
1596
|
+
if len(name) > 50:
|
1597
|
+
name = name[:50] + "…"
|
1598
|
+
if hasattr(e, "fillrule"):
|
1599
|
+
if e.fillrule == Fillrule.FILLRULE_EVENODD:
|
1600
|
+
rulename = SVG_RULE_EVENODD
|
1601
|
+
elif e.fillrule == Fillrule.FILLRULE_NONZERO:
|
1602
|
+
rulename = SVG_RULE_NONZERO
|
1603
|
+
channel(
|
1604
|
+
_("{index}: fillrule = {fillrule} - {name}").format(
|
1605
|
+
index=i, fillrule=rulename, name=name
|
1606
|
+
)
|
1607
|
+
)
|
1608
|
+
i += 1
|
1609
|
+
channel("----------")
|
1610
|
+
return
|
1611
|
+
else:
|
1612
|
+
rulevalue = None
|
1613
|
+
if rule.lower() == SVG_RULE_EVENODD:
|
1614
|
+
rulevalue = Fillrule.FILLRULE_EVENODD
|
1615
|
+
elif rule.lower() == SVG_RULE_NONZERO:
|
1616
|
+
rulevalue = Fillrule.FILLRULE_NONZERO
|
1617
|
+
if rulevalue is not None:
|
1618
|
+
for e in apply:
|
1619
|
+
if hasattr(e, "fillrule"):
|
1620
|
+
if hasattr(e, "lock") and e.lock:
|
1621
|
+
channel(
|
1622
|
+
_("Can't modify a locked element: {name}").format(
|
1623
|
+
name=str(e)
|
1624
|
+
)
|
1625
|
+
)
|
1626
|
+
continue
|
1627
|
+
e.fillrule = rulevalue
|
1628
|
+
e.altered()
|
1629
|
+
return "elements", data
|
1630
|
+
|
1631
|
+
@self.console_option(
|
1632
|
+
"classify", "c", type=bool, action="store_true", help="Reclassify element"
|
1633
|
+
)
|
1634
|
+
@self.console_option("filter", "f", type=str, help="Filter indexes")
|
1635
|
+
@self.console_argument(
|
1636
|
+
"color", type=Color, help=_("Color to color the given stroke")
|
1637
|
+
)
|
1638
|
+
@self.console_command(
|
1639
|
+
"stroke",
|
1640
|
+
help=_("stroke <svg color>"),
|
1641
|
+
input_type=(
|
1642
|
+
None,
|
1643
|
+
"elements",
|
1644
|
+
),
|
1645
|
+
output_type="elements",
|
1646
|
+
)
|
1647
|
+
def element_stroke(
|
1648
|
+
command, channel, _, color, data=None, classify=None, filter=None, **kwargs
|
1649
|
+
):
|
1650
|
+
if data is None:
|
1651
|
+
data = list(self.elems(emphasized=True))
|
1652
|
+
was_emphasized = True
|
1653
|
+
old_first = self.first_emphasized
|
1654
|
+
else:
|
1655
|
+
was_emphasized = False
|
1656
|
+
old_first = None
|
1657
|
+
apply = data
|
1658
|
+
if filter is not None:
|
1659
|
+
apply = list()
|
1660
|
+
for value in filter.split(","):
|
1661
|
+
try:
|
1662
|
+
value = int(value)
|
1663
|
+
except ValueError:
|
1664
|
+
continue
|
1665
|
+
try:
|
1666
|
+
apply.append(data[value])
|
1667
|
+
except IndexError:
|
1668
|
+
channel(_("index {index} out of range").format(index=value))
|
1669
|
+
if color is None:
|
1670
|
+
channel("----------")
|
1671
|
+
channel(_("Stroke Values:"))
|
1672
|
+
i = 0
|
1673
|
+
for e in self.elems():
|
1674
|
+
name = str(e)
|
1675
|
+
if len(name) > 50:
|
1676
|
+
name = name[:50] + "…"
|
1677
|
+
if not hasattr(e, "stroke"):
|
1678
|
+
pass
|
1679
|
+
elif hasattr(e, "stroke") and e.stroke is None or e.stroke == "none":
|
1680
|
+
channel(f"{i}: stroke = none - {name}")
|
1681
|
+
else:
|
1682
|
+
channel(f"{i}: stroke = {e.stroke.hex} - {name}")
|
1683
|
+
i += 1
|
1684
|
+
channel("----------")
|
1685
|
+
return
|
1686
|
+
self.set_start_time("full_load")
|
1687
|
+
# _("Set stroke")
|
1688
|
+
with self.undoscope("Set stroke"):
|
1689
|
+
if color == "none":
|
1690
|
+
self.set_start_time("stroke")
|
1691
|
+
for e in apply:
|
1692
|
+
if hasattr(e, "lock") and e.lock:
|
1693
|
+
channel(
|
1694
|
+
_("Can't modify a locked element: {name}").format(name=str(e))
|
1695
|
+
)
|
1696
|
+
continue
|
1697
|
+
e.stroke = None
|
1698
|
+
e.translated(0, 0)
|
1699
|
+
# e.altered()
|
1700
|
+
self.set_end_time("stroke")
|
1701
|
+
else:
|
1702
|
+
self.set_start_time("stroke")
|
1703
|
+
for e in apply:
|
1704
|
+
if hasattr(e, "lock") and e.lock:
|
1705
|
+
channel(
|
1706
|
+
_("Can't modify a locked element: {name}").format(name=str(e))
|
1707
|
+
)
|
1708
|
+
continue
|
1709
|
+
e.stroke = Color(color)
|
1710
|
+
e.translated(0, 0)
|
1711
|
+
# e.altered()
|
1712
|
+
self.set_end_time("stroke")
|
1713
|
+
if classify is None:
|
1714
|
+
classify = False
|
1715
|
+
if classify:
|
1716
|
+
self.set_start_time("classify")
|
1717
|
+
self.remove_elements_from_operations(apply)
|
1718
|
+
self.classify(apply)
|
1719
|
+
if was_emphasized:
|
1720
|
+
for e in apply:
|
1721
|
+
e.emphasized = True
|
1722
|
+
if len(apply) == 1:
|
1723
|
+
apply[0].focus()
|
1724
|
+
if old_first is not None and old_first in apply:
|
1725
|
+
self.first_emphasized = old_first
|
1726
|
+
else:
|
1727
|
+
self.first_emphasized = None
|
1728
|
+
self.set_end_time("classify")
|
1729
|
+
# self.signal("rebuild_tree")
|
1730
|
+
self.signal("refresh_tree", apply)
|
1731
|
+
else:
|
1732
|
+
self.signal("element_property_reload", apply)
|
1733
|
+
self.signal("refresh_scene", "Scene")
|
1734
|
+
return "elements", data
|
1735
|
+
|
1736
|
+
@self.console_option(
|
1737
|
+
"classify", "c", type=bool, action="store_true", help="Reclassify element"
|
1738
|
+
)
|
1739
|
+
@self.console_option("filter", "f", type=str, help="Filter indexes")
|
1740
|
+
@self.console_argument("color", type=Color, help=_("Color to set the fill to"))
|
1741
|
+
@self.console_command(
|
1742
|
+
"fill",
|
1743
|
+
help=_("fill <svg color>"),
|
1744
|
+
input_type=(
|
1745
|
+
None,
|
1746
|
+
"elements",
|
1747
|
+
),
|
1748
|
+
output_type="elements",
|
1749
|
+
)
|
1750
|
+
def element_fill(
|
1751
|
+
command, channel, _, color, data=None, classify=None, filter=None, **kwargs
|
1752
|
+
):
|
1753
|
+
if data is None:
|
1754
|
+
data = list(self.elems(emphasized=True))
|
1755
|
+
was_emphasized = True
|
1756
|
+
old_first = self.first_emphasized
|
1757
|
+
else:
|
1758
|
+
was_emphasized = False
|
1759
|
+
old_first = None
|
1760
|
+
apply = data
|
1761
|
+
if filter is not None:
|
1762
|
+
apply = list()
|
1763
|
+
for value in filter.split(","):
|
1764
|
+
try:
|
1765
|
+
value = int(value)
|
1766
|
+
except ValueError:
|
1767
|
+
continue
|
1768
|
+
try:
|
1769
|
+
apply.append(data[value])
|
1770
|
+
except IndexError:
|
1771
|
+
channel(_("index {index} out of range").format(index=value))
|
1772
|
+
if color is None:
|
1773
|
+
channel("----------")
|
1774
|
+
channel(_("Fill Values:"))
|
1775
|
+
i = 0
|
1776
|
+
for e in self.elems():
|
1777
|
+
name = str(e)
|
1778
|
+
if len(name) > 50:
|
1779
|
+
name = name[:50] + "…"
|
1780
|
+
if not hasattr(e, "fill"):
|
1781
|
+
pass
|
1782
|
+
elif e.fill is None or e.fill == "none":
|
1783
|
+
channel(
|
1784
|
+
_("{index}: fill = none - {name}").format(index=i, name=name)
|
1785
|
+
)
|
1786
|
+
else:
|
1787
|
+
channel(
|
1788
|
+
_("{index}: fill = {fill} - {name}").format(
|
1789
|
+
index=i, fill=e.fill.hex, name=name
|
1790
|
+
)
|
1791
|
+
)
|
1792
|
+
i += 1
|
1793
|
+
channel("----------")
|
1794
|
+
return "elements", data
|
1795
|
+
# _("Set fill")
|
1796
|
+
with self.undoscope("Set fill"):
|
1797
|
+
|
1798
|
+
if color == "none":
|
1799
|
+
self.set_start_time("fill")
|
1800
|
+
for e in apply:
|
1801
|
+
if hasattr(e, "lock") and e.lock:
|
1802
|
+
channel(
|
1803
|
+
_("Can't modify a locked element: {name}").format(name=str(e))
|
1804
|
+
)
|
1805
|
+
continue
|
1806
|
+
e.fill = None
|
1807
|
+
e.translated(0, 0)
|
1808
|
+
# e.altered()
|
1809
|
+
self.set_end_time("fill")
|
1810
|
+
else:
|
1811
|
+
self.set_start_time("fill")
|
1812
|
+
for e in apply:
|
1813
|
+
if hasattr(e, "lock") and e.lock:
|
1814
|
+
channel(
|
1815
|
+
_("Can't modify a locked element: {name}").format(name=str(e))
|
1816
|
+
)
|
1817
|
+
continue
|
1818
|
+
e.fill = Color(color)
|
1819
|
+
e.translated(0, 0)
|
1820
|
+
# e.altered()
|
1821
|
+
self.set_end_time("fill")
|
1822
|
+
if classify is None:
|
1823
|
+
classify = False
|
1824
|
+
if classify:
|
1825
|
+
self.set_start_time("classify")
|
1826
|
+
self.remove_elements_from_operations(apply)
|
1827
|
+
self.classify(apply)
|
1828
|
+
if was_emphasized:
|
1829
|
+
for e in apply:
|
1830
|
+
e.emphasized = True
|
1831
|
+
if len(apply) == 1:
|
1832
|
+
apply[0].focus()
|
1833
|
+
if old_first is not None and old_first in apply:
|
1834
|
+
self.first_emphasized = old_first
|
1835
|
+
else:
|
1836
|
+
self.first_emphasized = None
|
1837
|
+
self.signal("refresh_tree", apply)
|
1838
|
+
# self.signal("rebuild_tree")
|
1839
|
+
self.set_end_time("classify")
|
1840
|
+
else:
|
1841
|
+
self.signal("element_property_update", apply)
|
1842
|
+
self.signal("refresh_scene", "Scene")
|
1843
|
+
return "elements", data
|
1844
|
+
|
1845
|
+
@self.console_argument(
|
1846
|
+
"x_offset", type=self.length_x, help=_("x offset."), default="0"
|
1847
|
+
)
|
1848
|
+
@self.console_argument(
|
1849
|
+
"y_offset", type=self.length_y, help=_("y offset"), default="0"
|
1850
|
+
)
|
1851
|
+
@self.console_command(
|
1852
|
+
"frame",
|
1853
|
+
help=_("Draws a frame the current selected elements"),
|
1854
|
+
input_type=(
|
1855
|
+
None,
|
1856
|
+
"elements",
|
1857
|
+
),
|
1858
|
+
output_type="elements",
|
1859
|
+
)
|
1860
|
+
def element_frame(
|
1861
|
+
command,
|
1862
|
+
channel,
|
1863
|
+
_,
|
1864
|
+
x_offset=None,
|
1865
|
+
y_offset=None,
|
1866
|
+
data=None,
|
1867
|
+
post=None,
|
1868
|
+
**kwargs,
|
1869
|
+
):
|
1870
|
+
"""
|
1871
|
+
Draws an outline of the current shape.
|
1872
|
+
"""
|
1873
|
+
bounds = self.selected_area()
|
1874
|
+
if bounds is None:
|
1875
|
+
channel(_("Nothing Selected"))
|
1876
|
+
return
|
1877
|
+
x_pos = bounds[0]
|
1878
|
+
y_pos = bounds[1]
|
1879
|
+
width = bounds[2] - bounds[0]
|
1880
|
+
height = bounds[3] - bounds[1]
|
1881
|
+
x_pos -= x_offset
|
1882
|
+
y_pos -= y_offset
|
1883
|
+
width += x_offset * 2
|
1884
|
+
height += y_offset * 2
|
1885
|
+
node = self.elem_branch.add(
|
1886
|
+
x=x_pos,
|
1887
|
+
y=y_pos,
|
1888
|
+
width=width,
|
1889
|
+
height=height,
|
1890
|
+
stroke=Color("red"),
|
1891
|
+
type="elem rect",
|
1892
|
+
)
|
1893
|
+
self.set_emphasis([node])
|
1894
|
+
node.focus()
|
1895
|
+
if data is None:
|
1896
|
+
data = list()
|
1897
|
+
data.append(node)
|
1898
|
+
# Newly created! Classification needed?
|
1899
|
+
post.append(classify_new(data))
|
1900
|
+
return "elements", data
|
1901
|
+
|
1902
|
+
@self.console_argument("angle", type=Angle, help=_("angle to rotate by"))
|
1903
|
+
@self.console_option("cx", "x", type=self.length_x, help=_("center x"))
|
1904
|
+
@self.console_option("cy", "y", type=self.length_y, help=_("center y"))
|
1905
|
+
@self.console_option(
|
1906
|
+
"absolute",
|
1907
|
+
"a",
|
1908
|
+
type=bool,
|
1909
|
+
action="store_true",
|
1910
|
+
help=_("angle_to absolute angle"),
|
1911
|
+
)
|
1912
|
+
@self.console_command(
|
1913
|
+
"rotate",
|
1914
|
+
help=_("rotate <angle>"),
|
1915
|
+
input_type=(
|
1916
|
+
None,
|
1917
|
+
"elements",
|
1918
|
+
),
|
1919
|
+
output_type="elements",
|
1920
|
+
)
|
1921
|
+
def element_rotate(
|
1922
|
+
command,
|
1923
|
+
channel,
|
1924
|
+
_,
|
1925
|
+
angle,
|
1926
|
+
cx=None,
|
1927
|
+
cy=None,
|
1928
|
+
absolute=False,
|
1929
|
+
data=None,
|
1930
|
+
**kwargs,
|
1931
|
+
):
|
1932
|
+
if angle is None:
|
1933
|
+
channel("----------")
|
1934
|
+
channel(_("Rotate Values:"))
|
1935
|
+
i = 0
|
1936
|
+
for node in self.elems():
|
1937
|
+
name = str(node)
|
1938
|
+
if len(name) > 50:
|
1939
|
+
name = name[:50] + "…"
|
1940
|
+
channel(
|
1941
|
+
_("{index}: rotate({angle}turn) - {name}").format(
|
1942
|
+
index=i,
|
1943
|
+
angle=Angle(node.matrix.rotation).angle_turns[:-4],
|
1944
|
+
name=name,
|
1945
|
+
)
|
1946
|
+
)
|
1947
|
+
i += 1
|
1948
|
+
channel("----------")
|
1949
|
+
return
|
1950
|
+
if data is None:
|
1951
|
+
data = list(self.elems(emphasized=True))
|
1952
|
+
if len(data) == 0:
|
1953
|
+
channel(_("No selected elements."))
|
1954
|
+
return
|
1955
|
+
self.validate_selected_area()
|
1956
|
+
bounds = self.selected_area()
|
1957
|
+
if bounds is None:
|
1958
|
+
channel(_("No selected elements."))
|
1959
|
+
return
|
1960
|
+
|
1961
|
+
if cx is None:
|
1962
|
+
cx = (bounds[2] + bounds[0]) / 2.0
|
1963
|
+
if cy is None:
|
1964
|
+
cy = (bounds[3] + bounds[1]) / 2.0
|
1965
|
+
images = []
|
1966
|
+
try:
|
1967
|
+
if not absolute:
|
1968
|
+
for node in data:
|
1969
|
+
if hasattr(node, "lock") and node.lock:
|
1970
|
+
continue
|
1971
|
+
node.matrix.post_rotate(angle, cx, cy)
|
1972
|
+
node.modified()
|
1973
|
+
if hasattr(node, "update"):
|
1974
|
+
images.append(node)
|
1975
|
+
else:
|
1976
|
+
for node in data:
|
1977
|
+
if hasattr(node, "lock") and node.lock:
|
1978
|
+
continue
|
1979
|
+
start_angle = node.matrix.rotation
|
1980
|
+
node.matrix.post_rotate(angle - start_angle, cx, cy)
|
1981
|
+
node.modified()
|
1982
|
+
if hasattr(node, "update"):
|
1983
|
+
images.append(node)
|
1984
|
+
except ValueError:
|
1985
|
+
raise CommandSyntaxError
|
1986
|
+
for node in images:
|
1987
|
+
self.do_image_update(node)
|
1988
|
+
|
1989
|
+
self.signal("refresh_scene", "Scene")
|
1990
|
+
return "elements", data
|
1991
|
+
|
1992
|
+
@self.console_argument("scale_x", type=str, help=_("scale_x value"))
|
1993
|
+
@self.console_argument("scale_y", type=str, help=_("scale_y value"))
|
1994
|
+
@self.console_option("px", "x", type=self.length_x, help=_("scale x origin point"))
|
1995
|
+
@self.console_option("py", "y", type=self.length_y, help=_("scale y origin point"))
|
1996
|
+
@self.console_option(
|
1997
|
+
"absolute",
|
1998
|
+
"a",
|
1999
|
+
type=bool,
|
2000
|
+
action="store_true",
|
2001
|
+
help=_("scale to absolute size"),
|
2002
|
+
)
|
2003
|
+
@self.console_command(
|
2004
|
+
"scale",
|
2005
|
+
help=_("scale <scale> [<scale-y>]?"),
|
2006
|
+
input_type=(None, "elements"),
|
2007
|
+
output_type="elements",
|
2008
|
+
)
|
2009
|
+
def element_scale(
|
2010
|
+
command,
|
2011
|
+
channel,
|
2012
|
+
_,
|
2013
|
+
scale_x=None,
|
2014
|
+
scale_y=None,
|
2015
|
+
px=None,
|
2016
|
+
py=None,
|
2017
|
+
absolute=False,
|
2018
|
+
data=None,
|
2019
|
+
**kwargs,
|
2020
|
+
):
|
2021
|
+
if scale_x is None:
|
2022
|
+
channel("----------")
|
2023
|
+
channel(_("Scale Values:"))
|
2024
|
+
i = 0
|
2025
|
+
for node in self.elems():
|
2026
|
+
name = str(node)
|
2027
|
+
if len(name) > 50:
|
2028
|
+
name = name[:50] + "…"
|
2029
|
+
channel(
|
2030
|
+
f"{i}: scale({node.matrix.value_scale_x()}, {node.matrix.value_scale_y()}) - {name}"
|
2031
|
+
)
|
2032
|
+
i += 1
|
2033
|
+
channel("----------")
|
2034
|
+
return
|
2035
|
+
if data is None:
|
2036
|
+
data = list(self.elems(emphasized=True))
|
2037
|
+
if len(data) == 0:
|
2038
|
+
channel(_("No selected elements."))
|
2039
|
+
return
|
2040
|
+
# print (f"Start: {scale_x} ({type(scale_x).__name__}), {scale_y} ({type(scale_y).__name__})")
|
2041
|
+
factor = 1
|
2042
|
+
if scale_x.endswith("%"):
|
2043
|
+
factor = 0.01
|
2044
|
+
scale_x = scale_x[:-1]
|
2045
|
+
try:
|
2046
|
+
scale_x = factor * float(scale_x)
|
2047
|
+
except ValueError:
|
2048
|
+
scale_x = 1
|
2049
|
+
if scale_y is None:
|
2050
|
+
scale_y = scale_x
|
2051
|
+
else:
|
2052
|
+
factor = 1
|
2053
|
+
if scale_y.endswith("%"):
|
2054
|
+
factor = 0.01
|
2055
|
+
scale_y = scale_y[:-1]
|
2056
|
+
try:
|
2057
|
+
scale_y = factor * float(scale_y)
|
2058
|
+
except ValueError:
|
2059
|
+
scale_y = 1
|
2060
|
+
# print (f"End: {scale_x} ({type(scale_x).__name__}), {scale_y} ({type(scale_y).__name__})")
|
2061
|
+
|
2062
|
+
bounds = Node.union_bounds(data)
|
2063
|
+
if px is None:
|
2064
|
+
px = (bounds[2] + bounds[0]) / 2.0
|
2065
|
+
if py is None:
|
2066
|
+
py = (bounds[3] + bounds[1]) / 2.0
|
2067
|
+
if scale_x == 0 or scale_y == 0:
|
2068
|
+
channel(_("Scaling by Zero Error"))
|
2069
|
+
return
|
2070
|
+
matrix = Matrix(f"scale({scale_x},{scale_y},{px},{py})")
|
2071
|
+
images = []
|
2072
|
+
with self.undoscope("Scale"):
|
2073
|
+
try:
|
2074
|
+
if not absolute:
|
2075
|
+
for node in data:
|
2076
|
+
if hasattr(node, "lock") and node.lock:
|
2077
|
+
continue
|
2078
|
+
node.matrix *= matrix
|
2079
|
+
node.scaled(sx=scale_x, sy=scale_y, ox=px, oy=py)
|
2080
|
+
if hasattr(node, "update"):
|
2081
|
+
images.append(node)
|
2082
|
+
else:
|
2083
|
+
for node in data:
|
2084
|
+
if hasattr(node, "lock") and node.lock:
|
2085
|
+
continue
|
2086
|
+
osx = node.matrix.value_scale_x()
|
2087
|
+
osy = node.matrix.value_scale_y()
|
2088
|
+
nsx = scale_x / osx
|
2089
|
+
nsy = scale_y / osy
|
2090
|
+
matrix = Matrix(f"scale({nsx},{nsy},{px},{px})")
|
2091
|
+
node.matrix *= matrix
|
2092
|
+
node.scaled(sx=nsx, sy=nsy, ox=px, oy=py)
|
2093
|
+
if hasattr(node, "update"):
|
2094
|
+
images.append(node)
|
2095
|
+
except ValueError:
|
2096
|
+
raise CommandSyntaxError
|
2097
|
+
for node in images:
|
2098
|
+
self.do_image_update(node)
|
2099
|
+
self.process_keyhole_updates(None)
|
2100
|
+
self.signal("refresh_scene", "Scene")
|
2101
|
+
self.signal("modified_by_tool")
|
2102
|
+
return "elements", data
|
2103
|
+
|
2104
|
+
@self.console_option(
|
2105
|
+
"new_area", "n", type=self.area, help=_("provide a new area to cover")
|
2106
|
+
)
|
2107
|
+
@self.console_option(
|
2108
|
+
"density", "d", type=int, help=_("Defines the interpolation density")
|
2109
|
+
)
|
2110
|
+
@self.console_command(
|
2111
|
+
"area",
|
2112
|
+
help=_("provides information about/changes the area of a selected element"),
|
2113
|
+
input_type=(None, "elements"),
|
2114
|
+
output_type="elements",
|
2115
|
+
)
|
2116
|
+
def element_area(
|
2117
|
+
command,
|
2118
|
+
channel,
|
2119
|
+
_,
|
2120
|
+
new_area=None,
|
2121
|
+
density=None,
|
2122
|
+
data=None,
|
2123
|
+
**kwargs,
|
2124
|
+
):
|
2125
|
+
if density is None:
|
2126
|
+
density = 200
|
2127
|
+
if new_area is None:
|
2128
|
+
display_only = True
|
2129
|
+
else:
|
2130
|
+
if new_area == 0:
|
2131
|
+
channel(_("You shouldn't collapse a shape to a zero-sized thing"))
|
2132
|
+
return
|
2133
|
+
display_only = False
|
2134
|
+
if data is None:
|
2135
|
+
data = list(self.elems(emphasized=True))
|
2136
|
+
if len(data) == 0:
|
2137
|
+
channel(_("No selected elements."))
|
2138
|
+
return
|
2139
|
+
total_area = 0
|
2140
|
+
if display_only:
|
2141
|
+
channel("----------")
|
2142
|
+
channel(_("Area values (Density={density})").format(density=density))
|
2143
|
+
|
2144
|
+
units = ("mm", "cm", "in")
|
2145
|
+
square_unit = [0] * len(units)
|
2146
|
+
for idx, u in enumerate(units):
|
2147
|
+
value = float(Length(f"1{u}"))
|
2148
|
+
square_unit[idx] = value * value
|
2149
|
+
|
2150
|
+
i = 0
|
2151
|
+
for elem in data:
|
2152
|
+
try:
|
2153
|
+
geometry = elem.as_geometry()
|
2154
|
+
except AttributeError:
|
2155
|
+
continue
|
2156
|
+
# this_length = geometry.length()
|
2157
|
+
this_area = geometry.area(density=density)
|
2158
|
+
|
2159
|
+
if display_only:
|
2160
|
+
name = str(elem)
|
2161
|
+
if len(name) > 50:
|
2162
|
+
name = name[:50] + "…"
|
2163
|
+
channel(f"{i}: {name}")
|
2164
|
+
for idx, u in enumerate(units):
|
2165
|
+
this_area_local = this_area / square_unit[idx]
|
2166
|
+
channel(
|
2167
|
+
_(" Area= {area:.3f} {unit}²").format(
|
2168
|
+
area=this_area_local, unit=u
|
2169
|
+
)
|
2170
|
+
)
|
2171
|
+
i += 1
|
2172
|
+
total_area += this_area
|
2173
|
+
if display_only:
|
2174
|
+
channel("----------")
|
2175
|
+
else:
|
2176
|
+
if total_area == 0:
|
2177
|
+
channel(_("You can't reshape a zero-sized shape"))
|
2178
|
+
return
|
2179
|
+
|
2180
|
+
ratio = sqrt(new_area / total_area)
|
2181
|
+
self(f"scale {ratio}\n")
|
2182
|
+
|
2183
|
+
return "elements", data
|
2184
|
+
# Do we have a new value to set? If yes scale by sqrt(of the fraction)
|
2185
|
+
|
2186
|
+
@self.console_argument("tx", type=self.length_x, help=_("translate x value"))
|
2187
|
+
@self.console_argument("ty", type=self.length_y, help=_("translate y value"))
|
2188
|
+
@self.console_option(
|
2189
|
+
"absolute",
|
2190
|
+
"a",
|
2191
|
+
type=bool,
|
2192
|
+
action="store_true",
|
2193
|
+
help=_("translate to absolute position"),
|
2194
|
+
)
|
2195
|
+
@self.console_command(
|
2196
|
+
"translate",
|
2197
|
+
help=_("translate <tx> <ty>"),
|
2198
|
+
input_type=(None, "elements"),
|
2199
|
+
output_type="elements",
|
2200
|
+
)
|
2201
|
+
def element_translate(
|
2202
|
+
command, channel, _, tx, ty, absolute=False, data=None, **kwargs
|
2203
|
+
):
|
2204
|
+
if tx is None:
|
2205
|
+
channel("----------")
|
2206
|
+
channel(_("Translate Values:"))
|
2207
|
+
i = 0
|
2208
|
+
for node in self.elems():
|
2209
|
+
name = str(node)
|
2210
|
+
if len(name) > 50:
|
2211
|
+
name = name[:50] + "…"
|
2212
|
+
channel(
|
2213
|
+
f"{i}: translate({node.matrix.value_trans_x():.1f}, {node.matrix.value_trans_y():.1f}) - {name}"
|
2214
|
+
)
|
2215
|
+
i += 1
|
2216
|
+
channel("----------")
|
2217
|
+
return
|
2218
|
+
if data is None:
|
2219
|
+
data = list(self.elems(emphasized=True))
|
2220
|
+
if len(data) == 0:
|
2221
|
+
channel(_("No selected elements."))
|
2222
|
+
return
|
2223
|
+
if tx is None:
|
2224
|
+
tx = 0
|
2225
|
+
if ty is None:
|
2226
|
+
ty = 0
|
2227
|
+
changes = False
|
2228
|
+
matrix = Matrix.translate(tx, ty)
|
2229
|
+
with self.undoscope("Translate"):
|
2230
|
+
try:
|
2231
|
+
if not absolute:
|
2232
|
+
for node in data:
|
2233
|
+
if not node.can_move(self.lock_allows_move):
|
2234
|
+
continue
|
2235
|
+
|
2236
|
+
node.matrix *= matrix
|
2237
|
+
node.translated(tx, ty)
|
2238
|
+
changes = True
|
2239
|
+
else:
|
2240
|
+
for node in data:
|
2241
|
+
if not node.can_move(self.lock_allows_move):
|
2242
|
+
continue
|
2243
|
+
otx = node.matrix.value_trans_x()
|
2244
|
+
oty = node.matrix.value_trans_y()
|
2245
|
+
ntx = tx - otx
|
2246
|
+
nty = ty - oty
|
2247
|
+
matrix = Matrix.translate(ntx, nty)
|
2248
|
+
node.matrix *= matrix
|
2249
|
+
node.translated(ntx, nty)
|
2250
|
+
changes = True
|
2251
|
+
except ValueError:
|
2252
|
+
raise CommandSyntaxError
|
2253
|
+
if changes:
|
2254
|
+
self.signal("refresh_scene", "Scene")
|
2255
|
+
self.signal("modified_by_tool")
|
2256
|
+
return "elements", data
|
2257
|
+
|
2258
|
+
@self.console_argument("tx", type=self.length_x, help=_("New x value"))
|
2259
|
+
@self.console_argument("ty", type=self.length_y, help=_("New y value"))
|
2260
|
+
@self.console_command(
|
2261
|
+
"position",
|
2262
|
+
help=_("position <tx> <ty>"),
|
2263
|
+
input_type=(None, "elements"),
|
2264
|
+
output_type="elements",
|
2265
|
+
)
|
2266
|
+
def element_position(
|
2267
|
+
command, channel, _, tx, ty, absolute=False, data=None, **kwargs
|
2268
|
+
):
|
2269
|
+
if data is None:
|
2270
|
+
data = list(self.elems(emphasized=True))
|
2271
|
+
if len(data) == 0:
|
2272
|
+
channel(_("No selected elements."))
|
2273
|
+
return
|
2274
|
+
if tx is None or ty is None:
|
2275
|
+
channel(_("You need to provide a new position."))
|
2276
|
+
return
|
2277
|
+
with self.undoscope("Position"):
|
2278
|
+
changes = False
|
2279
|
+
dbounds = Node.union_bounds(data)
|
2280
|
+
for node in data:
|
2281
|
+
if not node.can_move(self.lock_allows_move):
|
2282
|
+
continue
|
2283
|
+
nbounds = node.bounds
|
2284
|
+
dx = tx - dbounds[0]
|
2285
|
+
dy = ty - dbounds[1]
|
2286
|
+
if dx != 0 or dy != 0:
|
2287
|
+
node.matrix.post_translate(dx, dy)
|
2288
|
+
# node.modified()
|
2289
|
+
node.translated(dx, dy)
|
2290
|
+
changes = True
|
2291
|
+
if changes:
|
2292
|
+
self.process_keyhole_updates(None)
|
2293
|
+
self.signal("refresh_scene", "Scene")
|
2294
|
+
self.signal("modified_by_tool")
|
2295
|
+
return "elements", data
|
2296
|
+
|
2297
|
+
@self.console_command(
|
2298
|
+
"move_to_laser",
|
2299
|
+
help=_("translates the selected element to the laser head"),
|
2300
|
+
input_type=(None, "elements"),
|
2301
|
+
output_type="elements",
|
2302
|
+
)
|
2303
|
+
def element_move_to_laser(command, channel, _, data=None, **kwargs):
|
2304
|
+
if data is None:
|
2305
|
+
data = list(self.elems(emphasized=True))
|
2306
|
+
if len(data) == 0:
|
2307
|
+
channel(_("No selected elements."))
|
2308
|
+
return
|
2309
|
+
tx, ty = self.device.current
|
2310
|
+
with self.undoscope("Translate to laser"):
|
2311
|
+
try:
|
2312
|
+
bounds = Node.union_bounds(data)
|
2313
|
+
otx = bounds[0]
|
2314
|
+
oty = bounds[1]
|
2315
|
+
ntx = tx - otx
|
2316
|
+
nty = ty - oty
|
2317
|
+
for node in data:
|
2318
|
+
if not node.can_move(self.lock_allows_move):
|
2319
|
+
continue
|
2320
|
+
node.matrix.post_translate(ntx, nty)
|
2321
|
+
# node.modified()
|
2322
|
+
node.translated(ntx, nty)
|
2323
|
+
except ValueError:
|
2324
|
+
raise CommandSyntaxError
|
2325
|
+
return "elements", data
|
2326
|
+
|
2327
|
+
@self.console_argument(
|
2328
|
+
"x_pos", type=self.length_x, help=_("x position for top left corner")
|
2329
|
+
)
|
2330
|
+
@self.console_argument(
|
2331
|
+
"y_pos", type=self.length_y, help=_("y position for top left corner")
|
2332
|
+
)
|
2333
|
+
@self.console_argument("width", type=self.length_x, help=_("new width of selected"))
|
2334
|
+
@self.console_argument(
|
2335
|
+
"height", type=self.length_y, help=_("new height of selected")
|
2336
|
+
)
|
2337
|
+
@self.console_command(
|
2338
|
+
"resize",
|
2339
|
+
help=_("resize <x-pos> <y-pos> <width> <height>"),
|
2340
|
+
input_type=(None, "elements"),
|
2341
|
+
output_type="elements",
|
2342
|
+
)
|
2343
|
+
def element_resize(
|
2344
|
+
command, channel, _, x_pos, y_pos, width, height, data=None, **kwargs
|
2345
|
+
):
|
2346
|
+
if height is None:
|
2347
|
+
raise CommandSyntaxError
|
2348
|
+
if data is None:
|
2349
|
+
data = list(self.elems(emphasized=True))
|
2350
|
+
if len(data) == 0:
|
2351
|
+
channel(_("No selected elements."))
|
2352
|
+
return
|
2353
|
+
area = Node.union_bounds(data)
|
2354
|
+
if area is None:
|
2355
|
+
channel(_("resize: nothing selected"))
|
2356
|
+
return
|
2357
|
+
x, y, x1, y1 = area
|
2358
|
+
w, h = x1 - x, y1 - y
|
2359
|
+
if w == 0 or h == 0: # dot
|
2360
|
+
channel(_("resize: cannot resize a dot"))
|
2361
|
+
return
|
2362
|
+
sx = width / w
|
2363
|
+
sy = height / h
|
2364
|
+
if sx == 0 or sy == 0:
|
2365
|
+
channel(_("Invalid width/height"))
|
2366
|
+
return
|
2367
|
+
px = area[0]
|
2368
|
+
py = area[2]
|
2369
|
+
matrix = Matrix(f"scale({sx},{sy},{px},{py})")
|
2370
|
+
with self.undoscope("Resize"):
|
2371
|
+
images = []
|
2372
|
+
if sx != 1.0 or sy != 1.0:
|
2373
|
+
# Don't do anything if scale is 1
|
2374
|
+
for node in data:
|
2375
|
+
if not hasattr(node, "matrix"):
|
2376
|
+
continue
|
2377
|
+
if hasattr(node, "lock") and node.lock:
|
2378
|
+
continue
|
2379
|
+
node.matrix *= matrix
|
2380
|
+
node.modified()
|
2381
|
+
if hasattr(node, "update") and node not in images:
|
2382
|
+
images.append(node)
|
2383
|
+
|
2384
|
+
# Calculate again
|
2385
|
+
area = Node.union_bounds(data)
|
2386
|
+
dx = x_pos - area[0]
|
2387
|
+
dy = y_pos - area[1]
|
2388
|
+
if dx != 0.0 or dy != 0.0:
|
2389
|
+
# Don't do anything if scale is 1
|
2390
|
+
for node in data:
|
2391
|
+
if not hasattr(node, "matrix"):
|
2392
|
+
continue
|
2393
|
+
node.matrix.post_translate(dx, dy)
|
2394
|
+
node.modified()
|
2395
|
+
if hasattr(node, "update") and node not in images:
|
2396
|
+
images.append(node)
|
2397
|
+
|
2398
|
+
for node in images:
|
2399
|
+
self.do_image_update(node)
|
2400
|
+
self.signal("refresh_scene", "Scene")
|
2401
|
+
return "elements", data
|
2402
|
+
|
2403
|
+
@self.console_argument("sx", type=float, help=_("scale_x value"))
|
2404
|
+
@self.console_argument("kx", type=float, help=_("skew_x value"))
|
2405
|
+
@self.console_argument("ky", type=float, help=_("skew_y value"))
|
2406
|
+
@self.console_argument("sy", type=float, help=_("scale_y value"))
|
2407
|
+
@self.console_argument("tx", type=self.length_x, help=_("translate_x value"))
|
2408
|
+
@self.console_argument("ty", type=self.length_y, help=_("translate_y value"))
|
2409
|
+
@self.console_command(
|
2410
|
+
"matrix",
|
2411
|
+
help=_("matrix <sx> <kx> <ky> <sy> <tx> <ty>"),
|
2412
|
+
input_type=(None, "elements"),
|
2413
|
+
output_type="elements",
|
2414
|
+
)
|
2415
|
+
def element_matrix(
|
2416
|
+
command, channel, _, sx, kx, ky, sy, tx, ty, data=None, **kwargs
|
2417
|
+
):
|
2418
|
+
if data is None:
|
2419
|
+
data = list(self.elems(emphasized=True))
|
2420
|
+
if ty is None:
|
2421
|
+
channel("----------")
|
2422
|
+
channel(_("Matrix Values:"))
|
2423
|
+
i = 0
|
2424
|
+
for node in data:
|
2425
|
+
name = str(node)
|
2426
|
+
if len(name) > 50:
|
2427
|
+
name = name[:50] + "…"
|
2428
|
+
channel(f"{i}: {str(node.matrix)} - {name}")
|
2429
|
+
i += 1
|
2430
|
+
channel("----------")
|
2431
|
+
return
|
2432
|
+
if len(data) == 0:
|
2433
|
+
channel(_("No selected elements."))
|
2434
|
+
return
|
2435
|
+
with self.undoscope("Matrix"):
|
2436
|
+
images = []
|
2437
|
+
try:
|
2438
|
+
# SVG 7.15.3 defines the matrix form as:
|
2439
|
+
# [a c e]
|
2440
|
+
# [b d f]
|
2441
|
+
m = Matrix(
|
2442
|
+
sx,
|
2443
|
+
kx,
|
2444
|
+
ky,
|
2445
|
+
sy,
|
2446
|
+
tx,
|
2447
|
+
ty,
|
2448
|
+
)
|
2449
|
+
for node in data:
|
2450
|
+
if hasattr(node, "lock") and node.lock:
|
2451
|
+
continue
|
2452
|
+
node.matrix = Matrix(m)
|
2453
|
+
node.modified()
|
2454
|
+
if hasattr(node, "update"):
|
2455
|
+
images.append(node)
|
2456
|
+
except ValueError:
|
2457
|
+
raise CommandSyntaxError
|
2458
|
+
for node in images:
|
2459
|
+
self.do_image_update(node)
|
2460
|
+
self.signal("refresh_scene", "Scene")
|
2461
|
+
return
|
2462
|
+
|
2463
|
+
@self.console_command(
|
2464
|
+
"reset",
|
2465
|
+
help=_("reset affine transformations"),
|
2466
|
+
input_type=(None, "elements"),
|
2467
|
+
output_type="elements",
|
2468
|
+
)
|
2469
|
+
def reset(command, channel, _, data=None, **kwargs):
|
2470
|
+
if data is None:
|
2471
|
+
data = list(self.elems(emphasized=True))
|
2472
|
+
with self.undoscope("Reset"):
|
2473
|
+
images = []
|
2474
|
+
for e in data:
|
2475
|
+
if hasattr(e, "lock") and e.lock:
|
2476
|
+
continue
|
2477
|
+
name = str(e)
|
2478
|
+
if len(name) > 50:
|
2479
|
+
name = name[:50] + "…"
|
2480
|
+
channel(_("reset - {name}").format(name=name))
|
2481
|
+
e.matrix.reset()
|
2482
|
+
e.modified()
|
2483
|
+
if hasattr(e, "update"):
|
2484
|
+
images.append(e)
|
2485
|
+
for e in images:
|
2486
|
+
self.do_image_update(e)
|
2487
|
+
self.signal("refresh_scene", "Scene")
|
2488
|
+
return "elements", data
|
2489
|
+
|
2490
|
+
@self.console_command(
|
2491
|
+
"reify",
|
2492
|
+
help=_("reify affine transformations"),
|
2493
|
+
input_type=(None, "elements"),
|
2494
|
+
output_type="elements",
|
2495
|
+
)
|
2496
|
+
def element_reify(command, channel, _, data=None, **kwargs):
|
2497
|
+
if data is None:
|
2498
|
+
data = list(self.elems(emphasized=True))
|
2499
|
+
with self.undoscope("Reify"):
|
2500
|
+
for e in data:
|
2501
|
+
try:
|
2502
|
+
if e.lock:
|
2503
|
+
continue
|
2504
|
+
except AttributeError:
|
2505
|
+
pass
|
2506
|
+
|
2507
|
+
name = str(e)
|
2508
|
+
if len(name) > 50:
|
2509
|
+
name = name[:50] + "…"
|
2510
|
+
try:
|
2511
|
+
e.stroke_reify()
|
2512
|
+
except AttributeError:
|
2513
|
+
pass
|
2514
|
+
|
2515
|
+
try:
|
2516
|
+
e.shape.reify()
|
2517
|
+
except AttributeError as err:
|
2518
|
+
try:
|
2519
|
+
e.path.reify()
|
2520
|
+
except AttributeError:
|
2521
|
+
channel(_("Couldn't reify - %s - %s") % (name, err))
|
2522
|
+
return "elements", data
|
2523
|
+
try:
|
2524
|
+
e.stroke_width_zero()
|
2525
|
+
except AttributeError:
|
2526
|
+
pass
|
2527
|
+
e.altered()
|
2528
|
+
channel(_("reified - %s") % name)
|
2529
|
+
return "elements", data
|
2530
|
+
|
2531
|
+
@self.console_command(
|
2532
|
+
"circle_arc_path",
|
2533
|
+
help=_("Convert paths to use circular arcs."),
|
2534
|
+
input_type=(None, "elements"),
|
2535
|
+
output_type="elements",
|
2536
|
+
)
|
2537
|
+
def element_circ_arc_path(command, channel, _, data=None, **kwargs):
|
2538
|
+
if data is None:
|
2539
|
+
data = list(self.elems(emphasized=True))
|
2540
|
+
# _("Convert paths")
|
2541
|
+
with self.undoscope("Convert paths"):
|
2542
|
+
for e in data:
|
2543
|
+
try:
|
2544
|
+
if e.lock:
|
2545
|
+
continue
|
2546
|
+
except AttributeError:
|
2547
|
+
pass
|
2548
|
+
if e.type == "elem path":
|
2549
|
+
g = e.geometry
|
2550
|
+
path = g.as_path()
|
2551
|
+
path.approximate_bezier_with_circular_arcs()
|
2552
|
+
e.geometry = Geomstr.svg(path)
|
2553
|
+
e.altered()
|
2554
|
+
|
2555
|
+
return "elements", data
|
2556
|
+
|
2557
|
+
@self.console_command(
|
2558
|
+
"classify",
|
2559
|
+
help=_("classify elements into operations"),
|
2560
|
+
input_type=(None, "elements"),
|
2561
|
+
output_type="elements",
|
2562
|
+
)
|
2563
|
+
def element_classify(command, channel, _, data=None, **kwargs):
|
2564
|
+
if data is None:
|
2565
|
+
data = list(self.elems(emphasized=True))
|
2566
|
+
was_emphasized = True
|
2567
|
+
old_first = self.first_emphasized
|
2568
|
+
else:
|
2569
|
+
was_emphasized = False
|
2570
|
+
old_first = None
|
2571
|
+
if len(data) == 0:
|
2572
|
+
channel(_("No selected elements."))
|
2573
|
+
return
|
2574
|
+
with self.undoscope("Classify"):
|
2575
|
+
self.classify(data)
|
2576
|
+
if was_emphasized:
|
2577
|
+
for e in data:
|
2578
|
+
e.emphasized = True
|
2579
|
+
if len(data) == 1:
|
2580
|
+
data[0].focus()
|
2581
|
+
if old_first is not None and old_first in data:
|
2582
|
+
self.first_emphasized = old_first
|
2583
|
+
else:
|
2584
|
+
self.first_emphasized = None
|
2585
|
+
|
2586
|
+
return "elements", data
|
2587
|
+
|
2588
|
+
@self.console_command(
|
2589
|
+
"declassify",
|
2590
|
+
help=_("declassify selected elements"),
|
2591
|
+
input_type=(None, "elements"),
|
2592
|
+
output_type="elements",
|
2593
|
+
)
|
2594
|
+
def declassify(command, channel, _, data=None, **kwargs):
|
2595
|
+
if data is None:
|
2596
|
+
data = list(self.elems(emphasized=True))
|
2597
|
+
was_emphasized = True
|
2598
|
+
old_first = self.first_emphasized
|
2599
|
+
else:
|
2600
|
+
was_emphasized = False
|
2601
|
+
old_first = None
|
2602
|
+
if len(data) == 0:
|
2603
|
+
channel(_("No selected elements."))
|
2604
|
+
return
|
2605
|
+
self.remove_elements_from_operations(data)
|
2606
|
+
# restore emphasized flag as it is relevant for subsequent operations
|
2607
|
+
if was_emphasized:
|
2608
|
+
for e in data:
|
2609
|
+
e.emphasized = True
|
2610
|
+
if len(data) == 1:
|
2611
|
+
data[0].focus()
|
2612
|
+
if old_first is not None and old_first in data:
|
2613
|
+
self.first_emphasized = old_first
|
2614
|
+
else:
|
2615
|
+
self.first_emphasized = None
|
2616
|
+
return "elements", data
|
2617
|
+
|
2618
|
+
# --------------------------- END COMMANDS ------------------------------
|