meerk40t 0.9.3001__py2.py3-none-any.whl → 0.9.7010__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- meerk40t/__init__.py +1 -1
- meerk40t/balormk/balor_params.py +167 -167
- meerk40t/balormk/clone_loader.py +457 -457
- meerk40t/balormk/controller.py +1566 -1512
- meerk40t/balormk/cylindermod.py +64 -0
- meerk40t/balormk/device.py +966 -1959
- meerk40t/balormk/driver.py +778 -591
- meerk40t/balormk/galvo_commands.py +1195 -0
- meerk40t/balormk/gui/balorconfig.py +237 -111
- meerk40t/balormk/gui/balorcontroller.py +191 -184
- meerk40t/balormk/gui/baloroperationproperties.py +116 -115
- meerk40t/balormk/gui/corscene.py +845 -0
- meerk40t/balormk/gui/gui.py +179 -147
- meerk40t/balormk/livelightjob.py +466 -382
- meerk40t/balormk/mock_connection.py +131 -109
- meerk40t/balormk/plugin.py +133 -135
- meerk40t/balormk/usb_connection.py +306 -301
- meerk40t/camera/__init__.py +1 -1
- meerk40t/camera/camera.py +514 -397
- meerk40t/camera/gui/camerapanel.py +1241 -1095
- meerk40t/camera/gui/gui.py +58 -58
- meerk40t/camera/plugin.py +441 -399
- meerk40t/ch341/__init__.py +27 -27
- meerk40t/ch341/ch341device.py +628 -628
- meerk40t/ch341/libusb.py +595 -589
- meerk40t/ch341/mock.py +171 -171
- meerk40t/ch341/windriver.py +157 -157
- meerk40t/constants.py +13 -0
- meerk40t/core/__init__.py +1 -1
- meerk40t/core/bindalias.py +550 -539
- meerk40t/core/core.py +47 -47
- meerk40t/core/cutcode/cubiccut.py +73 -73
- meerk40t/core/cutcode/cutcode.py +315 -312
- meerk40t/core/cutcode/cutgroup.py +141 -137
- meerk40t/core/cutcode/cutobject.py +192 -185
- meerk40t/core/cutcode/dwellcut.py +37 -37
- meerk40t/core/cutcode/gotocut.py +29 -29
- meerk40t/core/cutcode/homecut.py +29 -29
- meerk40t/core/cutcode/inputcut.py +34 -34
- meerk40t/core/cutcode/linecut.py +33 -33
- meerk40t/core/cutcode/outputcut.py +34 -34
- meerk40t/core/cutcode/plotcut.py +335 -335
- meerk40t/core/cutcode/quadcut.py +61 -61
- meerk40t/core/cutcode/rastercut.py +168 -148
- meerk40t/core/cutcode/waitcut.py +34 -34
- meerk40t/core/cutplan.py +1843 -1316
- meerk40t/core/drivers.py +330 -329
- meerk40t/core/elements/align.py +801 -669
- meerk40t/core/elements/branches.py +1844 -1507
- meerk40t/core/elements/clipboard.py +229 -219
- meerk40t/core/elements/element_treeops.py +4561 -2837
- meerk40t/core/elements/element_types.py +125 -105
- meerk40t/core/elements/elements.py +4329 -3617
- meerk40t/core/elements/files.py +117 -64
- meerk40t/core/elements/geometry.py +473 -224
- meerk40t/core/elements/grid.py +467 -316
- meerk40t/core/elements/materials.py +158 -94
- meerk40t/core/elements/notes.py +50 -38
- meerk40t/core/elements/offset_clpr.py +933 -912
- meerk40t/core/elements/offset_mk.py +963 -955
- meerk40t/core/elements/penbox.py +339 -267
- meerk40t/core/elements/placements.py +300 -83
- meerk40t/core/elements/render.py +785 -687
- meerk40t/core/elements/shapes.py +2618 -2092
- meerk40t/core/elements/trace.py +651 -563
- meerk40t/core/elements/tree_commands.py +415 -409
- meerk40t/core/elements/undo_redo.py +116 -58
- meerk40t/core/elements/wordlist.py +319 -200
- meerk40t/core/exceptions.py +9 -9
- meerk40t/core/laserjob.py +220 -220
- meerk40t/core/logging.py +63 -63
- meerk40t/core/node/blobnode.py +83 -86
- meerk40t/core/node/bootstrap.py +105 -103
- meerk40t/core/node/branch_elems.py +40 -31
- meerk40t/core/node/branch_ops.py +45 -38
- meerk40t/core/node/branch_regmark.py +48 -41
- meerk40t/core/node/cutnode.py +29 -32
- meerk40t/core/node/effect_hatch.py +375 -257
- meerk40t/core/node/effect_warp.py +398 -0
- meerk40t/core/node/effect_wobble.py +441 -309
- meerk40t/core/node/elem_ellipse.py +404 -309
- meerk40t/core/node/elem_image.py +1082 -801
- meerk40t/core/node/elem_line.py +358 -292
- meerk40t/core/node/elem_path.py +259 -201
- meerk40t/core/node/elem_point.py +129 -102
- meerk40t/core/node/elem_polyline.py +310 -246
- meerk40t/core/node/elem_rect.py +376 -286
- meerk40t/core/node/elem_text.py +445 -418
- meerk40t/core/node/filenode.py +59 -40
- meerk40t/core/node/groupnode.py +138 -74
- meerk40t/core/node/image_processed.py +777 -766
- meerk40t/core/node/image_raster.py +156 -113
- meerk40t/core/node/layernode.py +31 -31
- meerk40t/core/node/mixins.py +135 -107
- meerk40t/core/node/node.py +1427 -1304
- meerk40t/core/node/nutils.py +117 -114
- meerk40t/core/node/op_cut.py +462 -335
- meerk40t/core/node/op_dots.py +296 -251
- meerk40t/core/node/op_engrave.py +414 -311
- meerk40t/core/node/op_image.py +755 -369
- meerk40t/core/node/op_raster.py +787 -522
- meerk40t/core/node/place_current.py +37 -40
- meerk40t/core/node/place_point.py +329 -126
- meerk40t/core/node/refnode.py +58 -47
- meerk40t/core/node/rootnode.py +225 -219
- meerk40t/core/node/util_console.py +48 -48
- meerk40t/core/node/util_goto.py +84 -65
- meerk40t/core/node/util_home.py +61 -61
- meerk40t/core/node/util_input.py +102 -102
- meerk40t/core/node/util_output.py +102 -102
- meerk40t/core/node/util_wait.py +65 -65
- meerk40t/core/parameters.py +709 -707
- meerk40t/core/planner.py +875 -785
- meerk40t/core/plotplanner.py +656 -652
- meerk40t/core/space.py +120 -113
- meerk40t/core/spoolers.py +706 -705
- meerk40t/core/svg_io.py +1836 -1549
- meerk40t/core/treeop.py +534 -445
- meerk40t/core/undos.py +278 -124
- meerk40t/core/units.py +784 -680
- meerk40t/core/view.py +393 -322
- meerk40t/core/webhelp.py +62 -62
- meerk40t/core/wordlist.py +513 -504
- meerk40t/cylinder/cylinder.py +247 -0
- meerk40t/cylinder/gui/cylindersettings.py +41 -0
- meerk40t/cylinder/gui/gui.py +24 -0
- meerk40t/device/__init__.py +1 -1
- meerk40t/device/basedevice.py +322 -123
- meerk40t/device/devicechoices.py +50 -0
- meerk40t/device/dummydevice.py +163 -128
- meerk40t/device/gui/defaultactions.py +618 -602
- meerk40t/device/gui/effectspanel.py +114 -0
- meerk40t/device/gui/formatterpanel.py +253 -290
- meerk40t/device/gui/warningpanel.py +337 -260
- meerk40t/device/mixins.py +13 -13
- meerk40t/dxf/__init__.py +1 -1
- meerk40t/dxf/dxf_io.py +766 -554
- meerk40t/dxf/plugin.py +47 -35
- meerk40t/external_plugins.py +79 -79
- meerk40t/external_plugins_build.py +28 -28
- meerk40t/extra/cag.py +112 -116
- meerk40t/extra/coolant.py +403 -0
- meerk40t/extra/encode_detect.py +198 -0
- meerk40t/extra/ezd.py +1165 -1165
- meerk40t/extra/hershey.py +835 -340
- meerk40t/extra/imageactions.py +322 -316
- meerk40t/extra/inkscape.py +630 -622
- meerk40t/extra/lbrn.py +424 -424
- meerk40t/extra/outerworld.py +284 -0
- meerk40t/extra/param_functions.py +1542 -1556
- meerk40t/extra/potrace.py +257 -253
- meerk40t/extra/serial_exchange.py +118 -0
- meerk40t/extra/updater.py +602 -453
- meerk40t/extra/vectrace.py +147 -146
- meerk40t/extra/winsleep.py +83 -83
- meerk40t/extra/xcs_reader.py +597 -0
- meerk40t/fill/fills.py +781 -335
- meerk40t/fill/patternfill.py +1061 -1061
- meerk40t/fill/patterns.py +614 -567
- meerk40t/grbl/control.py +87 -87
- meerk40t/grbl/controller.py +990 -903
- meerk40t/grbl/device.py +1081 -768
- meerk40t/grbl/driver.py +989 -771
- meerk40t/grbl/emulator.py +532 -497
- meerk40t/grbl/gcodejob.py +783 -767
- meerk40t/grbl/gui/grblconfiguration.py +373 -298
- meerk40t/grbl/gui/grblcontroller.py +485 -271
- meerk40t/grbl/gui/grblhardwareconfig.py +269 -153
- meerk40t/grbl/gui/grbloperationconfig.py +105 -0
- meerk40t/grbl/gui/gui.py +147 -116
- meerk40t/grbl/interpreter.py +44 -44
- meerk40t/grbl/loader.py +22 -22
- meerk40t/grbl/mock_connection.py +56 -56
- meerk40t/grbl/plugin.py +294 -264
- meerk40t/grbl/serial_connection.py +93 -88
- meerk40t/grbl/tcp_connection.py +81 -79
- meerk40t/grbl/ws_connection.py +112 -0
- meerk40t/gui/__init__.py +1 -1
- meerk40t/gui/about.py +2042 -296
- meerk40t/gui/alignment.py +1644 -1608
- meerk40t/gui/autoexec.py +199 -0
- meerk40t/gui/basicops.py +791 -670
- meerk40t/gui/bufferview.py +77 -71
- meerk40t/gui/busy.py +170 -133
- meerk40t/gui/choicepropertypanel.py +1673 -1469
- meerk40t/gui/consolepanel.py +706 -542
- meerk40t/gui/devicepanel.py +687 -581
- meerk40t/gui/dialogoptions.py +110 -107
- meerk40t/gui/executejob.py +316 -306
- meerk40t/gui/fonts.py +90 -90
- meerk40t/gui/functionwrapper.py +252 -0
- meerk40t/gui/gui_mixins.py +729 -0
- meerk40t/gui/guicolors.py +205 -182
- meerk40t/gui/help_assets/help_assets.py +218 -201
- meerk40t/gui/helper.py +154 -0
- meerk40t/gui/hersheymanager.py +1430 -846
- meerk40t/gui/icons.py +3422 -2747
- meerk40t/gui/imagesplitter.py +555 -508
- meerk40t/gui/keymap.py +354 -344
- meerk40t/gui/laserpanel.py +892 -806
- meerk40t/gui/laserrender.py +1470 -1232
- meerk40t/gui/lasertoolpanel.py +805 -793
- meerk40t/gui/magnetoptions.py +436 -0
- meerk40t/gui/materialmanager.py +2917 -0
- meerk40t/gui/materialtest.py +1722 -1694
- meerk40t/gui/mkdebug.py +646 -359
- meerk40t/gui/mwindow.py +163 -140
- meerk40t/gui/navigationpanels.py +2605 -2467
- meerk40t/gui/notes.py +143 -142
- meerk40t/gui/opassignment.py +414 -410
- meerk40t/gui/operation_info.py +310 -299
- meerk40t/gui/plugin.py +494 -328
- meerk40t/gui/position.py +714 -669
- meerk40t/gui/preferences.py +901 -650
- meerk40t/gui/propertypanels/attributes.py +1461 -1131
- meerk40t/gui/propertypanels/blobproperty.py +117 -114
- meerk40t/gui/propertypanels/consoleproperty.py +83 -80
- meerk40t/gui/propertypanels/gotoproperty.py +77 -0
- meerk40t/gui/propertypanels/groupproperties.py +223 -217
- meerk40t/gui/propertypanels/hatchproperty.py +489 -469
- meerk40t/gui/propertypanels/imageproperty.py +2244 -1384
- meerk40t/gui/propertypanels/inputproperty.py +59 -58
- meerk40t/gui/propertypanels/opbranchproperties.py +82 -80
- meerk40t/gui/propertypanels/operationpropertymain.py +1890 -1638
- meerk40t/gui/propertypanels/outputproperty.py +59 -58
- meerk40t/gui/propertypanels/pathproperty.py +389 -380
- meerk40t/gui/propertypanels/placementproperty.py +1214 -383
- meerk40t/gui/propertypanels/pointproperty.py +140 -136
- meerk40t/gui/propertypanels/propertywindow.py +313 -181
- meerk40t/gui/propertypanels/rasterwizardpanels.py +996 -912
- meerk40t/gui/propertypanels/regbranchproperties.py +76 -0
- meerk40t/gui/propertypanels/textproperty.py +770 -755
- meerk40t/gui/propertypanels/waitproperty.py +56 -55
- meerk40t/gui/propertypanels/warpproperty.py +121 -0
- meerk40t/gui/propertypanels/wobbleproperty.py +255 -204
- meerk40t/gui/ribbon.py +2468 -2210
- meerk40t/gui/scene/scene.py +1100 -1051
- meerk40t/gui/scene/sceneconst.py +22 -22
- meerk40t/gui/scene/scenepanel.py +439 -349
- meerk40t/gui/scene/scenespacewidget.py +365 -365
- meerk40t/gui/scene/widget.py +518 -505
- meerk40t/gui/scenewidgets/affinemover.py +215 -215
- meerk40t/gui/scenewidgets/attractionwidget.py +315 -309
- meerk40t/gui/scenewidgets/bedwidget.py +120 -97
- meerk40t/gui/scenewidgets/elementswidget.py +137 -107
- meerk40t/gui/scenewidgets/gridwidget.py +785 -745
- meerk40t/gui/scenewidgets/guidewidget.py +765 -765
- meerk40t/gui/scenewidgets/laserpathwidget.py +66 -66
- meerk40t/gui/scenewidgets/machineoriginwidget.py +86 -86
- meerk40t/gui/scenewidgets/nodeselector.py +28 -28
- meerk40t/gui/scenewidgets/rectselectwidget.py +589 -346
- meerk40t/gui/scenewidgets/relocatewidget.py +33 -33
- meerk40t/gui/scenewidgets/reticlewidget.py +83 -83
- meerk40t/gui/scenewidgets/selectionwidget.py +2952 -2756
- meerk40t/gui/simpleui.py +357 -333
- meerk40t/gui/simulation.py +2431 -2094
- meerk40t/gui/snapoptions.py +208 -203
- meerk40t/gui/spoolerpanel.py +1227 -1180
- meerk40t/gui/statusbarwidgets/defaultoperations.py +480 -353
- meerk40t/gui/statusbarwidgets/infowidget.py +520 -483
- meerk40t/gui/statusbarwidgets/opassignwidget.py +356 -355
- meerk40t/gui/statusbarwidgets/selectionwidget.py +172 -171
- meerk40t/gui/statusbarwidgets/shapepropwidget.py +754 -236
- meerk40t/gui/statusbarwidgets/statusbar.py +272 -260
- meerk40t/gui/statusbarwidgets/statusbarwidget.py +268 -270
- meerk40t/gui/statusbarwidgets/strokewidget.py +267 -251
- meerk40t/gui/themes.py +200 -78
- meerk40t/gui/tips.py +591 -0
- meerk40t/gui/toolwidgets/circlebrush.py +35 -35
- meerk40t/gui/toolwidgets/toolcircle.py +248 -242
- meerk40t/gui/toolwidgets/toolcontainer.py +82 -77
- meerk40t/gui/toolwidgets/tooldraw.py +97 -90
- meerk40t/gui/toolwidgets/toolellipse.py +219 -212
- meerk40t/gui/toolwidgets/toolimagecut.py +25 -132
- meerk40t/gui/toolwidgets/toolline.py +39 -144
- meerk40t/gui/toolwidgets/toollinetext.py +79 -236
- meerk40t/gui/toolwidgets/toollinetext_inline.py +296 -0
- meerk40t/gui/toolwidgets/toolmeasure.py +160 -216
- meerk40t/gui/toolwidgets/toolnodeedit.py +2088 -2074
- meerk40t/gui/toolwidgets/toolnodemove.py +92 -94
- meerk40t/gui/toolwidgets/toolparameter.py +754 -668
- meerk40t/gui/toolwidgets/toolplacement.py +108 -108
- meerk40t/gui/toolwidgets/toolpoint.py +68 -59
- meerk40t/gui/toolwidgets/toolpointlistbuilder.py +294 -0
- meerk40t/gui/toolwidgets/toolpointmove.py +183 -0
- meerk40t/gui/toolwidgets/toolpolygon.py +288 -403
- meerk40t/gui/toolwidgets/toolpolyline.py +38 -196
- meerk40t/gui/toolwidgets/toolrect.py +211 -207
- meerk40t/gui/toolwidgets/toolrelocate.py +72 -72
- meerk40t/gui/toolwidgets/toolribbon.py +598 -113
- meerk40t/gui/toolwidgets/tooltabedit.py +546 -0
- meerk40t/gui/toolwidgets/tooltext.py +98 -89
- meerk40t/gui/toolwidgets/toolvector.py +213 -204
- meerk40t/gui/toolwidgets/toolwidget.py +39 -39
- meerk40t/gui/usbconnect.py +98 -91
- meerk40t/gui/utilitywidgets/buttonwidget.py +18 -18
- meerk40t/gui/utilitywidgets/checkboxwidget.py +90 -90
- meerk40t/gui/utilitywidgets/controlwidget.py +14 -14
- meerk40t/gui/utilitywidgets/cyclocycloidwidget.py +343 -340
- meerk40t/gui/utilitywidgets/debugwidgets.py +148 -0
- meerk40t/gui/utilitywidgets/handlewidget.py +27 -27
- meerk40t/gui/utilitywidgets/harmonograph.py +450 -447
- meerk40t/gui/utilitywidgets/openclosewidget.py +40 -40
- meerk40t/gui/utilitywidgets/rotationwidget.py +54 -54
- meerk40t/gui/utilitywidgets/scalewidget.py +75 -75
- meerk40t/gui/utilitywidgets/seekbarwidget.py +183 -183
- meerk40t/gui/utilitywidgets/togglewidget.py +142 -142
- meerk40t/gui/utilitywidgets/toolbarwidget.py +8 -8
- meerk40t/gui/wordlisteditor.py +985 -931
- meerk40t/gui/wxmeerk40t.py +1444 -1169
- meerk40t/gui/wxmmain.py +5578 -4112
- meerk40t/gui/wxmribbon.py +1591 -1076
- meerk40t/gui/wxmscene.py +1635 -1453
- meerk40t/gui/wxmtree.py +2410 -2089
- meerk40t/gui/wxutils.py +1769 -1099
- meerk40t/gui/zmatrix.py +102 -102
- meerk40t/image/__init__.py +1 -1
- meerk40t/image/dither.py +429 -0
- meerk40t/image/imagetools.py +2778 -2269
- meerk40t/internal_plugins.py +150 -130
- meerk40t/kernel/__init__.py +63 -12
- meerk40t/kernel/channel.py +259 -212
- meerk40t/kernel/context.py +538 -538
- meerk40t/kernel/exceptions.py +41 -41
- meerk40t/kernel/functions.py +463 -414
- meerk40t/kernel/jobs.py +100 -100
- meerk40t/kernel/kernel.py +3809 -3571
- meerk40t/kernel/lifecycles.py +71 -71
- meerk40t/kernel/module.py +49 -49
- meerk40t/kernel/service.py +147 -147
- meerk40t/kernel/settings.py +383 -343
- meerk40t/lihuiyu/controller.py +883 -876
- meerk40t/lihuiyu/device.py +1181 -1069
- meerk40t/lihuiyu/driver.py +1466 -1372
- meerk40t/lihuiyu/gui/gui.py +127 -106
- meerk40t/lihuiyu/gui/lhyaccelgui.py +377 -363
- meerk40t/lihuiyu/gui/lhycontrollergui.py +741 -651
- meerk40t/lihuiyu/gui/lhydrivergui.py +470 -446
- meerk40t/lihuiyu/gui/lhyoperationproperties.py +238 -237
- meerk40t/lihuiyu/gui/tcpcontroller.py +226 -190
- meerk40t/lihuiyu/interpreter.py +53 -53
- meerk40t/lihuiyu/laserspeed.py +450 -450
- meerk40t/lihuiyu/loader.py +90 -90
- meerk40t/lihuiyu/parser.py +404 -404
- meerk40t/lihuiyu/plugin.py +101 -102
- meerk40t/lihuiyu/tcp_connection.py +111 -109
- meerk40t/main.py +231 -165
- meerk40t/moshi/builder.py +788 -781
- meerk40t/moshi/controller.py +505 -499
- meerk40t/moshi/device.py +495 -442
- meerk40t/moshi/driver.py +862 -696
- meerk40t/moshi/gui/gui.py +78 -76
- meerk40t/moshi/gui/moshicontrollergui.py +538 -522
- meerk40t/moshi/gui/moshidrivergui.py +87 -75
- meerk40t/moshi/plugin.py +43 -43
- meerk40t/network/console_server.py +102 -57
- meerk40t/network/kernelserver.py +10 -9
- meerk40t/network/tcp_server.py +142 -140
- meerk40t/network/udp_server.py +103 -77
- meerk40t/network/web_server.py +390 -0
- meerk40t/newly/controller.py +1158 -1144
- meerk40t/newly/device.py +874 -732
- meerk40t/newly/driver.py +540 -412
- meerk40t/newly/gui/gui.py +219 -188
- meerk40t/newly/gui/newlyconfig.py +116 -101
- meerk40t/newly/gui/newlycontroller.py +193 -186
- meerk40t/newly/gui/operationproperties.py +51 -51
- meerk40t/newly/mock_connection.py +82 -82
- meerk40t/newly/newly_params.py +56 -56
- meerk40t/newly/plugin.py +1214 -1246
- meerk40t/newly/usb_connection.py +322 -322
- meerk40t/rotary/gui/gui.py +52 -46
- meerk40t/rotary/gui/rotarysettings.py +240 -232
- meerk40t/rotary/rotary.py +202 -98
- meerk40t/ruida/control.py +291 -91
- meerk40t/ruida/controller.py +138 -1088
- meerk40t/ruida/device.py +672 -231
- meerk40t/ruida/driver.py +534 -472
- meerk40t/ruida/emulator.py +1494 -1491
- meerk40t/ruida/exceptions.py +4 -4
- meerk40t/ruida/gui/gui.py +71 -76
- meerk40t/ruida/gui/ruidaconfig.py +239 -72
- meerk40t/ruida/gui/ruidacontroller.py +187 -184
- meerk40t/ruida/gui/ruidaoperationproperties.py +48 -47
- meerk40t/ruida/loader.py +54 -52
- meerk40t/ruida/mock_connection.py +57 -109
- meerk40t/ruida/plugin.py +124 -87
- meerk40t/ruida/rdjob.py +2084 -945
- meerk40t/ruida/serial_connection.py +116 -0
- meerk40t/ruida/tcp_connection.py +146 -0
- meerk40t/ruida/udp_connection.py +73 -0
- meerk40t/svgelements.py +9671 -9669
- meerk40t/tools/driver_to_path.py +584 -579
- meerk40t/tools/geomstr.py +5583 -4680
- meerk40t/tools/jhfparser.py +357 -292
- meerk40t/tools/kerftest.py +904 -890
- meerk40t/tools/livinghinges.py +1168 -1033
- meerk40t/tools/pathtools.py +987 -949
- meerk40t/tools/pmatrix.py +234 -0
- meerk40t/tools/pointfinder.py +942 -942
- meerk40t/tools/polybool.py +940 -940
- meerk40t/tools/rasterplotter.py +1660 -547
- meerk40t/tools/shxparser.py +989 -901
- meerk40t/tools/ttfparser.py +726 -446
- meerk40t/tools/zinglplotter.py +595 -593
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.dist-info}/LICENSE +21 -21
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.dist-info}/METADATA +150 -139
- meerk40t-0.9.7010.dist-info/RECORD +445 -0
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.dist-info}/WHEEL +1 -1
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.dist-info}/top_level.txt +0 -1
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.dist-info}/zip-safe +1 -1
- meerk40t/balormk/elementlightjob.py +0 -159
- meerk40t-0.9.3001.dist-info/RECORD +0 -437
- test/bootstrap.py +0 -63
- test/test_cli.py +0 -12
- test/test_core_cutcode.py +0 -418
- test/test_core_elements.py +0 -144
- test/test_core_plotplanner.py +0 -397
- test/test_core_viewports.py +0 -312
- test/test_drivers_grbl.py +0 -108
- test/test_drivers_lihuiyu.py +0 -443
- test/test_drivers_newly.py +0 -113
- test/test_element_degenerate_points.py +0 -43
- test/test_elements_classify.py +0 -97
- test/test_elements_penbox.py +0 -22
- test/test_file_svg.py +0 -176
- test/test_fill.py +0 -155
- test/test_geomstr.py +0 -1523
- test/test_geomstr_nodes.py +0 -18
- test/test_imagetools_actualize.py +0 -306
- test/test_imagetools_wizard.py +0 -258
- test/test_kernel.py +0 -200
- test/test_laser_speeds.py +0 -3303
- test/test_length.py +0 -57
- test/test_lifecycle.py +0 -66
- test/test_operations.py +0 -251
- test/test_operations_hatch.py +0 -57
- test/test_ruida.py +0 -19
- test/test_spooler.py +0 -22
- test/test_tools_rasterplotter.py +0 -29
- test/test_wobble.py +0 -133
- test/test_zingl.py +0 -124
- {test → meerk40t/cylinder}/__init__.py +0 -0
- /meerk40t/{core/element_commands.py → cylinder/gui/__init__.py} +0 -0
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.dist-info}/entry_points.txt +0 -0
meerk40t/gui/laserrender.py
CHANGED
@@ -1,1232 +1,1470 @@
|
|
1
|
-
from copy import copy
|
2
|
-
from math import ceil, isnan, sqrt
|
3
|
-
|
4
|
-
import wx
|
5
|
-
from PIL import Image
|
6
|
-
|
7
|
-
from meerk40t.core.elements.element_types import place_nodes
|
8
|
-
from meerk40t.core.node.node import Fillrule, Linecap, Linejoin, Node
|
9
|
-
from meerk40t.svgelements import (
|
10
|
-
Arc,
|
11
|
-
Close,
|
12
|
-
Color,
|
13
|
-
CubicBezier,
|
14
|
-
Line,
|
15
|
-
Matrix,
|
16
|
-
Move,
|
17
|
-
QuadraticBezier,
|
18
|
-
)
|
19
|
-
|
20
|
-
from ..core.cutcode.cubiccut import CubicCut
|
21
|
-
from ..core.cutcode.cutcode import CutCode
|
22
|
-
from ..core.cutcode.dwellcut import DwellCut
|
23
|
-
from ..core.cutcode.gotocut import GotoCut
|
24
|
-
from ..core.cutcode.homecut import HomeCut
|
25
|
-
from ..core.cutcode.inputcut import InputCut
|
26
|
-
from ..core.cutcode.linecut import LineCut
|
27
|
-
from ..core.cutcode.outputcut import OutputCut
|
28
|
-
from ..core.cutcode.plotcut import PlotCut
|
29
|
-
from ..core.cutcode.quadcut import QuadCut
|
30
|
-
from ..core.cutcode.rastercut import RasterCut
|
31
|
-
from ..core.cutcode.waitcut import WaitCut
|
32
|
-
from ..tools.geomstr import ( # , TYPE_RAMP
|
33
|
-
TYPE_ARC,
|
34
|
-
TYPE_CUBIC,
|
35
|
-
TYPE_LINE,
|
36
|
-
TYPE_QUAD,
|
37
|
-
Geomstr,
|
38
|
-
)
|
39
|
-
from .fonts import wxfont_to_svg
|
40
|
-
from .icons import icons8_image
|
41
|
-
from .zmatrix import ZMatrix
|
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
|
-
|
149
|
-
family
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
family
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
return
|
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
|
-
if
|
207
|
-
draw_mode =
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
self.
|
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
|
-
return
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
elif
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
)
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
p.
|
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
|
-
if subpath
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
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
|
-
|
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
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
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
|
-
gc.
|
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
|
-
gc.
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
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
|
-
if
|
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
|
-
|
1011
|
-
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
node
|
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
|
-
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1102
|
-
if
|
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
|
-
if
|
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
|
-
|
1
|
+
from copy import copy
|
2
|
+
from math import ceil, isnan, sqrt
|
3
|
+
|
4
|
+
import wx
|
5
|
+
from PIL import Image
|
6
|
+
|
7
|
+
from meerk40t.core.elements.element_types import place_nodes
|
8
|
+
from meerk40t.core.node.node import Fillrule, Linecap, Linejoin, Node
|
9
|
+
from meerk40t.svgelements import (
|
10
|
+
Arc,
|
11
|
+
Close,
|
12
|
+
Color,
|
13
|
+
CubicBezier,
|
14
|
+
Line,
|
15
|
+
Matrix,
|
16
|
+
Move,
|
17
|
+
QuadraticBezier,
|
18
|
+
)
|
19
|
+
|
20
|
+
from ..core.cutcode.cubiccut import CubicCut
|
21
|
+
from ..core.cutcode.cutcode import CutCode
|
22
|
+
from ..core.cutcode.dwellcut import DwellCut
|
23
|
+
from ..core.cutcode.gotocut import GotoCut
|
24
|
+
from ..core.cutcode.homecut import HomeCut
|
25
|
+
from ..core.cutcode.inputcut import InputCut
|
26
|
+
from ..core.cutcode.linecut import LineCut
|
27
|
+
from ..core.cutcode.outputcut import OutputCut
|
28
|
+
from ..core.cutcode.plotcut import PlotCut
|
29
|
+
from ..core.cutcode.quadcut import QuadCut
|
30
|
+
from ..core.cutcode.rastercut import RasterCut
|
31
|
+
from ..core.cutcode.waitcut import WaitCut
|
32
|
+
from ..tools.geomstr import ( # , TYPE_RAMP
|
33
|
+
TYPE_ARC,
|
34
|
+
TYPE_CUBIC,
|
35
|
+
TYPE_LINE,
|
36
|
+
TYPE_QUAD,
|
37
|
+
Geomstr,
|
38
|
+
)
|
39
|
+
from .fonts import wxfont_to_svg
|
40
|
+
from .icons import icons8_image
|
41
|
+
from .zmatrix import ZMatrix
|
42
|
+
from meerk40t.gui.wxutils import get_gc_scale
|
43
|
+
|
44
|
+
DRAW_MODE_FILLS = 0x000001
|
45
|
+
DRAW_MODE_GUIDES = 0x000002
|
46
|
+
DRAW_MODE_GRID = 0x000004
|
47
|
+
DRAW_MODE_LASERPATH = 0x000008
|
48
|
+
DRAW_MODE_RETICLE = 0x000010
|
49
|
+
DRAW_MODE_SELECTION = 0x000020
|
50
|
+
DRAW_MODE_STROKES = 0x000040
|
51
|
+
DRAW_MODE_CACHE = 0x000080 # Set means do not cache.
|
52
|
+
DRAW_MODE_REFRESH = 0x000100
|
53
|
+
DRAW_MODE_ANIMATE = 0x000200
|
54
|
+
DRAW_MODE_PATH = 0x000400
|
55
|
+
DRAW_MODE_IMAGE = 0x000800
|
56
|
+
DRAW_MODE_TEXT = 0x001000
|
57
|
+
DRAW_MODE_BACKGROUND = 0x002000
|
58
|
+
DRAW_MODE_POINTS = 0x004000
|
59
|
+
DRAW_MODE_REGMARKS = 0x008000
|
60
|
+
DRAW_MODE_VARIABLES = 0x010000
|
61
|
+
|
62
|
+
DRAW_MODE_ICONS = 0x0040000
|
63
|
+
DRAW_MODE_INVERT = 0x400000
|
64
|
+
DRAW_MODE_FLIPXY = 0x800000
|
65
|
+
DRAW_MODE_LINEWIDTH = 0x1000000
|
66
|
+
DRAW_MODE_ALPHABLACK = 0x2000000 # Set means do not alphablack images
|
67
|
+
DRAW_MODE_ORIGIN = 0x4000000
|
68
|
+
DRAW_MODE_EDIT = 0x8000000
|
69
|
+
|
70
|
+
|
71
|
+
def swizzlecolor(c):
|
72
|
+
if c is None:
|
73
|
+
return None
|
74
|
+
if isinstance(c, int):
|
75
|
+
c = Color(argb=c)
|
76
|
+
try:
|
77
|
+
return c.blue << 16 | c.green << 8 | c.red
|
78
|
+
except (ValueError, TypeError):
|
79
|
+
return None
|
80
|
+
|
81
|
+
|
82
|
+
def as_wx_color(c):
|
83
|
+
if c is None:
|
84
|
+
return None
|
85
|
+
if isinstance(c, int):
|
86
|
+
c = Color(argb=c)
|
87
|
+
return wx.Colour(red=c.red, green=c.green, blue=c.blue, alpha=c.alpha)
|
88
|
+
|
89
|
+
|
90
|
+
def svgfont_to_wx(textnode):
|
91
|
+
"""
|
92
|
+
Translates all svg-text-properties to their wxfont-equivalents
|
93
|
+
@param textnode:
|
94
|
+
@return:
|
95
|
+
"""
|
96
|
+
if not hasattr(textnode, "wxfont"):
|
97
|
+
textnode.wxfont = wx.Font()
|
98
|
+
if textnode.font_weight is not None:
|
99
|
+
try:
|
100
|
+
fw = float(textnode.font_weight)
|
101
|
+
if fw > 1000:
|
102
|
+
textnode.font_weight = 1000
|
103
|
+
except ValueError:
|
104
|
+
pass
|
105
|
+
wxfont = textnode.wxfont
|
106
|
+
# if the font_list is empty, then we do have a not properly initialised textnode,
|
107
|
+
# that needs to be resolved...
|
108
|
+
if textnode.font_family is None:
|
109
|
+
wxfont_to_svg(textnode)
|
110
|
+
|
111
|
+
svg_to_wx_family(textnode, wxfont)
|
112
|
+
svg_to_wx_fontstyle(textnode, wxfont)
|
113
|
+
try:
|
114
|
+
wxfont.SetNumericWeight(textnode.weight) # Gets numeric weight.
|
115
|
+
except AttributeError:
|
116
|
+
# Running version wx4.0. No set Numeric Weight, can only set bold or normal.
|
117
|
+
weight = textnode.weight
|
118
|
+
wxfont.SetWeight(
|
119
|
+
wx.FONTWEIGHT_BOLD if weight > 600 else wx.FONTWEIGHT_NORMAL
|
120
|
+
) # Gets numeric weight.
|
121
|
+
|
122
|
+
try:
|
123
|
+
font_size = float(textnode.font_size)
|
124
|
+
except ValueError:
|
125
|
+
font_size = 10
|
126
|
+
try:
|
127
|
+
wxfont.SetFractionalPointSize(font_size)
|
128
|
+
except AttributeError:
|
129
|
+
# If we cannot set the fractional point size, we scale up to adjust to fractional levels.
|
130
|
+
integer_font_size = int(round(font_size))
|
131
|
+
scale = font_size / integer_font_size
|
132
|
+
if scale != 1.0:
|
133
|
+
textnode.matrix.pre_scale(scale, scale)
|
134
|
+
textnode.font_size = integer_font_size
|
135
|
+
wxfont.SetPointSize(integer_font_size)
|
136
|
+
wxfont.SetUnderlined(textnode.underline)
|
137
|
+
wxfont.SetStrikethrough(textnode.strikethrough)
|
138
|
+
|
139
|
+
|
140
|
+
def svg_to_wx_family(textnode, wxfont):
|
141
|
+
font_list = textnode.font_list
|
142
|
+
# if font_list is None:
|
143
|
+
# print("Fontlist is empty...")
|
144
|
+
# else:
|
145
|
+
# print ("Fontlist was: ", font_list)
|
146
|
+
for ff in font_list:
|
147
|
+
if ff == "fantasy":
|
148
|
+
family = wx.FONTFAMILY_DECORATIVE
|
149
|
+
wxfont.SetFamily(family)
|
150
|
+
return
|
151
|
+
elif ff == "serif":
|
152
|
+
family = wx.FONTFAMILY_ROMAN
|
153
|
+
wxfont.SetFamily(family)
|
154
|
+
return
|
155
|
+
elif ff == "cursive":
|
156
|
+
family = wx.FONTFAMILY_SCRIPT
|
157
|
+
wxfont.SetFamily(family)
|
158
|
+
return
|
159
|
+
elif ff == "sans-serif":
|
160
|
+
family = wx.FONTFAMILY_SWISS
|
161
|
+
wxfont.SetFamily(family)
|
162
|
+
return
|
163
|
+
elif ff == "monospace":
|
164
|
+
family = wx.FONTFAMILY_TELETYPE
|
165
|
+
wxfont.SetFamily(family)
|
166
|
+
return
|
167
|
+
if wxfont.SetFaceName(ff):
|
168
|
+
# We found a correct face name.
|
169
|
+
return
|
170
|
+
|
171
|
+
|
172
|
+
def svg_to_wx_fontstyle(textnode, wxfont):
|
173
|
+
ff = textnode.font_style
|
174
|
+
if ff == "normal":
|
175
|
+
fontstyle = wx.FONTSTYLE_NORMAL
|
176
|
+
elif ff == "italic":
|
177
|
+
fontstyle = wx.FONTSTYLE_ITALIC
|
178
|
+
elif ff == "oblique":
|
179
|
+
fontstyle = wx.FONTSTYLE_SLANT
|
180
|
+
else:
|
181
|
+
fontstyle = wx.FONTSTYLE_NORMAL
|
182
|
+
wxfont.SetStyle(fontstyle)
|
183
|
+
|
184
|
+
|
185
|
+
class LaserRender:
|
186
|
+
"""
|
187
|
+
Laser Render provides GUI relevant methods of displaying the given elements.
|
188
|
+
"""
|
189
|
+
|
190
|
+
def __init__(self, context):
|
191
|
+
self.context = context
|
192
|
+
self.context.setting(int, "draw_mode", 0)
|
193
|
+
self.pen = wx.Pen()
|
194
|
+
self.brush = wx.Brush()
|
195
|
+
self.color = wx.Colour()
|
196
|
+
self.caches_generated = 0
|
197
|
+
self.nodes_rendered = 0
|
198
|
+
self.nodes_skipped = 0
|
199
|
+
self._visible_area = None
|
200
|
+
self.suppress_it = False
|
201
|
+
|
202
|
+
def set_visible_area(self, box):
|
203
|
+
self._visible_area = box
|
204
|
+
|
205
|
+
def render_tree(self, node, gc, draw_mode=None, zoomscale=1.0, alpha=255):
|
206
|
+
if not self.render_node(
|
207
|
+
node, gc, draw_mode=draw_mode, zoomscale=zoomscale, alpha=alpha
|
208
|
+
):
|
209
|
+
for c in node.children:
|
210
|
+
self.render_tree(
|
211
|
+
c, gc, draw_mode=draw_mode, zoomscale=zoomscale, alpha=alpha
|
212
|
+
)
|
213
|
+
|
214
|
+
def render(self, nodes, gc, draw_mode=None, zoomscale=1.0, alpha=255, msg="unknown"):
|
215
|
+
"""
|
216
|
+
Render scene information.
|
217
|
+
|
218
|
+
@param nodes: Node types to render.
|
219
|
+
@param gc: graphics context
|
220
|
+
@param draw_mode: draw mode flags for rendering
|
221
|
+
@param zoomscale: zoomscale at which to render nodes
|
222
|
+
@param alpha: render transparency
|
223
|
+
@return:
|
224
|
+
"""
|
225
|
+
# gc_win = gc.GetWindow()
|
226
|
+
# gc_mat = gc.GetTransform().Get()
|
227
|
+
# print (f"Window handle: {gc_win}, matrix: {gc_mat}")
|
228
|
+
self.suppress_it = self.context.setting(bool, "supress_non_visible", True)
|
229
|
+
self.context.elements.set_start_time(f"renderscene_{msg}")
|
230
|
+
self.caches_generated = 0
|
231
|
+
self.nodes_rendered = 0
|
232
|
+
self.nodes_skipped = 0
|
233
|
+
if draw_mode is None:
|
234
|
+
draw_mode = self.context.draw_mode
|
235
|
+
if draw_mode & (DRAW_MODE_TEXT | DRAW_MODE_IMAGE | DRAW_MODE_PATH) != 0:
|
236
|
+
if draw_mode & DRAW_MODE_PATH: # Do not draw paths.
|
237
|
+
path_elements = (
|
238
|
+
"elem ellipse",
|
239
|
+
"elem path",
|
240
|
+
"elem point",
|
241
|
+
"elem polyline",
|
242
|
+
"elem rect",
|
243
|
+
"elem line",
|
244
|
+
"effect hatch",
|
245
|
+
"effect wobble",
|
246
|
+
"effect warp",
|
247
|
+
)
|
248
|
+
nodes = [e for e in nodes if e.type not in path_elements]
|
249
|
+
if draw_mode & DRAW_MODE_IMAGE: # Do not draw images.
|
250
|
+
nodes = [e for e in nodes if hasattr(e, "as_image")]
|
251
|
+
if draw_mode & DRAW_MODE_TEXT: # Do not draw text.
|
252
|
+
nodes = [e for e in nodes if e.type != "elem text"]
|
253
|
+
if draw_mode & DRAW_MODE_REGMARKS: # Do not draw regmarked items.
|
254
|
+
nodes = [e for e in nodes if e._parent.type != "branch reg"]
|
255
|
+
nodes = [e for e in nodes if e.type not in place_nodes]
|
256
|
+
_nodes = list(nodes)
|
257
|
+
variable_translation = draw_mode & DRAW_MODE_VARIABLES
|
258
|
+
nodecopy = list(_nodes)
|
259
|
+
self.validate_text_nodes(nodecopy, variable_translation)
|
260
|
+
|
261
|
+
for node in _nodes:
|
262
|
+
if node.type == "reference":
|
263
|
+
# Reference nodes should be drawn per-usual, recurse.
|
264
|
+
self.render_node(
|
265
|
+
node.node, gc, draw_mode=draw_mode, zoomscale=zoomscale, alpha=alpha
|
266
|
+
)
|
267
|
+
continue
|
268
|
+
self.render_node(
|
269
|
+
node, gc, draw_mode=draw_mode, zoomscale=zoomscale, alpha=alpha
|
270
|
+
)
|
271
|
+
self.context.elements.set_end_time(f"renderscene_{msg}", message=f"Rendered: {self.nodes_rendered}, skipped: {self.nodes_skipped}, caches created: {self.caches_generated}")
|
272
|
+
|
273
|
+
def render_node(self, node, gc, draw_mode=None, zoomscale=1.0, alpha=255):
|
274
|
+
"""
|
275
|
+
Renders the specific node.
|
276
|
+
@param node:
|
277
|
+
@param gc:
|
278
|
+
@param draw_mode:
|
279
|
+
@param zoomscale:
|
280
|
+
@param alpha:
|
281
|
+
@return: True if rendering was done, False if rendering could not be done.
|
282
|
+
"""
|
283
|
+
node_bb = node.bounds if hasattr(node, "bounds") else None
|
284
|
+
vis_bb = self._visible_area
|
285
|
+
if self.suppress_it and vis_bb is not None and node_bb is not None and (
|
286
|
+
node_bb[0] > vis_bb[2] or
|
287
|
+
node_bb[1] > vis_bb[3] or
|
288
|
+
node_bb[2] < vis_bb[0] or
|
289
|
+
node_bb[3] < vis_bb[1]
|
290
|
+
):
|
291
|
+
self.nodes_skipped += 1
|
292
|
+
return False
|
293
|
+
if hasattr(node, "hidden") and node.hidden:
|
294
|
+
self.nodes_skipped += 1
|
295
|
+
return False
|
296
|
+
if hasattr(node, "is_visible") and not node.is_visible:
|
297
|
+
self.nodes_skipped += 1
|
298
|
+
return False
|
299
|
+
if hasattr(node, "output") and not node.output:
|
300
|
+
self.nodes_skipped += 1
|
301
|
+
return False
|
302
|
+
self.nodes_rendered += 1
|
303
|
+
if not hasattr(node, "draw"): # or not hasattr(node, "_make_cache"):
|
304
|
+
# No known render method, we must define the function to draw nodes.
|
305
|
+
if node.type in (
|
306
|
+
"elem path",
|
307
|
+
"elem ellipse",
|
308
|
+
"elem rect",
|
309
|
+
"elem line",
|
310
|
+
"elem polyline",
|
311
|
+
"effect hatch",
|
312
|
+
"effect wobble",
|
313
|
+
"effect warp",
|
314
|
+
):
|
315
|
+
node.draw = self.draw_vector
|
316
|
+
# node._make_cache = self.cache_geomstr
|
317
|
+
elif node.type == "elem point":
|
318
|
+
node.draw = self.draw_point_node
|
319
|
+
elif node.type in place_nodes:
|
320
|
+
node.draw = self.draw_placement_node
|
321
|
+
elif hasattr(node, "as_image"):
|
322
|
+
node.draw = self.draw_image_node
|
323
|
+
elif node.type == "elem text":
|
324
|
+
node.draw = self.draw_text_node
|
325
|
+
elif node.type == "cutcode":
|
326
|
+
node.draw = self.draw_cutcode_node
|
327
|
+
elif node.type == "group":
|
328
|
+
node.draw = self.draw_nothing
|
329
|
+
else:
|
330
|
+
# print (f"This node has no method: {node.type}")
|
331
|
+
return False
|
332
|
+
# We have now defined that function, draw it.
|
333
|
+
node.draw(node, gc, draw_mode, zoomscale=zoomscale, alpha=alpha)
|
334
|
+
if getattr(node, "label_display", False) and node.label:
|
335
|
+
# Display label
|
336
|
+
col = self.context.root.setting(str, "label_display_color", "#ff0000ff")
|
337
|
+
self.display_label(
|
338
|
+
node, gc, draw_mode, zoomscale=zoomscale, alpha=alpha, color=col
|
339
|
+
)
|
340
|
+
return True
|
341
|
+
|
342
|
+
def make_path(self, gc, path):
|
343
|
+
"""
|
344
|
+
Takes a svgelements.Path and converts it to a GraphicsContext.Graphics Path
|
345
|
+
"""
|
346
|
+
p = gc.CreatePath()
|
347
|
+
init = False
|
348
|
+
for e in path.segments(transformed=True):
|
349
|
+
if isinstance(e, Move):
|
350
|
+
p.MoveToPoint(e.end[0], e.end[1])
|
351
|
+
init = True
|
352
|
+
elif isinstance(e, Line):
|
353
|
+
if not init:
|
354
|
+
init = True
|
355
|
+
p.MoveToPoint(e.start[0], e.start[1])
|
356
|
+
p.AddLineToPoint(e.end[0], e.end[1])
|
357
|
+
elif isinstance(e, Close):
|
358
|
+
if not init:
|
359
|
+
init = True
|
360
|
+
p.MoveToPoint(e.start[0], e.start[1])
|
361
|
+
p.CloseSubpath()
|
362
|
+
elif isinstance(e, QuadraticBezier):
|
363
|
+
if not init:
|
364
|
+
init = True
|
365
|
+
p.MoveToPoint(e.start[0], e.start[1])
|
366
|
+
p.AddQuadCurveToPoint(e.control[0], e.control[1], e.end[0], e.end[1])
|
367
|
+
elif isinstance(e, CubicBezier):
|
368
|
+
if not init:
|
369
|
+
init = True
|
370
|
+
p.MoveToPoint(e.start[0], e.start[1])
|
371
|
+
p.AddCurveToPoint(
|
372
|
+
e.control1[0],
|
373
|
+
e.control1[1],
|
374
|
+
e.control2[0],
|
375
|
+
e.control2[1],
|
376
|
+
e.end[0],
|
377
|
+
e.end[1],
|
378
|
+
)
|
379
|
+
elif isinstance(e, Arc):
|
380
|
+
if not init:
|
381
|
+
init = True
|
382
|
+
p.MoveToPoint(e.start[0], e.start[1])
|
383
|
+
for curve in e.as_cubic_curves():
|
384
|
+
p.AddCurveToPoint(
|
385
|
+
curve.control1[0],
|
386
|
+
curve.control1[1],
|
387
|
+
curve.control2[0],
|
388
|
+
curve.control2[1],
|
389
|
+
curve.end[0],
|
390
|
+
curve.end[1],
|
391
|
+
)
|
392
|
+
return p
|
393
|
+
|
394
|
+
def make_geomstr(self, gc, path, node=None, settings=None):
|
395
|
+
"""
|
396
|
+
Takes a Geomstr path and converts it to a GraphicsContext.Graphics path
|
397
|
+
|
398
|
+
This also creates a point list of the relevant nodes and creates a ._cache_edit value to be used by node
|
399
|
+
editing view.
|
400
|
+
"""
|
401
|
+
p = gc.CreatePath()
|
402
|
+
pts = list()
|
403
|
+
for subpath in path.as_subpaths():
|
404
|
+
if len(subpath) == 0:
|
405
|
+
continue
|
406
|
+
end = None
|
407
|
+
for e in subpath.segments:
|
408
|
+
seg_type = int(e[2].real)
|
409
|
+
if settings is not None and settings != int(e[2].imag):
|
410
|
+
continue
|
411
|
+
start = e[0]
|
412
|
+
if end != start:
|
413
|
+
# Start point does not equal previous end point.
|
414
|
+
p.MoveToPoint(start.real, start.imag)
|
415
|
+
c0 = e[1]
|
416
|
+
c1 = e[3]
|
417
|
+
end = e[4]
|
418
|
+
|
419
|
+
if seg_type == TYPE_LINE:
|
420
|
+
p.AddLineToPoint(end.real, end.imag)
|
421
|
+
pts.extend((start, end))
|
422
|
+
elif seg_type == TYPE_QUAD:
|
423
|
+
p.AddQuadCurveToPoint(c0.real, c0.imag, end.real, end.imag)
|
424
|
+
pts.append(c0)
|
425
|
+
pts.extend((start, end))
|
426
|
+
elif seg_type == TYPE_ARC:
|
427
|
+
radius = Geomstr.arc_radius(None, line=e)
|
428
|
+
center = Geomstr.arc_center(None, line=e)
|
429
|
+
start_t = Geomstr.angle(None, center, start)
|
430
|
+
end_t = Geomstr.angle(None, center, end)
|
431
|
+
p.AddArc(
|
432
|
+
center.real,
|
433
|
+
center.imag,
|
434
|
+
radius,
|
435
|
+
start_t,
|
436
|
+
end_t,
|
437
|
+
clockwise=Geomstr.orientation(None, start, c0, end) != "ccw",
|
438
|
+
)
|
439
|
+
pts.append(c0)
|
440
|
+
pts.extend((start, end))
|
441
|
+
elif seg_type == TYPE_CUBIC:
|
442
|
+
p.AddCurveToPoint(
|
443
|
+
c0.real, c0.imag, c1.real, c1.imag, end.real, end.imag
|
444
|
+
)
|
445
|
+
pts.extend((c0, c1, start, end))
|
446
|
+
else:
|
447
|
+
print(f"Unknown seg_type: {seg_type}")
|
448
|
+
if subpath.first_point == end:
|
449
|
+
p.CloseSubpath()
|
450
|
+
if node is not None:
|
451
|
+
graphics_path_2 = gc.CreatePath()
|
452
|
+
for pt in pts:
|
453
|
+
graphics_path_2.AddCircle(pt.real, pt.imag, 5000)
|
454
|
+
node._cache_edit = graphics_path_2
|
455
|
+
|
456
|
+
return p
|
457
|
+
|
458
|
+
def _set_linecap_by_node(self, node):
|
459
|
+
if not hasattr(node, "linecap") or node.linecap is None:
|
460
|
+
self.pen.SetCap(wx.CAP_ROUND)
|
461
|
+
else:
|
462
|
+
if node.linecap == Linecap.CAP_BUTT:
|
463
|
+
self.pen.SetCap(wx.CAP_BUTT)
|
464
|
+
elif node.linecap == Linecap.CAP_ROUND:
|
465
|
+
self.pen.SetCap(wx.CAP_ROUND)
|
466
|
+
elif node.linecap == Linecap.CAP_SQUARE:
|
467
|
+
self.pen.SetCap(wx.CAP_PROJECTING)
|
468
|
+
else:
|
469
|
+
self.pen.SetCap(wx.CAP_ROUND)
|
470
|
+
|
471
|
+
def _set_linejoin_by_node(self, node):
|
472
|
+
if not hasattr(node, "linejoin"):
|
473
|
+
self.pen.SetJoin(wx.JOIN_BEVEL)
|
474
|
+
elif node.linejoin is None:
|
475
|
+
self.pen.SetJoin(wx.JOIN_MITER)
|
476
|
+
else:
|
477
|
+
if node.linejoin == Linejoin.JOIN_ARCS:
|
478
|
+
self.pen.SetJoin(wx.JOIN_ROUND)
|
479
|
+
elif node.linejoin == Linejoin.JOIN_BEVEL:
|
480
|
+
self.pen.SetJoin(wx.JOIN_BEVEL)
|
481
|
+
elif node.linejoin == Linejoin.JOIN_MITER:
|
482
|
+
self.pen.SetJoin(wx.JOIN_MITER)
|
483
|
+
elif node.linejoin == Linejoin.JOIN_MITER_CLIP:
|
484
|
+
self.pen.SetJoin(wx.JOIN_MITER)
|
485
|
+
else:
|
486
|
+
self.pen.SetJoin(wx.JOIN_ROUND)
|
487
|
+
|
488
|
+
def _get_fillstyle(self, node):
|
489
|
+
if not hasattr(node, "fillrule") or node.fillrule is None:
|
490
|
+
return wx.WINDING_RULE
|
491
|
+
if node.fillrule == Fillrule.FILLRULE_EVENODD:
|
492
|
+
return wx.ODDEVEN_RULE
|
493
|
+
else:
|
494
|
+
return wx.WINDING_RULE
|
495
|
+
|
496
|
+
@staticmethod
|
497
|
+
def _penwidth(pen, width):
|
498
|
+
try:
|
499
|
+
if isnan(width):
|
500
|
+
width = 1.0
|
501
|
+
try:
|
502
|
+
pen.SetWidth(width)
|
503
|
+
except TypeError:
|
504
|
+
pen.SetWidth(int(width))
|
505
|
+
except OverflowError:
|
506
|
+
pass # Exceeds 32 bit signed integer.
|
507
|
+
|
508
|
+
def _set_penwidth(self, width):
|
509
|
+
self._penwidth(self.pen, width)
|
510
|
+
|
511
|
+
def set_pen(self, gc, stroke, alpha=None):
|
512
|
+
c = stroke
|
513
|
+
if c is not None and c != "none":
|
514
|
+
swizzle_color = swizzlecolor(c)
|
515
|
+
if alpha is None:
|
516
|
+
alpha = c.alpha
|
517
|
+
self.color.SetRGBA(swizzle_color | alpha << 24) # wx has BBGGRR
|
518
|
+
self.pen.SetColour(self.color)
|
519
|
+
gc.SetPen(self.pen)
|
520
|
+
else:
|
521
|
+
gc.SetPen(wx.TRANSPARENT_PEN)
|
522
|
+
|
523
|
+
def set_brush(self, gc, fill, alpha=None):
|
524
|
+
c = fill
|
525
|
+
if c is not None and c != "none":
|
526
|
+
swizzle_color = swizzlecolor(c)
|
527
|
+
if alpha is None:
|
528
|
+
alpha = c.alpha
|
529
|
+
self.color.SetRGBA(swizzle_color | alpha << 24) # wx has BBGGRR
|
530
|
+
self.brush.SetColour(self.color)
|
531
|
+
gc.SetBrush(self.brush)
|
532
|
+
else:
|
533
|
+
gc.SetBrush(wx.TRANSPARENT_BRUSH)
|
534
|
+
|
535
|
+
def draw_nothing(
|
536
|
+
self,
|
537
|
+
node: Node,
|
538
|
+
gc: wx.GraphicsContext,
|
539
|
+
draw_mode,
|
540
|
+
zoomscale=1.0,
|
541
|
+
alpha=255,
|
542
|
+
x: int = 0,
|
543
|
+
y: int = 0,
|
544
|
+
):
|
545
|
+
# We don't do anything, just a placeholder
|
546
|
+
return
|
547
|
+
|
548
|
+
def draw_cutcode_node(
|
549
|
+
self,
|
550
|
+
node: Node,
|
551
|
+
gc: wx.GraphicsContext,
|
552
|
+
draw_mode,
|
553
|
+
zoomscale=1.0,
|
554
|
+
alpha=255,
|
555
|
+
x: int = 0,
|
556
|
+
y: int = 0,
|
557
|
+
):
|
558
|
+
cutcode = node.cutcode
|
559
|
+
self.draw_cutcode(cutcode, gc, x, y)
|
560
|
+
|
561
|
+
def draw_cutcode(
|
562
|
+
self,
|
563
|
+
cutcode: CutCode,
|
564
|
+
gc: wx.GraphicsContext,
|
565
|
+
x: int = 0, y: int = 0,
|
566
|
+
raster_as_image: bool = True,
|
567
|
+
residual = None,
|
568
|
+
laserspot_width = None,
|
569
|
+
):
|
570
|
+
"""
|
571
|
+
Draw cutcode object into wxPython graphics code.
|
572
|
+
|
573
|
+
This code accepts x,y offset values. The cutcode laser offset can be set with a
|
574
|
+
command with the rest of the cutcode remaining the same. So drawing the cutcode
|
575
|
+
requires knowing what, if any offset is currently being applied.
|
576
|
+
|
577
|
+
@param cutcode: flat cutcode object to draw.
|
578
|
+
@param gc: wx.graphics context
|
579
|
+
@param x: offset in x direction
|
580
|
+
@param y: offset in y direction
|
581
|
+
@return:
|
582
|
+
"""
|
583
|
+
|
584
|
+
def establish_linewidth(scale, spot_width):
|
585
|
+
default_pix = 1 / scale
|
586
|
+
# print (gcscale, laserspot_width, 1/gcscale)
|
587
|
+
pixelwidth = spot_width if spot_width is not None else default_pix
|
588
|
+
# How many pixels should the laserspotwidth be like,
|
589
|
+
# in any case at least 1 pixel, as otherwise it
|
590
|
+
# wouldn't show up under Linux/Darwin
|
591
|
+
return max(default_pix, pixelwidth)
|
592
|
+
|
593
|
+
def process_cut(cut, p, last_point):
|
594
|
+
|
595
|
+
def process_as_image():
|
596
|
+
image = cut.image
|
597
|
+
gc.PushState()
|
598
|
+
matrix = Matrix.scale(cut.step_x, cut.step_y)
|
599
|
+
matrix.post_translate(
|
600
|
+
cut.offset_x + x, cut.offset_y + y
|
601
|
+
) # Adjust image xy
|
602
|
+
gc.ConcatTransform(wx.GraphicsContext.CreateMatrix(gc, ZMatrix(matrix)))
|
603
|
+
_gcscale = get_gc_scale(gc)
|
604
|
+
try:
|
605
|
+
cache = cut._cache
|
606
|
+
except AttributeError:
|
607
|
+
cache = None
|
608
|
+
if cache is None:
|
609
|
+
# No valid cache. Generate.
|
610
|
+
cut._cache_width, cut._cache_height = image.size
|
611
|
+
try:
|
612
|
+
cut._cache = self.make_thumbnail(image, maximum=5000)
|
613
|
+
except (MemoryError, RuntimeError):
|
614
|
+
cut._cache = None
|
615
|
+
cut._cache_id = id(image)
|
616
|
+
if cut._cache is not None:
|
617
|
+
# Cache exists and is valid.
|
618
|
+
gc.DrawBitmap(cut._cache, 0, 0, cut._cache_width, cut._cache_height)
|
619
|
+
if cut.highlighted:
|
620
|
+
# gc.SetBrush(wx.RED_BRUSH)
|
621
|
+
self._penwidth(highlight_pen, 3 / gcscale)
|
622
|
+
gc.SetPen(highlight_pen)
|
623
|
+
gc.DrawRectangle(0, 0, cut._cache_width, cut._cache_height)
|
624
|
+
else:
|
625
|
+
# Image was too large to cache, draw a red rectangle instead.
|
626
|
+
gc.SetBrush(wx.RED_BRUSH)
|
627
|
+
gc.DrawRectangle(0, 0, cut._cache_width, cut._cache_height)
|
628
|
+
gc.DrawBitmap(
|
629
|
+
icons8_image.GetBitmap(),
|
630
|
+
0,
|
631
|
+
0,
|
632
|
+
cut._cache_width,
|
633
|
+
cut._cache_height,
|
634
|
+
)
|
635
|
+
gc.PopState()
|
636
|
+
|
637
|
+
def process_as_raster():
|
638
|
+
try:
|
639
|
+
cache = cut._plotcache
|
640
|
+
except AttributeError:
|
641
|
+
cache = None
|
642
|
+
if cache is None:
|
643
|
+
process_as_image()
|
644
|
+
return
|
645
|
+
p.MoveToPoint(start[0] + x, start[1] + y)
|
646
|
+
todraw = cache
|
647
|
+
if residual is None:
|
648
|
+
maxcount = -1
|
649
|
+
else:
|
650
|
+
maxcount = int(len(todraw) * residual)
|
651
|
+
count = 0
|
652
|
+
for px, py, pon in todraw:
|
653
|
+
if px is None or py is None:
|
654
|
+
# Passthrough
|
655
|
+
continue
|
656
|
+
if pon == 0:
|
657
|
+
p.MoveToPoint(px + x, py + y)
|
658
|
+
else:
|
659
|
+
p.AddLineToPoint(px + x, py + y)
|
660
|
+
count += 1
|
661
|
+
if 0 < maxcount < count:
|
662
|
+
break
|
663
|
+
|
664
|
+
def process_as_plot():
|
665
|
+
p.MoveToPoint(start[0] + x, start[1] + y)
|
666
|
+
try:
|
667
|
+
cache = cut._plotcache
|
668
|
+
except AttributeError:
|
669
|
+
cache = None
|
670
|
+
if cache is None:
|
671
|
+
return
|
672
|
+
todraw = cache
|
673
|
+
if residual is None:
|
674
|
+
maxcount = -1
|
675
|
+
else:
|
676
|
+
maxcount = int(len(todraw) * residual)
|
677
|
+
count = 0
|
678
|
+
for ox, oy, pon, px, py in todraw:
|
679
|
+
if pon == 0:
|
680
|
+
p.MoveToPoint(px + x, py + y)
|
681
|
+
else:
|
682
|
+
p.AddLineToPoint(px + x, py + y)
|
683
|
+
count += 1
|
684
|
+
if 0 < maxcount < count:
|
685
|
+
break
|
686
|
+
|
687
|
+
start = cut.start
|
688
|
+
end = cut.end
|
689
|
+
if last_point != start:
|
690
|
+
p.MoveToPoint(start[0] + x, start[1] + y)
|
691
|
+
|
692
|
+
if isinstance(cut, LineCut):
|
693
|
+
# Standard line cut. Applies to path object.
|
694
|
+
p.AddLineToPoint(end[0] + x, end[1] + y)
|
695
|
+
elif isinstance(cut, QuadCut):
|
696
|
+
# Standard quadratic bezier cut
|
697
|
+
p.AddQuadCurveToPoint(
|
698
|
+
cut.c()[0] + x, cut.c()[1] + y, end[0] + x, end[1] + y
|
699
|
+
)
|
700
|
+
elif isinstance(cut, CubicCut):
|
701
|
+
# Standard cubic bezier cut
|
702
|
+
p.AddCurveToPoint(
|
703
|
+
cut.c1()[0] + x,
|
704
|
+
cut.c1()[1] + y,
|
705
|
+
cut.c2()[0] + x,
|
706
|
+
cut.c2()[1] + y,
|
707
|
+
end[0] + x,
|
708
|
+
end[1] + y,
|
709
|
+
)
|
710
|
+
elif isinstance(cut, RasterCut):
|
711
|
+
# Rastercut object.
|
712
|
+
if raster_as_image:
|
713
|
+
process_as_image()
|
714
|
+
else:
|
715
|
+
process_as_raster()
|
716
|
+
|
717
|
+
elif isinstance(cut, PlotCut):
|
718
|
+
process_as_plot()
|
719
|
+
elif isinstance(cut, DwellCut):
|
720
|
+
pass
|
721
|
+
elif isinstance(cut, WaitCut):
|
722
|
+
pass
|
723
|
+
elif isinstance(cut, HomeCut):
|
724
|
+
p.MoveToPoint(0, 0)
|
725
|
+
elif isinstance(cut, GotoCut):
|
726
|
+
p.MoveToPoint(start[0] + x, start[1] + y)
|
727
|
+
elif isinstance(cut, InputCut):
|
728
|
+
pass
|
729
|
+
elif isinstance(cut, OutputCut):
|
730
|
+
pass
|
731
|
+
return end
|
732
|
+
|
733
|
+
gcscale = get_gc_scale(gc)
|
734
|
+
pixelwidth = establish_linewidth(gcscale, laserspot_width)
|
735
|
+
defaultwidth = 1 / gcscale
|
736
|
+
if defaultwidth > 0.25 * pixelwidth:
|
737
|
+
defaultwidth = 0
|
738
|
+
# print (f"Scale: {gcscale} - {mat_param}")
|
739
|
+
highlight_color = Color("magenta")
|
740
|
+
wx_color = wx.Colour(swizzlecolor(highlight_color))
|
741
|
+
highlight_pen = wx.Pen(wx_color)
|
742
|
+
highlight_pen.SetStyle(wx.PENSTYLE_SHORT_DASH)
|
743
|
+
p = None
|
744
|
+
last_point = None
|
745
|
+
color = None
|
746
|
+
for cut in cutcode:
|
747
|
+
if hasattr(cut, "visible") and getattr(cut, "visible") is False:
|
748
|
+
continue
|
749
|
+
c = highlight_color if cut.highlighted else cut.color
|
750
|
+
if c is None:
|
751
|
+
c = 0
|
752
|
+
try:
|
753
|
+
if c.value is None:
|
754
|
+
c = 0
|
755
|
+
except AttributeError:
|
756
|
+
pass
|
757
|
+
if c is not color:
|
758
|
+
if p is not None:
|
759
|
+
gc.StrokePath(p)
|
760
|
+
if defaultwidth:
|
761
|
+
self._penwidth(self.pen, defaultwidth)
|
762
|
+
self.set_pen(gc, color, 192)
|
763
|
+
gc.StrokePath(p)
|
764
|
+
del p
|
765
|
+
color = c
|
766
|
+
last_point = None
|
767
|
+
p = gc.CreatePath()
|
768
|
+
self._penwidth(self.pen, pixelwidth)
|
769
|
+
alphavalue = 192 if laserspot_width is None else 64
|
770
|
+
self.set_pen(gc, c, alpha=alphavalue)
|
771
|
+
if p is None:
|
772
|
+
p = gc.CreatePath()
|
773
|
+
last_point = process_cut(cut, p, last_point)
|
774
|
+
if p is not None:
|
775
|
+
gc.StrokePath(p)
|
776
|
+
if defaultwidth:
|
777
|
+
self._penwidth(self.pen, defaultwidth)
|
778
|
+
self.set_pen(gc, c, 192)
|
779
|
+
gc.StrokePath(p)
|
780
|
+
del p
|
781
|
+
|
782
|
+
def cache_geomstr(self, node, gc):
|
783
|
+
self.caches_generated += 1
|
784
|
+
|
785
|
+
try:
|
786
|
+
matrix = node.matrix
|
787
|
+
node._cache_matrix = copy(matrix)
|
788
|
+
except AttributeError:
|
789
|
+
node._cache_matrix = Matrix()
|
790
|
+
if hasattr(node, "final_geometry"):
|
791
|
+
geom = node.final_geometry()
|
792
|
+
else:
|
793
|
+
geom = node.as_geometry()
|
794
|
+
cache = self.make_geomstr(gc, geom, node=node)
|
795
|
+
node._cache = cache
|
796
|
+
|
797
|
+
def draw_vector(self, node, gc, draw_mode, zoomscale=1.0, alpha=255):
|
798
|
+
"""
|
799
|
+
Draw routine for vector objects.
|
800
|
+
|
801
|
+
Vector objects are expected to have a _make_cache routine which attaches a `_cache_matrix` and a `_cache`
|
802
|
+
attribute to them which can be drawn as a GraphicsPath.
|
803
|
+
"""
|
804
|
+
if hasattr(node, "mktext"):
|
805
|
+
newtext = self.context.elements.wordlist_translate(
|
806
|
+
node.mktext, elemnode=node, increment=False
|
807
|
+
)
|
808
|
+
oldtext = getattr(node, "_translated_text", "")
|
809
|
+
if newtext != oldtext:
|
810
|
+
node._translated_text = newtext
|
811
|
+
kernel = self.context.elements.kernel
|
812
|
+
for property_op in kernel.lookup_all("path_updater/.*"):
|
813
|
+
property_op(kernel.root, node)
|
814
|
+
if hasattr(node, "_cache"):
|
815
|
+
node._cache = None
|
816
|
+
try:
|
817
|
+
matrix = node.matrix
|
818
|
+
except AttributeError:
|
819
|
+
matrix = Matrix()
|
820
|
+
gc.PushState()
|
821
|
+
try:
|
822
|
+
cache = node._cache
|
823
|
+
except AttributeError:
|
824
|
+
cache = None
|
825
|
+
if cache is None:
|
826
|
+
self.cache_geomstr(node, gc)
|
827
|
+
|
828
|
+
try:
|
829
|
+
cache_matrix = node._cache_matrix
|
830
|
+
except AttributeError:
|
831
|
+
cache_matrix = None
|
832
|
+
|
833
|
+
stroke_factor = 1
|
834
|
+
if matrix != cache_matrix and cache_matrix is not None:
|
835
|
+
# Calculate the relative change matrix and apply it to this shape.
|
836
|
+
q = ~cache_matrix * matrix
|
837
|
+
gc.ConcatTransform(wx.GraphicsContext.CreateMatrix(gc, ZMatrix(q)))
|
838
|
+
# Applying the matrix will scale our stroke, so we scale the stroke back down.
|
839
|
+
stroke_factor = 1.0 if q.determinant == 0 else 1.0 / sqrt(abs(q.determinant))
|
840
|
+
self._set_linecap_by_node(node)
|
841
|
+
self._set_linejoin_by_node(node)
|
842
|
+
sw = node.implied_stroke_width * stroke_factor
|
843
|
+
if draw_mode & DRAW_MODE_LINEWIDTH:
|
844
|
+
# No stroke rendering.
|
845
|
+
sw = 1000
|
846
|
+
self._set_penwidth(sw)
|
847
|
+
self.set_pen(
|
848
|
+
gc,
|
849
|
+
node.stroke,
|
850
|
+
alpha=alpha,
|
851
|
+
)
|
852
|
+
self.set_brush(gc, node.fill, alpha=alpha)
|
853
|
+
if draw_mode & DRAW_MODE_FILLS == 0 and node.fill is not None:
|
854
|
+
gc.FillPath(node._cache, fillStyle=self._get_fillstyle(node))
|
855
|
+
if draw_mode & DRAW_MODE_STROKES == 0 and node.stroke is not None:
|
856
|
+
gc.StrokePath(node._cache)
|
857
|
+
|
858
|
+
if node.emphasized and draw_mode & DRAW_MODE_EDIT:
|
859
|
+
try:
|
860
|
+
edit = node._cache_edit
|
861
|
+
gc.StrokePath(edit)
|
862
|
+
except AttributeError:
|
863
|
+
pass
|
864
|
+
gc.PopState()
|
865
|
+
|
866
|
+
def draw_placement_node(self, node, gc, draw_mode, zoomscale=1.0, alpha=255):
|
867
|
+
"""Default draw routine for the placement operation."""
|
868
|
+
if node.type == "place current":
|
869
|
+
# no idea how to draw yet...
|
870
|
+
return
|
871
|
+
gc.PushState()
|
872
|
+
matrix = Matrix()
|
873
|
+
if node.rotation is not None and node.rotation != 0:
|
874
|
+
matrix.post_rotate(node.rotation, node.x, node.y)
|
875
|
+
gc.ConcatTransform(wx.GraphicsContext.CreateMatrix(gc, ZMatrix(matrix)))
|
876
|
+
# First x
|
877
|
+
dif = 20 * zoomscale
|
878
|
+
x_from = node.x
|
879
|
+
y_from = node.y
|
880
|
+
if node.corner == 0:
|
881
|
+
# Top Left
|
882
|
+
x_to = x_from + dif
|
883
|
+
y_to = y_from + dif
|
884
|
+
x_sign = 1
|
885
|
+
y_sign = 1
|
886
|
+
elif node.corner == 1:
|
887
|
+
# Top Right
|
888
|
+
x_to = x_from - dif
|
889
|
+
y_to = y_from + dif
|
890
|
+
x_sign = -1
|
891
|
+
y_sign = 1
|
892
|
+
elif node.corner == 2:
|
893
|
+
# Bottom Right
|
894
|
+
x_to = x_from - dif
|
895
|
+
y_to = y_from - dif
|
896
|
+
x_sign = -1
|
897
|
+
y_sign = -1
|
898
|
+
elif node.corner == 3:
|
899
|
+
# Bottom Left
|
900
|
+
x_to = x_from + dif
|
901
|
+
y_to = y_from - dif
|
902
|
+
x_sign = 1
|
903
|
+
y_sign = -1
|
904
|
+
else:
|
905
|
+
# Center
|
906
|
+
x_from -= dif
|
907
|
+
y_from -= dif
|
908
|
+
x_to = x_from + 2 * dif
|
909
|
+
y_to = y_from + 2 * dif
|
910
|
+
x_sign = 1
|
911
|
+
y_sign = 1
|
912
|
+
width = 1.0 * zoomscale
|
913
|
+
rpen = wx.Pen(wx.Colour(red=255, green=0, blue=0, alpha=alpha))
|
914
|
+
self._penwidth(rpen, width)
|
915
|
+
|
916
|
+
gpen = wx.Pen(wx.Colour(red=0, green=255, blue=0, alpha=alpha))
|
917
|
+
self._penwidth(gpen, width)
|
918
|
+
gc.SetPen(rpen)
|
919
|
+
dif = 5 * zoomscale
|
920
|
+
gc.StrokeLine(x_from, node.y, x_to, node.y)
|
921
|
+
gc.StrokeLine(x_to - x_sign * dif, node.y - y_sign * dif, x_to, node.y)
|
922
|
+
gc.StrokeLine(x_to - x_sign * dif, node.y + y_sign * dif, x_to, node.y)
|
923
|
+
gc.SetPen(gpen)
|
924
|
+
gc.StrokeLine(node.x, y_from, node.x, y_to)
|
925
|
+
gc.StrokeLine(node.x - x_sign * dif, y_to - y_sign * dif, node.x, y_to)
|
926
|
+
gc.StrokeLine(node.x + x_sign * dif, y_to - y_sign * dif, node.x, y_to)
|
927
|
+
|
928
|
+
loops = 1
|
929
|
+
if hasattr(node, "loops") and node.loops is not None:
|
930
|
+
# No zero or negative values please
|
931
|
+
try:
|
932
|
+
loops = int(node.loops)
|
933
|
+
except ValueError:
|
934
|
+
loops = 1
|
935
|
+
if loops < 1:
|
936
|
+
loops = 1
|
937
|
+
if loops > 1:
|
938
|
+
symbol = f"{loops}x"
|
939
|
+
font_size = 10 * zoomscale
|
940
|
+
if font_size < 1.0:
|
941
|
+
font_size = 1.0
|
942
|
+
try:
|
943
|
+
font = wx.Font(
|
944
|
+
font_size,
|
945
|
+
wx.FONTFAMILY_SWISS,
|
946
|
+
wx.FONTSTYLE_NORMAL,
|
947
|
+
wx.FONTWEIGHT_NORMAL,
|
948
|
+
)
|
949
|
+
except TypeError:
|
950
|
+
font = wx.Font(
|
951
|
+
int(font_size),
|
952
|
+
wx.FONTFAMILY_SWISS,
|
953
|
+
wx.FONTSTYLE_NORMAL,
|
954
|
+
wx.FONTWEIGHT_NORMAL,
|
955
|
+
)
|
956
|
+
gc.SetFont(font, wx.Colour(red=255, green=0, blue=0, alpha=alpha))
|
957
|
+
(t_width, t_height) = gc.GetTextExtent(symbol)
|
958
|
+
x = (x_from + x_to) / 2 - t_width / 2
|
959
|
+
y = (y_from + y_to) / 2 - t_height / 2
|
960
|
+
# is corner center then shift it a bit more
|
961
|
+
if node.corner == 4:
|
962
|
+
x += 0.25 * (x_to - x_from)
|
963
|
+
y += 0.25 * (y_to - y_from)
|
964
|
+
gc.DrawText(symbol, x, y)
|
965
|
+
symbol = ""
|
966
|
+
if hasattr(node, "nx") and hasattr(node, "ny"):
|
967
|
+
nx = node.nx
|
968
|
+
if nx is None:
|
969
|
+
nx = 1
|
970
|
+
ny = node.ny
|
971
|
+
if ny is None:
|
972
|
+
ny = 1
|
973
|
+
if nx != 1 or ny != 1:
|
974
|
+
symbol = f"{nx},{ny}"
|
975
|
+
if symbol:
|
976
|
+
font_size = 10 * zoomscale
|
977
|
+
if font_size < 1.0:
|
978
|
+
font_size = 1.0
|
979
|
+
try:
|
980
|
+
font = wx.Font(
|
981
|
+
font_size,
|
982
|
+
wx.FONTFAMILY_SWISS,
|
983
|
+
wx.FONTSTYLE_NORMAL,
|
984
|
+
wx.FONTWEIGHT_NORMAL,
|
985
|
+
)
|
986
|
+
except TypeError:
|
987
|
+
font = wx.Font(
|
988
|
+
int(font_size),
|
989
|
+
wx.FONTFAMILY_SWISS,
|
990
|
+
wx.FONTSTYLE_NORMAL,
|
991
|
+
wx.FONTWEIGHT_NORMAL,
|
992
|
+
)
|
993
|
+
gc.SetFont(font, wx.Colour(red=255, green=0, blue=0, alpha=alpha))
|
994
|
+
(t_width, t_height) = gc.GetTextExtent(symbol)
|
995
|
+
x = x_from + (x_from - (x_from + x_to) / 2) - t_width / 2
|
996
|
+
y = y_from + (y_from - (y_from + y_to) / 2) - t_height / 2
|
997
|
+
# is corner center then shift it a bit more
|
998
|
+
if node.corner == 4:
|
999
|
+
x += 0.75 * abs(x_to - x_from)
|
1000
|
+
y += 0.75 * abs(y_to - y_from)
|
1001
|
+
gc.DrawText(symbol, x, y)
|
1002
|
+
|
1003
|
+
gc.PopState()
|
1004
|
+
|
1005
|
+
def draw_point_node(self, node, gc, draw_mode, zoomscale=1.0, alpha=255):
|
1006
|
+
"""Default draw routine for the laser path element."""
|
1007
|
+
if draw_mode & DRAW_MODE_POINTS:
|
1008
|
+
return
|
1009
|
+
point = node.point
|
1010
|
+
gc.PushState()
|
1011
|
+
mypen = wx.Pen(wx.BLACK)
|
1012
|
+
try:
|
1013
|
+
mypen.SetWidth(zoomscale)
|
1014
|
+
except TypeError:
|
1015
|
+
mypen.SetWidth(int(zoomscale))
|
1016
|
+
gc.SetPen(mypen)
|
1017
|
+
dif = 5 * zoomscale
|
1018
|
+
gc.StrokeLine(point.x - dif, point.y, point.x + dif, point.y)
|
1019
|
+
gc.StrokeLine(point.x, point.y - dif, point.x, point.y + dif)
|
1020
|
+
gc.PopState()
|
1021
|
+
|
1022
|
+
def display_label(
|
1023
|
+
self, node, gc, draw_mode=0, zoomscale=1.0, alpha=255, color="#ff0000ff"
|
1024
|
+
):
|
1025
|
+
if node is None:
|
1026
|
+
return
|
1027
|
+
if not node.label:
|
1028
|
+
return
|
1029
|
+
try:
|
1030
|
+
bbox = node.bbox_group() if node.type == "group" else node.bbox()
|
1031
|
+
# print (f"{node.type}: {bbox}")
|
1032
|
+
except AttributeError:
|
1033
|
+
# print (f"This node has no bbox: {self.node.type}")
|
1034
|
+
return
|
1035
|
+
gc.PushState()
|
1036
|
+
cx = bbox[0] + 0.5 * (bbox[2] - bbox[0])
|
1037
|
+
cy = bbox[1] + 0.25 * (bbox[3] - bbox[1])
|
1038
|
+
symbol = node.display_label()
|
1039
|
+
font_size = 10 * zoomscale
|
1040
|
+
if font_size < 1.0:
|
1041
|
+
font_size = 1.0
|
1042
|
+
try:
|
1043
|
+
font = wx.Font(
|
1044
|
+
font_size,
|
1045
|
+
wx.FONTFAMILY_SWISS,
|
1046
|
+
wx.FONTSTYLE_NORMAL,
|
1047
|
+
wx.FONTWEIGHT_NORMAL,
|
1048
|
+
)
|
1049
|
+
except TypeError:
|
1050
|
+
font = wx.Font(
|
1051
|
+
int(font_size),
|
1052
|
+
wx.FONTFAMILY_SWISS,
|
1053
|
+
wx.FONTSTYLE_NORMAL,
|
1054
|
+
wx.FONTWEIGHT_NORMAL,
|
1055
|
+
)
|
1056
|
+
c = Color(color)
|
1057
|
+
gc.SetFont(font, wx.Colour(red=c.red, green=c.green, blue=c.blue, alpha=alpha))
|
1058
|
+
(t_width, t_height) = gc.GetTextExtent(symbol)
|
1059
|
+
x = cx - t_width / 2
|
1060
|
+
y = cy - t_height / 2
|
1061
|
+
gc.DrawText(symbol, x, y)
|
1062
|
+
|
1063
|
+
gc.PopState()
|
1064
|
+
|
1065
|
+
def draw_text_node(self, node, gc, draw_mode=0, zoomscale=1.0, alpha=255):
|
1066
|
+
if node is None:
|
1067
|
+
return
|
1068
|
+
text = node.text
|
1069
|
+
if text is None or text == "":
|
1070
|
+
return
|
1071
|
+
|
1072
|
+
try:
|
1073
|
+
matrix = node.matrix
|
1074
|
+
except AttributeError:
|
1075
|
+
matrix = None
|
1076
|
+
|
1077
|
+
svgfont_to_wx(node)
|
1078
|
+
font = node.wxfont
|
1079
|
+
|
1080
|
+
gc.PushState()
|
1081
|
+
if matrix is not None and not matrix.is_identity():
|
1082
|
+
gc.ConcatTransform(wx.GraphicsContext.CreateMatrix(gc, ZMatrix(matrix)))
|
1083
|
+
#
|
1084
|
+
# sw = node.implied_stroke_width
|
1085
|
+
# if draw_mode & DRAW_MODE_LINEWIDTH:
|
1086
|
+
# # No stroke rendering.
|
1087
|
+
# sw = 1000
|
1088
|
+
# self._set_penwidth(sw)
|
1089
|
+
# self.set_pen(
|
1090
|
+
# gc,
|
1091
|
+
# node.stroke,
|
1092
|
+
# alpha=alpha,
|
1093
|
+
# )
|
1094
|
+
# self.set_brush(gc, node.fill, alpha=255)
|
1095
|
+
|
1096
|
+
if node.fill is None or node.fill == "none":
|
1097
|
+
fill_color = wx.BLACK
|
1098
|
+
else:
|
1099
|
+
fill_color = as_wx_color(node.fill)
|
1100
|
+
gc.SetFont(font, fill_color)
|
1101
|
+
|
1102
|
+
if draw_mode & DRAW_MODE_VARIABLES:
|
1103
|
+
# Only if flag show the translated values
|
1104
|
+
text = self.context.elements.wordlist_translate(
|
1105
|
+
text, elemnode=node, increment=False
|
1106
|
+
)
|
1107
|
+
if node.texttransform is not None:
|
1108
|
+
ttf = node.texttransform.lower()
|
1109
|
+
if ttf == "capitalize":
|
1110
|
+
text = text.capitalize()
|
1111
|
+
elif ttf == "uppercase":
|
1112
|
+
text = text.upper()
|
1113
|
+
if ttf == "lowercase":
|
1114
|
+
text = text.lower()
|
1115
|
+
xmin, ymin, xmax, ymax = node.bbox(transformed=False)
|
1116
|
+
height = ymax - ymin
|
1117
|
+
width = xmax - xmin
|
1118
|
+
dy = 0
|
1119
|
+
dx = 0
|
1120
|
+
if node.anchor == "middle":
|
1121
|
+
dx -= width / 2
|
1122
|
+
elif node.anchor == "end":
|
1123
|
+
dx -= width
|
1124
|
+
gc.DrawText(text, dx, dy)
|
1125
|
+
gc.PopState()
|
1126
|
+
|
1127
|
+
def draw_image_node(self, node, gc, draw_mode, zoomscale=1.0, alpha=255):
|
1128
|
+
gc.PushState()
|
1129
|
+
|
1130
|
+
cache = None
|
1131
|
+
bounds = node.bbox()
|
1132
|
+
try:
|
1133
|
+
cache = node._cache
|
1134
|
+
except AttributeError:
|
1135
|
+
pass
|
1136
|
+
if cache is None:
|
1137
|
+
# We need to establish the cache
|
1138
|
+
try:
|
1139
|
+
image = node.active_image
|
1140
|
+
matrix = node.active_matrix
|
1141
|
+
bounds = 0, 0, image.width, image.height
|
1142
|
+
if matrix is not None and not matrix.is_identity():
|
1143
|
+
gc.ConcatTransform(wx.GraphicsContext.CreateMatrix(gc, ZMatrix(matrix)))
|
1144
|
+
except AttributeError:
|
1145
|
+
pass
|
1146
|
+
|
1147
|
+
try:
|
1148
|
+
max_allowed = node.max_allowed
|
1149
|
+
except AttributeError:
|
1150
|
+
max_allowed = 2048
|
1151
|
+
try:
|
1152
|
+
cache = self.make_thumbnail(
|
1153
|
+
image,
|
1154
|
+
maximum=max_allowed,
|
1155
|
+
alphablack=draw_mode & DRAW_MODE_ALPHABLACK == 0,
|
1156
|
+
)
|
1157
|
+
node._cache_width, node._cache_height = image.size
|
1158
|
+
node._cache = cache
|
1159
|
+
except Exception:
|
1160
|
+
pass
|
1161
|
+
|
1162
|
+
min_x, min_y, max_x, max_y = bounds
|
1163
|
+
gc.DrawBitmap(cache, min_x, min_y, max_x - min_x, max_y - min_y)
|
1164
|
+
|
1165
|
+
gc.PopState()
|
1166
|
+
if hasattr(node, "message"):
|
1167
|
+
txt = node.message
|
1168
|
+
if txt is not None:
|
1169
|
+
gc.PushState()
|
1170
|
+
gc.SetTransform(wx.GraphicsContext.CreateMatrix(gc, ZMatrix(None)))
|
1171
|
+
font = wx.Font()
|
1172
|
+
font.SetPointSize(20)
|
1173
|
+
gc.SetFont(font, wx.BLACK)
|
1174
|
+
gc.DrawText(txt, 30, 30)
|
1175
|
+
gc.PopState()
|
1176
|
+
|
1177
|
+
def measure_text(self, node):
|
1178
|
+
"""
|
1179
|
+
Use default measure text routines to calculate height etc.
|
1180
|
+
|
1181
|
+
Use the real draw of the font to calculate actual size.
|
1182
|
+
A 'real' height routine needs to draw the string on an
|
1183
|
+
empty canvas and find the first and last dots on a line...
|
1184
|
+
We are creating a temporary bitmap and paint on it...
|
1185
|
+
|
1186
|
+
@param node:
|
1187
|
+
@return:
|
1188
|
+
"""
|
1189
|
+
dimension_x = 1000
|
1190
|
+
dimension_y = 500
|
1191
|
+
scaling = 1
|
1192
|
+
bmp = wx.Bitmap(dimension_x, dimension_y, 32)
|
1193
|
+
dc = wx.MemoryDC()
|
1194
|
+
dc.SelectObject(bmp)
|
1195
|
+
dc.SetBackground(wx.BLACK_BRUSH)
|
1196
|
+
dc.Clear()
|
1197
|
+
gc = wx.GraphicsContext.Create(dc)
|
1198
|
+
|
1199
|
+
draw_mode = self.context.draw_mode
|
1200
|
+
if draw_mode & DRAW_MODE_VARIABLES:
|
1201
|
+
# Only if flag show the translated values
|
1202
|
+
text = self.context.elements.wordlist_translate(
|
1203
|
+
node.text, elemnode=node, increment=False
|
1204
|
+
)
|
1205
|
+
node.bounds_with_variables_translated = True
|
1206
|
+
else:
|
1207
|
+
text = node.text
|
1208
|
+
node.bounds_with_variables_translated = False
|
1209
|
+
if node.texttransform:
|
1210
|
+
ttf = node.texttransform.lower()
|
1211
|
+
if ttf == "capitalize":
|
1212
|
+
text = text.capitalize()
|
1213
|
+
elif ttf == "uppercase":
|
1214
|
+
text = text.upper()
|
1215
|
+
if ttf == "lowercase":
|
1216
|
+
text = text.lower()
|
1217
|
+
svgfont_to_wx(node)
|
1218
|
+
use_font = node.wxfont
|
1219
|
+
gc.SetFont(use_font, wx.WHITE)
|
1220
|
+
f_width, f_height, f_descent, f_external_leading = gc.GetFullTextExtent(text)
|
1221
|
+
needs_revision = False
|
1222
|
+
revision_factor = 3
|
1223
|
+
if revision_factor * f_width >= dimension_x:
|
1224
|
+
dimension_x = revision_factor * f_width
|
1225
|
+
needs_revision = True
|
1226
|
+
if revision_factor * f_height > dimension_y:
|
1227
|
+
dimension_y = revision_factor * f_height
|
1228
|
+
needs_revision = True
|
1229
|
+
if needs_revision:
|
1230
|
+
# We need to create an independent instance of the font
|
1231
|
+
# as we may to need to change the font_size temporarily
|
1232
|
+
fontdesc = node.wxfont.GetNativeFontInfoDesc()
|
1233
|
+
use_font = wx.Font(fontdesc)
|
1234
|
+
while True:
|
1235
|
+
try:
|
1236
|
+
fsize = use_font.GetFractionalPointSize()
|
1237
|
+
except AttributeError:
|
1238
|
+
fsize = use_font.GetPointSize()
|
1239
|
+
# print (f"Revised bounds: {dimension_x} x {dimension_y}, font_size={fsize} (original={fsize_org}")
|
1240
|
+
if fsize < 100 or dimension_x < 2000 or dimension_y < 1000:
|
1241
|
+
break
|
1242
|
+
# We consume an enormous amount of time and memory to create insanely big
|
1243
|
+
# temporary canvasses, so we intentionally reduce the resolution and accept
|
1244
|
+
# smaller deviations...
|
1245
|
+
scaling *= 10
|
1246
|
+
fsize /= 10
|
1247
|
+
dimension_x /= 10
|
1248
|
+
dimension_y /= 10
|
1249
|
+
try:
|
1250
|
+
use_font.SetFractionalPointSize(fsize)
|
1251
|
+
except AttributeError:
|
1252
|
+
use_font.SetPointSize(int(fsize))
|
1253
|
+
|
1254
|
+
gc.Destroy()
|
1255
|
+
dc.SelectObject(wx.NullBitmap)
|
1256
|
+
dc.Destroy()
|
1257
|
+
del dc
|
1258
|
+
bmp = wx.Bitmap(int(dimension_x), int(dimension_y), 32)
|
1259
|
+
dc = wx.MemoryDC()
|
1260
|
+
dc.SelectObject(bmp)
|
1261
|
+
dc.SetBackground(wx.BLACK_BRUSH)
|
1262
|
+
dc.Clear()
|
1263
|
+
gc = wx.GraphicsContext.Create(dc)
|
1264
|
+
gc.SetFont(use_font, wx.WHITE)
|
1265
|
+
|
1266
|
+
gc.DrawText(text, 0, 0)
|
1267
|
+
try:
|
1268
|
+
img = bmp.ConvertToImage()
|
1269
|
+
buf = img.GetData()
|
1270
|
+
image = Image.frombuffer(
|
1271
|
+
"RGB", tuple(bmp.GetSize()), bytes(buf), "raw", "RGB", 0, 1
|
1272
|
+
)
|
1273
|
+
node.text_cache = image
|
1274
|
+
img_bb = image.getbbox()
|
1275
|
+
if img_bb is None:
|
1276
|
+
node.raw_bbox = None
|
1277
|
+
else:
|
1278
|
+
newbb = (
|
1279
|
+
scaling * img_bb[0],
|
1280
|
+
scaling * img_bb[1],
|
1281
|
+
scaling * img_bb[2],
|
1282
|
+
scaling * img_bb[3],
|
1283
|
+
)
|
1284
|
+
node.raw_bbox = newbb
|
1285
|
+
except Exception:
|
1286
|
+
node.text_cache = None
|
1287
|
+
node.raw_bbox = None
|
1288
|
+
node.ascent = f_height - f_descent
|
1289
|
+
if node.baseline != "hanging":
|
1290
|
+
node.matrix.pre_translate(0, -node.ascent)
|
1291
|
+
if node.baseline == "middle":
|
1292
|
+
node.matrix.pre_translate(0, node.ascent / 2)
|
1293
|
+
node.baseline = "hanging"
|
1294
|
+
dc.SelectObject(wx.NullBitmap)
|
1295
|
+
dc.Destroy()
|
1296
|
+
del dc
|
1297
|
+
|
1298
|
+
def validate_text_nodes(self, nodes, translate_variables):
|
1299
|
+
self.context.elements.set_start_time("validate_text_nodes")
|
1300
|
+
for item in nodes:
|
1301
|
+
if item.type == "elem text" and (
|
1302
|
+
item._bounds_dirty
|
1303
|
+
or item._paint_bounds_dirty
|
1304
|
+
or item.bounds_with_variables_translated != translate_variables
|
1305
|
+
):
|
1306
|
+
# We never drew this cleanly; our initial bounds calculations will be off if we don't premeasure
|
1307
|
+
self.measure_text(item)
|
1308
|
+
item.set_dirty_bounds()
|
1309
|
+
dummy = item.bounds
|
1310
|
+
self.context.elements.set_end_time("validate_text_nodes")
|
1311
|
+
|
1312
|
+
def make_raster(
|
1313
|
+
self,
|
1314
|
+
nodes,
|
1315
|
+
bounds,
|
1316
|
+
width=None,
|
1317
|
+
height=None,
|
1318
|
+
bitmap=False,
|
1319
|
+
step_x=1,
|
1320
|
+
step_y=1,
|
1321
|
+
keep_ratio=False,
|
1322
|
+
):
|
1323
|
+
"""
|
1324
|
+
Make Raster turns an iterable of elements and a bounds into an image of the designated size, taking into account
|
1325
|
+
the step size. The physical pixels in the image is reduced by the step size then the matrix for the element is
|
1326
|
+
scaled up by the same amount. This makes step size work like inverse dpi and correctly sets the image scale to
|
1327
|
+
the step scale for 1:1 sizes independent of the scale.
|
1328
|
+
|
1329
|
+
This function requires both wxPython and Pillow.
|
1330
|
+
|
1331
|
+
@param nodes: elements to render.
|
1332
|
+
@param bounds: bounds of those elements for the viewport.
|
1333
|
+
@param width: desired width of the resulting raster
|
1334
|
+
@param height: desired height of the resulting raster
|
1335
|
+
@param bitmap: bitmap to use rather than provisioning
|
1336
|
+
@param step_x: raster step rate, scale rate of the image.
|
1337
|
+
@param step_y: raster step rate, scale rate of the image.
|
1338
|
+
@param keep_ratio: get a picture with the same height / width
|
1339
|
+
ratio as the original
|
1340
|
+
@return:
|
1341
|
+
"""
|
1342
|
+
if bounds is None:
|
1343
|
+
return None
|
1344
|
+
x_min = float("inf")
|
1345
|
+
y_min = float("inf")
|
1346
|
+
x_max = -float("inf")
|
1347
|
+
y_max = -float("inf")
|
1348
|
+
if not isinstance(nodes, (tuple, list)):
|
1349
|
+
_nodes = [nodes]
|
1350
|
+
else:
|
1351
|
+
_nodes = nodes
|
1352
|
+
|
1353
|
+
# if it's a raster we will always translate text variables...
|
1354
|
+
variable_translation = True
|
1355
|
+
nodecopy = list(_nodes)
|
1356
|
+
self.validate_text_nodes(nodecopy, variable_translation)
|
1357
|
+
|
1358
|
+
for item in _nodes:
|
1359
|
+
# bb = item.bounds
|
1360
|
+
bb = item.paint_bounds
|
1361
|
+
if bb is None:
|
1362
|
+
# Fall back to bounds
|
1363
|
+
bb = item.bounds
|
1364
|
+
if bb is None:
|
1365
|
+
continue
|
1366
|
+
if bb[0] < x_min:
|
1367
|
+
x_min = bb[0]
|
1368
|
+
if bb[1] < y_min:
|
1369
|
+
y_min = bb[1]
|
1370
|
+
if bb[2] > x_max:
|
1371
|
+
x_max = bb[2]
|
1372
|
+
if bb[3] > y_max:
|
1373
|
+
y_max = bb[3]
|
1374
|
+
raster_width = max(x_max - x_min, 1)
|
1375
|
+
raster_height = max(y_max - y_min, 1)
|
1376
|
+
if width is None:
|
1377
|
+
width = raster_width / step_x
|
1378
|
+
if height is None:
|
1379
|
+
height = raster_height / step_y
|
1380
|
+
width = max(width, 1)
|
1381
|
+
height = max(height, 1)
|
1382
|
+
bmp = wx.Bitmap(int(ceil(abs(width))), int(ceil(abs(height))), 32)
|
1383
|
+
dc = wx.MemoryDC()
|
1384
|
+
dc.SelectObject(bmp)
|
1385
|
+
dc.SetBackground(wx.WHITE_BRUSH)
|
1386
|
+
dc.Clear()
|
1387
|
+
|
1388
|
+
matrix = Matrix()
|
1389
|
+
|
1390
|
+
# Scale affine matrix up by step amount scaled down.
|
1391
|
+
try:
|
1392
|
+
scale_x = width / raster_width
|
1393
|
+
except ZeroDivisionError:
|
1394
|
+
scale_x = 1
|
1395
|
+
|
1396
|
+
try:
|
1397
|
+
scale_y = height / raster_height
|
1398
|
+
except ZeroDivisionError:
|
1399
|
+
scale_y = 1
|
1400
|
+
if keep_ratio:
|
1401
|
+
scale_x = min(scale_x, scale_y)
|
1402
|
+
scale_y = scale_x
|
1403
|
+
matrix.post_translate(-x_min, -y_min)
|
1404
|
+
matrix.post_scale(scale_x, scale_y)
|
1405
|
+
if scale_y < 0:
|
1406
|
+
matrix.pre_translate(0, -raster_height)
|
1407
|
+
if scale_x < 0:
|
1408
|
+
matrix.pre_translate(-raster_width, 0)
|
1409
|
+
|
1410
|
+
gc = wx.GraphicsContext.Create(dc)
|
1411
|
+
gc.dc = dc
|
1412
|
+
gc.SetInterpolationQuality(wx.INTERPOLATION_BEST)
|
1413
|
+
gc.PushState()
|
1414
|
+
if not matrix.is_identity():
|
1415
|
+
gc.ConcatTransform(wx.GraphicsContext.CreateMatrix(gc, ZMatrix(matrix)))
|
1416
|
+
gc.SetBrush(wx.WHITE_BRUSH)
|
1417
|
+
gc.DrawRectangle(x_min - 1, y_min - 1, x_max + 1, y_max + 1)
|
1418
|
+
self.render(_nodes, gc, draw_mode=DRAW_MODE_CACHE | DRAW_MODE_VARIABLES, msg="make_raster")
|
1419
|
+
img = bmp.ConvertToImage()
|
1420
|
+
buf = img.GetData()
|
1421
|
+
image = Image.frombuffer(
|
1422
|
+
"RGB", tuple(bmp.GetSize()), bytes(buf), "raw", "RGB", 0, 1
|
1423
|
+
)
|
1424
|
+
|
1425
|
+
gc.PopState()
|
1426
|
+
dc.SelectObject(wx.NullBitmap)
|
1427
|
+
gc.Destroy()
|
1428
|
+
del gc.dc
|
1429
|
+
del dc
|
1430
|
+
return bmp if bitmap else image
|
1431
|
+
|
1432
|
+
def make_thumbnail(
|
1433
|
+
self, pil_data, maximum=None, width=None, height=None, alphablack=True
|
1434
|
+
):
|
1435
|
+
"""Resizes the given pil image into wx.Bitmap object that fits the constraints."""
|
1436
|
+
image_width, image_height = pil_data.size
|
1437
|
+
if width is not None and height is None:
|
1438
|
+
height = width * image_height / float(image_width)
|
1439
|
+
if width is None and height is not None:
|
1440
|
+
width = height * image_width / float(image_height)
|
1441
|
+
if width is None and height is None:
|
1442
|
+
width = image_width
|
1443
|
+
height = image_height
|
1444
|
+
if maximum is not None and (width > maximum or height > maximum):
|
1445
|
+
scale_x = maximum / width
|
1446
|
+
scale_y = maximum / height
|
1447
|
+
scale = min(scale_x, scale_y)
|
1448
|
+
width = int(round(width * scale))
|
1449
|
+
height = int(round(height * scale))
|
1450
|
+
if image_width != width or image_height != height:
|
1451
|
+
pil_data = pil_data.resize((width, height))
|
1452
|
+
else:
|
1453
|
+
pil_data = pil_data.copy()
|
1454
|
+
if not alphablack:
|
1455
|
+
return wx.Bitmap.FromBufferRGBA(
|
1456
|
+
width, height, pil_data.convert("RGBA").tobytes()
|
1457
|
+
)
|
1458
|
+
if "transparency" in pil_data.info:
|
1459
|
+
pil_data = pil_data.convert("RGBA")
|
1460
|
+
try:
|
1461
|
+
# If transparent we paste 0 into the pil_data
|
1462
|
+
mask = pil_data.getchannel("A").point(lambda e: 255 - e)
|
1463
|
+
pil_data.paste(mask, None, mask)
|
1464
|
+
except ValueError:
|
1465
|
+
pass
|
1466
|
+
if pil_data.mode != "L":
|
1467
|
+
pil_data = pil_data.convert("L")
|
1468
|
+
black = Image.new("RGBA", pil_data.size, "black")
|
1469
|
+
black.putalpha(pil_data.point(lambda e: 255 - e))
|
1470
|
+
return wx.Bitmap.FromBufferRGBA(width, height, black.tobytes())
|