From 99fe2ca106a3a5c69217f445010e20e83b51c7a6 Mon Sep 17 00:00:00 2001 From: Silas Labor Zizou Date: Fri, 25 Oct 2024 20:31:50 +0200 Subject: [PATCH] Commit Friday evening. PDFA and EXFO Laser are now part of the family --- Classes/00_signals/Signal.m | 8 +- Classes/01_transmit/ChannelFreqResp.m | 11 +- Classes/05_Lab/AwgKeysight.m | 6 +- Classes/05_Lab/Exfo_laser.m | 361 +- Classes/05_Lab/ScopeKeysight.m | 2 +- Classes/05_Lab/Thor_PDFA.m | 309 + Functions/calcWavelengthPlan.m | 24 + .../Violinplot-Matlab-master/LICENSE | 11 + .../Violinplot-Matlab-master/README.md | 60 + .../Violinplot-Matlab-master/Violin.m | 713 ++ .../Violinplot-Matlab-master/example.png | Bin 0 -> 43429 bytes .../Violinplot-Matlab-master/example2.png | Bin 0 -> 34423 bytes .../test_cases/testviolinplot.m | 87 + .../Violinplot-Matlab-master/violinplot.m | 193 + .../autoArrangeFigures.m | 115 + .../boundedlines/.gitignore | 45 + .../boundedlines/Inpaint_nans/.gitignore | 37 + .../demo/html/inpaint_nans_demo.html | 161 + .../demo/html/inpaint_nans_demo.png | Bin 0 -> 3192 bytes .../demo/html/inpaint_nans_demo_01.png | Bin 0 -> 15931 bytes .../demo/html/inpaint_nans_demo_02.png | Bin 0 -> 17307 bytes .../demo/html/inpaint_nans_demo_03.png | Bin 0 -> 16350 bytes .../demo/html/inpaint_nans_demo_04.png | Bin 0 -> 15935 bytes .../demo/html/inpaint_nans_demo_05.png | Bin 0 -> 13255 bytes .../demo/html/inpaint_nans_demo_06.png | Bin 0 -> 9398 bytes .../Inpaint_nans/demo/inpaint_nans_demo_old.m | 42 + .../Inpaint_nans/doc/Nomination comments.rtf | 39 + .../doc/methods_of_inpaint_nans.m | 187 + .../boundedlines/Inpaint_nans/garden50.jpg | Bin 0 -> 22991 bytes .../boundedlines/Inpaint_nans/inpaint_nans.m | 1 + .../Inpaint_nans/inpaint_nans_bc.m | 1 + .../Inpaint_nans/inpaint_nans_demo.m | 43 + .../boundedlines/Inpaint_nans/license.txt | 24 + .../Inpaint_nans/monet_adresse.jpg | Bin 0 -> 148268 bytes .../Inpaint_nans/test/test_main.m | 178 + .../boundedlines/LICENSE.txt | 20 + .../boundedlines/README.html | 377 + .../boundedlines/README.m | 240 + .../boundedlines/README.md | 279 + .../boundedlines/boundedline/.gitignore | 37 + .../boundedlines/boundedline/boundedline.m | 501 ++ .../boundedlines/boundedline/outlinebounds.m | 43 + .../boundedlines/catuneven/.gitignore | 37 + .../boundedlines/catuneven/catuneven.m | 57 + .../boundedlines/readmeExtras/README_01.png | Bin 0 -> 17036 bytes .../boundedlines/readmeExtras/README_02.png | Bin 0 -> 27039 bytes .../boundedlines/readmeExtras/README_03.png | Bin 0 -> 34757 bytes .../boundedlines/readmeExtras/README_04.png | Bin 0 -> 45037 bytes .../boundedlines/readmeExtras/README_05.png | Bin 0 -> 16870 bytes .../boundedlines/readmeExtras/README_06.png | Bin 0 -> 24808 bytes .../boundedlines/readmeExtras/README_07.png | Bin 0 -> 25461 bytes .../boundedlines/readmeExtras/README_08.png | Bin 0 -> 24740 bytes .../boundedlines/singlepatch/.gitignore | 37 + .../boundedlines/singlepatch/singlepatch.m | 65 + .../cbrewer2/cbrewer2.m | 260 + .../cbrewer2/colorbrewer.mat | Bin 0 -> 4475 bytes .../cbrewer2/requireFEXpackage.m | 194 + .../colorspace/colorspace.c | 1285 +++ .../colorspace/colorspace.h | 72 + .../colorspace/colorspace.m | 493 ++ .../helper_functions_community/linspecer.m | 261 + .../mat2tikz/.gitignore | 6 + .../mat2tikz/.travis.yml | 16 + .../mat2tikz/AUTHORS.md | 64 + .../mat2tikz/CHANGELOG.md | 439 + .../mat2tikz/CONTRIBUTING.md | 66 + .../mat2tikz/LICENSE.md | 24 + .../mat2tikz/README.md | 95 + .../mat2tikz/logos/matlab2tikz.svg | 102 + .../mat2tikz/matlab2tikz.sublime-project | 49 + .../mat2tikz/runtests.sh | 86 + .../mat2tikz/src/cleanfigure.m | 1294 +++ .../mat2tikz/src/dev/formatWhitespace.m | 87 + .../mat2tikz/src/figure2dot.m | 123 + .../mat2tikz/src/m2tInputParser.m | 231 + .../mat2tikz/src/m2tcustom.m | 216 + .../mat2tikz/src/matlab2tikz.m | 7167 +++++++++++++++++ .../src/private/errorUnknownEnvironment.m | 5 + .../mat2tikz/src/private/getEnvironment.m | 25 + .../mat2tikz/src/private/guitypes.m | 19 + .../mat2tikz/src/private/isAxis3D.m | 5 + .../mat2tikz/src/private/isVersionBelow.m | 13 + .../mat2tikz/src/private/m2tUpdater.m | 320 + .../mat2tikz/src/private/m2tstrjoin.m | 35 + .../mat2tikz/src/private/versionArray.m | 18 + .../mat2tikz/src/private/versionString.m | 9 + .../mat2tikz/test/README.md | 101 + .../mat2tikz/test/codeReport.m | 280 + .../mat2tikz/test/compareTimings.m | 256 + .../mat2tikz/test/examples/example_bar_plot.m | 53 + .../mat2tikz/test/examples/example_quivers.m | 80 + .../mat2tikz/test/makeLatexReport.m | 222 + .../mat2tikz/test/makeTapReport.m | 74 + .../mat2tikz/test/makeTravisReport.m | 360 + .../mat2tikz/test/output/.gitignore | 4 + .../mat2tikz/test/private/OSVersion.m | 36 + .../mat2tikz/test/private/StreamMaker.m | 74 + .../test/private/VersionControlIdentifier.m | 47 + .../mat2tikz/test/private/calculateMD5Hash.m | 35 + .../mat2tikz/test/private/cleanFiles.m | 19 + .../test/private/countNumberOfErrors.m | 4 + .../mat2tikz/test/private/emptyStage.m | 4 + .../mat2tikz/test/private/emptyStatus.m | 24 + .../mat2tikz/test/private/errorHandler.m | 41 + .../mat2tikz/test/private/errorHasOccurred.m | 16 + .../test/private/execute_hash_stage.m | 34 + .../test/private/execute_plot_stage.m | 45 + .../test/private/execute_save_stage.m | 63 + .../test/private/execute_tikz_stage.m | 43 + .../test/private/execute_type_stage.m | 15 + .../mat2tikz/test/private/fillStruct.m | 10 + .../mat2tikz/test/private/getEnvironment.m | 25 + .../test/private/getStagesFromStatus.m | 5 + .../mat2tikz/test/private/hasTestFailed.m | 13 + .../mat2tikz/test/private/hashTableName.m | 55 + .../test/private/initializeGlobalState.m | 99 + .../mat2tikz/test/private/loadHashTable.m | 19 + .../mat2tikz/test/private/m2troot.m | 30 + .../mat2tikz/test/private/m2tstrjoin.m | 24 + .../test/private/restoreGlobalState.m | 11 + .../test/private/splitPassFailSkippedTests.m | 10 + .../test/private/splitUnreliableTests.m | 7 + .../mat2tikz/test/private/testMatlab2tikz.m | 146 + .../mat2tikz/test/runMatlab2TikzTests.m | 38 + .../mat2tikz/test/saveHashTable.m | 164 + .../mat2tikz/test/suites/ACID.MATLAB.8.3.md5 | 104 + .../mat2tikz/test/suites/ACID.MATLAB.8.4.md5 | 104 + .../test/suites/ACID.Octave.3.8.0.md5 | 87 + .../test/suites/ACID.Octave.4.2.0.md5 | 87 + .../test/suites/ACID.Octave.4.2.2.md5 | 87 + .../mat2tikz/test/suites/ACID.m | 2819 +++++++ .../mat2tikz/test/suites/issues.m | 43 + .../test/suites/private/getEnvironment.m | 25 + .../mat2tikz/test/suites/private/herrorbar.m | 167 + .../test/suites/private/isEnvironment.m | 46 + .../mat2tikz/test/suites/private/isMATLAB.m | 4 + .../mat2tikz/test/suites/private/isOctave.m | 5 + .../test/suites/private/isVersionBelow.m | 38 + .../test/suites/private/versionCompare.m | 20 + .../mat2tikz/test/suites/testPatches.m | 121 + .../mat2tikz/test/suites/testSurfshader.m | 102 + .../mat2tikz/test/template/.gitignore | 6 + .../mat2tikz/test/template/Makefile | 33 + .../mat2tikz/test/template/data/.gitignore | 8 + .../test/template/data/converted/Makefile | 23 + .../test/template/data/reference/Makefile | 19 + .../mat2tikz/test/testGraphical.m | 47 + .../mat2tikz/test/testHeadless.m | 62 + .../helper_functions_community/png/nt_.png | Bin 0 -> 58301 bytes .../schemer/.gitattributes | 46 + .../schemer/.gitignore | 5 + .../schemer/CONTRIBUTING.md | 47 + .../schemer/LICENSE | 23 + .../schemer/README.md | 204 + .../schemer/develop/RGBint2hex.m | 42 + .../schemer/develop/annotated_default.png | Bin 0 -> 60704 bytes .../schemer/develop/color2javaRGBint.m | 131 + .../schemer/develop/sample.cpp | 22 + .../schemer/develop/sample.html | 39 + .../schemer/develop/sample.java | 23 + .../schemer/develop/sample.m | 21 + .../schemer/develop/sample.tlc | 27 + .../schemer/develop/sample.v | 21 + .../schemer/develop/sample.vhdl | 19 + .../schemer/develop/sample.x3d | 52 + .../schemer/develop/sample.x3dv | 53 + .../schemer/develop/sample.xml | 17 + .../schemer/develop/short_sample.m | 15 + .../schemer/develop/template_scheme.prf | 34 + .../schemer/logo.png | Bin 0 -> 23752 bytes .../schemer/schemer_export.m | 677 ++ .../schemer/schemer_import.m | 712 ++ .../schemer/schemes/.gitattributes | 46 + .../schemer/schemes/.gitignore | 5 + .../schemer/schemes/CONTRIBUTING.md | 153 + .../schemer/schemes/LICENSE | 23 + .../schemer/schemes/README.md | 84 + .../schemes/SilasColorSchemeForMATLAB.prf | 33 + .../schemer/schemes/cobalt.prf | 45 + .../schemer/schemes/darkmate.prf | 34 + .../schemer/schemes/darksteel.prf | 44 + .../schemer/schemes/default.prf | 95 + .../schemer/schemes/matrix.prf | 31 + .../schemer/schemes/monokai.prf | 32 + .../schemer/schemes/oblivion.prf | 52 + .../schemer/schemes/screenshots/cobalt.png | Bin 0 -> 31170 bytes .../schemer/schemes/screenshots/darkmate.png | Bin 0 -> 31868 bytes .../schemer/schemes/screenshots/darksteel.png | Bin 0 -> 31598 bytes .../schemer/schemes/screenshots/default.png | Bin 0 -> 28705 bytes .../schemer/schemes/screenshots/matrix.png | Bin 0 -> 31324 bytes .../schemer/schemes/screenshots/monokai.png | Bin 0 -> 98691 bytes .../schemer/schemes/screenshots/oblivion.png | Bin 0 -> 32099 bytes .../schemes/screenshots/solarized-dark.png | Bin 0 -> 31523 bytes .../schemes/screenshots/solarized-light.png | Bin 0 -> 31602 bytes .../schemer/schemes/screenshots/tango.png | Bin 0 -> 31003 bytes .../schemer/schemes/screenshots/vibrant.png | Bin 0 -> 30190 bytes .../schemer/schemes/solarized-dark.prf | 55 + .../schemer/schemes/solarized-light.prf | 55 + .../schemer/schemes/tango.prf | 66 + .../schemer/schemes/vibrant.prf | 32 + .../helper_functions_community/sendolmail.m | 20 + Tests/05_Lab/Laser_minimal.m | 25 + projects/400G_FTN_setups/imdd_mpi_dsp.m | 341 +- .../baudrate_evaluation.m | 131 + .../HighSpeedExperiment_2024/baudrate_sweep.m | 341 + .../bias_evaluation.m | 66 + .../bias_optimization.m | 592 +- .../400G_simulative_setup_meas.mat | Bin 0 -> 2993953 bytes 208 files changed, 28244 insertions(+), 460 deletions(-) create mode 100644 Classes/05_Lab/Thor_PDFA.m create mode 100644 Functions/calcWavelengthPlan.m create mode 100644 Functions/helper_functions_community/Violinplot-Matlab-master/LICENSE create mode 100644 Functions/helper_functions_community/Violinplot-Matlab-master/README.md create mode 100644 Functions/helper_functions_community/Violinplot-Matlab-master/Violin.m create mode 100644 Functions/helper_functions_community/Violinplot-Matlab-master/example.png create mode 100644 Functions/helper_functions_community/Violinplot-Matlab-master/example2.png create mode 100644 Functions/helper_functions_community/Violinplot-Matlab-master/test_cases/testviolinplot.m create mode 100644 Functions/helper_functions_community/Violinplot-Matlab-master/violinplot.m create mode 100644 Functions/helper_functions_community/autoArrangeFigures.m create mode 100644 Functions/helper_functions_community/boundedlines/.gitignore create mode 100644 Functions/helper_functions_community/boundedlines/Inpaint_nans/.gitignore create mode 100644 Functions/helper_functions_community/boundedlines/Inpaint_nans/demo/html/inpaint_nans_demo.html create mode 100644 Functions/helper_functions_community/boundedlines/Inpaint_nans/demo/html/inpaint_nans_demo.png create mode 100644 Functions/helper_functions_community/boundedlines/Inpaint_nans/demo/html/inpaint_nans_demo_01.png create mode 100644 Functions/helper_functions_community/boundedlines/Inpaint_nans/demo/html/inpaint_nans_demo_02.png create mode 100644 Functions/helper_functions_community/boundedlines/Inpaint_nans/demo/html/inpaint_nans_demo_03.png create mode 100644 Functions/helper_functions_community/boundedlines/Inpaint_nans/demo/html/inpaint_nans_demo_04.png create mode 100644 Functions/helper_functions_community/boundedlines/Inpaint_nans/demo/html/inpaint_nans_demo_05.png create mode 100644 Functions/helper_functions_community/boundedlines/Inpaint_nans/demo/html/inpaint_nans_demo_06.png create mode 100644 Functions/helper_functions_community/boundedlines/Inpaint_nans/demo/inpaint_nans_demo_old.m create mode 100644 Functions/helper_functions_community/boundedlines/Inpaint_nans/doc/Nomination comments.rtf create mode 100644 Functions/helper_functions_community/boundedlines/Inpaint_nans/doc/methods_of_inpaint_nans.m create mode 100644 Functions/helper_functions_community/boundedlines/Inpaint_nans/garden50.jpg create mode 100644 Functions/helper_functions_community/boundedlines/Inpaint_nans/inpaint_nans.m create mode 100644 Functions/helper_functions_community/boundedlines/Inpaint_nans/inpaint_nans_bc.m create mode 100644 Functions/helper_functions_community/boundedlines/Inpaint_nans/inpaint_nans_demo.m create mode 100644 Functions/helper_functions_community/boundedlines/Inpaint_nans/license.txt create mode 100644 Functions/helper_functions_community/boundedlines/Inpaint_nans/monet_adresse.jpg create mode 100644 Functions/helper_functions_community/boundedlines/Inpaint_nans/test/test_main.m create mode 100644 Functions/helper_functions_community/boundedlines/LICENSE.txt create mode 100644 Functions/helper_functions_community/boundedlines/README.html create mode 100644 Functions/helper_functions_community/boundedlines/README.m create mode 100644 Functions/helper_functions_community/boundedlines/README.md create mode 100644 Functions/helper_functions_community/boundedlines/boundedline/.gitignore create mode 100644 Functions/helper_functions_community/boundedlines/boundedline/boundedline.m create mode 100644 Functions/helper_functions_community/boundedlines/boundedline/outlinebounds.m create mode 100644 Functions/helper_functions_community/boundedlines/catuneven/.gitignore create mode 100644 Functions/helper_functions_community/boundedlines/catuneven/catuneven.m create mode 100644 Functions/helper_functions_community/boundedlines/readmeExtras/README_01.png create mode 100644 Functions/helper_functions_community/boundedlines/readmeExtras/README_02.png create mode 100644 Functions/helper_functions_community/boundedlines/readmeExtras/README_03.png create mode 100644 Functions/helper_functions_community/boundedlines/readmeExtras/README_04.png create mode 100644 Functions/helper_functions_community/boundedlines/readmeExtras/README_05.png create mode 100644 Functions/helper_functions_community/boundedlines/readmeExtras/README_06.png create mode 100644 Functions/helper_functions_community/boundedlines/readmeExtras/README_07.png create mode 100644 Functions/helper_functions_community/boundedlines/readmeExtras/README_08.png create mode 100644 Functions/helper_functions_community/boundedlines/singlepatch/.gitignore create mode 100644 Functions/helper_functions_community/boundedlines/singlepatch/singlepatch.m create mode 100644 Functions/helper_functions_community/cbrewer2/cbrewer2.m create mode 100644 Functions/helper_functions_community/cbrewer2/colorbrewer.mat create mode 100644 Functions/helper_functions_community/cbrewer2/requireFEXpackage.m create mode 100644 Functions/helper_functions_community/colorspace/colorspace.c create mode 100644 Functions/helper_functions_community/colorspace/colorspace.h create mode 100644 Functions/helper_functions_community/colorspace/colorspace.m create mode 100644 Functions/helper_functions_community/linspecer.m create mode 100644 Functions/helper_functions_community/mat2tikz/.gitignore create mode 100644 Functions/helper_functions_community/mat2tikz/.travis.yml create mode 100644 Functions/helper_functions_community/mat2tikz/AUTHORS.md create mode 100644 Functions/helper_functions_community/mat2tikz/CHANGELOG.md create mode 100644 Functions/helper_functions_community/mat2tikz/CONTRIBUTING.md create mode 100644 Functions/helper_functions_community/mat2tikz/LICENSE.md create mode 100644 Functions/helper_functions_community/mat2tikz/README.md create mode 100644 Functions/helper_functions_community/mat2tikz/logos/matlab2tikz.svg create mode 100644 Functions/helper_functions_community/mat2tikz/matlab2tikz.sublime-project create mode 100644 Functions/helper_functions_community/mat2tikz/runtests.sh create mode 100644 Functions/helper_functions_community/mat2tikz/src/cleanfigure.m create mode 100644 Functions/helper_functions_community/mat2tikz/src/dev/formatWhitespace.m create mode 100644 Functions/helper_functions_community/mat2tikz/src/figure2dot.m create mode 100644 Functions/helper_functions_community/mat2tikz/src/m2tInputParser.m create mode 100644 Functions/helper_functions_community/mat2tikz/src/m2tcustom.m create mode 100644 Functions/helper_functions_community/mat2tikz/src/matlab2tikz.m create mode 100644 Functions/helper_functions_community/mat2tikz/src/private/errorUnknownEnvironment.m create mode 100644 Functions/helper_functions_community/mat2tikz/src/private/getEnvironment.m create mode 100644 Functions/helper_functions_community/mat2tikz/src/private/guitypes.m create mode 100644 Functions/helper_functions_community/mat2tikz/src/private/isAxis3D.m create mode 100644 Functions/helper_functions_community/mat2tikz/src/private/isVersionBelow.m create mode 100644 Functions/helper_functions_community/mat2tikz/src/private/m2tUpdater.m create mode 100644 Functions/helper_functions_community/mat2tikz/src/private/m2tstrjoin.m create mode 100644 Functions/helper_functions_community/mat2tikz/src/private/versionArray.m create mode 100644 Functions/helper_functions_community/mat2tikz/src/private/versionString.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/README.md create mode 100644 Functions/helper_functions_community/mat2tikz/test/codeReport.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/compareTimings.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/examples/example_bar_plot.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/examples/example_quivers.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/makeLatexReport.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/makeTapReport.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/makeTravisReport.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/output/.gitignore create mode 100644 Functions/helper_functions_community/mat2tikz/test/private/OSVersion.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/private/StreamMaker.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/private/VersionControlIdentifier.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/private/calculateMD5Hash.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/private/cleanFiles.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/private/countNumberOfErrors.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/private/emptyStage.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/private/emptyStatus.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/private/errorHandler.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/private/errorHasOccurred.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/private/execute_hash_stage.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/private/execute_plot_stage.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/private/execute_save_stage.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/private/execute_tikz_stage.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/private/execute_type_stage.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/private/fillStruct.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/private/getEnvironment.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/private/getStagesFromStatus.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/private/hasTestFailed.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/private/hashTableName.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/private/initializeGlobalState.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/private/loadHashTable.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/private/m2troot.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/private/m2tstrjoin.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/private/restoreGlobalState.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/private/splitPassFailSkippedTests.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/private/splitUnreliableTests.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/private/testMatlab2tikz.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/runMatlab2TikzTests.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/saveHashTable.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/suites/ACID.MATLAB.8.3.md5 create mode 100644 Functions/helper_functions_community/mat2tikz/test/suites/ACID.MATLAB.8.4.md5 create mode 100644 Functions/helper_functions_community/mat2tikz/test/suites/ACID.Octave.3.8.0.md5 create mode 100644 Functions/helper_functions_community/mat2tikz/test/suites/ACID.Octave.4.2.0.md5 create mode 100644 Functions/helper_functions_community/mat2tikz/test/suites/ACID.Octave.4.2.2.md5 create mode 100644 Functions/helper_functions_community/mat2tikz/test/suites/ACID.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/suites/issues.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/suites/private/getEnvironment.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/suites/private/herrorbar.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/suites/private/isEnvironment.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/suites/private/isMATLAB.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/suites/private/isOctave.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/suites/private/isVersionBelow.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/suites/private/versionCompare.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/suites/testPatches.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/suites/testSurfshader.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/template/.gitignore create mode 100644 Functions/helper_functions_community/mat2tikz/test/template/Makefile create mode 100644 Functions/helper_functions_community/mat2tikz/test/template/data/.gitignore create mode 100644 Functions/helper_functions_community/mat2tikz/test/template/data/converted/Makefile create mode 100644 Functions/helper_functions_community/mat2tikz/test/template/data/reference/Makefile create mode 100644 Functions/helper_functions_community/mat2tikz/test/testGraphical.m create mode 100644 Functions/helper_functions_community/mat2tikz/test/testHeadless.m create mode 100644 Functions/helper_functions_community/png/nt_.png create mode 100644 Functions/helper_functions_community/schemer/.gitattributes create mode 100644 Functions/helper_functions_community/schemer/.gitignore create mode 100644 Functions/helper_functions_community/schemer/CONTRIBUTING.md create mode 100644 Functions/helper_functions_community/schemer/LICENSE create mode 100644 Functions/helper_functions_community/schemer/README.md create mode 100644 Functions/helper_functions_community/schemer/develop/RGBint2hex.m create mode 100644 Functions/helper_functions_community/schemer/develop/annotated_default.png create mode 100644 Functions/helper_functions_community/schemer/develop/color2javaRGBint.m create mode 100644 Functions/helper_functions_community/schemer/develop/sample.cpp create mode 100644 Functions/helper_functions_community/schemer/develop/sample.html create mode 100644 Functions/helper_functions_community/schemer/develop/sample.java create mode 100644 Functions/helper_functions_community/schemer/develop/sample.m create mode 100644 Functions/helper_functions_community/schemer/develop/sample.tlc create mode 100644 Functions/helper_functions_community/schemer/develop/sample.v create mode 100644 Functions/helper_functions_community/schemer/develop/sample.vhdl create mode 100644 Functions/helper_functions_community/schemer/develop/sample.x3d create mode 100644 Functions/helper_functions_community/schemer/develop/sample.x3dv create mode 100644 Functions/helper_functions_community/schemer/develop/sample.xml create mode 100644 Functions/helper_functions_community/schemer/develop/short_sample.m create mode 100644 Functions/helper_functions_community/schemer/develop/template_scheme.prf create mode 100644 Functions/helper_functions_community/schemer/logo.png create mode 100644 Functions/helper_functions_community/schemer/schemer_export.m create mode 100644 Functions/helper_functions_community/schemer/schemer_import.m create mode 100644 Functions/helper_functions_community/schemer/schemes/.gitattributes create mode 100644 Functions/helper_functions_community/schemer/schemes/.gitignore create mode 100644 Functions/helper_functions_community/schemer/schemes/CONTRIBUTING.md create mode 100644 Functions/helper_functions_community/schemer/schemes/LICENSE create mode 100644 Functions/helper_functions_community/schemer/schemes/README.md create mode 100644 Functions/helper_functions_community/schemer/schemes/SilasColorSchemeForMATLAB.prf create mode 100644 Functions/helper_functions_community/schemer/schemes/cobalt.prf create mode 100644 Functions/helper_functions_community/schemer/schemes/darkmate.prf create mode 100644 Functions/helper_functions_community/schemer/schemes/darksteel.prf create mode 100644 Functions/helper_functions_community/schemer/schemes/default.prf create mode 100644 Functions/helper_functions_community/schemer/schemes/matrix.prf create mode 100644 Functions/helper_functions_community/schemer/schemes/monokai.prf create mode 100644 Functions/helper_functions_community/schemer/schemes/oblivion.prf create mode 100644 Functions/helper_functions_community/schemer/schemes/screenshots/cobalt.png create mode 100644 Functions/helper_functions_community/schemer/schemes/screenshots/darkmate.png create mode 100644 Functions/helper_functions_community/schemer/schemes/screenshots/darksteel.png create mode 100644 Functions/helper_functions_community/schemer/schemes/screenshots/default.png create mode 100644 Functions/helper_functions_community/schemer/schemes/screenshots/matrix.png create mode 100644 Functions/helper_functions_community/schemer/schemes/screenshots/monokai.png create mode 100644 Functions/helper_functions_community/schemer/schemes/screenshots/oblivion.png create mode 100644 Functions/helper_functions_community/schemer/schemes/screenshots/solarized-dark.png create mode 100644 Functions/helper_functions_community/schemer/schemes/screenshots/solarized-light.png create mode 100644 Functions/helper_functions_community/schemer/schemes/screenshots/tango.png create mode 100644 Functions/helper_functions_community/schemer/schemes/screenshots/vibrant.png create mode 100644 Functions/helper_functions_community/schemer/schemes/solarized-dark.prf create mode 100644 Functions/helper_functions_community/schemer/schemes/solarized-light.prf create mode 100644 Functions/helper_functions_community/schemer/schemes/tango.prf create mode 100644 Functions/helper_functions_community/schemer/schemes/vibrant.prf create mode 100644 Functions/helper_functions_community/sendolmail.m create mode 100644 Tests/05_Lab/Laser_minimal.m create mode 100644 projects/HighSpeedExperiment_2024/baudrate_evaluation.m create mode 100644 projects/HighSpeedExperiment_2024/baudrate_sweep.m create mode 100644 projects/HighSpeedExperiment_2024/bias_evaluation.m create mode 100644 projects/standard_system/400G_simulative_setup_meas.mat diff --git a/Classes/00_signals/Signal.m b/Classes/00_signals/Signal.m index 3ecb6e4..a3a5007 100644 --- a/Classes/00_signals/Signal.m +++ b/Classes/00_signals/Signal.m @@ -344,7 +344,7 @@ classdef Signal % spectrum_plot(obj.signal,options.fsamp,options.figurename,options.displayname); - N = 2^(nextpow2(length(obj.signal))-10); + N = 2^(nextpow2(length(obj.signal))-8); if options.normalizeToNyquist==0 [p_lin,w] = pwelch(obj.signal,hanning(N),N/2,N,obj.fs,"centered","power","mean"); @@ -379,7 +379,7 @@ classdef Signal %xlim([-obj.fs/2 obj.fs/2].*1e-9) edgetick = 2^(nextpow2(obj.fs*1e-9)); xticks([-edgetick:16:edgetick]); - xlim([100*round( min(w.*1e-9)/100,1)-10,100*round( max(w.*1e-9)/100,1)+10]) + xlim([100*round( min(w)/100,1)-10,100*round( max(w)/100,1)+10]) else xlabel("Normalized Frequency"); xlim([-pi, pi]); @@ -554,6 +554,10 @@ classdef Signal %return/keep the sinal with the highest correlation (only within positive shifts) [~,idx]=max(pks(shifts>0)); obj.signal = S{idx}.signal; + %put signal with highest corr. to first index in S array + swap = S{1}; + S{1} = S{idx}; + S{idx} = swap; for c = 1:numel(shifts(shifts>0)) S{c}.logbook = []; diff --git a/Classes/01_transmit/ChannelFreqResp.m b/Classes/01_transmit/ChannelFreqResp.m index 586e580..5bf2ede 100644 --- a/Classes/01_transmit/ChannelFreqResp.m +++ b/Classes/01_transmit/ChannelFreqResp.m @@ -241,10 +241,11 @@ classdef ChannelFreqResp < handle xlim([0.2 .5*max(obj.faxis)*1e-9]); grid on; %%% plot for publication - figure(98989);hold on;box on;title('Magnitude Freq. Response'); - xlim([0.2 .5*max(obj.faxis)*1e-9]); - ylim([-20, 2]); - plot(obj.faxis/1e9, 20*log10(abs(Havg)),'LineWidth',2); + figure(20);hold on;box on;title('Magnitude Freq. Response'); + % xlim([0 max(obj.faxis)*1e-9]); + % ylim([-20, 10]); + fax = obj.faxis - obj.f_ref/2; + plot(fax/1e9, 20*log10(abs(fftshift(Havg)))+7,'LineWidth',2); grid on; @@ -360,7 +361,7 @@ classdef ChannelFreqResp < handle end - fprintf('Frequency response information successfully loaded from %s\n', fullFileName); + % fprintf('Frequency response information successfully loaded from %s\n', fullFileName); end end diff --git a/Classes/05_Lab/AwgKeysight.m b/Classes/05_Lab/AwgKeysight.m index 1d35328..d236868 100644 --- a/Classes/05_Lab/AwgKeysight.m +++ b/Classes/05_Lab/AwgKeysight.m @@ -176,7 +176,11 @@ classdef AwgKeysight % The waveform granularity is e.g. 128. This means that the waveform length must be a multiple of this granularity. The minimum waveform length is 128 samples. if rem(length(signal{s}),obj.waveformGranularity) ~= 0 signal{s} = [signal{s} signal{s}(:,1:(obj.waveformGranularity-rem(length(signal{s}),obj.waveformGranularity)))]; - warning(['data should have a length multiple of ',num2str(obj.waveformGranularity),'!']); + + debug = 0; + if debug + warning(['data should have a length multiple of ',num2str(obj.waveformGranularity),'!']); + end end % Digitally apply skews to the signals diff --git a/Classes/05_Lab/Exfo_laser.m b/Classes/05_Lab/Exfo_laser.m index 1fa6acf..c6ae8bc 100644 --- a/Classes/05_Lab/Exfo_laser.m +++ b/Classes/05_Lab/Exfo_laser.m @@ -1,8 +1,14 @@ -classdef Exfo_laser +classdef Exfo_laser < handle + properties(Access=private) + serialport_number + mainframe_channel + end properties(Access=public) - wavelength - power + cur_state + cur_wavelength + cur_power + safety_mode end methods (Access=public) @@ -11,8 +17,9 @@ classdef Exfo_laser arguments - options.wavelength = 1310; %dbm - options.power = -10; %dbm + options.safety_mode = 1; + options.serialport_number = 'COM8'; + options.mainframe_channel = 1; end % @@ -23,52 +30,330 @@ classdef Exfo_laser end end - end - - function success = set(obj,options) - - - arguments - obj - options.wavelength = obj.wavelength; %dbm - options.power = obj.power; %dbm - end % Connect to the laser - o = serialport("COM8", 9600); - configureTerminator(o, "CR"); % Set the terminator to carriage return (CR) + o = serialport(obj.serialport_number, 9600); + configureTerminator(o, "CR", "CR"); % Set the terminator to carriage return (CR) + flush(o); writeline(o, "*IDN?"); - pause(1); - if o.NumBytesAvailable ~= 0 - disp(['Laser Mainframe: ', readline(o)]); + answ = readline(o); + + if obj.safety_mode + disp(['Yay! We can talk to Instrument: ',char(answ)]); + end + + % Read states the first time + obj.cur_state = obj.getLaserStatus_(o,obj.mainframe_channel); + obj.cur_wavelength = obj.getLaserWavelength_(o,obj.mainframe_channel); + obj.cur_power = obj.getLaserPower_(o,obj.mainframe_channel); + + clear o; + + if obj.safety_mode + warning("Safety_mode ON: Display all information. Ask when switching laser. Turn safety_mode off in class instance or during initialization Exfo_laser(...,'safety_mode',0)"); else - error('No connection to the mainframe'); - clear o; + warning("safety_mode OFF: EVERYTHING IS EXECUTED WITHOUT ASKING :-) BE SHURE WHAT YOU DO"); + end + end + + + function [isEnabled,power,lambda] = getLaserInfo(obj) + + o = obj.connectLaser_(); + + isEnabled = obj.getLaserStatus_(o,obj.mainframe_channel); + + power = obj.getLaserPower_(o,obj.mainframe_channel); + + lambda = obj.getLaserWavelength_(o,obj.mainframe_channel); + + end + + + function success = enableLaser(obj) + success = 0; + o = obj.connectLaser_(); + + if obj.safety_mode + % Prompt the user to enable the laser + choice = questdlg('Do you want to enable the laser?', ... + 'Enable Laser', ... + 'Yes', 'No', 'No'); + else + choice = 'Yes'; end + % Handle the response + switch choice + case 'Yes' + % Enable the laser on channel 1 + success = obj.enableLaser_(o,obj.mainframe_channel); + disp('Laser enabled.'); + case 'No' + % Abort the operation + disp('Operation aborted. Laser remains disabled.'); + end - end - % Function to set the wavelength of the laser - function setLaserWavelength(~,serialObj, channel, wavelength) - command = ['CH', num2str(channel), ':L=', num2str(wavelength)]; - writeline(serialObj, command); - pause(0.5); % Allow time for the wavelength to change + + function success = disableLaser(obj) + success = 0; + o = obj.connectLaser_(); + + if obj.safety_mode + % Prompt the user to enable the laser + choice = questdlg('Do you want to disable the laser?', ... + 'Enable Laser', ... + 'Yes', 'No', 'No'); + else + choice = 'Yes'; + end + + % Handle the response + switch choice + case 'Yes' + % Enable the laser on channel 1 + success = obj.disableLaser_(o,obj.mainframe_channel); + case 'No' + % Abort the operation + disp('Operation aborted. Laser remains enabled.'); + end + + end + + + function success = setPower(obj,desiredPower) + success = 0; + o = obj.connectLaser_(); + + if obj.safety_mode + % Prompt the user to enable the laser + choice = questdlg(['Do you want to set the laser to ', num2str(desiredPower) ,' dBm?'], ... + 'SET POWER', ... + 'Yes', 'No', 'No'); + else + choice = 'Yes'; + end + + % Handle the response + switch choice + case 'Yes' + % Enable the laser on channel 1 + success = obj.setLaserPower_(o,obj.mainframe_channel,desiredPower); + case 'No' + % Abort the operation + disp('Operation aborted. Laser remains at power.'); + end + + end + + + function success = setWavelength(obj,desriedLambda) + success = 0; + o = obj.connectLaser_(); + + if obj.safety_mode + % Prompt the user to enable the laser + choice = questdlg(['Do you want to set the laser wavelength to ', num2str(desriedLambda) ,' nm?'], ... + 'SET WAVELENGTH', ... + 'Yes', 'No', 'No'); + else + choice = 'Yes'; + end + + % Handle the response + switch choice + case 'Yes' + % Enable the laser on channel 1 + success = obj.setLaserWavelength_(o,obj.mainframe_channel,desriedLambda); + case 'No' + % Abort the operation + disp('Operation aborted. Laser remains at wavelength.'); + end + end + + end %public mehtods + methods (Access=private) + + function serialobj = connectLaser_(obj) + % Connect to the laser + serialobj = serialport(obj.serialport_number, 9600); + configureTerminator(serialobj, "CR", "CR"); % Set the terminator to carriage return (CR) + flush(serialobj); + writeline(serialobj, "*IDN?"); + answ = readline(serialobj); + + if obj.safety_mode + disp(['Yay! We can talk to Instrument: ',char(answ)]); + end + end + + % Function to GET the STATE of the laser + function isEnabled = getLaserStatus_(obj,serialObj, channel) + + writeline(serialObj, ['CH', num2str(channel), ':ENABLE?']); % Query current wavelength + + manual_flush = 1; + while manual_flush + response = readline(serialObj); + manual_flush = contains(response, {'OK'}); + end + + isEnabled = 0; + isEnabled = contains(response, {'ENABLED'}); + + isDisabled = 0; + isDisabled = contains(response, {'DISABLED'}); + + if isEnabled && ~isDisabled + obj.cur_state = isEnabled; + elseif ~isEnabled && isDisabled + obj.cur_state = isEnabled; + else + error(['Unknown Laser State ->', char(response)]); + end + + if obj.safety_mode + if isEnabled + disp(['Laser ', num2str(channel) ,' is currently ON -> ', char(response)]); + elseif isDisabled + disp(['Laser ', num2str(channel) ,' is currently OFF ->', char(response)]); + else + error(['Unknown Laser State ->', char(response)]); + end + end + + end + + % Function to GET the wavelength of the laser + function current_wavelen = getLaserWavelength_(obj,serialObj, channel) + writeline(serialObj, ['CH', num2str(channel), ':L?']); % Query current wavelength - current_wavelen = readline(serialObj); - disp(['Current Wavelength: ', current_wavelen]); + + response = readline(serialObj); + current_wavelen = str2double(regexp(response, '\d+\.\d+', 'match', 'once')); + + if obj.safety_mode + disp(['Current Laser ', num2str(channel) ,' Wavelength: ', num2str(current_wavelen),' --> ',char(response)]); + end + + obj.cur_wavelength = current_wavelen; end - % Function to set the laser power - function setLaserPower(~,serialObj, channel, power_dBm) - command = ['CH', num2str(channel), ':P=', num2str(power_dBm)]; - writeline(serialObj, command); - pause(0.2); % Allow time for power to adjust + % Function to GET the laser power + function powerValue = getLaserPower_(obj,serialObj, channel) + writeline(serialObj, ['CH', num2str(channel), ':P?']); % Query current power - current_power = readline(serialObj); - disp(['Current Power: ', current_power, ' dBm']); + + response = readline(serialObj); + + isDisabled = contains(response, 'Disabled'); + + if ~isDisabled + powerValue = str2double(regexp(response, '\d+\.\d+', 'match', 'once')); + else + powerValue = -200; + end + + if obj.safety_mode + disp(['Current Laser ', num2str(channel) ,' Power: ', num2str(powerValue), ' dBm --> ',char(response)]); + end + + obj.cur_power = powerValue; + end + + % Function to enable the laser + function success = enableLaser_(obj,serialObj,channel) + success = 0; + isEnabled = obj.getLaserStatus_(serialObj,obj.mainframe_channel); + + writeline(serialObj, ['CH',num2str(channel),':ENABLE']); + response = readline(serialObj); + isok = contains(response, {'OK'}); + + cnt=0; + while ~isEnabled || cnt>10 + isEnabled = obj.getLaserStatus_(serialObj,obj.mainframe_channel); + pause(0.2); + cnt = cnt+1; + end + + if ~isEnabled + error('Laser not enabled but command was send') + else + success = 1; + end + end + + % Function to enable the laser + function success = disableLaser_(obj,serialObj,channel) + success = 0; + isEnabled = obj.getLaserStatus_(serialObj,obj.mainframe_channel); + + writeline(serialObj, ['CH',num2str(channel),':DISABLE']); + response = readline(serialObj); + isok = contains(response, {'OK'}); + + cnt=0; + while isEnabled || cnt>10 + isEnabled = obj.getLaserStatus_(serialObj,obj.mainframe_channel); + pause(0.2); + cnt = cnt+1; + end + + if isEnabled + error('Laser not enabled but command was send') + else + success = 1; + end + end + + % Function to SET the wavelength of the laser + function success = setLaserWavelength_(~,serialObj, channel, desiredWavelength_nm) + success = 0; + + writeline(serialObj, ['CH',num2str(channel),':NM?']); + modeResponse = readline(serialObj); + isNMMode = contains(modeResponse, ['CH',num2str(channel),':1']); + + if ~isNMMode + warning(['Laser is in GHz Mode, you requested to set Wavelength (nm) -> ',modeResponse]) + return + end + + command = ['CH', num2str(channel), ':L=', num2str(desiredWavelength_nm)]; + writeline(serialObj, command); + + pause(0.2); + + response = readline(serialObj); + success = contains(response, 'OK'); + + end + + % Function to SET the laser power + function success = setLaserPower_(~,serialObj, channel, desiredPower_dBm) + success = 0; + + writeline(serialObj, ['CH',num2str(channel),':MW?']); + modeResponse = readline(serialObj); + isMWMode = contains(modeResponse, ['CH',num2str(channel),':1']); + + if isMWMode + warning('Laser is in MW Mode, you requested to set dBm power') + return + end + + command = ['CH', num2str(channel), ':P=', num2str(desiredPower_dBm)]; + writeline(serialObj, command); + + pause(0.2); + + response = readline(serialObj); + success = contains(response, 'OK'); + end end -end +end \ No newline at end of file diff --git a/Classes/05_Lab/ScopeKeysight.m b/Classes/05_Lab/ScopeKeysight.m index 3635a30..8eee6ba 100644 --- a/Classes/05_Lab/ScopeKeysight.m +++ b/Classes/05_Lab/ScopeKeysight.m @@ -83,7 +83,7 @@ classdef ScopeKeysight end end - debug = 1; + debug = 0; if debug disp(['Connected to Instrument: ',char(v.Vendor),' ',char(v.Model),' SerNo:',char(v.SerialNumber)]); end diff --git a/Classes/05_Lab/Thor_PDFA.m b/Classes/05_Lab/Thor_PDFA.m new file mode 100644 index 0000000..b0912c3 --- /dev/null +++ b/Classes/05_Lab/Thor_PDFA.m @@ -0,0 +1,309 @@ +classdef Thor_PDFA < handle + + properties(Access= public) + serialport_number + safety_mode + statusInfo + + StatusRegisterValue + Interlock + TEC0_temp + TEC1_temp + Temp_stable + Temp_fault + LASER + LOS_Status + PumpLevel + end + + methods (Access=public) + function obj = Thor_PDFA(options) + + arguments + options.serialport_number = 'COM12' + options.safety_mode = 1; + end + + % + fn = fieldnames(options); + for n = 1:numel(fn) + try + obj.(fn{n}) = options.(fn{n}); + end + end + + end + + function o = connectSerial(obj) + + % Connect to the PDFA + o = serialport(obj.serialport_number, 9600); + configureTerminator(o, "CR", "CR"); % Set the terminator to carriage return (CR) + flush(o); + + end + + function getStatus(obj) + + o = obj.connectSerial(); + obj.getStatus_(o); + if obj.safety_mode + obj + end + + end + + function enablePDFA(obj) + + o = obj.connectSerial(); + + if obj.safety_mode + % Prompt the user to enable + choice = questdlg('Do you want to enable the PDFA?', ... + 'TURN ON PUMP', ... + 'PUMP IT!', 'No', 'No'); + else + choice = 'PUMP IT!'; + end + + % Handle the response + switch choice + case 'PUMP IT!' + % Enable the pump + success = obj.enablePump_(o); + disp('PDFA enabled.'); + case 'No' + % Abort the operation + disp('Operation aborted. Laser remains disabled.'); + end + + + end + + function disablePDFA(obj) + + o = obj.connectSerial(); + + if obj.safety_mode + % Prompt the user to disable + choice = questdlg('Do you want to disable the PDFA?', ... + 'TURN OFF PUMP', ... + 'Turn off', 'No', 'No'); + else + choice = 'Turn off'; + end + + % Handle the response + switch choice + case 'Turn off' + % Enable + success = obj.disablePump_(o); + disp('PDFA enabled.'); + case 'No' + % Abort the operation + disp('Operation aborted. Laser remains disabled.'); + end + + + + end + + + + function success = setPumpLevel(obj,desiredPump) + success = 0; + o = obj.connectSerial(); + + obj.setPumpLevel_(o,desiredPump); + + + end + + + function cleanedResponse = cleanResponse(~,responseToClean) + cleanedResponse = regexprep(responseToClean, '\x1B\[[0-9;]*[a-zA-Z]', ''); + end + end + + methods(Access= private) + + function pumpLevel = readPumpLevel(obj,serialObj) + writeline(serialObj, "gloc"); + pause(0.05); + response = read(serialObj,serialObj.NumBytesAvailable,"char"); + cleanedResponse = obj.cleanResponse(response); + currentPercentage = regexp(cleanedResponse, '([0-9]+\.[0-9]+)%', 'tokens', 'once'); + + obj.PumpLevel = str2double(currentPercentage{1}); + + pumpLevel = obj.PumpLevel; + + end + + function getStatus_(obj,o) + + + writeline(o, "stat"); + pause(0.5); + response = read(o,o.NumBytesAvailable,"char"); + cleanedResponse = obj.cleanResponse(response); + + % Parse and convert values + % Convert Status Register Value (hexadecimal to decimal) + statusValueHex = regexp(cleanedResponse, 'Status Register Value = (0x[0-9A-F]+)', 'tokens', 'once'); + obj.StatusRegisterValue = hex2dec(statusValueHex{1}); + + % Convert Interlock status to logical (Closed = true, Open = false) + interlockStatus = regexp(cleanedResponse, 'Interlock\s*:\s*(\w+)', 'tokens', 'once'); + obj.Interlock = strcmp(interlockStatus{1}, 'Closed'); + + % Convert TEC0 and TEC1 temp to logical (Good = true, Fault = false) + tec0Temp = regexp(cleanedResponse, 'TEC0 temp\s*:\s*(\w+)', 'tokens', 'once'); + obj.TEC0_temp = strcmp(tec0Temp{1}, 'Good'); + + tec1Temp = regexp(cleanedResponse, 'TEC1 temp\s*:\s*(\w+)', 'tokens', 'once'); + obj.TEC1_temp = strcmp(tec1Temp{1}, 'Good'); + + % Convert Temp stable status to logical (Stable = true, Unstable = false) + tempStable = regexp(cleanedResponse, 'Temp stable\s*:\s*(\w+)', 'tokens', 'once'); + obj.Temp_stable = strcmp(tempStable{1}, 'Stable'); + + % Convert Temp fault status to logical (No Fault = false, Fault = true) + tempFault = regexp(cleanedResponse, 'Temp fault\s*:\s*(\w+)', 'tokens', 'once'); + obj.Temp_fault = strcmp(tempFault{1}, 'Fault'); + + % Convert LASER status to logical (OFF = false, ON = true) + laserStatus = regexp(cleanedResponse, 'LASER\s*:\s*(\w+)', 'tokens', 'once'); + obj.LASER = strcmp(laserStatus{1}, 'ON'); + + % Convert LOS Status to logical (Good = true, Faulty = false) + losStatus = regexp(cleanedResponse, 'LOS Status\s*:\s*(\w+)', 'tokens', 'once'); + obj.LOS_Status = strcmp(losStatus{1}, 'Good'); + + + writeline(o, "gloc"); + pause(0.5); + response = read(o,o.NumBytesAvailable,"char"); + cleanedResponse = obj.cleanResponse(response); + + % Use a regular expression to extract the operating current value in percentage + currentPercentage = regexp(cleanedResponse, '([0-9]+\.[0-9]+)%', 'tokens', 'once'); + + % Convert the extracted percentage to a numeric value + obj.PumpLevel = str2double(currentPercentage{1}); + + end + + function success = enablePump_(obj,o) + success = 0; + + curPump = obj.readPumpLevel(o); + + if curPump ~= 0 + cnt = 0; + pumpSetToZero = 0; + while ~pumpSetToZero + pumpZero = 0; + pumpSetToZero = obj.setPumpLevel_(o,pumpZero); + cnt = cnt+1; + end + end + + % set pump on + writeline(o, 'le'); + pause(0.5); + response = read(o,o.NumBytesAvailable,"char"); + cleanedResponse = obj.cleanResponse(response); + + isEnabled = contains(cleanedResponse, {'ENABLE LASER'}); + + pause(5); + + obj.getStatus_(o); + + if isEnabled && obj.LASER + success = 1; + else + error('Could not enable the pump....'); + end + + end + + function success = disablePump_(obj,o) + success = 0; + + curPump = obj.readPumpLevel(o); + + if curPump ~= 0 + cnt = 0; + pumpSetToZero = 0; + while ~pumpSetToZero + pumpZero = 0; + pumpSetToZero = obj.setPumpLevel_(o,pumpZero); + cnt = cnt+1; + end + end + + % set pump on + writeline(o, 'ld'); + pause(0.5); + response = read(o,o.NumBytesAvailable,"char"); + cleanedResponse = obj.cleanResponse(response); + + isDisabled = contains(cleanedResponse, {'DISABLE LASER'}); + + pause(5); + + obj.getStatus_(o); + + if isDisabled && ~obj.LASER + success = 1; + else + error('Could not disable the pump....'); + end + + end + + function success = setPumpLevel_(obj,o,desiredPump) + + success = 0; + + curPump = obj.readPumpLevel(o); + + while abs(curPump-desiredPump) > 0 + + % difference + diff_pump = curPump-desiredPump; + + % set new pump + increment_pump = -1 * sign(diff_pump) ; + + % set new incr. pump + writeline(o, ['sloc ',num2str(curPump+increment_pump)]); + + if increment_pump < 0 + pause(0.1); + else + pause(0.3); + end + + + response = read(o,o.NumBytesAvailable,"char"); + cleanedResponse = obj.cleanResponse(response); + curPump = str2double(regexp(cleanedResponse, '([0-9]+\.[0-9]+)%', 'tokens', 'once')); + + end + + curPump = obj.readPumpLevel(o); + + if obj.PumpLevel == desiredPump + success = 1; + else + warning('Pump nicht richtig?') + end + + end + + end + +end \ No newline at end of file diff --git a/Functions/calcWavelengthPlan.m b/Functions/calcWavelengthPlan.m new file mode 100644 index 0000000..dea849f --- /dev/null +++ b/Functions/calcWavelengthPlan.m @@ -0,0 +1,24 @@ +function channelplan_nm = calcWavelengthPlan(N, df_hz, center_nm) + +vec = [N:-1:1]-(N/2+0.5); + +channelplan_hz = nm2hz(center_nm) + (vec * df_hz) ; + +channelplan_nm = hz2nm(channelplan_hz); + +% dfft = 1.367053998632946e+07; %hz +% a = diff(channelplan_hz)./2./dfft; + +end + +function hz = nm2hz(nm) + wavelen_in_m = nm.* 1e-9; + hz = (299792458 ./ wavelen_in_m); %frequency in Terahertz +end + +function nm = hz2nm(hz) + m = (299792458 ./ hz); %wavelength in meter + nm = m .* 10^9; +end + + diff --git a/Functions/helper_functions_community/Violinplot-Matlab-master/LICENSE b/Functions/helper_functions_community/Violinplot-Matlab-master/LICENSE new file mode 100644 index 0000000..9eea65f --- /dev/null +++ b/Functions/helper_functions_community/Violinplot-Matlab-master/LICENSE @@ -0,0 +1,11 @@ +Copyright 2016 Bastian Bechtold + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Functions/helper_functions_community/Violinplot-Matlab-master/README.md b/Functions/helper_functions_community/Violinplot-Matlab-master/README.md new file mode 100644 index 0000000..4153322 --- /dev/null +++ b/Functions/helper_functions_community/Violinplot-Matlab-master/README.md @@ -0,0 +1,60 @@ +# Violin Plots for Matlab + +A violin plot is an easy to read substitute for a box plot that +replaces the box shape with a kernel density estimate of the data, and +optionally overlays the data points itself. The original boxplot shape +is still included as a grey box/line in the center of the violin. + +Violin plots are a superset of box plots, and give a much richer +understanding of the data distribution, while not taking more space. +You will be able to instantly spot too-sparse data, or multi-modal +distributions, which could go unnoticed in boxplots. + +`violinplot` is meant as a direct substitute for `boxplot` (excluding +named arguments). Additional constructor parameters include the width +of the plot, the bandwidth of the kernel density estimation, and the +X-axis position of the violin plot. + +For more information about violin plots, read "[Violin plots: a box +plot-density trace synergism](http://www.stat.cmu.edu/~rnugent/PCMI2016/papers/ViolinPlots.pdf)" +by J. L. Hintze and R. D. Nelson in The American Statistician, vol. +52, no. 2, pp. 181-184, 1998 (DOI: 10.2307/2685478). + +For a simple call: + +```matlab +load carbig MPG Origin +Origin = cellstr(Origin); +figure +vs = violinplot(MPG, Origin); +ylabel('Fuel Economy in MPG'); +xlim([0.5, 7.5]); +``` + +![example image](./example.png) + + +You can also play around with the different options, and tune your violin plots to your liking. + +```matlab +grouporder={'England','Sweden','Japan','Italy','Germany','France','USA'}; +vs = Violin({MPG(strcmp(Origin, grouporder{pos}))},... + position,... + 'HalfViolin','right',...% left, full + 'QuartileStyle','shadow',... % boxplot, none + 'DataStyle', 'histogram',... % scatter, none + 'ShowNotches', false,... + 'ShowMean', false,... + 'ShowMedian', true,... + 'ViolinColor', color); +``` +![example image2](example2.png) + +## Citation + +[![DOI](https://zenodo.org/badge/60771923.svg)](https://zenodo.org/badge/latestdoi/60771923) + +If you want to cite this repository, use + +> Bechtold, Bastian, 2016. Violin Plots for Matlab, Github Project +> https://github.com/bastibe/Violinplot-Matlab, DOI: 10.5281/zenodo.4559847 diff --git a/Functions/helper_functions_community/Violinplot-Matlab-master/Violin.m b/Functions/helper_functions_community/Violinplot-Matlab-master/Violin.m new file mode 100644 index 0000000..7f48f81 --- /dev/null +++ b/Functions/helper_functions_community/Violinplot-Matlab-master/Violin.m @@ -0,0 +1,713 @@ +classdef Violin < handle + % Violin creates violin plots for some data + % A violin plot is an easy to read substitute for a box plot + % that replaces the box shape with a kernel density estimate of + % the data, and optionally overlays the data points itself. + % It is also possible to provide two sets of data which are supposed + % to be compared by plotting each column of the two datasets together + % on each side of the violin. + % + % Additional constructor parameters include the width of the + % plot, the bandwidth of the kernel density estimation, the + % X-axis position of the violin plot, and the categories. + % + % Use violinplot for a + % boxplot-like wrapper for + % interactive plotting. + % + % See for more information on Violin Plots: + % J. L. Hintze and R. D. Nelson, "Violin plots: a box + % plot-density trace synergism," The American Statistician, vol. + % 52, no. 2, pp. 181-184, 1998. + % + % Violin Properties: + % ViolinColor - Fill color of the violin area and data points. + % Can be either a matrix nx3 or an array of up to two + % cells containing nx3 matrices. + % Defaults to the next default color cycle. + % ViolinAlpha - Transparency of the violin area and data points. + % Can be either a single scalar value or an array of + % up to two cells containing scalar values. + % Defaults to 0.3. + % EdgeColor - Color of the violin area outline. + % Defaults to [0.5 0.5 0.5] + % BoxColor - Color of the box, whiskers, and the outlines of + % the median point and the notch indicators. + % Defaults to [0.5 0.5 0.5] + % MedianColor - Fill color of the median and notch indicators. + % Defaults to [1 1 1] + % ShowData - Whether to show data points. + % Defaults to true + % ShowNotches - Whether to show notch indicators. + % Defaults to false + % ShowMean - Whether to show mean indicator. + % Defaults to false + % ShowBox - Whether to show the box. + % Defaults to true + % ShowMedian - Whether to show the median indicator. + % Defaults to true + % ShowWhiskers - Whether to show the whiskers + % Defaults to true + % HalfViolin - Whether to do a half violin(left, right side) or + % full. Defaults to full. + % QuartileStyle - Option on how to display quartiles, with a + % boxplot, shadow or none. Defaults to boxplot. + % DataStyle - Defines the style to show the data points. Opts: + % 'scatter', 'histogram' or 'none'. Default is 'scatter'. + % + % + % Violin Children: + % ScatterPlot - scatter plot of the data points + % ScatterPlot2 - scatter second plot of the data points + % ViolinPlot - fill plot of the kernel density estimate + % ViolinPlot2 - fill second plot of the kernel density estimate + % BoxPlot - fill plot of the box between the quartiles + % WhiskerPlot - line plot between the whisker ends + % MedianPlot - scatter plot of the median (one point) + % NotchPlots - scatter plots for the notch indicators + % MeanPlot - line plot at mean value + + + % Copyright (c) 2016, Bastian Bechtold + % This code is released under the terms of the BSD 3-clause license + + properties (Access=public) + ScatterPlot % scatter plot of the data points + ScatterPlot2 % comparison scatter plot of the data points + ViolinPlot % fill plot of the kernel density estimate + ViolinPlot2 % comparison fill plot of the kernel density estimate + BoxPlot % fill plot of the box between the quartiles + WhiskerPlot % line plot between the whisker ends + MedianPlot % scatter plot of the median (one point) + NotchPlots % scatter plots for the notch indicators + MeanPlot % line plot of the mean (horizontal line) + HistogramPlot % histogram of the data + ViolinPlotQ % fill plot of the Quartiles as shadow + end + + properties (Dependent=true) + ViolinColor % fill color of the violin area and data points + ViolinAlpha % transparency of the violin area and data points + MarkerSize % marker size for the data dots + MedianMarkerSize % marker size for the median dot + LineWidth % linewidth of the median plot + EdgeColor % color of the violin area outline + BoxColor % color of box, whiskers, and median/notch edges + BoxWidth % width of box between the quartiles in axis space (default 10% of Violin plot width, 0.03) + MedianColor % fill color of median and notches + ShowData % whether to show data points + ShowNotches % whether to show notch indicators + ShowMean % whether to show mean indicator + ShowBox % whether to show the box + ShowMedian % whether to show the median line + ShowWhiskers % whether to show the whiskers + HalfViolin % whether to do a half violin(left, right side) or full + end + + methods + function obj = Violin(data, pos, varargin) + %Violin plots a violin plot of some data at pos + % VIOLIN(DATA, POS) plots a violin at x-position POS for + % a vector of DATA points. + % + % VIOLIN(..., 'PARAM1', val1, 'PARAM2', val2, ...) + % specifies optional name/value pairs: + % 'Width' Width of the violin in axis space. + % Defaults to 0.3 + % 'Bandwidth' Bandwidth of the kernel density + % estimate. Should be between 10% and + % 40% of the data range. + % 'ViolinColor' Fill color of the violin area + % and data points.Can be either a matrix + % nx3 or an array of up to two cells + % containing nx3 matrices. + % 'ViolinAlpha' Transparency of the violin area and data + % points. Can be either a single scalar + % value or an array of up to two cells + % containing scalar values. Defaults to 0.3. + % 'MarkerSize' Size of the data points, if shown. + % Defaults to 24 + % 'MedianMarkerSize' Size of the median indicator, if shown. + % Defaults to 36 + % 'EdgeColor' Color of the violin area outline. + % Defaults to [0.5 0.5 0.5] + % 'BoxColor' Color of the box, whiskers, and the + % outlines of the median point and the + % notch indicators. Defaults to + % [0.5 0.5 0.5] + % 'MedianColor' Fill color of the median and notch + % indicators. Defaults to [1 1 1] + % 'ShowData' Whether to show data points. + % Defaults to true + % 'ShowNotches' Whether to show notch indicators. + % Defaults to false + % 'ShowMean' Whether to show mean indicator. + % Defaults to false + % 'ShowBox' Whether to show the box + % Defaults to true + % 'ShowMedian' Whether to show the median line + % Defaults to true + % 'ShowWhiskers' Whether to show the whiskers + % Defaults to true + % 'HalfViolin' Whether to do a half violin(left, right side) or + % full. Defaults to full. + % 'QuartileStyle' Option on how to display quartiles, with a + % boxplot or as a shadow. Defaults to boxplot. + % 'DataStyle' Defines the style to show the data points. Opts: + % 'scatter', 'histogram' or 'none'. Default is 'Scatter'. + + st = dbstack; % get the calling function for reporting errors + namefun = st.name; + args = obj.checkInputs(data, pos, varargin{:}); + + if length(data)==1 + data2 = []; + data = data{1}; + + else + data2 = data{2}; + data = data{1}; + end + + if isempty(args.ViolinColor) + Release= strsplit(version('-release'), {'a','b'}); %Check release + if str2num(Release{1})> 2019 || strcmp(version('-release'), '2019b') + C = colororder; + else + C = lines; + end + + if pos > length(C) + C = lines; + end + args.ViolinColor = {repmat(C,ceil(size(data,2)/length(C)),1)}; + end + + data = data(not(isnan(data))); + data2 = data2(not(isnan(data2))); + if numel(data) == 1 + obj.MedianPlot = scatter(pos, data, 'filled'); + obj.MedianColor = args.MedianColor; + obj.MedianPlot.MarkerEdgeColor = args.EdgeColor; + return + end + + hold('on'); + + + %% Calculate kernel density estimation for the violin + [density, value, width] = obj.calcKernelDensity(data, args.Bandwidth, args.Width); + + % also calculate the kernel density of the comparison data if + % provided + if ~isempty(data2) + [densityC, valueC, widthC] = obj.calcKernelDensity(data2, args.Bandwidth, args.Width); + end + + %% Plot the data points within the violin area + if length(density) > 1 + [~, unique_idx] = unique(value); + jitterstrength = interp1(value(unique_idx), density(unique_idx)*width, data, 'linear','extrap'); + else % all data is identical: + jitterstrength = density*width; + end + if isempty(data2) % if no comparison data + jitter = 2*(rand(size(data))-0.5); % both sides + else + jitter = rand(size(data)); % only right side + end + switch args.HalfViolin % this is more modular + case 'left' + jitter = -1*(rand(size(data))); %left + case 'right' + jitter = 1*(rand(size(data))); %right + case 'full' + jitter = 2*(rand(size(data))-0.5); + end + % Make scatter plot + switch args.DataStyle + case 'scatter' + if ~isempty(data2) + jitter = 1*(rand(size(data))); %right + obj.ScatterPlot = ... + scatter(pos + jitter.*jitterstrength, data, args.MarkerSize, 'filled'); + % plot the data points within the violin area + if length(densityC) > 1 + jitterstrength = interp1(valueC, densityC*widthC, data2); + else % all data is identical: + jitterstrength = densityC*widthC; + end + jitter = -1*rand(size(data2));% left + obj.ScatterPlot2 = ... + scatter(pos + jitter.*jitterstrength, data2, args.MarkerSize, 'filled'); + else + obj.ScatterPlot = ... + scatter(pos + jitter.*jitterstrength, data, args.MarkerSize, 'filled'); + + end + case 'histogram' + [counts,edges] = histcounts(data, size(unique(data),1)); + switch args.HalfViolin + case 'right' + obj.HistogramPlot= plot([pos-((counts')/max(counts))*max(jitterstrength)*2, pos*ones(size(counts,2),1)]',... + [edges(1:end-1)+max(diff(edges))/2; edges(1:end-1)+max(diff(edges))/2],'-','LineWidth',1, 'Color', 'k'); + case 'left' + obj.HistogramPlot= plot([pos*ones(size(counts,2),1), pos+((counts')/max(counts))*max(jitterstrength)*2]',... + [edges(1:end-1)+max(diff(edges))/2; edges(1:end-1)+max(diff(edges))/2],'-','LineWidth',1, 'Color', 'k'); + otherwise + fprintf([namefun, ' No histogram/bar plot option available for full violins, as it would look overcrowded.\n']) + end + case 'none' + end + + %% Plot the violin + halfViol= ones(1, size(density,2)); + if isempty(data2) % if no comparison data + switch args.HalfViolin + case 'right' + obj.ViolinPlot = ... % plot color will be overwritten later + fill([pos+density*width halfViol*pos], ... + [value value(end:-1:1)], [1 1 1],'LineWidth',1); + case 'left' + obj.ViolinPlot = ... % plot color will be overwritten later + fill([halfViol*pos pos-density(end:-1:1)*width], ... + [value value(end:-1:1)], [1 1 1],'LineWidth',1); + case 'full' + obj.ViolinPlot = ... % plot color will be overwritten later + fill([pos+density*width pos-density(end:-1:1)*width], ... + [value value(end:-1:1)], [1 1 1],'LineWidth',1); + end + else + % plot right half of the violin + obj.ViolinPlot = ... + fill([pos+density*width pos-density(1)*width], ... + [value value(1)], [1 1 1],'LineWidth',1); + % plot left half of the violin + obj.ViolinPlot2 = ... + fill([pos-densityC(end)*widthC pos-densityC(end:-1:1)*widthC], ... + [valueC(end) valueC(end:-1:1)], [1 1 1],'LineWidth',1); + end + + %% Plot the quartiles within the violin + quartiles = quantile(data, [0.25, 0.5, 0.75]); + flat= [halfViol*pos halfViol*pos]; + switch args.QuartileStyle + case 'shadow' + switch args.HalfViolin + case 'right' + w = [pos+density*width halfViol*pos]; + h= [value value(end:-1:1)]; + case 'left' + w = [halfViol*pos pos-density(end:-1:1)*width]; + h= [value value(end:-1:1)]; + case 'full' + w = [pos+density*width pos-density(end:-1:1)*width]; + h= [value value(end:-1:1)]; + end + w(hquartiles(3))=flat((h>quartiles(3))); + obj.ViolinPlotQ = ... % plot color will be overwritten later + fill(w, ... + h, [1 1 1]); + case 'boxplot' + obj.BoxPlot = ... % plot color will be overwritten later + fill(pos+[-1,1,1,-1]*args.BoxWidth, ... + [quartiles(1) quartiles(1) quartiles(3) quartiles(3)], ... + [1 1 1]); + case 'none' + end + + %% Plot the data mean + meanValue = mean(data); + if length(density) > 1 + [~, unique_idx] = unique(value); + meanDensityWidth = interp1(value(unique_idx), density(unique_idx), meanValue, 'linear','extrap')*width; + else % all data is identical: + meanDensityWidth = density*width; + end + if meanDensityWidth lowhisker))); + hiwhisker = quartiles(3) + 1.5*IQR; + hiwhisker = min(hiwhisker, max(data(data < hiwhisker))); + if ~isempty(lowhisker) && ~isempty(hiwhisker) + obj.WhiskerPlot = plot([pos pos], [lowhisker hiwhisker]); + end + + % Median + obj.MedianPlot = scatter(pos, quartiles(2), args.MedianMarkerSize, [1 1 1], 'filled'); + + % Notches + obj.NotchPlots = ... + scatter(pos, quartiles(2)-1.57*IQR/sqrt(length(data)), ... + [], [1 1 1], 'filled', '^'); + obj.NotchPlots(2) = ... + scatter(pos, quartiles(2)+1.57*IQR/sqrt(length(data)), ... + [], [1 1 1], 'filled', 'v'); + + %% Set graphical preferences + obj.EdgeColor = args.EdgeColor; + obj.MedianPlot.LineWidth = args.LineWidth; + obj.BoxColor = args.BoxColor; + obj.BoxWidth = args.BoxWidth; + obj.MedianColor = args.MedianColor; + obj.ShowData = args.ShowData; + obj.ShowNotches = args.ShowNotches; + obj.ShowMean = args.ShowMean; + obj.ShowBox = args.ShowBox; + obj.ShowMedian = args.ShowMedian; + obj.ShowWhiskers = args.ShowWhiskers; + + if not(isempty(args.ViolinColor)) + if size(args.ViolinColor{1},1) > 1 + ViolinColor{1} = args.ViolinColor{1}(pos,:); + else + ViolinColor{1} = args.ViolinColor{1}; + end + if length(args.ViolinColor)==2 + if size(args.ViolinColor{2},1) > 1 + ViolinColor{2} = args.ViolinColor{2}(pos,:); + else + ViolinColor{2} = args.ViolinColor{2}; + end + else + ViolinColor{2} = ViolinColor{1}; + end + else + % defaults + if args.scpltBool + ViolinColor{1} = obj.ScatterPlot.CData; + else + ViolinColor{1} = [0 0 0]; + end + ViolinColor{2} = [0 0 0]; + end + obj.ViolinColor = ViolinColor; + + + if not(isempty(args.ViolinAlpha)) + if length(args.ViolinAlpha{1})>1 + error('Only scalar values are accepted for the alpha color channel'); + else + ViolinAlpha{1} = args.ViolinAlpha{1}; + end + if length(args.ViolinAlpha)==2 + if length(args.ViolinAlpha{2})>1 + error('Only scalar values are accepted for the alpha color channel'); + else + ViolinAlpha{2} = args.ViolinAlpha{2}; + end + else + ViolinAlpha{2} = ViolinAlpha{1}/2; % default unless specified + end + else + % default + ViolinAlpha = {1,1}; + end + obj.ViolinAlpha = ViolinAlpha; + + + end + + %% SET METHODS + function set.EdgeColor(obj, color) + if ~isempty(obj.ViolinPlot) + obj.ViolinPlot.EdgeColor = color; + obj.ViolinPlotQ.EdgeColor = color; + if ~isempty(obj.ViolinPlot2) + obj.ViolinPlot2.EdgeColor = color; + end + end + end + + function color = get.EdgeColor(obj) + if ~isempty(obj.ViolinPlot) + color = obj.ViolinPlot.EdgeColor; + end + end + + + function set.MedianColor(obj, color) + obj.MedianPlot.MarkerFaceColor = color; + if ~isempty(obj.NotchPlots) + obj.NotchPlots(1).MarkerFaceColor = color; + obj.NotchPlots(2).MarkerFaceColor = color; + end + end + + function color = get.MedianColor(obj) + color = obj.MedianPlot.MarkerFaceColor; + end + + + function set.BoxColor(obj, color) + if ~isempty(obj.BoxPlot) + obj.BoxPlot.FaceColor = color; + obj.BoxPlot.EdgeColor = color; + obj.WhiskerPlot.Color = color; + obj.MedianPlot.MarkerEdgeColor = color; + obj.NotchPlots(1).MarkerFaceColor = color; + obj.NotchPlots(2).MarkerFaceColor = color; + elseif ~isempty(obj.ViolinPlotQ) + obj.WhiskerPlot.Color = color; + obj.MedianPlot.MarkerEdgeColor = color; + obj.NotchPlots(1).MarkerFaceColor = color; + obj.NotchPlots(2).MarkerFaceColor = color; + end + end + + function color = get.BoxColor(obj) + if ~isempty(obj.BoxPlot) + color = obj.BoxPlot.FaceColor; + end + end + + + function set.BoxWidth(obj,width) + if ~isempty(obj.BoxPlot) + pos=mean(obj.BoxPlot.XData); + obj.BoxPlot.XData=pos+[-1,1,1,-1]*width; + end + end + + function width = get.BoxWidth(obj) + width=max(obj.BoxPlot.XData)-min(obj.BoxPlot.XData); + end + + + function set.ViolinColor(obj, color) + obj.ViolinPlot.FaceColor = color{1}; + obj.ScatterPlot.MarkerFaceColor = color{1}; + obj.MeanPlot.Color = color{1}; + if ~isempty(obj.ViolinPlot2) + obj.ViolinPlot2.FaceColor = color{2}; + obj.ScatterPlot2.MarkerFaceColor = color{2}; + end + if ~isempty(obj.ViolinPlotQ) + obj.ViolinPlotQ.FaceColor = color{1}; + end + for idx = 1: size(obj.HistogramPlot,1) + obj.HistogramPlot(idx).Color = color{1}; + end + end + + function color = get.ViolinColor(obj) + color{1} = obj.ViolinPlot.FaceColor; + if ~isempty(obj.ViolinPlot2) + color{2} = obj.ViolinPlot2.FaceColor; + end + end + + + function set.ViolinAlpha(obj, alpha) + obj.ViolinPlotQ.FaceAlpha = .65; + obj.ViolinPlot.FaceAlpha = alpha{1}; + obj.ScatterPlot.MarkerFaceAlpha = 1; + if ~isempty(obj.ViolinPlot2) + obj.ViolinPlot2.FaceAlpha = alpha{2}; + obj.ScatterPlot2.MarkerFaceAlpha = 1; + end + end + + function alpha = get.ViolinAlpha(obj) + alpha{1} = obj.ViolinPlot.FaceAlpha; + if ~isempty(obj.ViolinPlot2) + alpha{2} = obj.ViolinPlot2.FaceAlpha; + end + end + + + function set.ShowData(obj, yesno) + if yesno + obj.ScatterPlot.Visible = 'on'; + for idx = 1: size(obj.HistogramPlot,1) + obj.HistogramPlot(idx).Visible = 'on'; + end + else + obj.ScatterPlot.Visible = 'off'; + for idx = 1: size(obj.HistogramPlot,1) + obj.HistogramPlot(idx).Visible = 'off'; + end + end + if ~isempty(obj.ScatterPlot2) + obj.ScatterPlot2.Visible = obj.ScatterPlot.Visible; + end + + end + + function yesno = get.ShowData(obj) + if ~isempty(obj.ScatterPlot) + yesno = strcmp(obj.ScatterPlot.Visible, 'on'); + end + end + + + function set.ShowNotches(obj, yesno) + if ~isempty(obj.NotchPlots) + if yesno + obj.NotchPlots(1).Visible = 'on'; + obj.NotchPlots(2).Visible = 'on'; + else + obj.NotchPlots(1).Visible = 'off'; + obj.NotchPlots(2).Visible = 'off'; + end + end + end + + function yesno = get.ShowNotches(obj) + if ~isempty(obj.NotchPlots) + yesno = strcmp(obj.NotchPlots(1).Visible, 'on'); + end + end + + + function set.ShowMean(obj, yesno) + if ~isempty(obj.MeanPlot) + if yesno + obj.MeanPlot.Visible = 'on'; + else + obj.MeanPlot.Visible = 'off'; + end + end + end + + function yesno = get.ShowMean(obj) + if ~isempty(obj.BoxPlot) + yesno = strcmp(obj.BoxPlot.Visible, 'on'); + end + end + + + function set.ShowBox(obj, yesno) + if ~isempty(obj.BoxPlot) + if yesno + obj.BoxPlot.Visible = 'on'; + else + obj.BoxPlot.Visible = 'off'; + end + end + end + + function yesno = get.ShowBox(obj) + if ~isempty(obj.BoxPlot) + yesno = strcmp(obj.BoxPlot.Visible, 'on'); + end + end + + + function set.ShowMedian(obj, yesno) + if ~isempty(obj.MedianPlot) + if yesno + obj.MedianPlot.Visible = 'on'; + else + obj.MedianPlot.Visible = 'off'; + end + end + end + + function yesno = get.ShowMedian(obj) + if ~isempty(obj.MedianPlot) + yesno = strcmp(obj.MedianPlot.Visible, 'on'); + end + end + + + function set.ShowWhiskers(obj, yesno) + if ~isempty(obj.WhiskerPlot) + if yesno + obj.WhiskerPlot.Visible = 'on'; + else + obj.WhiskerPlot.Visible = 'off'; + end + end + end + + function yesno = get.ShowWhiskers(obj) + if ~isempty(obj.WhiskerPlot) + yesno = strcmp(obj.WhiskerPlot.Visible, 'on'); + end + end + + end + + methods (Access=private) + function results = checkInputs(~, data, pos, varargin) + isscalarnumber = @(x) (isnumeric(x) & isscalar(x)); + p = inputParser(); + p.addRequired('Data', @(x)isnumeric(vertcat(x{:}))); + p.addRequired('Pos', isscalarnumber); + p.addParameter('Width', 0.3, isscalarnumber); + p.addParameter('Bandwidth', [], isscalarnumber); + iscolor = @(x) (isnumeric(x) & size(x,2) == 3); + p.addParameter('ViolinColor', [], @(x)iscolor(vertcat(x{:}))); + p.addParameter('MarkerSize', 24, @isnumeric); + p.addParameter('MedianMarkerSize', 36, @isnumeric); + p.addParameter('LineWidth', 0.75, @isnumeric); + p.addParameter('BoxColor', [0.5 0.5 0.5], iscolor); + p.addParameter('BoxWidth', 0.01, isscalarnumber); + p.addParameter('EdgeColor', [0.5 0.5 0.5], iscolor); + p.addParameter('MedianColor', [1 1 1], iscolor); + p.addParameter('ViolinAlpha', {0.3,0.15}, @(x)isnumeric(vertcat(x{:}))); + isscalarlogical = @(x) (islogical(x) & isscalar(x)); + p.addParameter('ShowData', true, isscalarlogical); + p.addParameter('ShowNotches', false, isscalarlogical); + p.addParameter('ShowMean', false, isscalarlogical); + p.addParameter('ShowBox', true, isscalarlogical); + p.addParameter('ShowMedian', true, isscalarlogical); + p.addParameter('ShowWhiskers', true, isscalarlogical); + validSides={'full', 'right', 'left'}; + checkSide = @(x) any(validatestring(x, validSides)); + p.addParameter('HalfViolin', 'full', checkSide); + validQuartileStyles={'boxplot', 'shadow', 'none'}; + checkQuartile = @(x)any(validatestring(x, validQuartileStyles)); + p.addParameter('QuartileStyle', 'boxplot', checkQuartile); + validDataStyles = {'scatter', 'histogram', 'none'}; + checkStyle = @(x)any(validatestring(x, validDataStyles)); + p.addParameter('DataStyle', 'scatter', checkStyle); + + p.parse(data, pos, varargin{:}); + results = p.Results; + end + end + + methods (Static) + function [density, value, width] = calcKernelDensity(data, bandwidth, width) + if isempty(data) + error('Empty input data'); + end + [density, value] = ksdensity(data, 'bandwidth', bandwidth); + density = density(value >= min(data) & value <= max(data)); + value = value(value >= min(data) & value <= max(data)); + value(1) = min(data); + value(end) = max(data); + value = [value(1)*(1-1E-5), value, value(end)*(1+1E-5)]; + density = [0, density, 0]; + + % all data is identical + if min(data) == max(data) + density = 1; + value= mean(value); + end + + width = width/max(density); + end + end +end + diff --git a/Functions/helper_functions_community/Violinplot-Matlab-master/example.png b/Functions/helper_functions_community/Violinplot-Matlab-master/example.png new file mode 100644 index 0000000000000000000000000000000000000000..deaddef08d92b89deede5f1c345a659cdbf00518 GIT binary patch literal 43429 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 z z7WFbcrk{koRuIQM^c2QVcOb?>jTosOlv(d7%pzktg~93C25ift^L!=+*ym}`GzGQF z697MDW|vX+_ho~49>#f^WU1UY_;z?vdgBufLwGQs<6@m>pQ~e^BB^T%kz=1`WV4+^ zehK@bwVGW0yNY_R$LO+J?D$jtOpjP}YZ$&MmIdL9%Z-+zun=GbT(&JQezw7dC+bIx zAoQk~KY||U>ow!@LDdGMailu-pL(w;q8r}}4Sc|hkr|`&6)AD%-W;t+>ra^n#CNJm zvt{U`=r1>4%1`?SL=95l!Cti;g;azjaaQDukx6_5+{yCmipom#=!)Y7AC^GJT8mw! z7J-KBeTol!QXsK)X#Dw)cTAoJf?BN~>L{b36OZqsn(ssLype*|$*ddqvY6@)A44Rr z4ATXY!V!%IInP?!X4^yiC@tg%OHsBP=bd-|`~d>fbOEP~s$C#1QrP?igRg4hBt<`s z*kdeK`A8^sG?1rCP)*OAlj{S~%;NRUQ1U*N>BoV{?EMc>V`H1b?0_NPH~9?r$5qF( zHoduiv!*TAU++o0yJkCH!#Dp_11J#3L}2Tv=gcL3xPN1qJO54;CBpax1s^r#FuzQL zh|q%9Z8dla=W99UB~OaUT<2?r=fXC#Kvx~eZPh55*JNvZmJSA^rvfaBv z)U;ovi$J<0mpBq1+25x0cj7^1VchPQRgtfdL1!ns`-#_Kw%D2xAt+C%dqVXfPAB2B z^P3f~GkZ|U(LDCqI8Rl~z={$?2r7_`vRsXI(k}-g8EtZcCGp~zFo>8IA=gXZRmF)f zd&qUk123ao7Et^DL3$vUSu=Z7Op{d<9dbg}d+2qj&jNw&U%T(@cHN{skU>oT#OEQ? zQ)^_I|7|Hf{3T1zw)8?pdCD0mWeVKl9=nrox!j-KpE$?B;4+`$mx>-)a&i;kLW(6W z=ZiMBW?+Ht?(RaaKiIfova_*0j)7RJ zho7)b-U1@t%9)TU7^mIw$5nbbx|@%Qj&KpC~1Nr9-B(jE2^sJe`xT>eICJfZ)_Qs~otL?BVn< z0X!ayp{70>W*baH8s+XD$Qy=48z2;$G^YJ0CT*ajl(NS?MjjcewJukyd5^%Zpp^lN z82y4@2`irO-Tl}Y_QZ&kh;1ct@VjDGyLc%Z$Z>Zk92Z;pdFR`1WD1-3$qp62x8W@^ z(bZN-=^Y||UFf}9({6ann2w=FuTl$>`k6Xp!6Mb9Y3um}u8!wvtjZ;yVwDuj)b`t160gbT7-ltt6Z$m-M4oH3xz<$0Nz$l)OD+Dy zdzKSr?JIjsds}#{4`YLC^5h{(@)3GbWZKANm^=7?{_))}mPpLsz``yGcI7BX`uDSg zvvY9(oOt*2gpf|}n|g&6Il(}yaA$u?a@3^a3;yjBZ~RDW;Y#>!w{wB8IaQod_ebs# zihplLFZrm|?SciJ3<=a-b@V*LKDFl8)lyYUlacGk7jt%7^T}wDLG@=cOh57+)#b$# ztMiLc>g$6}hH0-fBBVA2gt|<}qNqqpiZ=}oG&D!XXT@fpX}4XryvID*g~1a;iK_Cy zVW4H018&v0SmE**Nlsr=~|NEt#X zub$_r2YB8a;lq?#Mw{^ZzRpb2Lj$x`rVO#ZX`vA+IYJk9((gxADM|*b`wPO}N(P~D z?o%1~#WG#0HVhY`GjRX$)~D6kY`^mcbQ0*I2=Q*m(b;`em%@XU7yI4qxpmIpZE(F| zd8kCYOye(#Ubx2k;e<|-M22h8_ZuI251vG}kp<#w_Fdyja)4_Ps5lPN8 zrW6}7Oby|Iv&b}QrziEst-1PRMN%hYNvW>7u9J(dsD)i}@t=%*rq^21HiGZI{33E3 zU31JD-I`2H(PJJZD}ggs%8hI5>9MC(IrNR^!?b;yHQye;QtnI!l?Onz2(TM2LDx0C0)`BSz3WLWOrtK%O9^vbd;@s|0mw@$L%Vtb0SdP3>kE(5I(|LchIh<)Tls3M!6bM?^)%vyO zK*|qt17OM*ff^!?TB5`6dNfVO9^GI*2YFvK~aXU#rP=2I1WyHhTxl0&z>~A*fte{tl zwR9i9Y?+J^JBwWpyoSCKa`FHc2#|UCk5gZCc+aaaz|{$);tkbYG`$n|U!r_=Oyrf5 zT73}U@2w9c@mde{S4$aqcz7)MpAP?dS^nu$W=4kJ>(|Rb>mfr|`!M5@CLVANHG03;B<+cf@#R4j>A;?iR*UABIs-$V=ukO)qMa6*`~`o1_}j5mWD23}SbESYP5BGOAd@ALb-GxcjFkA(UNaXf8G-@rc~@yKNvCY#aI3pS)2kl@QcP;-50QdTMHl(ODtZy#{FooCD%Kg$g7JL9Y0wjq5r>lP@uKD?jTqqg2EQ(| z%=YevF}c3HTJ-!W21cLqcn1nxp&HY!V9@yZ)d*%LZYPwuD1~0-mX($LuR?{I7sT$A z6ogC0q4=x~j6&tjsf{C(;pXP1Vwv?jBxV6`nSluo5=&BCMJHQ(dy&_tP;Oox?E+PK>A3dZlUS6u^A6g#0TWUvUK?$ThmyXr=q9;R@wEtwL}vOEAoC+^SgHB%op8eYZeC z@h{@uj(;bEY7)16bH&U%*SpnmGIW8R67KuA9RWA?4zArR8u9KZcwII1o%W8zL$5D? z0Z92qmjM#8t{RH~;z+trQXefC9UFjqQ_@>A7*$yI|5>z|3dvA(UV|ZJ1}`% z?W5(PbXXp+_tF>J0^An01qGQK1j8u%G}`Eo*O&?p4^pgJ0_IhuJNYQ#sLXR& zx?~m?8ZgCDVIJD|X7IVmQP$mn(5{-ePVVx>6-M|!>6V%zjWIs9>3VYy7BzTjTr&pu5 z(4LUFnLi`4_s^lCMK?4Rg=pLo`{9N>%`5*lY+QcTV3Akji-z5QO0}9DPC@8K44Rlh zhhnWVixn}yq-%=Bc>ci9YpT~kj6<;t&C=m$L+{=_=QK!cM@+~s1=DX|XfCoo35Bb2 zOP`xk28#;p4jR6dIniPE9(jrnGkRWgmqb~n*VF_$J!I|KFu1M7mLj98re$JhNUK-Ht2ZkPFFKC-V;-=be7y2I*53-k8GKBNjn>86F-#j_#VtRh z$!{d+xOnu~D|215`$?1q!cn%p48dznB*w$_AzRQ7Y(IZTG*^vs2~aVGMQNj(@PNhV zjv+z?w8V3*Dv`Nond_g_YpQA={yaS`9-EpX(4v576v!F`n3y{{`&umUkzeZBhpL8+ zYR2;v(b@c3DbQbq`rA5g?57pvOt~Mcn{+Z=!38Frc*#)L zEMF5eNc2yk!V}SY$#ooQZ07>R2lVigC3%3@zeWW@M03j^keE%cqiY*w{8`n~BAh)Q zq)EtQQbba|_eE+(0fCQ?&tt%1sFE6~H0Cmo{>Gy0m5kkc=E`(oOr+l=PqPDT^zB{h zhHJEgX@FeT>+01_*-@xxHi;h&I!5--S7)8~ekapS?`7(2M(;)_+ve|FpAf{_?hI}CW}O|MIBg^Gnp)5c9btX~VnmKh!A zXuKrih!ZnfgDeeHZTPURjSm)9nFi~$CnV#sq}o`PMf>78HeeeqW2)gnmTz;F{+WAx z+|e&5oXxHlCjiPBd;?ikgI3vE0!5q0zTPDZ0!J(A%zU>V{4HIj@#V1*I9ol9>S~{z zFO;|p|NNLV*Zm#N$2aiq1-eRIMp@_@?ML-4b^q&yDuEVP9SdVD@(f9u_^NSklHk6r z>Pjw!se=c}W0vE7E%wL!XN^w9Q`*fD=dDhYmZHwVf9MqODEfwvA=9gc-;1xWPA1R= z+!A@fVFGhW*Rgft z{>{kD{LE(uIu?L)Qm+z8tfsRSl&XX2b^Ya{4YSuQWoU6rvL>h7HTA}S_2E{2xOBQ^C8+N>z6 zOh_F|$(;B2Cm%s!zSpWH8}wZhzEn>dhZd{y7+FX%9tPKf4JNWn*RgXPWjUAg{Z30s zA2Nb3yJyy7PMnTPm>3#fe?`I_$`QK(HEDOl3=>Kn`|B{?1D24mO*Fr$S?vFZrmqZ( z>igP0^ia|vJuo0cNh2WLLw9$#(hVXYsep7T-Q6H4NT+mz{^%Czeh>fWdgmJ#I?Oq< z&)#d@>kbnro_f0-?;hotltanMsPJ0*+jZG9!xzKVr%1SBNx;kri-)4F01@QkUhHlj!HHeaFi6;*DEF<&4UL>j@JS1@XpAe|(rT z_7HpOqgsRps=PBZOX)1tDX{@;tTZVE98v58yc%^3PWurY{aVqm?hZd*mm*#|%+OrS z#0K7MD{|nC(p0Up3XgU)DiEE$lM?Yc3DlAmseXAV7Q4p;By)e^1z-J~Dc42$0X9l= z)uyH5=$#JbFrsgvuVrAd4w7nN-c+w=VRt5rK8;lO%^<}mFA=}_+ zFIe5)z56ck=vT*%1q_qUtetKuy3;7sF{Z4V_Aav$g2Eu*tJf*_G>}oX*Nh`ITR}Hn z1P#9$6U)<&+Y6?(`GZ_D(4w27XhcaxrQS_ili_FNe*MprpfFi+7woAftd<1xC0Y*C zxCH6__WIj>s4eC(VPW5!kXmzzo;O+s(nN3b3Kq@&bwXeOiA8KHKtB=?Z3$07db3bu zZZAZlO-zf4%Ahi=3Vy3OuVH-PhHwWm;qO6pp>xL86Gaj{202VS@4$A!HrRUP%Dk6e zR(Aj~p!02RLetOo*UsL{4ib#5!NV^!kbbbqBwjp!TwM?$)E`pJTan+22Qk|$RvRKf z&6JU?!{=`q5)9j_sX4VeW9|JrgZ_U>oKrj)aU*9VC+$NKmDBNZHG;TTizLP#IkEqnvv_#``%8t%BxxWB70BdYdV&~FWAIoLs@!s-f)x;B9W z`6>UZ+!I6`oYy+SkJ@*TQpdNkK8(#+59GRU{ zDN^MxZxf+}VW@bEfRNm;4A>X>KHhi#jCof{Q>Je$jH5iXq0zZzHgU{uUrV9_D$ixl ziHFJyXH%Y4Nx#5c{CTU*K=3h}`7%fDvy!xZqtjHAGQi1}+;s5fbd7F3C#1 zZSVCk$R5>XkpaZ5)`}-9c71#gYGRG!LDHbC79%(#*#%{kgCiInj}N!OrDFBfS^)r zju{1~+l#_WwVwS-NmoQ%hZW1j3W;qSAI;r~m{ax?-FMId@?46|?0=76I=pcc%U>S{ zez~kW3e;b5pOep+#f8Ftg>?O%mwts#Ym32ub(M0Z_Qz)_+0A+>Jm%GdxS&dJa{Qw8 z3zcq+_i{I2mmC!g6&^E$loqzm^PTc&sAKcdSSwQAanKxOmDp3jMzH}d;j=%&o zQ}3l^~5jiuKc*6IGp*f)yM@FoX|mBsS#wFM8f__gTsC?F&0ZrrdsJHG^<7MQZQk7K~?drz37y~boc zS#3OLMWy_<(rsmBWmU3X)k|a8_ zk1j^wM`VVBkVkggmcK%j12lD}I%eK`Df{KOzumHX6tSx=jP@tB-yl-978x^C;t&F+ zR><(okM)Wz3+tounGE>2#y}Fw^gN{ZPRI5TZ5OmE*aO=hOLFu=?3bbVexQ?p0ar~L ze<-o`E)C>#J?=Bi2}Y0$OAk*U`d8fWGttu%=8eylDH?W8V`BVG8Nn^iYakW9rJy|b z*#Ly7`CNA^=od-a$8A6{u6OLZtH&$qR6vPX-%@$A^`kg@%C(jvG$LhWrvKF*QJAKzO=rbRN@ zUCb8Fpp{25QMiph_hzbHB_j=jrlWL!4uv{cRK~zAo4;`{G#Q_k@l_iL+*K)QGc390 z#gAN^v830q{uybj&oA5|*{fb=X%q5e$Tg=7;ba2m3Wyp}aR2>u9=H&`9mm{8kcbh{A8Cp24&`XSEkOb&% zQd>>bDgasca71nyWlcje!kfI~#3n7Ko+Ek1xtVY6nxLMME-Kddr)!wz&+bb4GI|*q zt<7Xfgv6uZ{*xh3s}YC62xwSCChFPWRg@ld7(%lBpD)ZC=B@WYviMfc(l{#b+Dc8_ zjtUUxuDE<>wfsEU{5c#Rru{rc&>i?n%?D2%p81wh_~Ooh)7?eFq)STFKi8v`uhb+C zFstCMEB78BsI8^`&*KxSzvVfWw>AgYpSGwiG z&Nq;9yvWL8OJj|JUR}kw|mJ%$lt!nKlDZf+hpd`KY+q z+IxAkko{9!VeCK$xwf;MQlZyXD}C~mSpBLbMa6ty)HQWA6f7tpmPfv?AppRtHC)6e z?CiW>d*ff3y_6X7oFXG-p+)Qq`{?4!E}FoziA&Iw=7|%;?r6R)(C70r^wz>a2G(Y1 zVUnt$7EKS#C#@q1BT=NoDBJ3!%Vw`Y z0Oc$bS{6Hl%U87n?Zv3a@X zuk|777oH&6&*!yp>Ix49@tMS1pO(#OQW~_x^&}e zKRx*GWHrXkBm<8gdVTkos4XE_Etw-L^H_dfugIS`6nOxECvb1m#MmKE!17{*Yzi~v zGuh|!(uOz3l(S~o_!c^L&iNEaoxXo#vMmx*CPoGd-|(}@@2*<4UHxs#m8&N_)< zj3-o2M8K%vd*kQWnWQW&jC%gVN1)5tlPL}8kX5|~8W6?x3$ST&+lWjvSTMk_Lnpkq z3UPh^=+q|Ay+gT<*>G;g*SSi@u|WbHjeB^}SQ#wTZ}It?s;{z2$UZRVVaCTYKMyP2 z;F!Gy%Jmu<%h^kQZbx;;|GMFy+Hh;FS{?hwGiC0r5q{Me^1-NaTIb;nBSDt~(U zGhAG2v8&RX6*X~2VkQZ68RhYpAM&U;+RB)(=o`0x)owquf`%`Hslj|_2Y9|rr5}|o zg}xu+KmoIla#N&yxa6ScQ*YgOKA(`7c*JFM!{Urnd&` z5|RT?8oTpnEJgQhuA?%4Hs|Gk9b^&lXS5zXf3skGivur{6>csJzF_GUI5*!x2@#}T zL8wMW;}+=|%2P`l=&nqw_z=aR?OwJ1ExKQkAFZgTkf9O{xEj^{s$$dW7-)T0JJ4cb&lTSNa2gn#=r;-~%S8IOMx6hStA#rk2VGGoX1AeGD zY%X^9Rzqwsjo-?{-xsoft9PuG3566u@)(&5Y@lhz7JSl#?FQUiMjV&t*&DL)=39{@jE8cdI)WwrHW>)HQg3>^?1*cneMq=v4Y&jY+Tcs#j&yzCFEhdii5?Y+F198wl9MpXZo-}(0+weiV;c>LHL&9g zTSaE;C5($kOEX;}_5rEr1BWhdU;I#@b}k(hEfA)&-|i0dwcaj@rCmoR@B^*8BrX&~gj6nZ|IQJ#&eseLMOx zWd#n^OVTkBQ{hymTu}%dOVWVei1+{TLRH1V#XZO2=L+B&=ssf>^Xq&#PW(VIz83Vm z=)!uyLErFr2suQKlOu_NReQ_tH6*eM8j3{6tSya?40Mw_Mo-L0C`)0t1idlsSH>8! zoy{cmnyIe}<>lcy{rw*ANj!2K{CF#NGx6!;r%$GLf?S!=&P&$)F{qx3G?ER2UBh_? z6@lY$dTfF!>=Por$~nS6$`IF3-SNn;YToCvqfDyuqX+`&7w)#wbu0=8&{C9X(bwGX z4gIeJHU<*hiEQ`1hc##ZX?b2q+FwU3%m|8}ZGmzx!&fzo(s>by!rE@dV3kz*uQgGa z&-i&K=j}evRJE$^2PC?UpqX*ut}I*WnU^C%mMUr%G)>O_u9t_0C9(;f9GUk2hK3ux zl!m^&LtY^@nLQ>RG!5Zgb5dT1)>0ph&M%8W7tY5Ts{3pEDoIe6sh zfh#F_ek@49kzZ|zx#z4&{1aRA$=NGEKOa1Q$;rt}v{=A%Zw^#%u5@?xHe}gFpaO_X zam#B7Ep0#aJnA|}8)F_Akk|<=Q$PvaO@O28F>ZadtNNKu@JAWTRim!#jjYf4NY;B6 zWwug>YmQIWrB`L`dj6Pk2vweP%NiXThlZjh(6p?;@Kd!TJEuJU8GO_bg)yjVrX`_tUDyjYdI9(}VKgC;SiinE%)V%J_ znjZMXs0p%dToaa;JdH0Ff^SXbL^)q7*^&0Y$RbEgTdI>9p`M4)As5&X=;Z59Nz|Te)KkT-2F{?cWWS81I~<`X#>88 zs(*;kz;bI1vvJSgOhZKy|MX4@i5j=JB0t#nmfNZ{cl}Ugk5XdJ|Je!MG`D>im;3U+ zbaQsc7yt#JM>SXSNPb7At~(dG$OWTFvCpaER6QSfowHf`ii`jK(rVt1YA)T>Sz{Q$k* zy--<4`*YAU`j2uB?RI;WYoMENlI&ZT|6)~Aunx;KtyPuf^hqYz%HSXz;6?dfMLQzQ z3VJDZggqFzrFD-Cm`MY4EQ*qDFDnYpR9;r7f17@h_a8~t*ZF@{E5CmIdbf7%;bGV% z-!*FO$(l=-G)XE_xMEL?M(>FBIFN;$a+WAnl4?dl~sEq(vg zQ)rfgPFus9A83PG>g##NqB|1xEoOfi%+_PtV}b^jK_`vd7)>i+Won=((2R&^I4a5| zWR6B)#f0qqw~iPli{Ascbg!>2#VsDbbSs$$E8nkILKB50YRb}K9t3O$TRo_n;dao= z{nlDYZq zXUkFM)}Teb)b31@KCmE7{PbC?sWY`n8?2nRgSjv7S6fo$>&mQ121Std%+U4~FYOUJ z$>!<83?a<)LOMJl%x33Tg0r(tddCk(_0>71Zk#jqf|+e8U-3mUcjhY8x9IsQ$8Thy z&w)g`Z8zo;66BJe`5Zwz`=)rO+2`ey3*j8xpKn%=W~<$B!Pn{;xEHL@jT#0x8_TjJ zBMg3Ee;3_XQTouBk%9O}_qC1Y!a7$G10KGOJ(gf^7F$~;55;E``b=pl+c~4Ji#-3d zQ-cG)!9fMMwXW{mM`~dMzKXO90{Hhi&G@}0sYEp3$1ob;vjkuYuvo|+)@Jk0Bm zPS8`i0zDMK8q)laGRI(Ie8o_?Enr^Zsi2ckG1Mwofn;&ybGqU#(0od$J+XVV#K*E#%EX&1);(XTX8*291P$sITU<5 zo6Uy08ErO_J!XlJ!M|Y%HP#o0z_DZ@_{gYQbNK;S*`_@iFPSWi(Mt8$&?WH0qa)0H zE)Mj8`HuGU?=4*HR%T{qRtEu6H)nH=mQDfNJH1;e$Cw zl5a0%q@cQUq7AOkOZkNel7{FZPzY+KAy_f05C4{c0D8H8v$9F@Py*R$ZqCBUc$I=4 zo&rVl!(hQDf7Zt>*9UZfLK_E?OY}me!OV!u*)rT-HAI&A;bfMje*10zC4J+)#i2+!7hi};-caxB=0bjIQWYB&t!{h0#t^Z}_gt4ab zPotn#v@+#4pW)$%IDFMM2|w1uBPS;2HWx;sz>c%SWcq z517et`L%bu@s;Qx0shg8ncQtck?#+aL)sjw6lk*?Ay=CcEB%yU_Rn@!Q z6N}6$&}i3A4~-$+MNUFUm(^1xS%1d7id8ZKA`6L%Cs2NW9g8WA2|7`sVIqbm4;suW zo?+SdEuK0 z3HM0%(8rc6+w(X(rlN64eVav$9F}Ax-`7e@7dvq`M@O%Bg)~b%s^Xa5u$u6vla_dR z*Tdgi&X}*FHp?z!c45Ky;Z8$eeI$i0bvQXYCU~Misq(><+6iXomC%H%ey(NLuud8L z=FJ-)r`!qkW*a>pM?S-9jYMs0y^W)mzkRJdAcg~$c5m;D$aNV2(4JcD^N68cjj%-N z-gbFV?tzLs!Hs#P8f7KMSsa-v55X*pNhaYKh(Fccqim|-XkVj!Fnx1{Xbo^F)FP+M zY3K4&ur5+=i^$E*wHm}^(LTDR$7$VmpCf`==Da9G1&*#wItPqRIc;3_k;2mU72rR; zBi?KHqWnW?7IOTu766K8K<8KgFQ9q;pWcQyZ@8Q-z~>QW+PP!@p~-vh(K*cKHt7f{ zafH5X(y4m!do@d{%YiHAd6lsTnR#--Gdey>d(xU!sgc&?8s9)_Mp;@7r!csDM5GF# z|LSWIk`J=7^G%g8ACo2u)nlRnV6x9B3zeu#?LluMz2wV?^ebc0xy}^hT0NZeje>Ty zcX>vgjGBcL&1(VcYY+W#{@$R$uav((cQJ_Q9N+!d>lM45N35FtJUbmRCz;=_Ia*L! zWv<+tq%r)9o4<${kRE6`W>py8Iob#UG-PMXL%E0s4o);{Tle-V1il`RKOBrx;PoK| z8isoAQ2-$Yb+E=73U#;p4uOVE#YfQl*|p>TUzW9$*5?tXpQNX65$?q@gPW1aKhSsf zo3Oz3$MgAT;F7!94PW+>K1zqTO^O#%?tO)_HxRK zGMLQU`7dOJ<0;%PY)}Y581vJx+EMVPhU_N*YwlM0X`Py&k8|Bg+i8Jsc*zCp>3a^`iAxyL?@boNdu^6#P}gcWtS* zo5<7nDjE9k5eWgq9wqF&xCY&cRVxU(4E%>PdcpTrbPF-n+4hcOzh*5I;(bAcKK}Iv zjs0IO&(O(fpIYV3Z+dJLr5-C+{eQUAUaC;Y0{B?WS9zR5-@cjKEj1Z-UR75mIGn)X zHD~e?WbZi9!)81X$0H8uyt7(qse$|1`oNE}ZV^*Ag5H~5C7rGoXUAQ)9!o=LB2RIXnCbj_5TtkrsVFJ)-el7!WVeB06#Dk=-k6O`1Rh zpqDmf;1g?tpy3o)~n$meUGU*BT zBukI4{2X)nE#Ckqq&Om9s0WKEl2nJQf^snRvUA()Hezwdv6&E3E?;Zi^0)Y--GAGz ztnc4fj%UudN%QY%TJ|0}Hx*hVZ^^;~z*A33eoR_#JO@8#a#qXp{nv^J>`pT`l>xJI zdB^$7#g4$=^Rdj9%5D*#e8c}A@d~gciq2N2K(U2l9U*LV}B~B}N zFhNMM5TD*^>?$ckWtJn>eO!)QbNF}FmqCipIqPK6;O9qF1k!3uy@qLP7Z<jF-omm2&(XO5!@M%aV&l0R zI4-<6^C1A3by^@(vdyJMd=3N^4%3q*rRq~!Q$-m^N(Ua;1(m0YX!%K@ZkUOR@h}+? zA%4o`c1^5B5OdJ99ebi;Pb65>UfrFJKmModx_p-Fza&s53j+EwGdth@YLKYfN-Dq5 z7MAf)9xiGei(ObMWFm^TmSz!_j{N%`{0gU!vX{>zi^_JbtZK`wr<(eihGYok=qr`7 zK6u2{fxU$9VFDYzOQC&@)57`E3aPN$#(-+@RR?pN1!K)B5#vu;Q~T{=pHe0_|7Q)J zW`-XoXMOd-b z=;6RtjB*?5OaeAS7iWAkd=z(~zS^tOb zt}Y4b|H>z1!2PDJN+Mt9zD2U3c&rB29@iU$1Xe^v#I3T08Y&>m z9XR#j=y2J4{nszG$8&*|hac}LL4?AriY#epnglPRaT*Dz>LPmi>& zZfYKL97b_*F=%zLF1WB*U`5Cn{u_tCsS93gQcf{I6}%} z(7elCYwcJ;S#slj7@s7rpWtQQThY(Ro)qTm6?*H!JjE~b)2+}b$u!4d1>Ldv+o}pT zpGSSOAu1Fg=9~1kh~(mUB}YzVj27}$D@76k#qUa3YKEkX8A?L9`q4sa45sRuHjGfs zEo{FhHZ(nFZn`%KKi`5P{Q!Ni3zM3cClSTlvN``tf|wGU@$AqjT?PddU}i&b6ql&q z*$!Ff-*7s}>v4b3nlSkIGN@YlIEdtlJNxCXI@q{VgcQy)dQsP%xBp6drZ2n36I3mi z)Fb9p(9v`#{xs$?C*V!=T*FPu!WDSYYk0ekDf*iZt3Ix_RpqKtvbXc_>2g+$39U~c zC-ysJSj@1jt{&?8xx+UV8N(gnJ2<6goM_mufmFJB!kX0+?9#`}(un^AF1`+o$&lNBx<~{B8fSe0x z1PKPZY=Y+wnD%0dEl0{FD*6B_k3>&Lc%EGBpPXZcLDZ(SIEPU3HJFeCie(2tAIXb@ z`f<^VdCM27IZoGxrsoxPvC{Q{aS@)8Wd>EgGYe@c)D@GBMDHLr;Q~mB3ObudEgP#R zxlh>hj}=_<`4metGrCDW;<9{5O=%HVo>5)AKSg7J4r}`qqjy@a{eE^|-^g&;j*unXo;q$UvBYcuV8OMnG7o6?YbO`hVV?w#6H( zgxkmDKxEPyqxX<4UubESo+NiHM7CgWJKufi$iq_!oK8t=M^+JN%0Zx^1kh;fQm>(d!`TC3Wj*jA)zQQpRf(c2 zNRh!^+1dNrCd)hXl34NhS$gCSl9>CyLmvSR%~_7yvF+2f>oM;p=Ot2V@Qe@x6L46Q z5xHu1D}R48&|b@TY`q;*&3Thb5C{)ba)MHx|Lt?RS*Yi6|Wdt zq}Bv{sHk}P?l;;=C~<`r(Qd&r0EA2h6HMRJjxiqk{psc=By-oO-Gx@KfT=32S%xOJ zscus((KkUwMWlGCXywhnAsjExr8`5Jk>C=@6MU!;af@&k%osZZzekr*6(pc&dU=*e z#}S|l1y~V!o<8J=u|oVVr&4Z@u8ZrP5iO(mjJm$iX=V;RANE*yp=1NM2#qC`J*i60 z>uWIBXPGnh1C8Gf=mzgh%w3=XAM>3mKK`=(#QpkHQboZrapBK6#Lm~#&J1#D?B`rW zlkivv{c;YXPB*|s3y4piRLwJiQld0B>A&4y64Soer4;>SU8Waty5J+if`o_ZobBiF zL1|F8S$YrbOh6Q7uBOZk5ic24WuNYG)3%cSeh$Qw-G>zcJ#FweG7{v9@K)N$_$WFn zaO*8MFWgcYgDH;foO$f762e(1^IW_CULQrU;D?P#Hb#eHm2SnBOL(7wGTQ!_q0i zIJY)XUAbv2Twt^1s%~*6bDB#;cJ}VD0~UzFpJAxj1D+)R&H~uwv_^`vS){RBF@Vk8 znw9MNckkZa?ewC|83LLR7VR1EE1Ws+8FP+$7gV8^#{yvpBLJSRK3?+8+FZIvNPQWR zYGO*gSMthJm>P|s+XfhU19}z$@#A1h)t86eF9!U)0DvzUaOD1Fe{b&w47-X)6z08O zaJm3XsB`y^v|_7kYir;GDf@J}uLN;`t{ve7{D5nO#;sDPF4$-NzMF3i=f%np=S9Mo z^{!dPNQ~Q9dHnb8lgma)6BXEO5QTed8tV*Zm)-b61vUL^^ig@0B_ z86-WCvo?=&{AXD-geLeQd2uHUHH!0Xne~p?2H7xK(1~JxyA-OqUMYMKl|Idm!aFc5 z!IaH$mpFZ33N<6a{J)Vmy5AJMxc1#{gFu$X!~NRhJ!q4A_LOA;G8~SNDC4%5VF`7|iDd`|cUX7X^m+dC5jyIzV! z9wGA!&&^Px+#K1+$K6bp}FyEISI`B`A+CK_`h0(Bq(4YRFsc8GING zq{YWKE=-YNCjuf%iKK=R+$J2esihWsMi)5tetvD`<>f)%mq#n6SKVZL-e4?OyZ`0U zEDkOJ1YOjB$p#gV;3_(2023yD1mC(o9vZIQ%s#~~fu?@{o$XG20qtV&s3NH?W+R_w zS&Eb~ZbRXQ#$6&M2Htp!W-?Ns(_>+@X_-%g4HS951I8s_OOYSws0o1EjmcJI8=+&^-w=gVU}g`(v@!+qCu z*K=M4oyoLkROBN#TCDAeh=fRPU4vaHq%@221nfo%(IM}WUVmmgIzkKiq4wh9>>05U zpr)z7Xb#7W5v+npit8N@l?}cAmRX{G{ESZ$MWwti{rtriq{H0?ga;2V-thXdgk!*x#oFGm!RrdwIO}JLarBwy0@M**FyV zIx*$~Gd)_apU9pZsBY6cp~Ti;0I_+zAXszl`$azxVZiUuIpK`~#OLWg;*sD*aa>bDQNaefTNR}xoJwZPz=`aoj29AtJjhj`1 z&hCU7`KN#7?+*L4&c7p3vbXxoB6w+oCDDon=&45*33a6J6^jLc6Noj>Ipc|$Nn>yv z*#AMy<7O+%EVh+KG6X_uE{~fn=%QDO1f;Wn*eq*bbzLiuzmjvSoYI=BNz69R_y2{S z79d~^qF9B@(E_TN~^)r@KHu)LyPYO3*ww)o0Ld#&RK zVr|M-J9sZOxJvGC_nH}~du&E!5XoCZKTg*RjpzFPKXF7UeNq>tnfLb@2o(4MU;(g( zXMf;H%vcjkZ6U2iJ&XNI$B#f-=!nP{{~Y4P3_ni8qLke;oBRd6b>YP(=Q9t|$93r;mFRexLE@3~ z7Fsg|0Igt-3ccS?HPs$7nt%ASrgS)3>E57=dx8T;H|zYtOE?1q=myzFFC0)XD1XMw z{wo~Kh>?ym~^ zEmbj!e?kSnmTpTnBo>_E{Bl}L)Gka2@Fnv8hlhic<*F0^A6kK8!NwSSHy2s1xZJyU zT$S3dAs!HqWS6Wp4USaGz}wLxJUIDt;&bu>sh^%AmxPkF(^4gzLSQ7$AJ30NFNuBe zsr*kztJ2Gaxu}(0^7vrSxx>UGpz7L3etpspPpE{$ljLJfP+OK<1|3(TaLDKr_>D-S zLfwsNP&39+EtgL_b_0y!_bLjlFTqHt!jq$pOBc1QH%ggcb@+G_qZ_o|RwzhZUUOn- zLHSf)-v6Og^wR2^%2#}`k&`@qNi+X;Pizm$urc~Yg|z?~z!UL!jEIA!4p&Y$wo6OG zarNedHgpXBI{q+ry|}VJy1`FhgvQ54(MgBNLu)0`$VSe^A%R6|w+j<5ke<9c?HG5# z8ujY>`{VDwOP0`(jn{0tsN3`q7+C_m1t%Sn&hdjm&j+U*>y&>wbRV%*hYjAV%q*i{ zub#3M2J`o6HAm3vBlLUwT%()|?1^PcvyY~ApBFg@OPu`ncf(lwTu!S**+E(3(vSs~ zMBZybj-zOUbl9JZQ_l85aULcXQ5*j{h_%DRQQ?fp;t(W&DXkBV1Ex5*^+!eCWdC;%sX@V6t*}~+w<6IcRjdk%!wsyEx5IB2 z(00KV#i{?51%E zQP+9?$5%ieGTd;#nl}G$CaS5Qp9Wq<*Ab(yvbb}o#FmkO<3Y7bPV&=j&!Al)l`KI0 zStiEO)AU?(Nb;HsvgpFDm;9;o-Ui$y7{90M1XnN28RMd8R7hiP(xK8xd*Bk~6%p!b za~6gqOiUHR*rsb7vCLslla})6l*ZO=hFGMKlCs2_ks}qpRK#ib<`3kt zC^LrT%B|u~^Nu)+NNWS)oMR5py5|oL6E&DIiGZ3iyL4V1?XcPm74*XB#{6o2p?7@Y zB`oyP!$pPh;59qE_)z*qut6+`hL4a8yqz%%K`+hSNpJ~vJ=%DW?^(WK>QJ$0-@({{ zp5WEgA#|s}4r#YmqpS68?0%EHC~IX)+&M+ne46UI0~xIRZ49x?^pse+;l*Wa=>Zx1 z2tG1`3Oy#eFwT@%%i*(_!Wu2c2qtnAXlMx;J>OjrU zhwEtgDY0g9?hujX+g@sQXTZcE&jpah@KiJW6t6P_KU`eoS2}%dZrl(cq$CFgNVF^L za2-K6Te@(4*h;g{Wwkst*XRoGp)w#f#F%Dc??fB;}^#=1J_sH(wINEh)D zLK-v>xc2gmbmYVwV`#j;XnE`Ow1SmtEWQqL1`IgEd%1bA z$lGRm{bxkhTahn8#Q~rz+pfvbhaAN$&{NS+O?y!r1l@IHkJbw;Me_*d!q}MsK99i` zvsl5L3>7=-CSu9s5f%l-uvq_~jc&ToczaZ!MsmhO0}Bb5{-w~zf7=Goxtp14h56tQ z#LEfrKe!uT8-aNoAhNf_d~1R%xziI>SSrY6h`iLK=;B!@Q_vQtCYYqa^0T%csEOhm zY&!gd&GS;-plWbk3JMf_wmFN3*eDpqH&IYT1QC4_iuo4m^hjgg#dO(1#YXk=05=1~ZuOU@DF(qYr(LNT%>}wu;S&m|5Fb z86#HxQkg?*tu2R8(BcDqtqES=FV-qmIHqmaVNa?C-%NSMGT2Nh6i;o|226eMX;u4f zjJdrfT7lCL3#4e=j-1z&g^5HhhDYiDO5gWf@p@IIP&38{oAZ3iJqW(* z55D^>c1@%_<+QX~yUy)(+i#=}Hf&#Vf^Lt4{|z(tKR&$pp5&Cs8feFi4VClFQU{(1cA45nbNZXb?ql zR!rY5kW4LC!)HYqg24E4T97;c)EOn{_Ji@;|BE*T!v$Y$E8crsHep?0S+(6xMv&g? ziluuwNi8x7^w)qnEzQz0kkaQ_^D>jQ`cfl)3WuZC&kiv{YW9GTL?tts1P!b{X?!}N z=DKjsS#Ep2?1~)PpC0C|*sNII_g^(gVv)K-)UC=XW~sJX+#VvbV3@RIc4@yb#l0o8N3Z z(*1rj1G2}R_}g!YESs3+dF;0NJ(qG?D0+>~ufp?8nd>qrbsQo1^yI9>P#i+ox-@uZ z(&La7$!P!ppRxmmO>lK3}3e`YQG4$?QedNg`h zqD$yV#uoralRix){BIANQGS3E$z%61Fh&;4be(Ri?FBJ@r)NRA^O<%19*Jn;Npx@m zNo4YFa&sJr0zOroaLU<#c zM|*#FiUP-2W6%8-WLPRva2T1Gn6|Jah6`>rE`+J0IHvH;AsPq{m}I0`7-l0*1+!kR z^xRn$n@)0AUeWpVm-lSsKHUyN%+#$K=Ib>B8Q#Br3#pp z+0K8vZt_IC%6{!zw|jzu@vP#S&(gxeq6&57!Q?cq8QFMKg6?_hDGv+|E0RY*!+!_; z2sA~7Hsa*51bv_&tmo(7RT-;s7)?(j%>s*@0;|_&f%t^1NxnISj?=&P5hqS7E*lw7 zayGn&-*|h`5%Q)O0En4Ahfrj&Qz z3r4buPWn7Yh^1>0zK&yZu6m$F#!gvMqCYZ$Bz__wJqG~b+zb>_aGfRQ2JEC{R^J}3 zEgsJ~%-&*IehaC- zzl0wsOHSQ5&M3Mj3qBzWbCN95Q37As3Wv{sOZ?un^@bddG@hajFNY=gc< zY>kSdn5d{$gQ&@Qbdkd4f!r(I%Qwp(C95lI-90B(f0>yPljm8uck+oXbU zcyE$ysH;OpwDl{I3nccH%4TgIC~wWd0|+<1J;pO19@UFf zX&D10IiINCr_^C4oO6LM!%+DZv)gV!OMx;({T>?j=I7xxWf&bIst6gN_jq`B^RuO; zeU2ulJ4y0@d^)y;?r8z=ejxWt(wf#?U-$RpghDLC2y~d~;x*{Q=-0F>)Btf*&8bNK z5eq~~>j3kQ!{G?bd=v;5FQ7&^TNF^jn*RUagu?Hl2qm5JVnNhap?o&IJ42}WFFLBm zzj&D?GlpSd;7#9)VcRhxWn2UayCVt!c?XdQ8(B_U5;Yj1S^fk+Y`Heu_IFucR`o`! zGig44dW6c;z*}09TdaBziv9CAC5sJT#F1y5Ok#ja7ZplZVC~FkZm#{OkYbXZJ=3Pj z_Kevih@V>pcW8zJJ3D8jtIEpZSlsV1=jig8%f*X!LVZPvn<3rKNuMV}vC6eR^2Z%H zF@xi;;AUr0MV{F11@k_XCy8;rzkGaw-*bTmyqUvBB=4ZksJu&pSPq77P!Z(8*all2 ziop%q4!Xx&zWlAyb*XdzHt6(X^o~=PKXs*Uc+<@RrQPdLgKX-fdf(Ie!_nlt=D9J@ zBIr#(dmSu?+Us`jm9&C!`XH6?Yb@dJ+h5m(ob@W*OU7vZG3)X8r(|HTu>IeDZ#({O zKltoXbeo{ST>&?HJe8EuKG*jHh4fa{zx*imjnlwrVFA~1KAKPV2sVevDDa_xfwg6a z_jqd(GHy5qkQCNyq>9z`o~}Y@`8;jiP3+B!qMrz5PA`+IInQ%saVN3{p|=vzPybQt zsP(ZkPxf^vKGaW_6eQ16AEqZuLd5=J_YVEF+gz5{DfW_ucVS z{_p=Mlp|6`GBYD1M6x$IKG}O6l5B_Uvg1TZsF1yPi0mE8itJ64aqR5K_+7rAe}Dge z|N8o)-jCkLeeU~pU$1LCuh;c_A~kh8YmhDJuNx`s3vQV1T3RqcjR!w*FENweS53|e ziRGc?b`3#bv8QC%ll-|JKPu^J_WP4IN?GdmE%asM93~>>XOdOUrybB=cpG&7yX%Ya zeVZ!k&Gjc&ZC_1fDhrks;0GvAj)e+QkQKGQ`RX3m63dBF2MSEavn|Ha&6|>5yS>1E z4%S0RNO(?!`J94~3ifXiV29Rbo8p5MP$(SUwe|_COFD%y39$S7c=*|5q}kN z?Vo@Ni*Xtg16&4<9&er=m7LZ3DImB0+plJYe)R5OxBfuOV5wD+J)`h;BAZ&_;Pwb z2u3iqh{hx*#Z4yPm}xAY8LQlI8OOU*x3C7xna`e8`&AhgafJ=+eh1Nd%+UG9EoLrP z5!1Z=0A1%MFgHXG#Y8BsUwi5>`ODR|uG((PhrLl{<7+Uayy6MGY|wgZ3jKX-kT>y;Vbn(bPA?M@n&_g44@pTtgX;em zcx4>+|NF&x>DHv{KVv(^w63W>T&&a{lp(0oK6HKuN9MmhkhOpGuyT!3@$g)oC7aK8 z8Rp+bKq4LV#10J}-=LuQ{6#~)DlehaN&@Zi_+rSdOm0i_1@sS53I0#xnUimsI2WuU z%_E-nn!zy7$-je1j~(u4_`BWy-0(3ie3ZFn;`_b3ZL8Mmi85AC#zH9(k8D@ZoHo@m zGpYxq0?ZT@NyuAdC?iUuZ8(wCxJy}zSwR6gU#gkHGFpG(afwy)wLcvqvv|W1R;?)c zz?qFUzC%DvS%Y3y^S!&m)ytQaf-gn)ywt5-J$Wi9lx!6p;Gz5OY*Sm;-F?UH!rqYY zMuV>Z=}zjTIG-$GdD%;aWgJdKWY)YH(R|g*TkOb&Xn6gMgEqurNXRHD3W0bwq*T&W z9Br=cUX=gEY2Zhy$`)lK??9ICxf7C;BVTmi1nHcAzngy#}?euy9p%wMb(S9wNZ8zl%ysQSqe# z&!S$@$B#f+H|Qno?fnl9uGsSz7t3I$IgXK`_X$Fgb?UQZ#RjD;_wK#lye*ayLzMsN zlZKK~h_Mhqzm2O+>+)c(5H%$N;TEnJyC!uwPm!(FSpg?#a zEi4>o+|%7nC+2oLatncwIk2^qOAv8>4D6XeQlfx-eza?=r8NK@)k9KX@2picUSdXr z%)z;m$SX3|n^Hd1^z`)PC-Qslz&J$W4b^hVT<8nfQO9SuR)Mg z5Ox@AhW}HnpriyUu=+YWe;APU#?TEeDk)hK4@*J7)p&V%LAnc4h+pDSmDedLpIBS( zFnpy$3c}fTJ8|(kR;4#4z>WyiD?TBEhAAm2e5UoY;$a@03)?L%a*df9`WhNf=R3&g z=;$t-GT1Q`)yJW)rs;5@(P%naS`rcxIBY^gOIuJ<^21Z3sT6n?l_GDF$)x~1d9Z0AmMn9S-tHy61-r374*;wKp#8$-nz8yQgtAHqaVj}s~h9_KE}ySTXA z+N0w`SHDcH%)OG`#^@f*wWtSmNl zb#+sD`$Q0V0SV`Zbd#(I%MU1U&zYjGW0dRG9&u$tiX*>#>xLx8azA0HE5y*UQeVoi;$NJ@Em zIS~c>_XHtsI4{Y}Ja>G!Q&CZ|dyADdv#YD?_3PKOv$M?d!Slejo2_*vWK{l7GTGT} zAh~6Wfq%%)&c4-M2D}0+(XYJSk9LyR^9^GTfMAfs?ZA@9vVwPbT+0N1--Nysaxcn`>-P3GtfrBH zfsFv0y}kYL{0(yQGN+$Xwe;7oD+(@zQgE;VmEaGE*FrSr_JD@z7n_(HR@T(Sn?oW& zuSYFAU^ln*yM5AUZz}q9YHSn0SNw$n5Khnt&ApX-g})-TS>Y2LrQ%My))iJze- zj~E_NFD`V)@pKoX>qaIgjd`dB*N-pdYuq+5F@Xnb<;U#XL943N;5+|o9j*m6YucQn zQEDnG2o2vF6t1#Y9c?c>3^vCp>%G(YS8fnnQC{xw8AEpSCT}zfbm#RnHPc_@xwyDc z2W#G@bQ?ZD4>jQhudAG{v!$iw>(`g&A0Rq)Ew~sR1~|&ed377<$BqdnU%aQDt|1~W zuB4#Qr<<=sOF_X(6<*~q-cq&(ABW>|-DMXqUOXP54+{(1-q~4QUA5)s>HZHJ`NRi` zP=#EzWUs^RTTQZhIy!A%zn1c>&drg58{FsG#MiCWSTZTM>Ce~By+@N}B$Q}r7>#OE z1c7JpvNaxWgGEI|KBDVljgzJQ8^go9L0GYdHTLb>$F{cPV`Cj?epyc@=7x*li(!jr zcVqNo2?i#=oF0VDz+sV|o``=p$mL!%3Q0>#1LH4zi2%vFl$74GkGZ+lIy#iTx?C~f zSSsG#Q)hpdUukdKT2)`4G)YcFlker*6V5v%+S=OMbb4gpgB>*$^8VTA3(j%M5#q#E zMyVkclpQ=QO>p{N(EZ_|p}xnhZ*M)kN39?E61z*)SDNBh)6n(ynwPjiYz&GGii4c) znVA_WDJi%3I6PnP-Xu|12#XUY7KX9GSsJ%t6|bDauI`TG)0g~bpAr-#uc;Zm5?f(E zDkd&o=s0)s_V8dtbZ2L$#;W0xn?_G%WuBKQQoXZca7?-E8~MqzwFj*}8tLL8G^L9_gaAZM96vgNjsEZtie% z>f!Lm`8r;>zVzK)w_tMxmS}e3l!6{JqvYR}jFMhD?&@9={-=l55no4)(CB2lZsQN` zy=wcmhetfBKou0A z<>ciLF_{M_6bd~$HAR2(W}`(rX>BcB0e19f9`3xZw-W@ceoNAt;3GRbJE!atPw0Q7 zO6wC z_?SAF+g^Luh2mYjrNIK`R z>uo_`AfA__ol_v0*-olRY;5e*Eg!-LM{px>Yyo~~8;hOLhQAV;-peckQL0v*|5>9Q zp*aeL^i&d3Jrx=#eQ7`PF}-kkc^STYDwzj6o;#e7nP~x^Ks(ynZtd=p5EK6v&XPv5 zW%X($*IC=z=6i8fefl&TeCgHK$5+&U&+6#v_R}KQ(bkixkJ{=W=xvavV46}{d3n_; zn*XYuGfpnVPJZOcclX>83VEM_A+6A~gKsk-&xp1!GmKA4a=>7wy-4s{`3oWhW%q0= zlz8I=Nq*ySC~Kv@MFtxLxmIpo&$O$HOYO5QmV*`G?5vSeuQBNzePqVL!QnWRq{9X7 zJ(Qn=TC=yecWV4|kRNHN!oUpr`OIan{VYxiKEBQG@q!NGF}L9L_aD=1Rq7_nyT7MR zPU*ar4*53kLX-VZpPh&mjeELw?W!8SMhrH#wqQST)$FXcfEUU_|rN}3@ zOma+mBnqZhL!|74R#74Gv@)x4xz);F9yMlasa$ke*eRM-pfgEtmpMdwQ=(&3gE-i{ z+Aj@WDGYG<`}^0f%+AUPN_H!yQrVEPW&L~=7WSB$ormXj9iO4M>j7jL^b8DO$Iue+ z7a(!{{p||q6M8b3CPrEik&t99rEPD!=Zfc9Ndt_!SKZSEoo_M3sJ=s0BF!Jw*_IBCYXqnbaBG^IdCrs z7tL0MT*s0SE$hRAfj$T%W*FZQN2iS$5U@&4N|KO}C@3fZA7|_6c&f%)@BI5xD}M+n z1Bh32|M+nuRKes9FB=>7O<2ymN7cNZrZ*q&KQM{O0Y`H1!Vb34+$D{E6SXShg0igp z9;qjo#GvcnZwr+gs94KZNSS~yE}-Z}OklBXW$QZ4HQ$HgYrJ7>bLvx)XI4;q=_ejK zDysYbr#?!C}H1P6x_ zhD9y$e;yx?Ci{U?Y5z>0>WotsK}!gBw{Yb8)8V?&a$(OO?d|OY16qQf*Oq6Ud4`Vm zR;Qzn_+Bvc=1bx{&h}1zR~XnIixq+Th>9KSg+VgxdYbo zo=1>?F1R))!HdCI>{Cp|t5?Y3_|cV6etu zrrbjzL2i#gj|DHuh`{IqTxsHC0Hc^2s^b=N!Ffbsvc(H%Dy!@WA@gxA49UUk? zd~IDr!+agCm66%&=vMn1vmuAOLoBcGh^TPe2{k{+dSH(6H0ft%>%&!x)?x&xNPY$MEs; zp2FM%n8=TZI*5c2DlNK|)m2T`K2yNIb{2awyG>0^T^GM^?d-5(LHlQEY3U)`Tfo~P zQ?atPCSIyip;Lw$&$z0MP^T?^kADZ;4^`YTUGTOmifLszxw-9aZ1@EP1n%9N7pFp~ z$}^JNNUE#oqoQQ&u^}P;hYN9T@gBsQ z0Lwz1Q*=;OQPBXuouL@sH;d@Ij6$UewLRX-)6BF{gKa`5?&09;E6Mz1xImYPhzQg8 zwYfQM@2MT)8ao?XA3I38|Ljc`*Ecr)2eT=6Osf07eardu>FL^7Y0*OuM4CH%D{;rd zHfUc^$N|HXb#D^QwQDlS@lWQh@Mut}nwzITwW`6Bi8CJ)MkZ+GCy6*it?cOQdjhwh zmMW!+qyb#~s`N3x4Agq=b)o9)y*EvsEI57%?3{MUL!+o2aqm;K+sTzy^Qi z>q zfP_=qFM}|kihzjd@6zgOp~h|4%n$SdodK*vdzM@omIO>=3djH8oNqqR#H_)yXQp!_5c{Mu}%K$U~TQ!^6iv zIX-@DVR3d&lMOLBb*q?$&p{Pl;1(!4__&xFMW3GT53@5`_CgG=@R_Q<2IxO zJu;0@UJft)HaEAcvvZB#No{B-5pz~;?L>e7R-Boix#526RcQwYhqgS&fRrn~C?-l6 zw<<0!h6v_1+|khi$ss5hHNU{ezeFN)9}l_8`N>g5cJ=~9QZR3os(V60%PT9} zbk`9!myA$MOt96?IaDA=t7M|PaO|A+k|1zrWtiI$C5$+BhGjfC8e@ zH5_K@pa0umpVGGcTvi6PJ-|zUzX>~nj3En4#yjbAf@#p9nAd|Nmr_nM5)j+uP?nYp z>GB~k<`nVz^~a*3puj*}=EZY(Z(SP3d%>>_o(xOvuM8K0MjY#d*PkjXFek$-EG!^? zg^T_5>lY4ZGt7_hx>+72V+=SUq;W6hzF;^xIl=#USkFK_89QWzf($+ic{{sm0$Vf+ zV6@MGc^zqKXhdn{n^ZZX!6iYiR-jvm+pn&!hSWwq;U1(hoWjET?C$`j%lFNxsygRN z2Xtnn#LOQy?1-6azK)AA2$BthtVnM=Nm@joTwqEL6mnTvZDV61ckW=ILA0^4F||K5 zq$AhS7D`c*41o})Sfo$?T0rK4)Z~h>wY3ExL|Iie`L0FqO1oOTz{?y0fC2$&Er<2m zTOED*^5xdnmK$L-Y90omj`r+p6GKAU11{hrnZb2KIGzNQ2@r;Ab#R{(0CZLPpZUqX z0%$S#3LO=n>C~6FBj9v{M^}iuepwL$5tMpxQIKpit{kwP1n}Fq4t^Uk@fjfg@ z2Yv-^ofTVMQ`7U|Zjgsm|K}ZWqeD{fRu>^lovC(aWzGV0{M=rj&dTWMyoZ)oaxz#; zGgNoR?H1AX@^XO;= zOGJTW78Mm05)v}8&j%}m>{dM$8W+2^$q`6qz)m4&i|by3NN+cE8!DnuDECp|v%&Fe zX}yh%`~kq&x#3kDP+h+ccD0$C>Nip2%EA2P2?_bfxjsDS5CtreZk&I{SRX+iZ^!E1Vij?BT6#r>Ls6* zXl60s1Tl(AX)$qeiShCMcU)@f&6@nMQ6usb5ZKJ>pUK97oB)y;usudtA4u?)A2_Ca zZVC^ThLF%a#$aHp43?NRegFPFD(V^>bAnzmAz?^HMg}CMRcfg71S$M0OSBFA2Q~^w zKiKZxErmV{U`K%Z%gV~am`^99`Rh|FGH5gdIuD#SBxO6{i`?^py~R8$lZ8M$P1)ehLRKZ6Wmzs`FI(a6%WGA>R| z_>(;R>t@s0DP;WO<09v|4NR#uG@O8_H*^l7p&!w|4gf!x&-{dW5~PmMUAkk|IMvpM zt<1XwTWw$fPRYo7!WPVj#zioPn+i)bn0yTHCQKb~FLW0*bwKmTsL6S>7!A)Y4D^h9 zd!UwBoR^2&CPO?^g5Yqc(sOeL#IRsTM$ibdb8tY`APUh0^F|50W%51Sw<_#(63_TC zl4BDSYRO{mUGF);<-nf*yO`)Cc4`RKrP~|?CD4t^ZfJny<-B@Z>J<_cgyn(S>?#q_ zTx|k`EF?38?aT)e6Cl4hL0t*s;w+q;ws1Lc%+=3#tP~WmrKQf^($S|(wX+Kg?%*1K zNT2zfx5;~Y=H}*(j*gNKSsEJ~0h4eu@WEi#p@;{+?>7pMGU`>z)9p!unAXzM+n?{G zfMP(ncL<1&`~Zjs>X@245aGaW0X?6uMRu{|f3mkBKZlofUs(98fl?%p8XNOvRM9nv7Fba#h}q=b}omk5G1C@JZ=`TgHB z^L{v=&dgzSU^e?<-?8Fa*Sc1;s|xfNIg|2pq8}`UzDG0(ercGl zwxD5B>Pb$g3r|&5KS|H{pqd^op0%AZ)m$QOYdfMi-I;oc@ICTGGPp6kM2V)r1(qa& zua`YL=t*&WVe_&0eQTFk?emew=j_QXbeE@_L)>mJM*i@w32f*3;q;IoMMxH?GDb(j zn-K^w!OxJYM4>7pfkLu4O7z2T;2}>ZQB{-Qiruc`=J}rtHQP=Ho>v*8pi%q({<62X z=dnLmW7HuiCbo8bnI{?`$j!~&*MZ`Z1xu;5At*j)-J$j)rm_D_=y}h%mA(oi^$nOt2>#Gk~ z(08BhoPss4yX$j^!|L0+vRq%g9qR~*Z%DyH$L%R`;9=CR7-A!DQ{9J;zV;WrCRT!b z*bF92;qtBdH|f{lAR|%-oINJJ+7|nV0hN@LfDcB6-t?}YUP%T}su7ImiK!U`W8{CQ{5qs+wnJW}<#R?vEu#_<~M4lLyfQyr6 zc}fr0Yx8^#3IFa!vhW(sd;!&4ju16g&F_Qr@G*6EcJ4YI6Vgkk?^25;dfvF`uvtHG zc|D<_$(&|X_ix|$U!`^xf_T+z=k3WzfFq1*3S4X$t$q#B=ex*>SX>J!BO@x(P5z_W zwxb@Bu0LPN8B(GtO4TcPoxiVKmVVJlXEzqzAugZaHx4-0O86O?tGY9^*mgmyekwaCfh^^S1eMps#VHr@#j6k;Qy5 z>D7Rdo{c9^3PJhB#o-55*49#9?Kqc#qHOxj=+HN-(;Tnw0s+_V5F;aF{qmvrk0eT{ zp4Fb~{ame~ko6cvqhjt-K{&<~IMyNa>z|2YRE5}CypCL1qtNT0@q7;(-Na;0+avD! z+s{2NELHn&uc|KcyZdyh5MjP{J(={z!{Ha4u}&B^}%7W(Q;&v@M|J9JA7)w5j`jF3cLvc#?F5H_s>s@%g&o4 zzpXT#o})I`t>Jt;*-ri|Nh;qR8Z&jqzV@$QWjCI|llxp&kZ^_ZnB@l&sUJ)O_&wYe zlQYeqXM$o%ybKw|_V|0pX3^A?I`sGMI7lp;S|}n}euo0TB#0zZuf>iKI#_8vrT+I9 z4XQTk_LlJ3t^|0yPvnzjN|34_vW%1u4tE;QS z&~U$dtp$L?KE1hX^E}k=3cRCD6y{>}yEe)$F2LLCoG4lp>CFDIdPH20}lotBBQ6bSPW0Orjpt;1xL)5 z5gV8E>=^t3b4yu${Y>Su>0Nh)lEyV8a5f(tmmdPLQjXyH&SX(w*T11>dC0*kqE~c1 zt4041oj~8Sq<=W#8Rgj^_w@fvBUUrFC<8JU%vd zpZM=@;-^Ic0&*kob(9?CjFgnqlM(i=t6y2z*w|K0@4W*p?!_`=Us==Z&c^ z)O>vfAqxK2KRPbIFSmIecio@Y)gwhM(tQx<({SoIpCe?^&KC68TWB!Xui^bxcL)Ap z(7&6te?QQ~&`)Qd!eTap?ze+by$CV;H~M3pyY4P%a&e%*t~^tc2+4KV*3N%-$G~XL z{MK&1qq7zm+|&8u$0pAp=XrrmU{KAq&K{Fr?T58>4LscU=tQa1@h>Mgey`Q6L5K z2~NPuvuNx_RzG2?2+7CU2LgT<4zFG<1H(hZM8dh-m~^{1(5unT%FCnX{XTynp+dt? zk>IvHrj*57bL>7ZcK;{QawNS2M7cu7TmiRVLTguJGZh+Q*E>ZC2{;>yv$L}S_gCBC ziRY}WbY*|<0@D;)K)Cq07Dx^W3JEDOXc!rNUu?3rx3haln5Wz0<5_&pvz1z#S$c+s zcNfdf^K)}!;HC4uS#T2HhtpWL$8v5SV8Hd+?s5C+*e92tr7C&f>*%40xw(s=f0sel zVC!q7Os;)_X};l!**gSc35p5|;39)&=I3>^w9d2YUmv#2 zKctCbwRfYxUO9KPw72{BgkykP;3dJz3%q7x%HebS^^z+wEj|5W$-ZvYeOl?Qxsz$S zrk2)!8|$B^KdoM&vL!5cguoqme-sX8zZW7srZ}>w6tb(Mf@(nf`L7~u%Al` zeJs@+ZqZO@+$G9r&f|C&6Y47uK4@TCMeGhlrP~8*u{Q5NPyeVzq2JwZiy41*x7F6x zUel=7-UqG|^zZI6=-6+~57-q*aw?TsYo5!Fg^Z}gVt1R*#AqV!bA9(Jwnj3Lp!cDl z-FMZyzI^0RbOO;3T4?jC)2<5GEy?IS%{iXP+|nV6&g}ze_fJ?cezies^53UhTU%eM zy&9>ioT)tP6{sX?v>$E~1c~LkM|P&KfQ8Ym1>}?cP8}i1yih7Ih>j{;n4EmPhqVgw7O?#U7`l{7#IfJw(6wV{ zTica=DUXBgLr0Gb8mMYF8JRYJPz6a7>U}7=iyV<%dcG6rWlgjEVn%^pg=E z{_y{sH^3rL;OSeXr3~<7d`I+$Pcsw8bdOsFfCN+vcq?}xQ1XzCHkc0_b^QzMT3cIO zZ1p+)wd~v_5<;Z@uv%Yr@g#kH{kb}0G0S0s#wzs+3%&2a$G*ms>(uCL85(x@U!BCn z#DH|)w*-IKmyJAvgrNW5-@td|0JNk9c3v^ErT>6IQd3jG@ofS}D*5jWn3NjFX@(kC z78kFw)^4qKr%Eor`yEdf$sy#ow)y~k`Ne#Aco>v4_|~-lv-rkG$)i?CP}AZqSozp{ z?rjjjjzBON%M%-KbofmY_GQA^uGg#UW;J@%VfFJ1CteM(3rl1=zrot#;y$|;E-a*n z5~DLSv_;XH0j6e^R*LV39UGdl&$w+C+jnOUpk|GoVscevw4;3Kg5S{e%xiO3h2wlsIMMI|e~Rdv;BJf$T*;Ai!qNt6u)Z`Y|{{OtQwJV&(!nWJvc6RUe0LkO(sZ9|DB zh4+9hZ*_}ynJzSgzX&W?b0Ue7p8iWA8YC*D{co|sw@A6g;ft0`D=O4oNURynn;=;b zhrUMxp4NT^K`q!g5W25C)LqVnZ}kLx8V&&KsmM$-0WELTEFZj*-lHH?8m>h^szI4k zUC?GpTQvNm(fZlUQ?Lp`%_PPOOqWQ!A}*Wmnr!*mC`q`-!^)`oCU{UWw_N{!{+UW3 zi-%mKY&*k*A|c=W9QcL)jv$>vrQ}@qM(`uZLPdnAgQq`15^h`mMj8&cF!sxDEmOs- zAP<*uMpOcEKur=QIypJ&&=~b2Hd%d}zwVjBQ7i@zsOP0I2ky3Y5r1d#%+W}}Woe-85qJHX~GB0DG3v1Ax7PBjO`Ie@BP zRwEt}gIbHx<(DcW1h&Q$^VGcZr+bbB5{}9|devx|lKC5DC8g07nH26OERf?j${S`6 zM4Cl<{>+aVJYk<^)M0yg3`4zKub01X7GYmeg)h6(i;=68VX0hG5-+?V^X!MyY(xMx zb8dP#YBbO!{`;K25TDrJgm_aGI-sDHi=9)U^cQFvf}) z7`ki@CZt}d9yU(=Nq6o0)Si4Z4S~XmqzE`ivyaQC=Z2PA@q%eEbm8%5bHyPxwjKuM zP7KS$m06EYxG_&`X(ce}DZt&cp(QWEUHRK3UOZLRiI&_Yey|6^tWnR=sg0l8`uL86 zu(K+W=g-GjQ)uqLVo59d9iA>zN4@TTt{5Zk6TeP3+ElnyE*B( z`f1^Jl3J(4rc_9GK9?Y9tbh7rK#j5J;dUyYqKB&Sg*c=L6!$H07prr*^KpZl&xe?a%nz5JB(UN8DXm?TCBFdF)biV7yO>68+ynTq-y zrC%lT%!FtVs(IrKEgxAt_Vb!As{m>|%YF5D{@)h0^V)_aEVZuibR{9iOaWk8Cc9gQw&S=G~jSnI=>_qscxX31HXIIZ^lIKj0mw_ zH5_DQ^Xf&oV-bZzL>)tGeA+d048n(5)lv%8*aQkB4_+!aA(Cvsn{*4ILvdulrSi9e zxxCau4~ww!#)8+5Tx9rR@fu_=cnt5k-u&k{b5~yxtY1&$BZLhbYs~ZT7Uif->Mw;Q zInW5T4R{=>7e`ecTzJgzy>%>_lqat>PA^V|4K>DbTIunATArFR<9Z)To63S5+wVI1 zaYSl1O4THENp@T7$#OXi8zpypS)Zaf)~9EP(`u81_HHKdYQ&hW)0AnkR?uhIfp;1? zeIzjzZikMH2m-wSM)b46BamT1Huyd29N1J?xY06iq*(2k>TTAv2!fXeLJ{UoImeS( zUj8J>6sRc@p8S%QelB=jUC@&2Dg%ItBAH?IsRz34^Jl@v=OZT|`SLlfcmqlbFf_uT zAF>3D6gx`mX|2vVtO#|{Fi<;7TB{jNvSyAx3`Vatl;@1rN{d#mBC@^SU5P3R@?qTuyV@*B`5^S$Blh*!+NP zn|{DV%TC_Le=loxZl!IlP4bM9Yk2TvL3&xfKvh-}^N zw_i*+wegl#si2S4IeV0fMqew^HjE5o=; zx`;tMHe(p0sj8cV575eM_yucQ*Xz{BR|<|4U7KvVIa%};K*x=_p? z?>=Pwl!8=m&Jw)!V2z6wBnXGU!6Mn<1!TE7O~-zdqgi}JfxqQSzlak3lRsXz$#Iz% zDzB}z(z^f`*#_9P8g2XE@18tU-rO$h7Ure*1-O{*Npi<%fxudFoAL!AN2NQ&v8Nfy z!*mt9WNGnhW+q#unXn?}yZm+%n@X}_(nERtP)|Qw2AtQFT?-(MFpv=gyMjQC#<%iX zN<(9MfI8?UcUK*r1Ml+l5~{^zKa^zFn8Sgz*7@X))bhqmEIrSPN285x2Jj1k;=pzO zqxXQ4I-8W!sWa+u-<_hq|NREwo@m|}u037W^eROx-Ls3!f;f7yMP{-hT#x(|qHFnh z)Jk{;{m>Cj#0kJ-44*`T?r$GR98v!V#KZKlGm(qS&dx68R4K|4y>7CbQIeRBaElCi z-91?dZr8#q)7L%NUuRLe&>GT4QA#=3gpV`eHdj_tdJfiHk?^ASX zQ6yCDdq&txFt1Qh;O9GCE@P>M*=N$P-kubzgAhxvp`r1lngluOOER(7hv7nEMGbMQ zeqI*FDS0I=)*I|>V?5PDB`W01`PTw!sW~{1eopTF%c2lp`P7+itERMgZiL{?*6=&e zzSr_Y+?fV_%$Ckg2lfMAMuwpb6j!EL-8;5ca|1L@Ilg?5yuNJ zG(duWjPXJ{Y4*=<>UR*JA(xOr{K1V)&RUT+ujKP4xFXk#eZKTibRC<>zwg%LU6*=} z8gGN2c zllk~V7ob7WVpXJyXLP>jm^DVGiEc=GllL;^IGmXn)mD<>KWFW7My(W2usjqjVj8Cr zrVxG%8cpnBdA5m65|wa(I;a@p&3e8=!&&)qaX!H^@4)eEAVPgO36X9*#cR_B4OipT zj_0~(W|KK}`+pnvSduoHXJlRVd)l8ay74yV5g5etd``y}SFm0r#X{9fPw9hW7M9So z?T{3_**y21wR-_MtbA%{Xedjk@8tlsu!6(OB2_3K@Xw)N1X*d76Ymhy>BU*i(yGT; z{q`(XSj+@u(-3r-M;*Lx44m@M>nnF&xzux)i+Eb=jG`AAG5L%!$IVOgv()AH*@?8< zOcqY^*0F=_cu09B`P-W_O>*N)0D|DY8D#mx<&P>^yoZBtX&YIEa+K@lA3>KZZYtH! ziw$VP|{P;)dGlEGvsM)Ka7-glV}DdIsos(3Lp17Q0ylt)Icc z2a*C1Q#ryuC!iU_#Lk}B5|PS6nKk<1*JnKo@{!d)e6k@VD3Hkzb`(8kI00E-scFR% z^T#wqOjn_2;*M_H$X$475|RcCIL)&LuB-eyY{MZ-41aHnAGy2_%YB#o*0DwZC%Zgv zmgDtG)rS9cNWGtdQqK=tSOJ}>0NAs!P5|4ab}>gUQSraafA?#EFajJpIj^YYa4INW zjtbsTDMWuxPt~MhH6l)6jXa_m3ntjB4N{J;+dhSp2+pDdCdX$QWhCgy* z*J@qV&vonbo0^(hT3$4_u|(iwzj*ON#vJR5VdV741O&NSKZ1dyC!vM#(9m53`*!Qs z5X!3x>S>f_SXMzX(d0ApK}T3b8wRC1RjNpvd7;DK`_Q z>TO+h&meUa@4F@xJF1l*izCl8VAV~Vji7tP{_(+BB}4tQvCQ6{wPhq#*M%I&$oXc z79HN}ovFOA%Kp2Tyn2YDKgK2)N!i%8mHyE0a0R6`NVbsy?w6}xiKN`JWz+qGyu)eA zx`4R`9>IF^xJ=IA63?t*EjqOy_E}HHW!@wzoLelO zaA*$_&D~B>qHM3ia2kefFoEDxzpNg7QV9A9I>PBUMj-@Gn8}@EvQB9LzOOsDq|Iah zvFj8?j4to!SUdM27g3&+RN&895=ePH!o`)3ZbJX*FYRww!7I(Z{Oq%YoU*z5%N3Cz zuz51sMx5O+T+s)z&Dduv4X|Qj{QRKw2aU;)m$Yh(ljaS&m`wy78y=8+T0;BimqH1{ zuxc_+g_SiKydvb_Mwim@DtrUd&n2_#r_saBMqxN__-5cS)^-4DXBoUDUa9$9fBf?& z##kdc+ZQwJXhU+p!q28_(RR>pHC!e;S>1~Dfd?8n==SHu#l_f|a>7q8Zf?>@Z5lG> zP#8%0nVDn~c;BqtTwGkTr>oRMJO2o>UP<62#ZTPV%NJNqaWxzqSA1fQb?pubpw!(* zg2EWM*V$`u-h4bZfx&!)1LI5g?Ma0?s~a1+oVu_IJi=b?M&LU0lL72cBK%5(R6f!S zTPI`F$S~R7%j^Co?;OgXrB{P-Zjlr`V8p+7_usyKi;Lq=t7&(@LpB{qpzQDO2Sw|q z&v&aPEp2Vg(4?dd&Y`$bt2QfJm)FE-*@brwI_)@$1(|(h3V3DK9?V05s3U<%GDBKO zeQsu2jde-`{O1Y+4bz~=nLp+qI7cSj&}%7hPB*NKQf)MysYEcjIiej^qWoD(x0Fd> zO&1CXRzAZ|uR|Qx0GW#``q$*8(&uj#0IRaG;S8(D$V4nUeE;B>wdhd#q&}tB>3?eh zmI}~AO=djOhL6-~Arpc)L2{;Z-Lt34<75i&c!Z)yXtamOo^1qcH0MbGsE1B;^|dib zLA18In&R;IbvOoHSZAL4W7#l4&7}OQ=GF2tUT`0hZgW2d=tY24n~w68b}hRg?5G#- zka%Vv0@C)=>-8RT=k`B~)_JRd2@kqI&4Yj7vy1}Iaac!fU!9z|E|ot|tw+RmwU$>S zl?~w+DH^j%mDNrm_9Ze)d#TURyGyDit2+|i^D=c|ApA={FUd@fQNi@Gge-poC3oqw zIRbR~8b{?WF)9&@l;6>dq`&3v=a2Qkazg&XQyED=ylM8kJgU$rOV0Fouq)%ByYsJK zzfR1`r=nvK#IO70UYO(1J0M2$Y&}9631mIL%r?`S*!B)X#j>5Uk?Td4;2?XN#$@ME zjaIIraYY!zlVC0~Mghr>f5Z4oY8;$B=zjfUV*Mw#b!vQW4oI$xL^O*cwQd=ach6K+ zbyWi{ZfzE%;OM<6BdG&Gn}NZK*KJI{S;NZfc~;gd zm+okOkq!5P=VoEyrMzcY7b3buAxbRYlw-qSe~`it&?#{Y%TzEROl@_>;-za|WtxTq z`t-Lvd%=>EY2hFL#Ria)E}AR4$_|YcGgm2a*tv5*9qT@byu7AV!3S;cOVJ`X`AP0% z0lLg(5i{@HZJ$6uz7(hlNj#wPo`^?JtaV%^HavjY+RRaKtanw#G5Fhh&VL#X7g`Qg zP6N0H;hB2LHuD~C?jzQBMtysVvO_26Zt|v4_$&&BL2?Lc$#kR;e*_jp=3&CR?aF^M zG#p!yGcKLjh{8pWaFWKnFoZFl>g<0glR-8QHeZuNMsOmt>zo1Y4e${MKpJ zE`Z!-=k={KWU1m52Y8ltsQm``t(mf3SyIX=DajzCiW-O`llZ$hBlwX`)}Q{mj- zSogiQ%F$tt#SwLk7LCU^r{>=t>r9)HY)*`(CnK3T$DVrxLkC^zvDLlv6n?#_Caq9C-O&yg>1)`&_9*m&uk$i77ljttEE{){8WXW=Ed=WFR_0 zxxYe|5;Aa?CtiM=>qed$f3?I5y{mP+noC7Zk`e#b_MvWkSRkX$5e0fiuF zq0IO&bqay&)B_75AUTL^9z*PGs#P*H*i02#mX%5ZPP8|GbIF=+DP5tnN}HHL-XX84 z8y~6pY~9|R(#hG;4Go&m_nhQ*ka)VzfN9B-v`UQ7eROiSUyK7Wkd7gAyDRmcWaB{M zahN11CUNw*Nof4(knhqNX)0A%BN3!9ZQrotg36ic8NPQ#$9~*!~pe~RRg)J;)OOmqbo>7U-3l4d& zf6nfhV2LR~Hp#EDZp}Co*%Npa8(dPCN z=W|AumTx${LKHO?Pqu`J%;Pwb>-(5|d6<;?-@0%i7M*5O+8|c66dhF#iG2C`mv4uh z6O@1$My91&NUFF@yjZW20Pirdu)wccNGZ9qIh3OIN~Tp(B3pUKX4cKAuUN??x!ZL%ddKLaiAh74jKt=$aNd(^i2G0M#uCu%(Q8-v&q4?i~4Y( zq1UY8x3ID3dt2MXPczl0-U6_M704SbQI{nroF%F7Sp(whb1fgb3e)BOCm1|GPfS4J z0TegB9p4{#3eaCU&JDO&JUBQ2Ey(YIH;2t?tn_3S#%&At^k!)Z%3J(nC{QrHIA&j| z$CWIkd+~UnyzKmOI!8JJL<2DRNavBuW5{dXMLd`QB1uAynq;Atny! zd%=o-96x}d!aMgt)}!o+BJp?i_#?hj4-!ilUna-wziiUas?GA(t^5ISPd}Wd=1I&nz`yc@Q!Eb7=y{ zQD$s1610&2da#ps@_Rx9fz&hGd~ZV~C2Xc!63p>aif1(8{4#Yu>~Rf1Jwg*Hfli}l zW}an3Y>J`|$a;HfcmryHM0tw)B3_sRHgDD;sKx}{e#NO*WWTW2J|iwR2;r#2TAas$ zJTp|rTiawGPqPHzDC%NDk`M_8)C-ecJZF;ZV0%NqvMNxqAGG2TXS81L#{nf%5L>A9 zil4uUlnM9FfuV!;bNbN4La20wHHWTvR6faqPt4ab=arct{A#9--=)syjH-6$vcv3* z@(xW^9L<6OJ(7D2CRQQ)xoW_38b?G_oGHG9Gm|(XJT7LdkK2OG z_~S}y-;ocgc0c`bl|au^#7@ZJmXABDy33&zu28N?7XGeMhLYlhWemg5ebWE3!cHJ5Ne;GOqd3jAmV4= z3=HT*^M#=v775ZIgk|!WCfwy?Sp=u72psukT>4WzR-!O%W-1f7gN#f+;2(Qu=0-+F5)vV6 z$7}6AZaZaIy%Xv)HHA&S3~zT#l0n2VZj@^yQeKA>MShr*5K-UKppr~egL zW3{%ui%SCxUAyYstfQ{i6ib9v^0Q&}C)OvO7()|$=^r=}F#tU4Fgo^Z%TcxlB=nk! znl59WBq`47dAphp8DhFi1{uO4wldS*Vb$_n%`0MeA!Lb#hA^=i)TYMHJ}$`JlE;LV zR=Us!3kG!ugH{|@%xsWM9^-R%GftyOkLDS6gXniV>6Dg=rrL` zDdE`&9q-)`i3mwGq;B-$;y_y(r3sZ?bNJLB6b-X>HGNZJxtdI2B@Q7cXMYYGPspW^_-I3xBj!3kSct?MjLq{RY*rHJT4>?mg|OS(oOfC z90`Jz+S5G#n$h0+cL}j*`?^gyQnFq=4C90h zG~I@y9$HVLBq%TxB9T>6cl3HJbcnN?86nJQ_wW*16Au}Q-F=9p!V8VWYnva|2dU5a zxpn1M4HWWS;}8Io$XeqV3DI7>Ate0*zInk5vIIk`mHGv#l-fyV4Fs*G%GwO;Qb4l@B0 zesL@qO1%OU=|Ef3+5O|-)%K`agN?tIZ^RP_q%}MkDn!!<0e25pOUiamj(ZcJCc=d* zQLoEt`7>4G#h&B^{AilCjX^TX@_jzp`_cwHJ(`xhs*^!4BXq)QuJ*(#74tP zqyr7DyPYpT;XC%%``mYohc15+pws{D*C$Oc9|}Vcg^hF&ou#FwN?YU8zeS#9l+WK1 zt{SyyX*jN1Vz+AYnP30*-tcMxy}_U!7sZT#j&ddc%)jWz&KGu(as3*rrbVkJc&;Lg zV#2QqeT1ocf?!qnPOShkbe_u@@!s$rt>}5wS7p%ATFbG7UCvkQUz{^l^WhUeVXGaywUV=(C&e|w!^^o496QejnfSyd2O@16m zU4riBgAl}RoQu`=-q69--Q1dnEH?Pr_!&oB6%|1@hpb%SiRGs#NfBsawKL^KhtVxz z1`bTwP)+Ueo0dRe`iM)p-<-w2*i}aD`c#so=Jun=QQl9tjR!dT`a0U0K8H-AQCMlT zqY)(Mrjoas@nM?;=cw2)<#EgL1S>>aP=A55COcPHEE`Z4qgQ=ggcvofqF&FbN($>hAbjh`|cB@X8K#_U@s3pYi|ATZUK-2M) zqRV4WJ}jquMjPR}%)5VmzAv|on!BA77_Tw2|L&r;7_|=;iDKe= z9*zP*VN*|TsUbXd6IOsLbyr%d(GWNwbP^)Y6?*6I3oa41IyEs|M%F+p@~IUM!n5wl zo#vmwfuhuC*^uxC%Ig}1?pn^3g9C5zr-+E4Yb!RBxG*@jEpyi*MHqk91C_+-ZRWeX z;Ilt>r*igD5mtQQm7FR+I5etQdE#5qgN`z^eF{%*HtUa>)GjJ5Ej7MB$q@29AQ21n zmy+tm=3A);e0Zzdw$sXgDxfOun#$T*qfUPx85tQ{+ud?~SNd+oS?I^_zgC8;=R0D$ z?4!fhnm$+Z1-G}F*+yccR!zNilO-))?1^dhaikG!?V-#S%*Z5)qJS0L2v&nl@nSRt zbC{4e*piF{TlT#Ue&;*>u4Ic(xo%8+=N+tr4 zC=I_B8DA~-^>waM`2ytN9@YSVqwAPQjr%IrTlQoKzIG_4tcBmZYIRQ#c5xq(Y(l7y z8|+L7qSTV+z(@FF^Nw#}X%5(XnvvIrkpK(Ac`JDRpXU5)C>&8W%Jbbz8r7tfpv#_wJ z&D_wS6e&cjzrCO6nE&~l(28{9bZX#KR!;nlpYM@rH-5TB@dR=r zjhoE6No(F0Bb|hhaZZ7MRiSae*m|$I&GOv>L>~jX=9>$CS&~3R@umg+v#M#Zbk$;} zPoUGx3Bh7=6qK#r+4#lF*#r~F0x8Oq@fworG~#c%DdC6A@@cbwr4Q}01P_kJLPST0 zMumc=1ebUX0^O#@xy8x7hTt|qI^HvZk*Yy zdR5H|(z3tX8->DmpW?`FUYl{0$DU?J{bnT4Z#3l~IX!)}&-NjLYS)#woIg*2aJDjT z5Id>I=CHiY?u3K+1)Zbbo7t9mZ?W8!~p^6h71Uq>*rMtM&&bx{rFK%_HCQ&z*pBcKZ>LHUZm zm;T;F(vU>=!t2c>9tURMnI>0_S5|G}Qb*IG0}BJM*C7YRAeVbqNCUdAEPB``>CPFa z&-B#HZUODp)q4u!T2nUb-eS`Ea?QIgE9|$XB6x}eW3N718S;Eo#K$+XOcUqslKWck zwpm&A@D)ZL5M2`hMiL%+hkkxS&|m*fzVF`Vw}1aUB4Yi0a|a}V$id)@6UFYm`(sR{ z$r&HxA%EfT7HW|wiWy%cDH_WW0LJV+)6h$6s z<^(FGvX9Xj5px9xt08Pa4J5HpFrF=Q6avTKU7s6IrqS9mr@42$?2wDYkX8rTn5S=q zvjK4~9;bo6&>nxw__`hGS>Ea*{#Hlwo6w%niI20$l3ZP&6B5JefV+z(_7|a-1I#&1 zF16U-h~}Dga(DvER|I-hz$M3#awkxU2m+-8_1%sf#em4^H*TP~@cz?J3^J%6$mA0Q zZSMdGk>EB7A1K$_6=1@t%abo)V;lV5wif*EJDa2p0S--HWdntQe@w7iFh%YQNEf*p z>eKch(9+bDw$%j+M+skzcMaW)2++Cb{Dn@?;Y`V&mzni|?b+Da_)){n?cv)o^z$4I zv2ZdU<62faW$@F;B1uODRDU^f;rUO3gjGjON?@d03G}rvxnIaklGo+aC5$ARKANgE zY#aQ#ofkx9Z+^Y~FaiV=Fe=&noTfdESMTZY{N{&a{ zVc%OlgTxxYY5M;hM%fY`PwDgzg&~g|xlq_|UJngoCIorhcZZ_574m2Sgv_^_P8h~Y zBIfd`grlpty?q3X4UyT|3EMV)l+zQ8!mDm1%PTi@bd!56)Ep}?@$LBWgA5Zn>ij4O)o8H6?$oaY6(818^$*Ely0QpQN$I! zX=&&0u2=t#^T^xP@9Ld0(v1FoQ=x6qAR{P}7G-o>fPQt0c zT_$6mDpJ*BTPpNmORSUrgq7m}KhvEz!s8hk8MrQj1)ZIO3=&NR)#8;+Ei31z)DeJ7 z5i?&xJVfyvo>@E-&m|Ze#7Tg)6?80^zUwby@J1l9f-k19y&3~ zQLn{UPRN2&??h7*xZAkqIFN(aC7XWrNT4^6vHAp`i5@fC4VYF;Gm)9A^S1e2sdaIX z+^~CYTE;Q$`?2S4-|)WP%XqZi34}||@jc%Prsg(wbP9ZO$GMeSJ%63>FK{UI)#w@T zm@OQ(CGuRdQA&e|(LKmA*=kTR-yV*S0Mbi|chbeOY)uRSUQCz>sz;tytt+%Cn)X-{ zEFzOFFKY7_4Jx#^`o4&~`)vTkWyW7r`cL~q7(TkZ=bMim9Ygyb1AUrVqeP2R>bSq1 zXwW5J+U?NeH45{N@pb&CP%*-|)9(<4DQKod_792_gV8v9p3KoKK4Y&{k0L{}G<-DF z9Py`pHK12GKkw&S&suo&y>7x-rtLKz=Ao`cx!1OZ%Veh*MyQEA`H!cDOrC@Jp8Ki~ z+JDKSOwbejPnZLQ{1<+n=0zr6-U#_Et$X|P&*P`YekjqhN6*@5u|B)B-}~&r)CFVe zdfi<5Ht=StHbnH~oT;{QdOxN3+Pjj&Rl@gtR)^1VsX6e@SB(G+as4Y3yNmvnQp_V| zfKQ%hOE+YA>|D16p8Zc z>Thg6$d;5*Hw%kv`Z)+>$KjuC$2LPm1vhZ>q^73IS2;*qVW7Hd2V+o>s)oktf_dUw zgB*IrbO$i|%Ce8i%;CStv4f0Xz%c?tFO!>vCkGsw-DE)dIzBlF*n>}D$%#{WILlJjYMJOV-T>NPq(y8HuXqhOVw-SH2xtOiD>9 z)3$%5pQFbB9RI{DClprxCMmyeva(i>|LUMZe!HubI@f zi-KlIW;j08^Yez^*eTTWjPJ@Z)NNuHRE?Gcik)vT^y^GN^}#9%K7IPj9&|Nwe|K}- zx}Ez!^B8N?+*aRW**p0GOy6Nwj>eUGFOJEz6u?-&lP0wMy4zOuItRzlD?;#bJt8M- z`khGpO)x^Y?p`0zUL&c)`kqGicElbEA}yU|ub0GQqajBjtu!&r=X*ZSIoXx*`?H=d zP_SMH;(8cb7rF>H-cExC0T|;ndijA;**P_orw`wgtftcJ*TM&)H|IOM)HOGHuZ4Y9 zXeICfZLF8O&BDx_KqcI6zb(h&+%{`MLid4UTv|v{5AHPb{x{Jto>RY1pez>6I|d^t z0ecl?U>y=0quUI7M678aS7#bIR z;;6yC)(%UsKf=eq?a=-v5#Vin-hX@9CH?U@56G%jt9q$*nbV$d?xP50YXqAms3ppZJmMk;22r9d7rX8botv!A{}J2(qP(1}1hY40 z{>u(=i+QSSA7jG-pIws1j2GV`?W%cO-g%!&&nGWxOu#kvnXuAC#!q0FAxjg`Nq+Ri zAx(?P(l+w{rvHSiki<_*lN39=;2TYUdQ$`A?US#&Zq~$X1|+;Tyjc%oM>$a!P3|rr zqEOZ}-V^#hFIDI!-)p%UdIsPkm=OhHCiQf`Eq1`AAc)J#IuIFZY;@Oec!am6oPzR1 z4xW^hY#slEK4UcBMG)!pQ9?ruddIP3H#D(Ihyiu)&eNUtkQKsl%I4R9S85ta1e)Ey z)?cz;yWjcvWfh3i6$Zv)UxOi11Tevf{UrH@_`+`Fw7qwoT6M(LRlY*Qj%5 zh_EzZIua956r2CI7C`iRC(F6BLc5AECN~xqZo~H{)m-;peb8nvz+6~0&MB%~ z(;z)&go~c5eR@evC#d|{qx_N%r%$1p+QOo9vR~9{^DxIi3G6-`+&b@)&nVOZ7`19Z z#xTUGCRykOo{sW?fz|%2)9rydm_jzGQ+tAVp(jzSW$I@_#<-tvCBp(7Wmi$;#GO^6 zyBYDU8a59vQt)6z^>M0s3~=3a89oyX2%%Rnpi;Mk!F&kG^_hHb?GA-mK8pt;g4wl_ zg*+iRwpSmIg76(>_(3OR4OAu%J|HF`f#-R!;4*?&$?~>6a`H6k=|>J+$&9DivE_{i zR%6+uEJBRYJwRGWX4Rg+;pKXdP~~y9v|YdCKkn+3RuWT~V`KbC4QOq=<)$zl1N+Zr2-xqFOjDmM-GZqiR8q z7fcxIK7p&BJ&LsjquW3h`F?Wv>dhxITTKjm)4+`?#k{i&?U!Y5Bm2=6?>VqeYn%i2 z!K9F(Lrp;+HJBf0@mZk4cPG}TJpAkln5o;$+<lbr!3;(QlM40 zfmGKNup8IdZkCFgkqMjao`hvLlksyF`%d<^yI{wvQ(~70(=bTw4@uo^^aZHQ=k5H|6G0G-Ult zt7RAJJ|du`8cX_x92|NUS^Mv?{ls4^yUXBs<_*O0)7ck5i?8^2uyO%Vncs5B9oKr^ znRxl_OJ1KmzBVLz1nGBl7JLw7^EAYf)-q_RumAm4j%sKqS|Xj9o(4>Q8ySmQ^_3Yr z^7a?h=FsLNu$orfoIg<{k)C4Vy;Tjrj(wQ55*=2pDmG#h9U8EFko5qEr6>2tozW=1kg|#%#i~d=OQENnlfWio_70XDr zmnn4L@K1Big?iK_D)TXB^8CI>+bXd32nE99Eu2(cpx}a`UtO=zP$kNA9k?L^G4abx zDhRU)%eOXAGlwpBd;I2wh~13iqjdi}TdZkAy#!72P+#X<38biLxmOh-yZROSyi9J# z#5JdQXmMl~?9M#DWrHcOAe}@MZFAX}TB_eK&c_1M7px_5!301@?mC9Lz1*r+id=(w z?`yY<bflp?szoT1c zX|(_2iH~nsC8K8fZ})tyBQ<7nJYPyzC#%e}xDmr-G+S`-s!Tn$l1GNgHE|LvKU6d^ z3O#ecOjk1|)GSIZdYDVGSMS{aU+sNmRMdME?GQ>xw{)r?pwdVPLk%FPfPkc=fV8x9 zO1FYYgOsFnN(m|mNQ-oLhm`NkeevmiecvAIuIoZ!=0CqUvClbs%idCLV3Q1wQPmLM z5PL$1<%SX{Fw%|?H$6UGFTG>Xcv9ZQn8Kf@^<20zn1n-g_roL>!kOnM<@|M+!fbejsjjRh3(;MNe9lzw>o?Cy09ea5R)7mj)H*mI|>brYuy8Z`Uz4G|=N z`GqW=hh0_Slm+0@DLh`Qrl|Sb>gY!OiJ*M4?GR`99-(kzJ$^(^WE!KCPX&<-!t_{9 zGli+?-kagF_jrTmFa90oE|)aPUjZZ*L%8iz%#$G5O*K@^eG~na)>#@#bobB^;lY#X zt6tc@KiaMQAAskUgUr!u#7tnZb6mmb`GdD@>A223M0-9_fBM=n!@aprW09i2pNMtj z)2O`Nuw-tK{ZPUSMMCn8k~rdHP3lFMY#+%LhJN?{p_dIfbk1DdK`q1bSbh7e$?kAq z=gv(XLQmD8QL5jQ#gTeHr)|q2l`hE2?cNJ$IwL+9kYXe=Tj|JPBIBrfUX|?kcE8n4 zpG2PW(yjQVL)^btd}l2b$u6|R`3!x~eGvU58R2U|W}JX78S*1&oMBpomfTph2y>jc z;ua1)v&-*P`B(K1PN&F?xbYI&86quFFOiauQgvOs4uhIWQ&#+nMv+dP^fwk z^dviFpwwre*VfH$MT?H(7#9Kk;~}r{c}DI!Ws&8KjEq3EH#9WVUGX6`^#V;lQ&%UW zGM>ebFJIttHL2f?0l^%^hg1t_OIo*cIbvX^yGr?sWxGzMA#AUL(i5X7a(6J@g3PYR zM5O7@c(B<6#G502=5X!3AKs-4_$VR<=Bo}K@`;>(@3X%Uq>|9yn@8NNyYoHuB9J~? zAas^5G@kuz4FeIbnj^rX8J(2#CN8;mDcY5gZQy5uf!kmWYpz+eOHGadubOJFeW#Qftn5UxcU``x z_I|p-L=&{o%zn1Xd`6Oo!>PQHf6bo$+opU3Svc?bP2UaZW6Eobxe@l$DyURE*c$>4 z7^Uud4h2OFXM*~=_JyXUXduhgXA zB*|nEq(UGIbKbzJaf*hIT4T{U1~`OF7!rdd@)>!l+Q*`@1!7*VFaoGpX?Tn;cVtDQ zNc~Kw7uN@7sc@tuiQMEe(}vBzc07W#Dz)0x{tJg|^_xw>H&2xixDMKT#)d%j^tSAS zpB(YgHw2=PfcuPNR8GFrSnL{&ICN|gqs$=UYm=|^5@m_JkteUE=+bMyKsiYIY{F3RV z+1c|zMPaZqQ`3A{4BoeEPG*~Z^_1!7C*(>T0%N5}osFmet)HVSsNjMnwT!eI%YqYCb`3-7m)W+|#lJ)s}H z^=aO0hMm!AWW2Zl1M+Jnnte|$bfM&(UejPnS)kk<1arwm?ltovXTcas43EURS5906 zvCf>HZY>5yG z-L-XIUj0VX%55kt$h@N0ak3;Z{8IRf-Edip(B{R@v}LKDZ@qc1P;d`e8lHVNF9r&i zv!l(c0T>8+qAsX@soWsnq)b_e$at_J#Vi+%6JzIFPj$17uZV$}$4=zAA)TeEK~kus z-4Lc?<`*)xeA1$gwa}r>tE85)pJ-{Wyrd8DVj#(qe>2Gcd+gntC4R@2zmfJ`Fspz+ zFLVbfo)G){-n!+Ft0rgDntXp)Cr|TJmF7fYRr+C(!Qs(V^Glv;m*%X}5lzEzkHg<; z<*K)yVsmk^vikczWQhrkiCJiTP%ROejLY&(wDs`peEvhcB`M48*V|Rc^7PagU2-#X zuk> zlv@bL!A*pYC9u{&rZVh)an_|@;J%Zcr&RL=)*^je{fS; zVuoc|pO|=MbKrd|9rYESt~FmGQcZ}Ym$w_f+RoD>%8Th&Dm63lh{Gm0vWI7&xMCYR zbwxu$qo%wgg8 z&|Ebc&`HTGh(sw{iH&VNVzI#~cpCfNykoBHir2U;x1Mp=E=9Rp8h6^w@ab?KTQf`X zI)i{(Nm8Nou)v)*!l`oFXQbl;&>dBNg}x6()pwNKXVo4;{IBm@I(J=OOW&Zxjm1Cp z4ujrj@@so8)Q=|)^cxMLbW!pSdi7NabMe`_c#5pC`5Ih$y}t$qo){QJk>KOwSEyKg zA%YdZdNSd5|K?}|6Z~gbyjN1EJ2A^qF)SMrZRW{%Rv=5T-E!e=)|?lhb(v82wW z@{^|8sg7*2);Qh9sSACN1~n`lT$bvhI}^iN!G&I@gf4hAcG+(to;&=hohr}y82LNh z|H&uqqXr!NhoPxyRCR4-L9!0J8&`PPm9^7jj&wIYB&ApZVtud#(>X6{+h*G+{ zs_BH=2UTO^IUm(aQ$^%`*TtA*U52|hZ|$->X*YC?wLD9|4yl;eZ=5hTv6Io=q5j48 z>;bW2_CQb4J*)~+DW9#$(dl^S#pcM-F4KkwxlhB=iFOLD;s{S(IpYaUcZ(1fJc;GH zXS}OTPpwBi#j4Kt;)14Ld+ep_`c`Kfj#lp<7Sn$1*QI95n@dLVc@-~1C&MgvJ+$%} zdS5Y8q?UdGlf)7-br01K*9+A;`-)@!`)OglT4Vet zsZe&ZM-?)yYu~d3m&*T2(wDYAI<}GcCx&drW$7?(SV`=OWvGZc)zy)cD0spw^saM% z0r&K~K8?nvJlnO`o5-t?KxA@YS=kKSy4Kg7_K+WLr z<1LR#mt?i*J?s81Q>wRPlv)A?yyV18l6%)<^I!cCOsNIhejelH)-n{Deq`5y8r00@SZCV2m-#gQyE!59cXl-BsII^{8L zP@91_Mq*X*BOIE)sNrg1i)V+?-eE+X6T{S&Pl_H&xZM&MAw{BYQOL{7>xjsVjE}z_ zU4>rk>Psuh`+*Wt3rT2*eUSW9mgn*WqN5jxES(hVj_$L^kb z7s9#keZ<8=`vjkF6uX8ce?Br0=su@fYD(i1<)K}p<;FN;@l(kEDSU_ett{)nS&`8F z?6;e1-{$IL*1E_j!d}=&5cg-vGFjxh-iCU@% zr9&Cy*vSr$i0=-*939hJye{=mNOf|-H|*-ZZc>URi-R7vjTP0w?_?bG+iBBFs`6Yk zJ$~{eb}Bux08gH`Dd6d0jq|HGUUGE;)bN7V!rDPUtA8$6oin`LVP}-I6vChfosFPv$Q$#ZvK*DVmO^=XZbYn$74ea z#?{b>2v7T#kbgFsS`r!S|Gcc-`UkxtmbviZp5Y(KU`E^jxv1RaD^^?URNxPB^lovm zb$t>G1e&9x9t{nRdw=ZJi9J2%C0`duvr^zc(~}N=xVpKU{~~lFh7~Tg5gWTSH<7^x zQF)y;^)^m#W5(lr3pawan2s@>h9jb<$|;{x9{u|L+p6WLt8;zP{H>)FuJdoCzfq2A zt2b(f6kPW5?O$BEw9n#&wM5MVx3`B)w|c{B`!D5RFpH)=zGWSInHa-pqiW>4PHyl_ z7d!mwb>lt83LcvZF~LbI2@NfYvX^#azLHHn-05K?--UI}LY~un!~9v!6bDpECp(!f zEf1o*2AjtE`)8JyacuamT`x`;44Sy_V^gZ7KRNu)BDQE_^hK7|=$EB4tNS_Nl5`(L zg#-`XA_@QD;DZm7XE&0@WvZq1>RirHYfdW}<+qp2MD7XR08-)b2AJLF*Yf`=Wu9hJJe<&DD4^!LY%V+R(U@zsmsR|hKtJi67j_4P`* zzaTIG%AAVv4S#=sV6{`pRjaD00e(?Ss7AYZIQMfpD@RuA%UdN_H%AIr_h-Xnx*T#; zDFqgiB6H+3ZR})eISStXbJ$Nq3G|f2ig4~Xx$xgC&WlRTYSWFz;-WpEu{D}nm4P)8 zUnh@4X1Zu| z-5OiK2tU8*MCXm zANB91Qsdxw$^*d(TunhnM(c8&bDYsGA>AuS>>oWg&%M%IzIaTzm~C^GQpPbAY|1B#!+lFF{?aGxa0|NtYcv?(M=|FsB z2uT(Y=rN!_IHqileT)^p&O4Iv)~PPL4_RB1 zSuygwoR8`qyVwr?G;Ssi*=Sy)nU!LwCTF!e`%h)C8ACc^uU*I8U)Opxq+Gm^z7`4t zi-sRmdrvF2p|1gE{14Vgi493ZWr1t#tx~RWInRSDA0H+o7sVsxU#*Yu#G*6|4NFu0 zwzanx6&6~bWJx5U*sZE2sj_lT%kv6tIdD#1&sA+$ACh@I^q#XA9-74pT}K#FAyE@V zV*M-G=H0&--b9Le$J)v8RJbT_1&73L(1(An;A2^(qCP*=vlarHRCv8>ivL6%dfSS1 zYoOeNjoGSvrwL6osoOoXxR^U&5g{pl^62_?B8E&azN*5Q)avY9?5kHbB?WGou@2Y2 zDBje&BP?sBfim$NFTbhx{P}Z}rr5wC=8~*SQOQDE$1kkYJQYV&b#*>(D`Q9q*pkf_ zX~jlGqtah#o`38{~?}|VBdbEoLq3Maa-&Cir>mBoLxA6N6 z)DnHIPhG&k%Qbg_3m1GH(u32v^rQJ6oT>KHtY9>Q+`W7ExVd{qN2yuh;suiYZt-d9 zMatl^`EUvSY6La90^%3+HYH$t1eTD-F{3P!H`hn4j z@gwKgT>I*=2QCwzu^~i9;=c3JQl^ZTe^hC>kIS;K*?N3rU-0SO{AQ1X4(n4M>r5u( z8;vmC#1cQO6YFN-(i(>SEOmaJ^Udx3%0UhmeR#Tzq-UQpVux>BZqiw+t?>nke- zBqSKDnt9-oj`L!w>&CeX(i!P(dv`-FmQ&YqyjQb*$yRG$@-)-6JhpSaeb$||&u=*B z{jN+f3zb$8#pl0e7H=FN0$8LJPOdWzprn5`V82%pONjbLlv`h=I;pXo^|-Fv!~Lq$ z-gVq|)JG>#7U1)^==*n0%C-X z=4g5dF%#o;QDxRx4Gj&wTBl4`?Z)I60Y>cV^}}W%B=xS_X)1GNm?7<yvh$B z_K5zS|08>0*A=|Z9e7f%`YyY_nQEBmsbD2hlt6Q5Esq2CqvON#d7%QIRCuJxeAf05 z@xp@E*U4Dffk>kiz404b^>zFlmaiM!307a&O8go8XjgGeOPF}r8{+g9_gAjXOVVz} zhv||G$1Qk4(MJ+>3F+I)NqCrQr+zE<(ru-h`0*J7@e@;_oH=%W{}3>*NO6_3Q4lYB!5K9QCkcVhEnK zsA1WwmBdXMb%;rVACzWWLYr7;X@6H@r!*r9@r+1)FI0S|Lv5r-7Y6Nsj{`w^Ei2^d@kSDN4oE86r!>P``zoWlc=4Oce6#Cl{>4Ss+LqibtB1*U}Aiej>)vHt7Eq6A8CXRn=Ua_)S zX6>-C89X@(ac<@@b`px?uP{ce&K>TBcP!DJEmGOfI__-vA^E0SGpu$cJ9AH(HtMao&3^XSQ= zli9uO*%D>f{#-F!7K&Bh!mu>|S-NhWpJANkM$#Bq_a|)sI4%l@!^FLVet;}1CpUMa zJ!hY1B7;9tjJ*Cs3_vVU0CK)>KO@zo+rUI##uU}immbMps(Tpiu~}ai9m{sMUIsbN zwKRUd{T`0zjco1qF38L>72d-tyQa#?XZJX$h%v_(h95j|!CLM(J*AB#-jt3-3YXhP*iUjuLZ5kpvosAki zbf#Wv4KwTWu??tV&~?t?YU^PXcw6@;^&b0WX7lbI9hKNv(3?nzRRy zAa9A|vLyKM>VDsM-mR?M>~wGlLW)V;4L&fP#Xo!b)P{l3Y--Ch^W6)T6diW5a(xNK zDy3(yE(TH)P;7Rr!%B_ZG47p|k&;bV^ydnlbNFtT_pElAhN2X#n~6VB4m|}2c;3vD zS1y*;u1WCg>9gtc=^w%brK7rKL3O8Pzdf^S^rmIw|dIV#TZj5FMcgE_wOfi;B zIli)Cz9zhqEu|lj$$K+rZ%=jY*rwsPa9L4zH%gCr)FAdn?Y2mZ54INWW^`J_cST4- zt0olGMni~b*JlV%8z;rvuzH=FFD&dkTPX>Ru+FY;78HH5GGSllHG*7F;! zTDQ7}N3-ULM2`|m%pnfi>J)g~Gh)M^iDPJuP3P8RcZK=QtE5XdLH&b8;H3vUx#XXQ zoT}X6iHVf0TbEX%&uW82?`G?opzwp{U>Xt-;)zCg{`5X-{2;8WBD){sABmKdJrD>@ z6F_F#Lnp42f)QyfD&g;qdg<6F>Sv9!LvK98lVfQ9%V z6aMEot#SGR-E+Gy$imKFl>9M{fOE8B))-ISrAgnaatSudC6 zXQh#it=UkLHS+%H$6T&iyL@jzb0zVL*&`uOjg#0&(CbEo+}wfSY9Gh4cWb{QO)i z2QLO>_vomwu<$5)34P;F&c}jI>z7*Fxto0SZB*PW$|Tqvl_qrxq>nW+A2$NAEGEVV z3n9X7E`pHwyIu3kzZdIPMR}6KoGF_j>?F9CFlr3(5%3^SwYRrxKDvfL2ps^( z;o@QbLl?D}%i)+zAwNwx?iL_P6vFoI2MY@Zf+kbjHVwO9ETcfX$o=G3UC zYZhIA+(f~E1@OeyGf*NBV`()F&#h~lTu68t%vXIonMO-;dehAE?tXZGh-W$57UXfZ zU0+vcGu_OfsR@HX>I-TGdN+auX(K8mS)>IS5D2GX_#G*qRL+eO$>;D3!0c9~a0`Lx zpC2C9ym{lsFNP~J&UHb+9WBQY-Z?imHU>#LEKmwmr4d4!o~0k<%uOe7b<0gtNA`Zj zu>{~qd!=#;l~e!)k$K^#ms>xB%Tg=YClaSQlleHuz3+*Lp->3ln{BU5pbw>$D>+pq zqPVD7lk0cJht>$*X!!7mvbMvQTF(!<#o;Gmu-dVpvZVUz+Ir9Dk3Vcdv7UB_6&b2U_coU}F%7rL*%fIdx%9l{+Q)y=>Q{6Jb@fzxTXdex^{UGO6L6hb@#+ zhRx0n&y_&gkY;#4h5Y(;ym%uX3>isyJcUJ9rHq!J$bKXy!k71K%c*&)o>;ysW=E~C zU%Y|@{d+4FI~l$^8HmZ-f4SI9P@|>#P{E4K_ILD9jgP6+;ZcmMPvHS|gW%w+4<0-i z^TR{TJP#J@TS^~ndE{=1ClFoVVfi%%bRtfGaa}zRvU^}kR#s7ojEFejYS^|QAV44@ zPUn9o0(Lma5XA4`Po*X;-3mQh$D0$v-sf(faz(=>|5u0tx7Y|1@x}1Fvhw+brxE8c zFZy>OBLTZN-RxCxoBEDH!Z%aEHRN&OZ8>h2`}cW^Z63&yH*qu3SO5kp&TMgxucZW1>)}{#Wb5VROWN>hGwjI*#1i-R0!s%2Evv z4+kNGM_W~4r%|s6r3-_7u=W_`0g$y`d9%O10Ejy!S?_E z>A%&O>A+Jg8iqbkO99$?ta1Z+`KyyGPMEBfcuP2Vq%KUdV_w{G?5k0&v~ZM5n~vTq zOnMM*lhf0C!h!1QG8cVQc9z7%;uy@#(zX?V+YqhniWup)s)E@~^d$m{ z`yW4kp!xHGxtoj(>8b~t@M$?g-{t>$TNuz=Z0qoyN)0ZRlaqimEhD4%;&;mShDey6 z5fK^lb9jcux*@rw@a6*x3f`KAaTb(5R*5pnRg+_2CR~J`$a!sX(X6s^_KP1D5dmmj zN!5s$y_?LC4gl-n%oYF5O`I`u7oK88GVdWuQc@DeCWkD6lk4B*g=GW|{QR+~8Io4m z_JdL`2yRdy!ASCoiM6}rKK?@pE&s^ONVvNJ;{~_!jKnemhzsS95H(3 z+>alttE+!81StYbpP8ANlS1Y;u&o2F|M(kHHky;yD^4FAc^Vi&=14W^u5{*OH+Od+ zTSN<3FfuYa;T$HtGxYuo@(Q?_!NI}nqosokQ4SG=zq-1*hKCQ&wp+GGOVj1E-FIgE zii%zW(z_B>L=H2Z#^qx_euTplCJ9(V9JgRE0SX>Z@kyUMB(=%5a0fO8j!^LY)M&jC}Dm6Zkgk%+A~mwRZf43JX*Jt@)uzA!K_ z;PB;&-SX-o-UHnucVNNm`yfhAOIr`Kvw?wu)P|n1TNx1|9tXFaH@3IaQ&Uw;?tW}M zT`S_`V@Mk7sxSyk0#K0sE zBfx=p#;JUkKR;WMa9K7*47-lF1vUq(ho?221cO=4VJVfD^%A+e?DA9(+{*ZMe; zU&+zY5sAbkDlINn$sQoVb~xVB_q;7x{OOa^LpOO#f%mnwwK+LCkSxH}ReLhPXzJy4 z0mq!XZdrlJD_BzN#>$#sZLX;~0^34QuLc|9;!-`3LUqUBA&IPo#VzNMgaitIX8Q(m zZ6dO`$>dWCl#(n&`u+#rdF6- zUg}MWis*lAZM^|Paf)#o1sd4W6jW4kJ-B4YRNgT9BX4&Bl%y>ubJyzhyyrUb{Fy;g zfu25%cmLwU-@kur`NO7|fzn!887?oAVjNduVOAEV;Rld<0b>q&H85?xRmQuuwq~^^ z!p@F;{3Dhf;$~QAkaGGBuxkwaGd(?Vh>iH8uTViaLJeX#L&C27>6jqhp$g9hv`q}+ zCl03vmXj0~g#nUWTx-CV>}2}f9A>=}l@#MVK>-Zv2~cv`b@m7tvJij{mgw+sHr%$A ze0ex}VCtnwV2_8ok^@#Rg}|x_iHauIqf<&Saka}>PUP(D zbo?97SjEmL?4hy}9}@%Pkwl*F6B3-DIt3%4ED4>nFq zP3_mnNN@m)A|Egn3NK}4WdYWdtH#+RJu*59kzOpjsQcam9OCs!rPrBZloubMLBFr3 z$H_FtqiLL7QC?2&327YrUy#jE+hoTH4-IvPMadsT3+KU80-t!!R5{4O1O){_y-wXQ zNy=3US>Xpo1?XFtHOZDxCb+)I{O??ziJUzC!-t0#c=s{`ruxyOcVuMfnI3;;m>);Q zJp_q??(Q!&@cg9M(pB;FcQ`oYYV^bDnxVxZr(|QQN%#0nkmu9NGTN2`u^@3*;EO`x zv+QWeu;}Q#Eg{fz0Tr{Zz??kLkz>aF+Hy5RcoD5&Vn2hhEdl8^nO_zHu(={9aO6vMs+5ig zr+1BwjbT;uUN08cOevvI{Y}U-t)-HqS8o#%9`}0S+#O6|AcYbA;XWmAYE!Om1G zpeK3c;c*Hh*N42|r(B_wQGZX}kPn!r-~c*|Un#>wp{ir!;ws$t&3`%dktrTq;Fz0V z#s`S49IiyY(ra*uWX7Gm(+ST4l^aWt=#wUS^5jX>#?Fw<<=m9$+9~*j%-4_3y)R+B zi731mkb+SgcmB`mgKhVUAw^J-lY_86#WG_|6P9E+Drd%_3bt%^F!8ola8M9rQqRFy zVM~LUlUP*fCLRXfH=&(UzO6&P;m@+8c~Z{Kf`Shvj5}~ATWY|*4veh_U1xAk5qIxJ{Xh|2M=0LX&AA#Y-+tJxd`Lrt+t_)oCGfhiW;?xG7qL~-w=xl8Wd?o`G<%ke?-;{QDcTXD9wjL6l3(G6_#ihXwh`(`zW;CQA%E!u;?T;mt>w693++U#}DEtr;<9qwZZX(n$ z$gus2$Qk}kjE#&`TMV#Hej8m~U4@Ino~+YQiu3Q^nn7y|BI?!RT zOfRYVtWA5L@m$iq^6ZN%0sA%6cSD^#V&(lmS-fl4vYXpdt~+1D!+QqK{8z3r!+FzG z*^5xCOJjm$nL^!Mm;EJ`NsKnwQMKEJqI)%@qf{{TmU1F+=gu7oiBz3${>dgf!Nw*g z;BrHwhppfftwDUYO1O&NZngSX$8BwGP7l6=2u;B+-<1^$oJaE+0)m3Gk|g5ws_)aD z%QkFmSg+j{xAgeH1`ALVFi>&{?EO!=T&M_XT^oL?Rv{-*bMY6Yr}uG$*)=rV-D zu9l&f9EmJ!KeyIB|XZ>o2`#!wkoZSM>q@LeXOs4e}t@p z)-Clb8Y1uJ*CW=R{VJNg$z=B`CZa$sJSytz=L~}B5-5>QhMFO0uHc2Ts~q^MCx45p zdkqWs@w?+jM~@3PyCV4AqabPnl(KSr>T`%3>WlpD;n*6_3GYMq`Ka#85Gp)8JdJnY z!X(4<=m&m%)&QOqZS8zv(k_e5W`mRfmZw&3p@izWDV29}u#Xnl^Y)Yk z&M|jxP7VQ+q&Nr16}yKDUQB#X8~S>Ly1=46x5w2cgtzdJm_dWhDL5|*wH5%@i7GJT z`^<37IUU-{)lHNijtmVglwJAmR8?6?qv8jOAUVISpe);;syI&rtsnKy%k_tg=2pko z$Ckb*Ti?xdxhg`9Liw~x%)p|{;}9hE-!`@qqw>gh#CNO{my~1_enI$RdFGjzMs)rx z)wYW>+G6DXDgz-*KxtD=*Or*gt*083{nW#K)qMjGsIw(z(47MD4b1tQ+P?0YnVBNf zNxz@nWMc~`8IlC>935-t!L0~~EF(0Oj-t8YuNtqkJ3VXrBzi=xvAI!;g-8Wx&d|>z&XbIP8Ois0(q#D!g04R29_tl zciv|2OsNzU%Y;5|MXBq)ctbN{!=_&5KHUw|Xt5-|H!cG+q zYJ)I<4bk)Eh^`$a1;yQEpXy*_mQ{w`Qx|x5%%<8cJn~kMbXyQ z*4w)|B%}-4jnJZ?kodC(dV_`^L?5IqLoGi4px+3*O+A}e4^&CtaX-t_ENW_MVv_U} z0SP#HLIcbhL`;knaV30ASiuWG+vtg2z*>X#=^CxlDOM{OqAN5?`2AZ4v_I$P*(mT= zqplzn0`9TT9H^+0${m!e}7NRm@U9E>3V2mK9a(2(F4g>DH2UNMC4pdb90)c;Ih(fy}C z;f21%ao}dctP@?11jvkSm+eH0CSe7WDBIL-7zent|End1qbIJ zNCHEAs(=gA5;o8(hnopE}mqDc$xP*KFhX$Qo)+Q$$6bf+SHtdpPvHD)-rL5x))7h$ z=nGmO7^d=xm?R&@EdV5B#y1W`Xy(1|gqUX2?q8929XET4?dTkjcfVu-i<>2FFq~~HeXy^eCNUr95bZQx3;f-8JB*7 z^*j*tav>(7qNd(=6F*o+=0o$9JT98X29^m3-DlyG0}E{p*xcjQ!H_LC3`8~h^UFb? z@8)&_wqR!!BtEGb7@9yr_QMCLhQZ3?reI<82S_6^A3o956@$zJ*yvBr&Y)Gp*a3n~ zfE|!=rl9%@oLHHKbiQeXUt|dA}7#JW`MC*;hg6&e~ssXFoY9u~lh6aic1a0%Xnh-F6Qt=wTDH|e<9{w_dE&f}6 zoGs!@JQf+C$q0F0I9~tnfn&XTEW{Tj+DLP>R5gHBE{W*Ja&Xv%qki9O29kf*jWMIy zYn+MSp@bHFpOAn+uk5+s<6E-0ft3@REAy-W2fK@&CGhlV z%9DT!u^ulIhzo<`AoQt(iDP>ade7XfEMqt-^+ssG{2HFfZBWNeOoSf4{M%sC(RdsO zF(m!9i+7-S34NRpp8yIE$zF+G-5wAX%%!~L(fv4V1;%p|SUdo-fnx4eL(toxQpd)K8YcsRxbj9( zR$2o*BWMAGnEV5fbe+sgUs=>Q#33TjEv3T=>Y8O+}vGE#1eqmv~$01irFC(k~0G<)eScv}rJ{DTkQ*65tirqIrHyCQe#1GC% zScXPMgdi*eYYW+q+Jz-J>XXZ}Z8+^{1r48n-e@2h#KVWt0;?%0FAu&|>1=`|tSkw3 zdf)Q?Up-!Ur6x!Pa9RAMV&DfV8RFl+_t;xdoV*Q1rR|n~l9IMIHo3n7IH6SyAo+Gu zUhT3O5UvFG2h3DZ?%_L^hNlGA2j7y0cS3n8S@0$F$lp5!&T=*j7C*)nkTD4%V%Xc+ zaRZCvvi0XLcsmAQBLu+2jo>2~3fL-lcXzPRU=9^;V!r zA=^K1Nal9kK+|CI^NUCF^1&@F{{R{Uek?w;Eq6o8$iN48E`|FKU(^&7P&-%#?nn5A zsvU*1)x7jTBnuRmQz zZ7%?LX#^K16HE{aQ5n?4!ovqG1R%}m{rNKu^iF~2N|rTt%4B=8VR>%Oac!souyM9P zv`(w@nnvJbZS65oc(sSlY|toLTv(XlmE7Ok--r9Nvzx%FPJ)E%SvN&IH#=)|LIlz5 z5;}BKQlgdcIDl>pEKE$aH#{K%4Ljv&GlVrE8JWFiE#@XfC(_c=CrXf=hlgK7c9-R- z+@b&&f6gRVEtJI3*%|DECkR#V%B}hKZsjmFKtnT_20)c~d3hnEhwv8wcW7FLFW89& z_x+DX$a;r)c_$|)4UHjqWM2|m0gHYpB|PKSw6ZEpPoIXkQ}S%%^ZB{wWGfHxSy7T&Q?GP=)QR{$Z zNFCk@Cs{)*!V%iDBO)T8k@fG{**w)#xNU#~fd=+21H2a0MDq8r_aQp_`9U-w`oYOe zfT7RD@D5JwH`8EZIRDFlPtsFR2tcR`Soi6vN6|C}Vgp+T^(7Qt!-Oc7LC|Ul%*nND z*FXX;3EECVv53|Rf_iM^oAz0P>wF9C#1Dpwj8dQ(-o@nzPRCUu zdN%+kz*3(8pPq-@Ir8K=oG`Ez?$B%xzVj*9Fj!yk)Sw^({=EfqS3Rmbu_yzd3uovv z=4}GTa=LJK#d;GcSXsS%iKc8p&k8(bDA(prRKmi-D!7dw^uNIS)T5!~tYIs=Keesq zpCR?EKV=8|0K>{)zK5{2e=yyOMO`Ike5hZ(gTs0I_Wb@|tg8eoYgice2z1v0f(E0R g99j{c2mZq;`}yJWZfXBt_*;acoT_XI^11*20PhgGnE(I) literal 0 HcmV?d00001 diff --git a/Functions/helper_functions_community/Violinplot-Matlab-master/test_cases/testviolinplot.m b/Functions/helper_functions_community/Violinplot-Matlab-master/test_cases/testviolinplot.m new file mode 100644 index 0000000..339632e --- /dev/null +++ b/Functions/helper_functions_community/Violinplot-Matlab-master/test_cases/testviolinplot.m @@ -0,0 +1,87 @@ +function testviolinplot() +figure(); +% One could use tiled layout for better plotting them but it would be +% incompatible with older versions +subplot(2,4,1); + +% TEST CASE 1 +disp('Test 1: Violin plot default options'); +load carbig MPG Origin +Origin = cellstr(Origin); +vs = violinplot(MPG, Origin); +plotdetails(1); + +% TEST CASE 2 +disp('Test 2: Test the plot ordering option'); +grouporder={'USA','Sweden','Japan','Italy','Germany','France','England'}; + +subplot(2,4,2); +vs2 = violinplot(MPG,Origin,'GroupOrder',grouporder); +plotdetails(2); + +% TEST CASE 3 +disp('Test 3: Test the numeric input construction mode'); +subplot(2,4,3); +cats = categorical(Origin); +catnames = (unique(cats)); % this ignores categories without any data +catnames_labels = {}; +thisData = NaN(length(MPG),length(catnames)); +for n = 1:length(catnames) + thisCat = catnames(n); + catnames_labels{n} = char(thisCat); + thisData(1:length(MPG(cats == thisCat)),n) = MPG(cats == thisCat); +end +vs3 = violinplot(thisData,catnames_labels); +plotdetails(3); + +% TEST CASE 4 +disp('Test 4: Test two sided violin plots. Japan is being compared.'); +subplot(2,4,4); +C = colororder; +vs4 = violinplot({thisData,repmat(thisData(:,5),1,7)},catnames_labels,'ViolinColor',{C,C(5,:)},'ViolinAlpha',{0.3 0.3}, 'ShowMean', true, 'MarkerSize',8); +plotdetails(4); + +% TEST CASE 5 +disp('Test 5: Test shadow for quartiles.'); +subplot(2,4,5); +vs5 = violinplot(MPG, Origin, 'QuartileStyle','shadow'); +plotdetails(5); + +% TEST CASE 6 +disp('Test 6: Test plotting only right side & histogram plot, with quartiles as boxplot.'); +subplot(2,4,6); +vs5 = violinplot(MPG, Origin, 'QuartileStyle','boxplot', 'HalfViolin','right',... + 'DataStyle', 'histogram'); +plotdetails(6); + +% TEST CASE 7 +disp('Test 7: Test plotting only left side & histogram plot, and quartiles as shadow.'); +subplot(2,4,7); +vs5 = violinplot(MPG, Origin, 'QuartileStyle','shadow', 'HalfViolin','left',... + 'DataStyle', 'histogram', 'ShowMean', true); +plotdetails(7); + + +% TEST CASE 8 +disp('Test 8: Same as previous one, just removing the data of half of the violins afterwards.'); +subplot(2,4,8); +vs5 = violinplot([MPG; 5;5;5;5;5], [Origin; 'test';'test';'test';'test';'test'], 'QuartileStyle','shadow', 'HalfViolin','full',... + 'DataStyle', 'scatter', 'ShowMean', false); +plotdetails(8); +for n= 1:round(length(vs5)/2) + vs5(1,n).ShowData = 0; +end +xlim([0, 9]); +%other test cases could be added here + +end + +function plotdetails(n) +title(sprintf('Test %02.0f \n',n)); +ylabel('Fuel Economy in MPG '); +xlim([0, 8]); grid minor; +set(gca, 'color', 'none'); +xtickangle(-30); +fprintf('Test %02.0f passed ok! \n ',n); +end + diff --git a/Functions/helper_functions_community/Violinplot-Matlab-master/violinplot.m b/Functions/helper_functions_community/Violinplot-Matlab-master/violinplot.m new file mode 100644 index 0000000..63af839 --- /dev/null +++ b/Functions/helper_functions_community/Violinplot-Matlab-master/violinplot.m @@ -0,0 +1,193 @@ +function violins = violinplot(data, cats, varargin) +%Violinplots plots violin plots of some data and categories +% VIOLINPLOT(DATA) plots a violin of a double vector DATA +% +% VIOLINPLOT(DATAMATRIX) plots violins for each column in +% DATAMATRIX. +% +% VIOLINPLOT(DATAMATRIX, CATEGORYNAMES) plots violins for each +% column in DATAMATRIX and labels them according to the names in the +% cell-of-strings CATEGORYNAMES. +% +% In the cases above DATA and DATAMATRIX can be a vector or a matrix, +% respectively, either as is or wrapped in a cell. +% To produce violins which have one distribution on one half and another +% one on the other half, DATA and DATAMATRIX have to be cell arrays +% with two elements, each containing a vector or a matrix. The number of +% columns of the two data sets has to be the same. +% +% VIOLINPLOT(DATA, CATEGORIES) where double vector DATA and vector +% CATEGORIES are of equal length; plots violins for each category in +% DATA. +% +% VIOLINPLOT(TABLE), VIOLINPLOT(STRUCT), VIOLINPLOT(DATASET) +% plots violins for each column in TABLE, each field in STRUCT, and +% each variable in DATASET. The violins are labeled according to +% the table/dataset variable name or the struct field name. +% +% violins = VIOLINPLOT(...) returns an object array of +% Violin objects. +% +% VIOLINPLOT(..., 'PARAM1', val1, 'PARAM2', val2, ...) +% specifies optional name/value pairs for all violins: +% 'Width' Width of the violin in axis space. +% Defaults to 0.3 +% 'Bandwidth' Bandwidth of the kernel density estimate. +% Should be between 10% and 40% of the data range. +% 'ViolinColor' Fill color of the violin area and data points. Accepts +% 1x3 color vector or nx3 color vector where n = num +% groups. In case of two data sets being compared it can +% be an array of up to two cells containing nx3 +% matrices. +% Defaults to the next default color cycle. +% 'ViolinAlpha' Transparency of the violin area and data points. +% Can be either a single scalar value or an array of +% up to two cells containing scalar values. +% Defaults to 0.3. +% 'MarkerSize' Size of the data points, if shown. +% Defaults to 24 +% 'MedianMarkerSize' Size of the median indicator, if shown. +% Defaults to 36 +% 'EdgeColor' Color of the violin area outline. +% Defaults to [0.5 0.5 0.5] +% 'BoxColor' Color of the box, whiskers, and the outlines of +% the median point and the notch indicators. +% Defaults to [0.5 0.5 0.5] +% 'MedianColor' Fill color of the median and notch indicators. +% Defaults to [1 1 1] +% 'ShowData' Whether to show data points. +% Defaults to true +% 'ShowNotches' Whether to show notch indicators. +% Defaults to false +% 'ShowMean' Whether to show mean indicator +% Defaults to false +% 'ShowBox' Whether to show the box. +% Defaults to true +% 'ShowMedian' Whether to show the median indicator. +% Defaults to true +% 'ShowWhiskers' Whether to show the whiskers +% Defaults to true +% 'GroupOrder' Cell of category names in order to be plotted. +% Defaults to alphabetical ordering + +% Copyright (c) 2016, Bastian Bechtold +% This code is released under the terms of the BSD 3-clause license + +hascategories = exist('cats','var') && not(isempty(cats)); + +%parse the optional grouporder argument +%if it exists parse the categories order +% but also delete it from the arguments passed to Violin +grouporder = {}; +idx=find(strcmp(varargin, 'GroupOrder')); +if ~isempty(idx) && numel(varargin)>idx + if iscell(varargin{idx+1}) + grouporder = varargin{idx+1}; + varargin(idx:idx+1)=[]; + else + error('Second argument of ''GroupOrder'' optional arg must be a cell of category names') + end +end + +% check and correct the structure of ViolinColor input +idx=find(strcmp(varargin, 'ViolinColor')); +if ~isempty(idx) && iscell(varargin{idx+1}) + if length(varargin{idx+1}(:))>2 + error('ViolinColor input can be at most a two element cell array'); + end +elseif ~isempty(idx) && isnumeric(varargin{idx+1}) + varargin{idx+1} = varargin(idx+1); +end + +% check and correct the structure of ViolinAlpha input +idx=find(strcmp(varargin, 'ViolinAlpha')); +if ~isempty(idx) && iscell(varargin{idx+1}) + if length(varargin{idx+1}(:))>2 + error('ViolinAlpha input can be at most a two element cell array'); + end +elseif ~isempty(idx) && isnumeric(varargin{idx+1}) + varargin{idx+1} = varargin(idx+1); +end + +% tabular data +if isa(data, 'dataset') || isstruct(data) || istable(data) + if isa(data, 'dataset') + colnames = data.Properties.VarNames; + elseif istable(data) + colnames = data.Properties.VariableNames; + elseif isstruct(data) + colnames = fieldnames(data); + end + catnames = {}; + if isempty(grouporder) + for n=1:length(colnames) + if isnumeric(data.(colnames{n})) + catnames = [catnames colnames{n}]; %#ok<*AGROW> + end + end + catnames = sort(catnames); + else + for n=1:length(grouporder) + if isnumeric(data.(grouporder{n})) + catnames = [catnames grouporder{n}]; + end + end + end + + for n=1:length(catnames) + thisData = data.(catnames{n}); + violins(n) = Violin({thisData}, n, varargin{:}); + end + set(gca, 'XTick', 1:length(catnames), 'XTickLabels', catnames); + set(gca,'Box','on'); + return +elseif iscell(data) && length(data(:))==2 % cell input + if not(size(data{1},2)==size(data{2},2)) + error('The two input data matrices have to have the same number of columns'); + end +elseif iscell(data) && length(data(:))>2 % cell input + error('Up to two datasets can be compared'); +elseif isnumeric(data) % numeric input + % 1D data, one category for each data point + if hascategories && numel(data) == numel(cats) + if isempty(grouporder) + cats = categorical(cats); + else + cats = categorical(cats, grouporder); + end + + catnames = (unique(cats)); % this ignores categories without any data + catnames_labels = {}; + for n = 1:length(catnames) + thisCat = catnames(n); + catnames_labels{n} = char(thisCat); + thisData = data(cats == thisCat); + violins(n) = Violin({thisData}, n, varargin{:}); + end + set(gca, 'XTick', 1:length(catnames), 'XTickLabels', catnames_labels); + set(gca,'Box','on'); + return + else + data = {data}; + end +end + +% 1D data, no categories +if not(hascategories) && isvector(data{1}) + violins = Violin(data, 1, varargin{:}); + set(gca, 'XTick', 1); +% 2D data with or without categories +elseif ismatrix(data{1}) + for n=1:size(data{1}, 2) + thisData = cellfun(@(x)x(:,n),data,'UniformOutput',false); + violins(n) = Violin(thisData, n, varargin{:}); + end + set(gca, 'XTick', 1:size(data{1}, 2)); + if hascategories && length(cats) == size(data{1}, 2) + set(gca, 'XTickLabels', cats); + end +end + +set(gca,'Box','on'); + +end diff --git a/Functions/helper_functions_community/autoArrangeFigures.m b/Functions/helper_functions_community/autoArrangeFigures.m new file mode 100644 index 0000000..a47012c --- /dev/null +++ b/Functions/helper_functions_community/autoArrangeFigures.m @@ -0,0 +1,115 @@ +function autoArrangeFigures(NH, NW, monitor_id) +% INPUT : +% NH : number of grid of vertical direction +% NW : number of grid of horizontal direction +% OUTPUT : +% +% get every figures that are opened now and arrange them. +% +% autoArrangeFigures selects automatically Monitor1. +% If you are dual(or more than that) monitor user, I recommend to set wide +% monitor as Monitor1. +% +% if you want arrange automatically, type 'autoArrangeFigures(0,0)' or 'autoArrangeFigures()'. +% But maximum number of figures for automatic mode is 27. +% +% if you want specify grid for figures, give numbers for parameters. +% but if your grid size is smaller than required one for accommodating +% all figures, this function changes to automatic mode and if more +% figures are opend than maximum number, then it gives error. +% +% Notes +% + 2017.1.20 use monitor id(Adam Danz's idea) +% +% leejaejun, Koreatech, Korea Republic, 2014.12.13 +% jaejun0201@gmail.com + +if nargin < 2 + NH = 0; + NW = 0; + monitor_id = 1; +end + +task_bar_offset = [30 50]; + +%% +N_FIG = NH * NW; +if N_FIG == 0 + autoArrange = 1; +else + autoArrange = 0; +end +figHandle = sortFigureHandles(findobj('Type','figure')); + +moveitHandle = findall(figHandle, 'Type', 'figure', 'Tag', 'Hfroot'); + +figHandle = figHandle(ismember(figHandle,moveitHandle)~=1); + +n_fig = size(figHandle,1); + +if n_fig <= 0 + warning('figures are not found'); + return +end + +screen_sz = get(0,'MonitorPositions'); +screen_sz = screen_sz(monitor_id, :); +scn_w = screen_sz(3) - task_bar_offset(1); +scn_h = screen_sz(4) - task_bar_offset(2); +scn_w_begin = screen_sz(1) + task_bar_offset(1); +scn_h_begin = screen_sz(2) + task_bar_offset(2); + +if autoArrange==0 + if n_fig > N_FIG + autoArrange = 1; + warning('too many figures than you told. change to autoArrange'); + else + nh = NH; + nw = NW; + end +end + +if autoArrange == 1 + grid = [2 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4; + 3 3 3 3 3 3 3 3 4 4 4 5 5 5 5 5 5 5 5 6 6 6 7 7 7 7 7]'; + + if n_fig > length(grid) + warning('too many figures(maximum = %d)',length(grid)) + return + end + + if scn_w > scn_h + nh = grid(n_fig,1); + nw = grid(n_fig,2); + else + nh = grid(n_fig,2); + nw = grid(n_fig,1); + end +end + + +fig_width = scn_w/nw; +fig_height = scn_h/nh; + +fig_cnt = 1; +for i=1:1:nh + for k=1:1:nw + if fig_cnt>n_fig + return + end + fig_pos = [scn_w_begin+fig_width*(k-1) ... + scn_h_begin+scn_h-fig_height*i ... + fig_width ... + fig_height]; + set(figHandle(fig_cnt),'OuterPosition',fig_pos); + figure(figHandle(fig_cnt)); %bring to top + fig_cnt = fig_cnt + 1; + end +end + +end + +function figSorted = sortFigureHandles(figs) + [tmp, idx] = sort([figs.Number]); + figSorted = figs(idx); +end \ No newline at end of file diff --git a/Functions/helper_functions_community/boundedlines/.gitignore b/Functions/helper_functions_community/boundedlines/.gitignore new file mode 100644 index 0000000..c2bfeef --- /dev/null +++ b/Functions/helper_functions_community/boundedlines/.gitignore @@ -0,0 +1,45 @@ +# MATLAB # +########## +# Editor autosave files +*~ +*.asv +# Compiled MEX binaries (all platforms) +*.mex* + +# Compiled source # +################### +*.com +*.class +*.dll +*.exe +*.o +*.so + +# Packages # +############ +# it's better to unpack these files and commit the raw source +# git has its own built in compression methods +*.7z +*.dmg +*.gz +*.iso +*.jar +*.rar +*.tar +*.zip + +# Logs and databases # +###################### +*.log +*.sql +*.sqlite + +# OS generated files # +###################### +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db \ No newline at end of file diff --git a/Functions/helper_functions_community/boundedlines/Inpaint_nans/.gitignore b/Functions/helper_functions_community/boundedlines/Inpaint_nans/.gitignore new file mode 100644 index 0000000..e492200 --- /dev/null +++ b/Functions/helper_functions_community/boundedlines/Inpaint_nans/.gitignore @@ -0,0 +1,37 @@ +# Compiled source # +################### +*.com +*.class +*.dll +*.exe +*.o +*.so + +# Packages # +############ +# it's better to unpack these files and commit the raw source +# git has its own built in compression methods +*.7z +*.dmg +*.gz +*.iso +*.jar +*.rar +*.tar +*.zip + +# Logs and databases # +###################### +*.log +*.sql +*.sqlite + +# OS generated files # +###################### +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db \ No newline at end of file diff --git a/Functions/helper_functions_community/boundedlines/Inpaint_nans/demo/html/inpaint_nans_demo.html b/Functions/helper_functions_community/boundedlines/Inpaint_nans/demo/html/inpaint_nans_demo.html new file mode 100644 index 0000000..de6698a --- /dev/null +++ b/Functions/helper_functions_community/boundedlines/Inpaint_nans/demo/html/inpaint_nans_demo.html @@ -0,0 +1,161 @@ + + + + + + inpaint_nans_demo + + + +
% Surface fit artifact removal
+[x,y] = meshgrid(0:.01:1);
+z0 = exp(x+y);
+
+znan = z0;
+znan(20:50,40:70) = NaN;
+znan(30:90,5:10) = NaN;
+znan(70:75,40:90) = NaN;
+
+z = inpaint_nans(znan,3);
+
+% Comparison to griddata
+k = isnan(znan);
+zk = griddata(x(~k),y(~k),z(~k),x(k),y(k));
+zg = znan;
+zg(k) = zk;
+
+close all
+figure
+surf(z0)
+title 'Original surface'
+
+figure
+surf(znan)
+title 'Artifacts (large holes) in surface'
+
+figure
+surf(zg)
+title(['Griddata inpainting (',num2str(sum(isnan(zg(:)))),' NaNs remain)'])
+
+figure
+surf(z)
+title 'Inpainted surface'
+
+figure
+surf(zg-z0)
+title 'Griddata error surface'
+
+figure
+surf(z-z0)
+title 'Inpainting error surface (Note z-axis scale)'
+
+ + + \ No newline at end of file diff --git a/Functions/helper_functions_community/boundedlines/Inpaint_nans/demo/html/inpaint_nans_demo.png b/Functions/helper_functions_community/boundedlines/Inpaint_nans/demo/html/inpaint_nans_demo.png new file mode 100644 index 0000000000000000000000000000000000000000..954751b398b8e39da28808d333e89a895bd24589 GIT binary patch literal 3192 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 D#1-8O literal 0 HcmV?d00001 diff --git a/Functions/helper_functions_community/boundedlines/Inpaint_nans/demo/html/inpaint_nans_demo_02.png b/Functions/helper_functions_community/boundedlines/Inpaint_nans/demo/html/inpaint_nans_demo_02.png new file mode 100644 index 0000000000000000000000000000000000000000..a810f1365885816a156713b9ce51aa93c7869c60 GIT binary patch literal 17307 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�b!G9 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 Cf98Pz literal 0 HcmV?d00001 diff --git a/Functions/helper_functions_community/boundedlines/Inpaint_nans/test/test_main.m b/Functions/helper_functions_community/boundedlines/Inpaint_nans/test/test_main.m new file mode 100644 index 0000000..1dde28e --- /dev/null +++ b/Functions/helper_functions_community/boundedlines/Inpaint_nans/test/test_main.m @@ -0,0 +1,178 @@ +%% Repair to an image with 50% random artifacts + +% Garden at Sainte-Adresse (Monet, 1867) +garden = imread('monet_adresse.jpg'); +G = double(garden); +G(rand(size(G))<0.50) = NaN; +Gnan = G; + +G(:,:,1) = inpaint_nans(G(:,:,1),2); +G(:,:,2) = inpaint_nans(G(:,:,2),2); +G(:,:,3) = inpaint_nans(G(:,:,3),2); + +figure +subplot(1,3,1) +image(garden) +title 'Garden at Sainte-Adresse (Monet)' + +subplot(1,3,2) +image(uint8(Gnan)) +title 'Corrupted - 50%' + +subplot(1,3,3) +image(uint8(G)) +title 'Inpainted Garden' + +%% Surface fit artifact removal + +[x,y] = meshgrid(0:.01:1); +z0 = exp(x+y); + +znan = z0; +znan(20:50,40:70) = NaN; +znan(30:90,5:10) = NaN; +znan(70:75,40:90) = NaN; + +tic,z = inpaint_nans(znan,3);toc + +tic +k = isnan(znan); +zk = griddata(x(~k),y(~k),z(~k),x(k),y(k)); +zg = znan; +zg(k) = zk; +toc + +figure +surf(z0) +title 'Original surface' + +figure +surf(znan) +title 'Artifacts (large holes) in surface' + +figure +surf(zg) +title(['Griddata inpainting (',num2str(sum(isnan(zg(:)))),' NaNs remain)']) + +figure +surf(z) +title 'Inpainted surface' + +figure +surf(zg-z0) +title 'Griddata error surface' + +figure +surf(z-z0) +title 'Inpainting error surface (Note z-axis scale)' + +%% Comparison of methods + +[x,y] = meshgrid(-1:.02:1); +r = sqrt(x.^2 + y.^2); +z = exp(-(x.^2+ y.^2)); + +z(r>=0.9) = NaN; + +z((r<=.5) & (x<0)) = NaN; + +figure +pcolor(z); +title 'Surface provided to inpaint_nans' + +% Method 0 +tic,z0 = inpaint_nans(z,0);toc + +% Method 1 +tic,z1 = inpaint_nans(z,1);toc + +% Method 2 +tic,z2 = inpaint_nans(z,2);toc + +% Method 3 +tic,z3 = inpaint_nans(z,3);toc + +% Method 4 +tic,z4 = inpaint_nans(z,4);toc + +% Method 5 +tic,z5 = inpaint_nans(z,5);toc + +figure +surf(z0) +colormap copper +hold on +h = surf(z); +set(h,'facecolor','r') +hold off +title 'Method 0 (Red was provided)' + +figure +surf(z1) +hold on +h = surf(z); +set(h,'facecolor','r') +hold off +title 'Method 1 (Red was provided)' + +figure +surf(z2) +hold on +h = surf(z); +set(h,'facecolor','r') +hold off +title 'Method 2 (Red was provided) - least accurate, but fastest' + +figure +surf(z3) +hold on +h = surf(z); +set(h,'facecolor','r') +hold off +title 'Method 3 (Red was provided) - Slow, but accurate' + +figure +surf(z4) +hold on +h = surf(z); +set(h,'facecolor','r') +hold off +title 'Method 4 (Red was provided) - designed for constant extrapolation!' + +figure +h = surf(z5); +set(h,'facecolor','y') +hold on +h = surf(z); +set(h,'facecolor','r') +hold off +title 'Method 5 (Red was provided)' + + +%% 1-d "inpainting" using interp1 + +x = linspace(0,3*pi,100); +y0 = sin(x); +y = y0; +% Drop out 2/3 of the data +y(1:3:end) = NaN; +y(2:3:end) = NaN; + +% inpaint_nans +y_inpaint = inpaint_nans(y,1); + +% interpolate using interp1 +k = isnan(y); +y_interp1c = y; +y_interp1s = y; +y_interp1c(k) = interp1(x(~k),y(~k),x(k),'cubic'); +y_interp1s(k) = interp1(x(~k),y(~k),x(k),'spline'); + +figure +plot(x,y,'ro',x,y_inpaint,'b+') +legend('sin(x), missing 2/3 points','inpaint-nans','Location','North') + +figure +plot(x,y0-y_inpaint,'r-',x,y0-y_interp1c,'b--',x,y0-y_interp1s,'g--') +title 'Inpainting residuals' +legend('Inpaint-nans','Pchip','Spline','Location','North') diff --git a/Functions/helper_functions_community/boundedlines/LICENSE.txt b/Functions/helper_functions_community/boundedlines/LICENSE.txt new file mode 100644 index 0000000..e73f1fe --- /dev/null +++ b/Functions/helper_functions_community/boundedlines/LICENSE.txt @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2015 Kelly Kearney + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Functions/helper_functions_community/boundedlines/README.html b/Functions/helper_functions_community/boundedlines/README.html new file mode 100644 index 0000000..3cbf5db --- /dev/null +++ b/Functions/helper_functions_community/boundedlines/README.html @@ -0,0 +1,377 @@ + + + + +boundedline.m: line with shaded error/confidence bounds

boundedline.m: line with shaded error/confidence bounds

Author: Kelly Kearney

This repository includes the code for the boundedline.m Matlab function and the accompanying outlinebounds.m function, along with all dependent functions required to run them.

The boundedline function allows a user to easily plot and line with a shaded patch around it. Ths sort of plot is often used to indicate uncertainty intervals or error bounds around a line.

Contents

Getting started

Prerequisites

This function requires Matlab R14 or later.

Downloading and installation

This code can be downloaded from Github or the MatlabCentral File Exchange. The File Exchange entry is updated daily from the GitHub repository.

Matlab Search Path

The following folders need to be added to your Matlab Search path (via addpath, pathtool, etc.):

boundedline-pkg/Inpaint_nans
+boundedline-pkg/boundedline
+boundedline-pkg/catuneven
+boundedline-pkg/singlepatch
+

Syntax

boundedline(x, y, b) plots a line with coordinates given by x and y, surrounded by a patch extending a certain distance b above/below that line. The dimensions of the x, y, and b arrays can vary to allow for multiple lines to be plotted at once, and for patch bounds to be either constant or varying along the length of the line. See function header help for full details of these variations.

boundedline(..., 'alpha') renders the bounded area patch using a partially-transparent patch the same color as the corresponding line(s). If not included, the bounded area will use a fully-opaque patch in a lighter shade of the corresponding line color.

boundedline(..., 'transparency', transp) indicates the tranparency or intensity of the bounds patch, using a scalar between 0 and 1. Default is 0.2.

boundedline(..., 'orientation', orient) indicates the orientation of the bounds. Orientation can be either 'vert' for vertical (y-direction) bounds, or 'horiz' for horizontal (x-direction) bounds. Default is 'vert'.

boundedline(..., 'nan', nanflag) indicates how the bounds patch should handle NaNs in the line coordinates or bounds values. Options are 'fill', to smooth over the gap using neighboring values, 'gap' to leave a blank space in the patch at those points, or 'remove' to drop the NaN-points entirely, leading to linear interpolation of the gap in the patch. See examples below for more details on these options.

boundedline(..., 'cmap', cmap) colors the lines (in order of plotting) acording to the colors in this n x 3 colormap array, overriding any linespec or default colors.

boundedline(..., ax) plots the bounded line to the axis indicated by handle ax. If not included, the current axis is used.

[hl, hp] = boundedline(...) returns the handles the resulting line and patch object(s).

hout = outlinebounds(hl, hp) adds an outline to the bounds patch generated by boundedline, returning the handle of the resulting line object in hout.

Full details of all input and output variables for both functions can be accessed via the help function.

Example 1: Plotting lines using various syntax options

This example builds the 4-panel example image used on the MatlabCentral File Exchange, which shows several different methods for supplying line coordinates, bounds coordinates, and shading options.

The first axis plots two lines using the LineSpec option for input, which allows yoy to set line color, line color, and marker type for each line. The bounds on the first line vary over x, while the bounds on the second line are constant for all x. An outline is added to the bounds so the overlapping region can be seen more clearly.

x = linspace(0, 2*pi, 50);
+y1 = sin(x);
+y2 = cos(x);
+e1 = rand(size(y1))*.5+.5;
+e2 = [.25 .5];
+
+ax(1) = subplot(2,2,1);
+[l,p] = boundedline(x, y1, e1, '-b*', x, y2, e2, '--ro');
+outlinebounds(l,p);
+title('Opaque bounds, with outline');
+axis tight;
+

For our second axis, we use the same 2 lines, and this time assign x-varying bounds to both lines. Rather than using the LineSpec syntax, this example uses the default color order to assign the colors of the lines and patches. I also turn on the 'alpha' option, which renders the patch wit partial transparency.

ax(2) = subplot(2,2,2);
+boundedline(x, [y1;y2], rand(length(y1),2,2)*.5+.5, 'alpha');
+title('Transparent bounds');
+axis tight;
+

The bounds can also be assigned to a horizontal orientation, for a case where the x-axis represents the dependent variable. In this case, the scalar error bound value applies to both lines and both sides of the lines.

ax(3) = subplot(2,2,3);
+boundedline([y1;y2], x, e1(1), 'orientation', 'horiz')
+title('Horizontal bounds');
+axis tight;
+

Rather than use a LineSpec or the default color order, a colormap array can be used to assign colors. In this case, increasingly-narrower bounds are added on top of the same line.

ax(4) = subplot(2,2,4);
+boundedline(x, repmat(y1, 4,1), permute(0.5:-0.1:0.2, [3 1 2]), ...
+'cmap', cool(4), ...
+'transparency', 0.5);
+title('Multiple bounds using colormap');
+
+set(ax([1 2 4]), 'xlim', [0 2*pi]);
+set(ax(3), 'ylim', [0 2*pi]);
+axis tight;
+

Example 2: Filling gaps

If you plot a line with one or more NaNs in either the x or y vector, the NaN location is rendered as a missing marker with a gap in the line. However, the patch command does not handle NaNs gracefully; it simply fails to show the patch at all if any of the coordinates include NaNs.

Because of this, the expected behavior of the patch part of boundedline when confronted with a NaN in either the bounds array (b) or the x/y-coordinates of the line (which are used to calculate the patch coordinates) is ambiguous. I offer a few options.

Before I demonstrate the options, I'll create a dataset that has a few different types of gaps:

x = linspace(0, 2*pi, 50);
+y = sin(x);
+b = [ones(size(y))*0.2; rand(size(y))*.5+.5]';
+
+y(10)   = NaN;  % NaN in the line but not bounds
+b(20,1) = NaN;  % NaN in lower bound but not line
+b(30,2) = NaN;  % NaN in upper bound but not line
+b(40,:) = NaN;  % NaN in both sides of bound but not line
+

Here's what that looks like in an errorbar plot.

figure;
+he = errorbar(x,y,b(:,1), b(:,2), '-bo');
+
+
+line([x([10 20 30 40]); x([10 20 30 40])], [ones(1,4)*-2;ones(1,4)*2], ...
+'color', ones(1,3)*0.5, 'linestyle', ':');
+text(x(10), sin(x(10))-0.2, {'\uparrow','Line','gap'}, 'vert', 'top', 'horiz', 'center');
+text(x(20), sin(x(20))-0.2, {'\uparrow','Lower','bound','gap'}, 'vert', 'top', 'horiz', 'center');
+text(x(30), sin(x(30))-0.2, {'\uparrow','Upper','bound','gap'}, 'vert', 'top', 'horiz', 'center');
+text(x(40), sin(x(40))-0.2, {'\uparrow','Two-sided','bound','gap'}, 'vert', 'top', 'horiz', 'center');
+
+axis tight equal;
+

The default method for dealing with NaNs in boundedline is to leave the gap in the line, but smooth over the gap in the bounds based on the neighboring points. This option can be nice if you only have one or two missing points, and you're not interested in emphasizing those gaps in your plot:

delete(he);
+[hl,hp] = boundedline(x,y,b,'-bo', 'nan', 'fill');
+ho = outlinebounds(hl,hp);
+set(ho, 'linestyle', ':', 'color', 'r', 'marker', '.');
+

I've added bounds outlines in a contrasting color so you can see how I'm handling individual points.

The second option leaves a full gap in the patch for any NaN. I considered allowing one-sided gaps, but couldn't think of a good way to distinguish a gap from a zero-valued bound. I'm open to suggestions if you have any (email me).

delete([hl hp ho]);
+[hl,hp] = boundedline(x,y,b,'-bo', 'nan', 'gap');
+ho = outlinebounds(hl,hp);
+set(ho, 'linestyle', ':', 'color', 'r', 'marker', '.');
+

The final option removes points from the patch that are NaNs. The visual result is very similar to the fill option, but the missing points are apparent if you plot the bounds outlines.

delete([hl hp ho]);
+[hl,hp] = boundedline(x,y,b,'-bo', 'nan', 'remove');
+ho = outlinebounds(hl,hp);
+set(ho, 'linestyle', ':', 'color', 'r', 'marker', '.');
+

Contributions

Community contributions to this package are welcome!

To report bugs, please submit an issue on GitHub and include:

  • your operating system
  • your version of Matlab and all relevant toolboxes (type ver at the Matlab command line to get this info)
  • code/data to reproduce the error or buggy behavior, and the full text of any error messages received

Please also feel free to submit enhancement requests, or to send pull requests (via GitHub) for bug fixes or new features.

I do monitor the MatlabCentral FileExchange entry for any issues raised in the comments, but would prefer to track issues on GitHub.

diff --git a/Functions/helper_functions_community/boundedlines/README.m b/Functions/helper_functions_community/boundedlines/README.m new file mode 100644 index 0000000..1666634 --- /dev/null +++ b/Functions/helper_functions_community/boundedlines/README.m @@ -0,0 +1,240 @@ +%% |boundedline.m|: line with shaded error/confidence bounds +% Author: Kelly Kearney +% +% This repository includes the code for the |boundedline.m| Matlab function +% and the accompanying |outlinebounds.m| function, along with all dependent +% functions required to run them. +% +% The |boundedline| function allows a user to easily plot and line with a +% shaded patch around it. Ths sort of plot is often used to indicate +% uncertainty intervals or error bounds around a line. +% +%% Getting started +% +% *Prerequisites* +% +% This function requires Matlab R14 or later. +% +% *Downloading and installation* +% +% This code can be downloaded from +% or the +% . The File Exchange entry is updated daily +% from the GitHub repository. +% +% *Matlab Search Path* +% +% The following folders need to be added to your Matlab Search path (via +% |addpath|, |pathtool|, etc.): +% +% boundedline-pkg/Inpaint_nans +% boundedline-pkg/boundedline +% boundedline-pkg/catuneven +% boundedline-pkg/singlepatch + +%% Syntax +% +% |boundedline(x, y, b)| plots a line with coordinates given by +% |x| and |y|, surrounded by a patch extending a certain distance |b| +% above/below that line. The dimensions of the |x|, |y|, and |b| arrays +% can vary to allow for multiple lines to be plotted at once, and for +% patch bounds to be either constant or varying along the length of the +% line. See function header help for full details of these variations. +% +% |boundedline(..., 'alpha')| renders the bounded area patch using a +% partially-transparent patch the same color as the corresponding line(s). +% If not included, the bounded area will use a fully-opaque patch in a +% lighter shade of the corresponding line color. +% +% |boundedline(..., 'transparency', transp)| indicates the +% tranparency or intensity of the bounds patch, using a scalar between 0 +% and 1. Default is 0.2. +% +% |boundedline(..., 'orientation', orient)| indicates the orientation of +% the bounds. Orientation can be either |'vert'| for vertical (y-direction) +% bounds, or |'horiz'| for horizontal (x-direction) bounds. Default is +% |'vert'|. +% +% |boundedline(..., 'nan', nanflag)| indicates how the bounds patch should +% handle NaNs in the line coordinates or bounds values. Options are +% |'fill'|, to smooth over the gap using neighboring values, |'gap'| to +% leave a blank space in the patch at those points, or |'remove'| to drop +% the NaN-points entirely, leading to linear interpolation of the gap in +% the patch. See examples below for more details on these options. +% +% |boundedline(..., 'cmap', cmap)| colors the lines (in order of plotting) +% acording to the colors in this n x 3 colormap array, overriding any +% linespec or default colors. +% +% |boundedline(..., ax)| plots the bounded line to the axis indicated by +% handle |ax|. If not included, the current axis is used. +% +% |[hl, hp] = boundedline(...)| returns the handles the resulting line +% and patch object(s). +% +% |hout = outlinebounds(hl, hp)| adds an outline to the bounds patch +% generated by |boundedline|, returning the handle of the resulting line +% object in |hout|. +% +% Full details of all input and output variables for both functions can be +% accessed via the |help| function. + +%% Example 1: Plotting lines using various syntax options +% +% This example builds the 4-panel example image used on the MatlabCentral +% File Exchange, which shows several different methods for supplying line +% coordinates, bounds coordinates, and shading options. +% +% The first axis plots two lines using the LineSpec option for input, which +% allows yoy to set line color, line color, and marker type for each line. +% The bounds on the first line vary over x, while the bounds on the second +% line are constant for all x. An outline is added to the bounds so the +% overlapping region can be seen more clearly. + +x = linspace(0, 2*pi, 50); +y1 = sin(x); +y2 = cos(x); +e1 = rand(size(y1))*.5+.5; +e2 = [.25 .5]; + +ax(1) = subplot(2,2,1); +[l,p] = boundedline(x, y1, e1, '-b*', x, y2, e2, '--ro'); +outlinebounds(l,p); +title('Opaque bounds, with outline'); +axis tight; + +%% +% For our second axis, we use the same 2 lines, and this time assign +% x-varying bounds to both lines. Rather than using the LineSpec syntax, +% this example uses the default color order to assign the colors of the +% lines and patches. I also turn on the |'alpha'| option, which renders +% the patch with partial transparency. + +ax(2) = subplot(2,2,2); +boundedline(x, [y1;y2], rand(length(y1),2,2)*.5+.5, 'alpha'); +title('Transparent bounds'); +axis tight; + +%% +% The bounds can also be assigned to a horizontal orientation, for a case +% where the x-axis represents the dependent variable. In this case, the +% scalar error bound value applies to both lines and both sides of the +% lines. + +ax(3) = subplot(2,2,3); +boundedline([y1;y2], x, e1(1), 'orientation', 'horiz') +title('Horizontal bounds'); +axis tight; + +%% +% Rather than use a LineSpec or the default color order, a colormap array +% can be used to assign colors. In this case, increasingly-narrower bounds +% are added on top of the same line. + +ax(4) = subplot(2,2,4); +boundedline(x, repmat(y1, 4,1), permute(0.5:-0.1:0.2, [3 1 2]), ... + 'cmap', cool(4), ... + 'transparency', 0.5); +title('Multiple bounds using colormap'); + +set(ax([1 2 4]), 'xlim', [0 2*pi]); +set(ax(3), 'ylim', [0 2*pi]); +axis tight; + +%% Example 2: Filling gaps +% +% If you plot a line with one or more NaNs in either the |x| or |y| vector, +% the NaN location is rendered as a missing marker with a gap in the line. +% However, the |patch| command does not handle NaNs gracefully; it simply +% fails to show the patch at all if any of the coordinates include NaNs. +% +% Because of this, the expected behavior of the patch part of boundedline +% when confronted with a NaN in either the bounds array (|b|) or the +% x/y-coordinates of the line (which are used to calculate the patch +% coordinates) is ambiguous. I offer a few options. +% +% Before I demonstrate the options, I'll create a dataset that has a few +% different types of gaps: + +x = linspace(0, 2*pi, 50); +y = sin(x); +b = [ones(size(y))*0.2; rand(size(y))*.5+.5]'; + +y(10) = NaN; % NaN in the line but not bounds +b(20,1) = NaN; % NaN in lower bound but not line +b(30,2) = NaN; % NaN in upper bound but not line +b(40,:) = NaN; % NaN in both sides of bound but not line + +%% +% Here's what that looks like in an errorbar plot. + +figure; +he = errorbar(x,y,b(:,1), b(:,2), '-bo'); + + +line([x([10 20 30 40]); x([10 20 30 40])], [ones(1,4)*-2;ones(1,4)*2], ... + 'color', ones(1,3)*0.5, 'linestyle', ':'); +text(x(10), sin(x(10))-0.2, {'\uparrow','Line','gap'}, 'vert', 'top', 'horiz', 'center'); +text(x(20), sin(x(20))-0.2, {'\uparrow','Lower','bound','gap'}, 'vert', 'top', 'horiz', 'center'); +text(x(30), sin(x(30))-0.2, {'\uparrow','Upper','bound','gap'}, 'vert', 'top', 'horiz', 'center'); +text(x(40), sin(x(40))-0.2, {'\uparrow','Two-sided','bound','gap'}, 'vert', 'top', 'horiz', 'center'); + +axis tight equal; + +%% +% The default method for dealing with NaNs in boundedline is to leave the +% gap in the line, but smooth over the gap in the bounds based on the +% neighboring points. This option can be nice if you only have one or two +% missing points, and you're not interested in emphasizing those gaps in +% your plot: + +delete(he); +[hl,hp] = boundedline(x,y,b,'-bo', 'nan', 'fill'); +ho = outlinebounds(hl,hp); +set(ho, 'linestyle', ':', 'color', 'r', 'marker', '.'); + +%% +% I've added bounds outlines in a contrasting color so you can see how I'm +% handling individual points. +% +% The second option leaves a full gap in the patch for any NaN. I +% considered allowing one-sided gaps, but couldn't think of a good way to +% distinguish a gap from a zero-valued bound. I'm open to suggestions if +% you have any (email me). + +delete([hl hp ho]); +[hl,hp] = boundedline(x,y,b,'-bo', 'nan', 'gap'); +ho = outlinebounds(hl,hp); +set(ho, 'linestyle', ':', 'color', 'r', 'marker', '.'); + +%% +% The final option removes points from the patch that are NaNs. The visual +% result is very similar to the fill option, but the missing points are +% apparent if you plot the bounds outlines. + +delete([hl hp ho]); +[hl,hp] = boundedline(x,y,b,'-bo', 'nan', 'remove'); +ho = outlinebounds(hl,hp); +set(ho, 'linestyle', ':', 'color', 'r', 'marker', '.'); + + +%% Contributions +% +% Community contributions to this package are welcome! +% +% To report bugs, please submit +% on GitHub and +% include: +% +% * your operating system +% * your version of Matlab and all relevant toolboxes (type |ver| at the Matlab command line to get this info) +% * code/data to reproduce the error or buggy behavior, and the full text of any error messages received +% +% Please also feel free to submit enhancement requests, or to send pull +% requests (via GitHub) for bug fixes or new features. +% +% I do monitor the MatlabCentral FileExchange entry for any issues raised +% in the comments, but would prefer to track issues on GitHub. +% + diff --git a/Functions/helper_functions_community/boundedlines/README.md b/Functions/helper_functions_community/boundedlines/README.md new file mode 100644 index 0000000..c74349c --- /dev/null +++ b/Functions/helper_functions_community/boundedlines/README.md @@ -0,0 +1,279 @@ + +# boundedline.m: line with shaded error/confidence bounds + + +Author: Kelly Kearney +[![View boundedline.m on File Exchange](https://www.mathworks.com/matlabcentral/images/matlab-file-exchange.svg)](https://www.mathworks.com/matlabcentral/fileexchange/27485-boundedline-m) + + +This repository includes the code for the `boundedline.m` Matlab function and the accompanying `outlinebounds.m` function, along with all dependent functions required to run them. + + +The `boundedline` function allows a user to easily plot and line with a shaded patch around it. Ths sort of plot is often used to indicate uncertainty intervals or error bounds around a line. + + + +## Contents + + +- Getting started +- Syntax +- Example 1: Plotting lines using various syntax options +- Example 2: Filling gaps +- Contributions + +## Getting started + + +**Prerequisites** + + +This function requires Matlab R14 or later. + + +**Downloading and installation** + + +This code can be downloaded from [Github](https://github.com/kakearney/boundedline-pkg/) or the [MatlabCentral File Exchange](http://www.mathworks.com/matlabcentral/fileexchange/27485-boundedline-m). The File Exchange entry is updated daily from the GitHub repository. + + +**Matlab Search Path** + + +The following folders need to be added to your Matlab Search path (via `addpath`, `pathtool`, etc.): + + + +```matlab +boundedline-pkg/Inpaint_nans +boundedline-pkg/boundedline +boundedline-pkg/catuneven +boundedline-pkg/singlepatch +``` + + + +## Syntax + + +`boundedline(x, y, b)` plots a line with coordinates given by `x` and `y`, surrounded by a patch extending a certain distance `b` above/below that line. The dimensions of the `x`, `y`, and `b` arrays can vary to allow for multiple lines to be plotted at once, and for patch bounds to be either constant or varying along the length of the line. See function header help for full details of these variations. + + +`boundedline(..., 'alpha')` renders the bounded area patch using a partially-transparent patch the same color as the corresponding line(s). If not included, the bounded area will use a fully-opaque patch in a lighter shade of the corresponding line color. + + +`boundedline(..., 'transparency', transp)` indicates the tranparency or intensity of the bounds patch, using a scalar between 0 and 1. Default is 0.2. + + +`boundedline(..., 'orientation', orient)` indicates the orientation of the bounds. Orientation can be either `'vert'` for vertical (y-direction) bounds, or `'horiz'` for horizontal (x-direction) bounds. Default is `'vert'`. + + +`boundedline(..., 'nan', nanflag)` indicates how the bounds patch should handle NaNs in the line coordinates or bounds values. Options are `'fill'`, to smooth over the gap using neighboring values, `'gap'` to leave a blank space in the patch at those points, or `'remove'` to drop the NaN-points entirely, leading to linear interpolation of the gap in the patch. See examples below for more details on these options. + + +`boundedline(..., 'cmap', cmap)` colors the lines (in order of plotting) acording to the colors in this n x 3 colormap array, overriding any linespec or default colors. + + +`boundedline(..., ax)` plots the bounded line to the axis indicated by handle `ax`. If not included, the current axis is used. + + +`[hl, hp] = boundedline(...)` returns the handles the resulting line and patch object(s). + + +`hout = outlinebounds(hl, hp)` adds an outline to the bounds patch generated by `boundedline`, returning the handle of the resulting line object in `hout`. + + +Full details of all input and output variables for both functions can be accessed via the `help` function. + + + +## Example 1: Plotting lines using various syntax options + + +This example builds the 4-panel example image used on the MatlabCentral File Exchange, which shows several different methods for supplying line coordinates, bounds coordinates, and shading options. + + +The first axis plots two lines using the LineSpec option for input, which allows you to set line color, line color, and marker type for each line. The bounds on the first line vary over x, while the bounds on the second line are constant for all x. An outline is added to the bounds so the overlapping region can be seen more clearly. + + + +```matlab +x = linspace(0, 2*pi, 50); +y1 = sin(x); +y2 = cos(x); +e1 = rand(size(y1))*.5+.5; +e2 = [.25 .5]; + +ax(1) = subplot(2,2,1); +[l,p] = boundedline(x, y1, e1, '-b*', x, y2, e2, '--ro'); +outlinebounds(l,p); +title('Opaque bounds, with outline'); +axis tight; +``` + + +![](./readmeExtras/README_01.png) + +For our second axis, we use the same 2 lines, and this time assign x-varying bounds to both lines. Rather than using the LineSpec syntax, this example uses the default color order to assign the colors of the lines and patches. I also turn on the `'alpha'` option, which renders the patch with partial transparency. + + + +```matlab +ax(2) = subplot(2,2,2); +boundedline(x, [y1;y2], rand(length(y1),2,2)*.5+.5, 'alpha'); +title('Transparent bounds'); +axis tight; +``` + + +![](./readmeExtras/README_02.png) + +The bounds can also be assigned to a horizontal orientation, for a case where the x-axis represents the dependent variable. In this case, the scalar error bound value applies to both lines and both sides of the lines. + + + +```matlab +ax(3) = subplot(2,2,3); +boundedline([y1;y2], x, e1(1), 'orientation', 'horiz') +title('Horizontal bounds'); +axis tight; +``` + + +![](./readmeExtras/README_03.png) + +Rather than use a LineSpec or the default color order, a colormap array can be used to assign colors. In this case, increasingly-narrower bounds are added on top of the same line. + + + +```matlab +ax(4) = subplot(2,2,4); +boundedline(x, repmat(y1, 4,1), permute(0.5:-0.1:0.2, [3 1 2]), ... +'cmap', cool(4), ... +'transparency', 0.5); +title('Multiple bounds using colormap'); + +set(ax([1 2 4]), 'xlim', [0 2*pi]); +set(ax(3), 'ylim', [0 2*pi]); +axis tight; +``` + + +![](./readmeExtras/README_04.png) + + +## Example 2: Filling gaps + + +If you plot a line with one or more NaNs in either the `x` or `y` vector, the NaN location is rendered as a missing marker with a gap in the line. However, the `patch` command does not handle NaNs gracefully; it simply fails to show the patch at all if any of the coordinates include NaNs. + + +Because of this, the expected behavior of the patch part of boundedline when confronted with a NaN in either the bounds array (`b`) or the x/y-coordinates of the line (which are used to calculate the patch coordinates) is ambiguous. I offer a few options. + + +Before I demonstrate the options, I'll create a dataset that has a few different types of gaps: + + + +```matlab +x = linspace(0, 2*pi, 50); +y = sin(x); +b = [ones(size(y))*0.2; rand(size(y))*.5+.5]'; + +y(10) = NaN; % NaN in the line but not bounds +b(20,1) = NaN; % NaN in lower bound but not line +b(30,2) = NaN; % NaN in upper bound but not line +b(40,:) = NaN; % NaN in both sides of bound but not line +``` + + +Here's what that looks like in an errorbar plot. + + + +```matlab +figure; +he = errorbar(x,y,b(:,1), b(:,2), '-bo'); + + +line([x([10 20 30 40]); x([10 20 30 40])], [ones(1,4)*-2;ones(1,4)*2], ... +'color', ones(1,3)*0.5, 'linestyle', ':'); +text(x(10), sin(x(10))-0.2, {'\uparrow','Line','gap'}, 'vert', 'top', 'horiz', 'center'); +text(x(20), sin(x(20))-0.2, {'\uparrow','Lower','bound','gap'}, 'vert', 'top', 'horiz', 'center'); +text(x(30), sin(x(30))-0.2, {'\uparrow','Upper','bound','gap'}, 'vert', 'top', 'horiz', 'center'); +text(x(40), sin(x(40))-0.2, {'\uparrow','Two-sided','bound','gap'}, 'vert', 'top', 'horiz', 'center'); + +axis tight equal; +``` + + +![](./readmeExtras/README_05.png) + +The default method for dealing with NaNs in boundedline is to leave the gap in the line, but smooth over the gap in the bounds based on the neighboring points. This option can be nice if you only have one or two missing points, and you're not interested in emphasizing those gaps in your plot: + + + +```matlab +delete(he); +[hl,hp] = boundedline(x,y,b,'-bo', 'nan', 'fill'); +ho = outlinebounds(hl,hp); +set(ho, 'linestyle', ':', 'color', 'r', 'marker', '.'); +``` + + +![](./readmeExtras/README_06.png) + +I've added bounds outlines in a contrasting color so you can see how I'm handling individual points. + + +The second option leaves a full gap in the patch for any NaN. I considered allowing one-sided gaps, but couldn't think of a good way to distinguish a gap from a zero-valued bound. I'm open to suggestions if you have any (email me). + + + +```matlab +delete([hl hp ho]); +[hl,hp] = boundedline(x,y,b,'-bo', 'nan', 'gap'); +ho = outlinebounds(hl,hp); +set(ho, 'linestyle', ':', 'color', 'r', 'marker', '.'); +``` + + +![](./readmeExtras/README_07.png) + +The final option removes points from the patch that are NaNs. The visual result is very similar to the fill option, but the missing points are apparent if you plot the bounds outlines. + + + +```matlab +delete([hl hp ho]); +[hl,hp] = boundedline(x,y,b,'-bo', 'nan', 'remove'); +ho = outlinebounds(hl,hp); +set(ho, 'linestyle', ':', 'color', 'r', 'marker', '.'); +``` + + +![](./readmeExtras/README_08.png) + + +## Contributions + + +Community contributions to this package are welcome! + + +To report bugs, please submit [an issue](https://github.com/kakearney/boundedline-pkg/issues) on GitHub and include: + + + +- your operating system +- your version of Matlab and all relevant toolboxes (type `ver` at the Matlab command line to get this info) +- code/data to reproduce the error or buggy behavior, and the full text of any error messages received + +Please also feel free to submit enhancement requests, or to send pull requests (via GitHub) for bug fixes or new features. + + +I do monitor the MatlabCentral FileExchange entry for any issues raised in the comments, but would prefer to track issues on GitHub. + + + +[Published with MATLAB R2016b]("http://www.mathworks.com/products/matlab/") diff --git a/Functions/helper_functions_community/boundedlines/boundedline/.gitignore b/Functions/helper_functions_community/boundedlines/boundedline/.gitignore new file mode 100644 index 0000000..e492200 --- /dev/null +++ b/Functions/helper_functions_community/boundedlines/boundedline/.gitignore @@ -0,0 +1,37 @@ +# Compiled source # +################### +*.com +*.class +*.dll +*.exe +*.o +*.so + +# Packages # +############ +# it's better to unpack these files and commit the raw source +# git has its own built in compression methods +*.7z +*.dmg +*.gz +*.iso +*.jar +*.rar +*.tar +*.zip + +# Logs and databases # +###################### +*.log +*.sql +*.sqlite + +# OS generated files # +###################### +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db \ No newline at end of file diff --git a/Functions/helper_functions_community/boundedlines/boundedline/boundedline.m b/Functions/helper_functions_community/boundedlines/boundedline/boundedline.m new file mode 100644 index 0000000..2f36e7c --- /dev/null +++ b/Functions/helper_functions_community/boundedlines/boundedline/boundedline.m @@ -0,0 +1,501 @@ +function varargout = boundedline(varargin) +%BOUNDEDLINE Plot a line with shaded error/confidence bounds +% +% [hl, hp] = boundedline(x, y, b) +% [hl, hp] = boundedline(x, y, b, linespec) +% [hl, hp] = boundedline(x1, y1, b1, linespec1, x2, y2, b2, linespec2) +% [hl, hp] = boundedline(..., 'alpha') +% [hl, hp] = boundedline(..., ax) +% [hl, hp] = boundedline(..., 'transparency', trans) +% [hl, hp] = boundedline(..., 'orientation', orient) +% [hl, hp] = boundedline(..., 'nan', nanflag) +% [hl, hp] = boundedline(..., 'cmap', cmap) +% +% Input variables: +% +% x, y: x and y values, either vectors of the same length, matrices +% of the same size, or vector/matrix pair where the row or +% column size of the array matches the length of the vector +% (same requirements as for plot function). +% +% b: npoint x nside x nline array. Distance from line to +% boundary, for each point along the line (dimension 1), for +% each side of the line (lower/upper or left/right, depending +% on orientation) (dimension 2), and for each plotted line +% described by the preceding x-y values (dimension 3). If +% size(b,1) == 1, the bounds will be the same for all points +% along the line. If size(b,2) == 1, the bounds will be +% symmetrical on both sides of the lines. If size(b,3) == 1, +% the same bounds will be applied to all lines described by +% the preceding x-y arrays (only applicable when either x or +% y is an array). Bounds cannot include Inf, -Inf, or NaN, +% +% linespec: line specification that determines line type, marker +% symbol, and color of the plotted lines for the preceding +% x-y values. +% +% 'alpha': if included, the bounded area will be rendered with a +% partially-transparent patch the same color as the +% corresponding line(s). If not included, the bounded area +% will be an opaque patch with a lighter shade of the +% corresponding line color. +% +% ax: handle of axis where lines will be plotted. If not +% included, the current axis will be used. +% +% transp: Scalar between 0 and 1 indicating with the transparency or +% intensity of color of the bounded area patch. Default is +% 0.2. +% +% orient: direction to add bounds +% 'vert': add bounds in vertical (y) direction (default) +% 'horiz': add bounds in horizontal (x) direction +% +% nanflag: Sets how NaNs in the boundedline patch should be handled +% 'fill': fill the value based on neighboring values, +% smoothing over the gap +% 'gap': leave a blank space over/below the line +% 'remove': drop NaNs from patches, creating a linear +% interpolation over the gap. Note that this +% applies only to the bounds; NaNs in the line will +% remain. +% +% cmap: n x 3 colormap array. If included, lines will be colored +% (in order of plotting) according to this colormap, +% overriding any linespec or default colors. +% +% Output variables: +% +% hl: handles to line objects +% +% hp: handles to patch objects +% +% Example: +% +% x = linspace(0, 2*pi, 50); +% y1 = sin(x); +% y2 = cos(x); +% e1 = rand(size(y1))*.5+.5; +% e2 = [.25 .5]; +% +% ax(1) = subplot(2,2,1); +% [l,p] = boundedline(x, y1, e1, '-b*', x, y2, e2, '--ro'); +% outlinebounds(l,p); +% title('Opaque bounds, with outline'); +% +% ax(2) = subplot(2,2,2); +% boundedline(x, [y1;y2], rand(length(y1),2,2)*.5+.5, 'alpha'); +% title('Transparent bounds'); +% +% ax(3) = subplot(2,2,3); +% boundedline([y1;y2], x, e1(1), 'orientation', 'horiz') +% title('Horizontal bounds'); +% +% ax(4) = subplot(2,2,4); +% boundedline(x, repmat(y1, 4,1), permute(0.5:-0.1:0.2, [3 1 2]), ... +% 'cmap', cool(4), 'transparency', 0.5); +% title('Multiple bounds using colormap'); + + +% Copyright 2010 Kelly Kearney + +%-------------------- +% Parse input +%-------------------- + +% Color and cmap are mechanically the same: + +tmp = strncmpi(varargin,'color',3); +if any(tmp) + varargin{tmp} = 'cmap'; +end + +% Alpha flag + +isalpha = cellfun(@(x) ischar(x) && strcmp(x, 'alpha'), varargin); +if any(isalpha) + usealpha = true; + varargin = varargin(~isalpha); +else + usealpha = false; +end + +% Axis + +isax = cellfun(@(x) isscalar(x) && ishandle(x) && strcmp('axes', get(x,'type')), varargin); +if any(isax) + hax = varargin{isax}; + varargin = varargin(~isax); +else + hax = gca; +end + +% Transparency + +[found, trans, varargin] = parseparam(varargin, 'transparency'); + +if ~found + trans = 0.2; +end + +if ~isscalar(trans) || trans < 0 || trans > 1 + error('Transparency must be scalar between 0 and 1'); +end + +% Orientation + +[found, orient, varargin] = parseparam(varargin, 'orientation'); + +if ~found + orient = 'vert'; +end + +if strcmp(orient, 'vert') + isvert = true; +elseif strcmp(orient, 'horiz') + isvert = false; +else + error('Orientation must be ''vert'' or ''horiz'''); +end + +% Colormap + +[hascmap, cmap, varargin] = parseparam(varargin, 'cmap'); + +% NaN flag + +[found, nanflag, varargin] = parseparam(varargin, 'nan'); +if ~found + nanflag = 'fill'; +end +if ~ismember(nanflag, {'fill', 'gap', 'remove'}) + error('Nan flag must be ''fill'', ''gap'', or ''remove'''); +end + +[haslw, lwidth, varargin] = parseparam(varargin, 'linewidth'); +if ~haslw + lwidth = get(0, 'DefaultLineLineWidth'); +end + +% X, Y, E triplets, and linespec + +[x,y,err,linespec] = deal(cell(0)); +while ~isempty(varargin) + if length(varargin) < 3 + error('Unexpected input: should be x, y, bounds triplets'); + end + if all(cellfun(@isnumeric, varargin(1:3))) + x = [x varargin(1)]; + y = [y varargin(2)]; + err = [err varargin(3)]; + varargin(1:3) = []; + else + if any(cellfun(@(x) isa(x, 'datetime'), varargin(1:3))) + % Special error message for most likely culprit: datetimes + error('boundedline cannot support datetime input due to incompatibility between patches and datetime axes; please convert to datenumbers instead'); + else + % Otherwise + error('Unexpected input: should be numeric x, y, bounds triplets'); + end + end + if ~isempty(varargin) && ischar(varargin{1}) + linespec = [linespec varargin(1)]; + varargin(1) = []; + else + linespec = [linespec {[]}]; + end +end + +%-------------------- +% Reformat x and y +% for line and patch +% plotting +%-------------------- + +% Calculate y values for bounding lines + +plotdata = cell(0,7); + +htemp = figure('visible', 'off'); +for ix = 1:length(x) + + % Get full x, y, and linespec data for each line (easier to let plot + % check for properly-sized x and y and expand values than to try to do + % it myself) + + try + if isempty(linespec{ix}) + hltemp = plot(x{ix}, y{ix}); + else + hltemp = plot(x{ix}, y{ix}, linespec{ix}); + end + catch + close(htemp); + error('X and Y matrices and/or linespec not appropriate for line plot'); + end + + linedata = get(hltemp, {'xdata', 'ydata', 'marker', 'linestyle', 'color'}); + + nline = size(linedata,1); + + % Expand bounds matrix if necessary + + if nline > 1 + if ndims(err{ix}) == 3 + err2 = squeeze(num2cell(err{ix},[1 2])); + else + err2 = repmat(err(ix),nline,1); + end + else + err2 = err(ix); + end + + % Figure out upper and lower bounds + + [lo, hi] = deal(cell(nline,1)); + for iln = 1:nline + + x2 = linedata{iln,1}; + y2 = linedata{iln,2}; + nx = length(x2); + + if isvert + lineval = y2; + else + lineval = x2; + end + + sz = size(err2{iln}); + + if isequal(sz, [nx 2]) + lo{iln} = lineval - err2{iln}(:,1)'; + hi{iln} = lineval + err2{iln}(:,2)'; + elseif isequal(sz, [nx 1]) + lo{iln} = lineval - err2{iln}'; + hi{iln} = lineval + err2{iln}'; + elseif isequal(sz, [1 2]) + lo{iln} = lineval - err2{iln}(1); + hi{iln} = lineval + err2{iln}(2); + elseif isequal(sz, [1 1]) + lo{iln} = lineval - err2{iln}; + hi{iln} = lineval + err2{iln}; + elseif isequal(sz, [2 nx]) % not documented, but accepted anyways + lo{iln} = lineval - err2{iln}(:,1); + hi{iln} = lineval + err2{iln}(:,2); + elseif isequal(sz, [1 nx]) % not documented, but accepted anyways + lo{iln} = lineval - err2{iln}; + hi{iln} = lineval + err2{iln}; + elseif isequal(sz, [2 1]) % not documented, but accepted anyways + lo{iln} = lineval - err2{iln}(1); + hi{iln} = lineval + err2{iln}(2); + else + error('Error bounds must be npt x nside x nline array'); + end + + end + + % Combine all data (xline, yline, marker, linestyle, color, lower bound + % (x or y), upper bound (x or y) + + plotdata = [plotdata; linedata lo hi]; + +end +close(htemp); + +% Override colormap + +if hascmap + nd = size(plotdata,1); + cmap = repmat(cmap, ceil(nd/size(cmap,1)), 1); + cmap = cmap(1:nd,:); + plotdata(:,5) = num2cell(cmap,2); +end + + +%-------------------- +% Plot +%-------------------- + +% Setup of x and y, plus line and patch properties + +nline = size(plotdata,1); +[xl, yl, xp, yp, marker, lnsty, lncol, ptchcol, alpha] = deal(cell(nline,1)); + +for iln = 1:nline + xl{iln} = plotdata{iln,1}; + yl{iln} = plotdata{iln,2}; +% if isvert +% xp{iln} = [plotdata{iln,1} fliplr(plotdata{iln,1})]; +% yp{iln} = [plotdata{iln,6} fliplr(plotdata{iln,7})]; +% else +% xp{iln} = [plotdata{iln,6} fliplr(plotdata{iln,7})]; +% yp{iln} = [plotdata{iln,2} fliplr(plotdata{iln,2})]; +% end + + [xp{iln}, yp{iln}] = calcpatch(plotdata{iln,1}, plotdata{iln,2}, isvert, plotdata{iln,6}, plotdata{iln,7}, nanflag); + + marker{iln} = plotdata{iln,3}; + lnsty{iln} = plotdata{iln,4}; + + if usealpha + lncol{iln} = plotdata{iln,5}; + ptchcol{iln} = plotdata{iln,5}; + alpha{iln} = trans; + else + lncol{iln} = plotdata{iln,5}; + ptchcol{iln} = interp1([0 1], [1 1 1; lncol{iln}], trans); + alpha{iln} = 1; + end +end + +% Plot patches and lines + +if verLessThan('matlab', '8.4.0') + [hp,hl] = deal(zeros(nline,1)); +else + [hp,hl] = deal(gobjects(nline,1)); +end + + +for iln = 1:nline + hp(iln) = patch(xp{iln}, yp{iln}, ptchcol{iln}, ... + 'facealpha', alpha{iln}, ... + 'edgecolor', 'none', ... + 'parent', hax); +end + +for iln = 1:nline + hl(iln) = line(xl{iln}, yl{iln}, ... + 'marker', marker{iln}, ... + 'linestyle', lnsty{iln}, ... + 'color', lncol{iln}, ... + 'linewidth', lwidth, ... + 'parent', hax); +end + +%-------------------- +% Assign output +%-------------------- + +nargoutchk(0,2); + +if nargout >= 1 + varargout{1} = hl; +end + +if nargout == 2 + varargout{2} = hp; +end + +%-------------------- +% Parse optional +% parameters +%-------------------- + +function [found, val, vars] = parseparam(vars, param) + +isvar = cellfun(@(x) ischar(x) && strcmpi(x, param), vars); + +if sum(isvar) > 1 + error('Parameters can only be passed once'); +end + +if any(isvar) + found = true; + idx = find(isvar); + val = vars{idx+1}; + vars([idx idx+1]) = []; +else + found = false; + val = []; +end + +%---------------------------- +% Calculate patch coordinates +%---------------------------- + +function [xp, yp] = calcpatch(xl, yl, isvert, lo, hi, nanflag) + +ismissing = isnan([xl;yl;lo;hi]); + +% If gap method, split + +if any(ismissing(:)) && strcmp(nanflag, 'gap') + + tmp = [xl;yl;lo;hi]; + + idx = find(any(ismissing,1)); + n = diff([0 idx length(xl)]); + + tmp = mat2cell(tmp, 4, n); + isemp = cellfun('isempty', tmp); + tmp = tmp(~isemp); + + tmp = cellfun(@(a) a(:,~any(isnan(a),1)), tmp, 'uni', 0); + isemp = cellfun('isempty', tmp); + tmp = tmp(~isemp); + + xl = cellfun(@(a) a(1,:), tmp, 'uni', 0); + yl = cellfun(@(a) a(2,:), tmp, 'uni', 0); + lo = cellfun(@(a) a(3,:), tmp, 'uni', 0); + hi = cellfun(@(a) a(4,:), tmp, 'uni', 0); +else + xl = {xl}; + yl = {yl}; + lo = {lo}; + hi = {hi}; +end + +[xp, yp] = deal(cell(size(xl))); + +for ii = 1:length(xl) + + iseq = ~verLessThan('matlab', '8.4.0') && isequal(lo{ii}, hi{ii}); % deal with zero-width bug in R2014b/R2015a + + if isvert + if iseq + xp{ii} = [xl{ii} nan(size(xl{ii}))]; + yp{ii} = [lo{ii} fliplr(hi{ii})]; + else + xp{ii} = [xl{ii} fliplr(xl{ii})]; + yp{ii} = [lo{ii} fliplr(hi{ii})]; + end + else + if iseq + xp{ii} = [lo{ii} fliplr(hi{ii})]; + yp{ii} = [yl{ii} nan(size(yl{ii}))]; + else + xp{ii} = [lo{ii} fliplr(hi{ii})]; + yp{ii} = [yl{ii} fliplr(yl{ii})]; + end + end + + if strcmp(nanflag, 'fill') + xp{ii} = inpaint_nans(xp{ii}', 4); + yp{ii} = inpaint_nans(yp{ii}', 4); + if iseq % need to maintain NaNs for zero-width bug + nx = length(xp{ii}); + xp{ii}((nx/2)+1:end) = NaN; + end + elseif strcmp(nanflag, 'remove') + if iseq + nx = length(xp{ii}); + keepnan = false(size(xp)); + keepnan((nx/2)+1:end) = true; + isn = (isnan(xp{ii}) | isnan(yp{ii})) & ~keepnan; + else + isn = isnan(xp{ii}) | isnan(yp{ii}); + end + xp{ii} = xp{ii}(~isn); + yp{ii} = yp{ii}(~isn); + end + +end + +if strcmp(nanflag, 'gap') + [xp, yp] = singlepatch(xp, yp); +else + xp = xp{1}; + yp = yp{1}; +end + diff --git a/Functions/helper_functions_community/boundedlines/boundedline/outlinebounds.m b/Functions/helper_functions_community/boundedlines/boundedline/outlinebounds.m new file mode 100644 index 0000000..36724bd --- /dev/null +++ b/Functions/helper_functions_community/boundedlines/boundedline/outlinebounds.m @@ -0,0 +1,43 @@ +function hnew = outlinebounds(hl, hp) +%OUTLINEBOUNDS Outline the patch of a boundedline +% +% hnew = outlinebounds(hl, hp) +% +% This function adds an outline to the patch objects created by +% boundedline, matching the color of the central line associated with each +% patch. +% +% Input variables: +% +% hl: handles to line objects from boundedline +% +% hp: handles to patch objects from boundedline +% +% Output variables: +% +% hnew: handle to new line objects + +% Copyright 2012 Kelly Kearney + + +hnew = zeros(size(hl)); +for il = 1:numel(hp) + col = get(hl(il), 'color'); + xy = get(hp(il), {'xdata','ydata'}); + ax = ancestor(hl(il), 'axes'); + + nline = size(xy{1},2); + if mod(size(xy{1}, 1), 2) == 0 + % Insert a NaN between upper and lower lines, so they're disconnected + L = size(xy{1}, 1) / 2; + xy{1} = [xy{1}(1:L, :); nan(1, nline); xy{1}(L+1:end, :)]; + xy{2} = [xy{2}(1:L, :); nan(1, nline); xy{2}(L+1:end, :)]; + end + if nline > 1 + xy{1} = reshape([xy{1}; nan(1,nline)], [], 1); + xy{2} = reshape([xy{2}; nan(1,nline)], [], 1); + end + hnew(il) = line(xy{1}, xy{2}, 'parent', ax, 'linestyle', '-', 'color', col); + +end + diff --git a/Functions/helper_functions_community/boundedlines/catuneven/.gitignore b/Functions/helper_functions_community/boundedlines/catuneven/.gitignore new file mode 100644 index 0000000..e492200 --- /dev/null +++ b/Functions/helper_functions_community/boundedlines/catuneven/.gitignore @@ -0,0 +1,37 @@ +# Compiled source # +################### +*.com +*.class +*.dll +*.exe +*.o +*.so + +# Packages # +############ +# it's better to unpack these files and commit the raw source +# git has its own built in compression methods +*.7z +*.dmg +*.gz +*.iso +*.jar +*.rar +*.tar +*.zip + +# Logs and databases # +###################### +*.log +*.sql +*.sqlite + +# OS generated files # +###################### +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db \ No newline at end of file diff --git a/Functions/helper_functions_community/boundedlines/catuneven/catuneven.m b/Functions/helper_functions_community/boundedlines/catuneven/catuneven.m new file mode 100644 index 0000000..b302c0f --- /dev/null +++ b/Functions/helper_functions_community/boundedlines/catuneven/catuneven.m @@ -0,0 +1,57 @@ +function b = catuneven(dim, padval, varargin) +%CATUNEVEN Concatenate unequally-sized arrays, padding with a value +% +% This function is similar to cat, except it does not require the arrays to +% be equally-sized along non-concatenated dimensions. Instead, all arrays +% are padded to be equally-sized using the value specified. +% +% b = catuneven(dim, padval, a1, a2, ...) +% +% Input variables: +% +% dim: dimension along which to concatenate +% +% padval: value used as placeholder when arrays are expanded +% +% a#: arrays to be concatenated, numerical +% +% Output variables: +% +% b: concatenated array + +% Copyright 2013 Kelly Kearney + +ndim = max(cellfun(@ndims, varargin)); +ndim = max(ndim, dim); + +for ii = 1:ndim + sz(:,ii) = cellfun(@(x) size(x, ii), varargin); +end +maxsz = max(sz, [], 1); + +nv = length(varargin); +val = cell(size(varargin)); +for ii = 1:nv + sztmp = maxsz; + sztmp(dim) = sz(ii,dim); + + idx = cell(ndim,1); + [idx{:}] = ind2sub(sz(ii,:), 1:numel(varargin{ii})); + + idxnew = sub2ind(sztmp, idx{:}); + + try + val{ii} = ones(sztmp) * padval; + catch + val{ii} = repmat(padval, sztmp); + end + val{ii}(idxnew) = varargin{ii}; + +end + +b = cat(dim, val{:}); + + + + + diff --git a/Functions/helper_functions_community/boundedlines/readmeExtras/README_01.png b/Functions/helper_functions_community/boundedlines/readmeExtras/README_01.png new file mode 100644 index 0000000000000000000000000000000000000000..2bd5bcce228e9984aed32a1d8a31a2d6b3f795ed GIT binary patch literal 17036 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%0D+KberB*Vp^?fC2+gW)`Wu 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%0D+KberB*Vp^?fC2+gW)`Wu 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%0D+KberB*Vp^?fC2+gW)`Wu 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

@PY~vdE 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%0DqEBysTcF{SiH-TdkEi_PLniAO)Q+q@elb_QA-hb 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 literal 0 HcmV?d00001 diff --git a/Functions/helper_functions_community/boundedlines/readmeExtras/README_05.png b/Functions/helper_functions_community/boundedlines/readmeExtras/README_05.png new file mode 100644 index 0000000000000000000000000000000000000000..0fdc67be1763eeeaa915752d06d86df6c0f19461 GIT binary patch literal 16870 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 P0-C&xvUGvugJ=IAFrmwJ literal 0 HcmV?d00001 diff --git a/Functions/helper_functions_community/boundedlines/readmeExtras/README_06.png b/Functions/helper_functions_community/boundedlines/readmeExtras/README_06.png new file mode 100644 index 0000000000000000000000000000000000000000..0e7bd7acdfb1924efd2ca1ce38c7f2b9d744e31b GIT binary patch literal 24808 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 literal 0 HcmV?d00001 diff --git a/Functions/helper_functions_community/boundedlines/readmeExtras/README_07.png b/Functions/helper_functions_community/boundedlines/readmeExtras/README_07.png new file mode 100644 index 0000000000000000000000000000000000000000..4ed1ae9c044a5201d5ce5c0ed0748a391e4b552e GIT binary patch literal 25461 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 literal 0 HcmV?d00001 diff --git a/Functions/helper_functions_community/boundedlines/readmeExtras/README_08.png b/Functions/helper_functions_community/boundedlines/readmeExtras/README_08.png new file mode 100644 index 0000000000000000000000000000000000000000..29855165d33d9c411c93f37b03050d41cec98df2 GIT binary patch literal 24740 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 BKSKZj literal 0 HcmV?d00001 diff --git a/Functions/helper_functions_community/schemer/.gitattributes b/Functions/helper_functions_community/schemer/.gitattributes new file mode 100644 index 0000000..07ef0d9 --- /dev/null +++ b/Functions/helper_functions_community/schemer/.gitattributes @@ -0,0 +1,46 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# +# The above will handle all files NOT found below +# + +##--------------------------------------------------- +# Common .gitattributes +##--------------------------------------------------- + +# Documents +# ============ +*.txt text +*.md text + +# Graphics +# ============ +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.tif binary +*.tiff binary +*.ico binary +*.svg text +*.eps binary + + +##--------------------------------------------------- +# Basic .gitattributes for a MATLAB repo +##--------------------------------------------------- + +# Source files +# ============ +*.m text +*.prf text + +# Binary files +# ============ +*.p binary +*.mex* binary +*.fig binary +*.mat binary +*.mdl binary +*.slx binary diff --git a/Functions/helper_functions_community/schemer/.gitignore b/Functions/helper_functions_community/schemer/.gitignore new file mode 100644 index 0000000..f7bb9d7 --- /dev/null +++ b/Functions/helper_functions_community/schemer/.gitignore @@ -0,0 +1,5 @@ +# Editor autosave files +*~ +*.asv +# Compiled MEX binaries (all platforms) +*.mex* diff --git a/Functions/helper_functions_community/schemer/CONTRIBUTING.md b/Functions/helper_functions_community/schemer/CONTRIBUTING.md new file mode 100644 index 0000000..2429e1c --- /dev/null +++ b/Functions/helper_functions_community/schemer/CONTRIBUTING.md @@ -0,0 +1,47 @@ +Contributing +============ + +Adding a new colour scheme +-------------------------- + +If you have created a new colour scheme, please add it to the [matlab-schemes] +repository and *not* here. Instructions for this are available +[here](https://github.com/scottclowe/matlab-schemes/blob/master/CONTRIBUTING.md). + +If you have already added a new colour scheme to [matlab-schemes] via an +accepted pull request and want to mirror the change in [matlab-schemer], you +will need to use [git-subtree]. + +Some general handy instructions for `git subtree` are available +[here](https://medium.com/@v/git-subtrees-a-tutorial-6ff568381844#.lwmv2uwwk), +[here](https://blogs.atlassian.com/2013/05/alternatives-to-git-submodule-git-subtree/), +and [here](https://developer.atlassian.com/blog/2015/05/the-power-of-git-subtree/). +Specifically for use with this repository, the instructions are as follows. + +You will first need to set up subtree so there is a remote tracking the +[matlab-schemes] repository in addition to the usual origin remote tracking +your fork of [matlab-schemer]. +```bash +# One-time setup +git remote add matlab-schemes https://github.com/scottclowe/matlab-schemes.git +``` + +Having done this setup, you can now merge commits from matlab-schemes into +matlab-schemer as follows: +```bash +git pull +git subtree pull --prefix schemes matlab-schemes master +git push +``` + +Improving MATLAB Schemer +------------------------ + +If you have made enhancements or bug fixes to MATLAB Schemer, itself please add +them with a regular [pull request] to this repository. + + + [matlab-schemer]: https://github.com/scottclowe/matlab-schemer + [matlab-schemes]: https://github.com/scottclowe/matlab-schemes + [git-subtree]: https://github.com/git/git/blob/master/contrib/subtree/git-subtree.txt + [pull request]: https://help.github.com/articles/using-pull-requests/ diff --git a/Functions/helper_functions_community/schemer/LICENSE b/Functions/helper_functions_community/schemer/LICENSE new file mode 100644 index 0000000..c3bcef6 --- /dev/null +++ b/Functions/helper_functions_community/schemer/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2013-2016, Scott C. Lowe +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/Functions/helper_functions_community/schemer/README.md b/Functions/helper_functions_community/schemer/README.md new file mode 100644 index 0000000..ec09f3f --- /dev/null +++ b/Functions/helper_functions_community/schemer/README.md @@ -0,0 +1,204 @@ +[![MATLAB FileExchange](https://img.shields.io/badge/MATLAB-FileExchange-orange.svg)][fex] + + +MATLAB Schemer +============== + +This MATLAB package makes it easy to change the color scheme (a.k.a. theme) of +the MATLAB display and GUI. + +You can use Schemer to import a predefined color scheme, transfer your color +settings between installations, or create your own color scheme. + +A collection of color schemes is available in the schemes folder. +Samples of these can be seen in [schemes/README.md](schemes/README.md), +and browsed in the folder [schemes/screenshots](schemes/screenshots). +This is a static subtree copy of the contents of the repository +[matlab-schemes]. + +![MATLAB Schemer logo](logo.png) + + +Importing a color scheme +------------------------ + +Color schemes can be easily imported by running `schemer_import` at the MATLAB +command prompt, without needing any inputs. This will open a GUI to select the +file to import the color scheme from. + +When importing a color scheme, most of the settings will change immediately. +However, some settings will require MATLAB to be restarted: +- Variable highlighting colours +- Wavy underlines for errors +- Wavy underlines for warnings + +For more details, see the documentation on the function `schemer_import`, +available with `help schemer_import`. + + +Transferring a color scheme between MATLAB installations +-------------------------------------------------------- + +If you are using a personalised color scheme in MATLAB, you may wish to save +it for yourself so you can re-implement it easily if you reintall MATLAB. +Or you may wish to transfer your personalised color scheme from one machine +to another. This section describes the steps relevant in either scenario. + +On the source machine, run `schemer_export` to save a temporary color scheme +file, and then transfer this file to the destination machine. + +When importing the new color scheme, instead of running `schemer_import` without +any inputs, you should run `schemer_import(true)`. This passes a flag to tell +the function to not only import the colour preferences, but to also import your +boolean settings (such as whether to highlight the current cell/line, etc) +from the file you exported from the source installation. + + +Reverting to the MATLAB default color scheme +-------------------------------------------- + +Should you wish to revert to the set of colours which MATLAB ships with, you +should run `schemer_import('schemes/default.prf')`. + +This will import the MATLAB default theme from the stylesheet `default.prf` +which comes as part of Schemer. + +To restore the out-of-the-box state for boolean settings (such as whether to +highlight the current line) in addition to the colours, run +`schemer_import('schemes/default.prf', true)`. + +You may be tempted to instead revert the colours by clicking the +`Restore Default Colors` buttons in the `Color` pane of the MATLAB preferences. +However, this will be less effective than importing the default colours through +Schemer because these buttons will not reset all the MATLAB colour preferences. +(There is no reset button for the Editor display preferences pane, nor for the +other language syntax supported by MATLAB, which is available in the Editor +Languages panel.) + + +Creating a color scheme for others to use +----------------------------------------- + +When creating a color scheme to share with the rest of the world, it is +recommended to ensure colours are chosen appropriately for all possible +settings, even if they are not enabled. + +For example, if you are creating a dark colour scheme, you may have cell +highlighting disabled but it would still be ill-advised to leave the background +highlight colour for cell displays as the default pale beige because other users +may have this setting enabled. + + +### Exporting through the GUI + +If you have made a custom color scheme using the MATLAB GUI to pick the colours, +you can export the new color scheme with `schemer_export`. + +Please note, this requires you to have visited all relevant panes of the +Preferences window at least once since MATLAB was installed, even if the +settings have not been changed from the default. +See the help for `schemer_export` for more details. + + +### Porting a theme from a different editor + +If you are converting a color scheme designed for another editor into a MATLAB +stylesheet, you may find it easier to start with a duplicate of the template +stylesheet [develop/template_scheme.prf](develop/template_scheme.prf) and copy +the colours into this. + +To understand which colour settings the parameter names correspond to, you can +refer to the annotated screenshot, +[develop/annotated_default.png](develop/annotated_default.png). + +![Annotated screenshot](develop/annotated_default.png) + +The values for each colour in the .prf stylesheet must be recorded as single +RGB integers, with each colour channel in 8-bit (0-255), R as big endian, +and a opaque alpha channel. Because the format for RGB colours in Java allows +for an alpha channel and the integers are signed, all the colours you record +in your .prf file should be negative, spanning the range +`-16777216` (black, `[0,0,0]`) to `-1` (white, `[255,255,255]`). + +The text file for your pre-existing theme will typically specify its colous in +hexadecimal format, or in terms of R, G, B values. You will need +to convert the colours from this format into the format which MATLAB preference +files use to specify colours instead. +The Schemer package comes with a utility function +[develop/color2javaRGBint.m](develop/color2javaRGBint.m) +to help make this easier. See the `color2javaRGBint` documentation for more +details. + + +### Setting colours for additional languages + +MATLAB supports syntax highlighting for several languanges in addition to its own. +Currently these languages are +MuPAD, TLC, VRML, C/C++, Java, VHDL, Verilog, and XML/HTML. +The colours used for the syntax highlighting of all these languages can be set +in the Languages subpanel of the Editor/Debugger pane in the Preferences window. + +Typically, one will want to make a color scheme which has colours in these +languages which match the colours of the analogous MATLAB syntax. +By default, this is the behaviour which Schemer will perform. + +Without specifying any inputs to `schemer_export`, an exported color scheme file +will contain only the colours used for MATLAB syntax highlighting and no other +languages, since it is expected that users will typically not set the colours +for any of these languages. When this is loaded with `schemer_import`, the missing +colours for additional languages are automatically completed based on the MATLAB +syntax. + +If you do customise the colours for the additional language syntax highlighting, +you can export this by setting an appropriate flag, such as `schemer_export(3)`. +For more details, see the `schemer_export` documentation. + +If you are designing a color scheme and are very detail-oriented, you may like to +do the following. + +1. Configure the colours for the MATLAB syntax first in the Preferences > Colors + panel and Programming Tools subpanel, along with Editor/Debugger > Display + preference pane for the Current line highlighting and Right-hand text limit. +2. Export the colorscheme with `schemer_export('tmp.prf',1)`, excluding additional + language syntax from the export. +3. Load the colorscheme with `schemer_import('tmp.prf')`, which overwrites the + syntax for the additional languages with automatically inferred colours. +4. Open up the panel Preferences > Editor/Debugger > Languages and inspect the + quality of the colorscheme in the addition languages. +5. Fix or improve any perceived issues with the colorscheme in the Languages panel. +6. Export the colorscheme again, but this time with `schemer_export(3)` to include + the customised colours for the additional languages. + + +### Adding your scheme to this package + +If you are particularly proud of your new color scheme (and why wouldn't you be) +and would like to to share it with the world as part of the `matlab-schemer` +package, this is possible! + +Please head over to our daughter repository, [matlab-schemes][], which holds the +master copy of the color schemes issued here as part of Schemer in the schemes +directory. + +If you fork [matlab-schemes][], add your `.prf` file and issue a pull request, +the new scheme will be reviewed and added to the repository. +This content is then mirrored here using `git subtree`. For more details, see +our [CONTRIBUTING.md](CONTRIBUTING.md). + + +Addendum +-------- + +### Requirements + +Please note that Schemer requires MATLAB to be run with Java support enabled. + + +### Further information + +For details on how the method was implemented, see +[this Undocumented Matlab article](http://undocumentedmatlab.com/blog/changing-system-preferences-programmatically). + + + [matlab-schemes]: https://github.com/scottclowe/matlab-schemes + [fex]: http://mathworks.com/matlabcentral/fileexchange/53862-matlab-schemer diff --git a/Functions/helper_functions_community/schemer/develop/RGBint2hex.m b/Functions/helper_functions_community/schemer/develop/RGBint2hex.m new file mode 100644 index 0000000..862a82e --- /dev/null +++ b/Functions/helper_functions_community/schemer/develop/RGBint2hex.m @@ -0,0 +1,42 @@ +%RGBINT2HEX Convert RGB integer into hexadecimal colour +% HEX = RGBINT2HEX(INT) given an RGB integer, convert it into a +% hexadecimal string. +% +% This is a helper function which you can use when manually creating your +% own MATLAB colour schemes. It is the inverse of COLOR2JAVARGBINT. +% +% See also COLOR2JAVARGBINT. + +% Copyright (c) 2015-2016, Scott C. Lowe +% All rights reserved. +% +% Redistribution and use in source and binary forms, with or without +% modification, are permitted provided that the following conditions are +% met: +% * Redistributions of source code must retain the above copyright +% notice, this list of conditions and the following disclaimer. +% * Redistributions in binary form must reproduce the above copyright +% notice, this list of conditions and the following disclaimer in +% the documentation and/or other materials provided with the distribution +% +% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +% POSSIBILITY OF SUCH DAMAGE. + +function hex = RGBint2hex(int) + +% Make a Java colour object +jc = java.awt.Color(int); +% Convert R,G,B into hexadecimal +hex = sprintf('%02s', ... + dec2hex(jc.getRed), dec2hex(jc.getGreen), dec2hex(jc.getBlue)); + +end diff --git a/Functions/helper_functions_community/schemer/develop/annotated_default.png b/Functions/helper_functions_community/schemer/develop/annotated_default.png new file mode 100644 index 0000000000000000000000000000000000000000..e41e9c58a28a9d2f92d1cef698af9aba13c0463e GIT binary patch literal 60704 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�d<7d{KaQAc=lxr)x-2;vFTnE1hvz0ZBSmq|OitGM9;7SG_~4fu{* z3|q0|T=J^*HLARamy}w>i6)XD(hEQFuOdJwByO@|^@uC0?qzivH-INK ze2i(pVkB)yZC<~j+PO;Gmwa*_6=VUr4d_0{ufSI=gK1`-L%dnyxHNLMbIOtN;N;GF zH{3JQ5`+1tSvD3OGvTk_WnwZALoH~e)7@e&#gxT~5g^jx<#xCQg@=&xA`iZ~_^0V( zf2P&@-roiTqYj9(<&nE`1WuhXcrSa0OkVp*srS zyY+G_V;vcr>+t7@UZEiGrRR02nlHoM8V`e+$j=j>Mf5Ufu3ks`8iDQ3c9Cvg`CMl4 zPp)%%ipqvc?dzT{D_LQ8*W-^c&m`xx%>=k2~QSJOs?>(7Az{%qg1ld0&rzF29{ zweyjc_f;gg543E3M_J!=7sLP2@Z6vRpLzMCO?Ef-fD1S4hH}wD$Lgyk28TW~(Z<{7vc<&gH@B4A*SD7e3y|Ry-x1D_V5Zg_BAL+^A@<1L;akwh zJBtD%yX)qL0kBB_4cyMpVKw6k*i)!r#sCs!yYx}33HGiX{#c|LM@Rls8;0q+m|xJ7 zZZiv(W)+n&p59n#zncWQcZc_dryZSmGC!b=L&o+D7kzi;-CT2zaPF5>1UvHs+rU1+ z7qp_T{Yvm$!dUh!I}5lBeL; zA|KC?s+Ye%!NdnIqq~Y;NJh!oUJq{me_Xv~SerrDE!-Ab+`TxoNb%zCP~6>$L-FDg z(&8>H?oy<<2TyT|7MCDJf&~cfgp)qc`+ncK&cEc!ea~cc?^$cj-i2`kZ)KXDqZ4Rf z>G3Q013!FCE3*Q^91ktX$QSypud_tCbmycxKCI*AhFR_jBEqxo3@#?Lc~7L8RYZ{H zcYZs&24(F|@((}J@Zi@w?{(?D<=>ZYEvHl(KB|?k!-IMhF5aR5ummL$o5xO=GMDgc zD9U7n%idLTxzkhJC&@o2{y>EH0$;-2?P)z;N;>aKHOEj^?fNB=erF1G3La1o=V0<& zQ#`Vm0j(ey$lGpJOL1(Cqx7A)GXdZy>YMw^iqu1IAwkWjcJ=Q7GWu5RYzR&*UcyWF zU2%y>gOrNwc8!yE4fuFHpeurHE?xl2{@sRmlI0m% z+rjwAHcBMqc8lqi|FSc{15~#pez!Sf!Nymm(j<|{}h3L`5=d|ifft(dK}W0_ja zNZx6DkNVJt*5x7kA21JPFi~D1jbLLWHp=gY0;w>i#L7GlBu?v!d~};J3l0m3>8QkB z9i1~URQsSn<}CFTMBeOzX6jy})3h(rGkT-7SGy)iIwRc69%5i|EK6rR3ek)3IoG+$ z;H>=_ZjnIlb8E%ic}k;*N8G;b0X^SQ$5AfS?{r(N{t*(DB78FEs$YWW_~ZGOK!~X8B?6V*^+)3jZZyK zgM|)OqGV({oTrf%?NZ*>rV#w}ysKlSyci-INswMdnD1X8#x)kFM{0b_I&V#4lD_Z3d){ZQ^=X)BST zKlt&9U1hscLji;1i`T&s!N-=m%aS>cRJN}!kdwi{td(X(u^MJ5t+%dr5izLiv9 z##qa_guy0%9Qn-7a24kTzw}9!9=dY(X1Edt6)!7NSnXd0ZuCtSLVuogY%G$k#fwx{ zdd%bkD6SICWiR(=gj|Pu7Qu$`2P-|FJ!+-IFZhVldy;||$<`Cw|C;JvkSt0DQ_roQ zhs$|)kt01IP5b=lnC=jCSOUS9E+^)Tycg+by8ntWbz$H8ygdVDIHXL=zPyJpo@~dS zY-w;GT3zU!az82<1Rhntu{sgzvxk=mYF&$AM|OwPKmGycCbVDS9(K$ZEi3wOJ3hHm zQk9>%74Nat0b7Ylw%Tmt4ur_>nceU`M%l^#pxqbUvJ8!CFH8RY1KO(swl4MukFfrE zRQ>Qe^s!qMdm-_uGdAnHYh3VEoWlBNoKLs+drd#N?S$@PbLCV!@aElKLX7qXu0>bh zRz2d77pqNj_2pe-2DQaTNtR0Q)3gS0n~LH4q!wE|mQ5n?yv9)*JPLy6!jUtDS)*2v>%pJT+3$9@1g08U#ve#^=GCRfFX82EN=L&)Smbs z=dSe$r#w-wzTs(k^HKz?KWDfbEUpx2Px*-u!xL$do$Q+FG&t^DKdA@96TbNGccsc< zKZplB-PP^D#z|>fuC)ZX!0`b$6o-`ht;;{2;D7We;{h{}7KO=d%bm+D4LPs2;X2lW zWgI#CmC)|s7YBmXooQgl8TwbaahR~nCgi98-tWR{dmH{;`ftRHk%hb*IoqLI{${}1 zsoS;d9&|DQ2L;=b!g(c>QtVNNIYC~73Pxa&4m|Xv&s_seyj`p9-ZUCH*KetLSZNLh zg0T?6>A*m5m=&jH+s0F&+YQjQSS&ke=jXE*OT!i&u;l{DfN@^4J8@2bzs6`@g!k6e zLcH(zJr^kBlv9W-_nH>&U3Ve)Y{peeqoYU2_cVO(NZ3vy8YpKWx#sqS)^7J7Kc@mY zt0>JR3yP>?4^ZiegKic?YfGLXcWQ~7!n^Ch^REWsNKN~IIz2{#0C4{z-J6v+DG^Z6 z$qwW1&8#&rnZ`r z2tF%fj!Uo15Ah?lR(pE}m-Mq{ctzLLQou&q%XGBAQv7$z9#&L^Mt_ef;nH+K4_MX< z^d<0mHeTm)-FVx4PxM^mskT0SL&{zGS)8KV1v!VrBXf zWhMTdL!i1LtM#S5!hgK)y8C6apec0VcxeeL#n`r{W#Mx2Lo#o6SiV82)WeZa4bpOB z04g(oe8BdH)nF)4K79e;J~H&5%Kk>Wfe|C{4Yj{9tt46T{~Xe9n8vh*7uJEStNC<@`I*sxii*b9p&aUL5h@lnu#GOC_iT*~kd=5^(w~k?%_WT#JhxS(GM& z9Y@$=Y#ifaXVu2W*g`Ca*ojulDKLAo^Jub6cEFs0Q}qqQnGC}>xO8Io3lf(qoT&v3 zI;&-XoEJ^ZNHErYEQQ*xpaE$$2DA3b-Bw)VT~zWvu)eG~;xrc=|i;0|TtXO?duTZ$| zb;=&D%6zheqFG@{I|y8RquegW!h;!!Iuj<*DD!cy2EEpnegEA0Z=j@}LmPW#c%l09 zR7=07iU(YWPN}D!K0?8nnSRk;ij1~?o7H0NXS_UNNHkD~u#*`pA$iU|zw0+uTgUy( z7Y`~#Kn>%UJjUROWJog>2V8R*G}`5>*#P18LhgL-=?Q6 zebbXu9<}v6np8T6ZJY_katWfw``kW%YdX?ag<|Xd*@3GC{IWR@a=AkLxGebRa*ORr z>Jh%$-PIaceYIC+W@0To8Ci>1);M_3ebV`r;X*=xyZY&oak7SQ24rZoF@-RL2;So? zkjUHlkppbw+g&!7jAH>#Y~)+|k#}kEam6LbKY>{@?H3wx@~&p~JtxI~l1Rc3UjhT~ zqdhOj)&M1WB7q)oX8V@LL~;ke>J`J{n2=&Zkks`1K8wkdRgRtE7)F{(Qq}Y~G~B-##v7 zbxn9eoz_Bs7?$Oi&*#4%EkSU*G|T~i#q<;CP7&fw}o-mRik zbyU8`uRoOC-UiQjpPk&huuHJji}8-4e(E1nT$qoid(8+uzNDp=ffXypd$cTQnIp5ory;siB5Aw% zq#j}Rv?Z_HflHVbT{IxnrH~-99-4qBzxw>q54qmkT@n(V-S_KDU{|au1u-hq$7Vz_ zKlDV^!czsj0q8(meKIzvZ)j?^zngxi0_hB+y_<5REfG06fu;rC9^#IPUFa-^S$JLJ zYMSf$0$v}f9Uv%GfP$vCYloDpAW>ek!+)wt2^S-E0jGSr?=ilV5n zWFzyJ$a!Ql|7)6P=eh1RnmiaDi>T4OtrQ_lm&}yiV*53 zt`D)XJ;2`U&oFnV8=V1-xFXD=z>fbYXTTesBI#O zu*7QotpDR|nTuCXuP=T;YxO(Aq)&zYv#0&ZAX+0+>VnLe^+GtG;qnN1ckJuj^;}_cJw8!R#EJ}$ zh#y0J>3DV&Cog!Y*j9Z1Ahi3`B$yb)tUID$qePyW3m>f z#Hi%iq4H$7M?)+!o{`K}X*ieG{nL`@bt_BkVI5DOQMzFu9<~lQpY9U_2mXSlB8FKrPY=Pnw zQMRDPE&e0Rr-#MLgsW%NsnuuEUV!5CxT}@VtxWGP`-yo(WB#vEG6`{VvW_r1mA#PW zxL?V%N%apNm4Gn(?9=X&hkxI>f4<$}ue|S{U}`TSh%2p&m>OeZ{549cdw3nO*zJ&!pdqDgOh#We;fULx^Gjy5|)KF?TJvn8+kV9>5SlP2Z0#QEg2`MWI zc$?I#*|iD075dP$Lc(z0}<|Zn<;gh%0O+AK8kxl9vy^_bS(SI-b{id!F2FC;jZ5 zPid)jAsF+7bM~5Ce+PPfa4Je9ZZiX$%wF2!3&#!S$F;YFJWd~gMB#M6kM|$nPit2n zr3(@UKIICuj8@H!nQ_Ntv>k*OX__XQva1>7YU1F(yp>P4y^;@AkRZ-usrhr6XLETl zC#Rqw(pB1YVQtk(6kT2*rRNthqlhO&(oHs09vPKa1x+MsD&?X$g~D6x5K59o3?bTD zjz7dkVxlzXv6uT_34&Qzi7^M7)ju65g@nQaJIAD+NK+?jt^7y~r~?ygZd+>(UQ@nM z43^sefcC3DdE*v-C95Z z4uO;g#oXjWS-b5(HqyiG=cc*KtE zHT+l8ztYP;Bh$W_=&Kg2E>HUPueU4M4PRu2={EB7Qu`lY?6N;;#HB2gL`84$ml`oD&N6xxiE`L|eT{Kt9E;mhj1g3ETS3aw) zLltZHZuVeTSxu`@e!Kf7zM-QA3bwK>2%#H%gZ(4L##%L1n%?TrIj;v9QptS*d~FYX~K$LUJGE$-xoTUlj>l|!xUO(9P4P>`|0Yzp(DRfqsL%T^sf z@V7HL?*apRLz<+zkVo1@oHQkR%#tNz+QmK(TA9d_YA-2$7NG?C{2QvqoKf|~XQ49e zCNs)yx#Zbb(tcrp`!t@rzvSN^v&oxw7(rpl)qk6wTG?u^8ry0qh&ttaw$$GgTbwo_wH5v%mm-T;Sef)ODg5gBxlhge0g}@rQDL9;Q)4sKKi&6{N zYhAkd`Q`HY2EbB{(5Uo`aQ;`piHXbZ`b1@y_UzF zE7J-2YT?PV_q=FRRb|lD2m_TlR?Kj;Q;Fp5t6jpEaNZ!E>U3!N zQXNLz{iH$}Pp6$n)sra%C}fmz3Pg13X$(Yr+JAiC5%&kgHvHo#BB9 zsa0XP+@zoIwvQDRCifSuH!1zC8(Lm}5E2|z>ZP4$rjV^NO;gzLw03`?ssk|FKL0-O zie9w2_RQxU@m%UR1$vSR9ZMvPf(R3@OZ1hPXwDgZym28FUoBf!GoC>meZMJaH0c~e zaqJUUQu%fH(2U>` zE{9DXFP7%^8?IN06CpXdbqTtfJ^2*P&dv#W#w_q+_(7(>4#U`ZhQ|0_d%8d;aQpX6 zRDiF;0`<5o-|;7iAahKU1P|>~CoREQ-F(_wXma3Hn_C%j!)X1>U)u*6u9b{69DW9V zB>+3U!?Wn~r07XATv4~UW|k-0)HFMZ!GZon)Crp2E42@POM7_KXD7<(w2D06>9tLR zIk7uN8yucs?&iZ>)UHM9#x|F#>VLcirM*d>3W(+x7a&esEM?|ivApF^bJ%tFrb14! zw8jD;p@xe?Nh46mjk5{y23{Tn2s|1EzmH>>pOrK>=fGGO&Bg|dRa44L_@su%#EQ@d zaC4HH{f~h*9Mr&K&GDli8(AjDH9(3u_|w=@zjcDd-*EJ8h&OylHPhZh&{iOfCN%t%SC_o$ZNYPh~B`}a#U}RmxvelQR!80(9BAOfoOo-A58y8K^g~;0B zHaV20Hg4JP1(&&OL?%jA?#z>&Z4o6~M_93|N~;52^&MQgLVjwpn0Ah0oyBs-s+37H z(qd{58|(#WGz46JVqj#NS6peQ$jbHZ>xeeR=D> znQF#M!8wJAJO@B&j&N+DlJ7RHqp=xRS9FyQB~xOxVTY_n2PYtt2|awcH&a-lip!CFDcmHvIcPk8_s+3Q z#-sUznz)j?J0LNgVsE`@`gU%<7dI?hq-L?c=&q>xAxX}w*Yd3oNuK?xLL6j%b8q6? z05eZ#8+NzymlZK=V9xVj31gtg9G|j?e8u?xI#7bXFUUO?yON>X_!wq65YP{?t=jA+)zM8;92$2xh!=-p%0B2l|HoDm}Sp z^$UDywQMp!hx5K6N&0IGP*7ASc{OBJ1>C_F;a@*2IpILGTgvf;pOX7P7Bg7#C7JSw zTO1pu=t{)K<$&=$E+WEm&$ABBoiTGsV@{6`11lmf$&i+*PvS)%j!*oBO4#r`$A>R^pij}cEB~1&P?|HdIvl_CgCM6rN#6{Y zwSy1Y>_r4H!fK@nv+#WQ?BN48^?gs9Bo87vqTD-F&7;Pd53#F2dW`qQY$nIv04gm9a13{TUr3r-GNDg4S-$@AJe^Nh%`BEoyT#Az2wE z^VB)oa z)#)wx_L&9&Eh9@M<;I&qQw~w%(yJXQoU^RS5 zU7nBsgkDtNTrm@xpgA4G-%ru0PLR;R>Y80xW_7lBf2ka)Tw-CB91Z5Q8XuyKOQaGbWxHx8lAg zigr(7kI*Q)S-o}R#moZlH&G-p2a_bEEzted8(TTWe%(Gqhky*d45I=84LH<)B)c5x za^kja+y4HcYITcs^3j2JE}P`IBRgZ8Bn$+i#VVI=^;PNYd~GssF*i*KsT!(~w7&&f zS$B4ZsL}${?3X5FQv~*vr z_Dje6n)y_4u96RI?qE`Q&G=@dMvJ{se`diBusXzX+f~akB3A}a!MxOw1rrw zi|H5ub*YBfGS>ATp-Ws)(zVm`;=u1;gT|}|1st_Yu~|J0$@IDb#uU6pWMsB&c0s1< zMvNv+1(R#+AUKXjQD+D8DAA7J*+vTo4aS5%$eXS>>E< zuews!?}0*Dg_nh6yx`5oi{hahL-Qb`(Z|!gz^`%Nyhiy6PJ`bl8>v51kWxC&P&cwN zeVL*@W4C4YI-zGNO@LgrS?Q><6pQv7&6la|aiUIDN#+uF%)jQhtIcq zcMFF(O|e6L{>RlHKz#eQ#BJyL67~AM0xgx!&#MlTV85V@x$ZE5IGXdH#|VEacpJws zOMMGH!`d2x3fI+HR`Uzs9&`e)fnltB=j8hPAC8S$51k7B9WWy8@fG8YIflQgKW@Fu zN(wU)k!}qKGKZO**M`^2O8A&G(Im!Nu{&|xE?k;uJQrA~Z$g+w>fCr5c?Yxa+{iXy1}P+u&%b&tW+;^sB70V5BS|Hz z$?ocyb8)qDgl|}D4;lY+^%B+oLq_1M(XfU;ymFBX^hD@MYj3K)F1{TQAd2qYfo?@( zIB2iQJ%nKO+Zn>}s0~!}3q;;bh|88^-M?7H&pE}Ci5lN?6($pL6##?~D&;^{brH}9 znIeoG|8!ktLMOrmo@u^NbI6i&!%x8RHXglCj@kW?P4e3gqK~9iYok=#lmmlU#}|KC z2zQJ}nfc=OPUL1DJ!ie1uvF&>9S?8uths)xYF_`k{~3oKKvi!eGlSiupHRw;g?_e!pQF5->WX0}0O- zKHNR7)UBsxs2fua&YJ1pi^OMWT-Rg)=DB0JOA}fvRi9YJV#mgM+^;{{91Ez81vX#o z{SZaExw2(wN&cdvPcJs?a%)B!G_G~qFlz#6^%r=~npWAs_k%}Hv|Vt73R$P~(lBxY zYifpfb}Y2CRR{(K4} zlC3VaHuTDk4@0~0BL{PY!N`$w>km?gJd=g%M9J!VDmq5W-WT-*m6J5<0|v6~(=J7q z4|UB${x#{(8ic?3iPEbV722p%P5R^x{P&`MQ9e zT+1>gfQ_V0Z~AWeakH}q)+9K{eP6=i6^E;-Z9}J@f%Z6;E$?ZIj3SQ*Ql z)iWy4mlkVhQnS5bHXix1;uA^mw^Le@z(UP@_1?LZ&AK~TNcE=`Cottk{UlqNW`n7{t$$rxXd?DtAzEJdaYQDQlpgZQbIgxzkW!lCN=$MwLoheyUlvC9U#~TDK8zWF4%f zdcN}^Dg|F6>OD_m^mi&XshKdRaMzC>X;FJwappGhORG)8k?~XZVhx|Z;EP8^sastX zQMaF!HSmax>2@QBoV^j2e};PSRk=w6%aO(V#)BH|&|#W9Gt;?=Af@Jz3ljV)yp`4Y z-Lzyvc$Lpn&?n!glz0o=c=Cae8){qBEzzs*oejL~YufY=`}25qUe3kO?E$0cOa&`h;TK&fhzXq{@#g6E#!(#gq!OJ54mrHvQZN~5%brh~2JNZN-B*oT&B5k)TWTnmI!R9|*eK1bqs8=78}w2> zvwK`3A}WA%141xW*EkPKiyZR#y zJQLbnblRXI6td5>&INuBl?iMj2%l<^>40r9xEMK6oSD*2)*Irx`4}r&ysV~D)h?a^*=JY zHaIvcsrnUDB)^Ukd}Z`xiTJ7cJ3k8PW00*ZlYS>KQhYU>D<>GzQd8Z_~@#D zwG&`+bdEc;N#Qez(CTk7w{ns6aD|scU7v)&K#77kOyB9;02H`lkU~p2E<>xv-cbB5 zKOX~pT0?#2lj3_RXBqS?Y>R<}Q;LfPv+u+;C0J9j_6uthhc$Oi&Dh0Nb%2xj+aGxB z9hfrTH!2OuE^3+)OkBN`#~iXd>PVxukk0ET(f+yW$~&ti_UApC@ma#EWWM*zaYaTX zn-jk$SsVky#fVsX#eiJ%QD6a84;xT2d}FO}80qrby|G$B2s20!2*BlhT=D*MGF@K0 zo!#B6@a2&Rk|a}^;V66_v6eh60J2iOC$*j+b%0>@vFJG8Z!nT0ghFp_pAD|Ul8lr} z-DDi2xDb&Zz36d6K6D0SvYj{WZxh?p{U1;xfMPLJ!sJ`gX59!*?a>>Y(BjYtxtW0Ri-E&;pp!m*PR`)e=@6T^Hk0H;>|ZB0 z{h#mE^2Dm+^?q@XgQ}+P?h*vZ6*RmdOp#DhQc+sjFO;8T9DnLh_NJbUJ{P(!^AURz z-`*~xUJv~37?s%l>*mZaj@gG1?P}oUV$}GlwS0l2L)98q;kL5xHen&v34XQJ&_*7s zom5{-&(yhj10VdoDAI8MPz@4kf#^|=-rt}0i?-2$_Zm8HDf!L{PvktpYp$Dayh$bl zk>~Tz3~aIF$>WGi zj`}&ld)47~h7(&hZ;q)7fIOu?CJt+?O__iD-xK&;0`pi%_9sSr6OWua zWL#B^Hf$r0{yo=>8i!X7Z3%ZpaRtc7T&u#Xe)a256%aLW4_Jbuqidyemxn2cssMmv z4ea=iPk^HcHQZ5e{s^XyxFwTP_?3exT-+r>|4GIZt{@v4aS!!BQ>p_k%{>j2B3 zf|}DHt!!t9wVW{T)EZh7-@8Fu;AHGTM#I{b#L2An^DO~1=rx8%E&J*=okkk0j_cXR0*)IK@3HSn zVm0uOUFRybiK0&+8JTEH2O&^0ueZRQ;ve|mip-McypAF+nhKOQr{9yy<$FzlWnj!) zMJ*BHykyE;e#HoPwiZWhvGcVqUCFn5kz8~vWP#O^s?}H>|FnfxQ;Py$omafL6Tynv z*a;AjNe+9%vCPUuPN#cs|^ji$t{+#75-7kXcJ4wjC%C7gG5_G);qwnF?iF=QcM@+y5{Ea<+PX&1OEbUzRWqKk zSmeb%h3tM_m*-VWUrtNxsdmw_F+@p4eM=cSUZ#)FAOzID}$&e*(gWKeP7PxiT}4nnx8#iL}@3 ztkTD#VdUZ@nSe1sHdQL@6Sg!g+W-j*rm-SgkYLzxvjzg$BfE_ zW-(Vb0q`_F=!NP&_d!`zJoLH0+fLPb&=2|>gFGBKVXirW!!73*2PI#vX>3djK0gVz zeua~NOSb05QTC=>cz`IcpV^X4(akCJ)Qc4nnUDl&rI@}5E`Pcpnmt;?^ElRMNGP@P zvu46E&rq+n^QG_V5~f_s-n^u>t^)814deZB_I*8Mkvk&4D*Qw(>3I=vWP4C0?E4xg zFQLb25b`ir%?R{9iu3J@AkH{zf2Wdv{QEQY<;!N*Iuc?m^THl1-tybQz?8f7-e^Jl zlXOu`xAj*IgSJMA$+dOo0$bTTkE7ge7z%ZA@P{F39uK z3i`yH8>;K_cAY61a+bxW3j*Wfw-ZIW-P=<{_+1NvkCwN^F6fG=j{g1Ea5r$sWJda_ z`s8*HNS(OaOnP5U!o7XgwyS3P(t5kX?cL1!|4h1jknLzWZ1`CD;nDAK^yE^^k>mV2 z^njyO)3l7G$K-i^_9>@X$e;nsTxU^9zB;WHM*5w-+q{?sos z{k@}FI3LTiB+2nD{d|Fy1Kz|V8uJE~5$euhijnH_$2K;_&#+&U!(A^_zO^lIH80Yx zWQ^xNEE1b0hP<;fC|Pc(UgMv_)v!u=-g$5Nrb9B$<~p*ATr>W@q{wO^Ad%Mk@|S>E z%UMP{{fg8>@VD%;?gV=9GubtapwiBW!nzDz6nO17D3TJvXjTq=!J2rw4?*)6_m)h4 zo@ZfT6om}PbR4XAl>QlwR_)7z4w7PusiNlS_jlD0*Pt|tMWJ>pAG}T^TdFrl4ff9Y zW%oj}1Mr+&PGF06bdUwbuxa|K{SbxT)$+uo7N3@C=t|N2N8~cb#BKVIcAzZzTQyl4 zxd49;i9^ma36q$Uvx;v}b2a0eCVHL6kb+j-o_~z!<6R^A7yBCqw^#J-XsIskTTT_j zGxL{-LQJ~I^tcRWbjhUHfZOwgmW(-6`%rOa$Gw6F z&&r@B@kvNP*Qv&Gnx{+Z0m$X@#&IS78K0?F4kR!}g(}jRYR@o{CplPqy@&m~Y6`zheBT|$v-aT8a_jsvsi)rqrsvpY`P?oZcQFo^q{xTz_ zS%`Z+_SfYQMgi{c#{(UAjH|2x*wSx%2fiWk+9doE)HqJtb45WyA!uhi23t6hrAd50 z@LGbAyxoIY=lHlLLbp&;m%0a*!~UKypFSw{ShrRpjz-Ui zZAb-R@Qm@M_V@QsS5<3wSJI`bi%1xEEb>91vcrs`Wnty<3NPqpkHZIQGTJ;DI4MHD z3=F@u+q|5$m3tNS`LHoG2sG~*R|V*5_0}Dbx8687M?Gehe&X#CChiGRhbx6Q ztmy&*Y&-j2)(YA^*_XB---On{^9!#?NJW7)HABhsWqZ^ZXsRI#ho>Ce=-g7$yOg(c z-2m~4X|Wa>qc&jL>6x(@DAp2O0#tVBMof$KlJt--eyz1@F3E(BIqIv$+6SjK2U~yO z(#~r#uXovhH^`itZtgJ&`?AZJ`N2%gj}lH zL`?}b$!FyGxbKHxOKI>7671C*u&Ysc$;C3c!1WL-6_t)#YdusFA=ecGn(isei1=8KStkQga8(Dw3m->iCXL9PTX{u zwxyn+zf#jdq+q5z2HkZ@uDQYu68uXA^0=qQG>{E?iAF7+{^{=V+RYE#Rc4O}`)3h6@)97D7U`9-59OUIxd zrN@aQP&`iyh9mc*DED2y9Mq*)MYTmfC%w6rLJQ>J^5?Er97X?aa+Tn$8}vwZIhztZjr#y9hKg6T6`3-KadKRacDcr4VHjWh(mf1vK$5n73o!wMH}V5NK-=cSU2;Wr+1ammh)LKURKrLxYZ# z!o8y3$t!bVc8BlOF2^)R=%^cw5N?WrG^R+Fx42%%cYSS?a!ChOwLM63Kv&8A(-f%P z3O=6Yb9VI??Xm`x<-GUTeq40MpUu;CAj;dgn^eGxcj)f^^X4zxR8OgZXv?jb3$qDi zw;VV^Njs^ToL0Nh;Rae!I z56GEcvSfeBkZ(7)yUc%wsN5rivmRJhYO<`@Kz-L+0Y}Bxs}{mNl>;ZQ&gW2m5^p8A+gRM~otLf7OzgGF+9_hhJUzQ1w}rWH>=kpKEy#boMXM-!7Hjv-M10z02+ zedV0DyY@!FX~mTQWFZsv6@M2aCx{4Kb5&}oJGXQ=K-WNVy~p}*g?2Dj?N-xcU7}xz zj^ingEG*l~GVt--g_@AgGXAOM6t|Zr(EGnYIrZ;_lfZ3a;aTtCc*f5S!tNSaG+KDN z;W8en6p90Q*sS@ti90*x-6u($kJA3Pnj?oD(B@S#~ItQZUtWG-~R@3lnswKDQbGw z-40rI&s%Cs3-~8rwgzU4&bsOwfAtaIY;+7GRZh}?r-+U29&dl= ztLpC;rJtCgYjCC%Ki};PU0|zsjA`KsTaCa+cyp|qVQ&&sYSSI;i#n8b*!?<(x-sx@ zFHgO<`qv$UUB2lg5K8TX_xvK7_B!HB6V(q)BzJdgc3`E&<;C=Iyr16DcaoI=l&4yb zac$3cok5u`%F2R?3HdU`&GcRWs|7%sK~%x{jd1y3tooW}*yM>GZ1@DLbOURLYVt(h zoW$hY7|s+#2EEz6Sw-O_VD!3mZkOliB+p>6vosqb)1RwHY#Li~EOv&xDvjH{O1x_j zy|hAKD7N$@uAg|YbG7qYCZ}wiM(EE};^SQs>Z3A-miz^7)sRMRw&64-r^v*S%(E7v zp{Azqe8vD-3QNk91B!3=jvg&{sZ^g7Nv8D3WSz|i3X069&zS1XlwON{j;KmMaIYCk zwXp5o00EN6TOBWNMwk@l1;ReAAK-Y2EXSs#&KHEbs2K2=qhs6fy8e`8ZBGhwQ32I+ zEP~_eaGl2(M%4JGo0`^mq<$IX z(*r^szb~8X?zHfC*47WFdiDOEmz0aDXEF1@vEqd4V70oOrRX2P5me&(XE?H#Xn>@B z?R%AZSJv?Ghg~2ym{zx=xs|wTLSM4ne7lB93GV%5bDkxAP&QX7*8_`Z#UAb0R^uLt zgQ2pIF13yJ5oynTF;)0xppdL}fs&8go_>S4V(T!yyJ01csZcp}vp;ETMuvmIzHrmp ze^_{&@68-QOWEsEcevX792}^7**DieV{XoxSZ}*Znr8~Kh%R{-)$(cdIm6t)5-Q`; z4_>?tAc7R~{uyZc$ zOMA;W*f2{St2XVQ=FvAo#v@aSwaQZuW+BFPn036ajx%9uhN54-TQ1|#rF|%6?O!ye z(o+x-{=NwGPnkvVU$r0c0y^@zI#t;k^1-P8#}f^MR<&Tj%ew$9_|5Iq_++AMFt3t6 zpaxkl^0AaXJ(ilcVS59fNJ8r$6;&bnPu7QF9*AT!6|60grme(sYyZuXzk>4&z)^@- zDcT&5p4St9yeg1i4{eGA>ciweu4O)od2K@Q9nnEjtI=X4Y0z^I!>B!P_QP8Yh&qzY zJ12L#WWj4fw%*jEhfNNh_bgT@8I<6GI}I8;nCuqRVIAf|j&M{;0mxs7b2sad&Yoyd zZWk@7Bly$-z<^YHdAU~xgGM{Cecp+2&-bSL%fgG^90(MZaA9bPvrq?6TF%+%U?bV< zPB#^%x61%}^_)t40z$U*}&p+Bka4!WwI)NF5AuV5f>mxffCkc4PUb1h# zx}wWu5)JuLPC639J|Ci9q-6@M&H1(nsnoWX@%fk5Aj*Xj47kCSccYE#htkJuHgvR& z8TD#eDXg+1RP*wwZpaKMot&H_pU3?~cSCRCvnr36#sB1^m(Qa2ZTyY@*w`~NGA0YI z$6Z{?bDEZ)GrawD--h+IN(XJJL-4&GC3I+ zM%A&{9MsN>Aa6bQF*!J@1tJeaN#9=OE;DN6Da>}rb{sp<6nPjxx`nmE92S=X9?pa1 zhWm_70`L@$mv+-~v#n$(R_PO_r;*I^jM9Ip;Nxh|DWt-7KP9D~V4Q_e88>?*^Dr%a z`i{Zp!s8lhI=nZ7NI~Y!-cQPVEO_eAjU>=^*ayqRUhOl3uQ_YTmY3a1(#Dcd7oBPPlt1+PE$-Xb4wo2}snj z^R~S@(!WRMbDR1Kh|SF9S(^oRpj&`t!WEI*%RmXHIlT-zFn}H&l;b3vet!yK|D0S~ zD6z9};c>?x?D0ZIw$RBy$P4}I@M~9KHCfP;_tSw|!{p2e<>d0l`A8fKscuE1WJoZ>kF5#B5 zHr04R2c8~t9ptp#`LQMY$lXnm?}Qa%An6R<+%#b2_%Mi6vfK_o!%ozgJ-B%!jtGoo z5CsioBu9DN??&CoEp`$uwTav8NMVh~a{4@Aufm%+9!>}x=2k?K5;f!!C}QflUkYDR zU{@}5H0g{S^>d*kscF4}u!L;2{Fl)N{X{mTVehJHyP(SotLyW>K7lNsg>$LH98o6dGUeM$GW4Y%pYb75I978llsC;T7f4X58 zM<1K-iLJgv4@YjcGl5329c($4(G*mkqrp12jK>W*=ad3@ma#-$AzE;M4$3}QRj}`& z@7vPvq>)RD3y%Fk?T|&Zk6jAuK-;GYCWCBu3M2sQJ~#sJ_PDn$)vkPTot36+r^}6F z!7c8(9+d4Nl*rF9%vE9d78y9bKsqX#sCw7Rx*G4)z`VXIc$Q`RhZ3oD;NWCpz|$jF zkpj*UbgG__JYK&CQb979xISG1lFyjNspSkC4(9f+vPIF|N~tH$G7K;L@JQlsPr1ug zWi++iyhFsuNN-o8KLm^2_zIT2U~!`UK4f*{FIl9-xY&Z0m5mCY(6s@5G^x)>ekuKR zo=7pF*Mdw+xwTgCtb5IMc`2gc*{Y=!9ktiziUXbOg7)^l_U3)}hA34!e2zEto7q$X z%bk0GXzu8ZHrWuPdU}%DuKA0-B$}~vGR~O|4ct7jp^%%SxR!b)HVc(nwXiP0gTMST zt@{$V&Ve3>QOzQc{mjW+q{I~pA7~u%J)KLc@tS$e#4`!Z*$u#;%SMJdC7K@ly>9p) zqCM)!jnEpyqw?kd#k3>p*(D^ZO}`#=1}|K7?D%Bup9?xktGv&#KyIMK3v{_1UQ?`V zsU#B?tUYXDl8IHEn*`^wl8z$rdB=U&8Uc97T$DaS2uCwgvc8AgI#=rL9R~IMdBQBg z1%#RgGsr~nAiJNvz&}#dr|Adp$1n1SFQU583av$t{xnJie%WIf27bS(ws{9_Wd z5wWdEvO_Sv#{eTKJ8}chWr5N9qs~#t|D!E*BIrDPpkPXDrVi~>OcKGXeQ=l9Y?hBA zGnZATz~Zr9X&}N)`af-b2UHW$7A+m52`XKIfb=G!bO=>Is&oidinI{vy(2}CBGRNI zO^^-&q!*D6QWARa%}@*-{-E!@zt&$rD=RCRBs1sUJ9FpUbM`(vp2XaGFFoUj$&)+q z{yosFTJ#CZ*?$=TrYl9$(SLQZOb@H@F6bQFn1tNC=nhg<>FZ;0nZ3Q>Kjh`#9u3Bf(fg9wA;tkU zi4{hc!wipa!HCtRTJxWzn~1@WY9+exEwO;GGQ$Dxfb@5Ga5uW7|KcPIGqpNp)umNo z(Wcz(o~~(>nkYp7NM)%zknK2qUW5MfD?-11^r^Y3IoEq%s0>>59Sol(e)wh1in*b_BiRpN3kIt>3dDno?`*t} z%2Ot>75=&Dhe(-Hy>On?Z*_0DUmyq-v#DHIayBZwvHX&@B{R+{zW8wIdx|l~aHY6O z|Cf9%gkj7K+LtdL}__Qo>GHG;~A))`22B3+6^j+@bGQvknz8F^|=XHsx@yjK^K#kaEG1L~Por-0`; z698Fz5356m1Xlfk)(R4k%~M|rF~fm8p9IzmAz@?tLw7tj2V>C5wq?jokB) zr2wMCoi+H`9;`{vG*Hj`a)SZj)1gFhg4q3lFF5OyIItwH#o`JMX5 z))W>K%#4pG;NT<6HHSMnxjY->Vs#!%X@5GBE?MT|>KtsR=hM2f{HkFDPSG2!*Bp*5 z;feTxmY`-JR`74>x)NV=Kr<^VlV{Ew6zLa|ETu>lI z%QC@hs+#-(yl1f2GxU}xhmUa8gf2KFBcF)S^v%@#oCuR!UN-{}S>)8g1g;fHFB)ge z7C?|RD((=JNyL5mHAn5$B8TSY!2S<0ib}GGuOI3ILgCWCGW_B>Y~fq1{47!a3ej0X z1cdVAjo3~%1eQbmGyeNiUmb2>s?z99{cb@o_WK7X<`L6S@{IF#tTqJl z;#dpX{i*t*WPsXuyV-`#p?r0LiqBPTF=O zV{pU0s)v{SrkVOf=B$YX=a3f)aB}h6gfc5Xg@u|jBZ4NXp`oQl;Eu-mCO`JT{HmhvvWC?zrgpy(%jvK`cs|N z>=upAz*BCM84dR5^G;J@novQ8r!d#mCtxXOS|(Um5k$}h+J}(wfVSiFd`6Q1VbNn>ZT3)?h5TW)q>&`!NCXUBx| zgq6!$_NfUQpe!G(Un9t^7xFQElv@f8yKYIQ^!*B}6lO-PMri~jJ)7~E#mYcwmv>CV4pdGJe7;+=k_v%?h(VwJa8J5i@W0d1S&4tHUV_U z`!UC*u+qlO$K7+LAQfe|zM(zx^}S9vczjL2#hNIsgF3?^)&F}*w-SDyob8Rz6(92bi*2&379{6X7( zt&tzrU?vG7!>uM5It^Vv8K;={rX4$gPjB}%rTm3JoMuOib{ATrC%-%>>cjhoiKTVO zi5Ch$pvl|4D#93p@O@5v`SUq(8oNEboP#CO?7138YsQ?FJQv@R@-t>5ig_xYi`H>LP->>+T8yb7Q>jH+O$-Q%8uS<^rBOG&p@m{{*Jd z%4@l9=6OxAYR|{ySl|BVc90NLJDjbGO4WA)Fvy%tJ3{=vZLRyWvv+%LQ?0&}?fKZL zs5M)_tx}10xr=|*A`8X$kt%s5cFpwwR7nymrD%^&{qo2EjqUl!t|f z)J6Dv_aNpePt_qSej28GM$;h6-;SU0Q72j`G+|dw-M81X(o=#PMr-Q-Kj=!5Yk#eT z*z1&Q!KbZc2AaXM9t8Z}oKG)O%Cf{n_XDaICuR`Wf_qUvrd)bHJtM|`AzMoKO}Tn& zM=-fofMSZL-r@Uy1`ELMxF5g;&x|KvLf6g%Pl(l3rT3@8FL(GXO@ah59j`S{_WAsL zIGy-7`^cL#S^2A#MTgfYG~-O=r2WZn*}7WV4@ERT-WQ6%Y;93}X#!$9)HFcZ2hhj# zziI}UTtH~J6D~1ME!b|I)BwDGbhsQYA-iD?dtuB^Odsz1+YbGg@TzGf0VYGIYwaB|k6+wf5lYnC z^Fd23KWj9CW1u+}7l-|b43|`}2no%_Ez#-8shfiDF>d#7UBe#+&a_ntXszahDM$|pC*zIxI}^0x{JcR}j+MvN@{`+BneTVWjk*O_I#MIYab*V{5^NRw4Qy`1 zD~_lv>%{sBe5#D_EgZ&%!(#)RZN}F;3|gDZXm6;UPi3j;(<0gnnZx$6$$b3fV@6ol z8=6{=Empt!9C%-7?(VDEPrG;0on0J|KcqRt8mMz*<93mJjgr%^uoJ#6sQ;xrXS=6q z(Cqy@K0aQpyup+I%S&RJ$_T(t z@Odp~r)4gGP|h6r)7=@~S*eN+`_Nyeq#tMMYCu;v?dW5!N58;z{p3FcjyF$+SxBj0 zsnmEpy0?)DdJrh^c%Zi6zIWIrO4Jeyb2KfwnabfXJcg@aIQaASknU~FZz6wwq0Od? z%oiy88Hgf_Uic>E>7_I1T%vsSzMt9C3RouZKMiuXCrTw}8X1Jh-qN7L0psrQ_BG5d zBgl1B7nAYGN1qGw>jI3=yOr6Q?FB#2@StMnorl=FD*-p6de z!@20zW~m*?=TJS--m+ekiOSo_1lW5SZn?^5wJ$*z`V}*xA|z4-0Uh*#n~~G+Ni^5V zgq*O%$*fZ=3f>+;a@j$?8mb*`{EeF4)lD#qd-<*cU;vOm*lHVpmre)yw~RuLhW=aX zQL~sS+=%_rKpW-0TZUOk+>-q%Q)jZ8e*uf>Z94yLz(iUi<6BEo8i0aycqowZOV^y> zK75IJ=#Wi?u;ZcVI2%00hGH}uun#cPynWBAm-D@0lFGk4&hq&eZ$9OMe_sN(WLcm- z!`~nN&HMasvtC)h&c{8Wy#H?S3`^pHR9O!?#wLHof#H5Vr_*GAphp9GYoAK?w=Z@T z)zr}Sh?i<=EqRxmc}q`Gxq%EiLjT#d_MQKG7^AFUz1zwT065*|TF&2c+JGmLCh2HQ zNfYezpMGF3PW#KVGamKgEsqPnWCB)Zie6@qGIE!D@~Qrtn<>8eJE;REPP{@9l6OR3 zriK!jiy$2)ak_!1q#KEQM=YP6i)$!`6N4bs_f=agczYC=dyo@+(2mG4IAWx+8GDJ;%@n8-a%+?}O?ud~p1iGR+f_069;TlsOZ#TgcTc6a zWhhm$d^qF4#tdU=xUEGOFaMnnx!Xi0sn0TAV4qZ)oxBlW<|~^bJ^jg8kL0!X*z((l zJv@k&Mvcqt9K;eW1#GtfqkemsqUm7d!)hY%y}A4#H=g`RcgsgwXh zIHNrSDw`8TPP>y67md!Gl4ZddW}ebyjH%bbj)Ph@=i>i2biF3Y_wG zZh6;{SkW_Bd&QwaVEN@m+5E^zepDG*iPXnOVoV-;hbgL~W>WLsT!%a5d%VtCpIF#J zh=OwK3^IY(s7{GWmi}%74yN^j&{H9#m#LnvrzlNqzi#1|TsTx#5`{5}r1I({>EShw z%ktl@bkh1a(-6M%ahM6)U0ASouU0NMeED9MpGjGHX#`}ma6Jm$MygjA=HXZmhgDi| z>FGK@b=}r(VXv+>7%zS&96+BZXKl@Nb?I=?J&}wlw8LMQkSW2pkephgQJB9E2re=) zFU2b`GGo7ws&xXKoPX{#ds*0GzC!HbJ7s@K=S>aE4ACA|)i@$5!>$N!-WQ0HLSsFn zXcYQIx>$ITG2|$Wl9DO*k*p+|y2@PEa_)Q2^;KjJliP!xeyTBbmhL< z9ul*|q2N1xQUWCC&f4(}o85Mp7QBhhj)p(~JO=*Oa#qmkn{2eg{liL13|hBe^Q zP;Ka%_H>3Xy`x(E$v_?gREIz+%(~uRZn+?{N7x{R8~Ao{Vfp(Y7SG$VH#r*h%${q% zTV?fASjC?Tm~W_^uG3968HjnDKB=+GzoW&#u=*wo#LQeu|0*z!%w`PF|B51p{+;`t zSr-A%9?HCHYYY&njQbe;BuXKJJpmW@i4&9fLFhl8hHz+jXVEJSeF7&Z)TlCiiL$pB zb+$qFh{Vea1+>R&ZIYYzGQj5zJw&JS=fQ6b@;Ij8PnsdxxK_{{Mn_D-$UIu8S44TB z_)0J(Rp#?t#-3t*=zDb%ajal0o>xYh8X@GkstLL|c|3p2pDS8%9KiF_xN+YoV+|>@xFnR5>7P6~}vHVBq&as>}Y#wY9&%++MK90Cm~UF!=1I*{9ks;1ZLV221?U zC$H<>skZHj?1cdQ#3bkG^+AoR^Rx1wOuJD1GV%*FCYw6e#fghpsR;?7D~3YPNU4!l z*xs+Oj8tz4L{i!T{6}mC{~iTD31nIlr6I)98f&aj_RrQztLX#~yQb>)qZ1ssnw9b^ zqWGmY|2mlQw8fUiOqHxDR@Q*_XKy{*oc1oZeWx5dflYQW)nb@|VvU09*;~d6XBHpltI0f!k*>(LVx0nNfo;kp5R{XnoERQje2c$)-2N<=rMkd3Vms6-E^hw~DHCIDJ8&`P>@^CRkquM$Z zS!ISvMg!;%{OAhQ)C_!+wZ=eTkCgedvr$KH=YWIWt&DOhIek{?eSNxxl-6ke!z zYD*Q6vS!uReo*A+vp6fV;ClnhsGG&|J&yM>U0%T~m9>w8wJb4{@m>Xs(=QItI1bm; zu%6@~!aO~3L9?yV5}DcU6pmY}Wxv}nFEolY&8=?U)u`IAJfOmMa*`B#@O8df6qt_>fTZQ(|Q{oG@|uwb`OnrmPQ|dow{j zkO$&PT2wuh{*dwcM4hk2{+n0iadC?jjTn>nsTmk1tygG7JjrYvLbn&Pz^9ikX1(_` z8r`$uP-*D2l-o8=4Ggyai&of&a(f0AL?e*RJqGgK=HSJ{BVOFXQ~QS0*Ts9xI=#oh zkJU%!%fhn$r+zNBU|hsJ_^M@h$^x;1pw!WkQc+p--$3wvJMu^y3ztI?29xR5+4=R zGIeMl#gQfmXpfSQ3iLX<^uflG%Zys2D6?s&<9-bmq*PSL&k)9(ZcN5Cw*C6IVdviT zjzZMW343}l9)U?Y`S1s`hD}z^j;=QDKJG~P7ZF@ClnD_@*f2fLT9(?-Thjcr9JTuuK5SNI{ZA|De+p|M~ zU(7ZcOHx=j!TCA^XkJ@-$eug+Go-M3F1Q94!{pUUFPn6uM+hyQbYfyQ37u4GYR(^` z9Y1?DhAL4W-eYp!;AD1&C>WQlp%qDSiQOw>ov7u!hs0@3m}{bZW}hQWKt|bz&LeqB z*=5FFd!1|j(0SG0h9AkvvwwY&W??ZIj(}*FsT;TFy2?f#lDv*R$)_T3Uo-K0&MuhL zI(``;Shr~l^P`|>`@{iK$i!Z>*=+o6P4^-OXK&umV7JaV3Nl_b)BLRL&*rOrHiVm4 zz}Y+KpAB59GU4`;0+q^gfeTcp^?6_7uEE~?BhO6`B>>Q){PZcAuKF#F8J+)b{T$ zxlKP*M3tEW<>HTG1BWt#Y=JteD}nZ9O92uEuSS?eO*LE8sNboVDt zpqHm#hfLLOMzj-rk za7(uI!*;PD;hL~!s_d{i9<=z|9gYtX?Bi7*j&PSWvrCE<7d)A_22@5BV&WuSBV6r@ zQzc>aTRJ6*$ccDK?@xeAe4d4c2y{bjbZ;g>O$pTO!M8P5s05pm`3@1fahpIU(2J+I zzQEghPsj^HJB&s~f|98Q=$V)vJyP{mJR#2qeBOV%y*!b2Y054K7nO2ePs40cb}OUhXN##Hmh z#-^?n?#6G2Oe5g%F{l@1p2y zD@zO(7HjRYpV-8-I69_)IQ+T_;FZ`NygsTL|ED8%_P2RAG{fT|N{o~h!Y`F7u zD)u07Dd=pEt?_r% zJt?h18Mj?(Lmv~f3#eOOK4ylfbbeu47H~z&$-f&%BfdAU&3Unnf8xmAn+ZBQ%5mR~ z!+&Wr^}F%As7Z`Hf}7rMs0ZWlFfbUa$h}G<;jI(FOzFGG!%EL(XYmtt@M|H(&x|#S zf-pQ2PtamA_Fx7P?Xw8nbpAXm;<-loHBqOiQ8$j^iOgK4#j?hx*YV-dVoVDw%$K?{vdU7V|r&6t_g^Pt{2%KWKvr@5n^u!x5B~4$LAn_1l~#-G#Zq z$(LTgCA4rF{$mW+@Zha62*}=9b+s31j#f>By!ysE7X4kbZxd5pJuZpq#I^QR6@Fr7 zp5}ZxSCXOkY9D^v%Lw)uXD6M6)R6)nyxp;lS5hcttlbRd_MAsf%)_8V&um0F3=MA$ ze*1&aq|Y>hzHW`NVI+2vf# zfZ#{HE%dPR}|xl?{x9_PD%!iM2P` zj>+JIMxAFTPy4gw$noIY`vCHd@$qe6wPw{|EgZw~Kqb{gMo*3akhco=k5)o(C$msd z+!#*Ik7{cY)YVB7_8|6MR8krqmo7%EL^nBNrTZYg?$lG@9)gDx843AR2Jv!4KcG0! zO&S*O9K)!py1hU3lhAD7Rlaj}@#1>f*+}$^=g@as8=m9i0SAd)e4PHBye`KBnVC0T z_4XBi;?Aw@@J(FdR>LiHA^Ul*FZaF({S8{g53i-D9)CgpW)L=A&?isLA#EQ_=01|b z-?kDfD{)@*>_xU9;J0+g;7VRob58Cn0(&S;Pol1&Jme%%mB%R`aSmATEed@LO04(> z<|POo8O1Gk;Nh-O3t|d?2%-moXC*R^S%Yb5J6LflZ+F`CpMZKtxmTRz(kZ-rnv1ZTp`JCt3SnK|)}24hk%n4bw?$e$0jeJe1_s K +% All rights reserved. +% +% Redistribution and use in source and binary forms, with or without +% modification, are permitted provided that the following conditions are +% met: +% * Redistributions of source code must retain the above copyright +% notice, this list of conditions and the following disclaimer. +% * Redistributions in binary form must reproduce the above copyright +% notice, this list of conditions and the following disclaimer in +% the documentation and/or other materials provided with the distribution +% +% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +% POSSIBILITY OF SUCH DAMAGE. + +function int = color2javaRGBint(X, varargin) + +% Input handling +if nargin==3 + if ~isnumeric(X) || ~isnumeric(varargin{1}) || ~isnumeric(varargin{2}) + error('With three inputs given, all should be scalars'); + end + % Concatenate R,G,B together + X = [X(:); varargin{1}(:); varargin{2}(:)]; +elseif nargin~=1 + error('Incorrect number of inputs. Should be 1 or 3.'); +end + +if ischar(X) + % Handle Hex string conversion + int = hex2javaRGBint(X); +elseif isnumeric(X) + % Handle RGB conversion + int = RGB2javaRGBint(X); +else + error('Input was neither string nor numeric'); +end + +end + + +function int = hex2javaRGBint(hex) + +% Input handling +if length(hex)==7 && strcmp(hex(1),'#') + hex = hex(2:end); +end +if length(hex)==3 + % Assume a 16-bit colour (fff, 333, 840, etc) + % This should be replicated to make ffffff, 333333, 884400, etc. + % This is sometimes used on the web as shorthand for the 256-bit + % colours, particularly for the grey shades. + hex = reshape(repmat(hex, 2, 1), 1, 6); +elseif length(hex)~=6 + % Length should be 6 for a three channel 256-bit hexadecimal + error('Input string was not a hexadecimal number'); +end + +% Make a Java color object +jc = java.awt.Color(hex2dec(hex)); +% Check what the RGB int is equal to +int = jc.getRGB(); +% This is equivalent to +% int = hex2dec(hex) - (256^2*255 + 256*255 + 256); + +end + + + +function int = RGB2javaRGBint(X) + +% Input handling +if numel(X)==1 + % Assume input is greyscale + X = repmat(X, 3, 1); +end +if numel(X)~=3 + error('Input did not contain three colour channels'); +end +if any(X<0) + error('R,G,B cannot be negative'); +end +if any(X>255) + error('R,G,B should be not exceed 255'); +end +if all(X<1) + % Rescale from 0-1 to 0-255 + X = round(X*255); +end + +% Convert R, G, and B into a single integer +dec = 256^2 * X(1) + 256 * X(2) + X(3); + +% Make a Java color object +jc = java.awt.Color(dec); +% Check what the RGB int is equal to +int = jc.getRGB(); +% This is equivalent to +% int = dec - (256^2*255 + 256*255 + 256); + +end diff --git a/Functions/helper_functions_community/schemer/develop/sample.cpp b/Functions/helper_functions_community/schemer/develop/sample.cpp new file mode 100644 index 0000000..f83e711 --- /dev/null +++ b/Functions/helper_functions_community/schemer/develop/sample.cpp @@ -0,0 +1,22 @@ +/* Here's a simple Hello World program in C++, which won't + * do much, but does show syntax highlighting. + */ +#include + +void some_complex_work(); + +int main() +{ + if (2+2==5) + { + // This happens very often indeed + std::cout << "Hello World!"; + } + else + { + @ // bad character makes an error + character = 'a'; + len = strlen("foobar"); + return len; + } +} diff --git a/Functions/helper_functions_community/schemer/develop/sample.html b/Functions/helper_functions_community/schemer/develop/sample.html new file mode 100644 index 0000000..03bf097 --- /dev/null +++ b/Functions/helper_functions_community/schemer/develop/sample.html @@ -0,0 +1,39 @@ + + + + Latin Lipsum - Hello, World! + + + + + + + + + +

Hello, World!

+ +

+ Lorem ipsum dolor sit amet, appetere disputationi te eam, eu + cum eius nulla everti. Id eam sale nostrud menandri! Pri an + duis probo, splendide consequuntur ne vis. +

+ + + + + + + +
+ + + Dicta iudico eos ne, at iisque persecuti his. +
+

+ Tale iriure + laboramus te sea, nec iusto veritus cu, id purto patrioque + sea? Mei at dicit propriae. +

+ + diff --git a/Functions/helper_functions_community/schemer/develop/sample.java b/Functions/helper_functions_community/schemer/develop/sample.java new file mode 100644 index 0000000..953cb6e --- /dev/null +++ b/Functions/helper_functions_community/schemer/develop/sample.java @@ -0,0 +1,23 @@ +/* HelloWorld.java + * Demonstrates syntax highlighting for Java. + */ +package fibsandlies; +import java.util.HashMap; + +class HelloWorld { + /* + * Documentation for the method appears here. + * Prints the string "Hello, world!" to the console. + */ + public static void main(String[] args) { + // Make a character + char c = 'A'; + // Assemble the output string + string s = "Hello," + " world" + "!"; + int len = s.length(); + // Print to screen + System.out.println(s); + // Generate an error + ## + } +} diff --git a/Functions/helper_functions_community/schemer/develop/sample.m b/Functions/helper_functions_community/schemer/develop/sample.m new file mode 100644 index 0000000..b2b1e38 --- /dev/null +++ b/Functions/helper_functions_community/schemer/develop/sample.m @@ -0,0 +1,21 @@ +function sample() +% Create an output file with sys command +!touch test_file.txt +fid = fopen('test_file.txt', 'w'); +for i=1:20 + fprintf(fid, '%d unterminated\n, i); +end +fclose(fid); +end + +function unusedFunc(passedInput) +persistent global_value; +global_value = global_value + 1; +%% Title of cell +printed_var = subFunc(global_value) +unused_var = printed_var; + function out = subFunc(scaleFactor) + fprintf('Scale %.4f\n', scaleFactor); + out = passedInput * scaleFactor; + end +end diff --git a/Functions/helper_functions_community/schemer/develop/sample.tlc b/Functions/helper_functions_community/schemer/develop/sample.tlc new file mode 100644 index 0000000..0cc6f02 --- /dev/null +++ b/Functions/helper_functions_community/schemer/develop/sample.tlc @@ -0,0 +1,27 @@ +%implements Sample "C" + +%% Function: SampleFunction =============================================== +%% Abstract: +%% Doesn't do anything, but does contain some TLC code +%% with syntax highlighting. +%% +%function SampleFunction(inputVal, indexVal) void + %% Need this library to use FILE + %")> + + sprintf("index: %i", indexVal) + + %openfile testFile +Some text to verbosely go into the file. + %closefile testFile + + %assign myVar = STRING(ParamSettings.Input) + char fileName[%] = "%"; + + %if flagTrue + void *tp = %; + %else + %return "" + %endif + +%% [EOF] sample.tlc diff --git a/Functions/helper_functions_community/schemer/develop/sample.v b/Functions/helper_functions_community/schemer/develop/sample.v new file mode 100644 index 0000000..c050dc4 --- /dev/null +++ b/Functions/helper_functions_community/schemer/develop/sample.v @@ -0,0 +1,21 @@ +/* Sample Verilog code, which doesn't really do anything + * but it does show syntax highlighting + */ +module main(inputVar, flip); + input inputVar; + input flip; + + reg flop; + + // Comment about dummy code which doesn't work + always @(posedge flip or posedge inputVar) + if (reset) + begin + flop <= (0 & ~inputVar); + end + else + begin + $display("Hello world!"); + $finish; + end +endmodule diff --git a/Functions/helper_functions_community/schemer/develop/sample.vhdl b/Functions/helper_functions_community/schemer/develop/sample.vhdl new file mode 100644 index 0000000..12646c6 --- /dev/null +++ b/Functions/helper_functions_community/schemer/develop/sample.vhdl @@ -0,0 +1,19 @@ +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.NUMERIC_STD.ALL; + +-- Comment about the Driver here +entity Driver is +port( x : in std_logic; + F : out std_logic +); +end Driver; + +architecture gate_level of Driver is +begin + if newx(x downto (x-3))="0000" then + F <= '1'; + else + F <= not(x(2) xor x(2)); --XNOR gate with 2 inputs + end if; +end gate_level; diff --git a/Functions/helper_functions_community/schemer/develop/sample.x3d b/Functions/helper_functions_community/schemer/develop/sample.x3d new file mode 100644 index 0000000..c72ff91 --- /dev/null +++ b/Functions/helper_functions_community/schemer/develop/sample.x3d @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Functions/helper_functions_community/schemer/develop/sample.x3dv b/Functions/helper_functions_community/schemer/develop/sample.x3dv new file mode 100644 index 0000000..bb75b2b --- /dev/null +++ b/Functions/helper_functions_community/schemer/develop/sample.x3dv @@ -0,0 +1,53 @@ +#X3D V3.0 utf8 +PROFILE Interchange +META "filename" "sample.x3dv" + +# Define scripts +DEF Script1 Script { + inputOnly SFTime touchTime + outputOnly SFBool toScript2 + outputOnly SFString string + url [ "ecmascript: + function touchTime() { + toScript2 = TRUE; + } + function eventsProcessed() { + string = 'Script1.eventsProcessed'; + toScript3 = TRUE; + } + " ] +} + +# Draw a blue ball +Transform { + children [ + NavigationInfo { + headlight FALSE + avatarSize [ 0.25 1.6 0.75 ] + type [ "EXAMINE" ] + } + DirectionalLight { + } + Transform { + translation 2.0 0.0 1.0 + children [ + Shape { + geometry Sphere { radius 2.3 + } + appearance Appearance { + material Material { diffuseColor 0.0 0.0 1.0 + } + } + } + ] + } + ] +} + +# Set the viewpoint +Viewpoint { position 7 -1 18 } + +# Set up the scripts +ROUTE TS.touchTime TO Script1.touchTime +ROUTE Script1.string TO Collector.fromString +ROUTE Collector.string TO Result.string diff --git a/Functions/helper_functions_community/schemer/develop/sample.xml b/Functions/helper_functions_community/schemer/develop/sample.xml new file mode 100644 index 0000000..e0b44e3 --- /dev/null +++ b/Functions/helper_functions_community/schemer/develop/sample.xml @@ -0,0 +1,17 @@ + + + + + + Lorem ipsum dolor sit amet. + + Missing value error in bar tag + + Cibo malorum deseruisse nec at. Fabulas quaestio sit eu, nam + viderer percipit deserunt at. + & ". + ]]> + + diff --git a/Functions/helper_functions_community/schemer/develop/short_sample.m b/Functions/helper_functions_community/schemer/develop/short_sample.m new file mode 100644 index 0000000..f1f56d2 --- /dev/null +++ b/Functions/helper_functions_community/schemer/develop/short_sample.m @@ -0,0 +1,15 @@ +function short_sample() +% Short syntax highlighting sample +global gbl_value; +gbl_value = 20 +hghlghtvar = 'a_string_example_here'; + +%% Title of cell +badstring = 'unterminated_string; +unusedvar = hghlghtvar; +!echo "Example `!` system command" +for iterval = 1:2:gbl_value + % This is a very informative comment + fprintf('%s:%d\n',hghlghtvar,iterval); +end +end diff --git a/Functions/helper_functions_community/schemer/develop/template_scheme.prf b/Functions/helper_functions_community/schemer/develop/template_scheme.prf new file mode 100644 index 0000000..3320c20 --- /dev/null +++ b/Functions/helper_functions_community/schemer/develop/template_scheme.prf @@ -0,0 +1,34 @@ +# - MATLAB color scheme +# Created by +# +ColorsUseSystem=Bfalse +ColorsUseMLintAutoFixBackground=Btrue +Editor.VariableHighlighting.Automatic=Btrue +Editor.NonlocalVariableHighlighting=Btrue +EditorCodepadHighVisible=Btrue +EditorCodeBlockDividers=Btrue +Editorhighlight-caret-row-boolean=Btrue +EditorRightTextLineVisible=Btrue +EditorRightTextLimitLineWidth=I1 # Width of bar for right-hand text limit +ColorsText=C- # Main text colour +ColorsBackground=C- # Main background colour +Colors_M_Keywords=C- # Text for keywords (function, for, end, etc) +Colors_M_Comments=C- # Text for comments +Colors_M_Strings=C- # Text for strings +Colors_M_UnterminatedStrings=C- # Text for unterminated strings +Colors_M_SystemCommands=C- # Text for shell commands +Colors_M_Errors=C- #*Editor error messages and underlines, go-to bars +Colors_HTML_HTMLLinks=C- # Text for URLs +Color_CmdWinWarnings=C- #^Command window Warning text +Color_CmdWinErrors=C- #^Command window Error text +Colors_M_Warnings=C- #*Editor Warning underlines and go-to-warning bars +ColorsMLintAutoFixBackground=C- # Background for autofixable +Editor.VariableHighlighting.Color=C- #*Background for occurances of current variable +Editor.NonlocalVariableHighlighting.TextColor=C- # Text for global variables +Editorhighlight-lines=C- # Background of current cell +Editorhighlight-caret-row-boolean-color=C- # Background of current line +EditorRightTextLimitLineColor=C- # Colour of bar for right-hand text limit + +#Legend +# * = Preference does not update until after restarting MATLAB +# ^ = Preference not present on all versions of MATLAB diff --git a/Functions/helper_functions_community/schemer/logo.png b/Functions/helper_functions_community/schemer/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..b00c848cf1ba6a4368056dda0d4443c09cc6aa7a GIT binary patch literal 23752 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

lye<_ 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 c;~zqHU`cmbN@ Programming Tools pane +% - From Editor/Debugger > Display pane, the following: +% - Highlight current line (whether to highlight, and the colour) +% - Right-hand text limit (colour and thickness, whether on/off) +% The output format is the same as used in MATLAB's preferences file, +% which is found at FULLFILE(PREFDIR,'matlab.prf'). +% +% SCHEMER_EXPORT(FILENAME) exports the relevant MATLAB preferences +% to the file FILENAME. +% +% SCHEMER_EXPORT(FILENAME, FLAG_MODE) controls which settings are output +% into the preference file FILENAME. Along with the colour settings for +% MATLAB syntax highlighting, one can also export the boolean preferences +% (such as whether cells and non-local variables, etc, should be coloured +% differently to the regular backgound and text); and one can also export +% the colour settings for syntax highlighting in other languages +% supported by MATLAB. +% +% The FLAG_MODE settings available are: +% 0 - neither booleans, nor additional languages are exported +% 1 - boolean settings are exported, but not additional languages +% 2 - additional language colours are exported, but not boolean settings +% 3 - both booleans and additional languages are exported +% +% By default FLAG_MODE is set to 1, so boolean settings will be +% exported, but the settings for syntax highlighting in additional +% languages will not be. +% +% The colour settings for all MATLAB syntax highlighting will always be +% exported, even for syntax options which are currently disabled, and +% regardless of whether the boolean settings are being exported. This is +% because users loading your exported color scheme may want syntax +% options highlighted which you are not currently using. Consequently, +% when designing a color scheme, it is advisable to set colours for all +% possisble MATLAB colour settings, even if you don't usually use them. +% By default, SCHEMER_IMPORT will not import boolean settings, so users +% will keep their syntax options enabled or disabled as they prefer even +% after importing your color scheme. +% +% Colours for highlighting syntax in other languages supported by MATLAB +% (MuPAD, TLC, VRML, C++, Java, VHDL, Verilog, XML) can be set in the +% preferences panel Editor/Debugger > Language. If you have not set any +% of these colours yourself, you should not export them. If SCHEMER_IMPORT +% loads a color scheme without additional language syntax included, the +% MATLAB colours are extended to highlight syntax in the other languages +% consistent with the MATLAB scheme. +% +% SCHEMER_EXPORT(FLAG_MODE, FILENAME), with a numeric input followed by a +% string, will also work as above because the input order is reversible. +% +% SCHEMER_EXPORT(FLAG_MODE) with a single numeric input will open the GUI +% to pick the filename and will save the output according to FLAG_MODE. +% +% RET = SCHEMER_EXPORT(...) returns 1 on success, 0 on user +% cancellation at the output file selection screen, -1 on fopen error, +% and -2 on any other error. +% +% [RET, NAMES, PREFS] = SCHEMER_EXPORT(...) also returns two cell +% arrays listing the names and preferences which were saved to file. +% +% For more details on how to get and set MATLAB preferences with +% commands, see the following URL. +% http://undocumentedmatlab.com/blog/changing-system-preferences-programmatically +% +% IMPORTANT NOTE: +% You must have, at any point since installing MATLAB, visited the +% Color, Color>Programming Tools and Editor/Debugger>Display panes of +% the Preferences diaglogue within MATLAB and then clicked OK in order +% for all the settings to be exported correctly. You will obviously have +% done this for any settings you have changed but, for example, you may +% have left the Editor/Debugger>Display settings unchanged. If this +% preference pane has not been set its entries will not have been +% defined, and when trying to export these they will be incorrectly saved +% as off (for booleans) or black (for colours). +% +% Example 1: User is not sure if they have OK'd all the relevent +% Preferences panes (or sure they haven't). +% - Open File>Preferences +% - On the left-hand side +% - Click Colors +% - Click Programming Tools (underneath "Colors" as a subsection) +% - Click Editor/Debugger (a different panel to Colors) +% - Click Display (underneath "Editor/Debugger" as a subsection) +% - Click OK +% (Yes, that is literally all you need to do to make sure you can +% export correctly. There is no need to click anywhere which was +% not mentioned. You just have to have each pane appear, and then +% click OK at the end.) +% - Go to your command window and execute SCHEMER_EXPORT +% - The GUI appears, and you pick where to save the file. +% +% Example 2: User is sure they have OK'd all the relevent Preferences +% panes already. +% schemer_export +% +% Example 3: User is sure they have OK'd all the relevent Preferences +% panes already, knows the path they wish to save to, and doesn't want +% to export their boolean settings. +% schemer_export('some/path/schemeName.prf', 0) +% +% Example 4: User has set some colour preferences for C/C++ syntax +% highlighting in addition to MATLAB syntax highlighting, and wants to +% output this along with their boolean settings +% schemer_export(3) +% +% See also SCHEMER_IMPORT, PREFDIR, COLOR2JAVARGBINT. + +% Copyright (c) 2013-2016, Scott C. Lowe +% All rights reserved. +% +% Redistribution and use in source and binary forms, with or without +% modification, are permitted provided that the following conditions are +% met: +% * Redistributions of source code must retain the above copyright +% notice, this list of conditions and the following disclaimer. +% * Redistributions in binary form must reproduce the above copyright +% notice, this list of conditions and the following disclaimer in +% the documentation and/or other materials provided with the distribution +% +% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +% POSSIBILITY OF SUCH DAMAGE. + +function varargout = schemer_export(fname, flag_mode) + +% ------------------------ Parameters ------------------------------------- +SCHEMER_VERSION = 'v1.4.0'; +SCHEMER_URLGIT = 'https://github.com/scottclowe/matlab-schemer'; +SCHEMER_URLFEX = 'https://www.mathworks.com/matlabcentral/fileexchange/53862-matlab-schemer'; +DEFOUTNAME = 'ColorSchemeForMATLAB.prf'; + +% ------------------------ Input handling --------------------------------- +% Default inputs +if nargin<2 + % Default is on. Recipient can pick themselves whether to import. + flag_mode = 1; +end +if nargin<1 + fname = []; +end +% Input switching +if nargin>=1 && ~ischar(fname) && ~isempty(fname) + if ~isnumeric(fname) + error('Invalid input argument 1'); + end + if nargin==1 + % First input omitted + flag_mode = fname; + fname = []; + elseif ischar(flag_mode) + % Inputs switched + tmp = fname; + fname = flag_mode; + flag_mode = tmp; + clear tmp; + else + error('Invalid combination of inputs'); + end +end +% Mode handling +if ~isnumeric(flag_mode) + error('Export mode flag must be numeric.'); +elseif flag_mode < 0 || flag_mode > 3 + error('Bad mode specified: %s', num2str(flag_mode)); +end +inc_bools = mod(flag_mode, 2); +inc_otherlangs = flag_mode >= 2; +% 0: no bools, no other languages +% 1: yes bools, no other languages +% 2: no bools, yes other languages +% 3: yes bools, yes other languages + +% ------------------------ Settings --------------------------------------- +% Names of boolean preferences which should always be output +names_boolean = { ... + 'ColorsUseSystem' ; ... % Color: Desktop: Use system colors +}; +% Names of boolean preferences which the user can choose whether to output +names_boolextra = { ... + 'ColorsUseMLintAutoFixBackground' ; ... % Color>PT: Analyser: autofix highlight + 'Editor.VariableHighlighting.Automatic' ; ... % Color>PT: Var&fn: auto highlight + 'Editor.NonlocalVariableHighlighting' ; ... % Color>PT: Var&fn: with shared scope + 'EditorCodepadHighVisible' ; ... % Color>PT: CellDisp: highlight cells + 'EditorCodeBlockDividers' ; ... % Color>PT: CellDisp: show lines between cells + 'Editorhighlight-caret-row-boolean' ; ... % Editor>Display: Highlight current line + 'EditorRightTextLineVisible' ; ... % Editor>Display: Show Right-hand text limit +}; +% Names of preferences for which the values are integers +names_integer = { ... + 'EditorRightTextLimitLineWidth' ; ... % Editor>Display: Right-hand text limit Width +}; +% The names of the main colour panels, so we can let the user know by name +% if there is an issue with any of them +panels_main = { ... + 'Color' ; ... + 'Color > Programming Tools' ; ... + 'Editor/Debugger > Display' ; ... +}; +% Names of colour preferences which are in the main panels and used in +% MATLAB syntax highlighting +names_color_main = { ... + { ... % Color panel + 'ColorsText' ; ... % Color: Desktop: main text colour + 'ColorsBackground' ; ... % Color: Desktop: main background + 'Colors_M_Keywords' ; ... % Color: Syntax: keywords + 'Colors_M_Comments' ; ... % Color: Syntax: comments + 'Colors_M_Strings' ; ... % Color: Syntax: strings + 'Colors_M_UnterminatedStrings' ; ... % Color: Syntax: unterminated strings + 'Colors_M_SystemCommands' ; ... % Color: Syntax: system commands + 'Colors_M_Errors' ; ... % Color: Syntax: errors + 'Colors_HTML_HTMLLinks' ; ... % Color: Other: hyperlinks + } ... + { ... % Color > Programming Tools + 'Colors_M_Warnings' ; ... % Color>PT: Analyser: warnings + 'ColorsMLintAutoFixBackground' ; ... % Color>PT: Analyser: autofix + 'Editor.VariableHighlighting.Color' ; ... % Color>PT: Var&fn: highlight + 'Editor.NonlocalVariableHighlighting.TextColor' ; ... % Color>PT: Var&fn: with shared scope + 'Editorhighlight-lines' ; ... % Color>PT: CellDisp: highlight + } ... + { ... % Editor/Debugger > Display + 'Editorhighlight-caret-row-boolean-color' ; ... % Editor>Display: Highlight current line Color + 'EditorRightTextLimitLineColor' ; ... % Editor>Display: Right-hand text limit line Color + } +}; +% Names of colour preferences which are known to have been added since the +% year 2011, and so their presence in the preferences is not guaranteed +% column 1: name of setting +% column 2: first version in which preference might have been implemented +% column 3: first version in which preference has been observed to exist +names_color_versioned = { ... + 'Color_CmdWinWarnings' , ... % Color: Command Window Warning messages + '7.13' , ... % Known to NOT be in 2011a (7.12) + '8.2' ; ... % Known to be in 2013b + 'Color_CmdWinErrors' , ... % Color: Command Window Error messages + '8.3' , ... % Known to NOT be in 2013b (8.2) + '8.4' ; ... % Known to be in 2014b +}; +% Names of colour preferences for syntax highlighting in languages other +% than MATLAB +names_color_otherlangs = { ... + { ... % MuPAD + 'Editor.Language.MuPAD.Color.keyword' ; ... + 'Editor.Language.MuPAD.Color.operator' ; ... + 'Editor.Language.MuPAD.Color.block-comment' ; ... + 'Editor.Language.MuPAD.Color.option' ; ... + 'Editor.Language.MuPAD.Color.string' ; ... + 'Editor.Language.MuPAD.Color.function' ; ... + 'Editor.Language.MuPAD.Color.constant' ; ... + } ... + { ... % TLC + 'Editor.Language.TLC.Color.Colors_M_SystemCommands' ; ... + 'Editor.Language.TLC.Color.Colors_M_Keywords' ; ... + 'Editor.Language.TLC.Color.Colors_M_Comments' ; ... + 'Editor.Language.TLC.Color.string-literal' ; ... + } ... + { ... % C/C++ + 'Editor.Language.C.Color.keywords' ; ... + 'Editor.Language.C.Color.line-comment' ; ... + 'Editor.Language.C.Color.string-literal' ; ... + 'Editor.Language.C.Color.preprocessor' ; ... + 'Editor.Language.C.Color.char-literal' ; ... + 'Editor.Language.C.Color.errors' ; ... + } ... + { ... % Java + 'Editor.Language.Java.Color.keywords' ; ... + 'Editor.Language.Java.Color.line-comment' ; ... + 'Editor.Language.Java.Color.string-literal' ; ... + 'Editor.Language.Java.Color.char-literal' ; ... + } ... + { ... % VHDL + 'Editor.Language.VHDL.Color.Colors_M_Keywords' ; ... + 'Editor.Language.VHDL.Color.operator' ; ... + 'Editor.Language.VHDL.Color.Colors_M_Comments' ; ... + 'Editor.Language.VHDL.Color.string-literal' ; ... + } ... + { ... % Verilog + 'Editor.Language.Verilog.Color.Colors_M_Comments' ; ... + 'Editor.Language.Verilog.Color.operator' ; ... + 'Editor.Language.Verilog.Color.Colors_M_Keywords' ; ... + 'Editor.Language.Verilog.Color.string-literal' ; ... + } ... + { ... % XML + 'Editor.Language.XML.Color.error' ; ... + 'Editor.Language.XML.Color.tag' ; ... + 'Editor.Language.XML.Color.attribute' ; ... + 'Editor.Language.XML.Color.operator' ; ... + 'Editor.Language.XML.Color.value' ; ... + 'Editor.Language.XML.Color.comment' ; ... + 'Editor.Language.XML.Color.doctype' ; ... + 'Editor.Language.XML.Color.ref' ; ... + 'Editor.Language.XML.Color.pi-content' ; ... + 'Editor.Language.XML.Color.cdata-section' ; ... + } +}; + +% Names of colour preferences for syntax highlighting in languages other +% than MATLAB +names_color_vrml = { ... + % VRML + 'Editor.Language.VRML.Color.keyword' ; ... + 'Editor.Language.VRML.Color.node-keyword' ; ... + 'Editor.Language.VRML.Color.field-keyword' ; ... + 'Editor.Language.VRML.Color.data-type-keyword' ; ... + 'Editor.Language.VRML.Color.terminal-symbol' ; ... + 'Editor.Language.VRML.Color.comment' ; ... + 'Editor.Language.VRML.Color.string' ; ... +}; +% First version of matlab which uses VRMLX3DV instead of VRML +version_vrmlx3dv = '9.0'; + +% Names of preferences for other language syntax and where setting value +% is a string +% column 1: name of preference +% column 2: a regex which the string must match if it is to be exported +% use '.' or '.+' to allow any non-empty string to be output +% use '' or '.*' for anything, including empty strings +% use '\S' for anything except empty or whitespace-only strings +% use '^str1|str2|str3$' to allow only a finite set of strings +names_string_otherlang = { ... + 'Editor.Language.Java.method' , ... % Java: Show methods + '^(none|bold|italic)$' ; ... +}; + + +% ------------------------ Setup ------------------------------------------ +% Initialise output +if nargout==0 + % Empty if no output requested + varargout = {}; +else + % -2 to signify unknown error otherwise + varargout = {-2}; +end +% If we are outputting all the bools, add them to the list to do +if inc_bools + names_boolean = [names_boolean; names_boolextra]; +end + +% ------------------------ Check ------------------------------------- +% Check the user is not exporting without having visited any of the +% preference panels, or has otherwise set text and background to match +if isequal(com.mathworks.services.Prefs.getColorPref('ColorsText').getRGB, ... + com.mathworks.services.Prefs.getColorPref('ColorsBackground').getRGB) + + % Define the base error message. We will add to it depending on the + % exact set up. + msg = 'Colour for text and background appear to be the same.'; + + % The values match, so give an error + if com.mathworks.services.Prefs.getColorPref('ColorsText').getRGB ... + == -16777216 + % Since the colour is black, it seems the user hasn't visited the + % colour preference panes at all + msg = [msg, 10, ... + 'Are you sure you have visited all the preference panels,'... + ' as per the instructions in the function description?']; + + elseif com.mathworks.services.Prefs.getBooleanPref('ColorsUseSystem') + % The colour is something else, but both text and background match. + % The user is managing to use the colours by overriding them with + % the system colours. + msg = [msg, 10, ... + 'Although you have enabled system colors, the underlying ', ... + 'colour settings match. This is not permitted because the ', ... + 'text would be illegible if system colours were disabled.']; + + else + % The colour is something else, but both text and background match. + % Presumably the text is currently illegible to the user right now. + msg = [msg, 10, ... + 'This is not permitted because the text is illegible.']; + end + + % Raise the error with the completed message + error(msg); +end + +% Go through all the main color preference panels, checking their settings +% are available to us +% Initialise outer level of holding variables +cprefs_main = cell(size(names_color_main)); +colors_main = cell(size(names_color_main)); +% Loop over every one of the main colour preference panels +for iPanel = 1:numel(names_color_main) + % Fetch the colours for this panel + [cprefs_main{iPanel}, colors_main{iPanel}] = ... + fetch_colors(names_color_main{iPanel}); + % Only give the error on the Color and Programming Tools pages, because + % there are only two colours set in the Editor > Display and they could + % plausibly both be black. We instead check this panel seperately below. + if iPanel <= 2 && all(colors_main{iPanel} == -16777216) + % This panel appears to all be black, so we make an error + error(... + ['Colours for all of %1$s panel appear to be black. ' 10 ... + 'This will be because you have never set the preferences ' ... + 'in this panel. You can do so by visiting the %1$s ' ... + 'Preferences page and clicking OK, as detailed in the ' ... + 'description of this function.'], panels_main{iPanel}); + end +end +% Check the Editor > Display panel has been initialised. +% If it hasn't, the line width will appear to be 0, which is not a possible +% setting. +if com.mathworks.services.Prefs.getIntegerPref(... + 'EditorRightTextLimitLineWidth') == 0 + % It hasn't been set, so we generate an error + error(... + ['Properties from the %1$s preference panel could not be loaded. ' ... + 10 ... + 'This will be because you have never set the preferences in '... + 'this panel. You can do so by visiting the %1$s ' ... + 'Preferences page and clicking OK, as detailed in the ' ... + 'description of this function.'], panels_main{3}); +end + +% Let the user know they are doing a stupid thing if they export when using +% the default settings +if com.mathworks.services.Prefs.getBooleanPref('ColorsUseSystem') + warning(... + ['You are exporting with system colors for main text and background.'... + 10, 'The exported theme will look different on different systems.']); +end + +% ------------------------ File stuff ------------------------------------- +if isempty(fname) + % Get user's name + username = java.lang.System.getProperty('user.name'); + % Prepend username to default filename + outFileName = [char(username) DEFOUTNAME]; + % Dialog asking for savename, with smart default output filename + [filename, pathname] = uiputfile(outFileName, ... + 'Select file to write MATLAB color scheme'); + % End if user cancels + if isequal(filename, 0); + if nargout>0; varargout{1} = 0; end; + return; + end + fname = fullfile(pathname, filename); +end + +% Open for write access of text file, create if necessary +fid = fopen(fname, 'w+t', 'n'); +if isequal(fid, -1); + if nargout>0; varargout{1} = -1; end; + return; +end +% Add a cleanup object incase of failure +finishup = onCleanup(@() fclose(fid)); + +% Find the name of the color scheme based on the filename +[~, schemename] = fileparts(fname); + +% Write a few comments to the start of the file +fprintf(fid, '# %s - MATLAB color scheme\n', schemename); +fprintf(fid, '# Generated with schemer_export %s, on MATLAB %s\n', ... + SCHEMER_VERSION, version); +fprintf(fid, '# %s\n', char(java.util.Date)); +fprintf(fid, '# To enable this color scheme in MATLAB use schemer_import, available at:\n'); +fprintf(fid, '# %s\n', SCHEMER_URLGIT); +fprintf(fid, '# %s\n', SCHEMER_URLFEX); + + +% ------------------------ Read and Write --------------------------------- +% Loop through the boolean type settings +prefs_boolean = cell(size(names_boolean)); +for i=1:length(names_boolean) + % Check the name of this preference + nm = names_boolean{i}; + % Get the value of this boolean preference + prefs_boolean{i} = com.mathworks.services.Prefs.getBooleanPref(nm); + % Construct the string to output + outstr = [nm '=B']; + if prefs_boolean{i} + outstr = [outstr 'true']; + else + outstr = [outstr 'false']; + end + outstr = [outstr '\n']; + % Write this boolean value to the output file + fprintf(fid, outstr); +end + +% Loop through the integer type settings +prefs_integer = cell(size(names_integer)); +for i=1:length(names_integer) + % Check the name of this preference + nm = names_integer{i}; + % Get the integer value for this preference + prefs_integer{i} = com.mathworks.services.Prefs.getIntegerPref(nm); + % Write this integer value to the output file + fprintf(fid, '%s=I%d\n', nm, prefs_integer{i}); +end + +% Loop through the colour type settings for MATLAB syntax highlighting +for iPanel=1:numel(names_color_main) + for iPref=1:numel(names_color_main{iPanel}) + % Check the name of this preference + nm = names_color_main{iPanel}{iPref}; + % Write its colour value to the output file + fprintf(fid, '%s=C%d\n', nm, colors_main{iPanel}(iPref)); + end +end + +% Loop over colours which may or may not be available +% Initialise cell arrays for successful fields +onames_versioned = {}; +cprefs_versioned = {}; +% Loop over the colours +for iPref=1:size(names_color_versioned, 1) + % Check the name of this preference + nm = names_color_versioned{iPref, 1}; + % Try to get the colour for this preference + prf = com.mathworks.services.Prefs.getColorPref(nm); + % If the current MATLAB version is less the first version where it is + % not known whether it has implemented this preference feature + % Or if the MATLAB version is less than the first version known to have + % implemented the feature and the colour appears to be black + % Then we skip this preference + if verLessThan('matlab', names_color_versioned{iPref, 2}) ... + || ( ... + verLessThan('matlab', names_color_versioned{iPref, 3}) ... + && prf.getRGB==-16777216 ... + ) + % It appears that this version of MATLAB does not include this + % preference. So we skip it. + continue; + end + % Otherwise, we write its colour value to the output file + fprintf(fid, '%s=C%d\n', nm, prf.getRGB); + % And we add the values to the list to output + onames_versioned{end+1} = nm; + cprefs_versioned{end+1} = prf; +end + + +% Initialise a cell array to output for other language syntax +onames_langs = {}; +cprefs_langs = {}; +% Loop through the colour type settings for other language syntax, only if +% it is requested +if inc_otherlangs + % Go through all the language color syntax preference panels, checking + % their settings are available to us + % Loop over every one of the main colour preference panels + for iPanel = 1:numel(names_color_otherlangs) + [panel_prefs, panel_colors] = fetch_colors(... + names_color_otherlangs{iPanel}); + if all(panel_colors==-16777216) + % All the colours in this panel are black, so we assume the + % color settings have not loaded because they have not been set + continue; + end + % Not all the colours are black, so we assume we have loaded the + % values for this language panel. + + % Loop again over every colour setting in the panel + for iPref = 1:numel(names_color_otherlangs{iPanel}) + % Get the name for the color setting we are interested in + nm = names_color_otherlangs{iPanel}{iPref}; + % Write its colour value to the output file + fprintf(fid, '%s=C%d\n', nm, panel_colors(iPref)); + end + % Remember the prefences so we can return them + onames_langs = [onames_langs; names_color_otherlangs{iPanel}]; + cprefs_langs = [cprefs_langs; panel_prefs]; + end +end + +% We have to do special handling for VRML because in R2016a, MathWorks +% changed the encoding name from VRML to VRMLX3DV. Aside from this, nothing +% else was changed. The rest of the preference name is the same, and the +% default values are unchanged. +if inc_otherlangs + % Deal with VRML and VRMLX3DV possibilities + if verLessThan('matlab', version_vrmlx3dv) + names_color_vrml_usable = names_color_vrml; + else + names_color_vrml_usable = strrep(names_color_vrml, ... + '.VRML.', '.VRMLX3DV.'); + end + % Get the colours from the appropriate preference names + [panel_prefs, panel_colors] = fetch_colors(names_color_vrml_usable); + % If all the colours in this panel are black, we assume the color + % settings have not loaded because they have not been set. + if ~all(panel_colors==-16777216) + for iPref = 1:numel(names_color_vrml) + % Get the name for the color setting we are interested in + nm = names_color_vrml{iPref}; + % Write its colour value to the output file + fprintf(fid, '%s=C%d\n', nm, panel_colors(iPref)); + end + % Remember the prefences so we can return them + onames_langs = [onames_langs; names_color_otherlangs{iPanel}]; + cprefs_langs = [cprefs_langs; panel_prefs]; + end +end + +% Do strings for other languages +if inc_otherlangs + for iPref=1:size(names_string_otherlang, 1) + % Get the name for the string preference we are interested in + nm = names_string_otherlang{iPref, 1}; + % Read the string + str = com.mathworks.services.Prefs.getStringPref(nm); + % Turn it from a java.lang.String object to a regular char object + str = char(str); + % Check it is okay + if isempty(regexp(str, names_string_otherlang{iPref, 2}, ... + 'start', 'emptymatch')) + % It did not have any matches for the regex, so we will not use + % this setting. We will assume its value is not available. + continue; + end + % It matched the regex for acceptable values, so we will export it + fprintf(fid, '%s=S%s\n', nm, str); + % Remember the prefence so we can return it + onames_langs = [onames_langs; nm]; + cprefs_langs = [cprefs_langs; str]; + end +end + +% ------------------------ Tidy up ---------------------------------------- +% fclose(fid); % Don't need to close as it will autoclose +if nargout>0; varargout{1} = 1; end; + +fprintf('Exported color scheme to %s\n', fname); + +if nargout>1; + varargout{2} = cat(1, ... + names_boolean , ... + names_integer , ... + names_color_main{:} , ... + onames_versioned , ... + onames_langs ); + varargout{3} = cat(1, ... + prefs_boolean , ... + prefs_integer , ... + cprefs_main{:} , ... + cprefs_versioned , ... + cprefs_langs ); +end + +end + + +function [prefs, colors] = fetch_colors(names) + % Initialise holding variable for settings in this panel + prefs = cell(size(names)); + colors = nan(size(names)); + % Loop over every colour setting in the panel + for iName = 1:numel(names) + % Read the preference for this colour and get a Java color object + prefs{iName} = com.mathworks.services.Prefs.getColorPref(... + names{iName}); + % Turn this into an integer colour value + colors(iName) = prefs{iName}.getRGB; + end +end \ No newline at end of file diff --git a/Functions/helper_functions_community/schemer/schemer_import.m b/Functions/helper_functions_community/schemer/schemer_import.m new file mode 100644 index 0000000..9749ee6 --- /dev/null +++ b/Functions/helper_functions_community/schemer/schemer_import.m @@ -0,0 +1,712 @@ +%SCHEMER_IMPORT Import a MATLAB color scheme +% SCHEMER_IMPORT() with no input will prompt the user to locate the +% color theme source file via the GUI. +% +% SCHEMER_IMPORT(FILENAME) imports the color scheme options given in +% the file FILENAME. +% +% SCHEMER_IMPORT(FILENAME, INCLUDEBOOLS) can control whether boolean +% preferences are included in import (default: FALSE). If INCLUDEBOOLS +% is set to true, boolean preference options such as whether to +% highlight autofixable errors, or to show variables with shared scope in +% a different colour will also be overridden, should they be set in the +% input file. +% +% SCHEMER_IMPORT(INCLUDEBOOLS, FILENAME), with a boolean or numeric input +% followed by a string input, will also work as above because the input +% order is reversible. +% +% SCHEMER_IMPORT(INCLUDEBOOLS) with a single boolean input will open the +% GUI to pick the file, and will load boolean preferences in accordance +% with INCLUDEBOOLS. +% +% RET = SCHEMER_IMPORT(...) returns 1 on success, 0 on user +% cancellation at input file selection screen, -1 on fopen error, and -2 +% on any other error. +% +% NOTE: +% The file to import can either be a color scheme file as generated by +% SCHEMER_EXPORT, or an entire MATLAB preferences file such as the file +% you will find located at FULLFILE(PREFDIR,'matlab.prf'). This could be +% a MATLAB preferences file taken from a different computer or previous +% MATLAB installation. However, if you are importing from a matlab.prf +% file instead of a color scheme .prf file you should be aware that any +% colour preferences which have been left as the defaults on preference +% panels which the user has not visited on the origin system of the +% matlab.prf file will not be present in the file, and hence not updated +% on import. +% +% The MATLAB preference options which will be overwritten by +% SCHEMER_IMPORT are: +% - All settings in the Color pane of Preferencs +% - All colour settings in the Color > Programming Tools pane, but no +% checkboxes +% - From Editor/Debugger > Display pane, the following: +% - Highlight current line (colour, but not whether to) +% - Right-hand text limit (colour and thickness, but not on/off) +% - From Editor/Debugger > Language, the syntax highlighting colours for +% each language. +% +% Once the current colour preferences are overridden they cannot be +% undone, so it is recommended that you export your current preferences +% with SCHEMER_EXPORT before importing a new theme if you think you +% may wish to revert. +% +% This is not necessary if you are using the default MATLAB color scheme +% which ships with the installation, as SCHEMER comes with a copy of the +% MATLAB default color scheme (default.prf). +% +% If you wish to revert to the default MATLAB color scheme, it is +% recommended you import the file default.prf included in this +% package. This will reset Editor/Debugger>Display colours, colours for +% syntax highlighting in additional languages, as well as the colours set +% in the Colors pane. You can also revert the colors by clicking +% "Restore Default Colors" in the MATLAB preference panel interface, but +% this will be less effective because there are several panels which set +% syntax colours and not all of them have a restore button. +% +% For more details on how to get and set MATLAB preferences with +% commands, see the following URL. +% http://undocumentedmatlab.com/blog/changing-system-preferences-programmatically/ +% +% See also SCHEMER_EXPORT, PREFDIR. + +% Copyright (c) 2013-2016, Scott C. Lowe +% All rights reserved. +% +% Redistribution and use in source and binary forms, with or without +% modification, are permitted provided that the following conditions are +% met: +% * Redistributions of source code must retain the above copyright +% notice, this list of conditions and the following disclaimer. +% * Redistributions in binary form must reproduce the above copyright +% notice, this list of conditions and the following disclaimer in +% the documentation and/or other materials provided with the distribution +% +% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +% POSSIBILITY OF SUCH DAMAGE. + +% Known issues: +% +% 1. Text colour of automatically highlighted variables does not change +% colour immediately. This is an issue with matlab; if you change the main +% text colour in the preferences pane, highlighted variables will still +% have the old text colour until matlab is restarted. +% +% 2. Java exception is thrown when first trying to update the setting +% Editor.VariableHighlighting.Color. This only happens the first +% time SCHEMER_IMPORT is run, so the current fix is to catch the error +% and then try again. However, it might be possible for other Java +% exceptions get thrown under other mysterious circumstances, which could +% cause the function to fail. + +function varargout = schemer_import(fname, inc_bools) + +% ------------------------ Parameters ------------------------------------- +SCHEMER_VERSION = 'v1.4.0'; + +% ------------------------ Input handling --------------------------------- +% ------------------------ Default inputs --------------------------------- +if nargin<2 + inc_bools = false; % Default off, so only override extra options if intended +end +if nargin<1 + fname = []; % Ask user to select file +end +% Input switching +if nargin>=1 && ~ischar(fname) && ~isempty(fname) + if ~islogical(fname) && ~isnumeric(fname) + error('Invalid input argument 1'); + end + if nargin==1 + % First input omitted + inc_bools = fname; + fname = []; + elseif ischar(inc_bools) + % Inputs switched + tmp = fname; + fname = inc_bools; + inc_bools = tmp; + clear tmp; + else + error('Invalid combination of inputs'); + end +end + +% ------------------------ Check for file --------------------------------- +filefilt = ... + {'*.prf' , 'MATLAB preference files (*.prf)' ; ... + '*.prf;*.txt', 'Text and pref files (*.prf, *.txt)'; ... + '*' , 'All Files' }; + +if ~isempty(fname) + if ~exist(fname, 'file') + error('Specified file does not exist'); + end +else + % Dialog asking for input filename + % Need to make this dialogue include .txt by default, at least + [filename, pathname] = uigetfile(filefilt); + % End if user cancels + if isequal(filename, 0); + if nargout>0; varargout{1} = 0; end; + return; + end + fname = fullfile(pathname, filename); +end + +% ------------------------ Catch block ------------------------------------ +% Somewhat inexplicably, a Java exception is thrown the first time we try +% to set 'Editor.VariableHighlighting.Color'. +% But if we try again immediately, it can be set without any problems. +% The issue is very consistent. +% The solution is to try to set this colour along with all the others, +% catch the exception when it occurs, and then attempt to set all the +% preferences again. +try + [varargout{1:nargout}] = main(fname, inc_bools); +catch ME + if ~strcmp(ME.identifier, 'MATLAB:Java:GenericException'); + rethrow(ME); + end + % disp('Threw and ignored a Java exception. Retrying.'); + [varargout{1:nargout}] = main(fname, inc_bools); +end + +end + +% ======================== Main code ====================================== +function varargout = main(fname, inc_bools) + +% ------------------------ Parameters ------------------------------------- +% Names of boolean preferences which should always be imported +names_boolean = { ... + 'ColorsUseSystem' ... % Color: Desktop: Use system colors +}; +% Names of boolean preferences which the user can choose whether to import +names_boolextra = { ... + 'ColorsUseMLintAutoFixBackground' ... % Color>PT: Analyser: autofix highlight + 'Editor.VariableHighlighting.Automatic' ... % Color>PT: Var&fn: auto highlight + 'Editor.NonlocalVariableHighlighting' ... % Color>PT: Var&fn: with shared scope + 'EditorCodepadHighVisible' ... % Color>PT: CellDisp: highlight cells + 'EditorCodeBlockDividers' ... % Color>PT: CellDisp: show lines between cells + 'Editorhighlight-caret-row-boolean' ... % Editor>Display: Highlight current line + 'EditorRightTextLineVisible' ... % Editor>Display: Show Right-hand text limit +}; +% Names of preferences for which the values are integers +names_integer = { ... + 'EditorRightTextLimitLineWidth' ... % Editor>Display: Right-hand text limit Width +}; +% Names of preferences for which the values are strings +% column 1: name of preference +% column 2: a regex which the string must match if it is to be exported +% use '.' or '.+' to allow any non-empty string to be output +% use '' or '.*' for anything, including empty strings +% use '\S' for anything except empty or whitespace-only strings +% use '^str1|str2|str3$' to allow only a finite set of strings +names_string = { ... + 'Editor.Language.Java.method' , ... % Java: Show methods + '^(none|bold|italic)$' ; ... +}; +% Names of colour preferences, and their default value if not present in +% the .prf file +% column 1: name of preference +% column 2: default value. +% - Empty if no default, +% - Integer if specific colour, +% - String if inherited from another colour setting (which +% should appear higher up in the list) +% - Cell array of strings to take the average colour from +% column 3: whether a restart is required if this value changes +names_color = { ... + 'ColorsText' , ... % Color: Desktop: main text colour + '' , ... + false ; ... + 'ColorsBackground' , ... % Color: Desktop: main background + '' , ... + true ; ... + 'Colors_M_Errors' , ... % Color: Syntax: errors + -65536 , ... + true ; ... + 'Colors_M_Warnings' , ... % Color>PT: Analyser: warnings + -27648 , ... + true ; ... + 'Colors_M_Keywords' , ... % Color: Syntax: keywords + 'ColorsText' , ... + false ; ... + 'Colors_M_Comments' , ... % Color: Syntax: comments + {'ColorsText','ColorsBackground'} , ... + false ; ... + 'Colors_M_Strings' , ... % Color: Syntax: strings + 'ColorsText' , ... + false ; ... + 'Colors_M_UnterminatedStrings' , ... % Color: Syntax: unterminated strings + 'Colors_M_Errors' , ... + false ; ... + 'Colors_M_SystemCommands' , ... % Color: Syntax: system commands + 'Colors_M_Keywords' , ... + false ; ... + 'Colors_HTML_HTMLLinks' , ... % Color: Other: hyperlinks + 'ColorsText' , ... + false ; ... + 'Color_CmdWinWarnings' , ... % Color: Other: Warning messages + 'Colors_M_Warnings' , ... + false ; ... + 'Color_CmdWinErrors' , ... % Color: Other: Error messages + 'Colors_M_Errors' , ... + false ; ... + 'ColorsMLintAutoFixBackground' , ... % Color>PT: Analyser: autofix + 'ColorsBackground' , ... + false ; ... + 'Editor.VariableHighlighting.Color' , ... % Color>PT: Var&fn: highlight + 'ColorsBackground' , ... + true ; ... + 'Editor.NonlocalVariableHighlighting.TextColor' , ... % Color>PT: Var&fn: with shared scope + 'ColorsText' , ... + false ; ... + 'Editorhighlight-lines' , ... % Color>PT: CellDisp: highlight + 'ColorsBackground' , ... + false ; ... + 'Editorhighlight-caret-row-boolean-color' , ... % Editor>Display: Highlight current line Color + 'Editorhighlight-lines' , ... + false ; ... + 'EditorRightTextLimitLineColor' , ... % Editor>Display: Right-hand text limit line Color + -5723992 , ... + false ; ... + + 'Editor.Language.MuPAD.Color.keyword' , ... % MuPAD: Keywords + 'Colors_M_Keywords' , ... + false ; ... + 'Editor.Language.MuPAD.Color.operator' , ... % MuPAD: Operators + 'Colors_M_SystemCommands' , ... + false ; ... + 'Editor.Language.MuPAD.Color.block-comment' , ... % MuPAD: Comments + 'Colors_M_Comments' , ... + false ; ... + 'Editor.Language.MuPAD.Color.option' , ... % MuPAD: Options + 'Colors_M_UnterminatedStrings' , ... + false ; ... + 'Editor.Language.MuPAD.Color.string' , ... % MuPAD: Strings + 'Colors_M_Strings' , ... + false ; ... + 'Editor.Language.MuPAD.Color.function' , ... % MuPAD: System Functions + {'Colors_M_Keywords','ColorsBackground'} , ... + false ; ... + 'Editor.Language.MuPAD.Color.constant' , ... % MuPAD: Constants + 'Editor.NonlocalVariableHighlighting.TextColor' , ... + false ; ... + + 'Editor.Language.TLC.Color.Colors_M_SystemCommands' , ... % TLC: Commands + 'Colors_M_Keywords' , ... + false ; ... + 'Editor.Language.TLC.Color.Colors_M_Keywords' , ... % TLC: Macros + 'Colors_M_SystemCommands' , ... + false ; ... + 'Editor.Language.TLC.Color.Colors_M_Comments' , ... % TLC: Comments + 'Colors_M_Comments' , ... + false ; ... + 'Editor.Language.TLC.Color.string-literal' , ... % TLC: C Strings + 'Colors_M_Strings' , ... + false ; ... + + 'Editor.Language.C.Color.keywords' , ... % C/C++: Keywords + 'Colors_M_Keywords' , ... + false ; ... + 'Editor.Language.C.Color.line-comment' , ... % C/C++: Comments + 'Colors_M_Comments' , ... + false ; ... + 'Editor.Language.C.Color.string-literal' , ... % C/C++: Strings + 'Colors_M_Strings' , ... + false ; ... + 'Editor.Language.C.Color.preprocessor' , ... % C/C++: Preprocessor + 'Colors_M_SystemCommands' , ... + false ; ... + 'Editor.Language.C.Color.char-literal' , ... % C/C++: Characters + 'Colors_M_UnterminatedStrings' , ... + false ; ... + 'Editor.Language.C.Color.errors' , ... % C/C++: Bad characters + 'Colors_M_Errors' , ... + false ; ... + + 'Editor.Language.Java.Color.keywords' , ... % Java: Keywords + 'Colors_M_Keywords' , ... + false ; ... + 'Editor.Language.Java.Color.line-comment' , ... % Java: Comments + 'Colors_M_Comments' , ... + false ; ... + 'Editor.Language.Java.Color.string-literal' , ... % Java: Strings + 'Colors_M_Strings' , ... + false ; ... + 'Editor.Language.Java.Color.char-literal' , ... % Java: Characters + 'Colors_M_UnterminatedStrings' , ... + false ; ... + + 'Editor.Language.VHDL.Color.Colors_M_Keywords' , ... % VHDL: Keywords + 'Colors_M_Keywords' , ... + false ; ... + 'Editor.Language.VHDL.Color.operator' , ... % VHDL: Operators + 'Colors_M_SystemCommands' , ... + false ; ... + 'Editor.Language.VHDL.Color.Colors_M_Comments' , ... % VHDL: Comments + 'Colors_M_Comments' , ... + false ; ... + 'Editor.Language.VHDL.Color.string-literal' , ... % VHDL: Strings + 'Colors_M_Strings' , ... + false ; ... + + 'Editor.Language.Verilog.Color.Colors_M_Keywords' , ... % Verilog: Keywords + 'Colors_M_Keywords' , ... + false ; ... + 'Editor.Language.Verilog.Color.operator' , ... % Verilog: Operators + 'Colors_M_SystemCommands' , ... + false ; ... + 'Editor.Language.Verilog.Color.Colors_M_Comments' , ... % Verilog: Comments + 'Colors_M_Comments' , ... + false ; ... + 'Editor.Language.Verilog.Color.string-literal' , ... % Verilog: Strings + 'Colors_M_Strings' , ... + false ; ... + + 'Editor.Language.XML.Color.error' , ... % XML: Error + 'Colors_M_Errors' , ... + false ; ... + 'Editor.Language.XML.Color.tag' , ... % XML: Tag + 'Colors_M_Keywords' , ... + false ; ... + 'Editor.Language.XML.Color.attribute' , ... % XML: Attribute name + 'Colors_M_UnterminatedStrings' , ... + false ; ... + 'Editor.Language.XML.Color.operator' , ... % XML: Operator + 'Colors_M_SystemCommands' , ... + false ; ... + 'Editor.Language.XML.Color.value' , ... % XML: Attribute value + 'Colors_M_Strings' , ... + false ; ... + 'Editor.Language.XML.Color.comment' , ... % XML: Comment + 'Colors_M_Comments' , ... + false ; ... + 'Editor.Language.XML.Color.doctype' , ... % XML: DOCTYPE declaration + 'Colors_HTML_HTMLLinks' , ... + false ; ... + 'Editor.Language.XML.Color.ref' , ... % XML: Character + 'Colors_M_UnterminatedStrings' , ... + false ; ... + 'Editor.Language.XML.Color.pi-content' , ... % XML: Processing instruction + 'Colors_HTML_HTMLLinks' , ... + false ; ... + 'Editor.Language.XML.Color.cdata-section' , ... % XML: CDATA section + 'Editor.NonlocalVariableHighlighting.TextColor' , ... + false ; ... + + 'Editor.Language.VRML.Color.keyword' , ... % VRML: Keywords + 'Editor.Language.XML.Color.doctype' , ... + false ; ... + 'Editor.Language.VRML.Color.node-keyword' , ... % VRML: Node types + 'Editor.Language.XML.Color.tag' , ... + false ; ... + 'Editor.Language.VRML.Color.field-keyword' , ... % VRML: Fields + 'Editor.Language.XML.Color.attribute' , ... + false ; ... + 'Editor.Language.VRML.Color.data-type-keyword' , ... % VRML: Data types + 'Editor.Language.XML.Color.cdata-section' , ... + false ; ... + 'Editor.Language.VRML.Color.terminal-symbol' , ... % VRML: Terminal symbols + 'Editor.Language.XML.Color.operator' , ... + false ; ... + 'Editor.Language.VRML.Color.comment' , ... % VRML: Comments + 'Editor.Language.XML.Color.comment' , ... + false ; ... + 'Editor.Language.VRML.Color.string' , ... % VRML: Strings + 'Editor.Language.XML.Color.value' , ... + false ; ... + + 'Editor.Language.VRMLX3DV.Color.keyword' , ... % VRML/X3DV: Keywords + 'Editor.Language.VRML.Color.keyword' , ... + false ; ... + 'Editor.Language.VRMLX3DV.Color.node-keyword' , ... % VRML/X3DV: Node types + 'Editor.Language.VRML.Color.node-keyword' , ... + false ; ... + 'Editor.Language.VRMLX3DV.Color.field-keyword' , ... % VRML/X3DV: Fields + 'Editor.Language.VRML.Color.field-keyword' , ... + false ; ... + 'Editor.Language.VRMLX3DV.Color.data-type-keyword' , ... % VRML/X3DV: Data types + 'Editor.Language.VRML.Color.data-type-keyword' , ... + false ; ... + 'Editor.Language.VRMLX3DV.Color.terminal-symbol' , ... % VRML/X3DV: Terminal symbols + 'Editor.Language.VRML.Color.terminal-symbol' , ... + false ; ... + 'Editor.Language.VRMLX3DV.Color.comment' , ... % VRML/X3DV: Comments + 'Editor.Language.VRML.Color.comment' , ... + false ; ... + 'Editor.Language.VRMLX3DV.Color.string' , ... % VRML/X3DV: Strings + 'Editor.Language.VRML.Color.string' , ... + false ; ... +}; + +verbose = 0; + +% ------------------------ Setup ------------------------------------------ +if nargout==0 + varargout = {}; +else + varargout = {-2}; +end +if inc_bools + names_boolean = [names_boolean names_boolextra]; +end +needs_restart = false; + +% ------------------------ Check file seems okay -------------------------- +% Read in the contents of the entire file +flestr = fileread(fname); +% Search for occurances of main text colour +txtprf = regexp(flestr, '\sColorsText=(?[^#\s]+)\s', 'names'); +if isempty(txtprf) + error('Text colour not present in colorscheme file:\n%s', fname); +elseif length(txtprf) > 1 + error('Text colour defined multiple times in colorscheme file:\n%s', fname); +end +% Search for occurances of main background colour +bkgprf = regexp(flestr, '\sColorsBackground=(?[^#\s]+)\s', 'names'); +if isempty(bkgprf) + error('Background colour not present in colorscheme file:\n%s', fname); +elseif length(bkgprf) > 1 + error('Background colour defined multiple times in colorscheme file:\n%s', fname); +end +% Make sure the main text and background colours are not exactly the same +if strcmp(txtprf.pref, bkgprf.pref) + error('Main text and background colours are the same in this file:\n%s', fname); +end + +% ------------------------ File stuff ------------------------------------- +% Open for read access only +fid = fopen(fname, 'r', 'n'); +if isequal(fid, -1); + if nargout>0; varargout{1} = -1; end; + return; +end +% Add a cleanup object incase of failure +finishup = onCleanup(@() fclose(fid)); + +% ------------------------ Read and Write --------------------------------- +% Initialise tracker for unset colours +isColorSet = false(size(names_color, 1), 1); +% Loop over prf file +while ~feof(fid) + % Get one line of preferences/theme file + l = fgetl(fid); + + % Ignore empty lines and lines which begin with # + if length(l)<1 || strcmp('#', l(1)) + if verbose; disp('Comment'); end; + continue; + end + + % Look for name pref pair, seperated by '=' + % Must be at begining of string (hence ^ anchor) + % Cannot contain comment marker (#) + n = regexp(l, '^(?[^=#]+)=(?[^#]+)', 'names'); + + % If no match, continue and scan next line + if isempty(n) + if verbose; disp('No match'); end; + continue; + end + + % Trim whitespace from pref + n.pref = strtrim(n.pref); + + if ismember(n.name, names_boolean) + % Deal with boolean type + switch lower(n.pref) + case 'btrue' + % Preference is true + com.mathworks.services.Prefs.setBooleanPref(n.name, 1); + if verbose; fprintf('Set bool true %s\n', n.name); end + case 'bfalse' + % Preference is false + com.mathworks.services.Prefs.setBooleanPref(n.name, 0); + if verbose; fprintf('Set bool false %s\n', n.name); end + otherwise + % Shouldn't be anything else + warning('Bad boolean for %s: %s', n.name, n.pref); + end + + elseif ismember(n.name, names_integer) + % Deal with integer type + if ~strcmpi('I', n.pref(1)) + warning('Bad integer pref for %s: %s', n.name, n.pref); + continue; + end + int = str2double(n.pref(2:end)); + com.mathworks.services.Prefs.setIntegerPref(n.name, int); + if verbose; fprintf('Set integer %d for %s\n', int, n.name); end + + elseif ismember(n.name,names_string(:, 1)) + % Deal with string type + if ~strcmpi('S', n.pref(1)) + warning('Bad string pref for %s: %s', n.name, n.pref); + continue; + end + str = n.pref(2:end); + % Look up which of the preference settings this is + [~, idx] = ismember(n.name, names_string(:, 1)); + % Check that the setting allowed by the regex it must satisfy + if isempty(regexp(str, names_string{idx, 2}, ... + 'start', 'emptymatch')) + % If not, we can't set the value to be this + warning('Invalid string for %s: %s', n.name, str); + continue; + end + com.mathworks.services.Prefs.setStringPref(n.name, str); + if verbose; fprintf('Set string %s for %s\n', str, n.name); end + + elseif ismember(n.name, names_color(:, 1)) + % Deal with colour type (final type to consider) + if ~strcmpi('C', n.pref(1)) + warning('Bad color for %s: %s', n.name, n.pref); + continue; + end + [~, idx] = ismember(n.name, names_color(:, 1)); + % Get the current setting for this parameter + previousVal = ... + com.mathworks.services.Prefs.getColorPref(names_color{idx, 1}); + % Set the colour to the target value + rgb = str2double(n.pref(2:end)); + jc = java.awt.Color(rgb); + com.mathworks.services.Prefs.setColorPref(n.name, jc); + com.mathworks.services.ColorPrefs.notifyColorListeners(n.name); + if verbose + fprintf('Set color (%3.f, %3.f, %3.f) for %s\n', ... + jc.getRed, jc.getGreen, jc.getBlue, n.name); + end + % Note that we have allocated this colour + isColorSet(idx) = true; + % Check if the colour has changed + if names_color{idx, 3} && ~isequal(previousVal, jc) + needs_restart = true; + end + + else + % Silently ignore irrelevant preferences + % (This means you can load a whole matlab.prf file and anything not + % listed above as relevant to the color scheme will be ignored.) + + end + +end + +% Check that at least one colour was actually set +if ~any(isColorSet) + error('Did not find any colour settings in file\n%s', fname); + if nargout>0; varargout{1} = -2; end; + return; +end + +% For colours which have not been set by the color scheme, we set them from +% a backup +% Get a row vector of indices of all unset colours +unsetColorIndices = find(~isColorSet)'; +% Loop over unset colours +for idx=unsetColorIndices + % Get the backup setting for this colour parameter + backupVal = names_color{idx, 2}; + % Get the current setting for this parameter + previousVal = ... + com.mathworks.services.Prefs.getColorPref(names_color{idx, 1}); + + clear jc; % Clear variable + + % Switch based on the type of backup + if isempty(backupVal) + % No backup is set + continue; + + elseif iscell(backupVal) + % Backup is one of several methods of which involve refactoring one + % or more other colours + % Get an RGB value for the colour through whichever method + if all(cellfun(@ischar, backupVal)) + % Backup is a list of other names to sample and average + % Initialise a holding matrix + RGB = nan(numel(backupVal), 3); + for i=1:numel(backupVal) + % Load each of the other colours + jc = com.mathworks.services.Prefs.getColorPref(backupVal{i}); + % Put the R, G, B values into the holding matrix + RGB(i, 1) = jc.getRed; + RGB(i, 2) = jc.getGreen; + RGB(i, 3) = jc.getBlue; + end + % Take the average of each RGB value from the other colours + RGB = mean(RGB); + + elseif length(backupVal)==2 + % Backup is a name of a colour and a scale factor to apply + jc = com.mathworks.services.Prefs.getColorPref(backupVal{1}); + % Get the R, G, B values + RGB = [jc.getRed, jc.getGreen, jc.getBlue]; + % Rescale them + RGB = RGB * backupVal{2}; + + else + error('Bad backup cell for %s', names_color{idx, 1}); + + end + % Turn the RGB value into a Java color object + % Ensure RGB is integer and does not exceed 255 + RGB = min(255, round(RGB)); + % Convert to a float + RGB = RGB/255; + % Make a Java color object for this colour + jc = java.awt.Color(RGB(1), RGB(2), RGB(3)); + + elseif ischar(backupVal) + % The backup colour is a reference to another colour + % Look up the colour from the backup reference + jc = com.mathworks.services.Prefs.getColorPref(backupVal); + + elseif isnumeric(backupVal) && numel(backupVal)==1 + % The backup colour is a specific colour + % Make a java color object for this specific colour + jc = java.awt.Color(backupVal); + + else + error('Bad backup value for %s', names_color{idx, 1}); + + end + % Assign the neglected colour to be this Java colour object from the + % backup + com.mathworks.services.Prefs.setColorPref(names_color{idx, 1}, jc); + com.mathworks.services.ColorPrefs.notifyColorListeners(names_color{idx, 1}); + + % Check if the colour has changed + if names_color{idx, 3} && ~isequal(previousVal, jc) + needs_restart = true; + end + +end + +% ------------------------ Tidy up ---------------------------------------- +% fclose(fid); % Don't need to close as it will autoclose +if nargout>0; varargout{1} = 1; end; + +if inc_bools + fprintf('Imported color scheme WITH boolean options from\n%s\n', fname); +else + fprintf('Imported color scheme WITHOUT boolean options from\n%s\n', fname); +end +if needs_restart + disp('Some changes require MATLAB to be restarted to be activated.'); +end + +end diff --git a/Functions/helper_functions_community/schemer/schemes/.gitattributes b/Functions/helper_functions_community/schemer/schemes/.gitattributes new file mode 100644 index 0000000..07ef0d9 --- /dev/null +++ b/Functions/helper_functions_community/schemer/schemes/.gitattributes @@ -0,0 +1,46 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# +# The above will handle all files NOT found below +# + +##--------------------------------------------------- +# Common .gitattributes +##--------------------------------------------------- + +# Documents +# ============ +*.txt text +*.md text + +# Graphics +# ============ +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.tif binary +*.tiff binary +*.ico binary +*.svg text +*.eps binary + + +##--------------------------------------------------- +# Basic .gitattributes for a MATLAB repo +##--------------------------------------------------- + +# Source files +# ============ +*.m text +*.prf text + +# Binary files +# ============ +*.p binary +*.mex* binary +*.fig binary +*.mat binary +*.mdl binary +*.slx binary diff --git a/Functions/helper_functions_community/schemer/schemes/.gitignore b/Functions/helper_functions_community/schemer/schemes/.gitignore new file mode 100644 index 0000000..f7bb9d7 --- /dev/null +++ b/Functions/helper_functions_community/schemer/schemes/.gitignore @@ -0,0 +1,5 @@ +# Editor autosave files +*~ +*.asv +# Compiled MEX binaries (all platforms) +*.mex* diff --git a/Functions/helper_functions_community/schemer/schemes/CONTRIBUTING.md b/Functions/helper_functions_community/schemer/schemes/CONTRIBUTING.md new file mode 100644 index 0000000..ca40323 --- /dev/null +++ b/Functions/helper_functions_community/schemer/schemes/CONTRIBUTING.md @@ -0,0 +1,153 @@ +Contributing a colour scheme +============================ + +Glad to hear you've made a colour scheme to use with [MATLAB Schemer]! +Thanks for sharing this with us. + +Please only add one colour scheme per pull request. +If you have multiple colorschemes to add, make a pull request for each of them. + +The Schemer README includes some +[advice on creating a colour scheme][scheme creation advice], which you might +find useful. + +It is preferable to include a screenshot demonstrating an example of your new MATLAB theme. +To make your screenshot: + +- Load your color scheme `schemer_import('yourscheme.prf', 1)` +- Exit MATLAB `exit();` +- Reopen MATLAB +- Open the [matlab-schemer/develop/sample.m] file and take a screenshot with your favourite screenshot program. +- If not done at capture time, crop the screenshot down to the right size with your [favourite image editor]. +- Make sure the screenshot is saved in [matlab-schemes/screenshots] as `yourscheme.png`. + +If you are on Linux, you can follow the method below to create your screenshot, +which is mostly automated and produces the same output every time. + + +Reproducible screenshot workflow for Linux +------------------------------------------ + +Here is minimal-interaction workflow for Linux users, which will create the +same screenshots every time. +This has been tested on Ubuntu 15.10 with MATLAB 2016a, but should work on +any *nix OS and any MATLAB version since R2012b (which introduced the +toolstrip UI). + +The code blocks must be run from the system shell, not at the MATLAB command +prompt. + +1. **Setup** + + Define these variables appropriately. + + ```bash + # Set this variable appropriately, without extension + SCHEME_NAME='yourschemename' + # Set the path to your copy of MATLAB Schemer + PATH_TO_SCHEMER='../matlab-schemer/' + ``` + +2. **Load scheme** + + Run this code block to load the scheme, restart MATLAB, and edit + `sample.m`. + + ```bash + # Remove extension, if present + SCHEME_NAME=${SCHEME_NAME%.prf} + echo "Making screenshot for scheme $SCHEME_NAME"; + # Load the template and restart matlab + matlab -r "addpath(genpath('$PATH_TO_SCHEMER')); schemer_import('$SCHEME_NAME.prf',1); exit;"; + # Edit the sample.m file + matlab -r "edit(fullfile('$PATH_TO_SCHEMER','develop','sample.m'))" & + # What next + echo "Now undock sample.m, and highlight middle scaleFactor"; + ``` + +3. **Undock sample.m editor** + + Either + - in MATLAB GUI, undock `sample.m` only + + Or + - in MATLAB GUI, undock the Editor panel + - move tabs to bottom, if more than one file is being editted + + Also, make sure the MATLAB toolstrip is visible on the undocked panel. + If it is minimised, right-click on the toolstrip and restore it. + +4. **Highlight** + + Highlight the middle instance of `scaleFactor` (on line 18). + +5. **Resize window and take screenshot** + + Run this code block to resize the editor window and take a screenshot, + cropped with ImageMagick. + + The crop location is correct for MATLAB 2016a. + + ```bash + # Check for a window for the develop/sample.m file + if wmctrl -l | grep -q develop/sample.m + then + WINDOW_NAME='develop/sample.m'; + else + # If it's not there, use the Editor window + WINDOW_NAME='Editor'; + fi + if wmctrl -l | grep -qv "$WINDOW_NAME" + then + echo "Window $WINDOW_NAME is absent"; + else + # Resize the window + wmctrl -r "$WINDOW_NAME" -e 0,100,100,700,650; + # Try getting screenshot with Imagemagick, and cropping it down to the + # just the relevant section + wmctrl -a "$WINDOW_NAME"; sleep 0.1; import -window root -crop 700x379+100+249 +repage "${SCHEME_NAME}.png"; + # Inspect the result + xdg-open "${SCHEME_NAME}.png"; + # Is it cropped correctly? + echo "How does it look? If no good, try one of the other options to manually crop"; + fi + ``` + +6. If screenshot cropped incorrectly, **manually crop screenshot** + + If the ImageMagick crop is not aligned correctly, + - Either use gnome-screenshot for the whole window, and then crop in GIMP + ```bash + wmctrl -a "$WINDOW_NAME" \ + && gnome-screenshot -w -f "$SCHEME_NAME.png" \ + && gimp "$SCHEME_NAME.png"; + ``` + + - Or use gnome-screenshot to select the area to use (which is likely to + be less precise). + ```bash + wmctrl -a "$WINDOW_NAME" && gnome-screenshot -a -f "$SCHEME_NAME.png"; + ``` + +7. When you are happy, **move** the final copy into the screenshots folder + + ```bash + mv "$SCHEME_NAME.png" screenshots/"$SCHEME_NAME.png"; + ``` + + +Incorporating into MATLAB Schemer +--------------------------------- + +Once you've added the new scheme to this repository, it will be merged into +[MATLAB Schemer] using [git-subtree]. +If you are merging it into Schemer yourself, please consult this +[step-by-step guide](https://github.com/scottclowe/matlab-schemer/blob/master/CONTRIBUTING.md#adding-a-new-colour-scheme). + + + [MATLAB Schemer]: https://github.com/scottclowe/matlab-schemer + [matlab-schemes/screenshots]: https://github.com/scottclowe/matlab-schemes/tree/master/screenshots + [matlab-schemer/develop/sample.m]: https://github.com/scottclowe/matlab-schemer/blob/master/develop/sample.m + [scheme creation advice]: https://github.com/scottclowe/matlab-schemer#creating-a-color-scheme-for-others-to-use + [git-subtree]: https://github.com/git/git/blob/master/contrib/subtree/git-subtree.txt + [favourite image editor]: https://www.gimp.org diff --git a/Functions/helper_functions_community/schemer/schemes/LICENSE b/Functions/helper_functions_community/schemer/schemes/LICENSE new file mode 100644 index 0000000..212fa8b --- /dev/null +++ b/Functions/helper_functions_community/schemer/schemes/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2015, Scott Lowe +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/Functions/helper_functions_community/schemer/schemes/README.md b/Functions/helper_functions_community/schemer/schemes/README.md new file mode 100644 index 0000000..a9fc66c --- /dev/null +++ b/Functions/helper_functions_community/schemer/schemes/README.md @@ -0,0 +1,84 @@ +MATLAB Schemes +============== + +A collection of color schemes for MATLAB. + +These color schemes can be enabled using the [MATLAB Schemer package]. + + +Samples +------- + +### Cobalt +Based on the [GTK stylesheet], "Cobalt", by Will Farrington. + +![Cobalt sample](screenshots/cobalt.png) + +### Darkmate +Based on the [GTK stylesheet], "Darkmate", +by [Luigi Maselli](https://grigio.org/). + +![Darkmate sample](screenshots/darkmate.png) + +### Dark Steel +By [@scottclowe](https://github.com/scottclowe), inspired by the Darkmate and +Cobalt themes. + +![Dark Steel sample](screenshots/darksteel.png) + +### Matrix +By [@scottclowe](https://github.com/scottclowe), using only colors present in +the terminals shown in the movie, +[*The Matrix*](http://www.imdb.com/title/tt0133093/). + +![Matrix sample](screenshots/matrix.png) + +### Monokai +By [@dkearn](https://github.com/dkearn). Based on Sublime Text's Monokai theme. Change the font to Consolas to complete the look. + +![Monokai sample](screenshots/monokai.png) + +### Oblivion +Based on the [Gedit theme, "Oblivion"](https://github.com/mig/gedit-themes/blob/master/oblivion.xml), +by Paolo Borelli and GtkSourceView. + +![Oblivion sample](screenshots/oblivion.png) + +### Solarised Dark +Based on the [Solarized][Solarized] (1.0.0beta2) palette, in dark mode, +by [Ethan Schoonover]. + +![Solarized Dark sample](screenshots/solarized-dark.png) + +### Solarised Light +Based on the [Solarized][Solarized] (1.0.0beta2) palette, in light mode, +by [Ethan Schoonover]. + +![Solarized Light sample](screenshots/solarized-light.png) + +### Tango +Based on the colour palette for the [Tango Desktop Project]. + +![Tango sample](screenshots/tango.png) + +### Vibrant +Based on the [GTK stylesheet], "Vibrant", by Lateef Alabi-Oki. + +![Vibrant sample](screenshots/vibrant.png) + + +Default +------- + +And of course, you can always return to the original MATLAB color scheme +provided by [MathWorks](https://www.mathworks.com/) using the `default.prf` +color preferences file. + +![Default](screenshots/default.png) + + + [MATLAB Schemer package]: https://github.com/scottclowe/matlab-schemer + [GTK stylesheet]: https://wiki.gnome.org/Projects/GtkSourceView/StyleSchemes + [Solarized]: http://ethanschoonover.com/solarized + [Ethan Schoonover]: http://ethanschoonover.com/ + [Tango Desktop Project]: http://tango-project.org/ diff --git a/Functions/helper_functions_community/schemer/schemes/SilasColorSchemeForMATLAB.prf b/Functions/helper_functions_community/schemer/schemes/SilasColorSchemeForMATLAB.prf new file mode 100644 index 0000000..2c1ea59 --- /dev/null +++ b/Functions/helper_functions_community/schemer/schemes/SilasColorSchemeForMATLAB.prf @@ -0,0 +1,33 @@ +# SilasColorSchemeForMATLAB - MATLAB color scheme +# Generated with schemer_export v1.4.0, on MATLAB 9.5.0.1586782 (R2018b) Update 8 +# Wed Dec 07 13:28:52 CET 2022 +# To enable this color scheme in MATLAB use schemer_import, available at: +# https://github.com/scottclowe/matlab-schemer +# https://www.mathworks.com/matlabcentral/fileexchange/53862-matlab-schemer +ColorsUseSystem=Bfalse +ColorsUseMLintAutoFixBackground=Btrue +Editor.VariableHighlighting.Automatic=Btrue +Editor.NonlocalVariableHighlighting=Btrue +EditorCodepadHighVisible=Btrue +EditorCodeBlockDividers=Btrue +Editorhighlight-caret-row-boolean=Bfalse +EditorRightTextLineVisible=Btrue +EditorRightTextLimitLineWidth=I1 +ColorsText=C-2893873 +ColorsBackground=C-14144461 +Colors_M_Keywords=C-9720087 +Colors_M_Comments=C-6380369 +Colors_M_Strings=C-3231622 +Colors_M_UnterminatedStrings=C-3891200 +Colors_M_SystemCommands=C-10443343 +Colors_M_Errors=C-2920075 +Colors_HTML_HTMLLinks=C-9265201 +Colors_M_Warnings=C-217282 +ColorsMLintAutoFixBackground=C-2920075 +Editor.VariableHighlighting.Color=C-4423978 +Editor.NonlocalVariableHighlighting.TextColor=C-7487874 +Editorhighlight-lines=C-13288387 +Editorhighlight-caret-row-boolean-color=C-11184301 +EditorRightTextLimitLineColor=C-11775661 +Color_CmdWinWarnings=C-217282 +Color_CmdWinErrors=C-2920075 diff --git a/Functions/helper_functions_community/schemer/schemes/cobalt.prf b/Functions/helper_functions_community/schemer/schemes/cobalt.prf new file mode 100644 index 0000000..a0101d1 --- /dev/null +++ b/Functions/helper_functions_community/schemer/schemes/cobalt.prf @@ -0,0 +1,45 @@ +# Cobalt - MATLAB color scheme +# Created by Scott Lowe, 2015-07-05 +# Based on the GTK stylesheet "Cobalt", by Will Farrington. +# https://wiki.gnome.org/Projects/GtkSourceView/StyleSchemes +# To enable this color scheme in MATLAB use schemer_import, available at: +# https://github.com/scottclowe/matlab-schemer +# https://www.mathworks.com/matlabcentral/fileexchange/53862-matlab-schemer +ColorsUseSystem=Bfalse +ColorsUseMLintAutoFixBackground=Btrue +Editor.VariableHighlighting.Automatic=Btrue +Editor.NonlocalVariableHighlighting=Btrue +EditorCodepadHighVisible=Btrue +EditorCodeBlockDividers=Btrue +Editorhighlight-caret-row-boolean=Btrue +EditorRightTextLineVisible=Btrue +EditorRightTextLimitLineWidth=I1 +ColorsText=C-1 # white +ColorsBackground=C-16770253 # dark_blue +Colors_M_Keywords=C-25344 # bright_orange +Colors_M_Comments=C-16742145 # sky_blue +Colors_M_Strings=C-12920576 # spring_green +Colors_M_UnterminatedStrings=C-4480 # faded_yellow +Colors_M_SystemCommands=C-3355444 # light grey +Colors_M_Errors=C-65468 # nail_polish_pink +Colors_HTML_HTMLLinks=C-16751169 # light_blue +Colors_M_Warnings=C-20396 # +ColorsMLintAutoFixBackground=C-12303292 # +Editor.VariableHighlighting.Color=C-16728180 # eye-drop +Editor.NonlocalVariableHighlighting.TextColor=C-8323141 # teal_blue +Editorhighlight-lines=C-15981252 # eye-drop +Editorhighlight-caret-row-boolean-color=C-16762000 # medium_blue +EditorRightTextLimitLineColor=C-14665909 # eye-drop +# C/C++ +Editor.Language.C.Color.preprocessor=C-8323141 # teal_blue +# Java +Editor.Language.Java.Color.keywords=C-8323141 # teal_blue +# VHDL +Editor.Language.VHDL.Color.operator=C-65468 # nail_polish_pink +# Verilog +Editor.Language.Verilog.Color.operator=C-65468 # nail_polish_pink +# XML/HTML +Editor.Language.XML.Color.tag=C-8323141 # teal_blue +Editor.Language.XML.Color.doctype=C-3355444 # light grey +Editor.Language.XML.Color.pi-content=C-3355444 # light grey +Editor.Language.XML.Color.cdata-section=C-3355444 # light grey diff --git a/Functions/helper_functions_community/schemer/schemes/darkmate.prf b/Functions/helper_functions_community/schemer/schemes/darkmate.prf new file mode 100644 index 0000000..b253f63 --- /dev/null +++ b/Functions/helper_functions_community/schemer/schemes/darkmate.prf @@ -0,0 +1,34 @@ +# Darkmate - MATLAB color scheme +# Created by Scott Lowe, 2015-07-05 +# Based on the GTK stylesheet "Darkmate", by Luigi Maselli. +# https://wiki.gnome.org/Projects/GtkSourceView/StyleSchemes +# To enable this color scheme in MATLAB use schemer_import, available at: +# https://github.com/scottclowe/matlab-schemer +# https://www.mathworks.com/matlabcentral/fileexchange/53862-matlab-schemer +ColorsUseSystem=Bfalse +ColorsUseMLintAutoFixBackground=Btrue +Editor.VariableHighlighting.Automatic=Btrue +Editor.NonlocalVariableHighlighting=Btrue +EditorCodepadHighVisible=Btrue +EditorCodeBlockDividers=Btrue +Editorhighlight-caret-row-boolean=Btrue +EditorRightTextLineVisible=Btrue +EditorRightTextLimitLineWidth=I4 # slightly wider +ColorsText=C-1118482 # white +ColorsBackground=C-14474461 # carbon +Colors_M_Keywords=C-26368 # ambra +Colors_M_Comments=C-10920873 # asfalto +Colors_M_Strings=C-6881536 # lime +Colors_M_UnterminatedStrings=C-202417 # yellow +Colors_M_SystemCommands=C-16725605 # alga +Colors_M_Errors=C-53398 # red +Colors_HTML_HTMLLinks=C-6385153 # violet +Colors_M_Warnings=C-26368 # ambra +ColorsMLintAutoFixBackground=C-11184811 # +Editor.VariableHighlighting.Color=C-4495617 # purple +Editor.NonlocalVariableHighlighting.TextColor=C-16725760 # green +Editorhighlight-lines=C-15132391 # +Editorhighlight-caret-row-boolean-color=C-16777216 # black +EditorRightTextLimitLineColor=C-13948117 # +# XML/HTML +Editor.Language.XML.Color.pi-content=C-6425200 diff --git a/Functions/helper_functions_community/schemer/schemes/darksteel.prf b/Functions/helper_functions_community/schemer/schemes/darksteel.prf new file mode 100644 index 0000000..77627f2 --- /dev/null +++ b/Functions/helper_functions_community/schemer/schemes/darksteel.prf @@ -0,0 +1,44 @@ +# Dark Steel - MATLAB color scheme +# Created by Scott Lowe +# Generated with colortheme_export v1.0.0 +# Fri Aug 09 18:39:37 BST 2013 +# To enable this color scheme in MATLAB use schemer_import, available at: +# https://github.com/scottclowe/matlab-schemer +# https://www.mathworks.com/matlabcentral/fileexchange/53862-matlab-schemer +ColorsUseSystem=Bfalse +ColorsUseMLintAutoFixBackground=Btrue +Editor.VariableHighlighting.Automatic=Btrue +Editor.NonlocalVariableHighlighting=Btrue +EditorCodepadHighVisible=Btrue +EditorCodeBlockDividers=Btrue +Editorhighlight-caret-row-boolean=Btrue +EditorRightTextLineVisible=Btrue +EditorRightTextLimitLineWidth=I1 +ColorsText=C-1 +ColorsBackground=C-15066598 +Colors_M_Keywords=C-1208813 +Colors_M_Comments=C-14114579 +Colors_M_Strings=C-16724992 +Colors_M_UnterminatedStrings=C-4210944 +Colors_M_SystemCommands=C-7123493 +Colors_M_Errors=C-45747 +Colors_HTML_HTMLLinks=C-10592257 +Colors_M_Warnings=C-27648 +ColorsMLintAutoFixBackground=C-9223357 +Editor.VariableHighlighting.Color=C-11184786 +Editor.NonlocalVariableHighlighting.TextColor=C-16735351 +Editorhighlight-lines=C-14408662 +Editorhighlight-caret-row-boolean-color=C-12632257 +EditorRightTextLimitLineColor=C-5723992 +# TLC +Editor.Language.TLC.Color.Colors_M_Keywords=C-16735351 +# C/C++ +Editor.Language.C.Color.preprocessor=C-16735351 +# VHDL +Editor.Language.VHDL.Color.operator=C-16735351 +# Verilog +Editor.Language.Verilog.Color.operator=C-16735351 +# XML +Editor.Language.XML.Color.operator=C-1710454 +Editor.Language.XML.Color.doctype=C-6578958 +Editor.Language.XML.Color.pi-content=C-9868801 diff --git a/Functions/helper_functions_community/schemer/schemes/default.prf b/Functions/helper_functions_community/schemer/schemes/default.prf new file mode 100644 index 0000000..b444487 --- /dev/null +++ b/Functions/helper_functions_community/schemer/schemes/default.prf @@ -0,0 +1,95 @@ +# Default MATLAB color scheme +# This color scheme file restores the color scheme which ships with MATLAB. +# To enable this color scheme in MATLAB use schemer_import, available at: +# https://github.com/scottclowe/matlab-schemer +# https://www.mathworks.com/matlabcentral/fileexchange/53862-matlab-schemer +ColorsUseSystem=Btrue +ColorsUseMLintAutoFixBackground=Btrue +Editor.VariableHighlighting.Automatic=Btrue +Editor.NonlocalVariableHighlighting=Btrue +EditorCodepadHighVisible=Btrue +EditorCodeBlockDividers=Btrue +Editorhighlight-caret-row-boolean=Bfalse +EditorRightTextLineVisible=Btrue +EditorRightTextLimitLineWidth=I1 +ColorsText=C-16777216 +ColorsBackground=C-1 +Colors_M_Keywords=C-16776961 +Colors_M_Comments=C-14513374 +Colors_M_Strings=C-6283024 +Colors_M_UnterminatedStrings=C-5111808 +Colors_M_SystemCommands=C-5075968 +Colors_M_Errors=C-65536 +Colors_HTML_HTMLLinks=C-16776961 +Color_CmdWinWarnings=C-39936 +Color_CmdWinErrors=C-1703936 +Colors_M_Warnings=C-27648 +ColorsMLintAutoFixBackground=C-1121868 +Editor.VariableHighlighting.Color=C-3479320 +Editor.NonlocalVariableHighlighting.TextColor=C-16735325 +Editorhighlight-lines=C-197412 +Editorhighlight-caret-row-boolean-color=C-2167080 +EditorRightTextLimitLineColor=C-3355444 +# MuPAD +Editor.Language.MuPAD.Color.string=C-6283024 +Editor.Language.MuPAD.Color.constant=C-14638864 +Editor.Language.MuPAD.Color.keyword=C-16776961 +Editor.Language.MuPAD.Color.function=C-8355662 +Editor.Language.MuPAD.Color.block-comment=C-14513374 +Editor.Language.MuPAD.Color.option=C-5075968 +Editor.Language.MuPAD.Color.operator=C-5103616 +# TLC +Editor.Language.TLC.Color.Colors_M_SystemCommands=C-16776961 +Editor.Language.TLC.Color.Colors_M_Keywords=C-5075968 +Editor.Language.TLC.Color.Colors_M_Comments=C-14513374 +Editor.Language.TLC.Color.string-literal=C-6283024 +# VRML +Editor.Language.VRML.Color.comment=C-14513374 +Editor.Language.VRML.Color.node-keyword=C-16022329 +Editor.Language.VRML.Color.field-keyword=C-9145228 +Editor.Language.VRML.Color.terminal-symbol=C-2196224 +Editor.Language.VRML.Color.keyword=C-16776961 +Editor.Language.VRML.Color.data-type-keyword=C-6737152 +Editor.Language.VRML.Color.string=C-6283024 +# VRML/X3DV +Editor.Language.VRMLX3DV.Color.keyword=C-16776961 +Editor.Language.VRMLX3DV.Color.node-keyword=C-16022329 +Editor.Language.VRMLX3DV.Color.field-keyword=C-9145228 +Editor.Language.VRMLX3DV.Color.data-type-keyword=C-6737152 +Editor.Language.VRMLX3DV.Color.terminal-symbol=C-2196224 +Editor.Language.VRMLX3DV.Color.comment=C-14513374 +Editor.Language.VRMLX3DV.Color.string=C-6283024 +# C/C++ +Editor.Language.C.Color.keywords=C-16776961 +Editor.Language.C.Color.line-comment=C-14513374 +Editor.Language.C.Color.string-literal=C-6283024 +Editor.Language.C.Color.preprocessor=C-5075968 +Editor.Language.C.Color.char-literal=C-48897 +Editor.Language.C.Color.errors=C-65536 +# Java +Editor.Language.Java.method=Sbold +Editor.Language.Java.Color.keywords=C-16776961 +Editor.Language.Java.Color.line-comment=C-14513374 +Editor.Language.Java.Color.string-literal=C-6283024 +Editor.Language.Java.Color.char-literal=C-48897 +# VHDL +Editor.Language.VHDL.Color.Colors_M_Keywords=C-16776961 +Editor.Language.VHDL.Color.operator=C-5075968 +Editor.Language.VHDL.Color.Colors_M_Comments=C-14513374 +Editor.Language.VHDL.Color.string-literal=C-6283024 +# Verilog +Editor.Language.Verilog.Color.Colors_M_Comments=C-14513374 +Editor.Language.Verilog.Color.operator=C-5075968 +Editor.Language.Verilog.Color.Colors_M_Keywords=C-16776961 +Editor.Language.Verilog.Color.string-literal=C-6283024 +# XML +Editor.Language.XML.Color.error=C-65536 +Editor.Language.XML.Color.tag=C-16776961 +Editor.Language.XML.Color.attribute=C-5111808 +Editor.Language.XML.Color.operator=C-5075968 +Editor.Language.XML.Color.value=C-6283024 +Editor.Language.XML.Color.comment=C-14513374 +Editor.Language.XML.Color.doctype=C-16777092 +Editor.Language.XML.Color.ref=C-5111808 +Editor.Language.XML.Color.pi-content=C-16777092 +Editor.Language.XML.Color.cdata-section=C-8625664 diff --git a/Functions/helper_functions_community/schemer/schemes/matrix.prf b/Functions/helper_functions_community/schemer/schemes/matrix.prf new file mode 100644 index 0000000..f7b6e25 --- /dev/null +++ b/Functions/helper_functions_community/schemer/schemes/matrix.prf @@ -0,0 +1,31 @@ +# Matrix - MATLAB color scheme +# Created by Scott Lowe, 2015-07-05. Modified 2015-04-08. +# Based on the movie of the same name. +# To enable this color scheme in MATLAB use schemer_import, available at: +# https://github.com/scottclowe/matlab-schemer +# https://www.mathworks.com/matlabcentral/fileexchange/53862-matlab-schemer +ColorsUseSystem=Bfalse +ColorsUseMLintAutoFixBackground=Btrue +Editor.VariableHighlighting.Automatic=Btrue +Editor.NonlocalVariableHighlighting=Btrue +EditorCodepadHighVisible=Btrue # with cell bg +EditorCodeBlockDividers=Bfalse # without cell lines +Editorhighlight-caret-row-boolean=Btrue +EditorRightTextLineVisible=Btrue +EditorRightTextLimitLineWidth=I1 +ColorsText=C-15810737 +ColorsBackground=C-16448762 +Colors_M_Keywords=C-8524550 +Colors_M_Comments=C-14199226 +Colors_M_Strings=C-15236555 +Colors_M_UnterminatedStrings=C-13769101 +Colors_M_SystemCommands=C-15122561 +Colors_M_Errors=C-2678475 +Colors_HTML_HTMLLinks=C-14525287 +Colors_M_Warnings=C-10475466 +ColorsMLintAutoFixBackground=C-6544075 +Editor.VariableHighlighting.Color=C-14855081 +Editor.NonlocalVariableHighlighting.TextColor=C-16354525 +Editorhighlight-lines=C-16117996 +Editorhighlight-caret-row-boolean-color=C-15919586 +EditorRightTextLimitLineColor=C-5723992 diff --git a/Functions/helper_functions_community/schemer/schemes/monokai.prf b/Functions/helper_functions_community/schemer/schemes/monokai.prf new file mode 100644 index 0000000..99dee85 --- /dev/null +++ b/Functions/helper_functions_community/schemer/schemes/monokai.prf @@ -0,0 +1,32 @@ +# monokai - MATLAB color scheme +# Generated with schemer_export v1.3.5, on MATLAB 8.1.0.604 (R2013a) +# Mon Apr 25 15:03:32 BST 2016 +# To enable this color scheme in MATLAB use schemer_import, available at: +# https://github.com/scottclowe/matlab-schemer +# https://www.mathworks.com/matlabcentral/fileexchange/53862-matlab-schemer +ColorsUseSystem=Bfalse +ColorsUseMLintAutoFixBackground=Btrue +Editor.VariableHighlighting.Automatic=Btrue +Editor.NonlocalVariableHighlighting=Btrue +EditorCodepadHighVisible=Btrue +EditorCodeBlockDividers=Btrue +Editorhighlight-caret-row-boolean=Bfalse +EditorRightTextLineVisible=Btrue +EditorRightTextLimitLineWidth=I1 +ColorsText=C-460560 +ColorsBackground=C-14211038 +Colors_M_Keywords=C-448910 +Colors_M_Comments=C-9080482 +Colors_M_Strings=C-1647756 +Colors_M_UnterminatedStrings=C-65536 +Colors_M_SystemCommands=C-16711936 +Colors_M_Errors=C-65536 +Colors_HTML_HTMLLinks=C-16711681 +Colors_M_Warnings=C-27648 +ColorsMLintAutoFixBackground=C-11974594 +Editor.VariableHighlighting.Color=C-10066330 +Editor.NonlocalVariableHighlighting.TextColor=C-16729641 +Editorhighlight-lines=C-13421773 +Editorhighlight-caret-row-boolean-color=C-10066330 +EditorRightTextLimitLineColor=C-3355444 +Color_CmdWinWarnings=C-26368 diff --git a/Functions/helper_functions_community/schemer/schemes/oblivion.prf b/Functions/helper_functions_community/schemer/schemes/oblivion.prf new file mode 100644 index 0000000..371647d --- /dev/null +++ b/Functions/helper_functions_community/schemer/schemes/oblivion.prf @@ -0,0 +1,52 @@ +# Oblivion - MATLAB color scheme +# Created by Scott Lowe, 2015-07-05 +# Based on the Gedit theme "Oblivion", by Paolo Borelli. +# https://github.com/mig/gedit-themes +# To enable this color scheme in MATLAB use schemer_import, available at: +# https://github.com/scottclowe/matlab-schemer +# https://www.mathworks.com/matlabcentral/fileexchange/53862-matlab-schemer +ColorsUseSystem=Bfalse +ColorsUseMLintAutoFixBackground=Btrue +Editor.VariableHighlighting.Automatic=Btrue +Editor.NonlocalVariableHighlighting=Btrue +EditorCodepadHighVisible=Btrue +EditorCodeBlockDividers=Btrue +Editorhighlight-caret-row-boolean=Btrue +EditorRightTextLineVisible=Btrue +EditorRightTextLimitLineWidth=I1 +ColorsText=C-2893873 # aluminium2 +ColorsBackground=C-13749194 # aluminium6 +Colors_M_Keywords=C-1 # white +Colors_M_Comments=C-7828859 # aluminium4 +Colors_M_Strings=C-1190912 # butter2 +Colors_M_UnterminatedStrings=C-3891200 # butter3 +Colors_M_SystemCommands=C-7675340 # chameleon1 +Colors_M_Errors=C-1103575 # scarlet1 +Colors_HTML_HTMLLinks=C-9265201 # skyblue1 +Colors_M_Warnings=C-217282 # orange1 +ColorsMLintAutoFixBackground=C-6029312 # scarlet3 +Editor.VariableHighlighting.Color=C-11625978 # chameleon3 +Editor.NonlocalVariableHighlighting.TextColor=C-9265201 # skyblue1 +Editorhighlight-lines=C-13288387 # +Editorhighlight-caret-row-boolean-color=C-11184301 # aluminium5 +EditorRightTextLimitLineColor=C-11775661 # +# C/C++ +Editor.Language.C.Color.keywords=C-7675340 # chameleon1 +Editor.Language.C.Color.preprocessor=C-5406808 # plum1 +Editor.Language.C.Color.char-literal=C-9265201 # skyblue1 +# Java +Editor.Language.Java.Color.keywords=C-7675340 # chameleon1 +Editor.Language.Java.Color.char-literal=C-9265201 # skyblue1 +# VHDL +Editor.Language.VHDL.Color.Colors_M_Keywords=C-7675340 # chameleon1 +Editor.Language.VHDL.Color.operator=C-1 # white +# Verilog +Editor.Language.Verilog.Color.operator=C-1 # white +Editor.Language.Verilog.Color.Colors_M_Keywords=C-7675340 # chameleon1 +# XML/HTML +Editor.Language.XML.Color.tag=C-9265201 # skyblue1 +Editor.Language.XML.Color.attribute=C-7675340 # chameleon1 +Editor.Language.XML.Color.operator=C-1967753 # +Editor.Language.XML.Color.doctype=C-5406808 # plum1 +Editor.Language.XML.Color.pi-content=C-5406808 # plum1 +Editor.Language.XML.Color.cdata-section=C-5406808 # plum1 diff --git a/Functions/helper_functions_community/schemer/schemes/screenshots/cobalt.png b/Functions/helper_functions_community/schemer/schemes/screenshots/cobalt.png new file mode 100644 index 0000000000000000000000000000000000000000..89cec1a72e3ec75611ffb6b7b81513009578226a GIT binary patch literal 31170 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

u|i6HSa#KL0(+4DN8#3=?1(X$i6qbwgoL zP-cuq5*k|hhmBN61a1p&=gwTc=AamjJwVoq52Q7fW2BxTX}F~StDJ>TQ~=M-3^ zn@u;9pjVejSLWq;11a&RaI2iT=(*T*=sV%-5QvLgI#Ec8r%^L1HhJ6-no zhk-VFQ-OvUxc1NuO6WyvADWkkr|^q3DAtMJPL|Ng|B%sjSzb~2_@1H)sdqY$KDrx{ zHd-V%hwQEIZ+4-OEPqs6864QDV9R_5&?*yQ=~!u<{TqRSaYyS;<$SzyOx_)HOWB0& znt~6JqpCkxM{0kP5ZP)f90~AZbmI8UWy|B!t^5|(Tq@vMbH`_sX-Tt*Xo*<;RA+3Q zy=R|u9M6Ife>;K=Sol7!0>j9$jI8AB?a*dPCvI=e10lbmYQH{%o*hgME~9j`?S?)die$Zd%z@%}inJhNNr~~HgN7eRRgyF)nkid zB3BReV@;}>Ln@lAV*+&`!y|^#nL9QHhgtBGN4{?%e#tm|{&?a#j+o}J$FGyV7%lD< z;@IuXgpw;zmE{%vT^Bv0?ce)~6%3+|qZ-9VpYd1tb_rU{mi{b$oGv>Sw(@jb? zDsoYpoeyCve{m5yUYj%Q5jv%fXf5085Oxy_*vX-(PW#3EH2aik8R4!vEriusQQ)F9&l3 z86F>Q`m{!ldZZ$Q+|+l9hFXJNk!@!VFm}-wkSyRm9Jdy6d;$yHF>kt28RzQEVI3Y| zS3mC`wBc3o>4U}Oic|^4pv8+L%4Fp0qi&qLNFSw5gI=ny6y^$?#U}N;PskyJ)Tn5QjqI%?y+=8i&nt2acRN-==e?S~6y8D}E5f0bq}Y0) zoctUiDKwx<_$UtpOwqLI>BZ%j&GlLPm{NU(%?v@%7^Pz~WJ@*Nf@`Gp_g zLh3|~-lWo}6m^i#Rx;F65I~}eN2j1Hn%x1;E=|!ndRkV38SeG#O%FUB`vCEze)%9-SWhfwl0|P(r|0v2i!~~w-4gFUr|bJ*)r9;PEX9Xo8T$! z-rJ}VF0G%K70@IHPSbI&82!saeSt#;t%UF0lfT&^^wBuxqsQ+fpix#{VTc_a2&z3kLoKIu-d&M}2BtsYc5wB?v@(AJ|JO3Y! za&jIb4Ga>3p4=f`-`ip2Sab`x!#yfy4RkJw{_ykHAP5#EqOKXIJ7SY-nP0m~Acj>l z2Pb$48JHgh%$G&0>hi^4TiwMP>)+PKi?c}y0%hEY zmow*xHE}dIZ~{hkgl}1wZNUja3TMpxoMJ-@l{HNNgF(_UNaQj$K^foh!rAcR)_3-P zYT}49nWcxlkukZCDOoiA%z{+~`~AlfAUW{;eOc+3-KT1io^LLyyN#W=CF%8ICQrPn zr|y0tcxiMSzHfn;<8vWcQ1uU^1VlOUa4RZhDTVw5bE;=X;>H34qmF(zv_khST$x2& zgwVFw2HX!5BOc}FHmwehf?_nJL?hh5zXe0*LnIpH)@Yo&0 z5{=x)?e|bv7rg?8uzGthD4unv%$6&A`BA=)5J^ktNm6BQw$Syw$-JnQ35IQ)4tVLUrTEAt;9W9>_85RLuQOSy1A7B`6Q!4VjagL4pg1=TGp1gKYUHA7 z?7lL`t_Srpibc$)EsQ4Xnb2t%t7&VDMkj-NJa@!?e**seHx?W*KK)}KC+1Vg!FLQ? z7{Lz{RXmTWVV{vinW86aLe#32D)Hm}Emx>|w}{~Dp)uS;XUc#Zuu0%f{ls)K3{0-R z^~gPgLadwV$)}n^3cuC>`>hEI3nOzI9@{8#Nxjns%ALs>4j2E6OD+65MCc8 zE%LQy%&W)wE5K(wLsq0B2H9|uPWFkZ?GtmhVB{(8{-%pg&Q_9ICfHKb|H9S%QtgT4 zyAx|NES4Y7q~6b@S@cD0mVFPBuSMrB26&=vD4F%m>2UY{nC;~X8Ped1x4#Fy_Met~ zy=HAJ@b&mG_+KKx#3(xE(W{8<5;6Gnca}G*V-km@>bfUyY$J2LnyX#6r_3R^71@1n z>^@-Lhp+ds8EkdLSGwzAT+qxD`wlb@T(axV{S1mMoX!DbF8L=J>C)9AY8_F@M;jx3 z^0)b`h121%zlq?Lz>M;?-MK~K!uwWPkOEc`{^amNxwji$K5*Kx(J0-Ymdag)6z)(I z{sLLe$OO;m5Oa#~Kq>2)ww;w|ztY=X^i13=M+GCE4d|PVr20;H2Qc(=?Y(6(w({*2 zm79_-`DT(jO8_t!a|@c)I!`g44IW9KoV9;oNJj8&;vDgH!qn zWGatQa=+m-Ges`3n`}Ly)>71t@fX8=N|F}_=IiUL_#C+KPaa=Yizt7v2FJekiSQRq zhjka~u;JXZM@zxdCZ~0&EhIF&xwme;?ev%_)=L~h{^vm9#7JSwC zXsAE$3Bj~8M)K_6ne~j#tu$}2mUuG8M4{kyC5feF&_7RLaM?>+)tW!~IXx~)h`ZhH z5$Zxmkg^$QfdD=$|H5y8vKI6H=TPW?zBV;RFwrBQ5X(cV+=e5k(9M&xc&HHz6KMqO z?fLK)26KE9lU4fnxYZJP$LxApMk%YwExq+=f;>6~KH+Xes%d|PeG{q+ld9z}tgH$B z5xF|K1Dhm7S%dhNh<45>?d2dy_$344UZhbzygOlgI`sje55jpkgo6tM9Cc zc=JMr6S%i;!n%1jch2;;J=p>uASX$*hc_OAB?(BlCe)0cdmCwqe2L5tH1q0OXxv|R zcvVwLt^?*1lV0DQxsu5JL9S<}<80(i>PKF}yng5{+n#c3#5I}b!x*Zq&)1!Ne zWnd=({o;K*cZwpHX$4i=}^;PvXXai1Lt6c7W$2=BQ>SC_Niygh;l3^Bfi? z2-$US-xu*iJ|d-3FD$D*FP7^tRO;;AF>6?wZ{k;_skiPeXtSffPX9D9|8({1=?7>x zo|xBmZx7eIit+a`+m8b8z(7*xh=TG1NV<64zH6!Ztg%B`iJX=KeyNit>xk}c{Tidb zFJ(|OJvn3CE7NCYo0WAlmasODAHwlLf#j!&C_IYak66NPE{t~haOPj%bSzhN@-?R3 ztqjZ$mE2wr(wm(R)Xp*U8ssg*)hVwvO=8RPwf+9Bwx;U!@vT?eCO+L;@gUN@@+7@w zA)=zyXGM}0CHE#Xz=yp4Mv%i%l~8-(s{$lRffVe@1*(bKkib7V(Sw^_Wn4O<*3C(X zA`Y(!#9sDoyeGYfccn$2l=u`>`K^=Hn2fw>xydx|bbdc3nF~Ur zP0R)(MwzQldLI3|_&C|?0GYusi{bdE%j_d)N=m^1G;|hwoqN!@MvnH*a{S}nNK$1d zfB&x*j@bROV5k!23&)z%7=a>{*--LoWX>ed{f@2pXh?t$TC{>m$M+Zjv*rnhMChu< zrhA9kp&DmfYZhA>p{pq|yKUM5+qQOfSf)(8KCjaUXpy{C2hP@g+gD+m?Loh>)1}=1 zxJBOCA3qUo_gs8_va8d3Y(^GJwKU#bb1$f;S-K6N4!&CsvT~DYus;hMOVxI85lt=s ziR5{Map|arHH5WLCv=}F!zO>i30tmk!U?nqlI2-;*uYLl9h~~^amK{YagJ{aQC>|O_sXJ_{@XFp-nid`aIKdBjm*XX+@dM9kgqg0Fb@3r0f45 zg^Wh3$85N7fkWZ7lKBg^t9Z;TKC~8Bah4q2`}!v|Xvt#r93?Hvjx&jIXY`ofCB~-H z!TZxSuJ+BbS&vwGPMfXvx5wdl4Sl-m*^_v%fBOS`$Rvzc+5g~?#Bw_*9W_mM>x7W{ z$+>nRHoEgr_KG(w`Vh0V>^URF#9}d9z=Ni3xSxJkRlad+UrFWt#Xr*(3thqkHCdrl z8SP!ssg`bkmozs#3hi9Q*FE}Z#^&+T40QQU5v#;IHw|E-es>rk1D^dqAoHjBSZDx( zs`=&j0?;UjU+OjRqd1X6>=3b@yVOy8H&pvIKq~(FMYOjE>mNzRQcGI6#)|WFq#pd~ zbV?j(jX?d8&vxKqP9eSG)=z#T@JT~PGA$mcj-#0lDxx)V(V9XyM2j5;Xs-; zX8)wiJPrlrZQjAWxgM!jXievx2&pd_;O{At>ToDKo z?QF-pWw{P27k5x(UX)=J)aEAap1p2NKBcCx&|k`V&Z2MZjxwz@gD2jcZfhWUoO>J( zxW&4H3IMR_D+xT17x70$Ea$fM)3l;g6N&4XXEM_^yOA{xMXPo4xIiY<_5oQ@`v4$W z$QSX80=NS~7|8{5%9y50QS&nj7-ZtB2pEUxXKEOiH2U3F;?7 z3NMd}RY-%#y=Lt!dtcP>(TNK@Sw*ns1Oy2lL()WN6hX{!EGv`zZ|~Y*rRdH9DZ~CC zcQAb(@PdI8W1;|Q%U!PJ;bNud1tmtPF#t`8JKh3> z)PGKte?zSQd-oD>8GxOY-j*-wTSh?>Z2-sxJ?fjInzJO5rvQp&1xEHA0CPun#MoZ_ zhxluU7Qmeq9&#qXUomPsjiq4s_itS0`7_%#02&1XLt{y~dLxSSQsY6*>Q;U>zP$4R z!vKdQJBzJD&z6A zb(=0XxWtE{mO^mLcd=kf?IJL+p}!O7F0t_&(eQX!jm8%;ZW6NC?pvndt#58}0haF+ z?wEOc1#NN!qdi`5lTQS|OSw3=1zunPI>(>W42b95vnEEVehi30qcT=9G1y_-S-3a9 zw>I{t%>sIWuzdzQAfaylP+W-Cb{0)(Tc%Qo(85T0C5GOCM2MBgHngdx=o?v2Ze>YA z8>dId5==$^bcB||Lm3gdp(=%SCY;e=y>88>Y0qPb95aGpws zI+tSAq&Ul*7nZ-1g{Z%P`K@@3OMCGy2kA!uO#n&kZo>e?(ou4*{^D-&ztMPwFhEXM zji(G^9&P7xni(q1O^ZE96BJd*J+a(PB>bbDN@)k8As=eYsnhLZ#`Unn8E5dPDP4I7 z>DK18HzBMOL3|xx-#TBQ?{9-vQ0=Nk&C#34ip=)?!lAFknr?2)XRg!}Z;Y4z=&NmID?$9`RYx<{oa;(^LNb($U{MoAct8o)RuyR>H~wi_lwiG5Y`*<+x-rG^#K9YF#GrqF`6kDJR*PP<>!Y(C#Ag9qvyd3O+epWIs2aky$jD`LCrTI73 zi}#Akm;^rmbBAy!`5KU6mUNc9B$eee{-KH@)lGi87(^M86n!Uw12`hx4B!ZRyMfG1 z_|S-SDX_(^HgDrw@-G0U5P|%IN~5C3S;s!xi3vstTp*>T^NhA(lrnW|0%ufUAfEH^ z{GQ^ks_t6!wxzUmvBK))z(D7K;Rv^HCl%0V2ZP*}>uA8ufA%g8qy}5fqBJgbK>3ap z$&lb1R%f5y+h1-D6N(WLnMz*W=&=q326`(#de3fhrNXHyS6*h3wgE0x(RQRy4Y&ru zkC%x67>PoyKk`?5Wv!&&nv8pS{<1p@l<_a#?)j4U_g`mMjnvuf-QM)^uBJHM-&&fT z++U$lXu>W+o0#Z0GCkUd)MHT%`9b?PHsFNA#Vt&nO)mZ{$!$=^6LLA0W+jVeJGkE! zhYy!2U;VExuc<2MMdH{g@zO0nAXx5dpyQcsiUFS8VB=@(RP^CD{w`FOwZ`oeoT7Sa z7OC#UptUWs&rL$Y!d2;FzyA)2=s&{U=?JxRK>jjZyx90i^3eYTcsyiS&fqpKuk0VMT_vZ>FteGQc|lnGn}9(}!}~m}Z3>;1ChPt*F_!6*g;evk3?Tj*rPu z=|aQuLy)XQPG!kn95iMBjkKz3B*3I7s%*<``5Xf&Uy#Iq=SJjafunVQNbHK0R;pbu zf|nz>R|2FWB202#eCG#6Ea2z=U%M{J*MJ(i`%m77Snl$NNJh2R53#O};=t*c>6~IcoV(W7M739xT+07EwGMHWk0yFHKp6ljMEVSIO>kBwdxT>ijg1lJ%i6CC zIS2KQZ6mWi&I@Nm}ya6I$wR4lnQMHh7^aq!E90hiR2f&7~|yuu7cv%8Kl#MxV+g8Zu7imPbU-**=eY*|C6)jUIh5qg>G$Gbt!R-6tIHipO3*wHQ#4o56lWsb zp)#<5zPO{+($)&i%dU>`B$n^jQNEv3zO@8DaS6d#Lc9P1x0+#^zJ^(Eml8XE;Jjko z4yKFpur{SJ!@z0VCDQrn;?kmH+&!0-kE#JUB&u_gU@M}2-x#bmL~-;B#DfTxG50Bb znIck!v^kXle|{xY329h~G$Z*_G12_JYvEs6xj{Rku6YlXn%}BOtIG&l?}bmiID>_O_a; zZ_N^TQDB=h92qpL#Vq3R3`$x*FagTPe{5LBKtv$~i=zxM`+9dtAvN1HBUCM@aqU6! z_0Xs&k(74Fhx2f=-`(PJ2}CSpVq6^<9NdVA| zQHGOl1G0vG_lO z3iI2N{i(LGk$GmBTMlJvC5pj-JclpozDv3m4FdbYyHpINyu-9&MDoZ7lHu6j7GJ(1#(SW7*;2OxPjYN2`$) z0_ArNdeuX5FtW=krj#iLtqN%C#Ca#FS*lSp#F}$H5iR+!$h_y;>{{)s184Vs0qNu0 zMoz*r-7Q#5cWfTKPR0WSknI!zc;V^MeL(-64-azn(ON72ek48)G0IldLPg>Xq#MEw zP6J;Oq1Z%hKge$zEfzcfuaf%+O{O{>vlvq0)b~2aRMh9Eht#+Wm;DU;R?U=-@|svf zA28_oVi=u*_t1b8uCZi-_ogq{UD|nZw1m@wv!l?7(nZN{w4LXG9sYviP{k-ke{rK> zbg=Pia@3ZgCsh&-_kbpbOv8qMYQN${GBsf6{aM&lp!k)ksrlxB1k7~(UT}apjSZFiESP`9;;wW6mCP zK5&p^uNbkGCDy9u4U`Iu?*mAtMH1nw6U?nExL?qYQolyd@8eqt81jNs3xjmczX)v& z#ToP)Yj}$~NW0gpy~%=G`4wkq;gQsKgURDt;D6bK9T=EzRgQ|HVWYddvS3ggt@zFP zDDMgese-g?@2&$+Mw?^l^$Zv4ROSv0xJGksg|1Qc0XLz)-Qh2#^?=1^1sa#ScfRo* z;g-A5icSyRm6?lnEsz$wz6jLjW2^&4_G`&0r??!DM zyIMEeK4JQUwRq0Vpjs5$ZT~Q9B;ug@YUCy;5PtH+@}0V)Q4MEgZ*=r3F9qJo&>6>& z&kgM7k70gOD==4c<+9VBGCvH0njbQFg{|6W@vAByH7siKjVuVEnL%D7kgf??$(pD8 zgA3siC+TX^Kr6HfHX$wwe}TSY3PXWnLm9#zc^|c8y!(M1w0!-ik?VxQ4StxOY1gr2 zBxRl$Re+`AHf!X3R>Kx54Y8uUU=RJw5G3}Bi+r}IwtB8(}*}*%XPp$ zHbL=QF)+!#K5~%4AT@Lj8g)WAJYK+RVh;R)i}sKxc635_Rvi==EOU=`SChP!1q9rU zYDC?!@cP)*d+f0p)oup5ms8o-uNbD`nRf#m@oDvm4OK5Dliv)-dTAOJfb4 zg9A_@eW6R9TT0jzPP`Lc!z{(j>#+8k2yBfy-;`JCNYh_hz+9ZEKgp2%870fg@>bGo ziuD50cpBME-ZIAcwHXKTmI1_t9#R`qh=01Px|MO}nK>m^O20PmjX5ZYqM}ry!)!P8 zt}U<%xk(CM?EWaUE?P)%WEUaK`)j-Bd@*obWZykM=}N4iVGy;tU;$0vO6Zay|CvPOL{J9#bxu-KOP%t+Z z;i3^qV-9ERfg723R80P&KhMdVeYmkz*?V)|!pO0^oqVI7l&qedtWNdW_H{N=_A1a4 zfQtKRFGXt6nitB~rjJDqM*D%wV4ep}jrHaumgfh0!*5(D6NvcRKOJL1FO=EmAn}Az zwbsMhCsGH4?ACKYJ%wmd261oAr)Ob3`FR&9dx#s?z-rTEoEnrRvb_mEB^ABuFM9qt zG_F5;dO><}ih<-`mwXB7h&svE;{25K3o%)%j&t0H(8hI>dn5^o_M}uc*zp)sW+;xt z!upiqS-Q}9;UjMp(XVaaNi)a*oS@E&>s95FDtYsN$4{JBuver&tWY}=+>Rujecid{>9n0~ zQ|aUzG7cPdLgNa^t9&VR^OI^`gR?F5FoQu}~-TfYD zN07W24|1Z(R7$L%ey+*%;P6>v+D9!}opziS1vJh+AmG(9IE!Eefcj2ud9cQYrFjP> z#z+H<3K@nZ5d2*Hu9&BWjzB^<`0s{7?0~jFyn9kx4x6Psed|~CsPOOWHw;#q)lc* z_zY@XQN$tEbShSaDW&WgjrYgR@;mE{-9=W#qz_nr;aA)2gk$wp*wY5aA(vM}nSit8 zJhDM~DIx+&`T`7rK>9#+cJdb>*m{qKZ=FRVVd)k$R9607uaT_0csdq;X0txc%rmmU z3m`|!cobFR<%gTh+9ssml<3cMWi!ocuagBsiIDaADJ8VksA9E8KiaQc`0%Kg#TO$N zwd{dy_spZ?3^&iNx_Cs935x(lb^NB?)>zbS>&4}f!-1{3$K6L7xP+f7Li%FkwcI0Z z=Fv~U`6(^a#X61@=>XC51JKcFC$fLno*4~}%RLU|!R-{{`!s<|L=*+#-e?xkK}F$_ zyGS*Al7R?-wyo$E0KGwPA6$6=>)g{*c-HbE=BuHNT%5%?Emg^TPCGX31ggR`1ENv@E-cfz06Y3G&mrBnfrNeRa~17LC&?wz5AKKw)W8ej3hd8ztbFv38@v&6aYpL*owig+HE&C?kE zxZS77N!s4qTSSCa@92F>>s-1lce2@xybpPdJLoNM@EDb#dupWGhcDG;N7p)LuOMJI zOUu8z94Q)T6U|(Yc6cDAmeHXT#!j~=p|nLBOUj_#dGT>mjijr~Kggl#V#<@>V4Qt8LIWZ_N) zSR_G_f`8a&h7_m%$NoO^>L2>^VtjLLUHi~b(^qcf+3}W!>jQDir3YQN!qN&z@b1lDag$&e6zG?$DmbfMmkJ4unrU`rd~>?^ z%cnOt=UEDW*{ta(C{R;YgbLREvAq1s`mY~3ualzoS_M&*69MIL8zsn#7lCgBuzfZC;KpM6S1$isz6C(_34(!-fsrGwFFfY<6{c!Td^)r;mI*v6-N?p`BB zrnS9X0pQx#JeVJLnEIzmN`IfP-sc$ags<29jra3;Eg(varB+{XX2oIki=Uq%FfVvdkI>6=+h|CSPT30K_9oWfVl)@|hHj^pJN zOq3!+JU|)v5-YCTuRO~4|96v}p!nJ2(!U*K`)Z>jWJyPLMXq3-}ud#1wUi)8W+j5MTjhY~a_J|r|HTGDs z&fx=Jzm;UM_9I31OfkWJ0JzZ=ejNzvN0ju|VmXoH36=lU{Fl95e2Zrh)#kx5-J+$L z&qJH71O5-K>-iJLN^m_-=v($+2fQSB+M~Mz)%6Dd+i77nzLiiZW7bkZf>4`Oh#QII9Zsbxf^rlcqwV zy#tLKsZH_d$?9Vr^A;f6B=Qq43&~i!*Ih`bNO#X6ms_>T3Tuv6;XG^Du?4zsX}Pe< zuGk(+-&<_gNjx9>+#k%5?cYo#A5_=ZfM5MFSK*-uo{)K z>N;Gv5_5F@Q;dfJuSeKy|3LaL{(Q825m1eO`zIr8NR)s*Q*IeEiRe0Hc&wn$0D#hG02=ST(aLZgLq3Rk(M+T#em*BO^zRe*S`# zi_!@yaAWLbW2*H&|CG5vPD=xMA^i{Jng~d_cCRFT;&Q9ed>p=xwi3QD1uq;ruEvjV zFd#W(z>++bBV*JYivK*Fd}w_yYr({xk@BE$2lJm6$yH%Z2kc$S{o$Z zLO03v>a)*pk1EU$gN^Y+?Lf@s+FpV1kf78K3iF_iIGXJp)&AY`f zT%=R;emxBz$b$vzN%>Sz&b*k!(4OsoN*4}^Pc*m2Y!~!7&dKa}AM&}6@b}fqICwja z8ye-uTNAg3pbZw!k?>x~*$Uk+F9h(8(osoZIV>j=zoeKv8=tfrTDBW;X7w}vI!S~t zOPj~-;lP+hRd;K@NiV%e=LvljRV zBPXrG_NyMYW6EUQS~Sa^RL`tx#t1+8I_QO6NIStWz*{(^_}q4gF&DJEtbZQ#93y=P z<2gBfwJPp5X0rUOU0cJzpBlbj8A9f_j@(WyPT*h341`7?;>y2iSad)Ir@n>IwKVnK zwem+Q9rk6=qfL8FKr|U|u-sGohda8K7}2g=gy2Iw;N5h80me=-zR>r!35WYBon@>k z0gKVZ!~?iqP95_>KG5A784xVHuBhK9S)@_On&Y`}6V{9=ttsfcn414VY5!oG0FFj|vr2j?>eOWmIQZoz!Xb)F~RzJt{ zQcRZ3ww$i1#i`EPTPUt_xnZN(iutwQa%fba1%nVS}*MU^P6P7|~w7s^uXTTsK z$66ufV~ZMcov@bPKs|8mUh<2thsR~Qh*8{;_k|wYsrW1)HdL%1ETt^=45r>1MI7<+ z+h-OG?VmRfrkDiuz=+Q0FDZU%TEs$mAPvy1jn0`M82(FPg+uE z=qxwba;YJ!Hw<^nAubw*Z%f)Z1Qk-umT3h#>nEYfGgDju%t@Xx6?#cQKGoAwduU0 z?EZ1%K|qW*QWRzm)*omH54zp#UXv%KqmxB{IC!KT!Tt<;s5cb7Kc1B(r^!)0x+0N3 zKb@yy)mXXCqlI(4kB12(Ovwv4L1z&X;V>?D3eNpq5jxO&okds=Ys9Dg}Uou_#eJNDH) zsZxKcoYyu;P%B8qM07*ELNjlHKXfM`KH=0PlX=phQFWDLcCS&hVCYa-Z_xX#)kC=+ zwP2{r2p#G20#wZ{x3+c=L*w0xbbC>Fr|k5ubPDp1LHX{nAu3YXLGd~Qy}$1Z(T8jl z2yJD3*IT#AaQ6gWU3(S@gC`7)^1d!v^mrH3XJh9c8(`IVrt&TN!F_8wZ6teW5&Otk z`<`Wrd7#nCPD7UnHaWN57zM$8IDMRd+B$VF)6+A`6xn zNRx}3Q%H?nnMU=9gC<>TE`_$NCrJtTK3poS_1==vqr(`a87-2Yo!=@T zI|}hg&LdEbabV?9VR7dpZgJe=_gSRVq+P8wgF$-p`E4#()jQfrk9cMt@l@t` z*m>AZM9XU8UcD!{7=3L*61w#WeW~@-6Ly@1fHoGzJlnTR@9Rl5H&{J<+S*(bqi^rp zVP2v4WzABCe+hkJzWbqX`szC4Tjh>hwyh_PfIR!ucFN}vAL{T!Ms|N>Wm@0O{%xlD zhD7J@3RPR*w0LK%9JoDlwq!c$Jqa%TJyWK)JT;q?7gZ1}J=dD~!=ybhQCy zzjpW?Cq)o@yBn@e9v;9Wz9*uI3`&TxJutf);N>^pv#rtfdI1#Bsnb1jfKU9gmUcBh zD$UkIa`SCbZl)-COvt4uK1U@J;^v0g;gzDMxOdW1D$b?*r5aE7^nxju~`hqt}iYX8*vGL)kBU zQo{Xuqmh16{mn~0Ox&5FS37LooaT18Kd#R(2U6r5aZQ#6kL&#~H+=u$Y~FUJgxGpa znCE;G5fI4OZW?ZYK_`sgjH#n0d)3DjJf8j)j2-*B54vw)GGcI7=RN8v+OO=^nr$6I zTkP=43^?x5Fr7C0!e)ectq5rdaSzswUsEnfeJ0srTllpSMOQy09FfFK z3m-nw`pJ$fBqkSk3uS6@c?NZA`899kSAKahd zTc(mTZrSf&e~hF)`v&RZ-772I&W0ih5?g=%0N8kW~q>?BPw*hqk!N4!y&AuQ1R?_HWYf)Yc9;*aanksURpZr zKyx&H)vyWde%TpFps&|!d=}<#^=aMRQQ|*ecznhrhXa?*rpEU}3x7bqY|(n!>r6m8 z8twXDlz+n2$o3byn-OC0(c1m2wrhQ(kMHn@ZWy%+CYKh0dE~sP zkcmVVTpcM_vw_y@z=Q?}6f`sP9sIPHdU24spBMU;T;*#e%(r51igN|{eJk^ek3M_wor5$#)cf(Q zX|1Js)J-$LwHm7?wNoKgoL+@J@+_AK*f00s5Y-bEY0HHA&e^FP-(kg^4~GRd)eO$=X6w9dgYxLS>EC-2 z;=A;wk4z8!G+fWJ>?Yj3@inclddT^7W3Vw_IJcKyOHgX0;mSc32!9MU_UOKxfYGH>b{A={H+ z+6WINzBw)4C;PT{@q^&n&F-?CJ4BzPK8Scr&&W}RBuc4$V&(Bd|4diejPn*h!lLAW zDqY^3E!hw2YUY;FIfYV5!#w)Q z-=ntV^Ch>QC@$6OYl8TX2nV_^KinMLmy6}3M52M7hSJ7Mee>YTK!yj`La6zqVjvS$ zQNTj)wU@Q^OB!7aO5Vv+V{$Y|rddpwR&sP~e_(SUt=90(mnGO>|9$W=t3Ed>*`ZyY zzU^LTFqbfpS4L|gs@L3MhrRa)+O>hbO_LbB_TwMoaB2tug%LjTO$CgyI=Em0yXxpH z^I*H1Z;z=@?a}mSzk{n!E6lkBr}_JqR8F;_PD>|Nj>~1#NSBUBxlgD=9m_DF0Kkqw zX9S-8C_zTrbS?8k>=*KB&sm52bjLZw0JHM$(}{3^sCUXF3EA2aEKE8T8c>O}E$;$r zoj$jen1z{&-8{V1v()f~a814f*i;K&gnBEMG@!;CnUYcyl2U{?q7sniAR;qFx-`3;!*6O~ zMyX3{#F6(~5?dLNz1y*#hZ;;loQO88euYJPORC!JwCSnGnXce_37xwhZ&?oZeN)3e zKr-*M=2h!~KytG?C3+iO*NUr8#C+;AY?tZGhgSk#G$Y?Tj;^&MOBzS2L^C1z*F(_sPua{U1;TNunc7sU3P7<8qK=X2+4ECU z0=-<0e>q6*Xq;p1s*_^4DJjFYO8Nu8-u1*=Px?KPMDiI;WNW^ahCf}U8vBqGnnSO*6CF# zzVOmfN8mJ8AOotJ%NLw3kYfB*ma%g@P`UFF*#T(RN??a~r_5%dwVo|}+HBgaH9PXd ze2tMM$3Xw&`>8}qPhh#2#JuB4YQPOI1Xl?wO}}57QK^}ge7KYP1kMvnH9}aS!b2V? z`OoAIMK9mYVpb#sMGyB{jq8RsF#v=}dh{l2>?AC7Zg+XsiU669Z1h-%WKj8P!!*o7 zE?&c;@%y^{_M^zBsZK}vMQ|*wk8kk~tfE=lp+P<@tSKte0eGknQ%4BvpEfywT6g}p z9F5BzAxcpyP%Pra`q4qRvbbV{t`MzR2R4p8uj6p(?sNdLI>w=gxtF=fo_7fYT@&4$ z5ewmkKyB3tqqtl!kL>q|UTPwX=L!WEa!Sun_DyImQp(~2Z(P5S1GCm4B;I>h9+D?M z!+SR4cx}(Zr)1Hb@;xy4cumljuz3U%4?-55m4B7gZGvdmoW44q!l);rU*~9GH)|2+ zmsPiqaU!yi|HEsHsw=w*^;jE?Ed~x9jY{%|LRpc{5F*XdHWUBVPpBCu6vER3Il!U4Q!x2-a z%3G4>XG3$CoiXN?trn;Sp2F#=XjZ-cE$}s+yHRV$0Zjh*_F<#k@LX}~XwQ){3A0(G zz?k?P!qulT^&~ilC%q|6PQq!ZNSMg5c+-8b<7aw)}o@8wSMSq`S$`i*OXX@7}G$mA@&GxPn9=PtKqh} zl8{vsxIXGhhNcoME!uvebxmgc}WSW^Xya7 zL0sQ9bJ(-I1wDQhEVDI0GTZMEi(EIkX4%<*NL7f(JE9nAxwF|=^LAV8L)HxPz9vNB zG9*OsGhxgX&p22u8P;bWkXa9>HO^0Y{cC^asW^oDH0LBc~F3%EB%)%3=FP~X4mmDq% zMNsTfn%&Yb2ov=qOC1x-Au&wd00?XHDMOs{kYUI9%*RM;sP|JIC1u)TMCKmfZE7gp z;gbm5!FgD2o2bt#YZ+o{9n%?ji<`K!AI>$zG=^&QK0xcTP}Y1QSI&m6DSx)u}RC!vochYt=NS{9LMH*2zXwB{X z%2ZI|WpN#dsswJeFz`5_S?KKIoTREv-H^+!yWGxyI7IHrFN7Z{LPc1w}vi!JHjO1qi{p+EiOKxFWEsyc&%^ZWaWYAIJ|J z=7K%zAo~<@jBYAZVm&Z|r&d6W4aR=CA zyDV36aif#g#U5TvIuuGb_3@hI&R-i5rLuR_1pK?atk<8wx4wOaWdJ`iJ~7f^L9B`w zl<(3ylv+5-HMN%!SXIup8l6-W%YsQn!RhkE2lrnsU(2dPQ)KT*g@!h}7D}F!npn{< zn660c*(KMDy*5bZF6=NNE!A}oL?Sppp#a;HB$&UF6eMhsw^84KW~201MIj|6`hB$P zB?cw77ga|COFgGhoPCqb7alrPrf+lb@n!QXF#Q>a{d^GMTK>C{nD-TH?TQB-eoUBlOWI*o{b?w4=Q z>=3D={oq33YZ|Qi+qdsf3-(pt@o~@?_Npngta88i?ST)G7jM~z2iEPo{yz9n1Y`Te zSurtMo={PM-CtTqZJJ= zv6}r1Hktlz?Z};wwwpaXgpA|YVJf+TG5T1ZG!U$a*CX6%+M#W3>~Moumq$=uf);dm-d+wae+3+ZIW8ot;adQ8`hu^c z?y6;VjXOD;ED}#8fQ`UjxsGw~9I)IKlod7Xmmdvo3A4Ib7+NZ?dzn7HPF-;FSJhYG zHiX!bf4gRpkG-6(=qV zn{ooI*{_b(Bs!9}TXD>3bz9Fs)Bo1A9qD`U?#TL}EQY|4>V9fx&(;c6xI0`O^d#8H zdw*}Y-(AfmuMxO|<} zm|rv&(IFpY{H_+b&!XD=G(CvkQ1_)W$!csd4FdEKxRKlxI3e7Yr7lqvhDI?(Mss zxN}3fe5_N^keBD;f{*xogrKCf7_oJnP zo4mgn2LSQlgoQO6a{dO=%_%X90X62d^A+A>k)L;-U8!MhcdbqxO!#0q#?j2aJw=OE z|5}+nkq=7J*b;;o&{|hi)BpwD(T|J0v9d;=PQK_}Dm(j5yWc4qJ8A9=o*So_ zt;xttoGUA~#;ASFwB1*oH>f(6USK*DdGe5YPcZhdg&4(l2 zi2B5aaWQg^jjsP*0_di{T59}byKh;wBmU>&a5W{#2*|$A= zK-veVobw{jKjAKh@Vy2u%635OtNV}P#{;~S?xy+buIlZJ^ zsyoFc6W{H3$9V0^qKBvCNgT}$3eH!?-$Gu<>pyp6tqRI3FZ4gI@q>QscIlDcUEWg?#xVXFRJv8kbf*-SU_8?jDUp#org3I?AN``yN12ZZrJ*s;OFT`T zjy-j}p9vCYoAK#Rt9`eN{-6kqy z1yrX32aepU!5(tzd_bZ5VryO9!EQ~;lIEqG_=^)Y-h17Pda7oMpy*opALUJpz@3{{ zn;woG3e`4om0~)fe2vKj-wt4>)B%yfCC*M?CS74dX@Gc?ZP?{))WxG0^UvsHe*}v9 zHXi!MV){P7ZX4BYY0Wdp^R$6lj#n8}<4}moU?IwHONqZ+A?J4WcYo0z(@|vjSvEDN ztxVc$Xw57Gx1C~4`*HAGG9kXjs$+-U+hMX5W5FGtmJ)SFO*hPwD2r>=i+ckb)iGf+j+?i>bnaP8&n&Od@(NFG-lzTe&4xa=yI=uU?d_&DmZX zpZtPJ;`+gDR8kj5s*zUxY>7dm2DE+mSYErBC@Um+5M< zS$2q5VYz3_P8At(&FnFZ)jJTsVsZRPsjJcUD2TmCk8YIG-FPYk&c6XqdOI_s3;gtPoYLx#uJGa*qWn#nKH zw6mVwg(m6iV>nQc+IB8ezH!O_DVD<`LPf8RXBRt>T_MSPKS6uYmQZWm9&%=S0RVCN!LXz4CZbZ{W2=O4DbRZWuXZ=^W z;a%HtMniujSfpe0kq6ThYzU~}HCiiW3@E)svZj-D;AVQY9<-x<= zN5Mb~Tl2lUkbEKTkNha}sUHeM^!P6dpAOlcXqhkeT*{d;6eye@;Mp%y_)VgwA03`C zsZF()MswNhnn1|Ym)Yt@a!R+qR7@3)%W2(B*b$c(eD^^cZ(VLVw!%2)9-z0iyNkW8 z(W8dy8Fw9KMp+pPl0wec!Kk z2UTktPlj7NDsctIOE#(8HOiAwboD`9Y-uIyYf|=8EgCYc|30HfTRvWKC(SHz${W*t zX#GQY^3*I|7v>afBCDTDI-EyjA(_jPdpLK;u{am zRfIvJX)Uu$nr9C6))we26t`;mOO;@Un}drUZfxFJ%Iqsugp@LRcjGRh?3Tvh}hQ>M(=BAV| z%O=cRb-fFL(hV z=h2qBs|i}bH%$s#=h68p2hYCa zH;ar*GklQK6{6e^i0c-q90kg;ZI!kT@qYc9Df%p#L~Z+)e$Bj-^j%r$O8Z)Ub};ncQ4X28;4L=#ss5YO>vEvnz6eMPM8 z37VtIIu}@&;w=qJ%#-KMih*KufRmSLvpnhP)Com-_B53~WvN!l#?RulFk$W+*CBZ3 zYOz)OOgBf1+ezz;0||De6o;2?L>ot)$yOc0S{m@BjdMYe{PA(0bD1Jo=SGzNy0hRA zuY&mhYIz$6TM=!7L3!DWOgBP+f=^C%y*Ks>$2Ss}-pW(ccsgFV%HxJmy4R=A%8Yy) zEcE${B^qC+zs=Y;YCj?^mZG?AWRgu3@@8D#eN0Yl1S1berTQZ%QtvX4K6+()-51%j z)ve&|g&6z!i9Ac*@zJ+>f0KEhj@hU<48>ax1wZA23!i>$bejZEO{#kCFGO&^i_4L@ z;Br0rYO_sgK@m{Wx)ixX7_!?E)K@-3aodYcGa2V915c8D&;2lg+6{%fzcSB72F2(r zuS)xF>o#`g{iL@ibWjKqao-4laZ+I>lG3Ah?9>9Y{30c1!^AA4_QLAI^VwH|oQ5P{T1j-xF+`QNA0zMx0 z%Ptv;m+_qVp*8?81!nhDH=d{2+Ew@IAT-lwSX8FC*8_U07JpjH0IzE7qxwu&L zw<7c#x%|Im=o+0@N>`XqWZEukfE7?yK;u1xf75g<=orxj&|^sDPX^y2gF#HyoX0(B z7$eJOjjf%RPG!;0OXY~X<2v>El7YSqW*}(tO6kSCXhyRx%@MJCCJ_cCw3XpQp?wLT zuV zIrrq8e3DAUo!jwU;e(Dt)sfmk2I1cfhp*fyE|{hP>`E^I+3Giqg}*bAwtN|woANL4 zj;*8SS@MItxgj+a(ob!zcUP@w^o5M|sA_d(9b)a7;2&zqvp z+Pi)tpFI-Qd#3?f5{iHeEIZd6Ki`|LDA1MD3)b=v3G^n_v-PFarCtZL4!%5L^+8IJ z0vJC*io^7Za>_TX7Fp-e5UcEd`{)Wv0vedOU@#bQDown`518cBtID}9zeCB#z2W!Z zU?sQ~>wXfk7Bn@<@}Bbzv5Yy7TV%8A)ra%Y@XG)#GUEW3-pA)Qh`15DhqqOcI-`uT zjR4o=HN02008s2jxHgS`5dhk;r=C7<*~T}LaHf$Ak&#y^V78p52%z5eLT!!ZZxkfO zg?eER`9p_o!_uGt%>+k|ufi5QX4qIlD^Qy?6^T6BnV$GOBXKtQU|-?r!1_L(TXDj%w?}fbi@aai-r+4}F9^+@FWt?M>&*oZetOx3}Q&&YoRRJH?%&!UiILweI z015Ezh;RF%H?Fl^KQBRiTTootE(OJ|_`}5{7{B!sS-?!ncf^?$4W*5pG&(dg`RC#R zcL}nIDredmk!((EZz<<{vfdXFcf8zOt$x8KfxNdERMG<4U7xC3_0 zE+UuwE+dE7<;&eyq<@Ht`$pbrgw+q-bF#)7C@9tOGHtH^j%#cL>Ds zl-0;o#c`OE2wsbVK{ejQB@M=;t^L9#s0Eh0 z=x>C&Xbih1>>R001VDYZ&oGR$cW1=`o8$q`l&HEIH71*oNz={A-J-^+tvt;W4C zPF!&m$iO$-(-c%NYdpCxj6g$DSC>g6mSW-`#MPjm-0ZKb^jv+q{qv%0s^aHuT>sq4 zUhehzS9vA10^ux(TL#O0rOk}dB?mDk-cK3QR(_ToOkTTg#!>uo>dc-D7Dth;0%{+< z?P&Lufj)_l02{wEZ?I?IYM=rD_hm4Y8p^SQ>N&W*3=!15U~$jl|HEiGN-)2Y+j%nLp>I0<44CGrmBYIlcweO@vnI3nyejeluYz)LC>>PME#kKS?b{WZ&|7|a6rM25_52Y2t$5L z1t?_*ebV7LpkvoU-mw2f3gDkm{A;q8>O%sP8x2TJ+mT@r3}fQGy*55M|8OAz;{OH$ zz4GrvP6)PQrwwl)e)_Y{O|adA!BQ{428aH27eKEUcy?RvKQ%uG$8`awRSoOt{TJuMP#K_a3x{{Df{Pk)Yt(ynb3Uaezs zvoD;SYWZzP;;I<)2r-dT;p_wF>vLhQ6#W}!Dq&|#C8mVN=*P-o0g+!+Mmml_11Q*GNcP%YN=jLvae`|L z1uS4b37mW>FtnB^FBiPo(HH?5e%dvEHG35u-VyB|O>&zg|DMrG6ty_MUQ0F}_}Y`l zL0*8tPCqq=+ekE*752NHlZ;0+FbV@^`E%r6to6OTI>kZfu1bO$a7p9*o#`UY+AT1s zR;VE$>WqjhC$YxbP77Cv`^XJi(4zwS#00w?Nol)|i?(ORAdruF0G+iafIlD6X^yM0 zNKo9K_QbzHOYpszHz6?PhltD}MrDEfMyBbG+y7&wxirYd572B62mLVa;FV@e_R?mY!Ol?Q-6|?O+g8~j}iRS z#pjh*r<#)d+g*yuh9ocrL$3r$QDdYEqIthS zS9^DkU^f+AKqtCStHEEG=qxt~aQ2C=^#fhgu2O?6hc|6G1u>;bJ5%NSE-b@Y$%?xZ zR6E~-&3m!^ivEy2$atRL6zw}@2Ff?i?TW-zX9d%_oOw8)EBUuCR03cS_zW-}BYI?6 z{clR&&s0C_OCGEVon77lLb@)Z&4Wud)BTW__)pLD$FeEP$EUo%V_JdgA2Cf(;fAc@ zqcV0)4*hOuS^KDZSA*WAp8~p`JZTW<{#n4~KYg8IWj(om(8o`O#Jz}Q@@~OcI(avr zrUbpEboZ@h_Z{AMicWvn4bl9F>ip9@5s>N##J~7!;1+&)r?zHjXy#9HXVmWjT*AIO z1)N_{S%Re*bDl|$f*-TS*=jY2{Gr(9-aZloSS{osgc4X|f6E)f|3egd#};UVSQ!5| zpX@YMf*I6bHk$u#i}*kOB`C&b^N)1wKl|h!@Bw6Z!jlPfRAR#XLk&Qc0laKMZWd$n zf#vV5?`;rzFqfj|MMnO8yrFE({bI*zMGK}k(!B)Nr%!7x|C$6SQ*apw1qI|uTKdqR zvEVUol-cV*_-98!?8XeI;mV}b1PjFblGbuJfumP1F7p0!w%hH)7r;>g1^S>R-LgA( z#0{(^;t9rTpFY_D`GCym?|cAjlHPWS_(YHdlQ{#Y&5iVXoJd?EMERUju(D@UmZ33-U-h=(e zZlQ%?xVUZ@VX7rG(=_&?hnCjX+buKOjr3tOHsRGS@ip(|yAm`Fb)CoUTPC{pY7=&e zL?6WEmd;t`)---O5YN$&&_hx2M?89NY3h3Tb3giX^QS;rSImQ3K_Q{u)zzP$W2uh^ z+&*`3a&wCB^D5e!H;$SJDztn0h@evkDrew_N)+`x2>L!&XJ%%m^vNBuy|wM8p{Fy& z=iGzot6d7wg?WtKw9v^r6yS~PW5!d9qFG&y%Xg!?a%eXB z>UUG^dlma~6wP%xtFsLbw6q=p3e0Uvue@cwBchKC;*Q7<_OYo0gSk8Kkg8qmWrM|s zMJ&w3d}$zaquWS5;%^qCKHwh8BQ^E3GP%tY``kA;xDcl3kAM3}9aH6{)o@gR!LU#R zK@+112r)hY2PZQi(>XlfMT<8E`BoVeN|3mZ1bpZ#Q$jaMx?^>-x3A1fTwY#owB9~> z%*I$hXGD#gdxTVRWX!f3AvItEz{gaA*r8&_mY-!=K7&^9&6ix6M z4r^#;rr&FSe6r+~sqdYkC`g*FUf=vGj1jSYHNp|Ylq8*^2kn)JdQ8TAamB=wKYkFM zf^bx$iK#LQC*mzN8`=nY@f`RK0j9VT*a8b;Yf;0zKVQv7Un#qMp<5$W%; zJMrUCzEtAnL3W_MvW5?@UoEa}-{N5Ycm@%{_iecIl_U&L%)-{vBIzU{rAeTJ`0cuk zxMtJKnkhE61um8vM-_!A$5Trez&PCb0=jwG2rk+iJt`#Ny|r_vy^|7hu2A}j9+%c> z%ev)23A+-dTz>@zD!6v^Pw_eq(8a>7J2vQ$40ZX4lR6 zkY}n8^QzI0RlR?fSOJN?r;R@lm+VV~1lDevF}Cp%5_N3NsYnLg)fEOU#j@clqwHB?LE#V&2^qJ(Fc!B! zfG1eRiD(F-v9rbz=|fZ83fN}p=DD}GIWn2~ux5Dr;4r(+_Z)nXl=DhA0*X#ZoZk|n zF@ML4LZ{}WAq^#cMTX9&eS%}PLFvUCr>b7qw_ZD-u<_z4rr6P!n^GG{sFR#O8nttHq4<%R37oJV%MNpEae1LN|)9w;FILdr7OgOPnf8q;&c z-wJcX-TP#VT7RBJ-iPaN$Y$+h0Q zUr%enTCcBePh4AOPAt0+qvEaz|GQE1dhz=(qbAk$QiTU>lj-MjAL=t3A%Y*;KALGO zS4r3W{%RAo*pgglki52($2L_o_S-l8r2}TDeph}pmR+odmHu?u#Owp@YPqT#*h1>Z z?z~b};tsm!M%#Nz4f$a;^O=mjUwoI<8ZJmez^Bz(uJNIrAf)9^m2dW-K3sxHxU>Aa zl3Grhmgr5BAn~q1SKA*2uAk@O{G20mfBC@=cRTRur`YXcrbGk)+?V##YBo14Zx@=B zEG!#aOOid@uL{=Xw~T%-3n$jD25No`Y zdK0~p4jx|YP`;U!15JWe4gYF7V-$AyVXwlkdJUx0Pz+4^!4G7u)byaYa)G;R>R!@* zqjE+*J~sf32a8GnzKcP4cQ1JUFtk@m&sK_yC{{J2BxIC0HI!Y50SR>eJ{!d-#mL_| zuzzsgC}T#hJsAHDw~P*QZu+Ni--`R*f^-nQcV;;Qjkge7RAr;~ehe`%Y#@JwuQ9He zzwxjyGrCji7Uc#Hn{iP<_W(7$ldJwvLc^TenceHg-MVuIJWUcYak3~2kp%ydU#c#@ zN-$~bRwW!Bqf)a>hUqmzv&LRud0yW!DV^Wa1P-IqonU^9f1(d#wp+d{5u!KMk*9~8 zcZ>-S@7qZec-JWNPw)0N%#tg3m6hLwoINaTWo+BLj35ptd`KA)cO{CRMJ;{sg!-1z zs2JM!mK2D2`}M>C>1{@c?Db_{Ot;Dw`<%Svwpb|Etl5zF?9yNjF@o@e!rxA3?-S4Z z9B370TSBGey@Favz{ex&MMlBKB8VHt&2isqWpzI$N8v%p5kK&K0@0sl8Zxj_olDtR zDxoa)#Vsw-^VVBk<0b=E>lj(b1y@IS1vw`B(-DBPrQNVf-G&#LiFAT{Y&Q;gO@ryXG;2_T!Tfb0W;+%iZ`Cb!uh#HX4_PC3KhQx} z8!|QIMSVI=f5=+<;G-*NN9}woi@hEBzz?W~elv&ezVn_^$JnvjX~PfboJ~)eVPNp{ zs&39yhTnC+nM}&V6=nK|;?EEqZsq{pviNFTnzMs+7`LvI>~@qYTJJ^EKq9tNe}oH> zRw5y|nz~!tTed4?iRrI7IFzk}?WwXyGIdF-M0K zW%dPn2IkWt|0HXoYEH*+`#p_lV*(7kmF>*Vf}M4!JS-#1*l#sjIemKL0Jp~X*AwCE zti4>xO?{;`F(H-HP4{aX=n5FMI`u>0muE*&7tn z-(nI3sPWu;$6P^e8H+%Pq z?+iNd&}MAz33SNm9l?mbLQS?niHl^mWbiT9r|56uFO%-)iux7&&$p=_l_~Gm73S@| zR=VD;u&?)Q|7Q5!E$u3A;uxvHq&fR30NSa`JOK`{xWU_Ma2Kv4z_a9l0019bR!sYb zl7$y8&Kdi6HhAxB)YKksMvir8IE{@Q5x5AE^HG9Z4iHqXE~YpL$@FQGU8l>xK-$5zOz;ViEVp!WVX@1}qu8bHb6Tj>|~R0!cpW)=UO=QAvOHEM|R9Xu~5zI~Dnblbxk>gf|rq zFhhTS2|e}9KP_#NdG+Mk3PSt5pU`8L#u?B21v46gk)z|O5Y>gxcQ!Zc#rzf+78b4B zoBWp*dF0s|B_>iM_$TUx%HPWWPl(yoG@V`yPn$flxrO?F0I zZJmF>JHqqiqN!vOeUqcb&)t>-_wo6+%l1nA(IKBiA^wMWVga%Taq>bYKWb7xAONfO zu_=5hmRs*z)(Z1=94m;yv%gD-QZ=$qA zTgR0iQP%5M2XXz2>VWIq(4mvJp1QY}SPTl5)uJOMp#^*H%~@AfiRyyeU39UYJM_?W z>Lg}TUES678ch)aW_~~{hF;);29!*eEGyoqBJ?O(BkQM8i_7uWBVbX{ zswvUy_z9paLfpTB-!vY%WGE{va5rJCGrBC9ap^Jr?K>RP$;o}j&3&r*8u{8$ zl?II9zM>s6B8B}e_tlB-?^iD5zD%hp{*df(B58u!Wmf4`rnkF`qf>)H?+NwPcVx3r zf^+Dp=%aq!K@Mg9CWe<1^Sj+X!L@q2eAmL1`K;@jDa8ri%?W32;7CTO>CKJ*R82z@ zJY3wIJlvd#Ev~;^6oMtY{tR^s>fcC5`kX$CTIO`j%iv^<=Yb8P49jW3nAb62ozaF3 z6e+sP8SL$1K+}H!z*xrd2iEfE68SDh=L|elV1>Oia<_4j_F_c0F)wUpuvTvQ{zdDj zBozuErl)&i;1}aVK)KA!hVH7yfoDr|4Jw$CeqLOP?9k1+!ICfA!-QDp;qg6%JW6rS z?c>mJ!`RbCV?sfT5j%M3Cwv!a!K+pdt}s~ zO&YGApK7j*rI9dz>#)J~{7z{vULSYG9y&zkcCv2Lafv?1Mr}Ot8>I!t*>bntElIiC zr#o@-Ga9En&&Ih%o?go$?+J+*dY0(x@8^}4+8u%W3Ec0>61Vh9`eeEGzUeHlGFMb* z=Q`GUo*Br_N6)IM7#8mC&_y_YQQJD|SEIU=d4m1HWsTnFPP6GIbO`N%ccnvuW3n1W zqER}chMUwA`bxD#vzq`Bo;z_Grxl~2N1YjSQvDXo7}Bi#or#Qw-o;mMJ!A!IX{iE- zl}*ItkvfoRIXUToA1^fdOG;U<1;2XC4qwB{evX(s{p!^+pWwWG9fsi zfn&Z=JP*u`w4lndxf$gDRx)+=WK={S0{})-2>~fK??_E-zpWFmvR_46)3mzRXgmu6 z$~=%*4Qv?`fqs&$>&v*28 zxOz>ZzHO*OuA?l-q~%$|*rK2~-k&^*(9f*eE@G2#eWi*~l`d6hK8fNEVu6Z;3jsL+}H`dh}((@jiPH<4?o}7O)2LL)UOsG z*6!dcVv=47zl&p?A~1B?5C-aX0AWPxz_G~df)nRuJqCg=I z_-u@KX^JoI@pse~ScT9(oXMq#yh%J{%>iFVS zjB%G%V^R-coV%`)i_XxY7hM&O=W>V#X~z{>R%gr_3Y{Cb@O`qS^=e<5`ae;ns$(TZ z#i;P`j_3z}d;or^dNk34phD=-;9&Z*Q^Ir#pI-0;SX}6PnEV*su^RSgxdET~iVziB zoWg~URvM}7^(_^6X(m?{QSd|z6)7k9R!k=5H9qI?V$1qq8WY15`Bx6|Pa}&h(|V2z z<7)n`EJN)Fp}lOq(T5c~D0!RHc|458hGp-Qb5GrReX`BuHO}K?vcoPOJ}yS2<^+N} zl2GNmYJ+8~jM7t76*)Z)G2m4W&%y0gaNkR+CY&fNztj%~Fk-=EzmY*&a-wx{L8Sz| z;l_@fi+#go8@)VLMk6kTCA! zSUk#etM4shGj;b^R&*X=dF)!E>c@EKG|sGs7X!Gga(hq9>qm0 z`$`rE_weA0{rK{kn6o_Skcm~eg|v7;wM3Ut*^`lE`*lt)BuE8G%$zD|_z&#$ji^aJ zyZGh-bIwT|YJNgD>I*CYIbE2tpLq8qyzi+*O`3+yFHfz5`A^naL|z9gE5s&+MNT(>o10H$+KKCeCvGVsnz^Ylgx0BWV|%@6C{OK)0Tc-A;) z9Lx}ZCj`}}Ge7M<-n45QVjw84B?pTZ(93PQvVG5{rL*#cHtu-K{(O5e`>Z#sYsDtu zd(O&Mc5J>wZ!hWxJBUu+JmzUj69-Bwx4R<%Xj$0=xm_t2O|f*PMItEA-YNmqfj>>f zhLuwq%og%C1omRgIda#fSPl|Z_fkF@b&0eXth0RckT-b^2Z;K5y7IBs%N`*YY+y@7 zB%%SDxp_f9oCo#3?ennI=su!E4s`!)$;GZGr*;)R@l4gLd1 zC0C!Oz>4s&Mg?czP!Z#qZtG%G?{7EFbY>}@XfxW~wF%Ed(DwxaC>uW?fi~S+Ji@dsH{%_)o($e%H6u^cc zKL(;|!(LxrQA(ECo{Tc*pDNK24Lwn&Kc8v1kQil_bE3P3{NkE77xEft%+0gb)gFSW z3&r1eFWzQ4_RAJ}zWwxtxqB&`wD%$>!3N5)R8J2gxvC@m0`3OPxC&Dji#C2Try@U^ zP6m@VV~?l;WAc_7jN?D*V8IxeocCDxKx_=|v`Emfl+Pt>)C1R-IBnYgz|^pMWH@@Z z-P$7fR#M`XP+MBbK&W>5q?$Yuz|%*~+)byou8HlAMcyd?&^CNX@3bNKqK;$wr-%B+ksoZxtg1(tSP^@5q zqlGePk@s8HregRZC&KaVz@RW6A0I2LjrK(AgkZSPGU!%n7hiSShqp4Umal^Eh!K-x zjnP&KkEYyt;aIrM$UtTy8Z~FT1dWqNj#w7^>F(6jRBTTA_tBM+-yI5WeGl#lAF~S& z)1SI|`{^Ajwjs2N5q$SqBvz#e96ksZMcOh=_QFm^Ee-NvwYl*EFe)RJLRm1!AS7-r;}Ye^_LHN{b)t7-R)XYOsl-KAx0`vyM1Nxq$QK zH!fgQemZYUI{FWV^Rh7gM4FJ3S(*YKu=DWi&m>+icfzj6$gggrV!%2mIDMKLM~yv52=;4rs-7q3-$W)L z{_&$|c20#XRhLYYV5v@$@AEv}^R_o^7tr2p(5;)k?wM_H3seTr>o$_yu-kz27Uzdx z=W}E@;R7-$fN#*u^e2#45qN@whp3;?Rm`oMK3i2#9@+JrhXgz!#em%Unkh-G31ifx6EU8NVj6idZmFw@Q-# zqf3p58icA|=6jfYJL^cHBPO%~oxun3{lvDBu_mV?iTI+dk9F2IVu^Q&%)#@ppfLg- z+g3WTLixrW0exC>V2@IO1^|;$vR=tf3(iUhrcZ2O5*LNR%=#q_L`@*$R+#(}OpWR_ zswkDT;d3YrVg_8}cpzr`2o)H7#@UiRN=%%PGTAu$2aF)hyuocYFF z#L)+~(>H{0@#Bset*VU9lo+&H(qfJb9(Cnb6n<+^x!DQ*?Yo?&!utRpd=M&p{ygft za}wbg7x}4k@s@gGhKn7&^=3!JhfGS{6+@`eIZ*UK zDXc6}BqjmUWtWRKxfC+;WODN4j9b>Mm?tWB9mD1DEx~|kfENqK-mc$|Jz>i$WwVDL zJ`1k8QwEP1aakz!ZJ~R(-53*59iQByVD{)K22llGg$?h8zVrz{8?@48gH5KOhbcYA zs4?A0AD#^x|F6_nX@zAg(Xz_O@S|#JpR}0@lQ(QTt=w9V3MhZiQrDL|=v~LK&yt2& z95I`ntV*f0S(03g*k6_}n7Y_PJA?QLC>R(_z&5rH**jB2HwH4FuPZ7%%;>Kl3hVYE z|H0#1oc`|n1^i74A_g=H2}sa*)9ikFD<23x50Pwxq)tGX868l*(;@+z(YStVwI5je z^N$OD zj1Tc~3hLqV7Px19*=JEce?;~|EO-BBWAU^>G>ikbZ<0E{qS2HW6BL&I#l?q;-y@OL z_xftVAD8R_^6h5j`=vTcNeCu}{zBmBRzT&)Uq3};{$b@SBn8$6PtO;=*m+^2AO1+< zfzCI(9XpG?2buov>x;y-L9ff7{y&W6G%g9Zr({KTzNqO7c?-Oymeup(9C5f#HG$$}DMi|N;k&*kQ*Ds} zIY&K44M@kkYu^o6?_YlJLz*R0Z+WuZ#$J^gyft8J8gO@%bfrQjWC#HD1qF5l5Wj^o zbZmj|E4O1r3oNj!=Y58PJ1LIuI<@9XQg1z_MfUCBG9p>H>obTp?me{M>ae!ZZFQ1zAW4&R zdy6~n=csOg431U>vaz&P&?Xo-8}52*$ksn4-U6E&7kV7Kogdw1^`qqj3Xzg|20jS7 zy!OhPz7W29sAC(fayfIE!Lhgfg~vyoeYuCz{3fqWnPs%`_rvc6;@~9cI>>MF<@4~F z*Jq%0O%T5O28oX8i4UN$hRpF7u`-StsE=VOM^3pnPL8hb+=|F#3G)3S`yV@Ts3CsB(Z&Cb)%b(f4 z>c}Y!uPAktfvf{@NI?7JvafS4o8&of^v`cyx!z*P$+D_f)ZgDn&6Uf$S%GU*U@ z*en0g1usFjU6RxBy7zg5sP)JIP$fx$q;9r5UbpAdq}mkTa_@tt#=ln;-IL~ClS;1C zj?KlMpISZV+LE^5m7izM{!Unk_0xnUwz&wpPW>RC|3o9W z3OcH4Q$K;eMvUJO=26Y~qEkWBV4ltBV_wZ;kjKT*uI}4O!&AK*cr?t<8mYzbCm~J| z(7&}UoNLF(yI9vzc4{y4m@3RS&TY9$a93Sugs6Tqr2>aMAvk~LlU@_574?|!+ zG%2>Q?8d)OR;K9?oUr*ie=}iKe%9THb0sehCa1aY6=sSHyKOfheE{N07#6=t%4p?M zQxO2;!}Q7_608M&yaL|*=R30v)|$T@wTTf+{BxvqXA`T3#9I^ezZsqp|ScGZbvUL z-YhPAtR#HPKd||Eoq zIV?6=fcm_?S=#*Z2GJI(dY9p zV6nxne;&!z@rD>+Cac?l_`2*r8KCJ@cB|hN?UCF@IH4%RfbA5{&(G}PEZz}9t^$^h zY@_pMnGk{Eb+cw38>xh)A;r`B>%*hVS9eP=pQil=FC7?Z$;sTYG&(llD^qa?wkq4y z#bL+%F4tt=%e!~$8L6p{Nl468YPyx;3GIm}ZNb27fjKC_yfD7bLJ5CE-LCL}=u>OY zFaqVnpDV+2^ZV8=BuRJo&ndm`@i{z<;-(EL>wf2e>(fI0f6=l4R0nPQ;bCaVPSvSx zvUKo5g5XX8g=NOvSBwSgkP@;w=i&3qX`B1+{V$6JY1)j07VZWqv+%5Z15iQo07DFv zqCD`?UwSX&?VPyBjJ^5VmyUXOKScdxVSQ%d@@XI{Dk}C`du}?IZbXW>PJv+me;{sn zPdJqs?{^0BYIv<4gQy)op!Zir@L^bkm7xBVW&D0w`evE{{L=6b8uK&czo20HjJ_9| ztvKY;H`Y0LUSI$lFo4FEDV@Q)7bUx?AiQ+|jic^r`Q z)*1l5T3?Ay=D2@}ZCy)UeV&d89}xR7CtYt?Hnw|L&fm4gdyXBTLAqp+2AK-n8PC$VXt)BrxItu(sKpqOV{>zR%)qr9R zK0bNQjI%q6@zmATH{+%QdU9&hmrQIj!rvDps!jw#Mk|hL)Z@T?QU{UljGn5e61w%j z!SGd`-h2x!mdj9~pW2uTqCXGa>QeR4X2r zqBH*?m>IXLfp8cuzLdE09;#22)h6`5K)Dkx%XeJsj!v;sijb1@= z~*Bm}VA8y#7Pm8(TCv8bHvSu+be@G2Jz|cj6A9P zbw^ZQ9oxog7?tPX!;#h0kE)7S*{L~#wAsAIzZeBQK-0yh7Zrt1-~@9>b6$7w8u$`$ zD>qRH{#m(<+F$vnGMl~Y7d|JHf>TV&i#OGEZhngO1nh zP+s`W3}#F|VZv=bPaT;zo>tR@C-M3w0~P2X!qO(u(DF>UTRKhYz!0+8@KrFk&a(7O zUvb5hS^bi7eydP#wwS9fC%qBrxjC3BeL^5D^}`i2e}hlR$^L4%Dn+#olI`i8XS|S? zD@t6B9%LCIhNo9;S~{VP0M6`4d2%9%od1=iDYD3Mc{3QqClk}m^wQ2VV9LrHpa39p zoX5gTu+(FHemsLC>nQ||uA)6ZnzzbVzAR@3`*lI8RJS(rK8p%0V+jvG^Y|#p@^eZL zr=SmfqNuV=$^4g7GHwSyl8>@4HRVPgin_=074|@6Qd4OIoYdp41lvTc9-GV)n?A*6 zz&0ebWik_9$ckwaUe|1=c^k7-yLidx`S#;c9Fg7;`qHshURya+wsu-8v~QnboHaUT z=+i0)Se=sy=7Dt_?u*b&lwxhogo?FZpXLiw(*Q!?%_F8XO|3*PiV3f-{%%xEoiWSg zkd&PSv8V;Z!n7x7J$=_lh5RZ7~yFGy6kij86#=4mV@gecsZm8au@J zy-;1EN6VU?jM2DrGpq{7wrcBaks48^nQ+w3>f$w<8P5T1k~!KLv8C#zE!8v7AA8dl zdqRI)A)AuG(R}7U2(ZAV>oGKQ_ki$F>%m+`$Ndh6(Haha^uSh+Yaog?{lkH5{(c zj!?3x)8jJF;^eL23MCdy4CiMS9 zrL%O8BL6(+5x8GnU9GRLr`D?acaW`1Lyx<94@reXXrS5?;sIxFVBJrNzy<3FmU*TM z(WEELYlR31czhaDb9FDKR$&mw6DIsp7V^YAs$y88aOfCwHq7PfbthQL7i1U@v%j+a zhJ2gg3u#xl4>1Vcw)3j?8>wk2$ zsDN*6&L*p2(_;i*P-g6NIIl$+&GhumZXct@75qN!rN9s(I9)P;AEqV>q{-qkgMd>_1lQ>6=tl0dF*S_84#B z)?U=}3gwaj$gLjjpVy2R%2uxYRfVba=N{-;EZCT}_u4JNMlk!s`<*GQc=y;-FlzR0 zSZe&wsM%vb`|qfUj!i}?)hN{D1_1z&86)6kQ7Hd_fc>xN*}zJOkSR67)U1!Ng#fhI znBxDPLHNH@Du4gl-#(cZL>2J$E3RQE#47Ip1W~NG|Gyx*kraGkww#ul2$mV6P4W(t z@+>Cv$UvOeKE;uvHEe<(@J{HY7Q?FJ6agdUjFzT`4GK`eN5>aw`X^m_oiraU_Md5^ z_9VFW$_xLm_WprVblpcO@YKIJ&wmW={{ln*9bY$4vBCUmk=YIo$Mld?eqECf3Y~Qh zjXm0n0k4Nk#^)G-8jK-lZr-Zq<5~p*{M~&-iRW?SJadKjcIPnIe;VX2##Haqask>` zee76IvE`?bq!s(BXQ<%;oU<9@@u+RpR!&`h)6wUq{PW(v6m2)SAJw@g4^OvgBzRElDdD^n8T=QxX!TDr0xD_#*IfTM z+O5iOE0B9Mgz6lPWKhiM3hkX=^n-}}ovX+fX~@A8J}VrFvf$@>c41A%VpDTI!wSg+ zS>r%J(!DW%5-lp^NBa{zY7l{Qj_#ny{X0+Ln<(1r)dZq?pf@g_-6#>hwMgPfqs#)#9M@9*LCNq{m6^2`AeYlHLfs1|;R8 z3Pg<_k70%UFLeFOD0ilNGC~-<(ZV#Q`YSfzTcaSnk*q%CbEuY)w>|6ZJ1Pw^Vx()l zL;|ep(uVSFs5^wm-}_}&zg!wmw)Fr2^Y}nd!O~Y66iCwn~-p6+EocH$UiQpm1n&pA3KCC z9rLJrr_~~ltKreBvpllS*rp%@+W+YAh2l)kvBk`lv35b8^iZDi)%XKE_d8Z=`Q*Lj zntr@``lT)pBQL?y2;7ByD+drX+Bsl;iVRGYzSs5mwxoQ1gWwAwoI0n?c8FPi*E487 z3r8^C?JG*fB`;&^lsdDz5=HA|o#uni69buGejQuuUFA86f+Wj;t*D@9`}})!ld;+s zSWsFXfbxAYPm?3lKdd;g_=n@}^O2_j;EA5~!+AY?%&z>OH?}8nRjIh}777$I!*ud4 zgERm0Jk<3fGIqGULD68?!ra`-+39nmCKhn?yJ-590u&1RH4R^d=9fAlv%3Fw7<%eH zORrr0Oo)_Ve{&G!LG|%`Ijuk4(haRt8L$*HU}^30X^C%qRUP2B%#-zZ}R35xQKOws(8V$hJF&7Ioy4;O69 zn9HMVKQaLN+_bhM|C6U%#TQv@j|a@%^8U1L+)~{)ae|S_uEmpu=q>4Q=x06{+x(;W z{&P&Y(B6O(d(pq*#0E1xBUaTA2Y`1&{6-8^-a5WP{4tf@Z}5?Ir;nc1fbf-r!|SO0 zlLSkTy}Q(M;Ne>#1Tf8C$ot3!sxOi}MnH?Z)~K^wwVoPLqYNo|7G0*Kl8(%MJK|=X z%0(Uc;fV`lEa8V;!vq(^2t>f#2lwKGl_XWC`^|Qgh_9;njDU~ZgJ+b7m(l!-#)-6v zY9G-loOox7YQvA7)mAmR3zq5M<=lAI^kwJYWFLg1QNMZpHRVAh-G(C;BIZM~D|$KH zFAqah!x%L#;bGK(-Ss~c;6=z=mw&g3yy~r#%m*BOwv7rB{20xLU*`F}_!h3f^l`ox zIUX!Nz6-8z+(Z#i>1*V*Sw6J~N36Z=$O8aYbf{DkJGfhRP$h-&7v5C{$@wcTox{$) zgRJXkwK-ewhWpSmtNL}GP0W^EW@M)yrBHJI{I%H*s`U}Ls+;JpqMS|*f)bfTy!NX` ze)QCsju=ytQiJHy4G0qWUUfcJl>gI#0uCWeiDeQ85gnn(Ybt$ZYLY2)8yj;>a(wcl zbR!9MSq=b_CbG=SD_5?bS2vQcvgp{qeLk^bl);}aU|7~vN1(Tgf@30fVngCrqY;oM zUwtNsf*7X@CZ_u&BJbCzw;9?l|Jd`AFDmFzR><((IrZDTz83G-vZQqgV_+8QnKu*v zsk85vAx5xg7d1>yd($}w_C(4l68g$>6cjm&=j3BMDlFVqU)I2_ju5VSsHB@F*= zTB+edJ3cK$x3PhiSL|tKgw<_LUm*Qz#T?Bkb&Y(R$2WK{^e9BRVoVj7HMn|IxP z*QzHDeW!(Ql4dJmZ|~&cjI=gj|N;^sgDs4P*MF;afDrIVmp7DVbKWL z0*8Z5LnZRz_Y(BT@@<{JL2~%qd%km7zD}3f!J^U?GueVX3estu3A8!L#N^RsRqZEN zt#RkX-$g|rwp}Yt>s-|&axH{yol%F;q!ciJq!)cHV=qwA$S78PQ*=DAr!9}|;+RYz z?rHKJ48+VXQjB8e7p5R_eewF$58HCliKFUC7^|O?duFiILc4SqRmNq%eMG`RKFv|-Y5aYXH{R^?<^+42N z73+-J?iA;#e6j_TMyGxv&Sl;%Ne`wvS?BW}N~f+sX$1U`3JyH$SxZ8FSsDTA2%hKm z?1iRd&*fH(t04!mEWh7sho~Lz?;n9F+m;q7=Sc)SKw64twU`9lVQ- z1XI-wtxH`mwZH43@DQ)bH^#H@tSplLZ%wdhOI*-(*1^`J4;!D46Uf1C zcE51h5Dx55ySne4jbxs7@4=R&<^KSV-;X&YDD*UV(17-{0*Mo`#KSC%IOq8pwNXJl zpnirtTCHSrxMIn9uJUYeX7i=OIG#S=z~k2<7V9k#?1PU?brM{sjUFBZyi6|Cf<9># zB>YMmrB=+H5OFvXr`90fw8qL1 zf1YFrvHj%x>!_Mm!R2roh7OG7Z(fC;w_dsGe_ry$+SE7FLt|B&4D)NU^iMm~ctivp z=j5q><&~ynbeu3Smgeb}Y%qRVPzz>`hXhIn+vv07UPF+9?Fx}uF=cE&P|UTYvWRr? zZA#Z^Y2WFN|8%&aRdb~tT&~lEUZXy6IZ5FF<7-tfOijMRZV0bD2>Bq{qt|W*-2}!) zh+(+iF3U5(!!Y9b8N6Kd=;J~~JAhJm@>{fJAFX>_FF%UDkoz@l^8sTsGNH%DBg+2! zPn5&WLj$EGLzFn@yCXuGqYF<7H~bljk$zZ}bs;%DbzUSbV9sh+7uxXVED`zVg(tE@V2(Ld*b)+z~uipX=0?k z4=4CS(qo;A7J1WXx$>MzCrMG=P4CeS7%uq??~p|p9NnX!Mn6jAdr9U>MfUZ;kc4{} zoJIlmP{GsPk+In2{{CR%$_yCaSL=U((YLm&8wX}Tei`o{D@nnBKKiZ-S32txp zFLI}+K-z5$ZS7r{F8Ve|tLq_%%*{j6;u>KYR|%K0{EUCPccFVaK2co*h9i3gpSud8d#`Tku+T_tmTeu#TY%F zocE&&xRKvU~IDZ$o&l%xl^ z@kSZtpnwE5AF+MgJF4s$Bl~EZz@JneooZ(!MUSy=0>QQlUqYWs{V!1b#(9GKHnaHG zJEX(lfG2dO#jzJtxHsx3b2?akyTK#H_SX&;@;H-^vOHyd+#(E{Pu5K7D^5jS{wqju z9#v4Be_VAoancBMH}T(4qWA>i(swUNbd?B?L&!;6b(SB`;s<;&S=AouI?&o(q%`YJ z?LZUPOnCHc1bs}=oKGt_lt;1fAHH1(+`O~`3p}XT@SLd^i;?$)CjNfd`9}%#tk&=U z3mkhRem+Xk$m6=Fl{b&=9taAel`!FG7z0e31wHGvn8Pkckl5?3nSZsKK!;v~#bK*TV&uD^t0?xjEC~l=lqv&7V2n&V6%{rz}K_pI**|oN{up+Ag zeC>2~v|7CYrC~DYpzS<}0_t;l*9YfRB8Wn8D+T{&Gs+6-YK4ZA0|2~_mXly}e6;@} zA6~d5uZ05bb3?|dmexU0*mc_pH7*N^85TuR=1A;q>nS6H9v)CVD;9NZK1!KyeU$qYrYX->0ac2HJ!YDEV6zz&2I}YsAlW< zPRa=@mrIh@P!kKwjyJ95D?c~JdPwV474uV$JIldXyq62Dp7=Fw?KGHmJBig)KXu{o zAS`HUH!VA!EDRYuts(2lMRU2t7&GJ#d>bC2A6cG0PykLW*}GRdWO_9T<9QvCQ=;yp zpq2-u#Jpc)uF&t^z!+jYI~Uya;^->IwF|)Z&<4-il4piby}sxxHVC-P$Q^OkLbo<@ zXspE0@dWnNTiZn5pI0^$v`?RWiDD!}O7Y$lWVay{yn->19^vx!5~mWYnPOCK#m zulHD-H!rMbc`slH5q6q;#nDImJN7nEwNm`4VtW(FxeBDX^boF^XXVcgBRcpLjZ0Sf zb2Gg>hXZ>;6FV%*f~pi5@U?jIz&D*0HF(4d3I)rH>1qPXh|qJ#GWB!o6fI?q{!7!$ zxp(tCmwI|!zZ)d1&PT~xxj)ZC{7&C1LY@W=vp?Oyj0QVBIrDTT-tA4*{kHl<8<(yv ze1^n{b{y`<=$|rEV^~*fX&%cq{)&y*=>4QznIX2SmB7t`_Bjr;OV7z#>X^qy{THcg zQOPa?DHzwFhzOTf)+)JlomUly7-*S$d(Rt|mab-TZ{4PEZtGSb?F_cNbLXdD z6Z4Q0=UZ4S!mEM4ONqUCpa7cMNT4uTvnIC70kL?+Cu8vTwjXG<=v(< zx$gAXz#{s#8}E4AzugwuD{=4Lzr5C;#Q&Y`FJlz3t?_6_tnjWF@TyIB{c4&QD+Tg_ z!;54%n3^B=ALo0o7kIvK?V|dFbDg$vKOQ9d3f~_~!Ir}@si_f^AWoTux7Tt$uLhy; zWm-|jCacf-XwO$_9svTItM<0G%8w7J>hh-G2FVTH`NRAO727K~CisW|yAM5^QspXX z@(o6R-`jP$(giv%Cp{B!7-jhi=uH2=(!MjSsjb^O6akf{NEZ+R5s}^$q$41`cL+rU zqzO`_2Bk|ADUsepdI#wVf)pvzdkwvJ2ql5LgXg^Ox%a-`kMFtp#j~G~ovgLjnsbdg z#+WY=0LB@SB(hiSI`Bg6%R~LW=nE)b>N>vr+ty$Eo&yVR{-@TcFFGUsXRHG5!?=CM z0urV-WyGltzEFV{;gyI!aUe6tkwYf76e+l3(sG}?@#Yh9Xqt%~_4Aw|v)@Wi%D;Y* zu?S=)R3yhCN3w_Xcib$hBxpKSlf~3`-|}^p+YQEjh{;hgYLk5je3pDnsG-@-=y+&ogQc7 zNhxt)%EHY2)~1&DU`UGy+!w2fx{FOT5AI!_dX8?iE)!}gR~Gm20&q2KBF}0Pou_O* z-M^gpy9B3ei9kI)d@RJbL-~sMhNRM28?y$-3Xsb7glfx7P~(z?x=b2QqrdNgi|vmW zA7}Yf21qln@q|13*o8F-l)mNpacxvzv4PI&vGKEv`9->w=bqI!sqc)7w>^7+7vz*& z=CVf!Q}9wLdH$FJXG+Kxeio#ZXh4q{Y-`1qp8S~7ov&H1e{K4Z{kZ)=jo;`=k3gR0 z8w8%XPID|IN$4g_)b%s1cY0%rZ3091XR(v##Kh)Xd-!b%&Do=4rprEsHQoTPVNK@l z)v%M^7PN>;(&!)So9a+4ERAJ@Qmp;payF%Ezg|pJrQ~a4=h?v>Nk7=;Y?!>#&CgpT z9q0Rj-LF8VnS1}6EFa#ZM+dhQb86lgws|VMeH*g&IEe|>(U!Ou^5RLWD0JmUy&$r6>1P zf#oDi{0#{EQ2qZUP+QhXTqPb&#c;$w+Z2oT4-A-JEnMC|>|6~DxB7n4iL=NSU@9NPiZ)CvOj)D z)zxurtnW|W!B$Yr%T(4=IM^3zOg(=LQ8>Lp`yO&akTF9WDv86WA6;?3VwWCAnjt#_ zelxJJdX}B<%;xs}azn&C3Vb|&A)FPYbP*8Sh;*X}xNx9qFDJ;j$U4{V`a^B73XOT| zo#8Br9T8E-6@l9V`;iWLy{QrST@kGwyb+H}K_}1Ia?%?MKP_LxdH!B;#7y~KfYLV+ z($Do4F3$|VJSmenCvR}&=Y7$C&UfD>>FrsatJU=9rcAP1Px#xl5?DXZ#~1n+J}$fw z@aaX+aW%&%9x0`J_$5cZ_)wps5o_@}W$Et{6Bq}h3w&Q_uj~mpUjeezDRPi-x?@zE zdC=B}Mn&1Ah`A0QFYKtS<)L6s?I%81e)|dgd3Dd$Q`SX`VTBsN$aTtMZ+Z4{_^+?U zx<_pk;Y+9KBE1QRfuo$3mewZ8Flxv>goy}5peMCTj;}8sHaE8 zse+NC_{KD$^(HnW^(maT3)M*?&3*&L_z-*V-GFlX)OUTkWj7J08{v(89{KtBDf>7e zly{bna4RKkFR9dn+w}Zji@&UPxT+R*e$pRc#0^pro7=*?QV5+UDAg~rsrg3IOit?6 z6t4gJm@Hu17LE)%Tq3Jp#D(^Dztmr4m^~Vnhs2AXUxZW7%*<58`ZIbjM7B!uLd8&r z&4>K3Dj}Ckmf>TZ7-^h{Q86|VPZ|o&gN4*H4nu;RDCg;e*r0EDT{uqdv%@DJM@2cM zPb=)^mf;si7WNSI#w+PHHX>z3@H=5|K>nWK*upUAxvtrl3(?Qbr|&+wTR$qU+Y1$% z(i6A9Lk@(pBrYY0jT^f)YOzC$BO;1SclK>5q2T&(*ZSE7`!~MX(xR|vOk_*e4lb#QB6N0^MI~FO{5BCvUj=VJ<3Qt39|3AU%cJ%N4c*5$UDqv zbHV~ithpwfFRq=SgGqU#)6eAO~)4&za%_ zO7zPM7TXpE=;?sQQt1;6E%q}wWHZmc;o;N!F{&{hnGYR)x%lw{Ws4)@@1*53%Sgks znfIApic&VO9-JsgkK}*10zN@{?&U%@m#8NVAjE;j$2;@1OlD7c0h1E0^S*@KIz!r%jL#wc&x9UfX?dJ64w&|Cin#!tGksEyNr+ z;1kw}vgi~%XOM@WyS{et!&b16>f?t~u=I1%V64#FE*-_tM}|rYC0$bjT*Wk8M`dd< z)6**q4dptg$(FsTbJblc*75icke|pX1x_T{tlJ1Bd=?^ef)(Nf3cKNusIX6kAqnDe zuV;;!A7*#PkybLVZfS17??~(bB3P+3p4wD_bK=*Q(mS3n)DM*Hj6pRQz68zTKr8#Y zL%JmkF1O9z>!`bakO38_6CvwgVWR*-FJHYdfOD+6QON^9huM&Y(d>^j`bgg{k8ah@ z;((T|Wjd-+>3$lS=ewi|Tfl97V&KEMcx7mIi>gElew-3uge_S+OA%O%Uw;ZPAIkSt z;{iv>La~RMDizOSD!l=Y6$M{Rv*@9_xA$nxpmGHl+7`To+@GUh6)^?9;`!}w1GYzLek6KG*>2x+XMw>Jci=mE zK5#{Lap33gA72i2gI}IoKy}U-?Bgg6inj83@Dqh^8!pKn>>wOV-teEG_rn;%IfuX& z*!zD#mAKr>ENxNZtc@Swsy3}+!`pf;_sQTax1_!e*xOwBH+0n&bOO=4Ga+7b56<<4 zyFXgB^2McJkfcVetA+IQRuK?@p#_RVZ7O?3__v&=yo0d(PIZfamRZGE$sWFgvPzX- ze6B*w{b0An(pxeF7CAwbNZSrH4uBBamo(#1Qu`N*Q*PrK($7UBY(R6FL5FG`XY%93 zs`5HA`=8l6x1%a{;NUyfDvHv-D{4Wc`&*Av+GQj8?|XOdbwOv%sQRAhVO#Drrno*s zJGY7`RqtC5T>=j}3lzYSgVd#%nT&hgqs_6u$6oiz_4de>^%m`58NCl^}1m@|fu z(+ilD1UKbxkR~7^CnhG&Z|C6P_y$9FL^J&n4nBgmq`lhsu)nvfzf}JCG*CYsW`76R z@Iy_ZfG+H;)8{nGVvF0nuPH+Rho=NV0=|EcZ_YStW+?f{(+^5gA=Y*;XlEan3{P|K zrq$11Aap#C(c)5`oIp}^p9rXGe=6+I*Eb!Xg?lp+H3a3D&g!11i$Js7I$8ZsYQr{l zYR1+eMB*#~eV+n2h+6c`5W8zZm~?D)mlczhW2mDUB;lW`D)2YCoj9BUXh>;H^nC!$ zZ6DE2LTF_g-M9p~ODdFXG~P@Mq>b<$CTNPwcoQ0L)8P$a;>VvOyD~ zXLb4#bJ*S4g~SQ|aU&5wH~yd^50EGUjh3>t=0O4+fPt5aq5BoT{pZOz%(kwl%$Dcp zp#1yL4D(y@gTOsn@#Hj@lwGeaJa(%91SyYdXb z*VI@{DYnmhpEt~CC4eUwUazt^z+;29;Vv(70@iN~fqS9FMjlpTe3vQgx-K);Hb=kW zj&BQo1Ta_C08YK5EjjgGDeO@p#MiQSao28!8#*^nVqRXa%9Y;sXKia0DD1CU!H6JA z3!IF=t>^D0Ow#*$n$1I?DGL|xx5ihK&UOn!c4W0eq*V*TGu zeZTM<8XK*;^&p;z9jS9y$t_F8@jkoqw;29N|O8ShOa+Ja=RJlB->hw5L&{Y!ryn7K%>G_QS?Ju2Zp6o{Yk zLGX${WXAzaNTMYOvSD2mQX`WIMlRa_F~F`t2%O*K*f!O;IF zZ2Db=UUuCiTu{8I7dFWB?4|qo^`*>MHBV!_7_TvW_qYRpjlPC9EZ_l2rGKP7@|717 zx#?+h+4FI@qv5cw+xTF90&-5*b`%QG6VsZ~%?5^ATN>`y1bTnQ@`LYw4SXg0vKffQ zVJgpZ8l`XEij zI`$7vP-qnGkItNU+{^c{#!O8Fzmc?56fSzxoN8^Jl3lF3a4Gc)g0jhDAq>DhIhD1a zyA3nEdliu4Ox&I44u5SMZGHL^#eCiWj$-xZfftdpuxgpYj_d9;Rv=%S1?z_D>0w^hZ}7{A3wT-{Nm_t zKsX2GqhPNT+feMf^8rP@FXTtS<$`ygy|XNmt&r2-zli`2G3fI0!rhe`Zy>(O&p<*a zIYnG@b8~ozKt@BK=x^vVu!+zS{Q4+w;{1FZ?R1PrhGCaw11^eZgoEem1d(KS6Spg= zRLQt!XE#wXF)<7C^M^2IXjev#;?b_xl$TLG`nu572YZ%E1FD!;Jr7 zb0-N-$4a;X;6wSlKFD`xi#U8t72M_$1Vo}ZclqZg$b4jXtYua`B<|C6pDp4w%_rXg zsQcLdL1GrWudbUq34y3?E2X_HNjaGmwsF)_hfgqkj)@@>72rpgi@Pe9 zRiDa^fEg*WKPLY?cARfFc;{_;N+!@#m-1bIgyZ^SVbZ5#NLSPG$UG7Hu?_kX*Q4(| zMd2Q`u9b;LCdywQpGx#+vuxaB@A|B274Yd_49ZBWn`EpMt9D9iVsVy*-gJV;Nc*ox zc=*CUaHXd2G>T_o$aCty>sa1DXTAS8E93XFr(Jay%5)YXdLW~I7Z3^%$vx;z1yZX@ zp*gt1bg+f}YNl3n%-GouA*cz?`YT4?$p|&pOVDpat0t=KABp+)un&urOMp0L_|oT* z$2kSW1{n8s@Zyf_Q3=y;+uvl<03(dNeebmLLgX(dovob9f@5uxM`bwrh~6nQ{~6w~ z?OdNXu%W?pIzbTHHDo7n2Sqi}o&eXx>S*5_249$MvC6M$6XDu!c@Lmi*x=P*UZhol z3L+WlaN>9xO}Nv*2Yw4j)k`74K+rH0d)B}Ets zN=mr4z3A!jXqQX%O*`6B62}DI7%b5D7cQs%{U1{YXglFv9{h%nH{HL#h6P$O)WXJ> z*K_N^#rpFIIDIW=CTMqesHRI!E)R)5h?pSK^k(B|`7IC-L>c2BG|?@3F8EwXfy1mS z-&ZcgS~)XJC#3JdJ^h(SP^0Y!x;N@lrKm2^`P)(`urn=4sCyw-^^@BDT z*`0Ob9gqQ~Rgbj7e^O8DWs=-b_YyC+j z$E1&AJX2L8VyO3Mk8SfQ9@qGuQ+o@L-;obDvL~1%q75t4(ms`Y86`-&z1K~Y)j41v zem5=yMHJh^F3)2xM^w&zH`!??!I^?Kc*ln3+-ZF*@2m6?2~)0I2VDBJ1JHnh?ryPs zY=5LBZ{7NGv!$<^4j#&whUq;iw_toR5W{JfE*I{A6%?^R*t50HOLRpsf6nAj(lF_R zx$)oW114Q~9r3pNWB6uX)Iu~`Gip6D5*rjmtqtYHqcRrbh?{%-clkrJKb;AJ5dP&xhRc~=cjzJW$uf)lx|t#DV-%g zr_NdzTSQJedslYZ(j6B@l=qu!O#?Ql3(pCNjKyIarq6!F>{R*v!U(_c$`i~&WUfmc zBGeR;Z1cTe>IfmCYfn3h3w+t3CeR;?l7)0q<@aA*ptko8Ki;?)8rUQz)$&EM3}Pu@ z9Y)%uGaph9Dtw)cPv2Uht)l&Qk_VCF3s)W%6d4toT;`nbD%lKr$8W%!U!~G}52t6F z5M-*qp<}V+!EdnNAy%jDy2BiCc-GEO)iG=8K}egemR&-d-6uTDZ!&-wZNve*L+<`l zZdN+dox8G8gk*qXPp$jt(5-uaHIC`-mCJ{1o+?Kluk|PLguOS!`CzM`2_%(+gHn7E z?yA;Mcodg9(~Hg!M$#Fz!ZH`qVadyXBBtZ_8~et z*)G0yvkK)6{D~h}I88KXngX%#i+!r>=$WNx`MIQTs&3GfCM;k>WW*#XN~Z;42WuOp z-g_ZbSmR6u8%6soD|@Q;Ryo{#)@cvfc$ZO&dU>A(8=%wQ>aP%gqG)AJc;%084%lqdt@t?Rn;tq*O0 z{M`9W{p;^YI9p8H))%AGF!q-4*F_*`Y4H6e#bbo$_QYZ-;>WupqAYqr_px4WajP%t zMdVS>gj^oFBRg2y0_w%{_9@j;^BhByubfi8v3FH7>a z+GaJJyKT%Z41F|kkcVKoyRLsHJod9(0tw`|#FkE$ zx^{LeNKU%1Q?aS4k3tVGE|KTpj~k`C%pHPZ5h-x=Y*s{!-63 z#6d*Jx9-+vy)$aC9K@Ypct>uFlgl=#et}Z$_it-~sevYLyn|6g`r&$q%Z&_omx6*e z#>%^;WR2s3?)XAp=7&opcAlFP=`bf+>@Z@zj&Lo2+-d#~Zk{N=?cV~Um(u>Ui_TAd z1FY5F46@hcrPi2|v!X)Sh2q>1YrB-6otAN>aDM!d6!k01&Tbv5W_arS@=91_v~-*C zZ7hH^+aQ|c4%FlFp8i*-SF(!+|-!0!^sMT zHc7GKrMud(Vd=`#9tYz9**iTk5KwXG-{l2v>rz7XU_%3g#3T~ABVOpta^G8z)QjEr zWd?C?&-6zR5(A_3#@d_tOkbEkF4tPlENo9BDdZF_tvhV`K>k>SpTyixRVhIBAtBA< zAA<}I$u8VulK1sfCknhZph&@B`YtkZIy`E~yRGy0`H7;0cII=5fj*`inRDF-r3vqM zg4(M=ng{1Z<1obY=h7$lV2~{mYElY1fdCS$3`PkdjL|3H4*G`=sJ0*!+~zh7RV(pO zjh|^aMmOz@s&xe*_GLJT9-%6|I!KFlgplFr-CJFkw2KT&Jl;H+7Tq51F4g-Y${)@a z3~WZG{akw!Vh+^o3i_-Q`cPJ{*socNUQDra+?|CV+nDufgwv3&tJdK#QUms(8Beb? z4Cxac<>lc5U5q7pJb*p!cW&S2#PIUo7QD^vPaRU2dq39WWM1N9HFYAl8+am_sqZIw zWK(_4?5X0HWM_9pwy z@!-nV!K=9_a~q*t2brSQ@Na6iAbq(1YdLjOfC1U%+b54#BwV!tRA-hKfB z<-!}xHd_Bq5r#AXyNd-F!p-JQ8lH$dvaNf1=&{0r9-zxC`KaTJg64Vdb1N6sYYhuhFLktdJw zIkPU&yIsIY&dm8n#NyXDgkhUaKa3DU2(ix9JFq|N9wSj#MD!9uA|T5FXE~5V$V0;i z=BBemHTL|(U&pxH3tcC!3huEsgI(F;Uk!J6pJ(I>w zKgX@0-QHT;t&ifkMNfK+!$psKVovR!;7n05 z@BD^k!d}0YCHK;b(wv*XpS#hhqb2g@xa#IewpNj+)+e+FJ8(@-k9uI*CoP2T6SS9y`X@#2s5@xO_7W@--4~{}M^) z&htR2XGdE|^9cw0$5<5{?xA;IyH_57a6n6Gx_7b_bxVZ~K%l9GlkZsM#nBR~7fV51 z7N&4|#ncAmshcm{0|IYgd{OtIG0(2D26;N`h|Qh9Fg+wq?v%kT)$xU4b_?-}b!uY# z!v{>&zR+Wzf$L&y?A|qyew#wh@UUk)ziDirJAcsq^$TB`SoIjw+`Z9H?9rucpCQjI z#5e1)yjcAQnIDc$ie27qKoaFjsZgX(Sud-^m`(_lN|>Heovh-(MlDncy$`XxE7j|* zzS258Y@sKszYRORGqN4Lb02^8;GR;?^6vMddXtA}65p_L+GdA=pI)pE5=e5ks(L<9 z`f)&+=$LZi6HacM9vacLqbM8hPw*f6RBj~-aTl^lgl%h(7s1!*tO*$3e&POxsOmi} z!A83FC(SnT5TWfqpY7YRfdiL?E}SE<96%95X7g`B)0}PnvrVcrXmr0QQMO~l^`)3V zL7eWITn7WvCl3c~Zp#Zc%h4<_Fi>)TI#6Kduh>;uY>GfKD;X--2~wi^UWi@F-!O2h z<0hGK81Yg~A$+6B?o_=^80E9pb{R71Ffy7tmQQz=J3ND>mT08}{!BJPGbhcC^{}8V zfS5nv6Gbs&#>&oJvs&fEx+h01-3Axq2_JmI8XuU^A-VEi|Mt*w(Ss}hN~uX*anUQ zVY-n>BZHI|aZ_kUt;^yYv`N=NOS2mk!!E#%k`y?#rs!Ut>2jBHlIdoZGW7{`QgrjL6GxXml@%R9tS6NQE0)|X=&*12M%VcghP~sH zwQYVvJg30?^-;SLK!C-)s2K%FCo8Z?tT6HS)NE+pBx!uJ*Mg4K=fR$K@9VnK@1N)H z6kW4i^KO^Igo0jf+}DCT(0I0b+K3R{_1Q)|aU$V*>+W4BG?UXR>sgs9^`} zrSYN4RBMNZHud7@zKvM}Zpj-T7jtu;`$JGbDVkeC7wpcOgnkv?c}6k9D&=$CAFnHb zuM6-#&`-{w;|FxhP z37)ivMv>nl1X^C`eYaST#6Ko%DIXc0Y^$7qX$82{VH&I`wsQ-P&N^QKRKlf#fHPhP zfOXYO3iC{Vr!_3SK?G8e9^~YD%ENcxw&s#FN6YE*O@Yx-OCtFsQu@H3nuwO-;#RJ~ zgG!;qafKh(*o(%R-;=J>19lN=Qqph#a6R6h|1C|Yf}0NKhrg&bt2xA72M2@zC7STz zhFp?$fkcg2_oy6IF>b#5nzbx0mrF!K&1qgG-Se6FNgHL;oCoiQKD_dbQ^d_+M3*MF&YEA$opQnt%Zd}Cb_$@?$6P@@9gs% z29kD@(mlTn(Obhyq48lRx5!$Sgw)ce4S3U6sGkFH%S?iJnwOEw{lN}e%wuXxSHvJ} zRJ@WZ=InFu9*u8dKuyYFo3v)Hc}payeq>3(KVL=E{>lf!MlPzSEN!n2W-kQCEf@{@k3z>jl1jZD&zX3w}2gyi~oGm5-=n{fq^Ys^vgg_BCmLGPBG7J`O;RllKqR;PZWs8?|T zC>HPD2iTfaYiFCwM?ecWiZ<1;N`5SS{zIa*JK~^X@kvL8+2fNh!=nBD0%UWnfEsjs zxSxBSr`S=dpk5N#9z~#uqy;>eTg?IzZ{G*br&&A?SeFv?lr!J62R}M*ZR6*&eb07j77w3&1sw z;%8rL`8S}&UAsy=l#Kb#FAglnBiSIHrX_U&_4|~%LDa(rSe#}g&pV-;Edrm}p4z?R zHnDBV4ep^kemmSt{YuNWm8qAAu5(({IkZ0}iZLH`Ep{ckm&zAMpKVNDHe7>2te7GN#E2Vh1hZVmebX2hgR1mk_hcm5*`iVNKgJ)*JRC! zi15BB93HD?&fhxT11N;pAUz)e9n>OIA57IB#o%m)wCOYSW#3CJ>w&z^_0ldtb)MZ3 zJ?=e^nCd@TE4oM3_HDgIZbro5+inHxPz5;vP5l;nwyQ}jVhu!&-yj@8?OHnD&h4O) zWFB`b7wbjAe*sy!jzoD+Ub}nfPfpRn{hZxo3xGT+i02D@WvfbYy+1<$ zV221O{;9zi9;D8d*AWo80?uUuz;4MdBaIC6$%QfieD%9{L!lxhcE5i(fpD-mD?Z!*b-sr zy3^^Hy!fhn+iN>(2Rmy{uf5WpL_%u7gG;BlrXi;gwx)uPs^{VY$1%3lUHR+WnI0$m zA6?qU5^3d3-4(vXnt{HWv3XtGdaLn0(n}cCct;1i33PVluz)nm83E0-t}287qqf%n z0#fmAQ~6md@LPx|0Y=hQADB_*+&tm6t+nE*)$KK(a_0aeVl>!V3D0X3mvR&r3I_X} zSk}De0D0^a8akZ+EV1*YeT>YxQxhVYa6#67ei@lVh2fbrkNMC5LMEOku5V3ZVxMNs z@RrSupJfNl0Bu|Ta3h}pKH2&0K_eNfB(ZL2PZ|_Bl?YjRvQc)aU24l|<;*knsy2QaLv+=wy^pDqj~y+ZoamIvjY z`7mIV&OqFEP{a|6!~&Th-+HIaQUJm}(mTh`edB(*oAZO30P&L9z%30)miA;{wA8{@ zfDXox$0~rz!N%{ig^H6Iz{CW=Zi*;0<(0V|zY+I;GHf32`+`?%6v@Ddk$Q9i81GTd ze}{M*BKR?n=|8(4UOn%HLDsbbj!58(5=1ENG$K>i_W(ZM*oGtMK&{KK<@;^5Z|W0iU|<*>!*3cuN3V z^;PuFc+CjZ>494?13(VIeLB;I>C6rRZ2hr5qvLtASpn**(t8;hrz&VfkDeLufqnAX z0Il1wUE|-~mGt(Fvqp;x6%v6tD*F5>sy*(OR31vAuwjmhnZp(bWcrt6I^|Gcg;{9t z4#prSV9nJo)W|zu@u9&lWFWz6JcQ5n9WM|o3z^5D=*s(C= z{(n5(-;Ole%DWtmeMXDtFxqMh8CI+oMO~n8srLVJVLVi>U1SO}3X7uhBi$%@ME-*D z#x@{ro@9!^Jh{LX#%Cd_3=~6~?{h%FtTKoH_b2xVS!V@*dwL`W0*wjrkl%X*7h?=5 z3;C1@m@9nBv1JD|sk;@-#J9|Avavd0?z6D+d}nr$1Yq=Ka~I<^PH?^De<(lO>o6zF zv-sr+vVXL3{y$hr)iZtFoO|@IR$nv|A;4`jqNT4uei?Y@==xuN++Bddas7j=pww;H ztp&_7wU+5`jNr$3fa2z*)|G_KpkyV9$m07-)vOkjL2M%oz<}_ZAM$@gDE)x7n#2Uy zVRROKrLQa(1^zr68WB}_hYo+PT9szMkE0_@sJXij%q`UdEpXIk&S#zfwIK^uND4x`~o5%@H;WY7hp{O(R1x;mm;{ne~A@R X%t~0*lLc&&Akd5FDl)}VrXT(biN|1- literal 0 HcmV?d00001 diff --git a/Functions/helper_functions_community/schemer/schemes/screenshots/darksteel.png b/Functions/helper_functions_community/schemer/schemes/screenshots/darksteel.png new file mode 100644 index 0000000000000000000000000000000000000000..41b26a6b71ce730c7f3388473231de6f4796769f GIT binary patch literal 31598 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 Rpwb159~3ne%H?0Y{Xc!D0BHaK literal 0 HcmV?d00001 diff --git a/Functions/helper_functions_community/schemer/schemes/screenshots/default.png b/Functions/helper_functions_community/schemer/schemes/screenshots/default.png new file mode 100644 index 0000000000000000000000000000000000000000..982d9480377da343a27c1951b4d0e139c43dbf43 GIT binary patch literal 28705 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 bHf02o#0hC)xSG6DC@9pFHI>R0EnfXUgtpy= literal 0 HcmV?d00001 diff --git a/Functions/helper_functions_community/schemer/schemes/screenshots/matrix.png b/Functions/helper_functions_community/schemer/schemes/screenshots/matrix.png new file mode 100644 index 0000000000000000000000000000000000000000..de9c7f832f73ec1f783d6e12d0c70518aba66b21 GIT binary patch literal 31324 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 literal 0 HcmV?d00001 diff --git a/Functions/helper_functions_community/schemer/schemes/screenshots/monokai.png b/Functions/helper_functions_community/schemer/schemes/screenshots/monokai.png new file mode 100644 index 0000000000000000000000000000000000000000..ffaf06f88c7ca3b29418b5c4f39d51782946da0a GIT binary patch literal 98691 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%0DBn=Pz@PK8m|u&_F+Cxyse* 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 #ZlfMCgRYSnoaUtPZt)MJuuPsN(VZ^clfD zvoyC@8r#aFjqm|@nQ@|chX>+jW3voMW#Lhd7AUC zLzXo~vR&7d|NCO!KW60bN}qoIE_wE&@6yk^-t>5Iv;ebk*q=>GnBYR0uqMd>>eJyK zheh~rv9@*=GX2*;X>&SUs8IFyC>l$mw%=^Wb{I$t39G?$QrD&X#n(>TSJ0Le+uD@> zcme6d1kkex5brNGSXGqen-OxY9{?ukcDOu9S&&PTEHmD`&ujy8GdyhpT z?{G(}<4QH+7>nlSV8F`_j4_)#jYu~qi}HfXH=&Ou$b)j*J5SgLy=Zo>Mg|`2;TRBWLCJltCK)A*o^7Zn!KWUxENcT6iS~#n6 z_h4e=AZUX~r4Da(JzZ&#T~@H=ZK1TYv+mg84hWmRy}7K4#(=pV!J1Cj;b9YLF<2(} z)=69o^Vtv16){+cgrhnr{GU+?@c-;uY%uxHIZc;J#m?I!a-j{LXK+Q}uV&7LKc8*D zy@Ke6cAM>-Agb|EO#i^Z$RGUsi`B*+NP=l*o35KX>k@4yjq-jU;t6IzO<-6IRSA<{ zfdSk`jzgh|V|2#5w8cqY=f82}ymXFc6$NwI2-zfXj#07#waD=Oz14x=wR#;NeF+lh zcKmLNc8Bl-e80=|d>wsvmjuq281(+y9o}KnOENO2Mkb-SB5ytKeCM(aP!D8#8Knjx z6t4?^W)&}P^{{|~)1N{IC6}#UlU&B}Dz%Tv<7uuFJ~tQRQHS0jA;asa_pPW$0B+(G zEZ@)@^@_(KV&(*9E<-!INTozHYDpG}fCx2}9@cF#li&0q1l~B1T7ihqZ13ejT_70& z^`k3X4g$Ym*JbP#c!6RSiX=QxVfc{;7j(!=!x!OyxDer1gfbRyKolQD5Kaa6g1hjB z#?eWmrN!-lbHLH1fc&CL!2dlQRiKB1-&5P|-enaqGVUj!7Y88q1ASpYqXiC~?_{3R9jO)V zvlP)3Nrk9|JMW}lvp;*vOj{_K;-Ibn@>3q+w5AA_AFpEu5DEvbK@5U{ngaKtkcvS> zSs9M`XZVWo@Uq}lFT}hliyW#mFz59yS_@2A75`a96kPnjJ}N#QNZ10q!8&20W{hYQ^Gzmv@wY zx>1Ke68%EuD`kG*_`l@BkljTlS{(-J?WWo53D#NJKTC9=mpPQEXACEy0scO$Y0Tab z#;RD<`>L!(@?moVZ`cE16i~$~KhuTZkW%QwUB_|+jr*G{lLGwHe^~CJs;aC%DUS;X zK_HJX=RHT*Paipn)@T8k9|*k)bq1d%P(o!CVl0G?>W~n@b82hBQWvxZ8Iau!bTikB zUY8neV&dc|z!q-MEpCAHvQbI6pFZ2u6~Yudqe_|;%HarTdw(sp2y3-{u1|Ih5W|uu z@?}lyNKq;5rY-94N!Dd@_NqasrVn(8C4RXXR7PRh8V@)Uci?X3E9A1#;yv zz$N6+U)2Ndc&ZhxOsv-VhQmCZN?-_N{B*O`n^svEu63Hh2Z3}$=DQDu62`cv4+K$! z{}tHGi{|b8%CF_sd&>APT0J5P)F5P!*w|-fi!l!0nVQ=Z8E4}m-Dmgh&AvI5=41YY z<91BPP6S+6k}(~wP$O?qcD3eHvt3B?sA-W82OttNJZ_7}!DD7DQQuB?OPSkJARr8K z9`Sj@|B?U*g%ns8o!i>eVzFx(DZa2I|F}~&)T7hoZ}D$SSHs`m)lUIn>rO`A!_s#@ z+Ab{_M=I!IvV92I5j~^h8Y87acfd5gHe~E)evhhw;1wq}+; z_d7q$$$KK!^S|pKa2}el^R>x^fn>K>3-drLsfrc)PO%=NYAAzTJ5M>DM=h~Up+~yX zak(?*%jLJ~^zk+jVYUg*L3B3L1)d+c`47}OfWpXEtL5;)Em#w#3$4~O0;9OLv>^Tb zxQfIS`q%jncr_yF=Dy)RM8zQSeE=cT;29uy!h1-KkE0F=GAtH~^3T|1cw7Pf3aby! z8jq zs$-0fKd*2c0n=@FC5P`FL2>F?kISZ_p*YKDSeaw5xqtonobEPm#bBR^+M24)`U9t} znIJ4qa*(!}b+Z;d^Hi>W!4j~PxM_xkwRS-UWENSwUuyV%ef!_x!qoTw9ufCU@{5Su z*YNV}R~DE3Myuuh_#&a7Kx1KNAwspU4oZBX(c{;=^-?WQz16SN;NK;O`uFr{K5~I@ zYVh?1ZEdH!k*k@#FDnlA8z7*1HPYREgp?FIZFXxp&hlxZ@c=8pB2w*;LIWE5g;QQ; zYHaN8&-lZmiyG|rAVVi-*FO=l(o^H0+p+Q!sV}AFCgn20?Bq2+i^gQEF!FLYIcN#1 zk>#h&rIU@+Ft5&%lXLMhvM+HGaL^CVh;sWSnt1QtL%2}~&<8qI>w`ko(R)1BH|n6eY2Oa#EOAHGigL%%gmV*x0(H zf0?M>dDSX>V1rxWEt0n%ysgFtQ=6C;AK)J33(gYoz@(3CN++Qa?hxY@&2le(Q>alt zzBDv~ zSx(qaLETT!E$P5!V-?#0`@2SoWb5%!(tbu-R!)4z@!o0Ck`+I?mb#gmot?U;tqcGS=xAh<{pOYo%i0r^lwoXQoi3$D>ehZ&BiY zRj3Kpo`noLZ~@?Ie(HzAC-d^lLFDPrK9k?BjMZosf#?7HU&QOn_pK20nzZgZno9g) z#E<-8R@TJ2P5rH1Shq6sMiahr|vH$kJjwS&^s@F&D)GMZsKcQE8t61rmss3R@u_Eg@G33-h>)eI z7^bTP@jt-=grR&+zym=?$~JY%5EiDinZrA)-=oL0rf?P{3lP7dID6H{F7b51gqC zH0+SR+;C({YYkr9{YWI|%aOvt-4@A#Q#ti?$XMuHy@`X#_qGkLP#r(c=5}=-F#HXJ z@@4>a8_`8%qmt8ZLBUeFjZ>RDM%xF$g0=|LHv+4gYbmB{u7x{?k?;X+qqeA(bfl-` z7guQ3P>GFAX(j*?d1|@HSg~-!0k2q%Sl8$wn0t0~0^;*Xd|y4!fzyn*Q$~6i=EP{I ziC7KirDMV$Iqy8vHF7ZIr1l~GPTv}7KMhWv=K&#Q8KRjqvXT+A1ml<5akxT*UeGDv zi|@x*G8pTN3^rfb-)|9;jPCpax{ytDPKh9p-~V4=V}QaJ+u<;SER^3~)|uk>bg-7s z96~d70ulOZx*)uLTpwVXyJ~J8aQ+mhihX988g-V z^kW?BSceb>3(DJAGM_XJARMH)6-vbeE%nV9JMuq&aJzsK1SZ9ANpP-V_(D}0Ac^)r z^$nz~*5VR;k)u`@ijUDCmv9yKMq`u!KtY-R$i%0lO!2gk5D*_UudjiQU_fC*k71R@KHfe9$FLYHEl{O{ykAEeSgKlC6X8^Om- z7~^fOjGGLE$?ufimM9Ir_5;a=nlW^!X6XYLn@joMenZDDpy|Nw(@k|GAtDS;7R`ip9(4)a+0#Bup)A7p9v?yrL;bTI$RyFsS0YXnb+74-)vTHIY!F z5@|@oU}l`1(fF*29jYpHaN4rJ)ZAh?vvmhAc7KRhB|O4x9+aa$-*DZ zkjQL0%eqDXh~=*tXU2gq$5o#j&3bDyF57}+khHbypxKT=hefR#wsv4uN;=OvyW&zX z;q#h3oxR_*N#lo*Giy&kG)2~s8ZeA`>i4nOv#kJ*A;eO91SEa-#jFf|!khE^L z^>Nu>^P9@uPQUBn{4oL?Q4kcGxqOu==6f%@_V*ZIE=xo3!NIrV*1it4vz5bP>v<(Scs?m06MKOX0#FA^?Kr<-@ zG}R2a8tL$99Qr|7?-o3cIHY8M#1`L9#1(HRSNmxh*YpjWipdW9SMGCG^+Noy_E+Vu z3FV7$)MtE+M=-{|^XII+Ujpyg*{PdC;~3YhFU{e))Zo&j{z4DzxzJ{0p&e+F4RhNn zFGbRX{z|KI;VC!)2*q4+Keb}P&svQEzC++g`!WT>#C(FYap=uc?N9vOgakUAjP!F- z#kn%&5;Rwq@|X5Xnnl~EUd!5d_sww8aMO^{(2UM*N|sUCpK^`Y(cUnrGZTJMnGzx2 z<#Cyg;%pDbWUTfPR}9k>aHQn0=(Ug3(c0j@ zyDQ3?z5H2jvLJyVliVHB{eNiC3{8yy=$c<1XWfs^F^X-~G|YVNhYIHf6Oob~xdQ(3 zXn<%xZvU?xRP4hz;hG*)g0&V)DzpXzaX6Yji#i*I8+S3tQ6vH#>Y-*PuSJEnZH1j4x0Ko!$?^$mjUwewSjm*ZVMcng*r>l zli*z!Dc723=&#ZVY`O}y@0?O$h6J2E#+2N`$zun@TEv0HP9>t>7cH7tN@Z>_^>|r} z7lDuzfpKzC<=A(bI$zzr2zRHL%}{y)cciC@+C@p@Si>Y^CTyHMsIVEQrcT%=!K`Uj zi`uj&xhjSsVJR9U6U|Hq13!?w3m0ivL+w0DUt~_Nc3OwnXjiH+KI}nk4H@K2=n(K! zk>BZZ70F3cLAt3jhl>MXe}W2FqH*(&5}zPaOHgprnVF9^6Rj6pk^ zAm$`yJA({t(Kw9GPWS9=jckEP{|*k1;)*HF)N4FuuKz7sW2|6A%u9b9H*@{ZhX@u#{L0>j0Q> zO!PQc6juk?JlluDkmOa&jyrbAgje+{)+iVrg!K>-EO2j$mwA#Qe#^gt+PLN10RQ;5 zqmVYF+v?$vsjir>^*W9OU5l}s+0hX2XrY*)a9%WzZm}0PX9krh-J7Ey*+Eq`m)uHn z6UbhhQ}95xV@9SRoO3M8H|m-zM7sf39$jD>T%d418@rV$3}3rhk(yMMKkdH5d#?nPFcLzE z*=)W=;T?sCKHT1_NKpLP9jn}02TOJJgQ&{Pm<;}Knp9-cXW54g_xKAy@ekCYUXPzB zTCZYXc=2=K2wfuKSs++^;wI->KHA;7`%h>Xm=_qBJufb?SFcL2CveNx#a*W1FMjF> zk$Z(vAUAo4n$3AbLvnm!glGj*#Iv`q7r*&}6O#tWzTTIZNAl5)twt{5wjYClWg9(S z{L=#NkJg_8nNU#_KV*j*m=s#`NBM3QFCqTFn7zccXjE6zC;l%f@e}w9-|rrn!%_1# zB>21J(3r>JAyVX_IKk*b$5Y-*-M&YxL){{@zITrxASqY6!Ur~-Q+qw9`~9Rt;L7!Y z1uCsnO8`)y2UASLbj!WOJ1>OnBo*AMIzKvtCbOY^`OpWUgTTC>Y|a;>a0J2+jt&k? z4nJDzyXrrTgVGzGG~Ir9`k>x@zJsiP;KF0|JgbA}<~xiYw{M3{n`sS=jq{C8F+gC; zMEjqI>Wd2oU>&#`f|K)|dh8eoXJ5UW)^4Q;oscj~&5Rl#p_g`%J<(wf8Y2!Qty|t) zN^`MEeY-d|0;Z%fgA!xEEIo?yVgz+EQ!=wtlDHg&3HO$Mh-u{MP(~>D9-EpUVjr1q zugzMIWWZ9J&5%cU=bsJkKO?L^&RK$Ko%TvDp{A0aWC)#)Gi%x)=0v@Hho+M8?-0c0 zPy=0lnq*}?|2LDx>Y=i-^SYAI+Pt}@)w`mp8bbU_-6Eg8`u*t=^2vYr4}PQh42hr* zUN4-|4x&yjnE%~N>(5=9&B~jtuomTW{uQqbae&g^nRtycerjrk9INO6blWyXH9Pm9 z`+zG{ssqO9MOCE3vKUnZP)ifN)>*`HO1*jBS6VrQF0x|8!wWhpDb56IU;DS@D^-aZ z`CM)NRxluq+$;1pR}yaI)N2S;`_Pp}KR=Q@ZU8yQ>l?BVm}}wwmyW=abd_hA$wSg% z{ewb&64abme)Xt2GjXx%uYS*4nO^!wqIFAf8JZMCSkn#iT4*zz^?B3Wqou(%^m9@S z8)cbehV3ewf`|LapAuGxqKflG#V@OdMDa~M_2&l$=6!|pCGluJVWIIC zZ&5AxGH|q_qGIYzGhK-ska1R#p0LKOA`QihKh_*^-U&>Vf^Otj5-B|&Q#zuOW!_;pLG+-eROJ1WV3^(m5e}GS)T`p2g)Yg8yxp@%C5D{5xmKJfBVV+6% z=jWld{CGJ?kd|Jn!>dsc^31n7Ol7P*A@_ZIB-|>270J$UZx<&=Br2KT;BB}AZmPI0 zIQR<*r6;P>LektKtHi|IO!l<hV@^2p7SHIhLZFD=z4yH_)#)>W8!gj+<-7 zLd9!-IOXaUwRTa1b6ZINto;q#iP<;77XJc9QnQkOXUoeyo80x!Bb%TZb^SGLl%_kEr@2*%@~xW#Sn2Km?x0AS0z0_#Kld<-f1 zRrSO6w^C^Y_y&KPPJc&kG#=mhrQ;v`%cu^7sZ(&ls~%(m-e`##EfK5kDDqv+sM}!v zG>#=<*o{>Mif=0<3NjO)tAJcf+=oT@RBdF{!F*IEtmlPA$ z)ofdqP~ga-Ag8KFbF_IkI<-Sfkm9NdE|*Jl>$D8m+hjR|8O7*_l4vf(Z~a5_}5 ztq<6{f$|Bg3l=D|$Z}cD7_rrSyCt`ky(StR@ACiJh?GrJ@b!Pd8xY7HGw4Jn2qWBd zMr3;UfwIqb(cNe&B*bPS^WAX1yJoFyKe*rateBDEw}+lzdJ8^5_LUhRHq$X+;zCrw znjeFpKaPRKp@_^}eeHG#(F`?@TNanV5@=b%_83jyn5sjE{50F?WmSN}O~zs}{n9EC zjXRkk^t>~DxNMJ&n$dm{1I*_n2#2D-&in0Kw=)|Q>WssX{?};iV&pZ8OTYD;l*LS5 zMR>I+`c>i?px5Q|>9y2-kBb#tdj1$7m#JL8i+HS{)nw6EtkoFNA!%l|0iP;^8xzOG zASh%q?sX#UODHN`!>%B$goA67)38^~YeF_TYrt~m+MZn!%q-w}lGWN<{)^^ucvbsf zW|Zoi1n%Q_Z`M1;e01)Mr3wdusZ;UZXx}gR#ulRkcj*{l9j%C8G1*`GAee8(X0zUb zFr&)_1qC1&;=N~*j;=2G^*;)W@>-g8zalR~r@b!O5&4uwwHq<90c-i>e-;)FU=~UU zL7ql_^pPy#>#hRyxj`^N*jRScOKwIVaDa0X41#RXMDy`}@#5Ss1&A172COKFoS_xz?g=k53t456xU2&VU`AiNUW~af{B&+k0>se_p>q-OZQ*Tv+StLw{*5z8;$7pU0 z*S$jQM*1#rYG5wR(Rw}K^c4)0sjU@W>loM%`jiLO6_v|eY~bhjt>$_=d+%gUD6a1q zVy4*MPq6WJ0P1ZN8ylNV=PZoKw4wU$q(v#KBiMgGd7@*UnOvTU`pXy zFP>U1!uAKK^(buCnrC5MxHkD}i8@ln!Zp_DLFGR5bDzEWR793@l-Rk*B|<>hO@vg8 zcB7HbzkTYbqa40???y7$DLpaGV*mW0)-KDh)sZMLQ%sIt09=aM^JIdebrcFIulLJn z;D#*z@|6$)o9iiI0l1U5Rs7yqGcmQ@))aT9-^WUU@d(QG6gP)q-8N0qE^X_m$qgxsg zhan()3l90Pv?0u#Gk&LRrZd^ofKz1QmjGq?ci{b6R9n`)2D40!mqXj+f=A(CcyQQ_ z!~S1JL3_H}1rv);C6&cm*0q5cu=ehqHlj-d>QE|NwNFHdb58cs>O50g}2h& zl1&c=wf}z0wihqU=3+4VgZ^Ow4WDgIR; zA@so#*LQCm4Q|fiq8sVdETf}k{qFq>P2XSMT(jB8@z!|Ww(3mEj)s|@o@G%^QP0ciah&dsMr6` z%61f_2#zR9<#1S1cs!&lF9e^?mz_RLscpdH_5J#zd(f(DH2B!tp$Y^ez4eZ|A<~;s z(SE>pJ5hEMN0x0*;7O+J*BQkmKb+-P#qx(ZP zuYZwumpBmB!$=uVp>WvpD4(8TlOeVib7@h5fq&4stCuZZ(<=zaJ(bgdePk!{F)(g2 zuvMThIoBMyb#Q6RAdG1TFGrlXhAJq!(=I&YHrCAIgekF?117yp#CFtr5B|n3hiLjr zt~*KJ{(Y-nYn@OubPU&)Y<1phyIEW)q*JNQew7F?)%-KB-cXw@= z$4ol0T6H3G6@8rn_&_E!2A#UegH8aDfsi6He>FvLNn*{kO>)N|NK1%M7XYyLz zzvM>WicmKB@ocW-x@_Sc`&7Gs_oi`(K;jMzWQU+CGBbAmJGiKr504_jXhR&yEeAF2 z2v@HS#~uUvR6)EdrnoFEVHTUU_5C0d)A03-tVVm>Tt07pu5*`@xgsEJ_zckIYXcm7 zIoGTM-~1+{PJ$a`mb#(m8#%_->F{D~qI^>mOR6%oTOrni(Jd5KL+Sb&@pklULFM-HmbwIPAFC@c6&YE%~xeWDd#vJ z@6JZAij^^`SE@zMzVYFv>hwGXd=TtuA7#+D6&=~f)2n5v29x|a7>VHrLLuMu=vC>n z^6rV;R`jn!YdACI(Rb?je-3^)D~g$j?0Q|6!b4tf@Mrl~?O0tMW`(W>YY@TM0SW&Y z@?P#@Qp{Gi~&R-+tONB@buHX<4_2wZT47qB42hbkTEdkVCUY0!iFb5-4cLcSRm-no{Rcd zu5Jek(Mpeyfba2@?_YooGoZYHSAc<$d7PQ1&AF_a2(3_=6oTLc$r|bS>0~vf!5Q|w zsmb>SlceME>}W$|j(;eJj8JNjLUE%H^W5lPgJuxVY2;hPH}ZZa?E-7k$wkDphhbfo z28nMYye|EG;K*RVJ@%e_9dY6Ik7Q{CBq*AJ5;1LOA+;pwRfGIwnq)X*4zMxtf4mKA zpD19ZK4dnODKAQUbxhNvan1=MT+hjz!W`3$bfRSQ*}3tYp^ebuas8t@4Rpa64+tAU zb2B)Qof_;h(wHx1S4E0$RINohv{R!028|f??ma>wP+&1(| zwZMu(OQ{Nvi<4YN*?hZs4HDG7#1`pR`$txixVwRQ3`rQ#;g0qK>&eUAv42pIJ$%|1 z>u`YBwW*_aMz+F%dmRtP<+$t@cq2L2oD|&C6Q`PH#~phMoxV|J?SB7#K;Una>qGAklxNI;6uZ%q` zgSjR|@#ZZ=YV&1*wW5L+$YJ8rz*^X&4j?swla|Kk@$(s9Nlp$a7{I{9=!Zp`vuHNP zEFw=&&g6Hyz_TDq;umxR8sN#4%tCVgQ6z@l|3jL`&qT#ZXIHLX6&<+{i^tBu!M`P} zC%^jQ3W+8hqQ^()sAOe@!IQ^YL1|0@i-cdYZ0YJ{+^PU6zdu6Vrfa`JuEBx(gId=`eW4SiCxg|b+ZY~EZQ`;T(090mLCy*U;m2ak`NSzcT5 zc`jxwj+;;l>TF+A`J1=-=MW6Y*aA#~`1nk*DFkG)ov_NNs7#FoairvNa9lZ|>_f0O zG$0s^U0q3Y*@tcP%=49eaNdGYR#_`1)hJK=0`!qKTnxE0+8sw6ze0c*rVCmoUjX?w zSBce$*ieN;K4CLlE3KU!MZ#XQhiV90HMB**+4jQ2lk2AifqfF%fTs_5+z70yCK$yr zyrZ)c96*qKX#uL=0X-xW$5Ve$7``=-03QjUi~f^j8N83#s^q{0ij4dh1jZ5SiSQ$A z!iD(b$ZiSqprL;EhHxx+zzJhs?A&%VrocV-hNW-vS{=uegxi9Jpe7@J$a+;=hSE z1VAigkUAO6y%g|afcr)kK)GLs#$em6-k5+#1I{I!eO5hhWdMD|iwxijq%q2eVgb{w zc%lFFhs%uSNRKn`CF9k^dgHL8h071*M^@<-;-94Ic_U%BA|IxZ)l%xff`>m1M%Y5i z!Y1$w76e)CtX$G$i-ocRsBQ2*7R5S9nc9~Z+0zG%L>p}6vi$A){CXWagNq_s8&{2G zc;n0@aA+k&MXhkiEy!Z>!A})w5~L<~;7(e8q$~BZrzP0^_Zww>ML5=t@zgQ%*|E)# zkdOr(QJ^q^2;^Lz4j8hsva+Fn2q+CgIRBU@c90MzB*h(zVoCP7846+=2Eh~ywLw9M zI5;>6H%@GC>sP3KRkV`x^LKiK%**Yr!Ff{^Bm!P8XAAI-kaOEQa0F767B8xi)M(DD z2Q|3_|6)vVJY&roqbClZPUm6oT#SdmoZ*fzXD`mSdxXf6l|YJ6$Pr&QXYOiL$irU@ zP}Yc4fCKbzQ>R;+n<1U?IL^)jw#jf5ZOkPxo%|lH56g@kp;!2D6JYK`Wp91KiKy*Y<{Rdyi zH2swJw?wDYhZoeJ7(c>{6%g=;*(L#2&0Ve* zvW9R&XQ92HpdMrI{%P6m+(@*zfM@k#?g|0Hf`h}~c!)O11hUQelkC(KXK#3df+yYx zBqv&t-t|Y3g^{?wYolMc+SGJ=N4(qbggwK7?uiv~9=pkPsi&u9Ia?l7`mWu8UmwAA z_0gctLU&Q5h%ULfo&ZurZIxhPxv(SOnhW#$VnJipXLoOlh{{;Apl;TFmKjofPCbkW zi`kaIJNcOE4pedFmb0XwBhQe1X=4V3ZBvd`Pd{m1<%WE&4uN-9XuH;RsL{Gzki9)> zRIJty9jmPO`>ES?PnoZ9{M@D>3xlHF-^ob10>2M|ka7~LjvW~IX{B=|Ajo3!z#6Jk zHA}`vIO7yul?kTX4<1ieDL>EiD=T`MR5(qwtP90g!0(#*9+ z&s@V3Boa8elss40%xXMF^ zJCW0aj5n}tJ#w~XCf^Q(SIudpw8vwZHJ1xoKP{8J&7eLOa!`r-I{COuR0B9!CVldV z14)*6lmib%;i%diUN*0bU8;)M6_iFX_S9fxFctzGKd$w5wTK}5pomAzC!&amh+&};SE(VHYfjFVmQct&y7A7?qLZuA;#jq) zznwOw=fWlq)2-Q)+~L3Z?;ZR*U``|-b%BmniivR??K_%879vyRTSwH<(HT_8$?OJF zg$GUpp14NRb!K1CvVNBiLb;yvxO{)$W3|`bQ z_BY7~-@Ahb3Oj+keT!~#blLzI3Fh5_F2Q}bkp?ffP=3_lmD{?q)7&gYBp0;jv!gR$ zQC5;MH8mSg5P==JNpLn0b2j;f-FKi@DKd1E3S&}Er>Zq|H z8eR(W4(U$rn3R6fjD_6`QN{9uk+R^S(_v+1$jP&G@Kfp~?ApAj(z?RNVr zjiQk8eW!a-{?GUumpwSjpUETcfWj*$_yvc*4Oacd1KS->)Df`Md6<#OH@w2lngTI>|&U(>{ zLolU02}9@*0ns~}TCVFj4YOl)$zmEEXR&JQiaY(E3&`bu#|Ff~2Yw?H%iOL6SA2mW z`jI>`@&XjMW8#RIJpArRV)dREKZBB#ot%UUHtPd4WdYN9^PK2 zJ$MO>Qip=t{oCkIZ_Bp^JRflW1a78538q-hC4yyILKqesKCWLJK{5MxyIKR~@})nf zd@F-OAd zmW~Ss4l7s1!qIq~30s&x1L6<2Q;^(;w zU5-K-^@LflUwMF~UUMdcm1Qo*fqRTPEV`ATwi8iz~mQF|dT?La;x= z>31Ay+=})<5?#_)JSFlBd0&|V&$5fP&D~G+xXRKiUKKYZ6@eynxlIU_&w;0b{=nghc+jI zth66ZpFBaDXLyomTss-#3;<)vNt3zOdrF)0JUt4yyI0F3AGygEN{zm~>x|mfM6DY# zbHv*{c##F&kiW`VyXfGFVf`^IP6N@ExJQruN^_=Z0z~UVye#oPK~wjhw-A|D05s&5 z)B#V3*sUa8AKRmV1x)$pU^@(lU}DI9mZ`<6-Cy(=W89+iRIf|ytg%mB#JnqFYhlF* z_Ukw5j7NKzojH2^aNbVVQjc9Bzee_W-(9F@MFRA5FAcmnHe9T4KE1`lCzlXk6mE`C zAiuCcN>4zN8`41TRs?fb5||jW?z}!3nb2qw=3Bi%tpMIiX)8&ow{q6Z&~f!6K|NLA zQmpjWT9Dk9J({>DKfmB;H`(vaneq+JmXXT+`qp5`_+L*p#|_~Fq}zBOCqF;-32#}} zbyN$B8d0^r2ZU35@?P^*n$hz+lAwX{w3g(=x9+gJ-1i>i2Fw2qs^;4vO6$dCXlPhsy_FdqlBBGKCtNBeGJ zMP`M`r8^fkO5W|&+5L9qcn5u7$QDi}sx}7G^%esZHa!^|a5-E(-)R;>O94%;%}1xz zjsfMyb3EgU{famUsGz9=@ZwL6`U&3GZ3?hKHK?%D%qCV~I<0dJAdWXoFg>4eUt2 z&v7o(#RP=N3zMJSK0Cb=jUTxv8t)Xyr_d z6hiRf9$(JIZcQ4y2UN@3_7g%^Br;+lqwi@uX zfr6y{P)k{x?;asmalP{<*E+#nSq!$pW!F^%;dS4z+N!4KF}T6anr?6m^$xj#l8#U;1@W@ z!^xuiv5Cakz367$=jWSO@PUed5jsYzVW-|FjrID|xv7(_NkEUkfk8f#c+F_yD`qZVhncO{b;(>dbZxOt zkt#X`LZ>^o)AWRBYB?zaAxocsF9}{^77;)5evlhIwCwwOJ*z+7wbof(q(nufvnH9+ zJ+{=v!xWmVRNi;z`j*TIOE|>qRVkOdkZkoGn?DJ@2c68bOVd3jz`|N`(gkZjz6|!u zir3`1g!KTH4}kG!iGNJ-HA)&n8lOR8VmVUF&uwD#-EecyxmJn6xoDf0%j_j;!%gn` zakeH8EpGimkItm|oMM+*=0RSkDe9a8+ZdunlfScuQzY9Tn_!800_9-a^<;PKxS?iM zhr-|SBb6UzGi(U6jK(%Pm*_^#2K~m58y!O>k3PxU=_+90d0giW+L+mX(8@#ld)56Ib05d zJ~1+4zVBdjgfUhrZkjG9g>9H=44^RLv`(SLbCB#W`(T*-FxOz%nO}U=GB`*keBxZPXKF_5DRqx)ro&7mL=0|2*45AfFW6xUvQoWsW*8m%PFi_%W3 zSgdsM@(oe<^&1-v=DPcYRnQk&x>lC3vlthNkuhAbL^ae7OwBo5?yjJ45jNrwddvr0e*~r?Wab z`+fd!aQidpB*o>bf4X@5Zf?0dw;TW6xY^-jY4q9zyQJB6I_G46Wk%V1b27$V8Pu&aCaFbxJz(%f_redJLjDDyLGF+ zAGd!^P0h?+vv#lU-u?8`ZRpC_U2mZv-1F>YEa{iC9<$#@*6IR%5!{nX|1<5pJ8KBt zUQ3n~Uh~jq(!Tz8WmJmD;64j9eq{21b*qf9Xz6g{#zD6)0K@4FO^60Tpo}lzz+;62Qe3cd41O!{aps~Q<%P0=+WypXtKBk=n$NY1x?&mRZAtn zHaDX%uCNt6_Ew-HD7P_kC*0|xlv9owFQmW!=<&%OJ%!1rI@lZ4V=Ektd2>uz%6I6o zalEm!DO@7mq2;wh!?wLApU?_TbzajX3=>WP4)K&8CyqlvL)@qtiigHOjP(TRu*LOk z)HL7K|CUFP7yEMuHz{OxkvfA%Ylq_^AEZqpFbz)a@R`c{8f3~H*0{SCftyaN@&1;c z*yCocLYTtLVipK7UaEd65bch*Ltn{Ze1+G;n1zagFGuvg|>28xB9c$ zv?OD{6Y+bVPi_XSlSuzMpuw#tnez(Gz`(9t2_lP@y=!%30MBKVGykywZxTqguvi0yty#fZ@ctrTUAkLYT8~}TvANJU4sXC>kt}D46pMU^~TV*)>Yv?re#fF zMwmO-k|%(&Vw-kt2{s)cB&^Yx+&O<{M}`UWI%JbbZ*6jKt)3Q za3HuyZ`B{VNu*%buRPhcvsGd7#G;Ah0^$eGJE?QhyFTtlVOTg}zPAj2ob}fYkEF~uE-%bOuF@BM zFU3AM_cJRYNL~croemZFP#50C5S%2md~$tqTp5*y=688s1A#>1n>ndhI3e@BN$^Vh z<6NPyAI+hR7C78CeySa{nqx2vl(9TuDoy6H0UeQ(1O#3nkDg33Ug%7fpf!78vIGVq zOeN2=>v~;{K+(6|!ons))GkuNGG6YR&0B|(=*B6{EgsZix1_{5c`@wfE}kWPl;b03 z^8H&o6x=VR%t?GwMZYE@qP(xq?N=@fh#^>OYkF&I0s$QYJi6Q5ptdT(WQ1s&R(XtS zE%S95ZpGya^hm*;lv|99i{jHS|f{Os8p(RUhUF1dO zu)C=e9^#_bxMd6&I|a2M+ZKd4QVZRhng9IqB ziStMq(&wbz(UBIO&!vRUc&jKUhy@mg z)n0l0d6U}H2BP^TI>0o~d*jfZ_R|L`X8x?7WTix~dBXi7{9)(f{W^4qEBnh*jc)8Q z$IcncOd=H_A5op5Rk2$!O%%oPFL7swf`JPkn3(T)u_tV&3s~5=+qc@tMX+?BTL*d` zVhlmK5in1mEBabXK6iW3YfG6@41YUIUR-xSC$$^ZuyL|kEIKdG#vTgAqE6-*cHpw+ zW$eUp>!uMo0;Y<-0@u~~Y`ktQ3WA3ud$!9IrMow~;obLZv^>(K_v)sGS zr{7xYHl0WEFz9N|gNdB5`_PdrH?z|ASyxxp-DPMh5F8?FH|ZzHbTJgIGJhF)by;is zPDjh97D|drwa6ot<*6&n(nHY^ll^zM%-9$}oSM_n8-b)I`R)3?TwFB`8xP0>{AdTy z-4nB1(Q zc2CbXp4+RN6fPc7{C7sFEAtgjb|jg`-Lr!n0aM!9N z_d8ycEk9Udkuru_iT!*;z?SFHqjx+EMOkZoWr;6FpjLk0I`~Fc8lmOS@BKF<-J8Zu zQeu@&O>B%P&H>coab;}^8W9OWP?#-|A;K?Nc~1T_4JklZT28Y8d3kT>)A<2m;vHI$ z^Uak*JVNu`r#2V+6DU!KyL^Ex@f|MV9ok&cBI2wy_gN~i(7Zf`;JSPaQPvRMWpi^U z(6D%6c5EbMAinEL$d_c39O?RCXKxYf%iU6+`{Ytnn@^Q5qblZNXq2aGqPwS?>+x^% z4#sQAtxY3t{W!>)0?R~~gY(8!O|GwemX}L+$PVi7HLd9;eKRi_ge$caJ1kv)rbK4M zt-WKI`?t%!a%pEA!jZ?@r}4N1X{@#;GBS=bGRn#_-vm6v^$G&{kkh!7i-`H@KLSK8 z_JE%4&?DzoW{9i1x2ogi!c5}#yQvQp@Zu6^L&<4^*IU6lx*l_IR#;*qHRP4KzHt7#4O20XO>IW3Fg8(V+j zT&IL$Xfnokh{Y>t_c2UhtMz*ChGrruY zq;Vp{+tn)~lB^LpQJU!9%`0S(uJ+jahYJb86MW*0OCUSqVZRBRqh|GYUYohLd@2ge z8${i~j+ZQi6LEYa0)4Mg3d8YEOpgLyW>RMS$`td7yy~73!CTY}v1ak^5O&`Q4hE>= z3+rw*_&iAQ?)xB|{vv_hJ8#&R>#{#TK(%JD!UU~IW_9U%_vxgP-(2uTdIlpAm`o?* z`>@`}Q{mOO_J^j(#hI1xfWXky{sS{^u&U^Ar8HFL9B5abn7w45GX_|G*H<6M$}2)~ z%v$)r8ouGF6@XntnD$ah@W|}vTAbmcQ`QcL!B3rKSG&`Hb^Mi!9?F4>kH+Tz-rT@3 zB&byjgOgM07l{+sA33%04Gn_NYf@)S;WT(`mxvwU+>A|ioKe}g6MkD)JwfwE@O+h_RujZMuzrwaGWa)Yi*{h0;>f_}$0}HFdpS3CsWQvKb5gdR4@Rkt(Z(nUEq2Acu)uF>sxfd1I!M*)#CHD3Vdxuh{ zGx9I)ucoFUV#o)1XoTQ7eB4m8A)X=myRW81_RT>Qf zQq?J7_P7@U5gDt$sh0t${77rmiTfa<5PzX(0>h>4Xz_hWe>{nM^* z<{Kk}W;YuSTiR11dt7$ z$f(G3CGVnbI+e8{N|lNUoSw`t_Wb)*z=D7Z@<|dHP(u>+;6;)PNUjf82U^cc^B@h-eqz$9(%ykCdHfNB9W&I_B)Ypjjgi`}n# z1$Cl_oC=x_Z5O?FiFd>;(BT2A3BP>j+KdF~OV|SVYwjFGxW84~Jp?iYe)=x{^#`iJ8@tzYwB%vBvaqY1$5q+}dH=>+(t!(?ro))2|-y<34c z((>B;;H^*@6)lH*l%~8ZhS4QK&fJa%i?oBAHl6eV_d1}4M2Q?-s|I&X`U8h&mDdjsiBokg+6 z{NvsoU76RtLWEH7VUKx(1B*tD-p?b_{*XQKHF{af!$F+P@Z@>$E}ljtiSFWSX7Op+ z65Ymfr$Y&~`DDT_Cc&&~FBmqK?3`7eL&<>x3zp*s!8XpS!3pw&L_Y7gm#2}}z0zqx zSX(D!rNgD{`?xtJjUq2`6HCJW-g7cHz~o;1%6ssIsmLUzU59BAHh?7bO-V!O6pW~? zu2!r4Z3Fn;0HXJ)Z)`g*!S;Bi+4AsJLP@kcXfPZR8Gi!{MR4u)6AltmNm?Fs_@=W% zU+3f7(--}y_Y-m3Z5k?WEwGmFODZxUm(wN#O0W(msKW1;m|7c6J;kwTDW2P4?b5x0 z+%iWtet+1yw}2Kec;Nm`o?;ixy3SArPDgy60P@^@>Tr zWt(63M&XQWdxxXN+N`H})DEFA;?%VqbK0&eALHOzuh#fr?r)-iEiq#u%##%(lOLzK znZj*ZnN~b|=)yyLb~wlyv&R~__pufuzoX_lZ`fRC14j(3p&tUDwI+o6tXxfti=1oN zjd&mZh(}RGs2ma;-_-mu79b(PQ;fcuGx(@wjs7A`<&~P*LQL^NGuQ+gqE|5$OPEXF zT5rAW z)K*Buv-3eThMg}AHiu_d=T}9|h@2^>jO9Ruc2?6;1@sdBA!jmpU>}v1$p=|1Aa|29 zimr>7Cp^3?VG!~V_$2?{@RSUCy#fLX;^Oo?;J=((Ak4N;ohoR*F5TZ)EDLS^n5b`O zUTo{Gq^qwl3|7;SDP7$*LuxthN7qMh$Ky3{S4fFDC5Al41?E6jS09~EPeTp=*p1r% zd6QRIKVe4a<_$5==*xZ^1rwsbn;3aq6ve_rsS(5bl0$__AG1=!W&gZvT&OuQW#o>` z{PpDYX{a>>JM2`8kp6CKhCz7!-Y5^I7mjhuuOi#h;de)0_tR^3^N?C47^PK6pr7d6 zj^;tzBvFg7^+jum!uO7nRlyh+JndGXOCO`IN!X71;y9oyD-awE?`TAx0kx)qP%?}F4NlgRNgsI>3^#Td%JTPh zj4LC;=6Y<2&WR?oOD;phe+Ha7%2O8%sCgo=#Mg z(OrJ2XHFIy-AM3L{t>@Wf{njw&x6k%OK6f)8gg>R=lH&Id?VqmB=l<5Tcc?XE<$1YFuOqL7h4){%w+ zv+64MoX`yV#E283NTEFRM}K!lkH2t>rP%jy_J^FkCae0AWl9yUTFg&yxG>GxHi$g@hJ? z@ZpJb>gCJ2>|NX*>y0I0ziwF`nef(z^7G4K!?!rhi%UuZ2C7P1Tk*F5@0|d~;l23X z+cWEAJL7#pqAxLGhjZweJhNLYVQn+3r{iI8e!L=K9CtuSWEsxw^xaCk7lp@_!$(ML%d-nK z`-10f{X%2QSfo{qu21G?@2l4@V$+TM4NXCAp2Vxg*l+`Gme0}K^Et1^YlufDSQv~5 zb^AWNqF?x5JqKJ*iI)71%vaUvNn>H9V1HtltS;NMu9raSMIY@*88=1o`-xIB6|hl_ zteaOa%7}?R`^_@u{?b@hFr3c&(^#%h6b1~p*;$#c3UZ;-CMlXww;M>LSc3+=$|2TB zP3*`*7V>ORc*dV|u3_?v`(m7z?I;SdEoctv*s?UOYt)us?|reomZT*KZ0Y0x5Q}&K z@)e-384=o9CiC|!MfC5vSS-5U$d0z5fiB&NJeYXbJE9at&a`U zC~q?Bf!LtPub?8&jFXVH`Rt$9o-uv@A)C!-;eGiXDLuTKC(G74(Q_=luz&M|&%>W+ zCkF~y-jrSRNCS&Nc`u*kOg1Wox1!Ydj}Jc+*&_`g-5Vt3F~2{CTeA*M9G#6Kx&1Sl*v6Pke%p(ZK|lLu!T)-?wjybYwX0{LB;nG3+ys_tmVDr1??Sb>f^wg!ul6oQw>@^p~7kRxt+1@NnxTwQj33(1qgpExT&! z@M*H(C!JQ9H9R89Z@ox8*zOT>g@r(YKDY-UpcKhx@mv-DkWH%vh~gXYu%N1lZk#Hl zM#}I3{b1X7PV_C!nvu&`r_zzDS*zdXA*}qT06%oU`gmb0sAqW>BUo+O(C8w=Ay2wG zXlaq;;*{%Vm{Y%7ynx zIUnM}=|y+c^J*H?pqlvZ-zNBBsxPgXupd?Bf45QB{pRcp!{t9W z*TdWRIp=I}u1Wd!TKAe<+DhoQAO}NdRJFF+Zyc#JTG+FNeg~I@uWx4vSkJ+dn3LiKEU~aRfNZ?OuGp;naimEYXJmHj<0< zy~&s9F4GYlCY^Ts`Q%+U+x=*r`V#A4DGcYQ z=jVY50(4EsZG)GwFpXM9-LV$;>tS*66&(oBNX{ZmGK?`z&||T-@7Mkqh3&DHeTfV; zD3y!+Qg3RLCF-`s1pjauETf&}a4n?fu=fqF%~5r@dygC6)bhCxuYb#oYOP0ufTI>* z{(Vz_xK>|^J7a?}dDvp;uT!w{X8%=>e_wna=d-*Qtye$W-GUNh%*9x1YH#7%7>*kk zx7PN5!{f|yp;c{Zz4SC-j03(Fg;Ob=(8-W=!h>#ZZVDu#zsVHtgzS8|e|((C=5MR4 zL`??jjY^^L)e*D=Xo!6(v?%tpVc74kev2>nqsn*ADNoiNc5NJJK;d^7JKmuSSN6dT zWaMs64!QzwX;Zcn;FI3mY#@CjH~o|{0l+V7`QlF9C^$@$pp35V=JpGGXk>hW&us)K zcno`WL{jE=r)4U)Cqx4mgmC(WUmwqgDb~M!o^qVAj+#BPp681@U)YI26v@^VYN}HC z;wxbDN8?EToV+NauM5;1%#?fU^u%M@zpt|A-SPnDwatTwN>(;B^c{#Ym=QmIS(&j@ z-t$XfUw2%wUSWu&Fw`QHsY6@;u(!l@e%JV{Bk&n=Wd?9}m%M94X(AN6V9Yh!WWk*= zfuUz}dKfOZ=m|7Dag&pt#_?>+BHCugepnS2D*WJ7a2cJj&NnmHXbT|Ib_FS7B&j8FH`9(xV$k z!Q21|bANqw+OfHko=>!u#9a1&y7GMX<%ybaHvSBTd|pQcX%{%u^X-gZaO|J1a@+50 z#P>YL5=yT-G5ahex<}MDsk)bi-USosN!<2{`|r z9EVu+7O6SJE{Y+#zFrHNigGQ(s?H`6?nxCcCD8h`XE&ifv<=+Jr?fGmKLC)4El)BP30=f-mF2FtOGdiM(>p+E_9C{}ZKbrdAuhtUx&1Px3v-C4Fb zTTrMImb9Z?LZc%-fOSdqF&7r^BdE*QSYpQOyFJ-mYl<-PF=oz& z()&knfAs=$n&5V%3PI*s*Q?3i-9(dK&$|dH5Gr3l9Z}ZY#O2ZT$T$)FGTTx_NV}V; z=+SHnVWt=NR5>hrhC-w4c8uVm!O@J~;#G#t0r^B?%!xTe1B78Z=7Ks0XeW#5)LJ8; z&mG3$-ibNT9dAgt_k1YW*c1^6-y*Y513KN`ExWiLd=a?cfqV;(PYBu}(*U}q3(Z(b zRH3~+2cag1yx+mk`bZ|Fq+f5b43lrhDOeIa&>f=WmXrKdrx^|y^gjJC^p4H*hARAc z4fB@n3BqBi7Od>#;WQl3))%l;0?e>%hd_MVeG-M0lhm6Gg4^$W`+r-6H5?*Fei~KG z=Wn;yakwc6XZf_U!ly#v38pf`(J4(tfd3lJAuzO5f3PLzaaWxy^g&jdKqUJkw3eTk z-|VUiibi(JW`W#W)NhuFj<|jjfdR!WiS0Ba^K>EyM{ee}#|=fd`3cx?!|cEqRE1L0 z=?f?jrCWRZ4n~iCgy!}0yVWO-&-~6cKX04L0FtQ?z@IKHrM0rOBz5bQi~9u(yES_6 ztwSK-07?;md_u9xXk1^K_P|qFn&qP)PSTKFNtAB7*>!C0@U>B}38(Ss)+ycOFn7-Y zvp=ai$1uw-MHlmXp8kBcN>R{b>frZY{1{;@$&=EwAT9)U8_$CHF+M80zJyqX-~>sI zLYsp(wrsf)2D808G6!3{9gk|9_`g!DcY_E!{QHdR^y2+dNMhE#=^+=Hp<@|>x_*Dw zKe;}i)vliGXJfSuQF&um72I7}6uh=b5)@(s*UZI|*_-Vgmz6(1?nYLHAMLo}90ckV0 zh`5mkoENvX8HL6@-&U@Q*>t)b`i!o$&xn%-9n8V6y5+|?yyGukyPFE?g_}Wh9Za_V zf(B{IPNvJhe{TJJddl27THLSNrD0mPMvR&{ybih^-pWci6)0==WhiTYZ}wPHJ{X@+ zk34&^2$MZ0IM$Zz!6CTsxp}{p|Feef4?F@wQ5CLAs@sc_EjGa0_R;hd;B}k%8GR0q zzO3)ZVGJ-{m+$Xe;NTV)J?6iu7Y^+X7Z&Ws4lBfFxX6^9DhR`P0QEJ3UQ!FsEA^ja z?e_O|x&0^}`x;ZUW^QhQ@tx0~*!&zG^-%nEf39fgHS}W=>Wn&rl zS6iIg-g_-&kMM{V%M|GHy6r?2rDZGvNAJGM1RzkOf@+F!;h9^1Ky_`Y>w zB?tXlNDG$luAiJ&QTubX=x=>x&!D7>I!y4WF7d@>Cx-%d4|=YV!R}IUjJnm?YdiCg zM(9N;bR%?ey9a1;nhN?zYnr41+k4aRaQ<8-g%cHz5K+A$o1cx?B{HzR+`j?6<8~ou za@e`l^zo`|e`+E2%^zQdUDXjM(t#4-TICguTb;;R9f;aJ7&##zJzW58@!^=|WpJil z4jugwBW%jfXM_-PWmXk@*RK*BPdK^Cb-r3ooq>#*$rJ8#U6~`CDNJ^{vpGt%g0@6b}Og_yGa$ zz(T2doa~IWA1s_5rx%))q#mLtYV;)36*PL#I`NBk9w zhmUfRxKjfY7Q>Qi#r)g3_4_7^^w5qcLM&ksXXsCFXP%yFT+yd+B_10jr%p`$P3cZyePnvzFS#K9<-{KlHPNqEY?l~T5hDHii-=D7%eT?v?eM> zaPHzTwc9RQl)P1y@1(C`boE=pNF`bOD?;YGX`ssqqh-<)KWQ5 zX2(r1QTwdEVOFb|RE;I(ayq&+--0-8r zAZAnC?SSy+b?W>R<;w~qsT5f`5Jq0+OL7Ap=3iF4b+M%gx@)FbFEZXbSwku zw_sJp;D+uydoeW`p6ViSh2F8-Gp`H}KlE?O7s77xyE5dAee7(E17iBv4gR8*yv=!J z;T}l}e|S2Sg}Y!V00M!kFm}sDE!{>@xwo^ zunvc={1**|m2qD_cz!}>_-hP+(va`y?Ccr-1ca%;K(N0MVYS)PGhR4!aBiOTOgubs zOjBLH)@SX=W?5BK3?OrZ@(=nV<)_q5!n`wbeturv3CYjThr&N^dRH$bBI3#PA1hN| zRU~h6VuFd3wCmNbs;f&#Qc}{{8Tv0z^uM3bh2~V`q!bnD^$)(z1HhtdU>=sHQ6>Ym zoyp9d9pzv7j`ra%j1@Tv)yu3fX~DzU3+*Ph;t#MtgJ<48z~ttAYv=vwQT0VpT+YXM zMn}4JQ;Q0#s@%%Fl?E;_lyg}AYtBZ?k2)hl2eT&zEL^~y|J}v{0FJPv8*!vVl4V)> z`7_YYP|4Bt%WT~~7(u9*)DmM)zor_zq^Y@M9a&aFS0uydvoc9a{)sUvot~A1%|E9K zUcbNONB_%5qhn^ENe|F23Kqb|02#~w5(0o4i@TCHhSTVhB3@vzvDN`GnvGB=%;B=b zqf%OR!Vkwwf)Vfe-IGbTIwM;gj9kQTBvC#>29PfdK$O{vJ@WF}kHxwZ@Ro1UZ)20yW-!D~I)7mCE#xSwspPy? z)0!H4oB@1Jwd@rrxu4&S>@7zuT!7DZpJiYO46%t zpzd1Ve{2jeI7IY3W?L5HGz90uXwXk@3BPk$gOYfW$W>N^V#g?QciJdc^pHQIE$vX7vFK;#QIMoMq?6o(bzFR)%4aqOw; zm0g<{zHX{xte`c~q}IZeb4jjLv<1H;?s7*HjbLy4lB0KDll(+|RX2JK;pdHBcR-`V z_u&utM^pG7DCC3Llaglmm(!D%g%iA8kK%?eBZnO^g+v!t#uQkol_#r76BaV>;DO*y zv(5G7yL7@2$V_hprrXI9+{!s`p5Z`DR)?>iqemR8*xp=OMuqMp8ZszuC#&!7_DP=e zAP>|1a_ULoBCuoLga34qx)J*pskcu-3%P_ej%u$F%UGkOa=i|IIid!VdI!%?3wH`q z$jrhO3>>!r4h=wkmf+xIpHWREo&~dMe3$mJn5a;&EoMujwWO0Z^|&oF!mfg}eb&&E zV^>Z4HqLo0lb4i|-2TslyK(++0 z(wNP$p#GHz92sT){CQKLS#wgGTz210V)Eb9XKkr&Ud+g;+pJ$Iuhk?%QPxs}HvQCn z99q;AlbE8Bl&E2|?r}VMya{{J9tyBmiAGmgN&*T0+&JO;6Bu-B!1Y2oxS#WOX5U?w z`F+-IfL~r}c%0N%5c-A_abb~q#qpywYV!4ymmUgISxB%Ah2d47CaLYKin&2JOOVuz zkcP*l=x>Y{sXP^Km5v||+$_Y1fA2N0uJ=_XQj1x3hMU&6OeibRn8>i$ugj+wYtXy1 zAT=!0mOi!YO}52r^>xTL6gh6WLA$%Av$ILFRpI3DGD%pws@3gJVd#QJ=1|bc=VMfO z?~V72;lHSpMzJXRq#ts2b%roKioz1BIFu6nH8DVg5H%;!9=pi_(7%>V$`9*bXddaW z9GLwz*H)841)sH+>kVu0-UwJz{PR5wj1LBIX|j#hi>w3$8U2$?*in7g*GazCfCZl{ zNiYtVshlgm=7FI8^E*gG^hq-Op4X`rpPPnvFM*FIOBBR}M{@g|Zb`y{T-iTyHrYmq zVs350_mNZlBc0b>0ZIu)vMUfTjn|jWDvsxJVz84# zYxz8CgYo{#J=39jMd!tzgEsT2pH&_A#|=MnI4BqOPM^p1M;{P@giY3$YU`!ya>l7P zTA+PZxocI!zZZPPvN2v($N$}Cz4#62CD=Z9T^DM~-|p?p!LFJt8ffQFh*nzcv4+c0 z*7{Qn%v73#X+l(-@(?A`(?tI%aE>XNcrp9t^N--Kf91){$w^dH*Hp`viYyLPS6#iK zt(lmargZalClnde4b@uL3$Jut>6Mffsj;|KK~ znq2gA=a&W=b7Gi-O+@^-ct8YEIqBF{(}x-POVJWg1@YH{NV9F+Wt^+;a=z7Vmsw`( z`zc=N4$5YS^huH25*=~QBw{@QJ=l^>jR$JU1N<#Um|qk6Yk$~xhdv?6QBp0WZBK%T z7t%CM^ce9tFg$F9l)tyGazz5T>#9>S`TSe#Al-( z4vjgp$5rHL8F-CZ)s!k+T#~MEvV8$tIyX1>s<{KA2VNhqww)c>3Dh-Opfna}O=~r( z%a+N)yQY7T8aqoHf3Hg2Zb>)p# zil(hnGwaGaPn2ATD`*A!I8V7Q3Gq^|EcEc-dy|{! z!-59?3Q2SZ5Z}rLI-=!rceph@y!$g%Zv^SBX zuc2}4l?$C5`SopVRBS{vL1G-3g!IP3cwTS9Tt)s?yE|NG{9I@3Tx;k&t?xXZwbf@R zABO$=6#QkH4ncBKTrhkNxOFLLaSI(H`HsJyBg$^1_91vSIrm0*(*GZ|pK8|10|xCn zzxY~6O)n*-Z!BdT#OH-r zSaJq-wVw)ZwjnUIR`{n$>S)@j@^!YiyBf*sKF@`P4XerV>q!~eT_R)l~p)Ma*o5H7F zSwuNg)^c)NXr>WJ|Ieu^pH%!pOV^>{_+WB6#8wnTBIxjRck#6V@5dwDkITp+(;svp zZfr4x0xpNWLn=ivCYjAiUV|o{J@KJU2M|%1X8w^MD8E`sR2E_*JXmW4WOW927OKm6 z*(|bB-%uyeFLW6!UVo_#k*y>eN$;t5G@X8Ag_}(4-nlhc+K}zIw;Y|9{CmKGBRs~NPubw}1Rj6~W}LzNQ4s2w5s zE`94G803P~baFVutLGl_C~lMfW3*pqS63#_39bQq#`>?A{->*hgPnu_nXveHzj$=^ zf4@NSN>1_bz5t662V#zY%;(pqsIR%u{}^<`A(wnk4wlPwrw0f#wS)9nE$r%m3w7n5~>skefi2> zeKlNZZknBz!_<;B6!-bwkR{H|o^UB|d`Yb~lQY`=^=L}%EdRe<>jQSJ0qch(M{j*$ zTacF(qjl!wKPE(^y|jH)b)MO8iqh zYqd4pG`aV~*7@4=rG}oq!>gU9^nttPX<8R0s>6n!-kls3W$ShRa7#v4tpy2)0RPdF zCmM%35z(}g8U&n&7MIHd$r~$wy003^lOFmlw;K&L9J0AgnFA)InY*U+* z1;7`O3}q$7fk)WC%+`V!@Cvenw7dlJ5+VvV<*P3Uju8Oh(I+kbR?TH*f8Ok2KUO$+c4)_j!@JCc03RG zhJcBobad~#w~tq1r;Cqw$Pc&r8*Se1SC~MP!&lina@0UqVZ05h0F@axn!K3Hh692W zmQ4slBsG~VZ?qc+U;DsabAXM)J^h%3BTim+NX2*Opzxzd?f^{l#jlh2Tiy>{;@c>3 zNHV&Qd}4`Q7P-xrBdY9CU|u=TRc1s!mHnQicyQC zroBmf z0YWNqE?yYidushy1qH~qcZY?vdnp-54FEL=G;I(bp+i2|x{39HKpB}pqw?(+KZfhbvsIDfwL1yDCX4kqUBLz6N z0=Y5#v3cx$_gAm;Vq$E{3DE%{$7UhKKUH2&HWE8~k}K0I1&+Lr`<}%zuyI@nv@+S0 z^Wl2_J*McO*Em(nww}AcpzPBAXK=4fu~RUxhtic9WXrt;9beD+N(%`Zo(vf}dVNiu zJj(kKa1i!(Id7q5+4{oF(GK4)~J1p$a%;3fXCiA5fJQ!a=4lGgGXUev}k4I2>& zEo5bHf+do-RPMNxulG4dJp$l%`?-5EVsR27p30jm6#(K)%fiRc#FglGXbQ`UIUMAS z>+D)u(ek1y*^tNsX6UdvgB~C7P<$_h@_nOKGu+$w2X^~+qig!YR}+yk8h!c?77m-h ztCl4k9;#o7@_?oKJ@MhMVJH>f1|;#|oq|xw(c$l_VdZnE%4R-9gz>=5_j>AdOuHqvM0 z`N<4<`ivbTzu)6deU#x@>f~!3sItNUgDAa90*xL8Vj|nuoe4kLYl&o?x|L%wpWt!3 zbF_<`8T%wG>Ykqir%TKGhQ-wsg!^ziSI?u;nCvdQUqphtkS?*1Es--ns|=rb;?JAp zFSL1QS$t_L)iRZ9$0^Z!R^O_Bk(5L}sUe^j5U{y5hHsjG35aZ`wY?f!O2(5a{? zZdL5*j;}qKDkm|BwY>xZ2g5pb(o>6N$2C}XJED;AyE<(x2dg$W(JK`N&)j2`98Qr= zFK#BjWmu$FD(=fh$=z?f;}P+n#^$Tx0$x$NIX$XMK0VcdW38ci({blV@!DOd<-R-? zA)~5>W=b0ivATieSWeZE42M+c{o2B3S+Tw!J4;4%>^^Dec({TK6_07H z_M46dEj`}bGkZlSlyv3YWlwj!VE(RFmPc49yM=i9oy@s*jE?~H)9>GpZ4Q6PezCh) zY3^t#$ADqIrIh#(-Xb-B5S0G~b+Ki5wcd@MIxicvBmp$YSLrTtbkOojn z-avC|>t2}gA&`t2v4Ek$(UQbb4gnSck_qZL96kknvdQ>{&Qk=gu9AIwJQC=0QNH-a z{m1c>MB!-j@+h_En{!>$vY+I%xDeAx<@kmZi2OTxQe1Ua;)?LbNbub!J2)k52X4b1%`GtU#)4mCo0d z6+MG%TabqRM677==nHld!k}Qh&dvVq+u|Q5HeP(KmTr(j?-A|a$e;z13^9nh-8YHr zrMyJ}q*TQOyIu(kicaM}H;8?D-|8M~l12|qhWh5D_S^OItOY}O9SoOdp(hFjaqJe@ z(=Leif-lJfFn0!c)Co~u^fGYpWAMo76V@@7z>7>_uuK_m!QQM-r&%Gd=6yQ{6+s!D z(DiQX{nH@`EM2$u3%?v^|B~uSb8|)sh-3)zxYw^0xdlrXI2T0W06^Z!9GPAJl(@jn z3wYpOD19&I{9CBQSGMn7i@ybq47&8^1ic?FZ1P94#(F5PTC-XmhWhh!ekb1SQ`&MQ zK1wbX>hhd@d2pUq*+<~bH!3yB*R;+dj4jyiI=5vsm9Y}=Yh5x;gU_|WNV{VGaKCxF zY=AUmv3G0CePl<9J9O{vYQNRp-r!!n-qT|WVN7w~JB(4udaU(5ACL@MvA??q+E_1#p&*BUFt8&Pcf~x;g~M8ft@RarrLP)nvZze{Uxn zsd=@JOqa(++TovuW}n0%`S$J1a}{7q(9*K<>c@uHbOrr>aE9+T6{^$yzGa_k8N}(l zEOr}*J3f*PY4i~t@DVQ7_ocqmowSQNe~x$TE|!j$n-&A_Q*$Ujx=Gb@qR1K8mCJ50 z#qOB*`>R7^FAX9iVRL(HDryl+1-vtn&tk2pm7C~z>&;`q!jh|e!t=|+K$R@-%+|9z zvyGtNT+V5OOqAzOE^(IciL*XD1(xO-Y{pqMy(oKDv9Z@N=NfJb4(&6laW(3B2fGj7 z-kfg3+os`ve!i(lhA+`G+MG6k|*A+i>lK*f|bWbRQf9;+J@%aQ)(ytqtrFIvg~6`}%2 zJ0g6$O<&_IK{@%fk9fAh{Tg*5Y8lIDT zhY^Arai-vF#l3M6-Q3C;!WQOC#?u3b)7^*568G3XG|-G9{mO)oJaeUsGN~!xMYC*t$ zWpXcAPmV+U<5}3J2MYcf+qQYZ67TZDF$-jB^ptQSD`D=`$l+`*eoyRLdTE>UJ!4mc z-Tc$LALrxv1I(tPxpB~5K3=hkV!sdgpI7G;uUdATN;tA!sDS<>X?9@penV-WY(Hm2 zf6=vJF$L;&QC8d}DwT5_5!)p~8MkV3CHQADa^vv%fAB8-Vmjn=ieA6FqsBkiQdieZ~_4A2RneRH%=&~Iz`Y3ll zWgl+J(&f~tkh|@LB^9QqlxGLDP2t%mw5;JfVR%4k&Qqu|bUV@A`wdr#XdGl?FFuYP z5YZlIKZL{?1DS4b2?Z-UPS{R2-5{IYW-%U9)0pVxyXl4@+=cuya=qLa&GPOS2WL?< zCEjatIe{IE{?m`mjzt`om)EshqUDF;8!ga7!NSf1)PNZpm7GEgL#)3US48PAb|;U@ zpW79Y5z|dZG+ydfQr-~2d>h``jk`r(9XsfZB7hG~GOJR>{CY8rvhP-0)b!x^%OPq& zY2-l*Y5->S%f?GYQH_9Yg6-S8$r>i%Pz3UTfxtMVF`l`mqPlQGK$eL7Q(&b#JQ%e^ zQ$OZ`ar*DE11lDCA$&ZOcW)Uf=dUN3>LIAh4Mnf;8<~8dd1x?@g9UP1IWbd_?cEnK zSq@M79^-t5WI}bB6@7!qV<8wkp+AGbnIzl9B`Y$6dsJ zz1Ppd4RvW7!J=)!Y0}FdQ#1{|Kn5Z2JGx(~h^tjZcKJKy1mWdcQ>|k4aU$^B$j3%_ z92GGsA~i$CZAkK2BHqWV!@a7VD9Ju^Y?s~>I^jSP#>1*S&*q7DMksx_|z;dlLts4QytReyHg7y90ds zXC6uRi(<%tOq;g!+K3Co)2@6QzO;AHU9qAj%tYJK;9a&v#hUU<@;@(bx1!DMEbZ~A z-;%l8IguA3m`^x@lW30pGQ#ZFkj#mvY6;W8>mtXSF{$9?o^q`y&$ z{Cv_v@;ToWn%7fiQ6vTs>jZN9j4pp*}gCD4i5)&xaj4 zzMM^&h;HxtSiRDj4lD{`#U)7yJo@Fr_W>$<*2ViKQPI9X1Nc#Qi7jtV|0T4sa9^_LTPN3Wg8b|ZPOLVvOp zHqoXvNk_A;Uh9}^j_2gM%w?}zU$#VQ0nVC+I`E<>bu=cm9I#a!`-uPotbHNoe#NNI zS~j0=E;Qp^oru!yBJa+8cjzwQ1S}ZsSm4xzK6ex>fMcj63G#C*u`c{{P)gQHS{3z9 z&SfxQ*CD+2iT`!&v+KJ4G{47lIM(4T(3E~X)gBQvureY<+5XMxjJ~2(WXsxz zFSQx;Uf1&YCr>)4}T zdLp0K^W1~EvX1j!cg0h1V~niQ;%La^y(IH`?V2IAH6SQ-Am|I*-U_ydp-zg;6>qtT zb*^5BqHxlba@4x-K~#f;yH?(U+u!nkx+#Zf8HDpFp?JGj=bs&XCO%2YcxrA1ZIO*@ z1#Ao|?%JX*rrxL*#@VpS_e!zculD@3$;EbnRNPo0iCS}f4Xq86!Z}ty8M58g=6XUt z_RTSX4LvrDqW2V9o{?}9;xo}GELz z8ZMVZCd!!{W|S&z-v0czzocY#8y;7a#`P3tDRL={A6=nWp>VwyA3EPwpk%!?agz1_ zBu2*n;1=pAc)@$N&Oj>MeFq@AU@!=z+(>P13?HE`I5hqkP}~$2)oTcoEYs!M*chGp zJ*egJmtZPifFoID!4mydoTO=%`Td+c5AH$Ba$@KHMY%-7Yb}hl!Jj%*UGGAwh{1Ij z{Ca>YLq(JyyVvsLEK4&!ZY^D9F^=xrK>*<6zSF^bHnjXpFabq5SXDjbLK&|AyD*Ug zAo7Jvc7gsHE+1;xTl^6Y4xop=$4fa587wdz=k>C!Bmw@+N>W0GtP>SmuUsXUYIqO9 zyo7UeWwLM5w3Z6y29Al6bvF1gN{`LI{um5nKgj zhP{I~*UlRGWekV1iCtC-*2IksUlSDyhEKh+!b&l?JsU~=uQ_(zpzYo{4R~I;_-79a zMARKDSFl@UmXow6$K{06g|izH#7^2aGeCXsZIo-yB zX!Ad$zMvbW#P%hyzP=vYt=f6<48!v}tR*;t1)3?SA(9Qj!vcKB4KCY!-ojH-py!>>PTt4Y45$3aPzDXlX*vKGLzqx8c-J-5i>J3@;iJ%Cs~)^Aw&pXeht-FA zwgm=}i@+3cR`(CEMB7_-&GX6ZC()i96=7z|F{oW3;~X7ux%GR4q?LOZ@V*s_Og{F* zwv0o`OlIKCq}&e{k^0cjPIp2xHYt!QiBrS$pV0jTR%f5^q)nQ}*_03{MjpqRZ_?)Z zxl@H8UWMa=3lyOLfdVMGGpjP(+1n%-tv~NDt3@t6y*`?n&c<=P``*R>+7gL*9dFS3 zPGi{CC+pbbfo%dofx3n$D8Y;7*KKbNYV9Ne;NzCz{Mg)3$^c-FI3CIv5S0@fGiJQ3 zMZ`3$G0ah&X=;)EXm7VF!c1q&qG&%#SGKdUfBvj|MlR&X@snq6-UWl=73S0O$o)el z*XM@p2%?2aw#6Ju^zsq9J8sz;av|FWIujbQpG7XU?+ogQ_jGp0!g_Rv73XBD6Z#}lbiCxsgru0bK1g=Q44x5X2e8Zt09-aORQWN z-lQt_N59c?`*Mx<>dyh*XCh6v^@v9!lrp?#xb_;ZY7nM5 zIz(Y4oG4!AO-J-8-IspNheO|8SZ#pxj+Ea;`ttr{;}Snq_BP{QhbKIC+^y zx%x-@Hl6oYMl29V=UqJ8{;!asC(A1$Isd0f)~)nXO2gIu(Q5QJ__;G^^n zG1QLp+899SU(DNrGbV~s)Tq~%sh231I?2wOQ;>6eKL8)6DF_PNZNl^t0l}n3gO8!2X+@ zr&A)kLCybdz|`HU{)y-AY5wOyjK&lpTKc_*JnuQ=amG+GRPv8ChO1;|kH5kVOcl4} zs8|ytlNJ+}wQ-BjnyIPz^UC_0IhwzDB)hkoF0~EM9`fJydm0mvx6j^H+)B?{c&W>) zf4*_lUXfv$W{my-2aJdJ3`y&X(ljRAH6mQnh@sT+jGQT>{FhS8)XJG=Z;$1CRgG9Y zWJn;~WCaW2n$x@QSf$ZQ`zd%0JX_`Yla4q`FHEky7O9^8ME^~U^^AZpN|wgP{|vI$ z5%a?WGCD-xf)F-zi1M25<0sTWMYs zA;x^?fMbGCtnEzU8`+?jO|!Q5y>-i)qVJhbT8Nt!_qZsVIG7p}=Cc|C%LkoZr}g?h z@6kk2vg*5A^Rns^BDoqfWAuzWpC(PkHIswioR6sa6S`QQEO&?n;dLYs_ehh!&xslg zYLgsj7Dvg0FsNdb`dn+-h=33yXvX;x@qa75z{_R)ETUWt!^dK@V)vb~Zp!5m zmU%G+IFYUN3{~v@IlK%Nl1*&po00ijoIJ3)!^!P{tOI3@g)E!+o6O&kccHUX( z1~6PxxhB*WV<()1o4AK1SNl@I(kPBpdh`M2gyZ6d@93U;T&d;SHXpeq_`S&X8|3Gv zL@U_+ZZU9T6-aj}WmDq%<{ZJw1@xvzIM$??9=W>KKM4qk{%%Ds%Y?sNXim<01Xqyr zRXpcmxYy(q`coDdyVa>qQT@=JK@VFK6Y0x2ObvwGz(0S?Lu}3%trNI)l)Oo^h8d*ZA(P6RP@x&>uJWC^_P8Z#l zli8H+wm7fBVQ_Uj%5~jrF{Zc2d$#>K@(J1)@1Bk$7_Pa^;8RI#c;v ze~VRKwQ0u_dmn``QGgulbuXBf|M| zZNfjxt;%D^jvjRN1U;loZ_s>|3Oedw?Eh$sf?|ud&fnnnL@y+vwqas*Xz3q}JdwmL zvx^tu>A38E2yJs-iHNIfUI=yyh%WE7adh`ytEvC(vzB%L=K)$;B!wwD=x=c;e(^1@ zoW`6oaF4Ro#iKA!LdJSjamLme~oK0?g zK;DH}ao6}}UK_8KVqM7}8CAU_^HNhmU~H|qqHQ5=;+61T0XXB+wimQ$CuX&C2Pe#tv>AqzvYt)vPK|~h@y?MdG;_)= z_CtilV>K{aYZl3#zQcHnQiuG3yOgiZMtNzpz3o}V6B^$-M zz0(D9Q3FUmKE{3gUB+x?xNc<1^kH*sY)ez=vhTM!ctUso4W7%dVBnb`iZC4yKq3D~ zA>8gDwk9~XcVor)*zMv41L|;tj)T76TTB?_Qd;rS_aAv|7)APA4=qK-a}F!cN(+qr zvmeY>`5)f#JbT|bi3E7`E}|gc&b~}9;A?rz40I1{KMV3uvS%MJ_nUf66){#~&YJg{ z&{N-2Z+PpL;lXCI(0isZ#_e(NE!rhU_F2>)lsubygwY_?Gn>OzrJ64@Hb?%Kn@Tmu z{%!@9{EjzsCt|c^`eSVg)B@S>Oq7Kp;)q74Ds9$ku#@}Q;>hgVWL7890N{7|?*%9^ zK!2pjC`y8M=^O1X#++KNYzY8n=7pQ@HZ`Vdem9M$_s6n&53`u0XB_=#=&!OHKN7p3 z{97#M;u@)3JHkLE+Enoxwp4u4p2OAjI=^gX&Z}(KuAzaaE(l@$m*8@zyC`VoOuuLG zP&ls-&@P>x05Xr5XZ1g>4)x?X`p^rWr!tay*<@S@w@$?@?5p!ljFp9t2)CrZ^;|fy ze7l`(3aL%l;hox8iO>T?q$aU@14IB?z#)bKF0(}CKKom9&hj0mVa%DLI*#lqo$q5Z zeM57rOs~0q;L1+TQ}xnn~=iJd9fKVRS%uNSVg z&7g@*y|jmmUx-R*TX8t2WOVIrPi`yGEnT9jtDT5HE| zc+@g?YF0dlQgxjHe{H$=Xrg~OGt(x%K-N;AYn*mffgnnP+biQ=*d`nwM+(`vm|N7# zEVMq2*Ak^C?YC+rNl`KTQ|5HmfX88hk+$Pv*~L@oPZ1?H*kkr)5liQKR1=<51XOAq zJuKC!B;9L2j6FCNTjRO+PhNmF!Li?vP1cfhjF>-jEx#G_p#7j`ojv!v=E-jfE4JGz zC|SLtyZTc5lYp7$`Q%y3sU{%Bd6uD6eT#p)_KH6~x|X%jo|_%;0Snpn{u>}LPj9(V z4#z=h)AwdK)NfILV;QhM|1Lo70}GJ0JMti{yof6qlTzKOE)lUTsW-9?B`@ra3oHR!B&~_PYH`vJlr+Dkuyuj#=`{12X#C!z0{n?KS`B``NziG&I<46UFDsZmr%KjGBvI;c%jdB>Lsn(E&Z<^H6pcC3Fp8)9uZlF_ z^Sfr^r;OZJT~@B}jx$u0J|Qn=m(d{qj^b(T2vT?$9zm9Dlp|SLrWnogP^bEh4I_Swp8d(R)(@+D1C;jg$L)!nP8YAltAV z6@8+c%H#B1)aoD|K!6#*(;@R$0>HC921>MO4xzFrKebn3 zm<4%i1cq(M6~Ov9ZCv;1?bP<24oe5J0|0@WHalXYa|mAB4jw;80Uga zVAjyjJ-UQg84MQxP;QdaO+wMjJK{|Q0Qd)sqh>kSoyb3;>5yA<-oBkFjjEp~cRM!T z-j2J%20?v5Bb&RN|DKlUNM{~ckux)~)p4T0fH+)wP0ruWqlEHsvWcIEDt>4;ruLZ9 zFuTgf=a%s;Vk=znrhG~iAq)pTS_skUd%{GMpfv7?0}bQe!<_s2Z3{JajozL@eG3*k z`J&xmL#gooPw~wRGvDm)^Sx;Y2LRyLG)`Jyd7Hz@$@%z$KM+=E;hiN%BE0(XhLP;W z0}5;&mLtTX&YcSD~T|E?_?>i`Q;JLW_gGw}3Gw8~t19#rL_?^3TR3FG<`|X(J^z1&kt5K7S$ahEmCwxApflc@qqoKs0y|&)= z*WL_-aHs*XcduXc6!owv?KHg^9W#`NUGP0}wY&oXa_P1??9obk>hgS5%*8 z#SPXnKDfzrS7QgdmH<9URA1)QWD#(@1&v43Gm@^V&(3yv@?piJm7xC>)b{lL%?;BM za+I{_;@*2)eOvRS$YQ^|YW&La-%RG;rt?3K+ppq~cE~&#*F|=Qw}2QTC)J(imCtVy z66H6+wC=n_32(jze}D!|=j1jd`QiVi8C;c92A8jFUy)>qkQP@Wax+6$nC1KhTui?d z=J!_5cU-XaPe6x97Lv8P^ueQtvg{7OpC*Q3sUAQg$ECZ4y7wqPpxprlmA|nuvEu3L%H8}OUA+S=hMA=M^Su92ATgT4o;TF!Qv7FEe>nW8`k{w^ zcK#=I0A-f%5~j%ub)Z26=2V{#Wr0bBIpKbhx5G@TbK+!R@TOIpoE$Y(*jyF8mdHOI zxLOcxjLJdN<5h;MJdE*BZXiQslzp&l^zxuC^75N0{@rt0gi+AMJ1MIV62IjM^S{m4 zUEJ|x9Gitb{${b?gQUSl6_xKTf=L-|mvc|2j}14Lx%e5(43t`M=i+O7Y1i1CWz@?- z64d6bkp!tLStVc6q-Ha$Eb@v^CoWM;A3|}MWsYFIddnET zbO0$pS$gph06K`em*qx;-MhQ+EK_fc2wm{}%qNJfi5f<~x^Efv9*$~gN)&yrMNXZq zaJ^|ebIb;{YefF2)15({4u=I_|S5+1vZ)B`;*_3*&Tp0yUB$?3s@{6{Yn|K!qOA6#( zoqgDQVpHT;i{uf;MZ)>~7fBv!Q4=rm&56}KH}SIxoMDo(ctAUjqEY`bzZPL@LqCoz z;$P}JFWB%X*n2n_rJX-6{1p=o(D!iQn|4^GRD>>qNaZP2E`{T@pBnJ7mzxozv38T< zS1}HiYgN-dN%3TWngC2@$CWpdnF5LwDv9@9PwphAvO%W+TJv5y=K1Af^KIf+&xEwN zwa0qS3TAOBP#cGTd98LnBLml8tEu>1oz5Yj5$k)J=4XE9fPH&c;n&fciO^?iW7G=>Md2Lz3m(Vr9EsRi2sY>i?c}hHF|AHpe-S4eka% z_r56pfgKN!KnzI!Kh~-&ovHfe7XPG&O*1AIWr56EPp~%ZZ2?M9j$R6h@L!L_b zbK&nTimx3uNZTkGDXr4L(L_igd_K6q-)DO{LyH@6QOU&jhRos__+9T&Gzj@n^56Ih zzJ#4&SVu(sjf9o|iirO{@;T=Dj`ZE0Vu^lHM*z(%1i{h_7R4}WJ}{ui%B+m@D_cc> z?11gcH1tUsv5OvQD*eTB<*{+vos(M{Ir%uJEkoGgqJZjC|QZd5{^ z20P9x2PpKf`uhGu;&mWVUM6WN=J)oX%`~lR3@iM{0xeU9ABfV&Ob_W|L{zUQ+`!^T<`ARyOTYY(G|uKd;fvD2Fq|bGIp2yUd+q{ zED{anZ^=2~Zi33%iVHGCC2`$bG~=Bs0_0KCYpql3G}s7nmz%!U9yZMy8|*PBJm}Mi zcT^zdaB4EWP3fC2M&jY+`TDT&u95onre9Gvcq+w$Q=*~6%v^%_4^x>6)b(3+5ugfx@E+D=7)HDgOl&8GfCu#g`rp9?&pC6P`#g0L-~LRTQloMjDWA2@-?}j zLP=G|Y3QOZXbu|`sEXqk5cY!X=5IOGaLyMn#cJnf)p5ZPntgi)0N&S)KFNl@+gskS zst6DG_@r^OQv^;_rx+ejs#3(K(GKW;F5v zfe}uhWmxyUa=_8=T`6l9TE8 zIV50TL=FtOD@w6EqVPM5qo9(D<5y9_d9^TdLVo_mg?yxuMzF$&lcR>s!P?TE+x6sA zMgj1~LUuMAQ2PGe<>AJXkqdU=l&#{ghQ6wU-s$H%aLtu=SG3GPuf;+aQ?$_cTjp-D zhba#>3krd%;Im~tB8G&@$EnB^ad0}($sOTg`TYd-%P5Yd-Xrdc|C0Cvpkf!E@i$nF zL4EO#80s`hFqC`823e>WoII8^r0V{;x zl@c%Q4fI4mGg#+HY_Hvyx%W&ktHV%>gUGI4CMY!@s}R*KEmEU9chtvc`aH@A@*!9B zuBJTYMPTtrmLzNBKbUfdOFg$1+=Nt$wXsd`b1dW=5$O@3F<5zZWQHkQe4 zK3G?E+q7LvmmSoh{!74Fdwm39Vy{0Z-3eMH4-$onrRpt>C6DJnSWxmunxeP&457>a{L;mS_h%dh9 zBp{y-ysdV$-YL_GYH=y~G=#$Tp(n!Rg{al4;yws+zz&Na<{SX<72-;XVaMgl#H$dZ zH|qmd?E_vfp0!otu4vqXyOGgqLUQ(?kd#;qD{RWP5#qSEnUwqX28gZ+l@CTWg%-w9 zO}3@;VI8!;&LCxTHLc* zkgBT$lq}|~<`sl%OCF*OURe;hK!dP>%#%{bs$^0)$Ho=Q_?B-c+$J)rLVtku8Afk$4x$4hP)mOhgzFKtsK*`u$2ZBjUP_Xcd1-xC9SA2kW$VyLD8B ziyHn~^WO~`wwCzOiP-;D=l*#>6rhyyc`**@Z{yQ( zmi9BCk%|8seZ05}6CE`oAbPonA^2m>rRxmN2NNEwe~%SVlcFYgZIjwP1z)^NP1f3g zT*Zc-CVLN;+rP_|i;_bGKmfjXS~O|IPD&Um^Y4C(BqA=DJUkr1@bUu%EO#ZAN+L}{ z2bh;rxO+4tJ<`wtP+Ty+4F1>@kxmcPkM#8r`uj#O$^G)eJ#kjAz4|9y!WcUKKgDu4 zbo6qCR@EUIV+5b2u1^`m)v3F<19|AqeZm>BD1d{l!_rw1OO&wy!tqg2R~*6VbQnBt ztDt8U$RwImF58K^MK0_;wY(9Ov?1k%B>$*D6Oz*i27fpOVo4ZH?(Tfg?o<4uwqEts zwCB`j?Q$-Y3c(@8o>Zg%I-SBt@~QxheC%au(@5qdfk9IUzDqex2AA0ud^_j-rfA4H7T5(>p|G zcXht<46tM%w`q8I=jjGe4&xsuh2QSh#E}BOyRT3pE_|j`ytR`mHKLHNG^~>RiUx z0)9$OjAzcGY@e|DWJwGe!nJphw+mRK*ibxomXo}Cv|NB=sNmpGW%3O-A(o-@EL13H zEXA6!izEgy8HMpTehm^d6BfB~lBGWhHoK7JkCkB8fb$7(mT^v|(f`)7hJ!WV*XfwV zOfT|TJyl^}F;2(*&90y%gC4#5aol)?o4XUTOZn`4`*?81%_nY^%ba){ zxC@j$op8i%XZ3X{PJgsZh(RqphM*e*&dlZ$ciA>XgzJWukv}lfuDo5?1}xW%K$>Zi zq5gK-pS-z!_~jL43Z;7or>n|XzW!6}gadq|z?S5=b@(+*&J^=)dvA=9@={bma()hYHB)v~#p7HUAk_ z#X~zd_=auB+Cun=B|V-ta(hS>obbJ$qaV{w-+T2To!Ib7x~B_}A%kgfGRbK4pTfu{ zinafx$EB0|XJZj{NAUlv&!q+Pq6JpHV%FhMR{h(?@^P_+jEHw2`|75IopcGHWs2p0)JDgJW_R+iqQ&`W- zvmXtJAIAxz;NF0|)oHXG1&wYHBZ2=cA z&;5wul<6}wdDk97N0+|fJ~^0^e_x3_??|}!lEh)XR>8mHPV+9=(d%ps!Ph9%=|R1% zVNbHbe=!ZTc^ku!e>LuC?SEAZQ0kQgc?5a6&iG4>vXjtl9;04loz#b&sYDIpxsMXH zHqf+XPX8pm4mmi^`T=G4;jh;PYB)cP;!Had*vxox;mC8B|EEGggE0EM6Gks3P5h2S znp^*i_)?elP(QUM{*26=7s_wAyrT?=PQQ3>)-F#A0-~(3L-zNlK~s7R=sdq+hhSf~##B()+VlFqgnP{iuw_!4aLD9B< zx+@D}Ga^Tx=Bib9>`qdh%n(ZGMr1R4cbY*f)k?Wx?@W7IaDXb>ef)GV62KTuc*oRV z3}Dtf9wZGTt#5Z8u_6OLsVwtgQQe!cX>*f@4EPvtZ_>~@!7m;!irTHFT+d}=cPPMT+PxDh&F;rn5tmV#8$BvzazNskOv!Z(T+Yh zZ*1MpLX8buNQeBsmWSUv=^@w=Bx$;}Mm?*vz*j()M2_%8c9`ViI=#gLV;J+NG6U}9 zYBUlOL}#^vrF6)`6k2P;j@=)q@VRah7Vk3|49Ojv$xtvaL<{fX_+(0T-6q)kp1FhO z!x@;M(E%v)r>DXRf*yrRG`wNEdzTifvUMejzI6{pUY~%J0{)z`jcMBuy|Qe0%3hlot!eWh6%zSPCih@ZKL` zN(_Z=`en7+`PBjI2FqSgX47&{;)}&kUUpm(HOsaqCyvO&uTH~#R9-+)f1gg>KQB2Q zYBPTgVOcEFpn=2`m}5)LA1z!6(mgSe%f2~q=WeTS@N^D`l$k!a_K$H^lt~g`Ad4;e zT}d!9$q;NhZEC?e`2^PQR`ts89R(NrtAJ?Z|K7%e{y1%QqwsPgMu<^k|5o#Z#`Y43 zCs6(-swS6tpVmIGx}X36rk%O(q(6vVRq%U)J}mN`oXX`iytH2xqr5ib3np#FeVVtE z9Xnpp4yh8uVL|VmL_gf0CRySgjbr7kY|bxwjO?sVr9X{jP3Y}+L0S-K^ptcgH<4M) zlC;s`A3sQt|4jq#U8I3R4y8V~h1llE2@=GQC)eL$ zl8-}cqw=#f7Oxu<=PhYfBb0%!Hm`*Tx#HC>R?KXj|EYoPHFJ8QEJ}II9~F$VBtiWD z0ccey@`(sV2U$vf){Ek&xh(F-wc=mooCOf;8D0e_u`kaEW)-u2($bEA1Bg2}16eEb z!K67l9v53ws!tKi-~$Db!r!#o9`Brn8v~O!kvXDg745d!Y#}j)%xD|GYc4dcWxAsn zjAKo4NYmj3<#*&W$2GX?^?b+ur|R7A(`;#>7Ce>hULLt+eP>`h#u68gyuzWK+Vo8bF!h^sB^JL;#TXYX!pJ ztRF+7TMd5e0U^iL>SeaAw5p^m^G`&0J)96>Z@J47ySyA%dk%EZ+#~I zs%R#>kKIG6z`0r=QVsr;te*)R+F<`jz8(tUHc0(i*IIia0dQ3P zszjs2-7h*!zh)>Z-R+fasfpRo9dEB`&l`Gbp?jv?9=6b((3adgGgcx!r?bxUr@*V% zw-*x7SL!w1gMm493hDWy+>Qpn1*l5x{7$3PJh3+lDo}^RAMb8vh;q1Vt_wKV2`p?$ zq`}4o=G^TZOiNdrar%tJ$p7d+VXLony479!A(7Nf=s>glS9`_-wvL~_c!?%j=x_T! z`7l|2TC8rkJjemr&6^7MYouB8CL&B#p?`E|l|3N;7~j=O!-pbTZ6h3p@#fF`pP8{{ zIZhOte)$5FA}L*ni0RNX&;zj79mDjgHQVafS3!>~Ks&xb zYo>uw4&A64gny6Hy~LxL2O67GQPu|z2==2k+jmC1&U1dY`nW5#i;|yy8W6ndsXu3fWs3HqN12t#1<#4PzUxFlwnL7Q%P_)Su=CK1p zR&fo_b%M8LbK;FbKEG_05Rr_!ZB-6fGJZQzvV4)>JU|CaW8zLz5U0+cy%}?7aJZbu z+A^4AO~r`BAiTxkFvx?Gk+4zBK-0FELQmQgl|=3!8K-M3sNkj+IV@`;K0Aq8pM(6K z7BAO;NzlG`eRH>UwgHpF%Fl9QP8#1IXYET@;s2&bbel2Na3V(r)eNzT3s0y2hwBd2 zR%;(GYM>@@7GDosNew-ViF$D9of@E?42?avI#I8R=_n832^axw%Tp+y46fL z612~G%{f9vw@I*_DH%=CeiM)f@ZsCn_h=j%*Bv!39ZI@KHqfB)a$e`|oC>>3koO z*fRDhZ93-!9Q%J@Im`~E(0GEbjVjEj>HduRc5iSmz1^-pHx=xA_Ft~0fbnMkr9MQhmtibeDIm0=-4r8ky8ArZg05yVnU&}OKtm=t*1%I!`ZLvnMVqf*LrVe(tOt~ zrFpqoOt`2Oeu|^ik!Me4WfC;DzmKJh!owDYX|dwCmltPcE7qTmG6ss$ zmRdUK5Qs-7txYDw7wykBoRjWGlTV&GA-!eqw6hMho3KC}v3}^7#zx5s`tN_EOwPa`u^9Hsn(Fz5ct|c-K0zxsB^tLfzgY0MT@4j z8|YQ78ia{i-*g}6aJ|_;Xw1h1AvxS_m3~PE5jIHBKIRv_6qn7ZJ=_YlzQ}BGq;BgR z3zPPgRCL(wABt2>jGP-|Sjao(u%J{A7c?tZ7V@xKpFJ;-Fza+I==l zAZjGxdHMfS*;_`%v2I?P>;`kWGp zOc(&#p8qVJXg!e31_E;yoJ7g#fGC73tq;>QxSFP@PPfMcbTyK*9BLg-;eIz!!iTMn z&M}@m1jX_Ju-V<%buMaGS^&TpPez?S(Vy9jw52iy$ ze9U!q?`|bNqu0eKsi*#1suA5r&}a!~qsw(-_Kx36KDTp(JZnkln1ihvv{{ti0+}lN zK_`kuyq#HeN|OulYi@xp^tY(r9WMB;O~joA{K^^=dxCrN_!b4m_paHuF-lE?NnRmZ z_7Vo}mKogSkA|aQ&LRMSC@dsE1;E7MV={vHivvF=MFH}xJvSIV_urYjHlflZs^nP( zn==m{h(CR?Pco=mijD|&np4`8c-ie4QkI&WDtq)A5dw43K>Ehaaf4{YE3EHuyj1a@ z-lXLA+>(XHDn6V3Y}D5d&}tW4UklP$)(W{m_lY$f$s;}xYYh8LQtu_i`Tm=_ZBP~) zeQg;Vx>{MAN#ztWtFC_EBR4$BaT}_9UOm<0aQ~1u{+k}tro+Hf@#eb)LWgtEeVGj~ zV)3Y2e+MxpMQrli=DTgzhMJJ)ovJ(lMj?Tnl+#VR%sbcj=_NHO`=<-57B2VB%AM19 zn=#`GBDGyrbRMYvb#>#plVHo!!xkCGpT3V904Jz~Rh0;-PbSWJ^=%3B=z{{cxgFLg z(tbM5>(4K~yI=NxFZ7fvw1~H?$~;4k@NoY5;#tU1tD}am`~Y)#ZKSaV)i2ys!OP5H zuWPs>`UAO*Lzyp(R2xgCIDsjExIf{lK2&t|KIv7flFrGrY#xGg6YC#_3CK=yC8AIh{W5$J9v%p4EzAfiUA%G(h>Xsu*jy#4*PcxT zfjr#GS6#NHR5qI59UMp;mwn@zY*fsKAxOBFWRVu#UF4a2yxN1_%5Y#=%I6?|1mxyK zK2Cg4_xq!V1?Bi@nW+0f#Q?eLV6sz@6P53YOpLL0YV6V`IX%vmJ1+s5T#Dx>EAhjG zK|l|i03alwRx|TOY5kFXHg*Q9@}&II;IGTY7-ZN@?vzp6#3S6`ibY{{CdD{iFdS$n z!b<*1rtZdnPa&^wBqm&P=)QK%k?>(c4m_TJ7PNe`^vsosvCHNQ_g)aYRZR_@F)j1t zS|mMccaZ@tqISQY4hG(rF#rD4cQPSC_KeF{8g^V-3R+Ct2{&$}&v=a>b+RHN&PTQ0 zzn6Y=)PY)27x}OxWy0WA5$T=t9uMb{IhsF&YulytJ<1~z%W&`6NPa-a#nt8c0+>&D zT|?5rjOT|vBv`Ks^L^VG+E2I%L1#9$KK<6Z1=e>mvU;T?_3k}pI)GJ0)W8<6^9L`x zJM2I#7DPaRK4J2_)Qxs-w8yGrH{pEUpZV9{0V^todFAjX(`5*)34_PGnSouz{Jrgd z$ooJ#k6+&1^W9P{OOYMjb*k#T9h0fblB8mFBGcfDMk9ewxMX%@BK9R-WsEHVu)j5! zjk$D{W3T6wNwDq4bx%$Yw?^$;>mlAxA|-AmobmQM1X8Pu?;Z6mody?Ofa!;<&o3L5 zH)4_eTe=UhRFVTAkU!++vQy3xrFfIf-YQY$!N`)!LX)wCGNE2qlJtN?$SHm(EMxWi z=$P`g5_dtEVevtg>YcBoA{v3Z#g*7N#IBzX zu5?X4sfb&(AyJU_KBkF5*v@$<+M~kgIs$Wc@<#()ZLBAuR~s{W)j-~J z+Juuc$bw4rb}k)a8ft~#^k_Y&Os{=TqM}Yhe{^*q~yv*W>;_8viW{niFg?D1iaG?+#CNjHyZAfio4&uIE3wM|c z`DjB%wvJI_EsiecVtq_yg=dr?DA^@H(hhz(|M<;+nDs$)pJDp-X5k5AByE7(4dGyz z?6t_6OyrjJD^ihblYV=vK(}8x%wyaZ5;`>0j;FuJLiLo8F$%DnKeQ6pn>;o_OjEt* zcXy^$=Jz^F>6DjD9oy4Sj61yH@6zOM;-puq@WZ~f**0cUO!BeZHQ9>;+zrxHEDf1A z*81Vwm%D7DCMtQCy#9Fjo2z;fzTtJ!MH5iir64|L{d`P=*PEW$EPV|I{c{$Z)Jum= z`FCZRm-cB9W48%&Sgdu$B!c3H`9*1t7T5x#3BB?u6IQQ7tq|&i{j|nRK_qi0#ob~&?E-SR2J@d zo;o{y`2amC=2vEZw67Bi8Dx;0D+k58CPI)Ge{oWgfOK(niYE3_$lt3vU4Ng+;)56l zx_$iX)qbH>`at~E*>2Zg8+$8q`9=JJ=%wP3o6J7Ec2cEvVG8WGGrBT`_70rM&G{CE zO&W*;q^pWT#oO+jtR6Cfx_7`=pfqmG-c1nA?S||P%8zYgE=hXM`>?7!7dEE&7b*$8 ztmmJM3>|XjS>GHmz!2YRsXkSqk$eCuWMjakh_fY{@For+*Su_i}`YVJy7>= zui2n6kNT6F{897U%^jQ7FqD<@j55S-5hiYbGUYy?A!TPnI!qaX_j*a;bE9m$)J5cj z_lubpkYzIfD*tyr7H>sR=?fQkpMTfVWcCd9KJ@3fS`HY#?wlUId_d)Zko(T(Ug(&S zzveX1Wl+Cd(c_2|G$4daANKtpnjy=HDwlf% zi(bCU$VfL)@$&`R)zdEUFW`qLy`q2R6-$-s+}?|WB+8qlNLfdIxS-h8Ov z=bxx0_bF-tbI3r06NcA?-!s;)SIVv@ixyiqWm39sSK)X-@(=FrtPE?pvG4wgm9`uA zvMJA|WAsr+ZMZCA_8*il$BT-2;}`3NXc!U!$AuhQsB)U?f2nrv&XCf7Ji3x{Pi!>I z*#nL~xFZ5AoUff_jN`{pCl^%4{~X~G&60r=leO``mk#RcT8q>h)rS}Ldfp$Y284ZG z=EIge4KWdIiZ*idLjxs!*t0U-1%iYfXprFHU>d#(+}WPS@#@pS6|(?AQ#PTtKtXv| z=4@yD-Q(v1=|aWv-;H(1r*Vg#4sG8g{_3c>jNtLhFFPO$K&T?%Ei z3v|EWtcOJ9C%#I2m-twxD=Q$b!l_oW!>&5KC6=LS;%Y95xsL@*hP|iyx&FoQw(h?w zU~1M5XFqF7m63q!8n*lS=PK@c5f5;2J&2RZ!5|FSlwV9!R*?WPMS>sX<>Qq*$f))U zTOBw%4I4CKRvOVO#kh=Oyk~5vWmQQFm*_L1`9(BTmhH5jo4?Uzh&`>cE}?f`#?Vdy zChoCq5nJHRt@H1{Mwfd7zEN6hRq7A^*rX+oq?J0I`B`m<4XaG4hH&l(n+IflRNh`9 zH_}0F3_q*00e=+&%SY=Z(Isr&n_W@hdiOle} z(FmGeBYZ8+pjV?*!#Gzij=o!7ms_O!Rw}?H-&bEONu87hXa|?T$dSFKD z2*#+H83~@$Y4wz-P)#?ROM@g*fR-8Zw2CW)m}rnepzqJzVepLlH@bY0C_)*5crMh8 z^+K*!?h+vD?R4||uf*uamjgRD%jWV6uk4i$UQlnyJ>lWwV3pJk9R*0h6Z0^n8=nN& z>kZm5OOkGH-h#OXkXpCp-p;(_!r_Nr)zg6arm#cpfl~-~I480)800lFGr792z+(lR zCsGhClysf(mYluQWu3$;7(pM0Sze3Wi6Y6$O`#7MesDZNZIf2vZk1qTyM@%q@I@sJ zp?zqaG`_0%wd_m(`QLV(33}@Bs@J zwkw4h0cXE%t0~eW$BEq$i=T@?PT2+Teeqfgfc_b;oOne;#@9P8sSaKsC)f2H0Uo>1 z9oiJv57>laP>1Pd!}UX8z>^PEcG)nODW)6 z@l16<)w(i7lIp-}iM2Yhv;&1>$ti)z+2!<8uJI6Dwn4=DB0WUF8t>>lt#n~WZ3C;? z^>QVLd$klN9RnGtEoVCXuCOt<;cbb4(6VTib0&wA#a0~>KHo|0%+|if z<6Rk(oz8UJs>J#4&Xta>B{~e*N1z)H4E!O14lZCMR3(CWYE1hMGLe*e^h}yGz z+U~Th@~ifpf%{8h46cRym^*QzEKE?HUd1lc+5zE@3N{ywn>MDLDzQ^UiJIPpt>&p1HWTgEz{Y8|*LOXlEGP*FTAVqr1Oq0*jg0`Q8R>ejFr0OO zl{g0zjs!vO8`&a-J9;rcsD3HL-usr-lxMMQYEG zAAD9&lc8~x&f#()YP@@K&#K2u_Q9cdmj1<`hJJ{G1V3L|9c#Yf)W9rr9!!EowkXMF zEzR`zEp3XmS}zFB1}eR~G~XTW50S^=LA(L4zv@vI)kR}~fcKE;uWXt;Beh6xF!aMg z^20Otp_9DcLe*4V&q_yMc|-%mJ2j@5k7l*6TcNf zFCK^AdSW6sVnaH-_SYz=^Mf?Ws{(trH;t@=_j{WW!M#2Z@Y$dQ#ST4l!Z5X5&3q9( zmo&H9u=b5j$SpyFeRlk})}Qs#l+H^e{7?9VQ*<|+AW|HWlv<*r8117yJb;_PfY9#G zj=rYuX9U{q#0U7&#uzh2KOIrES03qkBb;k!ChIo#C9L8 z0)U9pQD7=^N5qT4!tJUNj)X;=`ZpPB(bsjQaJIaf@bsPa$;l_8FL;A%2GW_F3G$SC zZr-w;RLx1c_B+d*sUaK8Q^=l3I4|)x<&It{S4oorK0SMvy0Bkm7XU6=tvklSK)#|q z2kgyc*IxAGzYamN*E@AQuu(7sBol%S!9)hSp=S#(9Qro<)0$;LxO;cGWK9?3v$SSG zfqa5f=*+E$AwABzgyJ6>c;GC>ph=@?H&3tUHsu$k7uA zmxP6sNWsw}HH$Vn1Y=C3oVCQiV-WUus$l$PYorLKPG(x%A;&-$ia`i%BlZ zN9&?s!t#-g`Svd^@~5{wcUGX5{pc`VXR%o{f%4rIxuGM@qs7T$l5mD@&5yyk*6#!; z&nG&MC@Z#ZD7OC2n|~nzY&&XuO`OWdmvE7Hwo`Z@FGSo5g$L|8PO@M6h^#~+y-l7v z0cDOsIx3MfBF*D~!SqkwD!^d+WMgX)Vo*7U?aAg|Aacl#N@@P$d!n8tF>7qCDO72sBoVzQ!j_cie$y|Gy~X% zCg+1h4_x^~!5-#sv?)LRIXw^`rO79c>(5FU_VTmS{~>v;aG*T>jLC%;_3TMRBF{enm-zmez-L-a&?++O{`=eI=qW$t*wR8I+*g#aPCVxt$Xuc1Aya*thEmF_reIrMY~oP&)I*Xgz?I- zp7^%3_@jjTQdxDh{>N7`wnx>-x_{DZo?9=OB(JY`QO12fvv>H}Lw(`#(kqjWgvvl@ z_6c=v<2?cwLiGmYZLcmQ5XTzByYN*AyTG-L3H$lz4CbZ8m zw843X$Ik&A4mnG4DJ*DMTVB$+{M@Bk*W_5u#%kOdLhqXwSx|x|VK=zAehHsvY=T2z zZARW{dV_G9$-nnyt0$=O^zC z|4hmwF!Uj=f2gB{_~pqkP0+^Wr_>tYX5Rd47*t+f^V7SZ`oxX_p49%=;HyW6;d#Y- z*X|rH!di(M{$J%ZfI|ndg~_Tn5Sdk{f_ z)kglE{WYeXCrWGZq9ddZNAlyYMhyeJ!oL9F)BMZqMsk6L{XXImz$VEE##8uP%ZmKs z89hjPGeD8kQ?Hk6l!zSxMDAN-eTpL)tzbK)&ra>Z+;w!_g6W^6?66B9_1pm*J;q9_ zr__^`+3w_q>`Pr4x`UQ*BtTb4_;v^hY$#24*vYUzNv{1!G5P6^-^2V!wdUgEyKXJY z;2o2zaPJoFh1WBM64aUN1*k;4_NYCQ)4{5F>s4DKToXW74$)lU3tVUB%BUy>+nGf{ zp2yN>*5`EL7Dsm1A0;P0zhl11$_R?5A*SZs&z)jPA8J=c$*Hg_@1rMq4>_J^wD3m@ z$Pm7Z{7YVHtn`n;WpOTGW=iKs4iyPX*kn|TNrSv3r%&4zPbYw4v-QtUivbu{*=)%J z@&u`2a?fTSjM{Qu7Nmx=ye%P^Uq_J0XE7f?^&~^%g4s&k#=FIdk3f{vlm1#y*?UZV z;cE4Y2+Sv|y&r#|=YDcK%*rw&kDFfnNRAW3AICOHiV}3}sRF3ZdJeyS3y8&sFHo~Q zM*o)ZT?J-TH<6_nA=}E}7#k5V68GUKO3We>X{~~pG+GG7Yk>!yJ?zS?;98JoT<6K! zG3_=gCWkIlKdYu+!;!`yL;^+e_i>e7HZ}$=yHde1A>#ej&~}%nxN&@Qr<%KJrz05# zW1T8A5OL{)r{lXGV`Sht2T71Xg8RJHlw1;8Y251sEYBD-VX{nwJ_5rmtsT!w5wpzzpXZ3X4={|XQNM#jq6H3J%R zIB2WGP_cie#+t?CUFECI1~h>LFDB~xR%R@Qi}O6zO?q-me>9B5>D>yt*l@peXTUOl z3+U;DJH1hQU$YbUC-?o?Oxs+rGj80^A`O`0Pd-2-^{a%?{o9ADf)zPML*kV zIDxi*To;l1Mc73mw-9r1;0~1bQ&}8ixBV<}RPTqAJdUYz7a*HZrDoZrcY z)KO$EpMgP4Z;Z9$czZ02-T=|@arvPuY5UE#%TUo5na`@0o>Z@OGPpb(Wj?2ZzkuM$ zeKnuWc!#f_JC>z~Edi8&b(ebMrnTfR{}g0`WDjbRr2M2m2(D$5w2V4mY3!{99DMMl z9yz*CFOLYrl+DGhu(evbH?li@l$f#-mG?V)S7$A`tS(_k(jXLja-%n`^HXRS^$5rb zh3#hsua`*!-$op%^x)6o%N00*!il&t-;z3IZug(U1#J{~r_ClS8YycV zpL~+nojFVN8dIXqq~sgnX5;R;YL>tGb`V6xKG?nRVdC>#`&d(n|CVut?z%#gv+^XI zAiiRYp%DkxNy7Xyci|g;na||*Y8>{ghr(|&o%1o8D2L){Ns#0}zBW_g#+BwPf7kSS zCy&Wj73i*Fqux=Cp2r;W3}Kv+BZZ7&$MK8jRppuEfva2+U2>fQfiSId2;RuJoGJ+G z{9M+26YxO1_x}h2SsDxW)^g?klKB9Iq5c-5-wpaMu%m#XJq`d1URFT7Z-G3NtC|nV znR%+)sL<@~-ML(V>}1Qc@jFogJQtXcxA+%J6*UdZ0Q=fH_|bG?5jMrxbZr4XXB_gz zMT*%{cN?*lrg<8zFFebj3pgQYU90kiA_~&cJm*Yg)nLJmWcHAF;;Xki!eP_b{Pp4e zd4%V{I&8QG6KCxCmpw;L(V^p%AFf{_1d%tG?xAtK5@JjZF)%<_Cw@7h|Joz-n)z@} z#PROLjpmHyF5;y-&qBQ>@OXJ!am+ae65O}Btp+#ac}DMY(zEBxtwyGgbHlN+D-MUi zUrM48K1c5$;@v9qfaD%F>zV{xihUG~CKn490)*opAQ<*^4SlJNI^kJ7hizBreF}4_ zzsNzMKCkqUmtmKGdcFypepc5dlnKX0JAxV2!*|BD;L4f-{LM(#>q0XU^tcsiVfD@UR3{ z3kQpZ42V&$R!FS!zuw#)EqO@)nECuf*Ly{`5p$j+=f!i{&)2$BT%QngtzjNr!Rwkv&nJHZj)rEyo9AQJ5KK8BkgILQeD??V~HC*1& zz5;k^$6J^Cgxd27f5Mhm-N5#6Rcwuj-(@tc z?97=*^-dd$Q6BH5dTgIBelp73hkC5hV3lWH8A^~YxPl*w`?l-8JvnvpeUa?gv|0F_ zS}KN=aT>_lFAs?^zFA(B{<<*=Tcz1*&%lcxO3y{j57soX7h|m!oG_9Mb)Y-`IXzFA34zP9~6WF6n|2rOm+ zBO+_FpTfa8)=UC>ncqF_?qm4A@g03l-np?R>q$}5<+c|U%{=AGU-fA5#w^Je>QT2v z1@FI4;74iuH!Vmpj}y_Isr+N;oXB`EsxW86w6<7%^2zm;?}z-kLh;?GGDfCILz$kc zdi28iUIA6~`Lc(!a-*!eSSsOPV@HpEoDX4J0j1i_l2C~|S!{&ZwnEZM%`Pv-d(Lc{ z5cH^%MTC~?fqWU409I6R|EYUTQqRfzJU@V@nrsW`Tqa|Te}bd`#?m%9Z!P?NY7tqs_HE{k`2Iu zCv%{oA>ICaBHzUMGL#>%k{>*5d-*)APNqVeoAI(U{eeQ0>L~@?^_ERVMO|qEz^2)$ zor#aEi1vh!@*n!G#jVb19krcYTV2-rT_eqa^$rT?;r*!yLHoSyO)K>w2*o(L{H@0S zs?S*j$f5fKTZ^gW2VT9tJdDszPIOCL&Uz|**tOMlHI(EFcbe$@tq`xdiCyAIkC^@L29>6ljlb7lRN{q2~0xJpYCEWaMf z%_jojU_Ahk4On^2lh3DoM=UivD?zU927vmMcKr2@J$oufoPcpK?N9#W34mZi3GZNd zszJfiRNgqO{0{UR)*17~b5NQbBOaiImN@Is!`1oH1x|IG(Z@<81DLwkGZs?vEh=7N z^=~>ZDm#Tj*p0DXN$E9wl0HkZHm=ZX8_cNUvapv?Hu_0SYbfSBTd`C8eKEn|(D`Cm z27r}F*M}OLf&435RUpZ5!K-I=QJx1DbT-wOm8hr6iMOJDgMOO1^pV*`S~qVR92JUm zA$hW&RdzpxJxrG%CXOCSriUwQCIvr9gi!Y7oNpgERZAI`67gI3`W zU#`(NL{k}lCwlv~(dh~V8ihH%7Z%ZdIr0UDUxcb|Dp~hS5P+c}?u^1WZuFs0tVs$3 z#1Zso9QTLIwl9V3wws4h7mG1=fIA^&htON|JEZ;Ok%8Qan1`A)Go^SMZso6hH>ltx z1X6ne((V3628Led@^9GL2CQPi_3jq0l@!t(-j~hrhb5w-isQTY3{p2NHmTvWTxy23 z-SV2pCECuSY4s&o1cAKf50}<|NLp3A)IW;jf`2isEoGwn0UcS-opI^?cF6y4fNrD8 z`K53k->R}1`FoODoAE=#)UOo2AgJZovl4IrkMqa3rY)auy75Sfvom>@%qy$QI8&h1 zm)dR*w4u6j>0Q=nG#@eofjrfm`sZ&^7?AIq>Q};>U;BnHHtdPIdD0Y}+8<&323+^6 z!-C2hWf4HWz5Q||@_=_Sd6FU`&xddw;|Wh&ibAVUp0I6d!P%i4Rnijb+Sd-7oKCjc zEuXf^@H3v2sAHBZvC#4ZB29DRxdvFt+NaAMBdp<5g?sz|6-@W|_zz4sgc&JI#F|l8 zuzq_B`G}big9%#2<)&m*rec{8*C)S!yK&8Iw_N6IS@@=A_H0&1=1fG>F9vgq-m38x z#Xv#2)4Rq3m91RWhyLy+o>r-)Lj+J-RThR<_~Lj6%g*Q)q+|B?eBqcg8J6K12*Y}I zc3xIixkiC`7xb>tAhT$KzjSL$pZsx1#Z14RY8(b6#tc@&j@5?GbQTgG57TwWI*6_k z?G%%D6hSL<-#AWILm;B5Y5*+b#v6HZ?ATfl(Qn&yT_Fi4$~~OCIc?%MfS2Q;}69EWtS8cpsa%1dyn>C0oqs~N5wr$%XJC@HHO8C95p4(&XVH%9E7nBAw z&Q-dX4Zk(V^f(miVY>ah1q7}cULtY~r3v6v?GB#ZvHfho&zpCBZwTS^S~+iKY&r}s zBZTDS((nNvZYL+!N5hSG%}!7K38Ze!)}68E)*pM2e%MQk2xq7L`^jzY3t^;merKQ-pXV}~H@qEtZ z7cE?;7;z;2#`~*n+W|+ziav_wqo#(p>5+x#Fxbn8ZJ|&o!UZ zp4AE+q#s(8(`H_;+jOVQ(bvxnXBLiu_(-sIIFT?y*wWbarP{@pDd&L`ihy?SWWET% zS_WG4%KmI|K@u+kX*{V19>9LGM}DFC+`lwEN{-ogdocUJl*ky>s; z9>E1#=@;gj$(cNnk>eAvrCja^<&|)=`H0WZ0^nK)8fD;@Z7pxA#GKqRx$k0Im!z)% zLZo9WROf0bAVtz^mW<$Y9UHCFyvc4BwnC7R?CI#LiihYbD~rQ%sfgv9Mb`A@+i}D5 z{urPC1>t>MZrD#2uE!D=aFc6PnOd?rIxM-Z{#ZO|l?=2xAhb0^1@C1Q(29H~VXe9H zfGL88KrVrt$CWmOG`>VaD^`LkQb7e``gK^LMDXL7>kh*SPck5XHVyy#78YZACLOxrPeTxy?U2O^W zKMOI5i#|9!kQj)#@*8CZ)`eY5FkeRrg~PVyFiMLz$oZ$fNkGru6;;@$_n|M=!_WXu zP>+7qA2;Ec`jos7eC+cip`6pd6#HB$K0qyo}y4a2w z6Z5cTz%bzJ)!MKK%pq`Q%2O4w0JZczCN!C+eq9Aba8O@R5OaCHq9hHJ{qKkTkZgeS z8hrWSs8cJ+9_l^b#=H`*S@bpB{jnYcC|g{S4W;aP2{1$7fgeN)jx`hp^s&)WWdrGn z7xBD@mF(gehYlVJtWaiD@(RbkhOp4yG!W0k0EV7YuOy}jBU3Sh?LsQHB_B}2^Yc5q3c%Q#dR@o(PMn{vO{R^|uz zpwWG@(%vQE0G{ZN`Q3So?DStT?m(8{TjZB1KO0574j2X+cO3!Uaie6HQ)98rzbwH6 z*C6Gu#r*k~^w2=ZsIwBh|K>^?Fz!QglmtrcKr@s^h|a6h;y+$l zR7n?TQqkVlf830wXn;;(zzs)j$X{26W#1HcoMkHvRJ?61xHLywI<-#%$WXoG6oC4f z(svx{{r=Q}_W;qXl7HSQ0GRICxoYcjP#^pCoN{LLIgO__L07PgG0A~Gj7v=`j+YQl z;a9RUXbK+d*!VW_$Yj+jM9nnaA&UCuXa!d*f$d@&f3EMIjFK`wEy%mf&;8UyM=t8zv;usau9a z>_Bf};offq&<-H|Y^6{)>y0jBOYbG^AJ0BhnxtXa*x3-=4~5vsh!4XneMPsi3)iJK z-a`X*8Abn@$${7%(FkufMu0>0)cA8&xVrOydrmgccTP@EVfGvSyHD2Nb5~UlpsY(m zz9VfYZxB#B>cpFwms3JN5WM2$c$fs5xBhL^3GjFHyO}|P!Lh~WByAayQ{MOoRM1oG zyFTMnU*^1 z0$z@}?!URJ{~u=N|Ch^p=jgX^fFgLu?=;vFrj!*$g!&$sK=M23P!Fwt_ad0=pP3;- z_}B!sN!U|9i;bXsPg06ixWfIXJ?QuMowRpnu(TjwbqMq}xnr|{VQ10n#~M*>SYSng zC)4lyI!y+*RTQYq{WRT`2+^LtF2JNJ+gnm82br?%>%0pyGil%x1+mpe>c2J~4n=Wk zMCc62i)3Qk`d&=(i2%@ALs>jfTfQ?bqBt-r2y4TLxWk{GTZ7akT}VHNS2wr=Dnv<_ z?O2T(sJ}uuzC(k5y2okjlO^ka%WeOW-TvSH?R&&LSj9ivp$jJjxMm;;QQ7w;!uo&x EKVETuGXMYp literal 0 HcmV?d00001 diff --git a/Functions/helper_functions_community/schemer/schemes/screenshots/solarized-dark.png b/Functions/helper_functions_community/schemer/schemes/screenshots/solarized-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..fef6c373d1891d048b1ed20583c4f81c4f5e5f60 GIT binary patch literal 31523 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 c3;M1& literal 0 HcmV?d00001 diff --git a/Functions/helper_functions_community/schemer/schemes/screenshots/solarized-light.png b/Functions/helper_functions_community/schemer/schemes/screenshots/solarized-light.png new file mode 100644 index 0000000000000000000000000000000000000000..6724273af73038057328fbccf841908a9cbdbec8 GIT binary patch literal 31602 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

hHRJXW8zXM^Ws6Fyfjr>KzYGg=b9HU^8WmO@ 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 1 && ischar(cname) && ischar(ncol) + LI = ismember({cname ncol}, types); + if ~any(LI); error('Number of colors cannot be a string'); end; + if all(LI); error('Incorrect colormap name'); end; + if LI(1) + vgn = {cname; ncol; interp_method; interp_space}; + cname = vgn{2}; + ncol = vgn{3}; + interp_method = vgn{4}; + interp_space = varargin{1}; + ctype_input = vgn{1}; + elseif LI(2) + vgn = {cname; ncol; interp_method; interp_space}; + cname = vgn{1}; + ncol = vgn{3}; + interp_method = vgn{4}; + interp_space = varargin{1}; + ctype_input = vgn{2}; + end +else + ctype_input = ''; +end + +% Default values +if isempty(ncol) + % Number of colours in the colormap + ncol = size(get(gcf,'colormap'), 1); +end +if isempty(interp_method) + interp_method = 'pchip'; +end +if isempty(interp_space) + interp_space = 'lab'; +end + + +% Load colorbrewer data --------------------------------------------------- +Tmp = load('colorbrewer.mat'); +colorbrewer = Tmp.colorbrewer; + +[TF, idict] = ismember(lower(cname), lower(cbdict(:, 1))); + +if ~TF + error('%s is not a recognised Brewer colormap',cname); +end + +cname = cbdict{idict, 1}; +ctype = cbdict{idict, 2}; + +if (~isfield(colorbrewer.(ctype), cname)) + error('Colormap %s is not present in loaded data',cname); +end + + +% Main script ------------------------------------------------------------- + +if ncol > length(colorbrewer.(ctype).(cname)) + % If we specified too many colours, we take the maximum and interpolate + colormap = colorbrewer.(ctype).(cname){length(colorbrewer.(ctype).(cname))}; + colormap = colormap ./ 255; +elseif isempty(colorbrewer.(ctype).(cname){ncol}) + % If we specified too few colours, we take the minimum and interpolate + nmin = find(~cellfun(@isempty, colorbrewer.(ctype).(cname)), 1); + colormap = colorbrewer.(ctype).(cname){nmin}; + colormap = colormap./255; +else + % If we specified a number of colours in the pre-designed range, no + % need to interpolate + colormap = (colorbrewer.(ctype).(cname){ncol}) ./ 255; + return; +end + +% Don't interpolate if qualitative type +if strcmp(ctype,'qual') + if size(colormap, 1) >= ncol + colormap = colormap(1:ncol, :); + return; + end + warning('CBREWER2:QualTooManyColors', ... + ['Too many colors requested: cannot interpolate a qualitative' ... + ' colorscheme']); + % Cycle the colours from the beginning again, so we have enough to + % return + colormap = repmat(colormap, ceil(ncol / size(colormap, 1)), 1); + colormap = colormap(1:ncol, :); + return; +end + +% Make sure we have colorspace downloaded from the FEX +if ~strcmpi(interp_space, 'rgb') && ~exist('colorspace.m', 'file') + P = requireFEXpackage(28790); + if isempty(P); + error(... + ['You need to download COLORSPACE from the MATLAB FEX and' ... + ' add it to the MATLAB path.']); + end; +end + +% Move to perceptually uniform space +if ~strcmpi(interp_space,'rgb') + colormap = colorspace(['rgb->' interp_space], colormap); +end + +% Linearly interpolate +X = linspace(0, 1, size(colormap, 1)); +XI = linspace(0, 1, ncol); +colormap = interp1(X, colormap, XI, interp_method); + +% Move from perceptually uniform space back to sRGB +if ~strcmpi(interp_space,'rgb') + colormap = colorspace(['rgb<-' interp_space], colormap); +end + +end \ No newline at end of file diff --git a/Functions/helper_functions_community/cbrewer2/colorbrewer.mat b/Functions/helper_functions_community/cbrewer2/colorbrewer.mat new file mode 100644 index 0000000000000000000000000000000000000000..ec59ef47624450f36bf71566afd6fa552df80ce5 GIT binary patch literal 4475 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 literal 0 HcmV?d00001 diff --git a/Functions/helper_functions_community/cbrewer2/requireFEXpackage.m b/Functions/helper_functions_community/cbrewer2/requireFEXpackage.m new file mode 100644 index 0000000..86838cc --- /dev/null +++ b/Functions/helper_functions_community/cbrewer2/requireFEXpackage.m @@ -0,0 +1,194 @@ +function AddedPath = requireFEXpackage(FEXSubmissionID) +%Function requireFEXpackage - +%installs Matlab Central File Exchange (FEX) submission +%with given ID into the directory chosen by the user. +%A new FEX submissions may use previous FEX submissions as its part. +%The function 'requireFEXpackage' helps in adding those previous +%submissions to the user's MATLAB installation. +% +%This function is a part of File Exchange submission 31069. +%Download the entire submission: +%http://www.mathworks.com/matlabcentral/fileexchange/31069 +% +% SYNTAX: +% AddedPath = requireFEXpackage(FEXSubmissionID) +% +% INPUT: +% ID of the required submission to File Exchange +% +% OUTPUT: +% the path to that submission added to the user's MATLAB path. +% +% HOW TO CALL: +% The command +% P = requireFEXpackage(8277) +% will download and install the package with ID 8277 +% (namely, nice 'fminsearchbnd' by John D'Errico) +% +% EXAMPLES -- HOW TO USE: +% +% EXAMPLE 1 (using 'exist' command): +% +% % first, somewhere in the very beginning of your code, +% % check if the function 'fminsearchbnd' from the FEX package 8277 +% % is on your MATLAB path, and if it is not there, +% % require the FEX package 8277: +% if ~(exist('fminsearchbnd', 'file') == 2) +% P = requireFEXpackage(8277); % fminsearchbnd is part of 8277 +% end +% +% % Then just use 'fminsearchbnd' where you need it: +% syms x +% RosenbrockBananaFunction = @(x) (1-x(1)).^2 + 100*(x(2)-x(1).^2).^2; +% x = fminsearchbnd(RosenbrockBananaFunction,[3 3]) + +% EXAMPLE 2 (using 'try-catch' command): +% +% syms x +% RosenbrockBananaFunction = @(x) (1-x(1)).^2+100*(x(2)-x(1).^2).^2; +% try +% % if function 'fminsearchbnd' already exists in your MATLAB +% % installation, just use it: +% x = fminsearchbnd(RosenbrockBananaFunction,[3 3]) +% catch +% % if function 'fminsearchbnd' is not present in your MATLAB +% % installation, first get the package 8277 (to which it belongs) +% % from the MATLAB Central File Exchange (FEX) +% P = requireFEXpackage(8277); % fminsearchbnd is part of 8277 +% % and then use that function: +% x = fminsearchbnd(RosenbrockBananaFunction,[3 3]) +% end +% +% +% NOTE: on Mac platform, the title of the dialog box for +% choosing the directory for installing the required FEX package +% is not shown; this is not a bug, this is how UIGETDIR works on Macs -- +% see the documentation for UIGETDIR +% http://www.mathworks.com/help/techdoc/ref/uigetdir.html +% +% (C) Igor Podlubny, 2011 + +% Copyright (c) 2011, Igor Podlubny +% All rights reserved. +% +% Redistribution and use in source and binary forms, with or without +% modification, are permitted provided that the following conditions are +% met: +% +% * Redistributions of source code must retain the above copyright +% notice, this list of conditions and the following disclaimer. +% * Redistributions in binary form must reproduce the above copyright +% notice, this list of conditions and the following disclaimer in +% the documentation and/or other materials provided with the distribution +% +% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +% POSSIBILITY OF SUCH DAMAGE. + + +ID = num2str(FEXSubmissionID); + +% Ask user for the confirmation of the installation +% of the required FEX package +yes = ['YES, Install package ' ID]; +no = 'NO, do not install'; +userchoice = questdlg(['The Matlab function/toolbox, which you are running, ' ... + 'requires the presence of the package ' ID ... + ' from Matlab Central File Exchange.' ... + sprintf('\n\n') ... + 'Would you like to install the FEX package ' ID ' now?'] , ... + ['Required package ' ID], ... + yes, no, yes); + +% Handle response +switch userchoice + case yes, + install = 1; + case no, + install = 0; + otherwise, + install = 0; +end + + +if install == 1 + baseURL = 'http://www.mathworks.com/matlabcentral/fileexchange/'; + query = '?download=true'; + + location = uigetdir(pwd, ['Select the directory for installing the required FEX package' ID ]); + if location ~= 0 + % download package 'ID' from Matlab Central File Exchange + filetosave = [location filesep ID '.zip']; + FEXpackage = [baseURL ID query]; + [f,status] = urlwrite(FEXpackage,filetosave); + if status==0 + warndlg(['No connection to Matlab Central File Exchange,' ' or package ' ID ' does not exist.' ... + ' Package ' ID ' has not been installed. ' ... + ' Check you internet settings and the ID of the required package, and try again. '] , ... + ['No connection to Matlab Central File Exchange' ' or package ' ID ' does not exist'], ... + 'modal'); + AddedPath = ''; + return + end + % unzip the downloaded file to the subdirectory 'ID' + todir = [location filesep ID]; + % if the directory 'ID' doesn't exist at given location, create it + if ~(exist([location filesep ID], 'dir') == 7) + mkdir(location, ID); + end + try + unzip(filetosave, todir); + % after unzipping, delete the downloaded ZIP file + delete(filetosave); + % prepend the paths to the downloaded package to the MATLAB path + P = genpath([location filesep ID]); + path(P,path); + catch + % if the FEX package is not ZIP, then it is a single m-file + % just move the file to the ID directory + [pathstr, name, ext] = fileparts(filetosave); + movefile(filetosave, [todir filesep name '.m']); + P = genpath([location filesep ID]); + path(P,path); + end + else + P = ''; + end + + AddedPath = P; +else + AddedPath = ''; +end + + +if install == 1, + % Ask user about reviewing and saving the modified MATLAB path, + % and take him to PATHTOOL, if the user wants to save the modified path + yes = 'YES, I want to review and save the MATLAB path'; + no = 'NO, I don''t want to save the path permanently'; + userchoice = questdlg(['After adding the package ' ID ... + ' from Matlab Central File Exchange to your MATLAB installation,' ... + ' the MATLAB path has been modified accordingly. ', ... + 'Would you like to review and save the modified MATLAB path?'] , ... + 'Review and save the modified MATLAB path for future use?', ... + yes, no, yes); + + % Handle response + switch userchoice + case yes, + pathtool; + case no, + otherwise, + end +end + + + diff --git a/Functions/helper_functions_community/colorspace/colorspace.c b/Functions/helper_functions_community/colorspace/colorspace.c new file mode 100644 index 0000000..00fdb74 --- /dev/null +++ b/Functions/helper_functions_community/colorspace/colorspace.c @@ -0,0 +1,1285 @@ +/** + * @file colorspace.c + * @author Pascal Getreuer 2005-2010 + * + * == Summary == + * This file implements routines for color transformations between the spaces + * sRGB, Y'UV, Y'CbCr, Y'PbPr, Y'DbDr, Y'IQ, HSV, HSL, HSI, CIEXYZ, CIELAB, + * CIELUV, CIELCH, and CIECAT02 LMS. + * + * == Usage == + * First call GetColorTransform, specifying the source and destination color + * spaces as "dest<-src" or "src->dest". Then call ApplyColorTransform to + * perform the transform: +@code + num S[3] = {173, 0.8, 0.5}; + num D[3]; + colortransform Trans; + + if(!(GetColorTransform(&Trans, "HSI -> Lab"))) + { + printf("Invalid syntax or unknown color space\n"); + return; + } + + ApplyColorTransform(Trans, &D[0], &D[1], &D[2], S[0], S[1], S[2]); +@endcode + * "num" is a typedef defined at the beginning of colorspace.h that may be set + * to either double or float, depending on the application. + * + * Specific transformation routines can also be called directly. The following + * converts an sRGB color to CIELAB and then back to sRGB: +@code + num R = 0.85, G = 0.32, B = 0.5; + num L, a, b; + Rgb2Lab(&L, &a, &b, R, G, B); + Lab2Rgb(&R, &G, &B, L, a, b); +@endcode + * Generally, the calling syntax is +@code + Foo2Bar(&B0, &B1, &B2, F0, F1, F2); +@endcode + * where (F0,F1,F2) are the coordinates of a color in space "Foo" and + * (B0,B1,B2) are the transformed coordinates in space "Bar." For any + * transformation routine, its inverse has the sytax +@code + Bar2Foo(&F0, &F1, &F2, B0, B1, B2); +@endcode + * + * The conversion routines are consistently named with the first letter of a + * color space capitalized with following letters in lower case and omitting + * prime symbols. For example, "Rgb2Ydbdr" converts sRGB to Y'DbDr. For + * any transformation routine Foo2Bar, its inverse is Bar2Foo. + * + * All transformations assume a two degree observer angle and a D65 illuminant. + * The white point can be changed by modifying the WHITEPOINT_X, WHITEPOINT_Y, + * WHITEPOINT_Z definitions at the beginning of colorspace.h. + * + * == List of transformation routines == + * - Rgb2Yuv(num *Y, num *U, num *V, num R, num G, num B) + * - Rgb2Ycbcr(num *Y, num *Cb, num *Cr, num R, num G, num B) + * - Rgb2Jpegycbcr(num *Y, num *Cb, num *Cr, num R, num G, num B) + * - Rgb2Ypbpr(num *Y, num *Pb, num *Pr, num R, num G, num B) + * - Rgb2Ydbdr(num *Y, num *Db, num *Dr, num R, num G, num B) + * - Rgb2Yiq(num *Y, num *I, num *Q, num R, num G, num B) + * - Rgb2Hsv(num *H, num *S, num *V, num R, num G, num B) + * - Rgb2Hsl(num *H, num *S, num *L, num R, num G, num B) + * - Rgb2Hsi(num *H, num *S, num *I, num R, num G, num B) + * - Rgb2Xyz(num *X, num *Y, num *Z, num R, num G, num B) + * - Xyz2Lab(num *L, num *a, num *b, num X, num Y, num Z) + * - Xyz2Luv(num *L, num *u, num *v, num X, num Y, num Z) + * - Xyz2Lch(num *L, num *C, num *h, num X, num Y, num Z) + * - Xyz2Cat02lms(num *L, num *M, num *S, num X, num Y, num Z) + * - Rgb2Lab(num *L, num *a, num *b, num R, num G, num B) + * - Rgb2Luv(num *L, num *u, num *v, num R, num G, num B) + * - Rgb2Lch(num *L, num *C, num *h, num R, num G, num B) + * - Rgb2Cat02lms(num *L, num *M, num *S, num R, num G, num B) + * (Similarly for the inverse transformations.) + * + * It is possible to transform between two arbitrary color spaces by first + * transforming from the source space to sRGB and then transforming from + * sRGB to the desired destination space. For transformations between CIE + * color spaces, it is convenient to use XYZ as the intermediate space. This + * is the strategy used by GetColorTransform and ApplyColorTransform. + * + * == References == + * The definitions of these spaces and the many of the transformation formulas + * can be found in + * + * Poynton, "Frequently Asked Questions About Gamma" + * http://www.poynton.com/notes/colour_and_gamma/GammaFAQ.html + * + * Poynton, "Frequently Asked Questions About Color" + * http://www.poynton.com/notes/colour_and_gamma/ColorFAQ.html + * + * and Wikipedia articles + * http://en.wikipedia.org/wiki/SRGB + * http://en.wikipedia.org/wiki/YUV + * http://en.wikipedia.org/wiki/YCbCr + * http://en.wikipedia.org/wiki/YPbPr + * http://en.wikipedia.org/wiki/YDbDr + * http://en.wikipedia.org/wiki/YIQ + * http://en.wikipedia.org/wiki/HSL_and_HSV + * http://en.wikipedia.org/wiki/CIE_1931_color_space + * http://en.wikipedia.org/wiki/Lab_color_space + * http://en.wikipedia.org/wiki/CIELUV_color_space + * http://en.wikipedia.org/wiki/LMS_color_space + * + * == License (BSD) == + * Copyright (c) 2005-2010, Pascal Getreuer + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include +#include "colorspace.h" + +#ifdef MATLAB_MEX_FILE +#include "mex.h" +#endif + +/** @brief Min of A and B */ +#define MIN(A,B) (((A) <= (B)) ? (A) : (B)) + +/** @brief Max of A and B */ +#define MAX(A,B) (((A) >= (B)) ? (A) : (B)) + +/** @brief Min of A, B, and C */ +#define MIN3(A,B,C) (((A) <= (B)) ? MIN(A,C) : MIN(B,C)) + +/** @brief Max of A, B, and C */ +#define MAX3(A,B,C) (((A) >= (B)) ? MAX(A,C) : MAX(B,C)) + +#ifndef M_PI +/** @brief The constant pi */ +#define M_PI 3.14159265358979323846264338327950288 +#endif + +/** + * @brief sRGB gamma correction, transforms R to R' + * http://en.wikipedia.org/wiki/SRGB + */ +#define GAMMACORRECTION(t) \ + (((t) <= 0.0031306684425005883) ? \ + (12.92*(t)) : (1.055*pow((t), 0.416666666666666667) - 0.055)) + +/** + * @brief Inverse sRGB gamma correction, transforms R' to R + */ +#define INVGAMMACORRECTION(t) \ + (((t) <= 0.0404482362771076) ? \ + ((t)/12.92) : pow(((t) + 0.055)/1.055, 2.4)) + +/** + * @brief CIE L*a*b* f function (used to convert XYZ to L*a*b*) + * http://en.wikipedia.org/wiki/Lab_color_space + */ +#define LABF(t) \ + ((t >= 8.85645167903563082e-3) ? \ + pow(t,0.333333333333333) : (841.0/108.0)*(t) + (4.0/29.0)) + +/** + * @brief CIE L*a*b* inverse f function + * http://en.wikipedia.org/wiki/Lab_color_space + */ +#define LABINVF(t) \ + ((t >= 0.206896551724137931) ? \ + ((t)*(t)*(t)) : (108.0/841.0)*((t) - (4.0/29.0))) + +/** @brief u'v' coordinates of the white point for CIE Lu*v* */ +#define WHITEPOINT_U ((4*WHITEPOINT_X) \ + /(WHITEPOINT_X + 15*WHITEPOINT_Y + 3*WHITEPOINT_Z)) +#define WHITEPOINT_V ((9*WHITEPOINT_Y) \ + /(WHITEPOINT_X + 15*WHITEPOINT_Y + 3*WHITEPOINT_Z)) + +/** @brief Enumeration of the supported color spaces */ +#define UNKNOWN_SPACE 0 +#define RGB_SPACE 1 +#define YUV_SPACE 2 +#define YCBCR_SPACE 3 +#define JPEGYCBCR_SPACE 4 +#define YPBPR_SPACE 5 +#define YDBDR_SPACE 6 +#define YIQ_SPACE 7 +#define HSV_SPACE 8 +#define HSL_SPACE 9 +#define HSI_SPACE 10 +#define XYZ_SPACE 11 +#define LAB_SPACE 12 +#define LUV_SPACE 13 +#define LCH_SPACE 14 +#define CAT02LMS_SPACE 15 + +#define NUM_TRANSFORM_PAIRS 18 + + +/** @brief Table representing all transformations in this file */ +static const struct +{ + int Space[2]; + void (*Fun[2])(num*, num*, num*, num, num, num); +} TransformPair[NUM_TRANSFORM_PAIRS] = { + {{RGB_SPACE, YUV_SPACE}, {Rgb2Yuv, Yuv2Rgb}}, + {{RGB_SPACE, YCBCR_SPACE}, {Rgb2Ycbcr, Ycbcr2Rgb}}, + {{RGB_SPACE, JPEGYCBCR_SPACE}, {Rgb2Jpegycbcr, Jpegycbcr2Rgb}}, + {{RGB_SPACE, YPBPR_SPACE}, {Rgb2Ypbpr, Ypbpr2Rgb}}, + {{RGB_SPACE, YDBDR_SPACE}, {Rgb2Ydbdr, Ydbdr2Rgb}}, + {{RGB_SPACE, YIQ_SPACE}, {Rgb2Yiq, Yiq2Rgb}}, + {{RGB_SPACE, HSV_SPACE}, {Rgb2Hsv, Hsv2Rgb}}, + {{RGB_SPACE, HSL_SPACE}, {Rgb2Hsl, Hsl2Rgb}}, + {{RGB_SPACE, HSI_SPACE}, {Rgb2Hsi, Hsi2Rgb}}, + {{RGB_SPACE, XYZ_SPACE}, {Rgb2Xyz, Xyz2Rgb}}, + {{XYZ_SPACE, LAB_SPACE}, {Xyz2Lab, Lab2Xyz}}, + {{XYZ_SPACE, LUV_SPACE}, {Xyz2Luv, Luv2Xyz}}, + {{XYZ_SPACE, LCH_SPACE}, {Xyz2Lch, Lch2Xyz}}, + {{XYZ_SPACE, CAT02LMS_SPACE}, {Xyz2Cat02lms, Cat02lms2Xyz}}, + {{RGB_SPACE, LAB_SPACE}, {Rgb2Lab, Lab2Rgb}}, + {{RGB_SPACE, LUV_SPACE}, {Rgb2Luv, Luv2Rgb}}, + {{RGB_SPACE, LCH_SPACE}, {Rgb2Lch, Lch2Rgb}}, + {{RGB_SPACE, CAT02LMS_SPACE}, {Rgb2Cat02lms, Cat02lms2Rgb}} + }; + + +/* + * == Linear color transformations == + * + * The following routines implement transformations between sRGB and + * the linearly-related color spaces Y'UV, Y'PbPr, Y'DbDr, and Y'IQ. + */ + + +/** + * @brief Convert sRGB to NTSC/PAL Y'UV Luma + Chroma + * + * @param Y, U, V pointers to hold the result + * @param R, G, B the input sRGB values + * + * Wikipedia: http://en.wikipedia.org/wiki/YUV + */ +void Rgb2Yuv(num *Y, num *U, num *V, num R, num G, num B) +{ + *Y = (num)( 0.299*R + 0.587*G + 0.114*B); + *U = (num)(-0.147*R - 0.289*G + 0.436*B); + *V = (num)( 0.615*R - 0.515*G - 0.100*B); +} + + +/** + * @brief Convert NTSC/PAL Y'UV to sRGB + * + * @param R, G, B pointers to hold the result + * @param Y, U, V the input YUV values + */ +void Yuv2Rgb(num *R, num *G, num *B, num Y, num U, num V) +{ + *R = (num)(Y - 3.945707070708279e-05*U + 1.1398279671717170825*V); + *G = (num)(Y - 0.3946101641414141437*U - 0.5805003156565656797*V); + *B = (num)(Y + 2.0319996843434342537*U - 4.813762626262513e-04*V); +} + + +/** @brief sRGB to Y'CbCr Luma + Chroma */ +void Rgb2Ycbcr(num *Y, num *Cb, num *Cr, num R, num G, num B) +{ + *Y = (num)( 65.481*R + 128.553*G + 24.966*B + 16); + *Cb = (num)(-37.797*R - 74.203*G + 112.0 *B + 128); + *Cr = (num)(112.0 *R - 93.786*G - 18.214*B + 128); +} + + +/** @brief Y'CbCr to sRGB */ +void Ycbcr2Rgb(num *R, num *G, num *B, num Y, num Cr, num Cb) +{ + Y -= 16; + Cb -= 128; + Cr -= 128; + *R = (num)(0.00456621004566210107*Y + 1.1808799897946415e-09*Cr + 0.00625892896994393634*Cb); + *G = (num)(0.00456621004566210107*Y - 0.00153632368604490212*Cr - 0.00318811094965570701*Cb); + *B = (num)(0.00456621004566210107*Y + 0.00791071623355474145*Cr + 1.1977497040190077e-08*Cb); +} + + +/** @brief sRGB to JPEG-Y'CbCr Luma + Chroma */ +void Rgb2Jpegycbcr(num *Y, num *Cb, num *Cr, num R, num G, num B) +{ + Rgb2Ypbpr(Y, Cb, Cr, R, G, B); + *Cb += (num)0.5; + *Cr += (num)0.5; +} + +/** @brief JPEG-Y'CbCr to sRGB */ +void Jpegycbcr2Rgb(num *R, num *G, num *B, num Y, num Cb, num Cr) +{ + Cb -= (num)0.5; + Cr -= (num)0.5; + Ypbpr2Rgb(R, G, B, Y, Cb, Cr); +} + + +/** @brief sRGB to Y'PbPr Luma (ITU-R BT.601) + Chroma */ +void Rgb2Ypbpr(num *Y, num *Pb, num *Pr, num R, num G, num B) +{ + *Y = (num)( 0.299 *R + 0.587 *G + 0.114 *B); + *Pb = (num)(-0.1687367*R - 0.331264*G + 0.5 *B); + *Pr = (num)( 0.5 *R - 0.418688*G - 0.081312*B); +} + + +/** @brief Y'PbPr to sRGB */ +void Ypbpr2Rgb(num *R, num *G, num *B, num Y, num Pb, num Pr) +{ + *R = (num)(0.99999999999914679361*Y - 1.2188941887145875e-06*Pb + 1.4019995886561440468*Pr); + *G = (num)(0.99999975910502514331*Y - 0.34413567816504303521*Pb - 0.71413649331646789076*Pr); + *B = (num)(1.00000124040004623180*Y + 1.77200006607230409200*Pb + 2.1453384174593273e-06*Pr); +} + + +/** @brief sRGB to SECAM Y'DbDr Luma + Chroma */ +void Rgb2Ydbdr(num *Y, num *Db, num *Dr, num R, num G, num B) +{ + *Y = (num)( 0.299*R + 0.587*G + 0.114*B); + *Db = (num)(-0.450*R - 0.883*G + 1.333*B); + *Dr = (num)(-1.333*R + 1.116*G + 0.217*B); +} + + +/** @brief SECAM Y'DbDr to sRGB */ +void Ydbdr2Rgb(num *R, num *G, num *B, num Y, num Db, num Dr) +{ + *R = (num)(Y + 9.2303716147657e-05*Db - 0.52591263066186533*Dr); + *G = (num)(Y - 0.12913289889050927*Db + 0.26789932820759876*Dr); + *B = (num)(Y + 0.66467905997895482*Db - 7.9202543533108e-05*Dr); +} + + +/** @brief sRGB to NTSC YIQ */ +void Rgb2Yiq(num *Y, num *I, num *Q, num R, num G, num B) +{ + *Y = (num)(0.299 *R + 0.587 *G + 0.114 *B); + *I = (num)(0.595716*R - 0.274453*G - 0.321263*B); + *Q = (num)(0.211456*R - 0.522591*G + 0.311135*B); +} + + +/** @brief Convert NTSC YIQ to sRGB */ +void Yiq2Rgb(num *R, num *G, num *B, num Y, num I, num Q) +{ + *R = (num)(Y + 0.9562957197589482261*I + 0.6210244164652610754*Q); + *G = (num)(Y - 0.2721220993185104464*I - 0.6473805968256950427*Q); + *B = (num)(Y - 1.1069890167364901945*I + 1.7046149983646481374*Q); +} + + + +/* + * == Hue Saturation Value/Lightness/Intensity color transformations == + * + * The following routines implement transformations between sRGB and + * color spaces HSV, HSL, and HSI. + */ + + +/** + * @brief Convert an sRGB color to Hue-Saturation-Value (HSV) + * + * @param H, S, V pointers to hold the result + * @param R, G, B the input sRGB values scaled in [0,1] + * + * This routine transforms from sRGB to the hexcone HSV color space. The + * sRGB values are assumed to be between 0 and 1. The output values are + * H = hexagonal hue angle (0 <= H < 360), + * S = C/V (0 <= S <= 1), + * V = max(R',G',B') (0 <= V <= 1), + * where C = max(R',G',B') - min(R',G',B'). The inverse color transformation + * is given by Hsv2Rgb. + * + * Wikipedia: http://en.wikipedia.org/wiki/HSL_and_HSV + */ +void Rgb2Hsv(num *H, num *S, num *V, num R, num G, num B) +{ + num Max = MAX3(R, G, B); + num Min = MIN3(R, G, B); + num C = Max - Min; + + + *V = Max; + + if(C > 0) + { + if(Max == R) + { + *H = (G - B) / C; + + if(G < B) + *H += 6; + } + else if(Max == G) + *H = 2 + (B - R) / C; + else + *H = 4 + (R - G) / C; + + *H *= 60; + *S = C / Max; + } + else + *H = *S = 0; +} + + +/** + * @brief Convert a Hue-Saturation-Value (HSV) color to sRGB + * + * @param R, G, B pointers to hold the result + * @param H, S, V the input HSV values + * + * The input values are assumed to be scaled as + * 0 <= H < 360, + * 0 <= S <= 1, + * 0 <= V <= 1. + * The output sRGB values are scaled between 0 and 1. This is the inverse + * transformation of Rgb2Hsv. + * + * Wikipedia: http://en.wikipedia.org/wiki/HSL_and_HSV + */ +void Hsv2Rgb(num *R, num *G, num *B, num H, num S, num V) +{ + num C = S * V; + num Min = V - C; + num X; + + + H -= 360*floor(H/360); + H /= 60; + X = C*(1 - fabs(H - 2*floor(H/2) - 1)); + + switch((int)H) + { + case 0: + *R = Min + C; + *G = Min + X; + *B = Min; + break; + case 1: + *R = Min + X; + *G = Min + C; + *B = Min; + break; + case 2: + *R = Min; + *G = Min + C; + *B = Min + X; + break; + case 3: + *R = Min; + *G = Min + X; + *B = Min + C; + break; + case 4: + *R = Min + X; + *G = Min; + *B = Min + C; + break; + case 5: + *R = Min + C; + *G = Min; + *B = Min + X; + break; + default: + *R = *G = *B = 0; + } +} + + +/** + * @brief Convert an sRGB color to Hue-Saturation-Lightness (HSL) + * + * @param H, S, L pointers to hold the result + * @param R, G, B the input sRGB values scaled in [0,1] + * + * This routine transforms from sRGB to the double hexcone HSL color space + * The sRGB values are assumed to be between 0 and 1. The outputs are + * H = hexagonal hue angle (0 <= H < 360), + * S = { C/(2L) if L <= 1/2 (0 <= S <= 1), + * { C/(2 - 2L) if L > 1/2 + * L = (max(R',G',B') + min(R',G',B'))/2 (0 <= L <= 1), + * where C = max(R',G',B') - min(R',G',B'). The inverse color transformation + * is given by Hsl2Rgb. + * + * Wikipedia: http://en.wikipedia.org/wiki/HSL_and_HSV + */ +void Rgb2Hsl(num *H, num *S, num *L, num R, num G, num B) +{ + num Max = MAX3(R, G, B); + num Min = MIN3(R, G, B); + num C = Max - Min; + + + *L = (Max + Min)/2; + + if(C > 0) + { + if(Max == R) + { + *H = (G - B) / C; + + if(G < B) + *H += 6; + } + else if(Max == G) + *H = 2 + (B - R) / C; + else + *H = 4 + (R - G) / C; + + *H *= 60; + *S = (*L <= 0.5) ? (C/(2*(*L))) : (C/(2 - 2*(*L))); + } + else + *H = *S = 0; +} + + +/** + * @brief Convert a Hue-Saturation-Lightness (HSL) color to sRGB + * + * @param R, G, B pointers to hold the result + * @param H, S, L the input HSL values + * + * The input values are assumed to be scaled as + * 0 <= H < 360, + * 0 <= S <= 1, + * 0 <= L <= 1. + * The output sRGB values are scaled between 0 and 1. This is the inverse + * transformation of Rgb2Hsl. + * + * Wikipedia: http://en.wikipedia.org/wiki/HSL_and_HSV + */ +void Hsl2Rgb(num *R, num *G, num *B, num H, num S, num L) +{ + num C = (L <= 0.5) ? (2*L*S) : ((2 - 2*L)*S); + num Min = L - 0.5*C; + num X; + + + H -= 360*floor(H/360); + H /= 60; + X = C*(1 - fabs(H - 2*floor(H/2) - 1)); + + switch((int)H) + { + case 0: + *R = Min + C; + *G = Min + X; + *B = Min; + break; + case 1: + *R = Min + X; + *G = Min + C; + *B = Min; + break; + case 2: + *R = Min; + *G = Min + C; + *B = Min + X; + break; + case 3: + *R = Min; + *G = Min + X; + *B = Min + C; + break; + case 4: + *R = Min + X; + *G = Min; + *B = Min + C; + break; + case 5: + *R = Min + C; + *G = Min; + *B = Min + X; + break; + default: + *R = *G = *B = 0; + } +} + + +/** + * @brief Convert an sRGB color to Hue-Saturation-Intensity (HSI) + * + * @param H, S, I pointers to hold the result + * @param R, G, B the input sRGB values scaled in [0,1] + * + * This routine transforms from sRGB to the cylindrical HSI color space. The + * sRGB values are assumed to be between 0 and 1. The output values are + * H = polar hue angle (0 <= H < 360), + * S = 1 - min(R',G',B')/I (0 <= S <= 1), + * I = (R'+G'+B')/3 (0 <= I <= 1). + * The inverse color transformation is given by Hsi2Rgb. + * + * Wikipedia: http://en.wikipedia.org/wiki/HSL_and_HSV + */ +void Rgb2Hsi(num *H, num *S, num *I, num R, num G, num B) +{ + num alpha = 0.5*(2*R - G - B); + num beta = 0.866025403784439*(G - B); + + + *I = (R + G + B)/3; + + if(*I > 0) + { + *S = 1 - MIN3(R,G,B) / *I; + *H = atan2(beta, alpha)*(180/M_PI); + + if(*H < 0) + *H += 360; + } + else + *H = *S = 0; +} + + +/** + * @brief Convert a Hue-Saturation-Intesity (HSI) color to sRGB + * + * @param R, G, B pointers to hold the result + * @param H, S, I the input HSI values + * + * The input values are assumed to be scaled as + * 0 <= H < 360, + * 0 <= S <= 1, + * 0 <= I <= 1. + * The output sRGB values are scaled between 0 and 1. This is the inverse + * transformation of Rgb2Hsi. + * + * Wikipedia: http://en.wikipedia.org/wiki/HSL_and_HSV + */ +void Hsi2Rgb(num *R, num *G, num *B, num H, num S, num I) +{ + H -= 360*floor(H/360); + + if(H < 120) + { + *B = I*(1 - S); + *R = I*(1 + S*cos(H*(M_PI/180))/cos((60 - H)*(M_PI/180))); + *G = 3*I - *R - *B; + } + else if(H < 240) + { + H -= 120; + *R = I*(1 - S); + *G = I*(1 + S*cos(H*(M_PI/180))/cos((60 - H)*(M_PI/180))); + *B = 3*I - *R - *G; + } + else + { + H -= 240; + *G = I*(1 - S); + *B = I*(1 + S*cos(H*(M_PI/180))/cos((60 - H)*(M_PI/180))); + *R = 3*I - *G - *B; + } +} + + +/* + * == CIE color transformations == + * + * The following routines implement transformations between sRGB and + * the CIE color spaces XYZ, L*a*b, L*u*v*, and L*C*H*. These + * transforms assume a 2 degree observer angle and a D65 illuminant. + */ + + +/** + * @brief Transform sRGB to CIE XYZ with the D65 white point + * + * @param X, Y, Z pointers to hold the result + * @param R, G, B the input sRGB values + * + * Poynton, "Frequently Asked Questions About Color," page 10 + * Wikipedia: http://en.wikipedia.org/wiki/SRGB + * Wikipedia: http://en.wikipedia.org/wiki/CIE_1931_color_space + */ +void Rgb2Xyz(num *X, num *Y, num *Z, num R, num G, num B) +{ + R = INVGAMMACORRECTION(R); + G = INVGAMMACORRECTION(G); + B = INVGAMMACORRECTION(B); + *X = (num)(0.4123955889674142161*R + 0.3575834307637148171*G + 0.1804926473817015735*B); + *Y = (num)(0.2125862307855955516*R + 0.7151703037034108499*G + 0.07220049864333622685*B); + *Z = (num)(0.01929721549174694484*R + 0.1191838645808485318*G + 0.9504971251315797660*B); +} + + +/** + * @brief Transform CIE XYZ to sRGB with the D65 white point + * + * @param R, G, B pointers to hold the result + * @param X, Y, Z the input XYZ values + * + * Official sRGB specification (IEC 61966-2-1:1999) + * Poynton, "Frequently Asked Questions About Color," page 10 + * Wikipedia: http://en.wikipedia.org/wiki/SRGB + * Wikipedia: http://en.wikipedia.org/wiki/CIE_1931_color_space + */ +void Xyz2Rgb(num *R, num *G, num *B, num X, num Y, num Z) +{ + num R1, B1, G1, Min; + + + R1 = (num)( 3.2406*X - 1.5372*Y - 0.4986*Z); + G1 = (num)(-0.9689*X + 1.8758*Y + 0.0415*Z); + B1 = (num)( 0.0557*X - 0.2040*Y + 1.0570*Z); + + Min = MIN3(R1, G1, B1); + + /* Force nonnegative values so that gamma correction is well-defined. */ + if(Min < 0) + { + R1 -= Min; + G1 -= Min; + B1 -= Min; + } + + /* Transform from RGB to R'G'B' */ + *R = GAMMACORRECTION(R1); + *G = GAMMACORRECTION(G1); + *B = GAMMACORRECTION(B1); +} + + +/** + * Convert CIE XYZ to CIE L*a*b* (CIELAB) with the D65 white point + * + * @param L, a, b pointers to hold the result + * @param X, Y, Z the input XYZ values + * + * Wikipedia: http://en.wikipedia.org/wiki/Lab_color_space + */ +void Xyz2Lab(num *L, num *a, num *b, num X, num Y, num Z) +{ + X /= WHITEPOINT_X; + Y /= WHITEPOINT_Y; + Z /= WHITEPOINT_Z; + X = LABF(X); + Y = LABF(Y); + Z = LABF(Z); + *L = 116*Y - 16; + *a = 500*(X - Y); + *b = 200*(Y - Z); +} + + +/** + * Convert CIE L*a*b* (CIELAB) to CIE XYZ with the D65 white point + * + * @param X, Y, Z pointers to hold the result + * @param L, a, b the input L*a*b* values + * + * Wikipedia: http://en.wikipedia.org/wiki/Lab_color_space + */ +void Lab2Xyz(num *X, num *Y, num *Z, num L, num a, num b) +{ + L = (L + 16)/116; + a = L + a/500; + b = L - b/200; + *X = WHITEPOINT_X*LABINVF(a); + *Y = WHITEPOINT_Y*LABINVF(L); + *Z = WHITEPOINT_Z*LABINVF(b); +} + + +/** + * Convert CIE XYZ to CIE L*u*v* (CIELUV) with the D65 white point + * + * @param L, u, v pointers to hold the result + * @param X, Y, Z the input XYZ values + * + * Wikipedia: http://en.wikipedia.org/wiki/CIELUV_color_space + */ +void Xyz2Luv(num *L, num *u, num *v, num X, num Y, num Z) +{ + num u1, v1, Denom; + + + if((Denom = X + 15*Y + 3*Z) > 0) + { + u1 = (4*X) / Denom; + v1 = (9*Y) / Denom; + } + else + u1 = v1 = 0; + + Y /= WHITEPOINT_Y; + Y = LABF(Y); + *L = 116*Y - 16; + *u = 13*(*L)*(u1 - WHITEPOINT_U); + *v = 13*(*L)*(v1 - WHITEPOINT_V); +} + + +/** + * Convert CIE L*u*v* (CIELUV) to CIE XYZ with the D65 white point + * + * @param X, Y, Z pointers to hold the result + * @param L, u, v the input L*u*v* values + * + * Wikipedia: http://en.wikipedia.org/wiki/CIELUV_color_space + */ +void Luv2Xyz(num *X, num *Y, num *Z, num L, num u, num v) +{ + *Y = (L + 16)/116; + *Y = WHITEPOINT_Y*LABINVF(*Y); + + if(L != 0) + { + u /= L; + v /= L; + } + + u = u/13 + WHITEPOINT_U; + v = v/13 + WHITEPOINT_V; + *X = (*Y) * ((9*u)/(4*v)); + *Z = (*Y) * ((3 - 0.75*u)/v - 5); +} + + +/** + * Convert CIE XYZ to CIE L*C*H* with the D65 white point + * + * @param L, C, H pointers to hold the result + * @param X, Y, Z the input XYZ values + * + * CIE L*C*H* is related to CIE L*a*b* by + * a* = C* cos(H* pi/180), + * b* = C* sin(H* pi/180). + */ +void Xyz2Lch(num *L, num *C, num *H, num X, num Y, num Z) +{ + num a, b; + + + Xyz2Lab(L, &a, &b, X, Y, Z); + *C = sqrt(a*a + b*b); + *H = atan2(b, a)*180.0/M_PI; + + if(*H < 0) + *H += 360; +} + +/** + * Convert CIE L*C*H* to CIE XYZ with the D65 white point + * + * @param X, Y, Z pointers to hold the result + * @param L, C, H the input L*C*H* values + */ +void Lch2Xyz(num *X, num *Y, num *Z, num L, num C, num H) +{ + num a = C * cos(H*(M_PI/180.0)); + num b = C * sin(H*(M_PI/180.0)); + + + Lab2Xyz(X, Y, Z, L, a, b); +} + + +/** @brief XYZ to CAT02 LMS */ +void Xyz2Cat02lms(num *L, num *M, num *S, num X, num Y, num Z) +{ + *L = (num)( 0.7328*X + 0.4296*Y - 0.1624*Z); + *M = (num)(-0.7036*X + 1.6975*Y + 0.0061*Z); + *S = (num)( 0.0030*X + 0.0136*Y + 0.9834*Z); +} + + +/** @brief CAT02 LMS to XYZ */ +void Cat02lms2Xyz(num *X, num *Y, num *Z, num L, num M, num S) +{ + *X = (num)( 1.096123820835514*L - 0.278869000218287*M + 0.182745179382773*S); + *Y = (num)( 0.454369041975359*L + 0.473533154307412*M + 0.072097803717229*S); + *Z = (num)(-0.009627608738429*L - 0.005698031216113*M + 1.015325639954543*S); +} + + +/* + * == Glue functions for multi-stage transforms == + */ + +void Rgb2Lab(num *L, num *a, num *b, num R, num G, num B) +{ + num X, Y, Z; + Rgb2Xyz(&X, &Y, &Z, R, G, B); + Xyz2Lab(L, a, b, X, Y, Z); +} + + +void Lab2Rgb(num *R, num *G, num *B, num L, num a, num b) +{ + num X, Y, Z; + Lab2Xyz(&X, &Y, &Z, L, a, b); + Xyz2Rgb(R, G, B, X, Y, Z); +} + + +void Rgb2Luv(num *L, num *u, num *v, num R, num G, num B) +{ + num X, Y, Z; + Rgb2Xyz(&X, &Y, &Z, R, G, B); + Xyz2Luv(L, u, v, X, Y, Z); +} + + +void Luv2Rgb(num *R, num *G, num *B, num L, num u, num v) +{ + num X, Y, Z; + Luv2Xyz(&X, &Y, &Z, L, u, v); + Xyz2Rgb(R, G, B, X, Y, Z); +} + +void Rgb2Lch(num *L, num *C, num *H, num R, num G, num B) +{ + num X, Y, Z; + Rgb2Xyz(&X, &Y, &Z, R, G, B); + Xyz2Lch(L, C, H, X, Y, Z); +} + + +void Lch2Rgb(num *R, num *G, num *B, num L, num C, num H) +{ + num X, Y, Z; + Lch2Xyz(&X, &Y, &Z, L, C, H); + Xyz2Rgb(R, G, B, X, Y, Z); +} + + +void Rgb2Cat02lms(num *L, num *M, num *S, num R, num G, num B) +{ + num X, Y, Z; + Rgb2Xyz(&X, &Y, &Z, R, G, B); + Xyz2Cat02lms(L, M, S, X, Y, Z); +} + + +void Cat02lms2Rgb(num *R, num *G, num *B, num L, num M, num S) +{ + num X, Y, Z; + Cat02lms2Xyz(&X, &Y, &Z, L, M, S); + Xyz2Rgb(R, G, B, X, Y, Z); +} + + + +/* + * == Interface Code == + * The following is to define a function GetColorTransform with a convenient + * string-based interface. + */ + +/** @brief Convert a color space name to an integer ID */ +static int IdFromName(const char *Name) +{ + if(!strcmp(Name, "rgb") || *Name == 0) + return RGB_SPACE; + else if(!strcmp(Name, "yuv")) + return YUV_SPACE; + else if(!strcmp(Name, "ycbcr")) + return YCBCR_SPACE; + else if(!strcmp(Name, "jpegycbcr")) + return YCBCR_SPACE; + else if(!strcmp(Name, "ypbpr")) + return YPBPR_SPACE; + else if(!strcmp(Name, "ydbdr")) + return YDBDR_SPACE; + else if(!strcmp(Name, "yiq")) + return YIQ_SPACE; + else if(!strcmp(Name, "hsv") || !strcmp(Name, "hsb")) + return HSV_SPACE; + else if(!strcmp(Name, "hsl") || !strcmp(Name, "hls")) + return HSL_SPACE; + else if(!strcmp(Name, "hsi")) + return HSI_SPACE; + else if(!strcmp(Name, "xyz") || !strcmp(Name, "ciexyz")) + return XYZ_SPACE; + else if(!strcmp(Name, "lab") || !strcmp(Name, "cielab")) + return LAB_SPACE; + else if(!strcmp(Name, "luv") || !strcmp(Name, "cieluv")) + return LUV_SPACE; + else if(!strcmp(Name, "lch") || !strcmp(Name, "cielch")) + return LCH_SPACE; + else if(!strcmp(Name, "cat02lms") || !strcmp(Name, "ciecat02lms")) + return CAT02LMS_SPACE; + else + return UNKNOWN_SPACE; +} + + +/** + * @brief Given a transform string, returns a colortransform struct + * + * @param Trans a colortransform pointer to hold the transform + * @param TransformString string specifying the transformations + * @return 1 on success, 0 on failure + * + * This function provides a convenient interface to the collection of transform + * functions in this file. TransformString specifies the source and + * destination color spaces, + * TransformString = "dest<-src" + * or alternatively, + * TransformString = "src->dest". + * + * Supported color spaces are + * "RGB" sRGB Red Green Blue (ITU-R BT.709 gamma-corrected), + * "YPbPr" Luma (ITU-R BT.601) + Chroma, + * "YCbCr" Luma + Chroma ("digitized" version of Y'PbPr), + * "JPEG-YCbCr" Luma + Chroma space used in JFIF JPEG, + * "YUV" NTSC PAL Y'UV Luma + Chroma, + * "YIQ" NTSC Y'IQ Luma + Chroma, + * "YDbDr" SECAM Y'DbDr Luma + Chroma, + * "HSV" or "HSB" Hue Saturation Value/Brightness, + * "HSL" or "HLS" Hue Saturation Luminance, + * "HSI" Hue Saturation Intensity, + * "XYZ" CIE XYZ, + * "Lab" CIE L*a*b* (CIELAB), + * "Luv" CIE L*u*v* (CIELUV), + * "LCH" CIE L*C*H* (CIELCH), + * "CAT02 LMS" CIE CAT02 LMS. + * Color space names are case-insensitive and spaces are ignored. When sRGB + * is the source or destination, it can be omitted. For example "yuv<-" is + * short for "yuv<-rgb". + * + * The routine returns a colortransform structure representing the transform. + * The transform is performed by calling GetColorTransform. For example, +@code + num S[3] = {173, 0.8, 0.5}; + num D[3]; + colortransform Trans; + + if(!(GetColorTransform(&Trans, "HSI -> Lab"))) + { + printf("Invalid syntax or unknown color space\n"); + return; + } + + ApplyColorTransform(Trans, &D[0], &D[1], &D[2], S[0], S[1], S[2]); +@endcode + */ +int GetColorTransform(colortransform *Trans, const char *TransformString) +{ + int LeftNumChars = 0, RightNumChars = 0, LeftSide = 1, LeftToRight = 0; + int i, j, SrcSpaceId, DestSpaceId; + char LeftSpace[16], RightSpace[16], c; + + + Trans->NumStages = 0; + Trans->Fun[0] = 0; + Trans->Fun[1] = 0; + + /* Parse the transform string */ + while(1) + { + c = *(TransformString++); /* Read the next character */ + + if(!c) + break; + else if(c == '<') + { + LeftToRight = 0; + LeftSide = 0; + } + else if(c == '>') + { + LeftToRight = 1; + LeftSide = 0; + } + else if(c != ' ' && c != '-' && c != '=') + { + if(LeftSide) + { /* Append the character to LeftSpace */ + if(LeftNumChars < 15) + LeftSpace[LeftNumChars++] = tolower(c); + } + else + { /* Append the character to RightSpace */ + if(RightNumChars < 15) + RightSpace[RightNumChars++] = tolower(c); + } + } + } + + /* Append null terminators on the LeftSpace and RightSpace strings */ + LeftSpace[LeftNumChars] = 0; + RightSpace[RightNumChars] = 0; + + /* Convert names to colorspace enum */ + if(LeftToRight) + { + SrcSpaceId = IdFromName(LeftSpace); + DestSpaceId = IdFromName(RightSpace); + } + else + { + SrcSpaceId = IdFromName(RightSpace); + DestSpaceId = IdFromName(LeftSpace); + } + + /* Is either space is unknown? (probably a parsing error) */ + if(SrcSpaceId == UNKNOWN_SPACE || DestSpaceId == UNKNOWN_SPACE) + return 0; /* Return failure */ + + /* Is this an identity transform? */ + if(SrcSpaceId == DestSpaceId) + return 1; /* Return successfully */ + + /* Search the TransformPair table for a direct transformation */ + for(i = 0; i < NUM_TRANSFORM_PAIRS; i++) + { + if(SrcSpaceId == TransformPair[i].Space[0] + && DestSpaceId == TransformPair[i].Space[1]) + { + Trans->NumStages = 1; + Trans->Fun[0] = TransformPair[i].Fun[0]; + return 1; + } + else if(DestSpaceId == TransformPair[i].Space[0] + && SrcSpaceId == TransformPair[i].Space[1]) + { + Trans->NumStages = 1; + Trans->Fun[0] = TransformPair[i].Fun[1]; + return 1; + } + } + + /* Search the TransformPair table for a two-stage transformation */ + for(i = 1; i < NUM_TRANSFORM_PAIRS; i++) + if(SrcSpaceId == TransformPair[i].Space[1]) + for(j = 0; j < i; j++) + { + if(DestSpaceId == TransformPair[j].Space[1] + && TransformPair[i].Space[0] == TransformPair[j].Space[0]) + { + Trans->NumStages = 2; + Trans->Fun[0] = TransformPair[i].Fun[1]; + Trans->Fun[1] = TransformPair[j].Fun[0]; + return 1; + } + } + else if(DestSpaceId == TransformPair[i].Space[1]) + for(j = 0; j < i; j++) + { + if(SrcSpaceId == TransformPair[j].Space[1] + && TransformPair[j].Space[0] == TransformPair[i].Space[0]) + { + Trans->NumStages = 2; + Trans->Fun[0] = TransformPair[j].Fun[1]; + Trans->Fun[1] = TransformPair[i].Fun[0]; + return 1; + } + } + + return 0; +} + + +/** + * @brief Apply a colortransform + * + * @param Trans colortransform struct created by GetColorTransform + * @param D0, D1, D2 pointers to hold the result + * @param S0, S1, S2 the input values + */ +void ApplyColorTransform(colortransform Trans, + num *D0, num *D1, num *D2, num S0, num S1, num S2) +{ + switch(Trans.NumStages) + { + case 1: + Trans.Fun[0](D0, D1, D2, S0, S1, S2); + break; + case 2: + { + num T0, T1, T2; + Trans.Fun[0](&T0, &T1, &T2, S0, S1, S2); + Trans.Fun[1](D0, D1, D2, T0, T1, T2); + } + break; + default: + *D0 = S0; + *D1 = S1; + *D2 = S2; + break; + } +} + + +/* The code below allows this file to be compiled as a MATLAB MEX function. + * From MATLAB, the calling syntax is + * B = colorspace('dest<-src', A); + * See colorspace.m for details. + */ +#ifdef MATLAB_MEX_FILE +/** @brief MEX gateway */ +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray*prhs[]) +{ + #define S_IN prhs[0] + #define A_IN prhs[1] + #define B_OUT plhs[0] +#define IS_REAL_FULL_DOUBLE(P) (!mxIsComplex(P) \ +&& !mxIsSparse(P) && mxIsDouble(P)) + num *A, *B; + char *SBuf; + const int *Size; + colortransform Trans; + int SBufLen, NumPixels, Channel, Channel2; + + + /* Parse the input arguments */ + if(nrhs != 2) + mexErrMsgTxt("Two input arguments required."); + else if(nlhs > 1) + mexErrMsgTxt("Too many output arguments."); + + if(!mxIsChar(S_IN)) + mexErrMsgTxt("First argument should be a string."); + if(!IS_REAL_FULL_DOUBLE(A_IN)) + mexErrMsgTxt("Second argument should be a real full double array."); + + Size = mxGetDimensions(A_IN); + + if(mxGetNumberOfDimensions(A_IN) > 3 + || Size[mxGetNumberOfDimensions(A_IN) - 1] != 3) + mexErrMsgTxt("Second argument should be an Mx3 or MxNx3 array."); + + /* Read the color transform from S */ + SBufLen = mxGetNumberOfElements(S_IN)*sizeof(mxChar) + 1; + SBuf = mxMalloc(SBufLen); + mxGetString(S_IN, SBuf, SBufLen); + + if(!(GetColorTransform(&Trans, SBuf))) + mexErrMsgTxt("Invalid syntax or unknown color space."); + + mxFree(SBuf); + + A = (num *)mxGetData(A_IN); + NumPixels = mxGetNumberOfElements(A_IN)/3; + + /* Create the output image */ + B_OUT = mxCreateDoubleMatrix(0, 0, mxREAL); + mxSetDimensions(B_OUT, Size, mxGetNumberOfDimensions(A_IN)); + mxSetData(B_OUT, B = mxMalloc(sizeof(num)*mxGetNumberOfElements(A_IN))); + + Channel = NumPixels; + Channel2 = NumPixels*2; + + /* Apply the color transform */ + while(NumPixels--) + { + ApplyColorTransform(Trans, B, B + Channel, B + Channel2, + A[0], A[Channel], A[Channel2]); + A++; + B++; + } + + return; +} +#endif diff --git a/Functions/helper_functions_community/colorspace/colorspace.h b/Functions/helper_functions_community/colorspace/colorspace.h new file mode 100644 index 0000000..8559840 --- /dev/null +++ b/Functions/helper_functions_community/colorspace/colorspace.h @@ -0,0 +1,72 @@ +/** + * @file colorspace.h + * @author Pascal Getreuer 2005-2010 + */ +#ifndef _COLORSPACE_H_ +#define _COLORSPACE_H_ + +/** @brief Datatype to use for representing real numbers + * Set this typedef to either double or float depending on the application. + */ +typedef double num; + + +/** @brief XYZ color of the D65 white point */ +#define WHITEPOINT_X 0.950456 +#define WHITEPOINT_Y 1.0 +#define WHITEPOINT_Z 1.088754 + + + +/** @brief struct for representing a color transform */ +typedef struct +{ + int NumStages; + void (*Fun[2])(num*, num*, num*, num, num, num); +} colortransform; + +int GetColorTransform(colortransform *Trans, const char *TransformString); +void ApplyColorTransform(colortransform Trans, + num *D0, num *D1, num *D2, num S0, num S1, num S2); + +void Rgb2Yuv(num *Y, num *U, num *V, num R, num G, num B); +void Yuv2Rgb(num *R, num *G, num *B, num Y, num U, num V); +void Rgb2Ycbcr(num *Y, num *Cb, num *Cr, num R, num G, num B); +void Ycbcr2Rgb(num *R, num *G, num *B, num Y, num Cb, num Cr); +void Rgb2Jpegycbcr(num *R, num *G, num *B, num Y, num Cb, num Cr); +void Jpegycbcr2Rgb(num *R, num *G, num *B, num Y, num Cb, num Cr); +void Rgb2Ypbpr(num *Y, num *Pb, num *Pr, num R, num G, num B); +void Ypbpr2Rgb(num *R, num *G, num *B, num Y, num Pb, num Pr); +void Rgb2Ydbdr(num *Y, num *Db, num *Dr, num R, num G, num B); +void Ydbdr2Rgb(num *R, num *G, num *B, num Y, num Db, num Dr); +void Rgb2Yiq(num *Y, num *I, num *Q, num R, num G, num B); +void Yiq2Rgb(num *R, num *G, num *B, num Y, num I, num Q); + +void Rgb2Hsv(num *H, num *S, num *V, num R, num G, num B); +void Hsv2Rgb(num *R, num *G, num *B, num H, num S, num V); +void Rgb2Hsl(num *H, num *S, num *L, num R, num G, num B); +void Hsl2Rgb(num *R, num *G, num *B, num H, num S, num L); +void Rgb2Hsi(num *H, num *S, num *I, num R, num G, num B); +void Hsi2Rgb(num *R, num *G, num *B, num H, num S, num I); + +void Rgb2Xyz(num *X, num *Y, num *Z, num R, num G, num B); +void Xyz2Rgb(num *R, num *G, num *B, num X, num Y, num Z); +void Xyz2Lab(num *L, num *a, num *b, num X, num Y, num Z); +void Lab2Xyz(num *X, num *Y, num *Z, num L, num a, num b); +void Xyz2Luv(num *L, num *u, num *v, num X, num Y, num Z); +void Luv2Xyz(num *X, num *Y, num *Z, num L, num u, num v); +void Xyz2Lch(num *L, num *C, num *H, num X, num Y, num Z); +void Lch2Xyz(num *X, num *Y, num *Z, num L, num C, num H); +void Xyz2Cat02lms(num *L, num *M, num *S, num X, num Y, num Z); +void Cat02lms2Xyz(num *X, num *Y, num *Z, num L, num M, num S); + +void Rgb2Lab(num *L, num *a, num *b, num R, num G, num B); +void Lab2Rgb(num *R, num *G, num *B, num L, num a, num b); +void Rgb2Luv(num *L, num *u, num *v, num R, num G, num B); +void Luv2Rgb(num *R, num *G, num *B, num L, num u, num v); +void Rgb2Lch(num *L, num *C, num *H, num R, num G, num B); +void Lch2Rgb(num *R, num *G, num *B, num L, num C, num H); +void Rgb2Cat02lms(num *L, num *M, num *S, num R, num G, num B); +void Cat02lms2Rgb(num *R, num *G, num *B, num L, num M, num S); + +#endif /* _COLORSPACE_H_ */ diff --git a/Functions/helper_functions_community/colorspace/colorspace.m b/Functions/helper_functions_community/colorspace/colorspace.m new file mode 100644 index 0000000..b16f461 --- /dev/null +++ b/Functions/helper_functions_community/colorspace/colorspace.m @@ -0,0 +1,493 @@ +function varargout = colorspace(Conversion,varargin) +%COLORSPACE Transform a color image between color representations. +% B = COLORSPACE(S,A) transforms the color representation of image A +% where S is a string specifying the conversion. The input array A +% should be a real full double array of size Mx3 or MxNx3. The output B +% is the same size as A. +% +% S tells the source and destination color spaces, S = 'dest<-src', or +% alternatively, S = 'src->dest'. Supported color spaces are +% +% 'RGB' sRGB IEC 61966-2-1 +% 'YCbCr' Luma + Chroma ("digitized" version of Y'PbPr) +% 'JPEG-YCbCr' Luma + Chroma space used in JFIF JPEG +% 'YDbDr' SECAM Y'DbDr Luma + Chroma +% 'YPbPr' Luma (ITU-R BT.601) + Chroma +% 'YUV' NTSC PAL Y'UV Luma + Chroma +% 'YIQ' NTSC Y'IQ Luma + Chroma +% 'HSV' or 'HSB' Hue Saturation Value/Brightness +% 'HSL' or 'HLS' Hue Saturation Luminance +% 'HSI' Hue Saturation Intensity +% 'XYZ' CIE 1931 XYZ +% 'Lab' CIE 1976 L*a*b* (CIELAB) +% 'Luv' CIE L*u*v* (CIELUV) +% 'LCH' CIE L*C*H* (CIELCH) +% 'CAT02 LMS' CIE CAT02 LMS +% +% All conversions assume 2 degree observer and D65 illuminant. +% +% Color space names are case insensitive and spaces are ignored. When +% sRGB is the source or destination, it can be omitted. For example +% 'yuv<-' is short for 'yuv<-rgb'. +% +% For sRGB, the values should be scaled between 0 and 1. Beware that +% transformations generally do not constrain colors to be "in gamut." +% Particularly, transforming from another space to sRGB may obtain +% R'G'B' values outside of the [0,1] range. So the result should be +% clamped to [0,1] before displaying: +% image(min(max(B,0),1)); % Clamp B to [0,1] and display +% +% sRGB (Red Green Blue) is the (ITU-R BT.709 gamma-corrected) standard +% red-green-blue representation of colors used in digital imaging. The +% components should be scaled between 0 and 1. The space can be +% visualized geometrically as a cube. +% +% Y'PbPr, Y'CbCr, Y'DbDr, Y'UV, and Y'IQ are related to sRGB by linear +% transformations. These spaces separate a color into a grayscale +% luminance component Y and two chroma components. The valid ranges of +% the components depends on the space. +% +% HSV (Hue Saturation Value) is related to sRGB by +% H = hexagonal hue angle (0 <= H < 360), +% S = C/V (0 <= S <= 1), +% V = max(R',G',B') (0 <= V <= 1), +% where C = max(R',G',B') - min(R',G',B'). The hue angle H is computed on +% a hexagon. The space is geometrically a hexagonal cone. +% +% HSL (Hue Saturation Lightness) is related to sRGB by +% H = hexagonal hue angle (0 <= H < 360), +% S = C/(1 - |2L-1|) (0 <= S <= 1), +% L = (max(R',G',B') + min(R',G',B'))/2 (0 <= L <= 1), +% where H and C are the same as in HSV. Geometrically, the space is a +% double hexagonal cone. +% +% HSI (Hue Saturation Intensity) is related to sRGB by +% H = polar hue angle (0 <= H < 360), +% S = 1 - min(R',G',B')/I (0 <= S <= 1), +% I = (R'+G'+B')/3 (0 <= I <= 1). +% Unlike HSV and HSL, the hue angle H is computed on a circle rather than +% a hexagon. +% +% CIE XYZ is related to sRGB by inverse gamma correction followed by a +% linear transform. Other CIE color spaces are defined relative to XYZ. +% +% CIE L*a*b*, L*u*v*, and L*C*H* are nonlinear functions of XYZ. The L* +% component is designed to match closely with human perception of +% lightness. The other two components describe the chroma. +% +% CIE CAT02 LMS is the linear transformation of XYZ using the MCAT02 +% chromatic adaptation matrix. The space is designed to model the +% response of the three types of cones in the human eye, where L, M, S, +% correspond respectively to red ("long"), green ("medium"), and blue +% ("short"). + +% Pascal Getreuer 2005-2010 + + +%%% Input parsing %%% +if nargin < 2, error('Not enough input arguments.'); end +[SrcSpace,DestSpace] = parse(Conversion); + +if nargin == 2 + Image = varargin{1}; +elseif nargin >= 3 + Image = cat(3,varargin{:}); +else + error('Invalid number of input arguments.'); +end + +FlipDims = (size(Image,3) == 1); + +if FlipDims, Image = permute(Image,[1,3,2]); end +if ~isa(Image,'double'), Image = double(Image)/255; end +if size(Image,3) ~= 3, error('Invalid input size.'); end + +SrcT = gettransform(SrcSpace); +DestT = gettransform(DestSpace); + +if ~ischar(SrcT) && ~ischar(DestT) + % Both source and destination transforms are affine, so they + % can be composed into one affine operation + T = [DestT(:,1:3)*SrcT(:,1:3),DestT(:,1:3)*SrcT(:,4)+DestT(:,4)]; + Temp = zeros(size(Image)); + Temp(:,:,1) = T(1)*Image(:,:,1) + T(4)*Image(:,:,2) + T(7)*Image(:,:,3) + T(10); + Temp(:,:,2) = T(2)*Image(:,:,1) + T(5)*Image(:,:,2) + T(8)*Image(:,:,3) + T(11); + Temp(:,:,3) = T(3)*Image(:,:,1) + T(6)*Image(:,:,2) + T(9)*Image(:,:,3) + T(12); + Image = Temp; +elseif ~ischar(DestT) + Image = rgb(Image,SrcSpace); + Temp = zeros(size(Image)); + Temp(:,:,1) = DestT(1)*Image(:,:,1) + DestT(4)*Image(:,:,2) + DestT(7)*Image(:,:,3) + DestT(10); + Temp(:,:,2) = DestT(2)*Image(:,:,1) + DestT(5)*Image(:,:,2) + DestT(8)*Image(:,:,3) + DestT(11); + Temp(:,:,3) = DestT(3)*Image(:,:,1) + DestT(6)*Image(:,:,2) + DestT(9)*Image(:,:,3) + DestT(12); + Image = Temp; +else + Image = feval(DestT,Image,SrcSpace); +end + +%%% Output format %%% +if nargout > 1 + varargout = {Image(:,:,1),Image(:,:,2),Image(:,:,3)}; +else + if FlipDims, Image = permute(Image,[1,3,2]); end + varargout = {Image}; +end + +return; + + +function [SrcSpace,DestSpace] = parse(Str) +% Parse conversion argument + +if ischar(Str) + Str = lower(strrep(strrep(Str,'-',''),'=','')); + k = find(Str == '>'); + + if length(k) == 1 % Interpret the form 'src->dest' + SrcSpace = Str(1:k-1); + DestSpace = Str(k+1:end); + else + k = find(Str == '<'); + + if length(k) == 1 % Interpret the form 'dest<-src' + DestSpace = Str(1:k-1); + SrcSpace = Str(k+1:end); + else + error(['Invalid conversion, ''',Str,'''.']); + end + end + + SrcSpace = alias(SrcSpace); + DestSpace = alias(DestSpace); +else + SrcSpace = 1; % No source pre-transform + DestSpace = Conversion; + if any(size(Conversion) ~= 3), error('Transformation matrix must be 3x3.'); end +end +return; + + +function Space = alias(Space) +Space = strrep(strrep(Space,'cie',''),' ',''); + +if isempty(Space) + Space = 'rgb'; +end + +switch Space +case {'ycbcr','ycc'} + Space = 'ycbcr'; +case {'hsv','hsb'} + Space = 'hsv'; +case {'hsl','hsi','hls'} + Space = 'hsl'; +case {'rgb','yuv','yiq','ydbdr','ycbcr','jpegycbcr','xyz','lab','luv','lch'} + return; +end +return; + + +function T = gettransform(Space) +% Get a colorspace transform: either a matrix describing an affine transform, +% or a string referring to a conversion subroutine +switch Space +case 'ypbpr' + T = [0.299,0.587,0.114,0;-0.1687367,-0.331264,0.5,0;0.5,-0.418688,-0.081312,0]; +case 'yuv' + % sRGB to NTSC/PAL YUV + % Wikipedia: http://en.wikipedia.org/wiki/YUV + T = [0.299,0.587,0.114,0;-0.147,-0.289,0.436,0;0.615,-0.515,-0.100,0]; +case 'ydbdr' + % sRGB to SECAM YDbDr + % Wikipedia: http://en.wikipedia.org/wiki/YDbDr + T = [0.299,0.587,0.114,0;-0.450,-0.883,1.333,0;-1.333,1.116,0.217,0]; +case 'yiq' + % sRGB in [0,1] to NTSC YIQ in [0,1];[-0.595716,0.595716];[-0.522591,0.522591]; + % Wikipedia: http://en.wikipedia.org/wiki/YIQ + T = [0.299,0.587,0.114,0;0.595716,-0.274453,-0.321263,0;0.211456,-0.522591,0.311135,0]; +case 'ycbcr' + % sRGB (range [0,1]) to ITU-R BRT.601 (CCIR 601) Y'CbCr + % Wikipedia: http://en.wikipedia.org/wiki/YCbCr + % Poynton, Equation 3, scaling of R'G'B to Y'PbPr conversion + T = [65.481,128.553,24.966,16;-37.797,-74.203,112.0,128;112.0,-93.786,-18.214,128]; +case 'jpegycbcr' + % Wikipedia: http://en.wikipedia.org/wiki/YCbCr + T = [0.299,0.587,0.114,0;-0.168736,-0.331264,0.5,0.5;0.5,-0.418688,-0.081312,0.5]*255; +case {'rgb','xyz','hsv','hsl','lab','luv','lch','cat02lms'} + T = Space; +otherwise + error(['Unknown color space, ''',Space,'''.']); +end +return; + + +function Image = rgb(Image,SrcSpace) +% Convert to sRGB from 'SrcSpace' +switch SrcSpace +case 'rgb' + return; +case 'hsv' + % Convert HSV to sRGB + Image = huetorgb((1 - Image(:,:,2)).*Image(:,:,3),Image(:,:,3),Image(:,:,1)); +case 'hsl' + % Convert HSL to sRGB + L = Image(:,:,3); + Delta = Image(:,:,2).*min(L,1-L); + Image = huetorgb(L-Delta,L+Delta,Image(:,:,1)); +case {'xyz','lab','luv','lch','cat02lms'} + % Convert to CIE XYZ + Image = xyz(Image,SrcSpace); + % Convert XYZ to RGB + T = [3.2406, -1.5372, -0.4986; -0.9689, 1.8758, 0.0415; 0.0557, -0.2040, 1.057]; + R = T(1)*Image(:,:,1) + T(4)*Image(:,:,2) + T(7)*Image(:,:,3); % R + G = T(2)*Image(:,:,1) + T(5)*Image(:,:,2) + T(8)*Image(:,:,3); % G + B = T(3)*Image(:,:,1) + T(6)*Image(:,:,2) + T(9)*Image(:,:,3); % B + % Desaturate and rescale to constrain resulting RGB values to [0,1] + AddWhite = -min(min(min(R,G),B),0); + R = R + AddWhite; + G = G + AddWhite; + B = B + AddWhite; + % Apply gamma correction to convert linear RGB to sRGB + Image(:,:,1) = gammacorrection(R); % R' + Image(:,:,2) = gammacorrection(G); % G' + Image(:,:,3) = gammacorrection(B); % B' +otherwise % Conversion is through an affine transform + T = gettransform(SrcSpace); + temp = inv(T(:,1:3)); + T = [temp,-temp*T(:,4)]; + R = T(1)*Image(:,:,1) + T(4)*Image(:,:,2) + T(7)*Image(:,:,3) + T(10); + G = T(2)*Image(:,:,1) + T(5)*Image(:,:,2) + T(8)*Image(:,:,3) + T(11); + B = T(3)*Image(:,:,1) + T(6)*Image(:,:,2) + T(9)*Image(:,:,3) + T(12); + Image(:,:,1) = R; + Image(:,:,2) = G; + Image(:,:,3) = B; +end + +% Clip to [0,1] +Image = min(max(Image,0),1); +return; + + +function Image = xyz(Image,SrcSpace) +% Convert to CIE XYZ from 'SrcSpace' +WhitePoint = [0.950456,1,1.088754]; + +switch SrcSpace +case 'xyz' + return; +case 'luv' + % Convert CIE L*uv to XYZ + WhitePointU = (4*WhitePoint(1))./(WhitePoint(1) + 15*WhitePoint(2) + 3*WhitePoint(3)); + WhitePointV = (9*WhitePoint(2))./(WhitePoint(1) + 15*WhitePoint(2) + 3*WhitePoint(3)); + L = Image(:,:,1); + Y = (L + 16)/116; + Y = invf(Y)*WhitePoint(2); + U = Image(:,:,2)./(13*L + 1e-6*(L==0)) + WhitePointU; + V = Image(:,:,3)./(13*L + 1e-6*(L==0)) + WhitePointV; + Image(:,:,1) = -(9*Y.*U)./((U-4).*V - U.*V); % X + Image(:,:,2) = Y; % Y + Image(:,:,3) = (9*Y - (15*V.*Y) - (V.*Image(:,:,1)))./(3*V); % Z +case {'lab','lch'} + Image = lab(Image,SrcSpace); + % Convert CIE L*ab to XYZ + fY = (Image(:,:,1) + 16)/116; + fX = fY + Image(:,:,2)/500; + fZ = fY - Image(:,:,3)/200; + Image(:,:,1) = WhitePoint(1)*invf(fX); % X + Image(:,:,2) = WhitePoint(2)*invf(fY); % Y + Image(:,:,3) = WhitePoint(3)*invf(fZ); % Z +case 'cat02lms' + % Convert CAT02 LMS to XYZ + T = inv([0.7328, 0.4296, -0.1624;-0.7036, 1.6975, 0.0061; 0.0030, 0.0136, 0.9834]); + L = Image(:,:,1); + M = Image(:,:,2); + S = Image(:,:,3); + Image(:,:,1) = T(1)*L + T(4)*M + T(7)*S; % X + Image(:,:,2) = T(2)*L + T(5)*M + T(8)*S; % Y + Image(:,:,3) = T(3)*L + T(6)*M + T(9)*S; % Z +otherwise % Convert from some gamma-corrected space + % Convert to sRGB + Image = rgb(Image,SrcSpace); + % Undo gamma correction + R = invgammacorrection(Image(:,:,1)); + G = invgammacorrection(Image(:,:,2)); + B = invgammacorrection(Image(:,:,3)); + % Convert RGB to XYZ + T = inv([3.2406, -1.5372, -0.4986; -0.9689, 1.8758, 0.0415; 0.0557, -0.2040, 1.057]); + Image(:,:,1) = T(1)*R + T(4)*G + T(7)*B; % X + Image(:,:,2) = T(2)*R + T(5)*G + T(8)*B; % Y + Image(:,:,3) = T(3)*R + T(6)*G + T(9)*B; % Z +end +return; + + +function Image = hsv(Image,SrcSpace) +% Convert to HSV +Image = rgb(Image,SrcSpace); +V = max(Image,[],3); +S = (V - min(Image,[],3))./(V + (V == 0)); +Image(:,:,1) = rgbtohue(Image); +Image(:,:,2) = S; +Image(:,:,3) = V; +return; + + +function Image = hsl(Image,SrcSpace) +% Convert to HSL +switch SrcSpace +case 'hsv' + % Convert HSV to HSL + MaxVal = Image(:,:,3); + MinVal = (1 - Image(:,:,2)).*MaxVal; + L = 0.5*(MaxVal + MinVal); + temp = min(L,1-L); + Image(:,:,2) = 0.5*(MaxVal - MinVal)./(temp + (temp == 0)); + Image(:,:,3) = L; +otherwise + Image = rgb(Image,SrcSpace); % Convert to sRGB + % Convert sRGB to HSL + MinVal = min(Image,[],3); + MaxVal = max(Image,[],3); + L = 0.5*(MaxVal + MinVal); + temp = min(L,1-L); + S = 0.5*(MaxVal - MinVal)./(temp + (temp == 0)); + Image(:,:,1) = rgbtohue(Image); + Image(:,:,2) = S; + Image(:,:,3) = L; +end +return; + + +function Image = lab(Image,SrcSpace) +% Convert to CIE L*a*b* (CIELAB) +WhitePoint = [0.950456,1,1.088754]; + +switch SrcSpace +case 'lab' + return; +case 'lch' + % Convert CIE L*CH to CIE L*ab + C = Image(:,:,2); + Image(:,:,2) = cos(Image(:,:,3)*pi/180).*C; % a* + Image(:,:,3) = sin(Image(:,:,3)*pi/180).*C; % b* +otherwise + Image = xyz(Image,SrcSpace); % Convert to XYZ + % Convert XYZ to CIE L*a*b* + X = Image(:,:,1)/WhitePoint(1); + Y = Image(:,:,2)/WhitePoint(2); + Z = Image(:,:,3)/WhitePoint(3); + fX = f(X); + fY = f(Y); + fZ = f(Z); + Image(:,:,1) = 116*fY - 16; % L* + Image(:,:,2) = 500*(fX - fY); % a* + Image(:,:,3) = 200*(fY - fZ); % b* +end +return; + + +function Image = luv(Image,SrcSpace) +% Convert to CIE L*u*v* (CIELUV) +WhitePoint = [0.950456,1,1.088754]; +WhitePointU = (4*WhitePoint(1))./(WhitePoint(1) + 15*WhitePoint(2) + 3*WhitePoint(3)); +WhitePointV = (9*WhitePoint(2))./(WhitePoint(1) + 15*WhitePoint(2) + 3*WhitePoint(3)); + +Image = xyz(Image,SrcSpace); % Convert to XYZ +Denom = Image(:,:,1) + 15*Image(:,:,2) + 3*Image(:,:,3); +U = (4*Image(:,:,1))./(Denom + (Denom == 0)); +V = (9*Image(:,:,2))./(Denom + (Denom == 0)); +Y = Image(:,:,2)/WhitePoint(2); +L = 116*f(Y) - 16; +Image(:,:,1) = L; % L* +Image(:,:,2) = 13*L.*(U - WhitePointU); % u* +Image(:,:,3) = 13*L.*(V - WhitePointV); % v* +return; + + +function Image = lch(Image,SrcSpace) +% Convert to CIE L*ch +Image = lab(Image,SrcSpace); % Convert to CIE L*ab +H = atan2(Image(:,:,3),Image(:,:,2)); +H = H*180/pi + 360*(H < 0); +Image(:,:,2) = sqrt(Image(:,:,2).^2 + Image(:,:,3).^2); % C +Image(:,:,3) = H; % H +return; + + +function Image = cat02lms(Image,SrcSpace) +% Convert to CAT02 LMS +Image = xyz(Image,SrcSpace); +T = [0.7328, 0.4296, -0.1624;-0.7036, 1.6975, 0.0061; 0.0030, 0.0136, 0.9834]; +X = Image(:,:,1); +Y = Image(:,:,2); +Z = Image(:,:,3); +Image(:,:,1) = T(1)*X + T(4)*Y + T(7)*Z; % L +Image(:,:,2) = T(2)*X + T(5)*Y + T(8)*Z; % M +Image(:,:,3) = T(3)*X + T(6)*Y + T(9)*Z; % S +return; + + +function Image = huetorgb(m0,m2,H) +% Convert HSV or HSL hue to RGB +N = size(H); +H = min(max(H(:),0),360)/60; +m0 = m0(:); +m2 = m2(:); +F = H - round(H/2)*2; +M = [m0, m0 + (m2-m0).*abs(F), m2]; +Num = length(m0); +j = [2 1 0;1 2 0;0 2 1;0 1 2;1 0 2;2 0 1;2 1 0]*Num; +k = floor(H) + 1; +Image = reshape([M(j(k,1)+(1:Num).'),M(j(k,2)+(1:Num).'),M(j(k,3)+(1:Num).')],[N,3]); +return; + + +function H = rgbtohue(Image) +% Convert RGB to HSV or HSL hue +[M,i] = sort(Image,3); +i = i(:,:,3); +Delta = M(:,:,3) - M(:,:,1); +Delta = Delta + (Delta == 0); +R = Image(:,:,1); +G = Image(:,:,2); +B = Image(:,:,3); +H = zeros(size(R)); +k = (i == 1); +H(k) = (G(k) - B(k))./Delta(k); +k = (i == 2); +H(k) = 2 + (B(k) - R(k))./Delta(k); +k = (i == 3); +H(k) = 4 + (R(k) - G(k))./Delta(k); +H = 60*H + 360*(H < 0); +H(Delta == 0) = nan; +return; + + +function Rp = gammacorrection(R) +Rp = zeros(size(R)); +i = (R <= 0.0031306684425005883); +Rp(i) = 12.92*R(i); +Rp(~i) = real(1.055*R(~i).^0.416666666666666667 - 0.055); +return; + + +function R = invgammacorrection(Rp) +R = zeros(size(Rp)); +i = (Rp <= 0.0404482362771076); +R(i) = Rp(i)/12.92; +R(~i) = real(((Rp(~i) + 0.055)/1.055).^2.4); +return; + + +function fY = f(Y) +fY = real(Y.^(1/3)); +i = (Y < 0.008856); +fY(i) = Y(i)*(841/108) + (4/29); +return; + + +function Y = invf(fY) +Y = fY.^3; +i = (Y < 0.008856); +Y(i) = (fY(i) - 4/29)*(108/841); +return; diff --git a/Functions/helper_functions_community/linspecer.m b/Functions/helper_functions_community/linspecer.m new file mode 100644 index 0000000..31c5e6e --- /dev/null +++ b/Functions/helper_functions_community/linspecer.m @@ -0,0 +1,261 @@ +% function lineStyles = linspecer(N) +% This function creates an Nx3 array of N [R B G] colors +% These can be used to plot lots of lines with distinguishable and nice +% looking colors. +% +% lineStyles = linspecer(N); makes N colors for you to use: lineStyles(ii,:) +% +% colormap(linspecer); set your colormap to have easily distinguishable +% colors and a pleasing aesthetic +% +% lineStyles = linspecer(N,'qualitative'); forces the colors to all be distinguishable (up to 12) +% lineStyles = linspecer(N,'sequential'); forces the colors to vary along a spectrum +% +% % Examples demonstrating the colors. +% +% LINE COLORS +% N=6; +% X = linspace(0,pi*3,1000); +% Y = bsxfun(@(x,n)sin(x+2*n*pi/N), X.', 1:N); +% C = linspecer(N); +% axes('NextPlot','replacechildren', 'ColorOrder',C); +% plot(X,Y,'linewidth',5) +% ylim([-1.1 1.1]); +% +% SIMPLER LINE COLOR EXAMPLE +% N = 6; X = linspace(0,pi*3,1000); +% C = linspecer(N) +% hold off; +% for ii=1:N +% Y = sin(X+2*ii*pi/N); +% plot(X,Y,'color',C(ii,:),'linewidth',3); +% hold on; +% end +% +% COLORMAP EXAMPLE +% A = rand(15); +% figure; imagesc(A); % default colormap +% figure; imagesc(A); colormap(linspecer); % linspecer colormap +% +% See also NDHIST, NHIST, PLOT, COLORMAP, 43700-cubehelix-colormaps +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% by Jonathan Lansey, March 2009-2013 – Lansey at gmail.com % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +%% credits and where the function came from +% The colors are largely taken from: +% http://colorbrewer2.org and Cynthia Brewer, Mark Harrower and The Pennsylvania State University +% +% +% She studied this from a phsychometric perspective and crafted the colors +% beautifully. +% +% I made choices from the many there to decide the nicest once for plotting +% lines in Matlab. I also made a small change to one of the colors I +% thought was a bit too bright. In addition some interpolation is going on +% for the sequential line styles. +% +% +%% + +function lineStyles=linspecer(N,varargin) + +if nargin==0 % return a colormap + lineStyles = linspecer(128); + return; +end + +if ischar(N) + lineStyles = linspecer(128,N); + return; +end + +if N<=0 % its empty, nothing else to do here + lineStyles=[]; + return; +end + +% interperet varagin +qualFlag = 0; +colorblindFlag = 0; + +if ~isempty(varargin)>0 % you set a parameter? + switch lower(varargin{1}) + case {'qualitative','qua'} + if N>12 % go home, you just can't get this. + warning('qualitiative is not possible for greater than 12 items, please reconsider'); + else + if N>9 + warning(['Default may be nicer for ' num2str(N) ' for clearer colors use: whitebg(''black''); ']); + end + end + qualFlag = 1; + case {'sequential','seq'} + lineStyles = colorm(N); + return; + case {'white','whitefade'} + lineStyles = whiteFade(N);return; + case 'red' + lineStyles = whiteFade(N,'red');return; + case 'blue' + lineStyles = whiteFade(N,'blue');return; + case 'green' + lineStyles = whiteFade(N,'green');return; + case {'gray','grey'} + lineStyles = whiteFade(N,'gray');return; + case {'colorblind'} + colorblindFlag = 1; + otherwise + warning(['parameter ''' varargin{1} ''' not recognized']); + end +end +% *.95 +% predefine some colormaps + set3 = colorBrew2mat({[141, 211, 199];[ 255, 237, 111];[ 190, 186, 218];[ 251, 128, 114];[ 128, 177, 211];[ 253, 180, 98];[ 179, 222, 105];[ 188, 128, 189];[ 217, 217, 217];[ 204, 235, 197];[ 252, 205, 229];[ 255, 255, 179]}'); +set1JL = brighten(colorBrew2mat({[228, 26, 28];[ 55, 126, 184]; [ 77, 175, 74];[ 255, 127, 0];[ 255, 237, 111]*.85;[ 166, 86, 40];[ 247, 129, 191];[ 153, 153, 153];[ 152, 78, 163]}')); +set1 = brighten(colorBrew2mat({[ 55, 126, 184]*.85;[228, 26, 28];[ 77, 175, 74];[ 255, 127, 0];[ 152, 78, 163]}),.8); + +% colorblindSet = {[215,25,28];[253,174,97];[171,217,233];[44,123,182]}; +colorblindSet = {[215,25,28];[253,174,97];[171,217,233]*.8;[44,123,182]*.8}; + +set3 = dim(set3,.93); + +if colorblindFlag + switch N + % sorry about this line folks. kind of legacy here because I used to + % use individual 1x3 cells instead of nx3 arrays + case 4 + lineStyles = colorBrew2mat(colorblindSet); + otherwise + colorblindFlag = false; + warning('sorry unsupported colorblind set for this number, using regular types'); + end +end +if ~colorblindFlag + switch N + case 1 + lineStyles = { [ 55, 126, 184]/255}; + case {2, 3, 4, 5 } + lineStyles = set1(1:N); + case {6 , 7, 8, 9} + lineStyles = set1JL(1:N)'; + case {10, 11, 12} + if qualFlag % force qualitative graphs + lineStyles = set3(1:N)'; + else % 10 is a good number to start with the sequential ones. + lineStyles = cmap2linspecer(colorm(N)); + end + otherwise % any old case where I need a quick job done. + lineStyles = cmap2linspecer(colorm(N)); + end +end +lineStyles = cell2mat(lineStyles); + +end + +% extra functions +function varIn = colorBrew2mat(varIn) +for ii=1:length(varIn) % just divide by 255 + varIn{ii}=varIn{ii}/255; +end +end + +function varIn = brighten(varIn,varargin) % increase the brightness + +if isempty(varargin), + frac = .9; +else + frac = varargin{1}; +end + +for ii=1:length(varIn) + varIn{ii}=varIn{ii}*frac+(1-frac); +end +end + +function varIn = dim(varIn,f) + for ii=1:length(varIn) + varIn{ii} = f*varIn{ii}; + end +end + +function vOut = cmap2linspecer(vIn) % changes the format from a double array to a cell array with the right format +vOut = cell(size(vIn,1),1); +for ii=1:size(vIn,1) + vOut{ii} = vIn(ii,:); +end +end +%% +% colorm returns a colormap which is really good for creating informative +% heatmap style figures. +% No particular color stands out and it doesn't do too badly for colorblind people either. +% It works by interpolating the data from the +% 'spectral' setting on http://colorbrewer2.org/ set to 11 colors +% It is modified a little to make the brightest yellow a little less bright. +function cmap = colorm(varargin) +n = 100; +if ~isempty(varargin) + n = varargin{1}; +end + +if n==1 + cmap = [0.2005 0.5593 0.7380]; + return; +end +if n==2 + cmap = [0.2005 0.5593 0.7380; + 0.9684 0.4799 0.2723]; + return; +end + +frac=.95; % Slight modification from colorbrewer here to make the yellows in the center just a bit darker +cmapp = [158, 1, 66; 213, 62, 79; 244, 109, 67; 253, 174, 97; 254, 224, 139; 255*frac, 255*frac, 191*frac; 230, 245, 152; 171, 221, 164; 102, 194, 165; 50, 136, 189; 94, 79, 162]; +x = linspace(1,n,size(cmapp,1)); +xi = 1:n; +cmap = zeros(n,3); +for ii=1:3 + cmap(:,ii) = pchip(x,cmapp(:,ii),xi); +end +cmap = flipud(cmap/255); +end + +function cmap = whiteFade(varargin) +n = 100; +if nargin>0 + n = varargin{1}; +end + +thisColor = 'blue'; + +if nargin>1 + thisColor = varargin{2}; +end +switch thisColor + case {'gray','grey'} + cmapp = [255,255,255;240,240,240;217,217,217;189,189,189;150,150,150;115,115,115;82,82,82;37,37,37;0,0,0]; + case 'green' + cmapp = [247,252,245;229,245,224;199,233,192;161,217,155;116,196,118;65,171,93;35,139,69;0,109,44;0,68,27]; + case 'blue' + cmapp = [247,251,255;222,235,247;198,219,239;158,202,225;107,174,214;66,146,198;33,113,181;8,81,156;8,48,107]; + case 'red' + cmapp = [255,245,240;254,224,210;252,187,161;252,146,114;251,106,74;239,59,44;203,24,29;165,15,21;103,0,13]; + otherwise + warning(['sorry your color argument ' thisColor ' was not recognized']); +end + +cmap = interpomap(n,cmapp); +end + +% Eat a approximate colormap, then interpolate the rest of it up. +function cmap = interpomap(n,cmapp) + x = linspace(1,n,size(cmapp,1)); + xi = 1:n; + cmap = zeros(n,3); + for ii=1:3 + cmap(:,ii) = pchip(x,cmapp(:,ii),xi); + end + cmap = (cmap/255); % flipud?? +end + + + diff --git a/Functions/helper_functions_community/mat2tikz/.gitignore b/Functions/helper_functions_community/mat2tikz/.gitignore new file mode 100644 index 0000000..b819eb3 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/.gitignore @@ -0,0 +1,6 @@ +*.sublime-workspace +*.tap +test/*.test.* +*.asv +*.m~ +octave-workspace diff --git a/Functions/helper_functions_community/mat2tikz/.travis.yml b/Functions/helper_functions_community/mat2tikz/.travis.yml new file mode 100644 index 0000000..e8001d9 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/.travis.yml @@ -0,0 +1,16 @@ +language: c++ +before_install: + - sudo add-apt-repository -y ppa:octave/stable + - sudo apt-get update -qq + - sudo apt-get install gdb # to capture backtrace of eventual failures + - sudo apt-get install octave + - sudo apt-get purge libopenblas-base # fixes PPA Octave 4.0 crash on Travis +before_script: + - ulimit -c unlimited -S # enable core dumps for Octave crash debugging +script: + - ./runtests.sh /usr/bin/octave +notifications: + hipchat: f4c2c5f87adc85025545e5b59b3fbe@Matlab2tikz +after_failure: + - COREFILE=$(find . -maxdepth 1 -name "core*" | head -n 1) # find core file + - gdb -c "$COREFILE" -ex "thread apply all bt" -ex "set pagination 0" -batch /usr/bin/octave-cli # print stack trace diff --git a/Functions/helper_functions_community/mat2tikz/AUTHORS.md b/Functions/helper_functions_community/mat2tikz/AUTHORS.md new file mode 100644 index 0000000..abe2056 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/AUTHORS.md @@ -0,0 +1,64 @@ +# Maintainer + * [Egon Geerardyn](https://github.com/egeerardyn) is the current maintainer (2015 - now). + * [Nico Schlömer](https://github.com/nschloe) designed and implemented the intial version and was the first maintainer (2008 - 2015). + +# Contributors +Thanks for patches, suggestions, and other contributions go to: + + * [Ben Abbott](https://github.com/bpabbott) + * Martijn Aben (The MathWorks) + * [Nicolas Alt](https://github.com/nalt) + * [Eshwar Andhavarapu](https://github.com/gontadu) + * [Matt Bauman](https://github.com/mbauman) + * Eike Blechschmidt + * [Klaus Broelemann](https://github.com/Broele) + * [Katherine Elkington](https://github.com/kelkington) + * [Thomas Emmert](https://github.com/murmlgrmpf) + * Andreas Gäb + * [Egon Geerardyn](https://github.com/egeerardyn) + * Roman Gesenhues + * Michael Glasser (The MathWorks) + * [David Haberthür](https://github.com/habi) + * [Patrick Häcker](https://github.com/MagicMuscleMan) + * [Ulrich Herter](https://github.com/ulijh) + * [David Horsley](https://github.com/widdma) + * Kári Hreinsson + * [Lucas Jeub](https://github.com/LJeub) + * [Martin Kiefel](https://github.com/mkiefel) + * [Andreas Kloeckner](https://github.com/akloeckner) + * Mykel Kochenderfer + * [Oleg Komarov](https://github.com/okomarov) + * Henk Kortier + * [Tom Lankhorst](https://github.com/tomlankhorst) + * [Burkart Lingner](https://github.com/burkart) + * Theo Markettos + * [Dragan Mitrevski](https://github.com/nidrosianDeath) + * [Jason Monschke](https://github.com/jam4375) + * Francesco Montorsi + * Ricardo Santiago Mozos + * Johannes Mueller-Roemer + * [Ali Ozdagli](https://github.com/aliirmak) + * [Richard Peschke](https://github.com/RPeschke) + * [Peter Ploß](https://github.com/PeterPablo) + * Julien Ridoux + * [Christoph Rüdiger](https://github.com/mredd) + * Carlos Russo + * [Michael Schellenberger Costa](https://github.com/miscco) + * [Manuel Schiller](https://github.com/dachziegel) + * [Nico Schlömer](https://github.com/nschloe) + * Johannes Schmitz + * Michael Schoeberl + * [Jan Taro Svejda](https://github.com/JTSvejda) + * [José Vallet](https://github.com/josombio) + * [Thomas Wagner](https://github.com/Aikhjarto) + * Donghua Wang + * [Patrick Wang](https://github.com/patrickkwang) + * Robert Whittlesey + * Pooya Ziraksaz + * Bastiaan Zuurendonk (The MathWorks) + * GitHub users: [andreas12345](https://github.com/andreas12345), [karih](https://github.com/karih), [theswitch](https://github.com/theswitch) + +# Acknowledgements +Matlab2tikz has once greatly profited from its ancestor: [Matfig2PGF](http://www.mathworks.com/matlabcentral/fileexchange/12962) written by Paul Wagenaars. + +Also, the authors would like to thank [Christian Feuersänger](https://github.com/cfeuersaenger) for the [Pgfplots](http://pgfplots.sourceforge.net) package which forms the basis for the matlab2tikz output on the LaTeX side. diff --git a/Functions/helper_functions_community/mat2tikz/CHANGELOG.md b/Functions/helper_functions_community/mat2tikz/CHANGELOG.md new file mode 100644 index 0000000..2529df2 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/CHANGELOG.md @@ -0,0 +1,439 @@ +# 2016-08-15 Version 1.1.0 [Egon Geerardyn](egon.geerardyn@gmail.com) + + * Added or improved support for: + - Octave 4.0 (#759) + - `scatter`, `quiver` and `errorbar` support in Octave (#669) + - `cleanfigure` has been improved: + * New and superior (Opheim) simplification algorithm + * Simplification for `plot3` (3D plots) (#790) + * Vectorized implementations (#756, #737) + * Overall clean-up of the code (#797, #787, #776, #744) + * Optional limitation of data precision (#791) + * Textbox removal is being phased out (#817) + - Quiver plots now translate to native pgfplots quivers (#679, #690) + - Legends, especially with `plotyy`, now use `\label` (#140, #760, #773) + - Tick labels with `datetime` (#383, #803) + - `contourf`/`contour` plots with matrix arguments and nonstandard line widths (#592, #721, #722, #871) + - Colored ticks and axes (#880, #908) + - Scatter plots with different marker colors and sizes (#859, #861) + - `colorbar` positioning and tick placement (#933, #937, #941) + - The self-updater has been improved + * New parameters: + - `arrowHeadSizeFactor` for tweaking the size of arrowheads + - `semanticLineWidths` for tweaking semantic line width conversion (e.g. `thick` instead of `0.8pt`) + * Extra requirements: + - Quiver plots require `\usetikzlibrary{arrows.meta}` + * Bug fixes: + - Errorbars without lines & markers (#813) + - `light`/`camera` objects are now ignored (#684) + - Draw baseline in bar/stem plots (#798) + - Multiple annotation containers (#728, #730) + - Legends of bode plots (#700, #702) + - Titles of bode plots (#715, #716, #753) + - Patch without fill/edge color (#682, #701, #740) + - Warn about usage of faceted interp shader (#699) + - Tick labels are properly escaped now (#711) + - Swapped image dimensions (#714) + - Width of bar plots was incorrect (#727, #696) + - Stacking and placement of bar plots (#851, #845, #840, #785, #903) + - Handling of tick labels when `parseStrings=false` (#86, #871) + - Properly escape tick labels for LaTeX (#710, #711, #820, #821) + - Respect edge color in `scatter` plots (#900) + - Output directory is created automatically (#889, #929) + - TikZ output format has been improved slightly (#936, #921, #801) + * For developers: + - Please check out the (guidelines)[CONTRIBUTING.md] + - We now use `allchild` and `findall` (#718) + - SublimeText project files + - Test hashes can be saved selectively (#720) + - Continuous testing for MATLAB and Octave 3.8 with Jenkins + - Test suite timing is tracked (#738) + - The testing reports have been improved for GitHub (#708) + - Testing can output to different directories (#818) + - A new tool to help track regressions (#814) + - A new tool to consistently format the code (#808, #809) + - `figure2dot` updated for HG2 + +# 2015-06-15 Version 1.0.0 [Egon Geerardyn](egon.geerardyn@gmail.com) + + * Added support for: + - Annotations (except arrows) in R2014b (#534) + - `Histogram` in R2014b (#525) + - Filled contour plots in R2014b (#379, #500) + - Contour plots with color maps in R2014b (#380, #500) + - Axes background color and overlap (#6, #509, #510) + - Horizontal/Vertical text alignment (#491) + * Extra requirements: + - Patch plots now require `\usepgfplotslibrary{patchplots}` (#386, #497) + * Bug fixes: + - Pgfplots 1.12 (`row sep=crcr`) in combination with `externalData==true` (#548) + - Updater has been fixed (#502) + - 3D plot sizing takes viewing angle into account (#560, #630, #631) + - Alpha channel (transparency) in images (#561) + - Colorbar labels in R2014b (#429, #488) + - Scaling of color data at axes level (#486) + - Text formatting (for `TeX` parser) is improved (#417) + - Support for `|` character in labels (#587, #589) + - Legends for `stairs` and `area` plots (#601, #602) + - `cleanfigure()` removes points outside of the axes for `stairs` plots (#226, #533) + - `cleanfigure()` removes points outside of the axes better (#392, #400, #547) + - Support `>` and `<` in text (#522) + - Better text positioning (#518) + - Text boxes on 3D graphs (#528) + - File closing is more robust (#496, #555) + - TikZ picture output, i.e.`imageAsPng==false`, improved (#581, #596) + - `standalone==true` sets the font and input encoding in LaTeX (#590) + - Legend text alignment in Octave (#668) + - Improved Octave legend if not all lines have an entry (#607, #619, #653) + - Legend without a drawn box in R2014b+ (#652) + - Misc. fixes: #426, #513, #520, #665 + * For developers: + - The testing framework has been revamped (see also `test/README.md`) + - A lot of the tests have been updated (#604, #614, #638, ...) + - Cyclomatic complexity of the code has been reduced (#391) + - Repository has been moved to [matlab2tikz/matlab2tikz](https://github.com/matlab2tikz/matlab2tikz) + - Extra files have been pruned (#616) + +# 2014-11-02 Version 0.6.0 [Nico Schlömer](nico.schloemer@gmail.com) + + * Annotation support in R2014a and earlier + * New subplot positioning approach (by Klaus Broelemann) that uses absolute instead of relative positions. + * Support stacked bar plots and others in the same axes (needs pgfplots 1.11). + * Support legends with multiline entries. + * Support for the alpha channel in PNG output. + * Test framework updated and doesn't display figures by default. + * Major code clean-up and code complexity checks. + * Bug fixes: + - Cycle paths only when needed (#317, #49, #404) + - Don't use infinite xmin/max, etc. (#436) + - Warn about the `noSize` parameter (#431) + - Images aren't flipped anymore (#401) + - No scientific notation in width/height (#396) + - Axes with custom colors (#376) + - Mesh plots are exported properly (#382) + - Legend colors are handled better (#389) + - Handle Z axis properties for quiver3 (#406) + - Better text handling, e.g. degrees (#402) + - Don't output absolute paths into TikZ by default + - ... + +# 2014-10-20 Version 0.5.0 [Nico Schlömer](nico.schloemer@gmail.com) + + * Support for MATLAB 2014b (with it's substantial graphics changes). + All credit goes to Egon Geerardyn. + * Bugfixes: + - single bar width + - invisible bar plots + - surface options + - patch plots and cycling + - patches with literal colors + +# 2014-03-07 Version 0.4.7 [Nico Schlömer](nico.schloemer@gmail.com) + + * Acid tests: Remove MATLAB-based `eps2pdf`. + * Bugfixes: + - multiple patches + - log plot with nonzero baseline + - marker options for scatter plots + - table data formatting + - several fixes for Octave + +# 2014-02-07 Version 0.4.6 [Nico Schlömer](nico.schloemer@gmail.com) + + * Set `externalData` default to `false`. + * Properly check for required Pgfplots version. + * Marker scaling in scatter plots. + +# 2014-02-02 Version 0.4.5 [Nico Schlömer](nico.schloemer@gmail.com) + + * Arrange data in tables. + * Optionally define custom colors. + * Allow for strict setting of font sizes. + * Bugfixes: + - tick labels for log plots + - tick labels with commas + +# 2014-01-02 Version 0.4.4 [Nico Schlömer](nico.schloemer@gmail.com) + + * Support for color maps with scatter plots. + * Support for different-length up-down error bars. + * Input options validation. + * Bugfixes: + - legends for both area and line plots + - invisible text fields + +# 2013-10-20 Version 0.4.3 [Nico Schlömer](nico.schloemer@gmail.com) + + * Support for 3D quiver plots. + * Extended support for colorbar axis options. + * New logo! + * Bugfixes: + - text generation + - extraCode option + - join strings + - ... + +# 2013-09-12 Version 0.4.2 [Nico Schlömer](nico.schloemer@gmail.com) + + * Support for explicit color specification in 3D plots. + * Better color handling for patch plots. + * Support for various unicode characters. + * Bugfixes: + - edge colors for bar plots + - multiple color bars + - ... + +# 2013-08-14 Version 0.4.1 [Nico Schlömer](nico.schloemer@gmail.com) + + * Replaced option `extraTikzpictureCode` by `extraCode` + for inserting code at the beginning of the file. + * Support for relative text positioning. + * Improved documentation. + * Code cleanup: moved all figure manipulations over to cleanfigure() + * Bugfixes: + - error bars + - empty tick labels + - ... + +# 2013-06-26 Version 0.4.0 [Nico Schlömer](nico.schloemer@gmail.com) + + * Added `cleanfigure()` for removing unwanted entities from a plot + before conversion + * Add option `floatFormat` to allow for custom specification of the format + of float numbers + * Bugfixes: + - linewidth for patches + - ... + +# 2013-04-13 Version 0.3.3 [Nico Schlömer](nico.schloemer@gmail.com) + + * Support for: + - pictures in LaTeX subfloats + * Bugfixes: + - axes labels + - extra* options + - logscaled axes + - ... + +# 2013-03-14 Version 0.3.2 [Nico Schlömer](nico.schloemer@gmail.com) + + * Support for: + - waterfall plots + * Bugfixes: + - axis locations + - color handling + - stacked bars + - ... + +# 2013-02-15 Version 0.3.1 [Nico Schlömer](nico.schloemer@gmail.com) + + * Use `table{}` for plots for cleaner output files. + * Support for: + - hg transformations + - pcolor plots + * Removed command line options: + - `minimumPointsDistance` + * Bugfixes: + - legend positioning and alignment + - tick labels + - a bunch of fixed for Octave + - line width for markers + - axis labels for color bars + - image trimming + - subplots with bars + - ... + +# 2012-11-19 Version 0.3.0 [Nico Schlömer](nico.schloemer@gmail.com) + + * Support for: + - area plots + - legend position + - inner color bars + - log-scaled color bars + * New command line options: + - `standalone` (create compilable TeX file) + - `checkForUpdates` + * `mlint` cleanups. + * Removed deprecated options. + * Bugfixes: + - colorbar-axis association + - option parsing + - automatic updater + - unit 'px' + - ... + +# 2012-09-01 Version 0.2.3 [Nico Schlömer](nico.schloemer@gmail.com) + + * Multiline text for all entities. + * Support for logical images. + * Support for multiple legends (legends in subplots). + * Fixed version check bug. + * Fix `minimumPointsDistance`. + +# 2012-07-19 Version 0.2.2 [Nico Schlömer](nico.schloemer@gmail.com) + + * Support for multiline titles and axis labels. + * Respect log-scaled axes for `minimumPointsDistance`. + * Add support for automatic graph labels via new option. + * About 5 bugfixes. + +# 2012-05-04 Version 0.2.1 [Nico Schlömer](nico.schloemer@gmail.com) + + * Support for color maps. + * Support for native color bars. + * Partial support for hist3 plots. + * Support for spectrogram plots. + * Support for rotated text. + * Native handling of `Inf`s and `NaN`s. + * Better info text. + * matlab2tikz version checking. + * Line plotting code cleanup. + * About 10 bugfixes. + +# 2012-03-17 Version 0.2.0 [Nico Schlömer](nico.schloemer@gmail.com) + + * Greatly overhauled text handling. (Burkhart Lingner) + * Added option `tikzFileComment`. + * Added option `parseStrings`. + * Added option `extraTikzpictureSettings`. + * Added proper documetion (for `help matlab2tikz`). + * Improved legend positioning, orientation. + * Support for horizontal bar plots. + * Get bar widths right. + * Doubles are plottet with 15-digit precision now. + * Support for rectangle objects. + * Better color handling. + * Testing framework improvements. + * Several bugfixes: + - ticks handled more concisely + - line splitting bugs + - ... + +# 2011-11-22 Version 0.1.4 [Nico Schlömer](nico.schloemer@gmail.com) + + * Support for scatter 3D plots. + * Support for 3D parameter curves. + * Support for 3D patches. + * Support for minor ticks. + * Add option `interpretTickLabelsAsTex` (default `false`). + * Several bugfixes: + - `%` sign in annotations + - fixed `\omega` and friends in annotations + - proper legend for bar plots + - don't override PNG files if there is more than one image plot + - don't always close patch paths + +# 2011-08-22 Version 0.1.3 [Nico Schlömer](nico.schloemer@gmail.com) + + * Greatly overhauled text handling. + * Better Octave compatibility. + * Several bugfixes: + - subplot order + - environment detection + + +# 2011-06-02 Version 0.1.2 [Nico Schlömer](nico.schloemer@gmail.com) + + * Support for logscaled color bar. + * Support for truecolor images. + * Initial support for text handles. + * Speed up processing for line plots. + * Several bugfixes: + - axis labels, tick labels, etc. for z-axis + - marker handling for scatter plots + - fix for unicolor scatter plots + +# 2011-04-06 Version 0.1.1 [Nico Schlömer](nico.schloemer@gmail.com) + + * Improved Octave compatibility. + * Several bugfixes: + - input parser + +# 2011-01-31 Version 0.1.0 [Nico Schlömer](nico.schloemer@gmail.com) + + * Basic Octave compatibility. + * Several bugfixes: + - bar plots fix (thanks to Christoph Rüdiger) + - fix legends with split graphs + +# 2010-09-10 Version 0.0.7 [Nico Schlömer](nico.schloemer@gmail.com) + + * Compatibility fixes for older MATLAB installations. + * Several bugfixes: + - line plots with only one point + - certain surface plots + - orientation of triangle markers (`<` vs. `>`) + - display of the color `purple` + +# 2010-05-06 Version 0.0.6 [Nico Schlömer](nico.schloemer@gmail.com) + + * Support for scatter plots. + * Preliminary support for surface plots; thanks to Pooya. + * Large changes in the codebase: + - next to `matlab2tikz.m`, the file `pgfplotsEnvironment.m` is now needed as well; it provides a much better structured approach to storing and writing environments when parsing the MATLAB(R) figure + * proper MATLAB(R) version check + * lots of small fixes + +# 2009-12-21 Version 0.0.5 [Nico Schlömer](nico.schloemer@ua.ac.be) + + * Improvements in axis handling: + - colored axes + - allow different left and right ordinates + * Improvements for line plots: + - far outliers are moved toward the plot, + avoiding `Dimension too large`-type errors in LaTeX + - optional point reduction by new option `minimumPointsDistance` + * Improvements for image handling: + - creation of a PNG file, added by `\addplot graphics` + - fixed axis orientation bug + * Bugfixes for: + - multiple axes + - CMYK colors + - legend text alignment (thanks Dragan Mitrevski) + - transparent patches (thanks Carlos Russo) + * Added support for: + - background color + - Bode plots + - zplane plots + - freqz plots + +# 2009-06-09 Version 0.0.4 [Nico Schlömer](nico.schloemer@ua.ac.be) + + * Added support for: + - error bars (thanks Robert Whittlesey for the suggestion) + * Improvents in: + - legends (thanks Theo Markettos for the patch), + - images, + - quiver plots (thanks Robert for spotting this). + * Improved options handling. + * Allow for custom file encoding (thanks Donghua Wang for the suggestion). + * Numerous bugfixes (thanks Andreas Gäb). + +# 2009-03-08 Version 0.0.3 [Nico Schlömer](nico.schloemer@ua.ac.be) + + * Added support for: + - subplots + - reverse axes + * Completed support for: + - images + +# 2009-01-08 Version 0.0.2 [Nico Schlömer](nico.schloemer@ua.ac.be) + + * Added support for: + - quiver (arrow) plots + - bar plots + - stem plots + - stairs plots + * Added preliminary support for: + - images + - rose plots + - compass plots + - polar plots + * Moreover, large code improvement have been introduced, notably: + - aspect ratio handling + - color handling + - plot options handling + +# 2008-11-07 Version 0.0.1 [Nico Schlömer](nico.schloemer@ua.ac.be) + + * Initial version diff --git a/Functions/helper_functions_community/mat2tikz/CONTRIBUTING.md b/Functions/helper_functions_community/mat2tikz/CONTRIBUTING.md new file mode 100644 index 0000000..d64abe9 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/CONTRIBUTING.md @@ -0,0 +1,66 @@ +# Contributing to matlab2tikz + +You can contribute in many ways to `matlab2tikz`: + + - report bugs, + - suggest new features, + - write documentation, + - fix some of our bugs and implement new features. + +The first part of this document is geared more towards users of `matlab2tikz`. +The latter part is only relevant if you want to write some code for `matlab2tikz`. + +## How to report a bug or ask for help + + 1. Make sure you are using the [latest release](https://github.com/matlab2tikz/matlab2tikz/releases/latest) or even the [development version](https://github.com/matlab2tikz/matlab2tikz/tree/develop) of `matlab2tikz` and check that the problem still exists. + 2. Also make sure you are using a recent version of the required LaTeX packages (especially [`pgfplots`](http://ctan.org/pkg/pgfplots) and the [`TikZ`](http://ctan.org/pkg/pgf) libraries) + 3. You can submit your bug report or question to our [issue tracker](https://github.com/matlab2tikz/matlab2tikz/issues). + Please, have a look at "[How to Ask Questions the Smart Way](http://www.catb.org/esr/faqs/smart-questions.html)" and "[Writing Better Bug Reports](http://martiancraft.com/blog/2014/07/good-bug-reports/)" for generic guidelines. In short: + - Mention the version of MATLAB/Octave, the operating system, `matlab2tikz`, `pgfplots` and which `LaTeX` compiler you are using. + - Choose a descriptive title for your issue report. + - A short MATLAB code snippet that generates a plot where the problem occurs. Please limit this to what is strictly necessary to show the issue! + - Explain what is wrong with the conversion of the figure (or what error messages you see). + - Often it can be useful to also include a figure, `TikZ` code, ... to illustrate your point. + +## How to request new features + +Please check first whether the feature hasn't been [requested](https://github.com/matlab2tikz/matlab2tikz/labels/feature%20request) before and do join the relevant topic in that case or maybe it has already been implemented in the [latest development version](https://github.com/matlab2tikz/matlab2tikz/tree/develop). + +If your feature is something new and graphical, please also have a look at the [`pgfplots`](https://www.ctan.org/pkg/pgfplots) manual to see if it supports the feature you want. +In some cases it is more constructive to request the feature in the [`pgfplots` bug tracker](https://sourceforge.net/p/pgfplots/bugs/). + +Please submit you feature request as any [bug report](https://github.com/matlab2tikz/matlab2tikz/labels/feature%20request) and make sure that you include enough details in your post, e.g.: + + - What are you trying to do? + - What should it look like or how should it work? + - Is there a relevant section in the `pgfplots` or `MATLAB` documentation? + +## Submitting pull requests (PRs) +Before you start working on a bug or new feature, you might want to check that nobody else has been assigned to the relevant issue report. +To avoid wasted hours, please just indicate your interest to tackle the issue. + +### Recommended workflow +[Our wiki](https://github.com/matlab2tikz/matlab2tikz/wiki/Recommended-git-workflow) contains more elaborate details on this process. Here is the gist: + + - It is highly recommended to start a feature branch for your work. + - Once you have finished the work, please try to run the test suite and report on the outcome in your PR (see below). + - Make sure that you file your pull request against the `develop` branch and *not* the `master` branch! + - Once you have filed your PR, the review process starts. Everybody is free to join this discussion. + - At least one other developer will review the code and signal their approval (often using a thumbs-up, :+1:) before the PR gets pulled into `develop`. + - Once you have addressed all comments, one of the developers will merge your code into the `develop` branch. + +If you still feel uncomfortable with `git`, please have a look at [this page](https://github.com/matlab2tikz/matlab2tikz/wiki/Learning-git) for a quick start. + +### Running the test suite +We know that at first the test suite can seem a bit intimidating, so we tend to be lenient during your first few PRs. However, we encourage you to run the test suite on your local computer and report on the results in your PR if any failures pop up. +To run the test suite, please consult its [README](https://github.com/matlab2tikz/matlab2tikz/blob/develop/test/README.md). + +## Becoming a member of [matlab2tikz](https://github.com/matlab2tikz) + +Once you have submitted your first pull request that is of reasonable quality, you may get invited to join the [Associate Developers](https://github.com/orgs/matlab2tikz/teams/associate-developers) group. +This group comes with *no* responsibility whatsoever and merely serves to make it easier for you to "claim" the features you want to work on. + +Once you have gained some experience (with `git`/GitHub, our codebase, ...) and have contributed your fair share of great material, you will get invited to join the [Developers](https://github.com/orgs/matlab2tikz/teams/developers) team. +This status gives you push access to our repository and hence comes with the responsibility to not abuse your push access. + +If you feel you should have gotten an invite for a team, feel free to contact one of the [owners](https://github.com/orgs/matlab2tikz/teams/owners). diff --git a/Functions/helper_functions_community/mat2tikz/LICENSE.md b/Functions/helper_functions_community/mat2tikz/LICENSE.md new file mode 100644 index 0000000..429a944 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/LICENSE.md @@ -0,0 +1,24 @@ +Copyright (c) 2008--2016 Nico Schlömer +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/Functions/helper_functions_community/mat2tikz/README.md b/Functions/helper_functions_community/mat2tikz/README.md new file mode 100644 index 0000000..9d467a0 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/README.md @@ -0,0 +1,95 @@ +**The updater in matlab2tikz 0.6.0 (and older) no longer works.** +**Please [update manually](http://www.mathworks.com/matlabcentral/fileexchange/22022-matlab2tikz-matlab2tikz?download=true) if you are not using matlab2tikz 1.0.0 or newer!** + +[![Build Status](https://travis-ci.org/matlab2tikz/matlab2tikz.svg?branch=master)](https://travis-ci.org/matlab2tikz/matlab2tikz) [![DOI](https://zenodo.org/badge/doi/10.5281/zenodo.18605.svg)](http://dx.doi.org/10.5281/zenodo.18605) +![matlab2tikz](https://raw.githubusercontent.com/wiki/matlab2tikz/matlab2tikz/matlab2tikz.png) + +`matlab2tikz` is a MATLAB(R) script to convert native MATLAB(R) figures to TikZ/Pgfplots figures that integrate seamlessly in LaTeX documents. + +To download the official releases and rate `matlab2tikz`, please visit its page on [FileExchange](http://www.mathworks.com/matlabcentral/fileexchange/22022). + +`matlab2tikz` converts most MATLAB(R) figures, including 2D and 3D plots. +For plots constructed with third-party packages, however, your mileage may vary. + +Installation +============ + +1. Extract the ZIP file (or clone the git repository) somewhere you can easily reach it. +2. Add the `src/` folder to your path in MATLAB/Octave: e.g. + - using the "Set Path" dialog in MATLAB, or + - by running the `addpath` function from your command window or `startup` script. + +Make sure that your LaTeX installation is up-to-date and includes: + +* [TikZ/PGF](http://www.ctan.org/pkg/pgf) version 3.0 or higher +* [Pgfplots](http://www.ctan.org/pkg/pgfplots) version 1.13 or higher +* [Amsmath](https://www.ctan.org/pkg/amsmath) version 2.14 or higher +* [Standalone](http://www.ctan.org/pkg/standalone) (optional) + +It is recommended to use the latest stable version of these packages. +Older versions may work depending on the actual MATLAB(R) figure you are converting. + +Usage +===== + +Typical usage of `matlab2tikz` consists of converting your MATLAB plot to a TikZ/LaTeX file and then running a LaTeX compiler to produce your document. + +MATLAB +------ + 1. Generate your plot in MATLAB(R). + + 2. Run `matlab2tikz`, e.g. using + +```matlab +matlab2tikz('myfile.tex'); +``` + +LaTeX +----- +Add the contents of `myfile.tex` into your LaTeX source code, for example using `\input{myfile.tex}`. +Make sure that the required packages (such as `pgfplots`) are loaded in the preamble of your document as in the example: + +```latex +\documentclass{article} + + \usepackage{pgfplots} + \pgfplotsset{compat=newest} + %% the following commands are needed for some matlab2tikz features + \usetikzlibrary{plotmarks} + \usetikzlibrary{arrows.meta} + \usepgfplotslibrary{patchplots} + \usepackage{grffile} + \usepackage{amsmath} + + %% you may also want the following commands + %\pgfplotsset{plot coordinates/math parser=false} + %\newlength\figureheight + %\newlength\figurewidth + +\begin{document} + \input{myfile.tex} +\end{document} +``` + +Remarks +------- +Most functions accept numerous options; you can check them out by inspecting their help: + +```matlab +help matlab2tikz +``` + +Sometimes, MATLAB(R) plots contain some features that impede conversion to LaTeX; e.g. points that are far outside of the actual bounding box. +You can invoke the `cleanfigure` function to remove such unwanted entities before calling `matlab2tikz`: + +```matlab +cleanfigure; +matlab2tikz('myfile.tex'); +``` + +More information +================ + +* For more information about `matlab2tikz`, have a look at our [GitHub repository](https://github.com/matlab2tikz/matlab2tikz). If you are a good MATLAB(R) programmer or LaTeX writer, you are always welcome to help improving `matlab2tikz`! +* Some common problems and pit-falls are documented in our [wiki](https://github.com/matlab2tikz/matlab2tikz/wiki/Common-problems). +* If you experience (other) bugs or would like to request a feature, please visit our [issue tracker](https://github.com/matlab2tikz/matlab2tikz/issues). diff --git a/Functions/helper_functions_community/mat2tikz/logos/matlab2tikz.svg b/Functions/helper_functions_community/mat2tikz/logos/matlab2tikz.svg new file mode 100644 index 0000000..42a4af9 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/logos/matlab2tikz.svg @@ -0,0 +1,102 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + MATLAB2TikZ + + diff --git a/Functions/helper_functions_community/mat2tikz/matlab2tikz.sublime-project b/Functions/helper_functions_community/mat2tikz/matlab2tikz.sublime-project new file mode 100644 index 0000000..e773d0c --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/matlab2tikz.sublime-project @@ -0,0 +1,49 @@ +{ + "folders": + [ + { + "path": "." + } + ], + "build_systems": + [ + { + "name": "CI tests (Octave)", + "selector": "source.matlab", + "working_dir": "${project_path}", + "cmd": ["./runtests.sh", "octave"] + }, + { + "name": "CI tests (MATLAB)", + "selector": "source.matlab", + "working_dir": "${project_path}", + "cmd": ["./runtests.sh", "matlab"] + }, + { + "name": "CI tests (MATLAB HG1)", + "selector": "source.matlab", + "working_dir": "${project_path}", + "cmd": ["./runtests.sh", "matlab-hg1"] + }, + { + "name": "CI tests (MATLAB HG2)", + "selector": "source.matlab", + "working_dir": "${project_path}", + "cmd": ["./runtests.sh", "matlab-hg2"] + }, + { + "name": "ACID: make", + "working_dir": "${project_path}/test/tex", + "cmd": ["make","-j8"] + }, + { + "name": "ACID: distclean", + "working_dir": "${project_path}/test/tex", + "cmd": ["make","distclean"] + } + ], + "settings": + { + "FuzzyFilePath":{} + } +} diff --git a/Functions/helper_functions_community/mat2tikz/runtests.sh b/Functions/helper_functions_community/mat2tikz/runtests.sh new file mode 100644 index 0000000..764e3d8 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/runtests.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash +# +# Test script runner for MATLAB2TIKZ continuous integration +# +# You can influence the execution by passing one or two parameters +# to this function, as +# +# ./runtests.sh RUNNER SWITCHES +# +# Arguments: +# - RUNNER: (path of) the binary you want to use to execute the tests +# default value: "octave" +# - SWITCHES: switches you want to pass to the executable +# default value: * "-nodesktop -r" if runner contains "matlab" +# * "--no-gui --eval" if runner contains "octave" and otherwise +# + +# Used resources: +# - http://askubuntu.com/questions/299710/how-to-determine-if-a-string-is-a-substring-of-another-in-bash +# - http://www.thegeekstuff.com/2010/07/bash-case-statement/ +# - http://stackoverflow.com/questions/229551/string-contains-in-bash +# - http://stackoverflow.com/questions/2870992/automatic-exit-from-bash-shell-script-on-error +# - http://www.davidpashley.com/articles/writing-robust-shell-scripts/ +# - http://stackoverflow.com/questions/13998941/how-can-i-propagate-an-exit-status-from-expect-to-its-parent-bash-script +# - http://tldp.org/HOWTO/Bash-Prog-Intro-HOWTO-8.html + +## Make sure some failures are detected by the CI runners +function exitIfError { + # pass "$?" as argument: i.e. the exit status of the last call + if [ "$1" -ne 0 ]; then + exit $1; + fi +} + +## Handle Runner and Switches variables +Runner=$1 +Switches=$2 +if [ -z "$Runner" ] ; then + Runner="octave" +fi +if [ -z "$Switches" ] ; then + case "$Runner" in + *matlab* ) + Switches="-nodesktop -r" + ;; + + *octave* ) + Switches="--no-gui --eval" + ;; + + * ) + # Fall back to Octave switches + Switches="--no-gui --eval" + ;; + esac +fi + +## Make sure MATLAB/Octave know the intent +# note: the export is required +export CONTINUOUS_INTEGRATION=true +export CI=true + +## Actually run the test suite +cd test +TESTDIR=`pwd` +# also CD in MATLAB/Octave to make sure that startup files +# cannot play any role in setting the path +${Runner} ${Switches} "cd('${TESTDIR}'); runMatlab2TikzTests" +exitIfError $? +cd .. + +## Post-processing + +# convert MD report into HTML using pandoc if available +MDFILE="test/results.test.md" +if [ ! -z `which pandoc` ]; then + if [ -f $MDFILE ]; then + HTMLFILE=${MDFILE/md/html} + # replace the emoji while we're at it + pandoc -f markdown -t html $MDFILE -o $HTMLFILE + sed -i -- 's/:heavy_exclamation_mark:/â—ï¸/g' $HTMLFILE + sed -i -- 's/:white_check_mark:/✅/g' $HTMLFILE + sed -i -- 's/:grey_question:/â”/g' $HTMLFILE + fi +fi + diff --git a/Functions/helper_functions_community/mat2tikz/src/cleanfigure.m b/Functions/helper_functions_community/mat2tikz/src/cleanfigure.m new file mode 100644 index 0000000..e4c3fbf --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/src/cleanfigure.m @@ -0,0 +1,1294 @@ +function cleanfigure(varargin) +% CLEANFIGURE() removes the unnecessary objects from your MATLAB plot +% to give you a better experience with matlab2tikz. +% CLEANFIGURE comes with several options that can be combined at will. +% +% CLEANFIGURE('handle',HANDLE,...) explicitly specifies the +% handle of the figure that is to be stored. (default: gcf) +% +% CLEANFIGURE('pruneText',BOOL,...) explicitly specifies whether text +% should be pruned. (default: true) +% +% CLEANFIGURE('targetResolution',PPI,...) +% CLEANFIGURE('targetResolution',[W,H],...) +% Reduce the number of data points in line objects by applying +% unperceivable changes at the target resolution. +% The target resolution can be specificed as the number of Pixels Per +% Inch (PPI), e.g. 300, or as the Width and Heigth of the figure in +% pixels, e.g. [9000, 5400]. +% Use targetResolution = Inf or 0 to disable line simplification. +% (default: 600) +% +% CLEANFIGURE('scalePrecision',alpha,...) +% Scale the precision the data is represented with. Setting it to 0 +% or negative values disable this feature. +% (default: 1) +% +% CLEANFIGURE('normalizeAxis','xyz',...) +% EXPERIMENTAL: Normalize the data of the dimensions specified by +% 'normalizeAxis' to the interval [0, 1]. This might have side effects +% with hgtransform and friends. One can directly pass the axis handle to +% cleanfigure to ensure that only one axis gets normalized. +% Usage: Input 'xz' normalizes only x- and zData but not yData +% (default: '') +% +% Example +% x = -pi:pi/1000:pi; +% y = tan(sin(x)) - sin(tan(x)); +% plot(x,y,'--rs'); +% cleanfigure(); +% +% See also: matlab2tikz + + % Treat hidden handles, too. + shh = get(0, 'ShowHiddenHandles'); + set(0, 'ShowHiddenHandles', 'on'); + + % Keep track of the current axes. + meta.gca = []; + + % Set up command line options. + m2t.cmdOpts = m2tInputParser; + m2t.cmdOpts = m2t.cmdOpts.addParamValue(m2t.cmdOpts, 'handle', gcf, @ishandle); + m2t.cmdOpts = m2t.cmdOpts.addParamValue(m2t.cmdOpts, 'targetResolution', 600, @isValidTargetResolution); + m2t.cmdOpts = m2t.cmdOpts.addParamValue(m2t.cmdOpts, 'pruneText', true, @islogical); + + m2t.cmdOpts = m2t.cmdOpts.addParamValue(m2t.cmdOpts, 'minimumPointsDistance', 1.0e-10, @isnumeric); + m2t.cmdOpts = m2t.cmdOpts.addParamValue(m2t.cmdOpts, 'scalePrecision', 1, @isnumeric); + m2t.cmdOpts = m2t.cmdOpts.addParamValue(m2t.cmdOpts, 'normalizeAxis', '', @isValidAxis); + + % Deprecated parameters + m2t.cmdOpts = m2t.cmdOpts.deprecateParam(m2t.cmdOpts, 'minimumPointsDistance', 'targetResolution'); + + % Finally parse all the elements. + m2t.cmdOpts = m2t.cmdOpts.parse(m2t.cmdOpts, varargin{:}); + + % Recurse down the tree of plot objects and clean up the leaves. + for h = m2t.cmdOpts.Results.handle(:)' + recursiveCleanup(meta, h, m2t.cmdOpts.Results); + end + + % Reset to initial state. + set(0, 'ShowHiddenHandles', shh); + + return; +end +% ========================================================================= +function recursiveCleanup(meta, h, cmdOpts) + % Recursive function, that cleans up the individual childs of a figure + + % Get the type of the current figure handle + type = get(h, 'Type'); + + %display(sprintf([repmat(' ',1,indent), type, '->'])) + + % Don't try to be smart about quiver groups. + % NOTE: + % A better way to write `strcmp(get(h,...))` would be to use + % isa(handle(h), 'specgraph.quivergroup'). + % The handle() function isn't supported by Octave, though, so let's stick + % with strcmp(). + if strcmp(type, 'specgraph.quivergroup') + %if strcmp(class(handle(h)), 'specgraph.quivergroup') + return; + end + + % Update the current axes. + if strcmp(type, 'axes') + meta.gca = h; + + if ~isempty(cmdOpts.normalizeAxis) + % If chosen transform the date axis + normalizeAxis(h, cmdOpts); + end + end + + children = get(h, 'Children'); + if ~isempty(children) + for child = children(:)' + recursiveCleanup(meta, child, cmdOpts); + end + else + if strcmp(type, 'line') + % Remove data points outside of the axes + % NOTE: Always remove invisible points before simplifying the + % line. Otherwise it will generate additional line segments + pruneOutsideBox(meta, h); + % Move some points closer to the box to avoid TeX:DimensionTooLarge + % errors. This may involve inserting extra points. + movePointsCloser(meta, h); + % Simplify the lines by removing superflous points + simplifyLine(meta, h, cmdOpts.targetResolution); + % Limit the precision of the output + limitPrecision(meta, h, cmdOpts.scalePrecision); + elseif strcmpi(type, 'stair') + % Remove data points outside of the visible axes + pruneOutsideBox(meta, h); + % Remove superfluous data points + simplifyStairs(meta, h); + % Limit the precision of the output + limitPrecision(meta, h, cmdOpts.scalePrecision); + elseif strcmp(type, 'text') && cmdOpts.pruneText + % Prune text that is outside of the axes + pruneOutsideText(meta, h); + end + end + + return; +end +% ========================================================================= +function pruneOutsideBox(meta, handle) + % Some sections of the line may sit outside of the visible box. + % Cut those off. + + % Extract the visual data from the current line handle. + [xData, yData] = getVisualData(meta, handle); + + % Merge the data into one matrix + data = [xData, yData]; + + % Dont do anything if the data is empty + if isempty(data) + return; + end + + % Check if the plot has lines + hasLines = ~strcmp(get(handle, 'LineStyle'),'none')... + && get(handle, 'LineWidth') > 0.0; + + % Extract the visual limits from the current line handle. + [xLim, yLim]= getVisualLimits(meta); + + tol = 1.0e-10; + relaxedXLim = xLim + [-tol, tol]; + relaxedYLim = yLim + [-tol, tol]; + + % Get which points are inside a (slightly larger) box. + dataIsInBox = isInBox(data, relaxedXLim, relaxedYLim); + + % Plot all the points inside the box + shouldPlot = dataIsInBox; + + if hasLines + % Check if the connecting line between two data points is visible. + segvis = segmentVisible(data, dataIsInBox, xLim, yLim); + + % Plot points which part of an visible line segment. + shouldPlot = shouldPlot | [false; segvis] | [segvis; false]; + end + + % Remove or replace points outside the box + id_replace = []; + id_remove = []; + if ~all(shouldPlot) + % For line plots, simply removing the data has the disadvantage + % that the line between two 'loose' ends may now appear in the figure. + % To avoid this, add a row of NaNs wherever a block of actual data is + % removed. + + % Get the indices of points that should be removed + id_remove = find(~shouldPlot); + + % If there are consecutive data points to be removed, only replace + % the first one by a NaN. Consecutive data points have + % diff(id_remove)==1, so replace diff(id_remove)>1 by NaN and remove + % the rest + idx = [true; diff(id_remove) >1]; + id_replace = id_remove(idx); + id_remove = id_remove(~idx); + end + % Replace the data points + replaceDataWithNaN(meta, handle, id_replace); + + % Remove the data outside the box + removeData(meta, handle, id_remove); + + % Remove possible NaN duplications + removeNaNs(meta, handle); + return; +end +% ========================================================================= +function movePointsCloser(meta, handle) + % Move all points outside a box much larger than the visible one + % to the boundary of that box and make sure that lines in the visible + % box are preserved. This typically involves replacing one point by + % two new ones and a NaN. + + % TODO: 3D simplification of frontal 2D projection. This requires the + % full transformation rather than the projection, as we have to calculate + % the inverse transformation to project back into 3D + if isAxis3D(meta.gca) + return; + end + + % Extract the visual data from the current line handle. + [xData, yData] = getVisualData(meta, handle); + + % Extract the visual limits from the current line handle. + [xLim, yLim] = getVisualLimits(meta); + + % Calculate the extension of the extended box + xWidth = xLim(2) - xLim(1); + yWidth = yLim(2) - yLim(1); + + % Don't choose the larger box too large to make sure that the values inside + % it can still be treated by TeX. + extendFactor = 0.1; + largeXLim = xLim + extendFactor * [-xWidth, xWidth]; + largeYLim = yLim + extendFactor * [-yWidth, yWidth]; + + % Put the data into one matrix + data = [xData, yData]; + + % Get which points are in an extended box (the limits of which + % don't exceed TeX's memory). + dataIsInLargeBox = isInBox(data, largeXLim, largeYLim); + + % Count the NaNs as being inside the box. + dataIsInLargeBox = dataIsInLargeBox | any(isnan(data), 2); + + % Find all points which are to be included in the plot yet do not fit + % into the extended box + id_replace = find(~dataIsInLargeBox); + + % Only try to replace points if there are some to replace + dataInsert = {}; + if ~isempty(id_replace) + % Get the indices of those points, that are the first point in a + % segment. The last data point at size(data, 1) cannot be the first + % point in a segment. + id_first = id_replace(id_replace < size(data, 1)); + + % Get the indices of those points, that are the second point in a + % segment. Similarly the first data point cannot be the second data + % point in a segment. + id_second = id_replace(id_replace > 1); + + % Define the vectors of data points for the segments X1--X2 + X1_first = data(id_first, :); + X2_first = data(id_first+1, :); + X1_second = data(id_second, :); + X2_second = data(id_second-1, :); + + % Move the points closer to the large box along the segment + newData_first = moveToBox(X1_first, X2_first, largeXLim, largeYLim); + newData_second= moveToBox(X1_second, X2_second, largeXLim, largeYLim); + + % Respect logarithmic scaling for the new points + isXlog = strcmp(get(meta.gca, 'XScale'), 'log'); + if isXlog + newData_first (:, 1) = 10.^newData_first (:, 1); + newData_second(:, 1) = 10.^newData_second(:, 1); + end + isYlog = strcmp(get(meta.gca, 'YScale'), 'log'); + if isYlog + newData_first (:, 2) = 10.^newData_first (:, 2); + newData_second(:, 2) = 10.^newData_second(:, 2); + end + + % If newData_* is infinite, the segment was not visible. However, as we + % move the point closer, it would become visible. So insert a NaN. + isInfinite_first = any(~isfinite(newData_first), 2); + isInfinite_second = any(~isfinite(newData_second), 2); + + newData_first (isInfinite_first, :) = NaN(sum(isInfinite_first), 2); + newData_second(isInfinite_second, :) = NaN(sum(isInfinite_second), 2); + + % If a point is part of two segments, that cross the border, we need to + % insert a NaN to prevent an additional line segment + [trash, trash, id_conflict] = intersect(id_first (~isInfinite_first), ... + id_second(~isInfinite_second)); + + % Cut the data into length(id_replace)+1 segments. + % Calculate the length of the segments + length_segments = [id_replace(1); + diff(id_replace); + size(data, 1)-id_replace(end)]; + + % Create an empty cell array for inserting NaNs and fill it at the + % conflict sites + dataInsert_NaN = cell(length(length_segments),1); + dataInsert_NaN(id_conflict) = mat2cell(NaN(length(id_conflict), 2),... + ones(size(id_conflict)), 2); + + % Create a cell array for the moved points + dataInsert_first = mat2cell(newData_first, ones(size(id_first)), 2); + dataInsert_second = mat2cell(newData_second, ones(size(id_second)), 2); + + % Add an empty cell at the end of the last segment, as we do not + % insert something *after* the data + dataInsert_first = [dataInsert_first; cell(1)]; + dataInsert_second = [dataInsert_second; cell(1)]; + + % If the first or the last point would have been replaced add an empty + % cell at the beginning/end. This is because the last data point + % cannot be the first data point of a line segment and vice versa. + if(id_replace(end) == size(data, 1)) + dataInsert_first = [dataInsert_first; cell(1)]; + end + if(id_replace(1) == 1) + dataInsert_second = [cell(1); dataInsert_second]; + end + + % Put the cells together, right points first, then the possible NaN + % and then the left points + dataInsert = cellfun(@(a,b,c) [a; b; c],... + dataInsert_second,... + dataInsert_NaN,... + dataInsert_first,... + 'UniformOutput',false); + end + + % Insert the data + insertData(meta, handle, id_replace, dataInsert); + + % Get the indices of the to be removed points accounting for the now inserted + % data points + numPointsInserted = cellfun(@(x) size(x,1), [cell(1);dataInsert(1:end-2)]); + id_remove = id_replace + cumsum(numPointsInserted); + + % Remove the data point that should be replaced. + removeData(meta, handle, id_remove); + + % Remove possible NaN duplications + removeNaNs(meta, handle); +end +% ========================================================================= +function simplifyLine(meta, handle, targetResolution) + % Reduce the number of data points in the line 'handle'. + % + % Aplies a path-simplification algorithm if there are no markers or + % pixelization otherwise. Changes are visually negligible at the target + % resolution. + % + % The target resolution is either specificed as the number of PPI or as + % the [Width, Heigth] of the figure in pixels. + % A scalar value of INF or 0 disables path simplification. + % (default = 600) + + % Do not simpify + if any(isinf(targetResolution) | targetResolution == 0) + return + end + + % Retrieve target figure size in pixels + [W, H] = getWidthHeightInPixels(targetResolution); + + % Extract the visual data from the current line handle. + [xData, yData] = getVisualData(meta, handle); + + % Only simplify if there are more than 2 points + if numel(xData) <= 2 || numel(yData) <= 2 + return; + end + + % Extract the visual limits from the current line handle. + [xLim, yLim] = getVisualLimits(meta); + + % Automatically guess a tol based on the area of the figure and + % the area and resolution of the output + xRange = xLim(2)-xLim(1); + yRange = yLim(2)-yLim(1); + + % Conversion factors of data units into pixels + xToPix = W/xRange; + yToPix = H/yRange; + + % Mask for removing data points + id_remove = []; + + % If the path has markers, perform pixelation instead of simplification + hasMarkers = ~strcmpi(get(handle,'Marker'),'none'); + hasLines = ~strcmpi(get(handle,'LineStyle'),'none'); + if hasMarkers && ~hasLines + % Pixelate data at the zoom multiplier + mask = pixelate(xData, yData, xToPix, yToPix); + id_remove = find(mask==0); + elseif hasLines && ~hasMarkers + % Get the width of a pixel + xPixelWidth = 1/xToPix; + yPixelWidth = 1/yToPix; + tol = min(xPixelWidth,yPixelWidth); + + % Split up lines which are seperated by NaNs + id_nan = isnan(xData) | isnan(yData); + + % If lines were separated by a NaN, diff(~id_nan) would give 1 for + % the start of a line and -1 for the index after the end of + % a line. + id_diff = diff([false; ~id_nan; false]); + lineStart = find(id_diff == 1); + lineEnd = find(id_diff == -1)-1; + numLines = numel(lineStart); + id_remove = cell(numLines, 1); + + % Simplify the line segments + for ii = 1:numLines + % Actual data that inherits the simplifications + x = xData(lineStart(ii):lineEnd(ii)); + y = yData(lineStart(ii):lineEnd(ii)); + + % Line simplification + if numel(x) > 2 + mask = opheimSimplify(x, y, tol); + % Remove all those with mask==0 respecting the number of + % data points in the previous segments + id_remove{ii} = find(mask==0) + lineStart(ii) - 1; + end + end + + % Merge the indices of the line segments + id_remove = cat(1, id_remove{:}); + end + + % Remove the data points + removeData(meta, handle, id_remove); +end +% ========================================================================= +function simplifyStairs(meta, handle) + % This function simplifies stair plots by removeing superflous data + % points + + % Some data might not lead to a new step in the stair. This is the case + % if the difference in one dimension is zero, e.g + % [(x_1, y_1), (x_2, y_1), ... (x_k, y_1), (x_{k+1} y_2)]. + % However, there is one exeption. If the monotonicity of the other + % dimension changes, e.g. the sequence [0, 1], [0, -1], [0, 2]. This + % sequence cannot be simplified. Therefore, we check for monoticity too. + % As an example, we can remove the data points marked with x in the + % following stair + % o--x--o + % | | + % x o --x--o + % | + % o--x--o + % | + % o + + % Extract the data + xData = get(handle, 'XData'); + yData = get(handle, 'YData'); + + % Do not do anything if the data is empty + if isempty(xData) || isempty(yData) + return; + end + + % Check for nonchanging data points + xNoDiff = [false, (diff(xData) == 0)]; + yNoDiff = [false, (diff(yData) == 0)]; + + % Never remove the last data point + xNoDiff(end) = false; + yNoDiff(end) = false; + + % Check for monotonicity (it changes if diff(sign)~=0) + xIsMonotone = [true, diff(sign(diff(xData)))==0, true]; + yIsMonotone = [true, diff(sign(diff(yData)))==0, true]; + + % Only remove points when there is no difference in one dimension and no + % change in monotonicity in the other + xRemove = xNoDiff & yIsMonotone; + yRemove = yNoDiff & xIsMonotone; + + % Plot only points, that generate a new step + id_remove = find(xRemove | yRemove); + + % Remove the superfluous data + removeData(meta, handle, id_remove); +end +% ========================================================================= +function limitPrecision(meta, handle, alpha) + % Limit the precision of the given data + + % If alpha is 0 or negative do nothing + if alpha<=0 + return + end + + % Extract the data from the current line handle. + xData = get(handle, 'XData'); + yData = get(handle, 'YData'); + if isAxis3D(meta.gca) + zData = get(handle, 'ZData'); + end + + % Check for log scaling + isXlog = strcmp(get(meta.gca, 'XScale'), 'log'); + isYlog = strcmp(get(meta.gca, 'YScale'), 'log'); + isZlog = strcmp(get(meta.gca, 'ZScale'), 'log'); + + % Put the data into a matrix and log bits into vector + if isAxis3D(meta.gca) + data = [xData(:), yData(:), zData(:)]; + isLog = [isXlog, isYlog, isZlog]; + else + data = [xData(:), yData(:)]; + isLog = [isXlog, isYlog]; + end + + % Only do something if the data is not empty + if isempty(data) || all(isinf(data(:))) + return + end + + % Scale to visual coordinates + data(:, isLog) = log10(data(:, isLog)); + + % Get the maximal value of the data, only considering finite values + maxValue = max(abs(data(isfinite(data)))); + + % The least significant bit is proportional to the numerical precision + % of the largest number. Scale it with a user defined value alpha + leastSignificantBit = eps(maxValue) * alpha; + + % Round to precision and scale back + data = round(data / leastSignificantBit) * leastSignificantBit; + + % Scale back in case of log scaling + data(:, isLog) = 10.^data(:, isLog); + + % Set the new data. + set(handle, 'XData', data(:, 1)); + set(handle, 'YData', data(:, 2)); + if isAxis3D(meta.gca) + set(handle, 'zData', data(:, 3)); + end +end +% ========================================================================= +function pruneOutsideText(meta, handle) + % Function to prune text outside of axis handles. + + % Ensure units of type 'data' (default) and restore the setting later + units_original = get(handle, 'Units'); + set(handle, 'Units', 'data'); + + % Check if text is inside bounds by checking if the position is inside + % the x, y and z limits. This works for both 2D and 3D plots. + xLim = get(meta.gca, 'XLim'); + yLim = get(meta.gca, 'YLim'); + zLim = get(meta.gca, 'ZLim'); + axLim = [xLim; yLim; zLim]; + + pos = get(handle, 'Position'); + % If the axis is 2D, ignore the z component and consider the extend of + % the textbox + if ~isAxis3D(meta.gca) + pos(3) = 0; + + % In 2D plots the 'extent' of the textbox is available and also + % considered to keep the textbox, if it is partially inside the axis + % limits. + extent = get(handle, 'Extent'); + + % Extend the actual axis limits by the extent of the textbox so that + % the textbox is not discarded, if it overlaps the axis. + axLim(1, 1) = axLim(1, 1) - extent(3); % x-limit is extended by width + axLim(2, 1) = axLim(2, 1) - extent(4); % y-limit is extended by height + end + + % Check if the (extended) textbox is inside the axis limits + bPosInsideLim = ( pos' >= axLim(:,1) ) & ( pos' <= axLim(:,2) ); + + % Restore original units (after reading all dimensions) + set(handle, 'Units', units_original); + + % Check if it is the title + isTitle = (handle == get(meta.gca, 'title')); + + % Disable visibility if it is outside the limits and it is not + % the title + if ~all(bPosInsideLim) && ~isTitle + % Warn about to be deprecated text removal + warning('cleanfigure:textRemoval', ... + 'Text removal by cleanfigure is planned to be deprecated'); + % Artificially disable visibility. m2t will check and skip. + set(handle, 'Visible', 'off'); + end +end +% ========================================================================= +function mask = isInBox(data, xLim, yLim) + % Returns a mask that indicates, whether a data point is within the + % limits + + mask = data(:, 1) > xLim(1) & data(:, 1) < xLim(2) ... + & data(:, 2) > yLim(1) & data(:, 2) < yLim(2); +end +% ========================================================================= +function mask = segmentVisible(data, dataIsInBox, xLim, yLim) + % Given a bounding box {x,y}Lim, determine whether the line between all + % pairs of subsequent data points [data(idx,:)<-->data(idx+1,:)] is + % visible. There are two possible cases: + % 1: One of the data points is within the limits + % 2: The line segments between the datapoints crosses the bounding box + n = size(data, 1); + mask = false(n-1, 1); + + % Only check if there is more than 1 point + if n>1 + % Define the vectors of data points for the segments X1--X2 + idx= 1:n-1; + X1 = data(idx, :); + X2 = data(idx+1, :); + + % One of the neighbors is inside the box and the other is finite + thisVisible = (dataIsInBox(idx) & all(isfinite(X2), 2)); + nextVisible = (dataIsInBox(idx+1) & all(isfinite(X1), 2)); + + % Get the corner coordinates + [bottomLeft, topLeft, bottomRight, topRight] = corners2D(xLim, yLim); + + % Check if data points intersect with the borders of the plot + left = segmentsIntersect(X1, X2, bottomLeft , topLeft); + right = segmentsIntersect(X1, X2, bottomRight, topRight); + bottom = segmentsIntersect(X1, X2, bottomLeft , bottomRight); + top = segmentsIntersect(X1, X2, topLeft , topRight); + + % Check the result + mask = thisVisible | nextVisible | left | right | top | bottom; + end +end +% ========================================================================= +function mask = segmentsIntersect(X1, X2, X3, X4) + % Checks whether the segments X1--X2 and X3--X4 intersect. + lambda = crossLines(X1, X2, X3, X4); + + % Check whether lambda is in bound + mask = 0.0 < lambda(:, 1) & lambda(:, 1) < 1.0 &... + 0.0 < lambda(:, 2) & lambda(:, 2) < 1.0; +end +% ========================================================================= +function mask = pixelate(x, y, xToPix, yToPix) + % Rough reduction of data points at a multiple of the target resolution + + % The resolution is lost only beyond the multiplier magnification + mult = 2; + + % Convert data to pixel units and magnify + dataPixel = round([x * xToPix * mult, ... + y * yToPix * mult]); + + % Sort the pixels + [dataPixelSorted, id_orig] = sortrows(dataPixel); + + % Find the duplicate pixels + mask_sorted = [true; diff(dataPixelSorted(:,1))~=0 | ... + diff(dataPixelSorted(:,2))~=0]; + + % Unwind the sorting + mask = false(size(x)); + mask(id_orig) = mask_sorted; + + % Set the first, last, as well as unique pixels to true + mask(1) = true; + mask(end) = true; + + % Set NaNs to true + inan = isnan(x) | isnan(y); + mask(inan) = true; +end +% ========================================================================= +function mask = opheimSimplify(x,y,tol) + % Opheim path simplification algorithm + % + % Given a path of vertices V and a tolerance TOL, the algorithm: + % 1. selects the first vertex as the KEY; + % 2. finds the first vertex farther than TOL from the KEY and links + % the two vertices with a LINE; + % 3. finds the last vertex from KEY which stays within TOL from the + % LINE and sets it to be the LAST vertex. Removes all points in + % between the KEY and the LAST vertex; + % 4. sets the KEY to the LAST vertex and restarts from step 2. + % + % The Opheim algorithm can produce unexpected results if the path + % returns back on itself while remaining within TOL from the LINE. + % This behaviour can be seen in the following example: + % + % x = [1,2,2,2,3]; + % y = [1,1,2,1,1]; + % tol < 1 + % + % The algorithm undesirably removes the second last point. See + % https://github.com/matlab2tikz/matlab2tikz/pull/585#issuecomment-89397577 + % for additional details. + % + % To rectify this issues, step 3 is modified to find the LAST vertex as + % follows: + % 3*. finds the last vertex from KEY which stays within TOL from the + % LINE, or the vertex that connected to its previous point forms + % a segment which spans an angle with LINE larger than 90 + % degrees. + + mask = false(size(x)); + mask(1) = true; + mask(end) = true; + + N = numel(x); + i = 1; + while i <= N-2 + % Find first vertex farther than TOL from the KEY + j = i+1; + v = [x(j)-x(i); y(j)-y(i)]; + while j < N && norm(v) <= tol + j = j+1; + v = [x(j)-x(i); y(j)-y(i)]; + end + v = v/norm(v); + + % Unit normal to the line between point i and point j + normal = [v(2);-v(1)]; + + % Find the last point which stays within TOL from the line + % connecting i to j, or the last point within a direction change + % of pi/2. + % Starts from the j+1 points, since all previous points are within + % TOL by construction. + while j < N + % Calculate the perpendicular distance from the i->j line + v1 = [x(j+1)-x(i); y(j+1)-y(i)]; + d = abs(normal.'*v1); + if d > tol + break + end + + % Calculate the angle between the line from the i->j and the + % line from j -> j+1. If + v2 = [x(j+1)-x(j); y(j+1)-y(j)]; + anglecosine = v.'*v2; + if anglecosine <= 0; + break + end + j = j + 1; + end + i = j; + mask(i) = true; + end +end +% ========================================================================= +function lambda = crossLines(X1, X2, X3, X4) + % Checks whether the segments X1--X2 and X3--X4 intersect. + % See https://en.wikipedia.org/wiki/Line-line_intersection for reference. + % Given four points X_k=(x_k,y_k), k\in{1,2,3,4}, and the two lines + % defined by those, + % + % L1(lambda) = X1 + lambda (X2 - X1) + % L2(lambda) = X3 + lambda (X4 - X3) + % + % returns the lambda for which they intersect (and Inf if they are parallel). + % Technically, one needs to solve the 2x2 equation system + % + % x1 + lambda1 (x2-x1) = x3 + lambda2 (x4-x3) + % y1 + lambda1 (y2-y1) = y3 + lambda2 (y4-y3) + % + % for lambda1 and lambda2. + + % Now X1 is a vector of all data points X1 and X2 is a vector of all + % consecutive data points X2 + % n is the number of segments (not points in the plot!) + n = size(X2, 1); + lambda = zeros(n, 2); + + % Calculate the determinant of A = [X2-X1, -(X4-X3)]; + % detA = -(X2(1)-X1(1))*(X4(2)-X3(2)) + (X2(2)-X1(2))*(X4(1)-X3(1)) + % NOTE: Vectorized this is equivalent to the matrix multiplication + % [nx2] * [2x2] * [2x1] = [nx1] + detA = -(X2(:, 1)-X1(:, 1)) .* (X4(2)-X3(2)) + (X2(:, 2)-X1(:, 2)) .* (X4(1)-X3(1)); + + % Get the indices for nonzero elements + id_detA = detA~=0; + + if any(id_detA) + % rhs = X3(:) - X1(:) + % NOTE: Originaly this was a [2x1] vector. However as we vectorize the + % calculation it is beneficial to treat it as an [nx2] matrix rather than a [2xn] + rhs = bsxfun(@minus, X3, X1); + + % Calculate the inverse of A and lambda + % invA=[-(X4(2)-X3(2)), X4(1)-X3(1);... + % -(X2(2)-X1(2)), X2(1)-X1(1)] / detA + % lambda = invA * rhs + + % Rotational matrix with sign flip. It transforms a given vector [a,b] by + % Rotate * [a,b] = [-b,a] as required for calculation of invA + Rotate = [0, -1; 1, 0]; + + + % Rather than calculating invA first and then multiply with rhs to obtain + % lambda, directly calculate the respective terms + % The upper half of the 2x2 matrix is always the same and is given by: + % [-(X4(2)-X3(2)), X4(1)-X3(1)] / detA * rhs + % This is a matrix multiplication of the form [1x2] * [2x1] = [1x1] + % As we have transposed rhs we can write this as: + % rhs * Rotate * (X4-X3) => [nx2] * [2x2] * [2x1] = [nx1] + lambda(id_detA, 1) = (rhs(id_detA, :) * Rotate * (X4-X3)')./detA(id_detA); + + % The lower half is dependent on (X2-X1) which is a matrix of size [nx2] + % [-(X2(2)-X1(2)), X2(1)-X1(1)] / detA * rhs + % As both (X2-X1) and rhs are matrices of size [nx2] there is no simple + % matrix multiplication leading to a [nx1] vector. Therefore, use the + % elementwise multiplication and sum over it + % sum( [nx2] * [2x2] .* [nx2], 2) = sum([nx2],2) = [nx1] + lambda(id_detA, 2) = sum(-(X2(id_detA, :)-X1(id_detA, :)) * Rotate .* rhs(id_detA, :), 2)./detA(id_detA); + end +end +% ========================================================================= +function minAlpha = updateAlpha(X1, X2, X3, X4, minAlpha) + % Checks whether the segments X1--X2 and X3--X4 intersect. + lambda = crossLines(X1, X2, X3, X4); + + % Check if lambda is in bounds and lambda1 large enough + id_Alpha = 0.0 < lambda(:,2) & lambda(:,2) < 1.0 ... + & abs(minAlpha) > abs(lambda(:,1)); + + % Update alpha when applicable + minAlpha(id_Alpha) = lambda(id_Alpha,1); +end +% ========================================================================= +function xNew = moveToBox(x, xRef, xLim, yLim) + % Takes a box defined by xlim, ylim, a vector of points x and a vector of + % reference points xRef. + % Returns the vector of points xNew that sits on the line segment between + % x and xRef *and* on the box. If several such points exist, take the + % closest one to x. + n = size(x, 1); + + % Find out with which border the line x---xRef intersects, and determine + % the smallest parameter alpha such that x + alpha*(xRef-x) + % sits on the boundary. Otherwise set Alpha to inf. + minAlpha = inf(n, 1); + + % Get the corner points + [bottomLeft, topLeft, bottomRight, topRight] = corners2D(xLim, yLim); + + % left boundary: + minAlpha = updateAlpha(x, xRef, bottomLeft, topLeft, minAlpha); + + % bottom boundary: + minAlpha = updateAlpha(x, xRef, bottomLeft, bottomRight, minAlpha); + + % right boundary: + minAlpha = updateAlpha(x, xRef, bottomRight, topRight, minAlpha); + + % top boundary: + minAlpha = updateAlpha(x, xRef, topLeft, topRight, minAlpha); + + % Create the new point + xNew = x + bsxfun(@times ,minAlpha, (xRef-x)); +end +% ========================================================================= +function [xData, yData] = getVisualData(meta, handle) + % Returns the visual representation of the data (Respecting possible + % log_scaling and projection into the image plane) + + % Check whether this is a 3D plot + is3D = isAxis3D(meta.gca); + + % Extract the data from the current line handle. + xData = get(handle, 'XData'); + yData = get(handle, 'YData'); + if is3D + zData = get(handle, 'ZData'); + end + + % Get info about log scaling + isXlog = strcmp(get(meta.gca, 'XScale'), 'log'); + if isXlog + xData = log10(xData); + end + isYlog = strcmp(get(meta.gca, 'YScale'), 'log'); + if isYlog + yData = log10(yData); + end + isZlog = strcmp(get(meta.gca, 'ZScale'), 'log'); + if isZlog + zData = log10(zData); + end + + % In case of 3D plots, project the data into the image plane. + if is3D + % Get the projection matrix + P = getProjectionMatrix(meta); + + % Put the data into one matrix accounting for the canonical 4th + % dimension + data = [xData(:), yData(:), zData(:), ones(size(xData(:)))]; + + % Project the data into the image plane + dataProjected = P * data'; + + % Only consider the x and y coordinates and scale them correctly + xData = dataProjected(1, :) ./ dataProjected(4, :); + yData = dataProjected(2, :) ./ dataProjected(4, :); + end + + % Turn the data into a row vector + xData = xData(:); + yData = yData(:); +end +% ========================================================================= +function [xLim, yLim] = getVisualLimits(meta) + % Returns the visual representation of the axis limits (Respecting + % possible log_scaling and projection into the image plane) + + % Check whether this is a 3D plot + is3D = isAxis3D(meta.gca); + + % Get the axis limits + xLim = get(meta.gca, 'XLim'); + yLim = get(meta.gca, 'YLim'); + zLim = get(meta.gca, 'ZLim'); + + % Check for logarithmic scales + isXlog = strcmp(get(meta.gca, 'XScale'), 'log'); + if isXlog + xLim = log10(xLim); + end + isYlog = strcmp(get(meta.gca, 'YScale'), 'log'); + if isYlog + yLim = log10(yLim); + end + isZlog = strcmp(get(meta.gca, 'ZScale'), 'log'); + if isZlog + zLim = log10(zLim); + end + + % In case of 3D plots, project the limits into the image plane. Depending + % on the angles, any of the 8 corners of the 3D cube mit be relevant so + % check for all + if is3D + % Get the projection matrix + P = getProjectionMatrix(meta); + + % Get the coordinates of the 8 corners + corners = corners3D(xLim, yLim, zLim); + + % Add the canonical 4th dimension + corners = [corners, ones(8,1)]; + + % Project the corner points to 2D coordinates + cornersProjected = P * corners'; + + % Pick the x and y values of the projected corners and scale them + % correctly + xCorners = cornersProjected(1, :) ./ cornersProjected(4, :); + yCorners = cornersProjected(2, :) ./ cornersProjected(4, :); + + % Get the maximal and minimal values of the x and y coordinates as + % limits + xLim = [min(xCorners), max(xCorners)]; + yLim = [min(yCorners), max(yCorners)]; + end +end +% ========================================================================= +function replaceDataWithNaN(meta, handle, id_replace) + % Replaces data at id_replace with NaNs + + % Only do something if id_replace is not empty + if isempty(id_replace) + return + end + + % Check whether this is a 3D plot + is3D = isAxis3D(meta.gca); + + % Extract the data from the current line handle. + xData = get(handle, 'XData'); + yData = get(handle, 'YData'); + if is3D + zData = get(handle, 'ZData'); + end + + % Update the data indicated by id_update + xData(id_replace) = NaN(size(id_replace)); + yData(id_replace) = NaN(size(id_replace)); + if is3D + zData(id_replace) = NaN(size(id_replace)); + end + + % Set the new (masked) data. + set(handle, 'XData', xData); + set(handle, 'YData', yData); + if is3D + set(handle, 'ZData', zData); + end +end +% ========================================================================= +function insertData(meta, handle, id_insert, dataInsert) + % Inserts the elements of the cell array dataInsert at position id_insert + + % Only do something if id_insert is not empty + if isempty(id_insert) + return + end + + % Check whether this is a 3D plot + is3D = isAxis3D(meta.gca); + + % Extract the data from the current line handle. + xData = get(handle, 'XData'); + yData = get(handle, 'YData'); + if is3D + zData = get(handle, 'ZData'); + end + + length_segments = [id_insert(1); + diff(id_insert); + length(xData)-id_insert(end)]; + + % Put the data into one matrix + if is3D + data = [xData(:), yData(:), zData(:)]; + else + data = [xData(:), yData(:)]; + end + + % Cut the data into segments + dataCell = mat2cell(data, length_segments, size(data, 2)); + + % Merge the cell arrays + dataCell = [dataCell'; + dataInsert']; + + % Merge the cells back together + data = cat(1, dataCell{:}); + + % Set the new (masked) data. + set(handle, 'XData', data(:, 1)); + set(handle, 'YData', data(:, 2)); + if is3D + set(handle, 'ZData', data(:, 3)); + end +end +% ========================================================================= +function removeData(meta, handle, id_remove) + % Removes the data at position id_remove + + % Only do something if id_remove is not empty + if isempty(id_remove) + return + end + + % Check whether this is a 3D plot + is3D = isAxis3D(meta.gca); + + % Extract the data from the current line handle. + xData = get(handle, 'XData'); + yData = get(handle, 'YData'); + if is3D + zData = get(handle, 'ZData'); + end + + % Remove the data indicated by id_remove + xData(id_remove) = []; + yData(id_remove) = []; + if is3D + zData(id_remove) = []; + end + + % Set the new data. + set(handle, 'XData', xData); + set(handle, 'YData', yData); + if is3D + set(handle, 'ZData', zData); + end +end +% ========================================================================= +function removeNaNs(meta, handle) + % Removes superflous NaNs in the data, i.e. those at the end/beginning of + % the data and consequtive ones. + + % Check whether this is a 3D plot + is3D = isAxis3D(meta.gca); + + % Extract the data from the current line handle. + xData = get(handle, 'XData'); + yData = get(handle, 'YData'); + if is3D + zData = get(handle, 'ZData'); + end + + % Put the data into one matrix + if is3D + data = [xData(:), yData(:), zData(:)]; + else + data = [xData(:), yData(:)]; + end + + % Remove consecutive NaNs + id_nan = any(isnan(data), 2); + id_remove = find(id_nan); + + % If a NaN is preceeded by another NaN, then diff(id_remove)==1 + id_remove = id_remove(diff(id_remove) == 1); + + % Make sure that there are no NaNs at the beginning of the data since + % this would be interpreted as column names by Pgfplots. + % Also drop all NaNs at the end of the data + id_first = find(~id_nan, 1, 'first'); + id_last = find(~id_nan, 1, 'last'); + + % If there are only NaN data points, remove the whole data + if isempty(id_first) + id_remove = 1:length(xData); + else + id_remove = [1:id_first-1, id_remove', id_last+1:length(xData)]'; + end + + % Remove the NaNs + data(id_remove,:) = []; + + % Set the new data. + set(handle, 'XData', data(:, 1)); + set(handle, 'YData', data(:, 2)); + if is3D + set(handle, 'ZData', data(:, 3)); + end +end +% ========================================================================== +function [bottomLeft, topLeft, bottomRight, topRight] = corners2D(xLim, yLim) + % Determine the corners of the axes as defined by xLim and yLim + bottomLeft = [xLim(1), yLim(1)]; + topLeft = [xLim(1), yLim(2)]; + bottomRight = [xLim(2), yLim(1)]; + topRight = [xLim(2), yLim(2)]; +end +% ========================================================================== +function corners = corners3D(xLim, yLim, zLim) + % Determine the corners of the 3D axes as defined by xLim, yLim, and + % zLim + + % Lower square of the cube + lowerBottomLeft = [xLim(1), yLim(1), zLim(1)]; + lowerTopLeft = [xLim(1), yLim(2), zLim(1)]; + lowerBottomRight = [xLim(2), yLim(1), zLim(1)]; + lowerTopRight = [xLim(2), yLim(2), zLim(1)]; + + % Upper square of the cube + upperBottomLeft = [xLim(1), yLim(1), zLim(2)]; + upperTopLeft = [xLim(1), yLim(2), zLim(2)]; + upperBottomRight = [xLim(2), yLim(1), zLim(2)]; + upperTopRight = [xLim(2), yLim(2), zLim(2)]; + + % Put the into one matrix + corners = [lowerBottomLeft; + lowerTopLeft; + lowerBottomRight; + lowerTopRight; + upperBottomLeft; + upperTopLeft; + upperBottomRight; + upperTopRight]; +end +% ========================================================================== +function P = getProjectionMatrix(meta) + % Calculate the projection matrix from a 3D plot into the image plane + + % Get the projection angle + [az, el] = view(meta.gca); + + % Convert from degrees to radians. + az = az*pi/180; + el = el*pi/180; + + % The transformation into the image plane is done in a two-step process. + % First: rotate around the z-axis by -az (in radians) + rotationZ = [ cos(-az) -sin(-az) 0 0 + sin(-az) cos(-az) 0 0 + 0 0 1 0 + 0 0 0 1]; + + % Second: rotate around the x-axis by (el - pi/2) radians. + % NOTE: There are some trigonometric simplifications, as we use + % (el-pi/2) + % cos(x - pi/2) = sin(x) + % sin(x - pi/2) = -cos(x) + rotationX = [ 1 0 0 0 + 0 sin(el) cos(el) 0 + 0 -cos(el) sin(el) 0 + 0 0 0 1]; + + % Get the data aspect ratio. This is necessary, as the axes usually do + % not have the same scale (xRange~=yRange) + aspectRatio = get(meta.gca, 'DataAspectRatio'); + scaleMatrix = diag([1./aspectRatio, 1]); + + % Calculate the projection matrix + P = rotationX * rotationZ * scaleMatrix; +end +% ========================================================================= +function [W, H] = getWidthHeightInPixels(targetResolution) + % Retrieves target figure width and height in pixels + % TODO: If targetResolution is a scalar, W and H are determined + % differently on different environments (octave, local vs. Travis). + % It is unclear why, as this even happens, if `Units` and `Position` + % are matching. Could it be that the `set(gcf,'Units','Inches')` is not + % taken into consideration for `Position`, directly after setting it? + + % targetResolution is PPI + if isscalar(targetResolution) + % Query figure size in inches and convert W and H to target pixels + oldunits = get(gcf,'Units'); + set(gcf,'Units','Inches'); + figSizeIn = get(gcf,'Position'); + W = figSizeIn(3) * targetResolution; + H = figSizeIn(4) * targetResolution; + set(gcf,'Units', oldunits) % restore original unit + + % It is already in the format we want + else + W = targetResolution(1); + H = targetResolution(2); + end +end +% ========================================================================= +function bool = isValidTargetResolution(val) + bool = isnumeric(val) && ~any(isnan(val)) && (isscalar(val) || numel(val) == 2); +end +% ========================================================================= +function bool = isValidAxis(val) + bool = length(val) <= 3; + for i=1:length(val) + bool = bool && ... + (strcmpi(val(i), 'x') || ... + strcmpi(val(i), 'y') || ... + strcmpi(val(i), 'z')); + end +end +% ======================================================================== +function normalizeAxis(handle, cmdOpts) + % Normalizes data from a given axis into the interval [0, 1] + + % Warn about normalizeAxis being experimental + warning('cleanfigure:normalizeAxis', ... + 'Normalization of axis data is experimental!'); + + for axis = cmdOpts.normalizeAxis(:)' + % Get the scale needed to set xyz-lim to [0, 1] + dateLimits = get(handle, [upper(axis), 'Lim']); + dateScale = 1/diff(dateLimits); + + % Set the TickLabelMode to manual to preserve the labels + set(handle, [upper(axis), 'TickLabelMode'], 'manual'); + + % Project the ticks + ticks = get(handle, [upper(axis), 'Tick']); + ticks = (ticks - dateLimits(1))*dateScale; + + % Set the data + set(handle, [upper(axis), 'Tick'], ticks); + set(handle, [upper(axis), 'Lim'], [0, 1]); + + % Traverse the children + children = get(handle, 'Children'); + for child = children(:)' + if isprop(child, [upper(axis), 'Data']) + % Get the data and transform it + data = get(child, [upper(axis), 'Data']); + data = (data - dateLimits(1))*dateScale; + % Set the data again + set(child, [upper(axis), 'Data'], data); + end + end + end +end +% ========================================================================= diff --git a/Functions/helper_functions_community/mat2tikz/src/dev/formatWhitespace.m b/Functions/helper_functions_community/mat2tikz/src/dev/formatWhitespace.m new file mode 100644 index 0000000..763d123 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/src/dev/formatWhitespace.m @@ -0,0 +1,87 @@ +function formatWhitespace(filename) + % FORMATWHITESPACE Formats whitespace and indentation of a document + % + % FORMATWHITESPACE(FILENAME) + % Indents currently active document if FILENAME is empty or not + % specified. FILENAME must be the name of an open document in the + % editor. + % + % Rules: + % - Smart-indent with all function indent option + % - Indentation is 4 spaces + % - Remove whitespace in empty lines + % - Preserve indentantion after line continuations, i.e. ... + % + import matlab.desktop.editor.* + + if nargin < 1, filename = ''; end + + d = getDoc(filename); + oldLines = textToLines(d.Text); + + % Smart indent as AllFunctionIndent + % Using undocumented feature from http://undocumentedmatlab.com/blog/changing-system-preferences-programmatically + editorProp = 'EditorMFunctionIndentType'; + oldVal = com.mathworks.services.Prefs.getStringPref(editorProp); + com.mathworks.services.Prefs.setStringPref(editorProp, 'AllFunctionIndent'); + restoreSettings = onCleanup(@() com.mathworks.services.Prefs.setStringPref(editorProp, oldVal)); + d.smartIndentContents() + + % Preserve crafted continuations of line + lines = textToLines(d.Text); + iContinuation = ~cellfun('isempty',strfind(lines, '...')); + iComment = ~cellfun('isempty',regexp(lines, '^ *%([^%]|$)','once')); + pAfterDots = find(iContinuation & ~iComment)+1; + for ii = 1:numel(pAfterDots) + % Carry over the change in space due to smart-indenting from the + % first continuation line to the last + p = pAfterDots(ii); + nWhiteBefore = find(~isspace(oldLines{p-1}),1,'first'); + nWhiteAfter = find(~isspace(lines{p-1}),1,'first'); + df = nWhiteAfter - nWhiteBefore; + if df > 0 + lines{p} = [blanks(df) oldLines{p}]; + elseif df < 0 + df = min(abs(df)+1, find(~isspace(oldLines{p}),1,'first')); + lines{p} = oldLines{p}(df:end); + else + lines{p} = oldLines{p}; + end + end + + % Remove whitespace lines + idx = cellfun('isempty',regexp(lines, '[^ \t\n]','once')); + lines(idx) = {''}; + + d.Text = linesToText(lines); +end + +function d = getDoc(filename) + import matlab.desktop.editor.* + + if ~ischar(filename) + error('formatWhitespace:charFilename','The FILENAME should be a char.') + end + + try + isEditorAvailable(); + catch + error('formatWhitespace:noEditorApi','Check that the Editor API is available.') + end + + if isempty(filename) + d = getActive(); + else + % TODO: open file if it isn't open in the editor already + d = findOpenDocument(filename); + try + [~,filenameFound] = fileparts(d.Filename); + catch + filenameFound = ''; + end + isExactMatch = strcmp(filename, filenameFound); + if ~isExactMatch + error('formatWhitespace:filenameNotFound','Filename "%s" not found in the editor.', filename) + end + end +end \ No newline at end of file diff --git a/Functions/helper_functions_community/mat2tikz/src/figure2dot.m b/Functions/helper_functions_community/mat2tikz/src/figure2dot.m new file mode 100644 index 0000000..5a4b6db --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/src/figure2dot.m @@ -0,0 +1,123 @@ +function figure2dot(filename, varargin) +%FIGURE2DOT Save figure in Graphviz (.dot) file. +% FIGURE2DOT(filename) saves the current figure as dot-file. +% +% FIGURE2DOT(filename, 'object', HGOBJECT) constructs the graph representation +% of the specified object (default: gcf) +% +% You can visualize the constructed DOT file using: +% - [GraphViz](http://www.graphviz.org) on your computer +% - [WebGraphViz](http://www.webgraphviz.com) online +% - [Gravizo](http://www.gravizo.com) for your markdown files +% - and a lot of other software such as OmniGraffle +% +% See also: matlab2tikz, cleanfigure, uiinspect, inspect + + ipp = m2tInputParser(); + ipp = ipp.addRequired(ipp, 'filename', @ischar); + ipp = ipp.addParamValue(ipp, 'object', gcf, @ishghandle); + ipp = ipp.parse(ipp, filename, varargin{:}); + args = ipp.Results; + + filehandle = fopen(args.filename, 'w'); + finally_fclose_filehandle = onCleanup(@() fclose(filehandle)); + + % start printing + fprintf(filehandle, 'digraph simple_hierarchy {\n\n'); + fprintf(filehandle, 'node[shape=box];\n\n'); + + % define the root node + node_number = 0; + p = get(args.object, 'Parent'); + % define root element + type = get(p, 'Type'); + fprintf(filehandle, 'N%d [label="%s"]\n\n', node_number, type); + + % start recursion + plot_children(filehandle, p, node_number); + + % finish off + fprintf(filehandle, '}'); + + % ---------------------------------------------------------------------------- + function plot_children(fh, h, parent_node) + + children = allchild(h); + + for h = children(:)' + if shouldSkip(h), continue, end; + node_number = node_number + 1; + + label = {}; + label = addHGProperty(label, h, 'Type', ''); + try + hClass = class(handle(h)); + label = addProperty(label, 'Class', hClass); + catch + % don't do anything + end + label = addProperty(label, 'Handle', sprintf('%g', double(h))); + label = addHGProperty(label, h, 'Title', ''); + label = addHGProperty(label, h, 'Axes', []); + label = addHGProperty(label, h, 'String', ''); + label = addHGProperty(label, h, 'Tag', ''); + label = addHGProperty(label, h, 'DisplayName', ''); + label = addHGProperty(label, h, 'Visible', 'on'); + label = addHGProperty(label, h, 'HandleVisibility', 'on'); + + % print node + fprintf(fh, 'N%d [label="%s"]\n', ... + node_number, m2tstrjoin(label, '\n')); + + % connect to the child + fprintf(fh, 'N%d -> N%d;\n\n', parent_node, node_number); + + % recurse + plot_children(fh, h, node_number); + end + end +end +% ============================================================================== +function bool = shouldSkip(h) + % returns TRUE for objects that can be skipped + objType = get(h, 'Type'); + bool = ismember(lower(objType), guitypes()); +end +% ============================================================================== +function label = addHGProperty(label, h, propName, default) + % get a HG property and assign it to a GraphViz node label + if ~exist('default','var') || isempty(default) + shouldOmit = @isempty; + elseif isa(default, 'function_handle') + shouldOmit = default; + else + shouldOmit = @(v) isequal(v,default); + end + + if isprop(h, propName) + propValue = get(h, propName); + if numel(propValue) == 1 && ishghandle(propValue) && isprop(propValue, 'String') + % dereference Titles, labels, ... + propValue = get(propValue, 'String'); + elseif ishghandle(propValue) + % dereference other HG objects to their raw handle value (double) + propValue = double(propValue); + elseif iscell(propValue) + propValue = ['{' m2tstrjoin(propValue,',') '}']; + end + + if ~shouldOmit(propValue) + label = addProperty(label, propName, propValue); + end + end +end +function label = addProperty(label, propName, propValue) + % add a property to a GraphViz node label + if isnumeric(propValue) + propValue = num2str(propValue); + elseif iscell(propValue) + propValue = m2tstrjoin(propValue,sprintf('\n')); + end + label = [label, sprintf('%s: %s', propName, propValue)]; +end +% ============================================================================== diff --git a/Functions/helper_functions_community/mat2tikz/src/m2tInputParser.m b/Functions/helper_functions_community/mat2tikz/src/m2tInputParser.m new file mode 100644 index 0000000..79b3c6d --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/src/m2tInputParser.m @@ -0,0 +1,231 @@ +function parser = m2tInputParser() +%MATLAB2TIKZINPUTPARSER Input parsing for matlab2tikz. +% This implementation exists because Octave is lacking one. + + % Initialize the structure. + parser = struct (); + % Public Properties + parser.Results = {}; + % Enabel/disable parameters case sensitivity. + parser.CaseSensitive = false; + % Keep parameters not defined by the constructor. + parser.KeepUnmatched = false; + % Enable/disable warning for parameters not defined by the constructor. + parser.WarnUnmatched = true; + % Enable/disable passing arguments in a structure. + parser.StructExpand = true; + % Names of parameters defined in input parser constructor. + parser.Parameters = {}; + % Names of parameters not defined in the constructor. + parser.Unmatched = struct (); + % Names of parameters using default values. + parser.UsingDefaults = {}; + % Names of deprecated parameters and their alternatives + parser.DeprecatedParameters = struct(); + + % Handles for functions that act on the object. + parser.addRequired = @addRequired; + parser.addOptional = @addOptional; + parser.addParamValue = @addParamValue; + parser.deprecateParam = @deprecateParam; + parser.parse = @parse; + + % Initialize the parser plan + parser.plan = {}; +end +% ========================================================================= +function p = parser_plan (q, arg_type, name, default, validator) + p = q; + plan = p.plan; + if (isempty (plan)) + plan = struct (); + n = 1; + else + n = numel (plan) + 1; + end + plan(n).type = arg_type; + plan(n).name = name; + plan(n).default = default; + plan(n).validator = validator; + p.plan = plan; +end +% ========================================================================= +function p = addRequired (p, name, validator) + p = parser_plan (p, 'required', name, [], validator); +end +% ========================================================================= +function p = addOptional (p, name, default, validator) + p = parser_plan (p, 'optional', name, default, validator); +end +% ========================================================================= +function p = addParamValue (p, name, default, validator) + p = parser_plan (p, 'paramvalue', name, default, validator); +end +% ========================================================================= +function p = deprecateParam (p, name, alternatives) + if isempty(alternatives) + alternatives = {}; + elseif ischar(alternatives) + alternatives = {alternatives}; % make cellstr + elseif ~iscellstr(alternatives) + error('m2tInputParser:BadAlternatives',... + 'Alternatives for a deprecated parameter must be a char or cellstr'); + end + p.DeprecatedParameters.(name) = alternatives; +end +% ========================================================================= +function p = parse (p, varargin) + plan = p.plan; + results = p.Results; + using_defaults = {}; + if (p.CaseSensitive) + name_cmp = @strcmp; + else + name_cmp = @strcmpi; + end + if (p.StructExpand) + k = find (cellfun (@isstruct, varargin)); + for m = numel(k):-1:1 + n = k(m); + s = varargin{n}; + c = [fieldnames(s).'; struct2cell(s).']; + c = c(:).'; + if (n > 1 && n < numel (varargin)) + varargin = horzcat (varargin(1:n-1), c, varargin(n+1:end)); + elseif (numel (varargin) == 1) + varargin = c; + elseif (n == 1); + varargin = horzcat (c, varargin(n+1:end)); + else % n == numel (varargin) + varargin = horzcat (varargin(1:n-1), c); + end + end + end + if (isempty (results)) + results = struct (); + end + type = {plan.type}; + n = find( strcmp( type, 'paramvalue' ) ); + m = setdiff (1:numel( plan ), n ); + plan = plan ([n,m]); + for n = 1 : numel (plan) + found = false; + results.(plan(n).name) = plan(n).default; + if (~ isempty (varargin)) + switch plan(n).type + case 'required' + found = true; + if (strcmpi (varargin{1}, plan(n).name)) + varargin(1) = []; + end + value = varargin{1}; + varargin(1) = []; + case 'optional' + m = find (cellfun (@ischar, varargin)); + k = find (name_cmp (plan(n).name, varargin(m))); + if (isempty (k) && validate_arg (plan(n).validator, varargin{1})) + found = true; + value = varargin{1}; + varargin(1) = []; + elseif (~ isempty (k)) + m = m(k); + found = true; + value = varargin{max(m)+1}; + varargin(union(m,m+1)) = []; + end + case 'paramvalue' + m = find( cellfun (@ischar, varargin) ); + k = find (name_cmp (plan(n).name, varargin(m))); + if (~ isempty (k)) + found = true; + m = m(k); + value = varargin{max(m)+1}; + varargin(union(m,m+1)) = []; + end + otherwise + error( sprintf ('%s:parse', mfilename), ... + 'parse (%s): Invalid argument type.', mfilename ... + ) + end + end + if (found) + if (validate_arg (plan(n).validator, value)) + results.(plan(n).name) = value; + else + error( sprintf ('%s:invalidinput', mfilename), ... + '%s: Input argument ''%s'' has invalid value.\n', mfilename, plan(n).name ... + ); + end + p.Parameters = union (p.Parameters, {plan(n).name}); + elseif (strcmp (plan(n).type, 'required')) + error( sprintf ('%s:missinginput', mfilename), ... + '%s: input ''%s'' is missing.\n', mfilename, plan(n).name ... + ); + else + using_defaults = union (using_defaults, {plan(n).name}); + end + end + + if ~isempty(varargin) + % Include properties that do not match specified properties + for n = 1:2:numel(varargin) + if ischar(varargin{n}) + if p.KeepUnmatched + results.(varargin{n}) = varargin{n+1}; + end + if p.WarnUnmatched + warning(sprintf('%s:unmatchedArgument',mfilename), ... + 'Ignoring unknown argument "%s"', varargin{n}); + end + p.Unmatched.(varargin{n}) = varargin{n+1}; + else + error (sprintf ('%s:invalidinput', mfilename), ... + '%s: invalid input', mfilename) + end + end + end + + % Store the results of the parsing + p.Results = results; + p.UsingDefaults = using_defaults; + + warnForDeprecatedParameters(p); +end +% ========================================================================= +function result = validate_arg (validator, arg) + try + result = validator (arg); + catch %#ok + result = false; + end +end +% ========================================================================= +function warnForDeprecatedParameters(p) + usedDeprecatedParameters = intersect(p.Parameters, fieldnames(p.DeprecatedParameters)); + + for iParam = 1:numel(usedDeprecatedParameters) + oldParameter = usedDeprecatedParameters{iParam}; + alternatives = p.DeprecatedParameters.(oldParameter); + + switch numel(alternatives) + case 0 + replacements = ''; + case 1 + replacements = ['''' alternatives{1} '''']; + otherwise + replacements = deblank(sprintf('''%s'' and ',alternatives{:})); + replacements = regexprep(replacements,' and$',''); + end + if ~isempty(replacements) + replacements = sprintf('From now on, please use %s to control the output.\n',replacements); + end + + message = ['\n===============================================================================\n', ... + 'You are using the deprecated parameter ''%s''.\n', ... + '%s', ... + '===============================================================================']; + warning('matlab2tikz:deprecatedParameter', ... + message, oldParameter, replacements); + + end +end diff --git a/Functions/helper_functions_community/mat2tikz/src/m2tcustom.m b/Functions/helper_functions_community/mat2tikz/src/m2tcustom.m new file mode 100644 index 0000000..03e1fd4 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/src/m2tcustom.m @@ -0,0 +1,216 @@ +function [value] = m2tcustom(handle, varargin) + % M2TCUSTOM creates user-defined options for matlab2tikz output + % + % M2TCUSTOM can be used to create, get and set a properly formatted data + % structure to customize the conversion of MATLAB figures to TikZ using + % matlab2tikz. In particular, it allows to: + % + % * add blocks of LaTeX/TikZ code around any HG object, + % * add blocks of comments around any HG object, + % * add TikZ options to any HG object, + % * add LaTeX/TikZ code inside some HG objects (e.g. axes), + % * provide a custom handler to convert a particular HG object. + % + % Note that this provides advanced functionality. Only very basic + % sanity checks are performed such that injudicious use may produce + % broken TikZ figures! + % + % It is HIGHLY recommended that you are comfortable with: + % + % * writing pgfplots, TikZ and LaTeX code, + % * using the Handle Graphics (HG) framework in MATLAB/Octave, and + % * the inner working of matlab2tikz (for custom handlers) + % + % when you use this function. I.e. you should know what you are doing. + % + % + % Usage as a GETTER: + % ------------------ + % + % value = M2TCUSTOM(handle) + % retrieves the current custom data structure from the HG object "handle" + % + % + % Usage as a SETTER: + % ------------------ + % + % M2TCUSTOM(handle, ...) + % value = M2TCUSTOM(handle, ...) + % will construct the proper data structure and try to set it to the object + % |handle| if possible. The arguments (see below) are specified in + % key-value pairs akin to a normal |struct|, but here we do a few checks + % and data normalization. + % + % If we denote BLOCK to mean either a |char| or a |cellstr|, the following + % options can be passed. Different entries in a cellstr are assumed + % separated by a newline. The default values are empty. + % + % M2TCUSTOM(handle, 'commentBefore', BLOCK, ...) + % M2TCUSTOM(handle, 'commentAfter' , BLOCK, ...) + % to add comments before/after the object. Our code translates newlines + % and adds the percentage signs for you. + % + % M2TCUSTOM(handle, 'codeBefore', BLOCK, ...) + % M2TCUSTOM(handle, 'codeAfter', BLOCK, ...) + % M2TCUSTOM(handle, 'codeInsideFirst', BLOCK, ...) + % M2TCUSTOM(handle, 'codeInsideLast', BLOCK, ...) + % to add raw LaTeX/TikZ code respectively before, after, as first thing + % inside or as last thing inside the pgfplots representation of the object. + % Note that for some HG objects, (e.g. line objects), |codeInsideFirst| + % and |codeInsideLast| do not make any sense and are hence ignored. + % + % M2TCUSTOM(handle, 'extraOptions', OPTIONS, ...) + % adds extra pgfplots/TikZ options to the end of the option list. Here, + % OPTIONS is properly formatted TikZ code in + % - a |char| (e.g. 'color=red, line width=1pt' ) + % - a |cellstr| (e.g. {'color=red','line width=1pt'}) + % + % M2TCUSTOM(handle, 'customHandler', FUNCTION_HANDLE, ...) + % allows you to replace the default matlab2tikz handler for this object. + % This is not for the faint of heart and requires intimate knowledge of + % the matlab2tikz code base! We expect a function (either as |char| or + % function handle) that will be called as + % + % [m2t, str] = feval(handler, m2t, handle, custom) + % + % such that the expected function signature is: + % + % function [m2t, str] = handler(m2t, handle, custom) + % + % where |m2t| is an undocumented/unstable data structure, + % |str| is a char containing TikZ code representing the + % HG object |handle| as generated by your handler, + % |custom| is a structure as returned by |m2tcustom| + % from which you only need to handle |extraOptions|, + % |codeInsideFirst| and |codeInsideLast| when applicable. + % A particularly useful value for |customHandler| is 'drawNothing', + % which remove the object from the output. + % + % + % Example: + % -------- + % + % Executing the following MATLAB code fragment: + % + % figure; + % plot(1:10); + % EOL = sprintf('\n'); + % m2tCustom(gca, 'codeBefore' , ['' EOL] , ... + % 'codeAfter' , ['' EOL] , ... + % 'commentsBefore' , '' , ... + % 'commentsAfter' , '' , ... + % 'codeInsideFirst' , ['' EOL], ... + % 'codeInsideLast' , ['' EOL], ... + % 'extraOptions' , ''); + % + % matlab2tikz('test.tikz') + % + % Should result in a |test.tikz| file with contents that look somewhat + % like this: + % + % \begin{tikzpicture} + % % + % + % \begin{axis}[..., ] + % + % \addplot{...}; + % + % \end{axis} + % % + % + % \end{tikzpicture} + % + % See also: matlab2tikz, setappdata, getappdata + + %% arguments specific to this constructor function + ipp = m2tInputParser(); + ipp = ipp.addRequired(ipp, 'handle', @isHgObject); + + %% Declaration of the custom data structure + ipp = ipp.addParamValue(ipp, 'codeBefore', '', @isCellstrOrChar); + ipp = ipp.addParamValue(ipp, 'codeAfter', '', @isCellstrOrChar); + + ipp = ipp.addParamValue(ipp, 'commentsBefore', '', @isCellstrOrChar); + ipp = ipp.addParamValue(ipp, 'commentsAfter', '', @isCellstrOrChar); + + ipp = ipp.addParamValue(ipp, 'codeInsideFirst', '', @isCellstrOrChar); + ipp = ipp.addParamValue(ipp, 'codeInsideLast', '', @isCellstrOrChar); + + ipp = ipp.addParamValue(ipp, 'extraOptions', '', @isCellstrOrChar); + ipp = ipp.addParamValue(ipp, 'customHandler', '', @isHandler); + + %% Parse the arguments + ipp = ipp.parse(ipp, handle, varargin{:}); + + %% Construct custom data structure + % We leverage the results from the input parser. It provides us + % with validation already. We just need to remove bookkeeping fields + value = ipp.Results; + value = rmfield(value, {'handle'}); + + %% Normalize the actual values + value.codeBefore = cellstr2char(value.codeBefore); + value.codeAfter = cellstr2char(value.codeAfter); + value.codeInsideFirst = cellstr2char(value.codeInsideFirst); + value.codeInsideLast = cellstr2char(value.codeInsideLast); + value.commentsBefore = cellstr2char(value.commentsBefore); + value.commentsAfter = cellstr2char(value.commentsAfter); + if isempty(value.customHandler) + value = rmfield(value, 'customHandler'); + end + % extraOptions gets normalized by |opts_append_userdefined| + + %% Different Usage modes + MATLAB2TIKZ = 'matlab2tikz'; % key used for application data storage + if numel(varargin) == 0 + %% GETTER MODE + % syntax: value = m2tcustom(h); + if ~isempty(handle) + object = getappdata(handle, MATLAB2TIKZ); + else + object = []; + end + if ~isempty(object) + value = object; + else + % |value| contains all default (empty) values + end + else + %% SETTER MODE + % syntax: m2tcustom(h , key1, val1, ...) + % syntax: value = m2tcustom([], key1, val1, ...) + if ~isempty(handle) + setappdata(handle, MATLAB2TIKZ, value); + end + end +end +% == INPUT VALIDATORS ========================================================== +function bool = isHgObject(value) + % true for HG object or empty (or numeric for backwards compatibility) + bool = isempty(value) || ishghandle(value) || isnumeric(value); +end +function bool = isCellstrOrChar(value) + % true for cellstr or char + bool = ischar(value) || iscellstr(value); +end +function bool = isHandler(value) + % true for char or function handle of the form [m2t, str] = f(m2t, h, opts) + bool = isempty(value) || ischar(value) || ... + (isa(value, 'function_handle') && ... + atLeastOrUnknown(nargin(value), 3) && ... + atLeastOrUnknown(nargout(value), 2)); +end +function bool = atLeastOrUnknown(nargs, limit) + % checks for |nargin| and |nargout| >= |limit| (or equal to -1) + UNKNOWN = -1; + bool = (nargs == UNKNOWN) || nargs >= limit; +end +% == FIELD NORMALIZATION ======================================================= +function value = cellstr2char(value) + % convert cellstr to char (and keep char unaffected) + if iscellstr(value) + EOL = sprintf('\n'); + value = m2tstrjoin(value, EOL); + end +end +% ============================================================================== diff --git a/Functions/helper_functions_community/mat2tikz/src/matlab2tikz.m b/Functions/helper_functions_community/mat2tikz/src/matlab2tikz.m new file mode 100644 index 0000000..9c25805 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/src/matlab2tikz.m @@ -0,0 +1,7167 @@ +function matlab2tikz(varargin) + %MATLAB2TIKZ Save figure in native LaTeX (TikZ/Pgfplots). + % MATLAB2TIKZ() saves the current figure as LaTeX file. + % MATLAB2TIKZ comes with several options that can be combined at will. For + % example: + % + % MATLAB2TIKZ('showInfo', false, 'strict', true, "figure.tex") + % + % MATLAB2TIKZ(FILENAME,...) or MATLAB2TIKZ('filename',FILENAME,...) + % stores the LaTeX code in FILENAME. + % + % MATLAB2TIKZ('filehandle',FILEHANDLE,...) stores the LaTeX code in the file + % referenced by FILEHANDLE. (default: []) + % + % MATLAB2TIKZ('figurehandle',FIGUREHANDLE,...) explicitly specifies the + % handle of the figure that is to be stored. (default: gcf) + % + % MATLAB2TIKZ('colormap',DOUBLE,...) explicitly specifies the colormap to be + % used. (default: current color map) + % + % MATLAB2TIKZ('strict',BOOL,...) tells MATLAB2TIKZ to adhere to MATLAB(R) + % conventions wherever there is room for relaxation. (default: false) + % + % MATLAB2TIKZ('strictFontSize',BOOL,...) retains the exact font sizes + % specified in MATLAB for the TikZ code. This goes against normal LaTeX + % practice. (default: false) + % + % MATLAB2TIKZ('showInfo',BOOL,...) turns informational output on or off. + % (default: true) + % + % MATLAB2TIKZ('showWarnings',BOOL,...) turns warnings on or off. + % (default: true) + % + % MATLAB2TIKZ('imagesAsPng',BOOL,...) stores MATLAB(R) images as (lossless) + % PNG files. This is more efficient than storing the image color data as TikZ + % matrix. (default: true) + % + % MATLAB2TIKZ('externalData',BOOL,...) stores all data points in external + % files as tab separated values (TSV files). (default: false) + % + % MATLAB2TIKZ('dataPath',CHAR, ...) defines where external data files + % and/or PNG figures are saved. It can be either an absolute or a relative + % path with respect to your MATLAB work directory. By default, data files are + % placed in the same directory as the TikZ output file. To place data files + % in your MATLAB work directory, you can use '.'. (default: []) + % + % MATLAB2TIKZ('relativeDataPath',CHAR, ...) tells MATLAB2TIKZ to use the + % given path to follow the external data files and PNG files. This is the + % relative path from your main LaTeX file to the data file directory. + % By default the same directory is used as the output (default: []) + % + % MATLAB2TIKZ('height',CHAR,...) sets the height of the image. This can be + % any LaTeX-compatible length, e.g., '3in' or '5cm' or '0.5\textwidth'. If + % unspecified, MATLAB2TIKZ tries to make a reasonable guess. + % + % MATLAB2TIKZ('width',CHAR,...) sets the width of the image. + % If unspecified, MATLAB2TIKZ tries to make a reasonable guess. + % + % MATLAB2TIKZ('noSize',BOOL,...) determines whether 'width', 'height', and + % 'scale only axis' are specified in the generated TikZ output. For compatibility with the + % tikzscale package set this to true. (default: false) + % + % MATLAB2TIKZ('extraCode',CHAR or CELLCHAR,...) explicitly adds extra code + % at the beginning of the output file. (default: []) + % + % MATLAB2TIKZ('extraCodeAtEnd',CHAR or CELLCHAR,...) explicitly adds extra + % code at the end of the output file. (default: []) + % + % MATLAB2TIKZ('extraAxisOptions',CHAR or CELLCHAR,...) explicitly adds extra + % options to the Pgfplots axis environment. (default: []) + % + % MATLAB2TIKZ('extraColors', {{'name',[R G B]}, ...} , ...) adds + % user-defined named RGB-color definitions to the TikZ output. + % R, G and B are expected between 0 and 1. (default: {}) + % + % MATLAB2TIKZ('extraTikzpictureOptions',CHAR or CELLCHAR,...) + % explicitly adds extra options to the tikzpicture environment. (default: []) + % + % MATLAB2TIKZ('encoding',CHAR,...) sets the encoding of the output file. + % + % MATLAB2TIKZ('floatFormat',CHAR,...) sets the format used for float values. + % You can use this to decrease the file size. (default: '%.15g') + % + % MATLAB2TIKZ('maxChunkLength',INT,...) sets maximum number of data points + % per \addplot for line plots (default: 4000) + % + % MATLAB2TIKZ('parseStrings',BOOL,...) determines whether title, axes labels + % and the like are parsed into LaTeX by MATLAB2TIKZ's parser. + % If you want greater flexibility, set this to false and use straight LaTeX + % for your labels. (default: true) + % + % MATLAB2TIKZ('parseStringsAsMath',BOOL,...) determines whether to use TeX's + % math mode for more characters (e.g. operators and figures). (default: false) + % + % MATLAB2TIKZ('showHiddenStrings',BOOL,...) determines whether to show + % strings whose were deliberately hidden. This is usually unnecessary, but + % can come in handy for unusual plot types (e.g., polar plots). (default: + % false) + % + % MATLAB2TIKZ('interpretTickLabelsAsTex',BOOL,...) determines whether to + % interpret tick labels as TeX. MATLAB(R) doesn't allow to do that in R2014a + % or before. In R2014b and later, please set the "TickLabelInterpreter" + % property of the relevant axis to get the same effect. (default: false) + % + % MATLAB2TIKZ('arrowHeadSize', FLOAT, ...) allows to resize the arrow heads + % in quiver plots by rescaling the arrow heads by a positive scalar. (default: 10) + % + % MATLAB2TIKZ('tikzFileComment',CHAR,...) adds a custom comment to the header + % of the output file. (default: '') + % + % MATLAB2TIKZ('addLabels',BOOL,...) add labels to plots: using Tag property + % or automatic names (where applicable) which make it possible to refer to + % them using \ref{...} (e.g., in the caption of a figure). (default: false) + % + % MATLAB2TIKZ('standalone',BOOL,...) determines whether to produce + % a standalone compilable LaTeX file. Setting this to true may be useful for + % taking a peek at what the figure will look like. (default: false) + % + % MATLAB2TIKZ('checkForUpdates',BOOL,...) determines whether to automatically + % check for updates of matlab2tikz. (default: true (if not using git)) + % + % MATLAB2TIKZ('semanticLineWidths',CELLMATRIX,...) allows you to customize + % the mapping of semantic "line width" values. + % A valid entry is an Nx2 cell array: + % - the first column contains the semantic names, + % - the second column contains the corresponding line widths in points. + % The entries you provide are used in addition to the pgf defaults: + % {'ultra thin', 0.1; 'very thin' , 0.2; 'thin', 0.4; 'semithick', 0.6; + % 'thick' , 0.8; 'very thick', 1.2; 'ultra thick', 1.6} + % or a single "NaN" can be provided to turn off this feature alltogether. + % If you specify the default names, their mapping will be overwritten. + % Inside your LaTeX document, you are responsible to make sure these TikZ + % styles are properly defined. + % (Default: NaN) + % + % Example + % x = -pi:pi/10:pi; + % y = tan(sin(x)) - sin(tan(x)); + % plot(x,y,'--rs'); + % matlab2tikz('myfile.tex'); + % + % See also: cleanfigure + + %% Check if we are in MATLAB or Octave. + minimalVersion = struct('MATLAB', struct('name','2014a', 'num',[8 3]), ... + 'Octave', struct('name','3.8', 'num',[3 8])); + checkDeprecatedEnvironment(minimalVersion); + + m2t.args = []; % For command line arguments + m2t.current = []; % For currently active objects + m2t.transform = []; % For hgtransform groups + m2t.pgfplotsVersion = [1,3]; + m2t.about.name = 'matlab2tikz'; + m2t.about.version = '1.1.0'; + m2t.about.years = '2008--2016'; + m2t.about.website = 'http://www.mathworks.com/matlabcentral/fileexchange/22022-matlab2tikz-matlab2tikz'; + m2t.about.github = 'https://github.com/matlab2tikz/matlab2tikz'; + m2t.about.wiki = [m2t.about.github '/wiki']; + m2t.about.issues = [m2t.about.github '/issues']; + m2t.about.develop = [m2t.about.github '/tree/develop']; + VCID = VersionControlIdentifier(); + m2t.about.versionFull = strtrim(sprintf('v%s %s', m2t.about.version, VCID)); + + m2t.tol = 1.0e-15; % numerical tolerance (e.g. used to test equality of doubles) + + % the actual contents of the TikZ file go here + m2t.content = struct('name', '', ... + 'comment', [], ... + 'options', {opts_new()}, ... + 'content', {cell(0)}, ... + 'children', {cell(0)}); + m2t.preamble = sprintf(['\\usepackage[T1]{fontenc}\n', ... + '\\usepackage[utf8]{inputenc}\n', ... + '\\usepackage{pgfplots}\n', ... + '\\usepackage{grffile}\n', ... + '\\pgfplotsset{compat=newest}\n', ... + '\\usetikzlibrary{plotmarks}\n', ... + '\\usetikzlibrary{arrows.meta}\n', ... + '\\usepgfplotslibrary{patchplots}\n', ... + '\\usepackage{amsmath}\n']); + + %% scan the options + ipp = m2tInputParser; + + ipp = ipp.addOptional(ipp, 'filename', '', @(x) filenameValidation(x,ipp)); + ipp = ipp.addOptional(ipp, 'filehandle', [], @filehandleValidation); + + ipp = ipp.addParamValue(ipp, 'figurehandle', get(0,'CurrentFigure'), @ishandle); + ipp = ipp.addParamValue(ipp, 'colormap', [], @isnumeric); + ipp = ipp.addParamValue(ipp, 'strict', false, @islogical); + ipp = ipp.addParamValue(ipp, 'strictFontSize', false, @islogical); + ipp = ipp.addParamValue(ipp, 'showInfo', true, @islogical); + ipp = ipp.addParamValue(ipp, 'showWarnings', true, @islogical); + ipp = ipp.addParamValue(ipp, 'checkForUpdates', isempty(VCID), @islogical); + + ipp = ipp.addParamValue(ipp, 'semanticLineWidths', NaN, @isValidSemanticLineWidthDefinition); + + ipp = ipp.addParamValue(ipp, 'encoding' , '', @ischar); + ipp = ipp.addParamValue(ipp, 'standalone', false, @islogical); + ipp = ipp.addParamValue(ipp, 'tikzFileComment', '', @ischar); + ipp = ipp.addParamValue(ipp, 'extraColors', {}, @isColorDefinitions); + ipp = ipp.addParamValue(ipp, 'extraCode', {}, @isCellOrChar); + ipp = ipp.addParamValue(ipp, 'extraCodeAtEnd', {}, @isCellOrChar); + ipp = ipp.addParamValue(ipp, 'extraAxisOptions', {}, @isCellOrChar); + ipp = ipp.addParamValue(ipp, 'extraTikzpictureOptions', {}, @isCellOrChar); + ipp = ipp.addParamValue(ipp, 'floatFormat', '%.15g', @ischar); + ipp = ipp.addParamValue(ipp, 'automaticLabels', false, @islogical); + ipp = ipp.addParamValue(ipp, 'addLabels', false, @islogical); + ipp = ipp.addParamValue(ipp, 'showHiddenStrings', false, @islogical); + ipp = ipp.addParamValue(ipp, 'height', '', @ischar); + ipp = ipp.addParamValue(ipp, 'width' , '', @ischar); + ipp = ipp.addParamValue(ipp, 'imagesAsPng', true, @islogical); + ipp = ipp.addParamValue(ipp, 'externalData', false, @islogical); + ipp = ipp.addParamValue(ipp, 'dataPath', '', @ischar); + ipp = ipp.addParamValue(ipp, 'relativeDataPath', '', @ischar); + ipp = ipp.addParamValue(ipp, 'noSize', false, @islogical); + ipp = ipp.addParamValue(ipp, 'arrowHeadSize', 10, @(x) x>0); + + % Maximum chunk length. + % TeX parses files line by line with a buffer of size buf_size. If the + % plot has too many data points, pdfTeX's buffer size may be exceeded. + % As a work-around, the plot is split into several smaller chunks. + % + % What is a "large" array? + % TeX parser buffer is buf_size=200 000 char on Mac TeXLive, let's say + % 100 000 to be on the safe side. + % 1 point is represented by 25 characters (estimation): 2 coordinates (10 + % char), 2 brackets, comma and white space, + 1 extra char. + % That gives a magic arbitrary number of 4000 data points per array. + ipp = ipp.addParamValue(ipp, 'maxChunkLength', 4000, @isnumeric); + + % By default strings like axis labels are parsed to match the appearance of + % strings as closely as possible to that generated by MATLAB. + % If the user wants to have particular strings in the matlab2tikz output that + % can't be generated in MATLAB, they can disable string parsing. In that case + % all strings are piped literally to the LaTeX output. + ipp = ipp.addParamValue(ipp, 'parseStrings', true, @islogical); + + % In addition to regular string parsing, an additional stage can be enabled + % which uses TeX's math mode for more characters like figures and operators. + ipp = ipp.addParamValue(ipp, 'parseStringsAsMath', false, @islogical); + + % As opposed to titles, axis labels and such, MATLAB(R) does not interpret tick + % labels as TeX. matlab2tikz retains this behavior, but if it is desired to + % interpret the tick labels as TeX, set this option to true. + ipp = ipp.addParamValue(ipp, 'interpretTickLabelsAsTex', false, @islogical); + + %% deprecated parameters (will auto-generate warnings upon parse) + ipp = ipp.addParamValue(ipp, 'relativePngPath', '', @ischar); + ipp = ipp.deprecateParam(ipp, 'relativePngPath', 'relativeDataPath'); + ipp = ipp.deprecateParam(ipp, 'automaticLabels', 'addLabels'); + + %% Finally parse all the arguments + ipp = ipp.parse(ipp, varargin{:}); + m2t.args = ipp.Results; % store the input arguments back into the m2t data struct + + %% Inform users of potentially dangerous options + warnAboutParameter(m2t, 'parseStringsAsMath', @(opt)(opt==true), ... + ['This may produce undesirable string output. For full control over output\n', ... + 'strings please set the parameter "parseStrings" to false.']); + warnAboutParameter(m2t, 'noSize', @(opt)(opt==true), ... + 'This may impede both axes sizing and placement!'); + warnAboutParameter(m2t, 'imagesAsPng', @(opt)(opt==false), ... + ['It is highly recommended to use PNG data to store images.\n', ... + 'Make sure to set "imagesAsPng" to true.']); + + %% Do some global initialization + m2t.color = configureColors(m2t.args.extraColors); + m2t.semantic.LineWidth = configureSemanticLineWidths(m2t.args.semanticLineWidths); + + % define global counter variables + m2t.count.pngFile = 0; % number of PNG files + m2t.count.tsvFile = 0; % number of TSV files + m2t.count.autolabel = 0; % number of automatic labels + m2t.count.plotyylabel = 0; % number of plotyy labels + + %% shortcut + m2t.ff = m2t.args.floatFormat; + + %% add global elements + if isempty(m2t.args.figurehandle) + error('matlab2tikz:figureNotFound','MATLAB figure not found.'); + end + m2t.current.gcf = m2t.args.figurehandle; + if m2t.args.colormap + m2t.current.colormap = m2t.args.colormap; + else + m2t.current.colormap = get(m2t.current.gcf, 'colormap'); + end + + %% handle output file handle/file name + [m2t, fid, fileWasOpen] = openFileForOutput(m2t); + + % By default, reference the PNG (if required) from the TikZ file + % as the file path of the TikZ file itself. This works if the MATLAB script + % is executed in the same folder where the TeX file sits. + if isempty(m2t.args.relativeDataPath) + if ~isempty(m2t.args.relativePngPath) + %NOTE: eventually break backwards compatibility of relative PNG path + m2t.relativeDataPath = m2t.args.relativePngPath; + userWarning(m2t, ['Using "relativePngPath" for "relativeDataPath".', ... + ' This will stop working in a future release.']); + else + m2t.relativeDataPath = m2t.args.relativeDataPath; + end + else + m2t.relativeDataPath = m2t.args.relativeDataPath; + end + if isempty(m2t.args.dataPath) + m2t.dataPath = fileparts(m2t.tikzFileName); + else + m2t.dataPath = m2t.args.dataPath; + end + + %% print some version info to the screen + userInfo(m2t, ['(To disable info messages, pass [''showInfo'', false] to matlab2tikz.)\n', ... + '(For all other options, type ''help matlab2tikz''.)\n']); + + userInfo(m2t, '\nThis is %s %s.\n', m2t.about.name, m2t.about.versionFull) + + % In Octave, put a new line and some spaces in between the URLs for clarity. + % In MATLAB this is not necessary, since the URLs get (shorter) descriptions. + sep = switchMatOct('', sprintf('\n ')); + versionInfo = ['The latest developments can be retrieved from %s.\n', ... + 'You can find more documentation on %s and %s.\n', ... + 'If you encounter bugs or want a new feature, go to %s.\n', ... + 'Please visit %s to rate %s or download the stable release.\n']; + userInfo(m2t, versionInfo, ... + clickableUrl(m2t.about.develop, 'our development branch'), ... + [sep clickableUrl(m2t.about.github, 'our GitHub page') sep], ... + [sep clickableUrl(m2t.about.wiki, 'wiki')], ... + [sep clickableUrl(m2t.about.issues, 'our issue tracker')],... + [clickableUrl(m2t.about.website, 'FileExchange') sep],... + m2t.about.name); + + %% Save the figure as TikZ to file + m2t = saveToFile(m2t, fid, fileWasOpen); + + %% Check for a new matlab2tikz version outside version control + if m2t.args.checkForUpdates + m2tUpdater(m2t.about, m2t.args.showInfo); + end + +end +% ============================================================================== +function [m2t, counterValue] = incrementGlobalCounter(m2t, counterName) + % Increments a global counter value and returns its value + m2t.count.(counterName) = m2t.count.(counterName) + 1; + counterValue = m2t.count.(counterName); +end +% ============================================================================== +function colorConfig = configureColors(extraColors) + % Sets the global color options for matlab2tikz + colorConfig = struct(); + + % Set the color resolution. + colorConfig.depth = 48; %[bit] RGB color depth (typical values: 24, 30, 48) + colorConfig.precision = 2^(-colorConfig.depth/3); + colorConfig.format = sprintf('%%0.%df',ceil(-log10(colorConfig.precision))); + + % The following color RGB-values which will need to be defined: + % + % - 'extraNames' contains their designated names, + % - 'extraSpecs' their RGB specifications. + [colorConfig.extraNames, colorConfig.extraSpecs] = ... + dealColorDefinitions(extraColors); +end +% ============================================================================== +function [m2t, fid, fileWasOpen] = openFileForOutput(m2t) + % opens the output file and/or show a dialog to select one + if ~isempty(m2t.args.filehandle) + fid = m2t.args.filehandle; + fileWasOpen = true; + if ~isempty(m2t.args.filename) + userWarning(m2t, ... + 'File handle AND file name for output given. File handle used, file name discarded.') + end + m2t.tikzFileName = fopen(fid); + else + fid = []; + fileWasOpen = false; + % set filename + if ~isempty(m2t.args.filename) + filename = m2t.args.filename; + else + [filename, pathname] = uiputfile({'*.tex;*.tikz'; '*.*'}, 'Save File'); + filename = fullfile(pathname, filename); + end + m2t.tikzFileName = filename; + end + +end +% ============================================================================== +function l = filenameValidation(x, p) + % is the filename argument NOT another keyword? + l = ischar(x) && ~any(strcmp(x,p.Parameters)); %FIXME: See #471 +end +% ============================================================================== +function l = filehandleValidation(x) + % is the filehandle the handle to an opened file? + l = isnumeric(x) && any(x==fopen('all')); +end +% ============================================================================== +function bool = isCellOrChar(x) + bool = iscell(x) || ischar(x); +end +% ============================================================================== +function bool = isRGBTuple(color) + % Returns true when the color is a valid RGB tuple + bool = numel(color) == 3 && ... + all(isreal(color)) && ... + all( 0<=color & color<=1 ); % this also disallows NaN entries +end +% ============================================================================== +function bool = isColorDefinitions(colors) + % Returns true when the input is a cell array of color definitions, i.e. + % a cell array with in each cell a cell of the form {'name', [R G B]} + isValidEntry = @(e)( iscell(e) && ischar(e{1}) && isRGBTuple(e{2}) ); + + bool = iscell(colors) && all(cellfun(isValidEntry, colors)); +end +% ============================================================================== +function bool = isValidSemanticLineWidthDefinition(defMat) + % Returns true when the input is a cell array of shape Nx2 and + % contents in each column a set of string and numerical value as needed + % for the semanticLineWidth option. + bool = iscell(defMat) && size(defMat, 2) == 2; % Nx2 cell array + bool = bool && all(cellfun(@ischar , defMat(:,1))); % first column: names + bool = bool && all(cellfun(@isnumeric, defMat(:,2))); % second column: line width in points + + % alternatively: just 1 NaN to remove the defaults + bool = bool || (numel(defMat)==1 && isnan(defMat)); +end +% ============================================================================== +function fid = fileOpenForWrite(m2t, filename) + % Set the encoding of the output file. + % Currently only MATLAB supports different encodings. + fid = -1; + + [filepath] = fileparts(filename); + if ~exist(filepath,'dir') && ~isempty(filepath) + mkdir(filepath); + end + + switch getEnvironment() + case 'MATLAB' + fid = fopen(filename, 'w', ... + 'native', m2t.args.encoding); + case 'Octave' + fid = fopen(filename, 'w'); + otherwise + errorUnknownEnvironment(); + end + + if fid == -1 + error('matlab2tikz:fileOpenError', ... + 'Unable to open file ''%s'' for writing.', filename); + end +end +% ============================================================================== +function path = TeXpath(path) + path = strrep(path, filesep, '/'); + % TeX uses '/' as a file separator (as UNIX). Windows, however, uses + % '\' which is not supported by TeX as a file separator +end +% ============================================================================== +function m2t = saveToFile(m2t, fid, fileWasOpen) + % Save the figure as TikZ to a file. All other routines are called from here. + + % get all axes handles + [m2t, axesHandles] = findPlotAxes(m2t, m2t.current.gcf); + + % Turn around the handles vector to make sure that plots that appeared + % first also appear first in the vector. This makes sure the z-order of + % superimposed axes is respected and is fundamental for plotyy. + axesHandles = axesHandles(end:-1:1); + + % Alternative Positioning of axes. + % Select relevant Axes and draw them. + [m2t, axesBoundingBox] = getRelevantAxes(m2t, axesHandles); + + m2t.axesBoundingBox = axesBoundingBox; + m2t.axes = {}; + for relevantAxesHandle = m2t.relevantAxesHandles(:)' + m2t = drawAxes(m2t, relevantAxesHandle); + end + + % Handle color bars. + for cbar = m2t.cbarHandles(:)' + m2t = handleColorbar(m2t, cbar); + end + + % Draw annotations + m2t = drawAnnotations(m2t); + + % Add all axes containers to the file contents. + for axesContainer = m2t.axes + m2t.content = addChildren(m2t.content, axesContainer); + end + + % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + % actually print the stuff + minimalPgfplotsVersion = formatPgfplotsVersion(m2t.pgfplotsVersion); + + m2t.content.comment = sprintf('This file was created by %s.\n', m2t.about.name); + + if m2t.args.showInfo + % disable this info if showInfo=false + m2t.content.comment = [m2t.content.comment, ... + sprintf(['\n',... + 'The latest updates can be retrieved from\n', ... + ' %s\n', ... + 'where you can also make suggestions and rate %s.\n'], ... + m2t.about.website, m2t.about.name ) ... + ]; + end + + userInfo(m2t, 'You will need pgfplots version %s or newer to compile the TikZ output.',... + minimalPgfplotsVersion); + + % Add custom comment. + if ~isempty(m2t.args.tikzFileComment) + m2t.content.comment = [m2t.content.comment, ... + sprintf('\n%s\n', m2t.args.tikzFileComment) + ]; + end + + m2t.content.name = 'tikzpicture'; + + % Add custom TikZ options if any given. + m2t.content.options = opts_append_userdefined(m2t.content.options, ... + m2t.args.extraTikzpictureOptions); + + m2t.content.colors = generateColorDefinitions(m2t.color); + + % Open file if was not open + if ~fileWasOpen + fid = fileOpenForWrite(m2t, m2t.tikzFileName); + finally_fclose_fid = onCleanup(@() fclose(fid)); + end + + % Finally print it to the file + fprintf(fid, '%s', texcomment(m2t.content.comment)); + addStandalone(m2t, fid, 'preamble'); + addCustomCode(fid, '', m2t.args.extraCode, ''); + addStandalone(m2t, fid, 'begin'); + + printAll(m2t, m2t.content, fid); % actual plotting happens here + + addCustomCode(fid, '\n', m2t.args.extraCodeAtEnd, ''); + + addStandalone(m2t, fid, 'end'); +end +% ============================================================================== +function addStandalone(m2t, fid, part) + % writes a part of a standalone LaTeX file definition + if m2t.args.standalone + switch part + case 'preamble' + fprintf(fid, '\\documentclass[tikz]{standalone}\n%s\n', m2t.preamble); + case 'begin' + fprintf(fid, '\\begin{document}\n'); + case 'end' + fprintf(fid, '\n\\end{document}'); + otherwise + error('m2t:unknownStandalonePart', ... + 'Unknown standalone part "%s"', part); + end + end +end +% ============================================================================== +function str = generateColorDefinitions(colorConfig) + % Output the color definitions to LaTeX + str = ''; + names = colorConfig.extraNames; + specs = colorConfig.extraSpecs; + ff = colorConfig.format; + + if ~isempty(names) + colorDef = cell(1, length(names)); + for k = 1:length(names) + % Append with '%' to avoid spacing woes in LaTeX + FORMAT = ['\\definecolor{%s}{rgb}{' ff ',' ff ',' ff '}%%\n']; + colorDef{k} = sprintf(FORMAT, names{k}, specs{k}); + end + str = m2tstrjoin([colorDef, sprintf('%%\n')], ''); + end +end +% ============================================================================== +function [m2t, axesHandles] = findPlotAxes(m2t, fh) + % find axes handles that are not legends/colorbars + % store detected legends and colorbars in 'm2t' + % fh figure handle + axesHandles = findall(fh, 'type', 'axes'); + + % Remove all legend handles, as they are treated separately. + if ~isempty(axesHandles) + % TODO fix for octave + tagKeyword = switchMatOct('Tag', 'tag'); + % Find all legend handles. This is MATLAB-only. + m2t.legendHandles = findall(fh, tagKeyword, 'legend'); + m2t.legendHandles = m2t.legendHandles(:)'; + idx = ~ismember(axesHandles, m2t.legendHandles); + axesHandles = axesHandles(idx); + end + + % Remove all colorbar handles, as they are treated separately. + if ~isempty(axesHandles) + colorbarKeyword = switchMatOct('Colorbar', 'colorbar'); + % Find all colorbar handles. This is MATLAB-only. + cbarHandles = findall(fh, tagKeyword, colorbarKeyword); + % Octave also finds text handles here; no idea why. Filter. + m2t.cbarHandles = []; + for h = cbarHandles(:)' + if any(strcmpi(get(h, 'Type'),{'axes','colorbar'})) + m2t.cbarHandles = [m2t.cbarHandles, h]; + end + end + m2t.cbarHandles = m2t.cbarHandles(:)'; + idx = ~ismember(axesHandles, m2t.cbarHandles); + axesHandles = axesHandles(idx); + else + m2t.cbarHandles = []; + end + + % Remove scribe layer holding annotations (MATLAB < R2014b) + m2t.scribeLayer = findall(axesHandles, 'Tag','scribeOverlay'); + idx = ~ismember(axesHandles, m2t.scribeLayer); + axesHandles = axesHandles(idx); +end +% ============================================================================== +function str = texcomment(str) + % format a (multiline) string as TeX comment + if ~isempty(str) + EOL = sprintf('\n'); + str = ['% ' strrep(str, EOL, [EOL '%']) EOL]; + end +end +% ============================================================================== +function addCustomCode(fid, before, code, after) + if ~isempty(code) + fprintf(fid, before); + if ischar(code) + code = {code}; + end + if iscellstr(code) + for str = code(:)' + fprintf(fid, '%s\n', str{1}); + end + else + error('matlab2tikz:saveToFile', 'Need str or cellstr.'); + end + fprintf(fid,after); + end +end +% ============================================================================== +function [m2t, pgfEnvironments] = handleAllChildren(m2t, h) + % Draw all children of a graphics object (if they need to be drawn). + % #COMPLEX: mainly a switch-case + str = ''; + children = allchild(h); + + % prepare cell array of pgfEnvironments + pgfEnvironments = cell(1, numel(children)); + envCounter = 1; + + % It's important that we go from back to front here, as this is + % how MATLAB does it, too. Significant for patch (contour) plots, + % and the order of plotting the colored patches. + for child = children(end:-1:1)' + + % Check if object has legend. Some composite objects need to determine + % their status at the root level. For detailed explanations check + % getLegendEntries(). + % TODO: could move this check into drawHggroup. Need to verify how + % hgtransform behaves though. (priority - LOW) + m2t = hasLegendEntry(m2t,child); + + switch char(get(child, 'Type')) + % 'axes' environments are treated separately. + + case 'line' + [m2t, str] = handleObject(m2t, child, @drawLine); + + case 'constantline' + [m2t, str] = handleObject(m2t, child, @drawConstantLine); + + case 'patch' + [m2t, str] = handleObject(m2t, child, @drawPatch); + + case 'image' + [m2t, str] = handleObject(m2t, child, @drawImage); + + case {'hggroup', 'matlab.graphics.primitive.Group', ... + 'scatter', 'bar', 'stair', 'stem' ,'errorbar', 'area', ... + 'quiver','contour'} + [m2t, str] = handleObject(m2t, child, @drawHggroup); + + case 'hgtransform' + % From http://www.mathworks.de/de/help/matlab/ref/hgtransformproperties.html: + % Matrix: 4-by-4 matrix + % Transformation matrix applied to hgtransform object and its + % children. The hgtransform object applies the transformation + % matrix to all its children. + % More information at http://www.mathworks.de/de/help/matlab/creating_plots/group-objects.html. + m2t.transform = get(child, 'Matrix'); + [m2t, str] = handleAllChildren(m2t, child); + m2t.transform = []; + + case 'surface' + [m2t, str] = handleObject(m2t, child, @drawSurface); + + case 'text' + [m2t, str] = handleObject(m2t, child, @drawVisibleText); + + case 'rectangle' + [m2t, str] = handleObject(m2t, child, @drawRectangle); + + case 'doubleendarrowshape' + [m2t, str] = handleObject(m2t, child, @drawArrow); + + case 'arrowshape' + [m2t, str] = handleObject(m2t, child, @drawArrow); + + case 'histogram' + [m2t, str] = handleObject(m2t, child, @drawHistogram); + + case guitypes() + % don't do anything for GUI objects and their children + [m2t, str] = handleObject(m2t, child, @drawNothing); + + case 'light' + % These objects are not supported and should not/cannot be + % supported by matlab2tikz or pgfplots. + [m2t, str] = handleObject(m2t, child, @drawNothing); + + case '' + % No children found for handle. (It has only a title and/or + % labels). Carrying on as if nothing happened + + otherwise + userWarning(m2t, 'matlab2tikz does not know how to handle object with type "%s"', get(child,'Type')); + % just try with the drawNothing handler (which does nothing) + % that at least allows a user to attach a custom handler from outside + [m2t, str] = handleObject(m2t, child, @drawNothing); + end + + % A composite object might nest handleAllChildren calls that can + % modify the m2t.currentHandleHasLegend value. Re-instate the + % legend status. For detailed explanations check getLegendEntries(). + m2t = hasLegendEntry(m2t,child); + [m2t, legendLabel, labelRef] = addPlotyyReference(m2t, child); + legendInfo = addLegendInformation(m2t, child); + % Add labelRef BEFORE next plot to preserve color order + str = join(m2t, {labelRef, str, legendLabel, legendInfo}, ''); + + % append the environment + pgfEnvironments{envCounter} = str; + envCounter = envCounter +1; + end +end +% ============================================================================== +function [m2t, label, labelRef] = addPlotyyReference(m2t, h) + % Create labelled references to legend entries of the main plotyy axis + + % This ensures we are either on the main or secondary axis + label = ''; + labelRef = ''; + if ~isAxisPlotyy(m2t.current.gca) + return + end + + % Get current label counter + + if hasPlotyyReference(m2t,h) + % Label the plot to later reference it. Only legend entries on the main + % plotyy axis will have a label + [m2t, labelNum] = incrementGlobalCounter(m2t, 'plotyylabel'); + label = sprintf('\\label{%s}\n\n', plotyyLabelName(labelNum)); + + elseif m2t.currentHandleHasLegend && ~isempty(m2t.axes{end}.PlotyyReferences) + % We are on the secondary axis. + + % We have produced a number of labels we can refer to so far. + % Also, here we have a number of references that are to be recorded. + % So, we make the last references (assuming the other ones have been + % realized already) + nReferences = numel(m2t.axes{end}.PlotyyReferences); + nLabels = m2t.count.plotyylabel; + + % This is the range of labels, corresponding to the references + labelRange = (nLabels-nReferences+1):nLabels; + + labelRef = cell(1, numel(labelRange)); + % Create labelled references to legend entries of the main axis + for iRef = 1:nReferences + ref = m2t.axes{end}.PlotyyReferences(iRef); + lString = getLegendString(m2t,ref); + labelRef{iRef} = sprintf('\\addlegendimage{/pgfplots/refstyle=%s}\n\\addlegendentry{%s}\n',... + plotyyLabelName(labelRange(iRef)), lString); + end + labelRef = join(m2t, labelRef, ''); + + % Clear plotyy references. Ensures that references are created only once + m2t.axes{end}.PlotyyReferences = []; + else + % Do nothing: it's gonna be a legend entry. + % Not a label nor a referenced entry from the main axis. + end +end +% ============================================================================== +function label = plotyyLabelName(num) + % creates a LaTeX label for a plotyy trace + label = sprintf('plotyyref:leg%d', num); +end +% ============================================================================== +function legendInfo = addLegendInformation(m2t, h) + % Add the actual legend string + + legendInfo = ''; + if ~m2t.currentHandleHasLegend + return + end + legendString = getLegendString(m2t,h); + + % We also need a legend alignment option to make multiline + % legend entries work. This is added by default in getLegendOpts(). + legendInfo = sprintf('\\addlegendentry{%s}\n\n', legendString); +end +% ============================================================================== +function data = applyHgTransform(m2t, data) + if ~isempty(m2t.transform) + R = m2t.transform(1:3,1:3); + t = m2t.transform(1:3,4); + n = size(data, 1); + data = data * R' + kron(ones(n,1), t'); + end +end +% ============================================================================== +function m2t = drawAxes(m2t, handle) + % Input arguments: + % handle.................The axes environment handle. + + assertRegularAxes(handle); + + % Initialize empty environment. + % Use a struct instead of a custom subclass of hgsetget (which would + % facilitate writing clean code) as structs are more portable (old MATLAB(R) + % versions, GNU Octave). + m2t.axes{end+1} = struct('handle', handle, ... + 'name', '', ... + 'comment', [], ... + 'options', {opts_new()}, ... + 'content', {cell(0)}, ... + 'children', {cell(0)}); + + % update gca + m2t.current.gca = handle; + + % Check if axis is 3d + % In MATLAB, all plots are treated as 3D plots; it's just the view that + % makes 2D plots appear like 2D. + m2t.axes{end}.is3D = isAxis3D(handle); + + % Flag if axis contains barplot + m2t.axes{end}.barAddedAxisOption = false; + + % Get legend entries + m2t.axes{end}.LegendHandle = getAssociatedLegend(m2t, handle); + m2t.axes{end}.LegendEntries = getLegendEntries(m2t); + m2t = getPlotyyReferences(m2t, handle); + + m2t = retrievePositionOfAxes(m2t, handle); + + m2t = addAspectRatioOptionsOfAxes(m2t, handle); + + % Axis direction + for axis = 'xyz' + m2t.([axis 'AxisReversed']) = ... + strcmpi(get(handle,[upper(axis),'Dir']), 'reverse'); + end + % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + % Add color scaling + CLimMode = get(handle,'CLimMode'); + if strcmpi(CLimMode,'manual') || ~isempty(m2t.cbarHandles) + clim = caxis(handle); + m2t = m2t_addAxisOption(m2t, 'point meta min', sprintf(m2t.ff, clim(1))); + m2t = m2t_addAxisOption(m2t, 'point meta max', sprintf(m2t.ff, clim(2))); + end + % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + % Recurse into the children of this environment. + [m2t, childrenEnvs] = handleAllChildren(m2t, handle); + m2t.axes{end} = addChildren(m2t.axes{end}, childrenEnvs); + % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + % The rest of this is handling axes options. + % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + % Get other axis options (ticks, axis color, label,...). + % This is set here such that the axis orientation indicator in m2t is set + % before -- if ~isVisible(handle) -- the handle's children are called. + [m2t, xopts] = getAxisOptions(m2t, handle, 'x'); + [m2t, yopts] = getAxisOptions(m2t, handle, 'y'); + + m2t.axes{end}.options = opts_merge(m2t.axes{end}.options, xopts, yopts); + + m2t = add3DOptionsOfAxes(m2t, handle); + + if ~isVisible(handle) + % Setting hide{x,y} axis also hides the axis labels in Pgfplots whereas + % in MATLAB, they may still be visible. Instead use the following. + m2t = m2t_addAxisOption(m2t, 'axis line style', '{draw=none}'); + m2t = m2t_addAxisOption(m2t, 'ticks', 'none'); + % % An invisible axes container *can* have visible children, so don't + % % immediately bail out here. + % children = allchild(handle); + % for child = children(:)' + % if isVisible(child) + % % If the axes contain something that's visible, add an invisible + % % axes pair. + % m2t.axes{end}.name = 'axis'; + % m2t.axes{end}.options = {m2t.axes{end}.options{:}, ... + % 'hide x axis', 'hide y axis'}; + % NOTE: getTag was removed in 76d260d12e615602653d6f7b357393242b2430b3 + % m2t.axes{end}.comment = getTag(handle); + % break; + % end + % end + % % recurse into the children of this environment + % [m2t, childrenEnvs] = handleAllChildren(m2t, handle); + % m2t.axes{end} = addChildren(m2t.axes{end}, childrenEnvs); + % return + end + m2t.axes{end}.name = 'axis'; + + m2t = drawBackgroundOfAxes(m2t, handle); + m2t = drawTitleOfAxes(m2t, handle); + m2t = drawBoxAndLineLocationsOfAxes(m2t, handle); + m2t = drawGridOfAxes(m2t, handle); + m2t = drawLegendOptionsOfAxes(m2t); + + m2t.axes{end}.options = opts_append_userdefined(m2t.axes{end}.options, ... + m2t.args.extraAxisOptions); +end +% ============================================================================== +function m2t = drawGridOfAxes(m2t, handle) + % Draws the grids of an axis + options = opts_new(); + + % Check for major/minor grids + hasGrid = [isOn(get(handle, 'XGrid')); + isOn(get(handle, 'YGrid')); + isOn(get(handle, 'ZGrid')) && isAxis3D(handle)]; + + hasMinorGrid = [isOn(get(handle, 'XMinorGrid')); + isOn(get(handle, 'YMinorGrid')); + isOn(get(handle, 'ZMinorGrid')) && isAxis3D(handle)]; + + xyz = {'x', 'y', 'z'}; + + % Check for local grid options + % NOTE: for individual axis color options see the pfgmanual under + % major x grid style + for i=1:3 + if hasGrid(i) + grid = [xyz{i}, 'majorgrids']; + options = opts_add(options, grid); + end + if hasMinorGrid(i) + grid = [xyz{i}, 'minorgrids']; + options = opts_add(options, grid); + end + end + + % Check for global grid options + if any(hasGrid) + gridOpts = opts_new(); + % Get the line style and translate it to pgfplots + [gridLS, isDefault] = getAndCheckDefault(... + 'axes', handle, 'GridLineStyle', ':'); + if ~isDefault || m2t.args.strict + gridOpts = opts_add(gridOpts, translateLineStyle(gridLS)); + end + + % Get the color of the grid and translate it to pgfplots usable + % values + [gridColor, defaultColor] = getAndCheckDefault(... + 'axes', handle, 'GridColor', [0.15, 0.15, 0.15]); + if ~defaultColor + [m2t, gridColor] = getColor(m2t, handle, gridColor, 'patch'); + gridOpts = opts_add(gridOpts, gridColor); + end + + % Get the alpha of the grid and translate it to pgfplots + [gridAlpha, defaultAlpha] = getAndCheckDefault(... + 'axes', handle, 'GridAlpha', 0.1); + if ~defaultAlpha + gridOpts = opts_add(gridOpts, 'opacity', num2str(gridAlpha)); + end + + if ~isempty(gridOpts) + options = opts_addSubOpts(options, 'grid style', gridOpts); + end + end + + if any(hasMinorGrid) + minorGridOpts = opts_new(); + % Get the line style and translate it to pgfplots + [minorGridLS, isDefault] = getAndCheckDefault(... + 'axes', handle, 'MinorGridLineStyle', ':'); + if ~isDefault || m2t.args.strict + minorGridOpts = opts_add(minorGridOpts, translateLineStyle(minorGridLS)); + end + + % Get the color of the grid and translate it to pgfplots usable + % values + [minorGridColor, defaultColor] = getAndCheckDefault(... + 'axes', handle, 'MinorGridColor', [0.1, 0.1, 0.1]); + if ~defaultColor + [m2t, minorGridColor] = getColor(m2t, handle, minorGridColor, 'patch'); + minorGridOpts = opts_add(minorGridOpts, minorGridColor); + end + + % Get the alpha of the grid and translate it to pgfplots + [minorGridAlpha, defaultAlpha] = getAndCheckDefault(... + 'axes', handle, 'MinorGridAlpha', 0.1); + if ~defaultAlpha + minorGridOpts = opts_add(minorGridOpts, 'opacity', num2str(minorGridAlpha)); + end + + if ~isempty(minorGridOpts) + options = opts_addSubOpts(options, 'minor grid style', minorGridOpts); + end + end + + if ~any(hasGrid) && ~any(hasMinorGrid) + % When specifying 'axis on top', the axes stay above all graphs (which is + % default MATLAB behavior), but so do the grids (which is not default + % behavior). + %TODO: use proper grid ordering + if m2t.args.strict + options = opts_add(options, 'axis on top'); + end + % FIXME: axis background, axis grid, main, axis ticks, axis lines, axis tick labels, axis descriptions, axis foreground + end + + m2t.axes{end}.options = opts_merge(m2t.axes{end}.options, options); +end +% ============================================================================== +function m2t = add3DOptionsOfAxes(m2t, handle) + % adds 3D specific options of an axes object + if isAxis3D(handle) + [m2t, zopts] = getAxisOptions(m2t, handle, 'z'); + m2t.axes{end}.options = opts_merge(m2t.axes{end}.options, zopts); + + VIEWFORMAT = ['{' m2t.ff '}{' m2t.ff '}']; + m2t = m2t_addAxisOption(m2t, 'view', sprintf(VIEWFORMAT, get(handle, 'View'))); + end +end +% ============================================================================== +function legendhandle = getAssociatedLegend(m2t, axisHandle) + % Get legend handle associated with current axis + + legendhandle = []; + [env, envVersion] = getEnvironment(); + switch env + case 'Octave' + % Make sure that m2t.legendHandles is a row vector. + for lhandle = m2t.legendHandles(:)' + if isVersionBelow(envVersion, [4,2,2]) % Octave commit 5865d2fef424 + lhandleProp{1}='UserData'; + lhandleProp{2}='handle'; + else + lhandleProp{1}='__appdata__'; + lhandleProp{2}='__axes_handle__'; + end + ud = get(lhandle, lhandleProp{1}); + % Empty if no legend and multiple handles if plotyy + if ~isempty(ud) && any(axisHandle == ud.(lhandleProp{2})) + legendhandle = lhandle; + break + end + end + case 'MATLAB' + if isprop(axisHandle, 'Legend') % R2017b behavior + legendhandle = axisHandle.Legend; + else + legendhandle = legend(axisHandle); + % this is the proper way for old versions of MATLAB + % in newer versions (see #1006), calling `legend` may + % create a legend in the figure where none existed before + end + end + + % NOTE: there is a BUG in HG1 and Octave. Setting the box off sets the + % legend visibility off too. We assume the legend is visible if it has + % a visible child. + isInvisibleHG2 = isHG2() && ~isVisible(legendhandle); + isInvisibleHG1orOctave = (~isHG2() || strcmpi(env,'Octave')) &&... + ~isVisibleContainer(legendhandle); + + % Do not return the handle if legend is invisible + if isInvisibleHG1orOctave || isInvisibleHG2; + legendhandle = []; + end +end +% ============================================================================== +function entries = getLegendEntries(m2t) + % Retrieve the handles of the objects that have a legend entry + + % Non-composite objects are straightforward, e.g. line, and have the + % legend entry at their same level, hence we return their handle. + % + % Hggroups behave differently depending on the environment and we might + % return the handle to the hgroot or to one of its children: + % 1) Matlab places the legend entry at the hgroot. + % + % Usually, the decision to place the legend is either unchanged from + % the first call to handleAllChildrena(axis) or delegated to a + % specialized drawing routine, e.g. drawContour(), if the group has to + % be drawn atomically. In this case, the legend entry stays with the + % hgroot. + % + % If the hggroup is a pure container like in a bodeplot, i.e. the + % `type` is not listed in drawHggroup(), a nested call to + % handleAllChildren(hgroot) follows. But, this second call cannot detect + % legend entries on the children. Hence, we pass down the legend entry + % from the hgroot to its first child. + % + % 2) Octave places the entry with one of the children of the hgroot. + % Hence, most of the hggroups are correctly dealt by a nested + % handleAllChildren() call which detects the entry on the child. + % However, when we can guess the type of hggroup with + % guessOctavePlotType(), the legend entry should be placed at the root + % level, hence we bubble it up from the child to the hgroot. + + entries = []; + legendHandle = m2t.axes{end}.LegendHandle; + + if isempty(legendHandle) + return + end + + switch getEnvironment() + case 'Octave' + if isappdata(legendHandle,'__peer_objects__') + % Octave version 6.x or newer with refactored legend + entries = getappdata(legendHandle,'__peer_objects__') + else + % See set(hlegend, "deletefcn", {@deletelegend2, ca, [], [], t1, hplots}); in legend.m + delfun = get(legendHandle,'deletefcn'); + entries = delfun{6}; + end + + % Bubble-up legend entry properties from child to hggroup root + % for guessable objects + for ii = 1:numel(entries) + child = entries(ii); + anc = ancestor(child,'hggroup'); + if isempty(anc) % not an hggroup + continue + end + cl = guessOctavePlotType(anc); + if ~strcmpi(cl, 'unknown') % guessable hggroup, then bubble-up + legendString = get(child,'displayname'); + set(anc,'displayname',legendString); + entries(ii) = anc; + end + end + + case 'MATLAB' + % Undocumented property (exists at least since 2008a) + entries = get(legendHandle,'PlotChildren'); + + % Take only the first child from a pure hggroup (e.g. bodeplots) + for ii = 1:numel(entries) + entry = entries(ii); + % Note that class() is not supported in Octave + isHggroupClass = strcmpi(class(handle(entry)),'hggroup'); + if isHggroupClass + children = get(entry, 'Children'); + firstChild = children(1); + if isnumeric(firstChild) + firstChild = handle(firstChild); + end + % Inherits DisplayName from hggroup root + set(firstChild, 'DisplayName', get(entry, 'DisplayName')); + entries(ii) = firstChild; + end + end + end +end +% ============================================================================== +function m2t = getPlotyyReferences(m2t,axisHandle) + % Retrieve references to legend entries of the main plotyy axis + % + % A plotyy plot has a main and a secondary axis. The legend is associated + % with the main axis and hence m2t will only include the legend entries + % that belong to the \axis[] that has a legend. + % + % One way to include the legend entries from the secondary axis (in the + % same legend) is to first label the \addplot[] and then reference them. + % See https://tex.stackexchange.com/questions/42697/42752#42752 + % + % However, in .tex labels should come before they are referenced. Hence, + % we actually label the legend entries from the main axis and swap the + % legendhandle to the secondary axis. + % + % The legend will not be plotted with the main \axis[] and the labelled + % legend entries will be skipped until the secondary axis. Then, they will + % be listed before any legend entry from the secondary axis. + + % Retrieve legend handle + if isAxisMain(axisHandle) + legendHandle = m2t.axes{end}.LegendHandle; + else + legendHandle = getAssociatedLegend(m2t,getPlotyyPeer(axisHandle)); + m2t.axes{end}.LegendHandle = legendHandle; + end + + % Not a plotyy axis or no legend + if ~isAxisPlotyy(axisHandle) || isempty(legendHandle) + m2t.axes{end}.PlotyyReferences = []; + + elseif isAxisMain(axisHandle) + % Mark legend entries of the main axis for labelling + legendEntries = m2t.axes{end}.LegendEntries; + ancAxes = ancestor(legendEntries,'axes'); + idx = ismember([ancAxes{:}], axisHandle); + m2t.axes{end}.PlotyyReferences = legendEntries(idx); + + % Ensure no legend is created on the main axis + m2t.axes{end}.LegendHandle = []; + else + % Get legend entries associated to secondary plotyy axis. We can do + % this because we took the legendhandle from the peer (main axis) + legendEntries = getLegendEntries(m2t); + ancAxes = ancestor(legendEntries,'axes'); + if iscell(ancAxes) + ancAxes = [ancAxes{:}]; + end + idx = ismember(double(ancAxes), axisHandle); + m2t.axes{end}.LegendEntries = legendEntries(idx); + + % Recover referenced legend entries of the main axis + m2t.axes{end}.PlotyyReferences = legendEntries(~idx); + end +end +% ============================================================================== +function bool = isAxisMain(h) + % Check if it is the main axis e.g. in a plotyy plot + + if ~isAxisPlotyy(h) + bool = true; + return % an axis not constructed by plotyy is always(?) a main axis + end + + % If it is a Plotyy axis + switch getEnvironment() + case 'Octave' + plotyyAxes = get(h, '__plotyy_axes__'); + bool = find(plotyyAxes == h) == 1; + + case 'MATLAB' + % LegendPeerHandle was renamed to LayoutPeers in 9.1 (R2016b) + if verLessThan('matlab', '9.1') + bool = ~isempty(getappdata(h, 'LegendPeerHandle')); + else + bool = ~isempty(getappdata(h, 'LayoutPeers')); + end + end +end +% ============================================================================== +function bool = isAxisPlotyy(h) + % Check if handle is a plotyy axis + + switch getEnvironment() + case 'Octave' + % Cannot test hidden property with isfield(), is always false + try + get(h, '__plotyy_axes__'); + bool = true; + catch + bool = false; + end + + case 'MATLAB' + bool = ~isempty(getappdata(h, 'graphicsPlotyyPeer')); + end +end +% ============================================================================== +function peer = getPlotyyPeer(axisHandle) + % Get the other axis coupled in plotyy plots + + switch getEnvironment() + case 'Octave' + plotyyAxes = get(axisHandle, '__plotyy_axes__'); + peer = setdiff(plotyyAxes, axisHandle); + + case 'MATLAB' + peer = getappdata(axisHandle, 'graphicsPlotyyPeer'); + end +end +% ============================================================================== +function legendString = getLegendString(m2t, h) + % Retrieve the legend string for the given handle + str = getOrDefault(h, 'displayname', ''); + interpreter = get(m2t.axes{end}.LegendHandle,'interpreter'); + + % HG1: autogenerated legend strings, i.e. data1,..., dataN, do not populate + % the 'displayname' property. Go through 'userdata' + if isempty(str) + ud = get(m2t.axes{end}.LegendHandle,'userdata'); + idx = ismember(ud.handles, h); + str = ud.lstrings{idx}; + end + + % split string to cell, if newline character '\n' (ASCII 10) is present + delimeter = sprintf('\n'); + str = regexp(str, delimeter, 'split'); + str = prettyPrint(m2t, str, interpreter); + legendString = join(m2t, str, '\\'); +end +% ============================================================================== +function [m2t, bool] = hasLegendEntry(m2t, h) + % Check if the handle has a legend entry and track its legend status in m2t + legendEntries = m2t.axes{end}.LegendEntries; + if isnumeric(h) + legendEntries = double(legendEntries); + end + + % Should not have a legend reference + bool = any(ismember(h, legendEntries)) && ~hasPlotyyReference(m2t,h); + m2t.currentHandleHasLegend = bool; +end +% ============================================================================== +function bool = hasPlotyyReference(m2t,h) + % Check if the handle has a legend reference + plotyyReferences = m2t.axes{end}.PlotyyReferences; + if isnumeric(h) + plotyyReferences = double(plotyyReferences); + end + + bool = any(ismember(h, plotyyReferences)); +end +% ============================================================================== +function m2t = retrievePositionOfAxes(m2t, handle) + % This retrieves the position of an axes and stores it into the m2t data + % structure + + pos = getAxesPosition(m2t, handle, m2t.args.width, ... + m2t.args.height, m2t.axesBoundingBox); + % set the width + if (~m2t.args.noSize) + % optionally prevents setting the width and height of the axis + m2t = setDimensionOfAxes(m2t, 'width', pos.w); + m2t = setDimensionOfAxes(m2t, 'height', pos.h); + + m2t = m2t_addAxisOption(m2t, 'at', ... + ['{(' formatDim(pos.x.value, pos.x.unit) ','... + formatDim(pos.y.value, pos.y.unit) ')}']); + % the following is general MATLAB behavior: + m2t = m2t_addAxisOption(m2t, 'scale only axis'); + end +end +% ============================================================================== +function m2t = setDimensionOfAxes(m2t, widthOrHeight, dimension) + % sets the dimension "name" of the current axes to the struct "dim" + m2t = m2t_addAxisOption(m2t, widthOrHeight, ... + formatDim(dimension.value, dimension.unit)); +end +% ============================================================================== +function m2t = addAspectRatioOptionsOfAxes(m2t, handle) + % Set manual aspect ratio for current axes + % TODO: deal with 'axis image', 'axis square', etc. (#540) + if strcmpi(get(handle, 'DataAspectRatioMode'), 'manual') ||... + strcmpi(get(handle, 'PlotBoxAspectRatioMode'), 'manual') + % we need to set the plot box aspect ratio + if m2t.axes{end}.is3D + % Note: set 'plot box ratio' for 3D axes to avoid bug with + % 'scale mode = uniformly' (see #560) + aspectRatio = getPlotBoxAspectRatio(handle); + m2t = m2t_addAxisOption(m2t, 'plot box ratio', ... + formatAspectRatio(m2t, aspectRatio)); + end + end +end +% ============================================================================== +function m2t = drawBackgroundOfAxes(m2t, handle) + % draw the background color of the current axes + backgroundColor = get(handle, 'Color'); + if ~isNone(backgroundColor) && isVisible(handle) + [m2t, col] = getColor(m2t, handle, backgroundColor, 'patch'); + m2t = m2t_addAxisOption(m2t, 'axis background/.style', sprintf('{fill=%s}', col)); + end +end +% ============================================================================== +function m2t = drawTitleOfAxes(m2t, handle) + % processes the title of an axes object + [m2t, m2t.axes{end}.options] = getTitle(m2t, handle, m2t.axes{end}.options); +end +% ============================================================================== +function [m2t, opts] = getTitle(m2t, handle, opts) + % gets the title and its markup from an axes/colorbar/... + [m2t, opts] = getTitleOrLabel_(m2t, handle, opts, 'Title'); +end +function [m2t, opts] = getLabel(m2t, handle, opts, tikzKeyword) + % gets the label and its markup from an axes/colorbar/... + [m2t, opts] = getTitleOrLabel_(m2t, handle, opts, 'Label', tikzKeyword); +end +function [m2t, opts] = getAxisLabel(m2t, handle, axis, opts) + % convert an {x,y,z} axis label to TikZ + labelName = [upper(axis) 'Label']; + [m2t, opts] = getTitleOrLabel_(m2t, handle, opts, labelName); +end +function [m2t, opts] = getTitleOrLabel_(m2t, handle, opts, labelKind, tikzKeyword) + % gets a string element from an object + if ~exist('tikzKeyword', 'var') || isempty(tikzKeyword) + tikzKeyword = lower(labelKind); + end + object = get(handle, labelKind); + + str = get(object, 'String'); + if ~isempty(str) + interpreter = get(object, 'Interpreter'); + str = prettyPrint(m2t, str, interpreter); + [m2t, style] = getFontStyle(m2t, object); + if length(str) > 1 %multiline + style = opts_add(style, 'align', 'center'); + end + if ~isempty(style) + opts = opts_addSubOpts(opts, [tikzKeyword ' style'], style); + end + str = join(m2t, str, '\\[1ex]'); + opts = opts_add(opts, tikzKeyword, sprintf('{%s}', str)); + end +end +% ============================================================================== +function m2t = drawBoxAndLineLocationsOfAxes(m2t, h) + % draw the box and axis line location of an axes object + isBoxOn = isOn(get(h, 'box')); + xLoc = get(h, 'XAxisLocation'); + yLoc = get(h, 'YAxisLocation'); + isXaxisBottom = strcmpi(xLoc, 'bottom'); + isYaxisLeft = strcmpi(yLoc, 'left'); + + if strcmpi(xLoc, 'origin') + xLoc = 'middle'; + end + if strcmpi(yLoc, 'origin') + yLoc = 'middle'; + end + + % Only flip the labels to the other side if not at the default + % left/bottom positions + if isBoxOn + if ~isXaxisBottom + m2t = m2t_addAxisOption(m2t, 'xticklabel pos','right'); + end + if ~isYaxisLeft + m2t = m2t_addAxisOption(m2t, 'yticklabel pos','right'); + end + + % Position axes lines (strips the box) + else + m2t = m2t_addAxisOption(m2t, 'axis x line*', xLoc); + m2t = m2t_addAxisOption(m2t, 'axis y line*', yLoc); + if m2t.axes{end}.is3D + % There's no such attribute as 'ZAxisLocation'. + % Instead, the default seems to be 'left'. + m2t = m2t_addAxisOption(m2t, 'axis z line*', 'left'); + end + end +end +% ============================================================================== +function m2t = drawLegendOptionsOfAxes(m2t) + legendHandle = m2t.axes{end}.LegendHandle; + if isempty(legendHandle) + return + end + + [m2t, key, legendOpts] = getLegendOpts(m2t, legendHandle); + m2t = m2t_addAxisOption(m2t, key, legendOpts); +end +% ============================================================================== +function m2t = handleColorbar(m2t, handle) + if isempty(handle) + return; + end + + % Find the axes environment that this colorbar belongs to. + parentAxesHandle = double(get(handle,'axes')); + parentFound = false; + for k = 1:length(m2t.axes) + if m2t.axes{k}.handle == parentAxesHandle + k0 = k; + parentFound = true; + break; + end + end + if parentFound + m2t.axes{k0}.options = opts_append(m2t.axes{k0}.options, ... + matlab2pgfplotsColormap(m2t, m2t.current.colormap), []); + % Append cell string. + m2t.axes{k0}.options = cat(1, m2t.axes{k0}.options, ... + getColorbarOptions(m2t, handle)); + else + warning('matlab2tikz:parentAxesOfColorBarNotFound',... + 'Could not find parent axes for color bar. Skipping.'); + end +end +% ============================================================================== +function [m2t, options] = getAxisOptions(m2t, handle, axis) + assertValidAxisSpecifier(axis); + + options = opts_new(); + % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + % axis colors + [color, isDfltColor] = getAndCheckDefault('Axes', handle, ... + [upper(axis),'Color'], [ 0 0 0 ]); + if ~isDfltColor || m2t.args.strict + [m2t, col] = getColor(m2t, handle, color, 'patch'); + if isOn(get(handle, 'box')) + % If the axes are arranged as a box, make sure that the individual + % axes are drawn as four separate paths. This makes the alignment + % at the box corners somewhat less nice, but allows for different + % axis styles (e.g., colors). + options = opts_add(options, 'separate axis lines'); + end + % set color of axis lines + options = ... + opts_add(options, ... + ['every outer ', axis, ' axis line/.append style'], ... + ['{', col, '}']); + % set color of tick labels + options = ... + opts_add(options, ... + ['every ',axis,' tick label/.append style'], ... + ['{font=\color{',col,'}}']); + % set color of ticks + options = ... + opts_add(options, ... + ['every ',axis,' tick/.append style'], ... + ['{',col,'}']); + end + % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + % handle the orientation + isAxisReversed = strcmpi(get(handle,[upper(axis),'Dir']), 'reverse'); + m2t.([axis 'AxisReversed']) = isAxisReversed; + if isAxisReversed + options = opts_add(options, [axis, ' dir'], 'reverse'); + end + % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + axisScale = getOrDefault(handle, [upper(axis) 'Scale'], 'lin'); + if strcmpi(axisScale, 'log'); + options = opts_add(options, [axis,'mode'], 'log'); + end + % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + % get axis limits + [m2t, options] = setAxisLimits(m2t, handle, axis, options); + % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + % get ticks along with the labels + [options] = getAxisTicks(m2t, handle, axis, options); + % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + % get axis label + [m2t, options] = getAxisLabel(m2t, handle, axis, options); +end +% ============================================================================== +function [options] = getAxisTicks(m2t, handle, axis, options) + % Return axis tick marks Pgfplots style. Nice: Tick lengths and such + % details are taken care of by Pgfplots. + assertValidAxisSpecifier(axis); + + keywordTickMode = [upper(axis), 'TickMode']; + tickMode = get(handle, keywordTickMode); + keywordTick = [upper(axis), 'Tick']; + ticks = get(handle, keywordTick); + + % hidden properties are not caught by hasProperties + isDatetimeTicks = isAxisTicksDateTime(handle, axis); + if isDatetimeTicks + ticks = datenum(ticks); + end + + if isempty(ticks) + % If no ticks are present, we need to enforce this in any case. + pgfTicks = '\empty'; + elseif strcmpi(tickMode, 'auto') && ~m2t.args.strict && ~isDatetimeTicks + % Let pgfplots decide if the tickmode is auto or conversion is not + % strict and we are not dealing with datetime ticks + pgfTicks = []; + else % strcmpi(tickMode,'manual') || m2t.args.strict + pgfTicks = join(m2t, cellstr(num2str(ticks(:))), ', '); + end + + keywordTickLabelMode = [upper(axis), 'TickLabelMode']; + tickLabelMode = get(handle, keywordTickLabelMode); + if strcmpi(tickLabelMode, 'auto') && ~m2t.args.strict && ~isDatetimeTicks + pgfTickLabels = []; + else % strcmpi(tickLabelMode,'manual') || m2t.args.strict + % HG2 allows to set 'TickLabelInterpreter'. + % HG1 tacitly uses the interpreter 'none'. + % See http://www.mathworks.com/matlabcentral/answers/102053#comment_300079 + fallback = defaultTickLabelInterpreter(m2t); + interpreter = getOrDefault(handle, 'TickLabelInterpreter', fallback); + keywordTickLabel = [upper(axis), 'TickLabel']; + tickLabels = cellstr(get(handle, keywordTickLabel)); + tickLabels = prettyPrint(m2t, tickLabels, interpreter); + + keywordScale = [upper(axis), 'Scale']; + isAxisLog = strcmpi(getOrDefault(handle,keywordScale, 'lin'), 'log'); + [pgfTicks, pgfTickLabels] = ... + matlabTicks2pgfplotsTicks(m2t, ticks, tickLabels, isAxisLog, tickLabelMode); + end + + keywordMinorTick = [upper(axis), 'MinorTick']; + hasMinorTicks = isOn(getOrDefault(handle, keywordMinorTick, 'off')); + tickDirection = getOrDefault(handle, 'TickDir', 'in'); + + options = setAxisTicks(m2t, options, axis, pgfTicks, pgfTickLabels, ... + hasMinorTicks, tickDirection, isDatetimeTicks); + + options = setAxisTickLabelStyle(options, axis, handle); +end +% ============================================================================== +function options = setAxisTickLabelStyle(options, axis, handle) + % determine the style of tick labels + %TODO: translate the style of tick labels fully (font?, weight, ...) + kwRotation = [upper(axis), 'TickLabelRotation']; + rotation = getOrDefault(handle, kwRotation, 0); + if rotation ~= 0 + options = opts_add(options, [axis, 'ticklabel style'], ... + sprintf('{rotate=%d}', rotation)); + end +end +% ============================================================================== +function interpreter = defaultTickLabelInterpreter(m2t) + % determines the default tick label interpreter + % This is only relevant in HG1/Octave. In HG2, we use the interpreter + % set in the object (not the global default). + if m2t.args.interpretTickLabelsAsTex + interpreter = 'tex'; + else + interpreter = 'none'; + end +end +% ============================================================================== +function isDatetimeTicks = isAxisTicksDateTime(handle, axis) + % returns true when the axis has DateTime ticks + + % If R2016b or above + if isa(get(handle, [upper(axis), 'Tick']), 'datetime') + isDatetimeTicks = true; + else + try + % Get hidden properties of the datetime axes manager + dtsManager = get(handle, 'DatetimeDurationPlotAxesListenersManager'); + oldState = warning('off','MATLAB:structOnObject'); + dtsManager = struct(dtsManager); + warning(oldState); + + isDatetimeTicks = dtsManager.([upper(axis) 'DateTicks']) == 1; + catch + isDatetimeTicks = false; + end + end +end +% ============================================================================== +function options = setAxisTicks(m2t, options, axis, ticks, tickLabels,hasMinorTicks, tickDir,isDatetimeTicks) + % set ticks options + + % According to http://www.mathworks.com/help/techdoc/ref/axes_props.html, + % the number of minor ticks is automatically determined by MATLAB(R) to + % fit the size of the axis. Until we know how to extract this number, use + % a reasonable default. + matlabDefaultNumMinorTicks = 3; + if ~isempty(ticks) + options = opts_add(options, [axis,'tick'], sprintf('{%s}', ticks)); + end + if ~isempty(tickLabels) + options = opts_add(options, ... + [axis,'ticklabels'], sprintf('{%s}', tickLabels)); + end + if hasMinorTicks + options = opts_add(options, [axis,'minorticks'], 'true'); + if m2t.args.strict + options = opts_add(options, ... + sprintf('minor %s tick num', axis), ... + sprintf('{%d}', matlabDefaultNumMinorTicks)); + end + end + + if strcmpi(tickDir,'out') + options = opts_add(options, 'tick align', 'outside'); + elseif strcmpi(tickDir,'both') + options = opts_add(options, 'tick align', 'center'); + end + + if isDatetimeTicks + options = opts_add(options, ['scaled ' axis ' ticks'], 'false'); + end +end +% ============================================================================== +function assertValidAxisSpecifier(axis) + % assert that axis is a valid axis specifier + if ~ismember(axis, {'x','y','z'}) + error('matlab2tikz:illegalAxisSpecifier', ... + 'Illegal axis specifier "%s".', axis); + end +end +% ============================================================================== +function assertRegularAxes(handle) + % assert that the (axes) object specified by handle is a regular axes and not a + % colorbar or a legend + tag = lower(get(handle,'Tag')); + if ismember(tag,{'colorbar','legend'}) + error('matlab2tikz:notARegularAxes', ... + ['The object "%s" is not a regular axes object. ' ... + 'It cannot be handled with drawAxes!'], handle); + end +end +% ============================================================================== +function [m2t, options] = setAxisLimits(m2t, handle, axis, options) + % set the upper/lower limit of an axis (but not for categorical values) + limits = get(handle, [upper(axis),'Lim']); + if isa(limits,'datetime') + limits = datenum(limits); + end + if ~isCategoricalType(limits(1)) && isfinite(limits(1)) + options = opts_add(options, [axis,'min'], sprintf(m2t.ff, limits(1))); + end + if ~isCategoricalType(limits(2)) && isfinite(limits(2)) + options = opts_add(options, [axis,'max'], sprintf(m2t.ff, limits(2))); + end + + % Take care of categorical variables + if isCategoricalType(limits(1)) + + % Categorical variables do not need to specify limits, but symbolic values + categories = get(get(handle, [upper(axis),'Axis']), 'Categories'); + symb_coords = sprintf('{%s}', join(m2t, cellstr(string(categories)), ',')); + m2t = m2t_addAxisOption(m2t, ['symbolic ',axis,' coords'], symb_coords); + + % Some extra space is needed, otherwise the bars will touch the boundary + options = opts_add(options, ['enlarge ',axis,' limits'], '0.2'); + + % Ticks only for data values, prevents randomly repeated labels + options = opts_add(options, [axis,'tick'], 'data'); + end +end +% ============================================================================== +function bool = isVisibleContainer(axisHandle) + if ~isVisible(axisHandle) + % An invisible axes container *can* have visible children, so don't + % immediately bail out here. Also it *can* have a visible title, + % labels or children + + bool = false; + for prop = {'Children', 'Title', 'XLabel', 'YLabel', 'ZLabel'} + property = prop{1}; + if strcmpi(property, 'Children') + children = allchild(axisHandle); + elseif isprop(axisHandle, property) + children = get(axisHandle, property); + else + continue; % don't check non-existent properties + end + for child = children(:)' + if isVisible(child) + bool = true; + return; + end + end + end + else + bool = true; + end +end +% ============================================================================== +function [m2t, str] = drawLine(m2t, h, custom) + % Returns the code for drawing a regular line and error bars. + % This is an extremely common operation and takes place in most of the + % not too fancy plots. + str = ''; + if ~isLineVisible(h) + return; % there is nothing to plot + end + + % Color + color = get(h, 'Color'); + [m2t, xcolor] = getColor(m2t, h, color, 'patch'); + % Line and marker options + [m2t, lineOptions] = getLineOptions(m2t, h); + [m2t, markerOptions] = getMarkerOptions(m2t, h); + + % Only marks for legend consistency + hasNoLine = opts_has(lineOptions, 'draw') && isNone(opts_get(lineOptions, 'draw')); + if ~isempty(markerOptions) && hasNoLine + lineOptions = opts_remove(lineOptions, 'draw'); + lineOptions = opts_add(lineOptions, 'only marks', []); + end + + drawOptions = opts_new(); + drawOptions = opts_add(drawOptions, 'color', xcolor); + drawOptions = opts_merge(drawOptions, lineOptions, markerOptions); + drawOptions = opts_append_userdefined(drawOptions, custom.extraOptions); + + % Check for "special" lines, e.g.: + if strcmpi(get(h, 'Tag'), 'zplane_unitcircle') + [m2t, str] = specialDrawZplaneUnitCircle(m2t, drawOptions); + return + end + + % build the data matrix + data = getXYZDataFromLine(m2t, h); + xDeviation = getXDeviations(h); + yDeviation = getYDeviations(h); + + errDir = ''; + if ~isempty(xDeviation) + data = [data, xDeviation]; + errDir = [errDir, 'x']; + end + if ~isempty(yDeviation) + data = [data, yDeviation]; + errDir = [errDir, 'y']; + end + + % Check if any value is infinite/NaN. In that case, add appropriate option. + m2t = jumpAtUnboundCoords(m2t, data); + + [m2t, dataString] = writePlotData(m2t, data, drawOptions, errDir); + [m2t, labelString] = addLabel(m2t, h); + + str = [dataString, labelString]; +end +% ============================================================================== +function [m2t, str] = specialDrawZplaneUnitCircle(m2t, drawOptions) + % Draw unit circle and axes. + + % TODO Don't hardcode "10", but extract from parent axes of |h| + opts = opts_print(drawOptions); + str = [sprintf('\\draw[%s] (axis cs:0,0) circle[radius=1];\n', opts), ... + sprintf('\\draw[%s] (axis cs:-10,0)--(axis cs:10,0);\n', opts), ... + sprintf('\\draw[%s] (axis cs:0,-10)--(axis cs:0,10);\n', opts)]; +end +% ============================================================================== +function bool = isLineVisible(h) + % check if a line object is actually visible (has markers and so on) + + lineStyle = get(h, 'LineStyle'); + lineWidth = get(h, 'LineWidth'); + marker = getOrDefault(h, 'Marker','none'); + hasLines = ~isNone(lineStyle) && lineWidth > 0; + hasMarkers = ~isNone(marker); + hasDeviations = ~isempty(getYDeviations(h)); + + bool = isVisible(h) && (hasLines || hasMarkers || hasDeviations); +end +% ============================================================================== +function [m2t, str] = writePlotData(m2t, data, drawOptions, errDir) + % actually writes the plot data to file + str = ''; + + is3D = m2t.axes{end}.is3D; + if is3D + % Don't try to be smart in parametric 3d plots: Just plot all the data. + [m2t, table, tableOptions] = makeTable(m2t, {'','',''}, data); + + % Print out + drawOpts = opts_print(drawOptions); + tabOpts = opts_print(tableOptions); + str = sprintf('\\addplot3 [%s]\n table[%s] {%s};\n ', ... + drawOpts, tabOpts, table); + else + % split the data into logical chunks + dataCell = splitLine(m2t, data); + + % plot them + strPart = cell(1, length(dataCell)); + for k = 1:length(dataCell) + %If drawOptions has error bar style options, then print these + %options on the variable errorBarOpts and remove them from + %drawOptions + if (opts_has(drawOptions,'error bar style')) + tmpOptions = opts_new(); + tmpOptions = opts_add(tmpOptions,'error bars/.cd',''); + % add x direction error bar style if necessary + if contains(errDir, 'x') + tmpOptions = opts_add(tmpOptions,'x dir','both'); + tmpOptions = opts_add(tmpOptions,'x explicit',''); + end + % add y direction error bar style if necessary + if contains(errDir, 'y') + tmpOptions = opts_add(tmpOptions,'y dir','both'); + tmpOptions = opts_add(tmpOptions,'y explicit',''); + end + tmpOptions = opts_copy(drawOptions, 'error bar style', tmpOptions); + tmpOptions = opts_copy(drawOptions, 'error mark options', tmpOptions); + + errorBarOpts = opts_print(tmpOptions); + + drawOptions = opts_remove(drawOptions, 'error bar style', ... + 'error mark options'); + + else + errorBarOpts = ''; + end + + % If the line has a legend string, make sure to only include a legend + % entry for the *last* occurrence of the plot series. + % Hence the condition k m2t.tol) + lineOpts = opts_add(lineOpts, translateLineStyle(lineStyle)); + end + + % Take over the line width in any case when in strict mode. If not, don't add + % anything in case of default line width and effectively take Pgfplots' + % default. + % Also apply the line width if no actual line is there; the markers make use + % of this, too. + matlabDefaultLineWidth = 0.5; + if ~isempty(m2t.semantic.LineWidth) + if ismember(lineWidth, [m2t.semantic.LineWidth{:,2}]) + semStrID = lineWidth == [m2t.semantic.LineWidth{:,2}]; + lineOpts = opts_add(lineOpts, m2t.semantic.LineWidth{semStrID,1}); + else + warning('matlab2tikz:semanticLineWidthNotFound',... + ['No semantic correspondance for lineWidth of ''%f'' found.'... + 'Falling back to explicit export in points.'], lineWidth); + lineOpts = opts_add(lineOpts, 'line width', sprintf('%.1fpt', lineWidth)); + end + elseif m2t.args.strict || ~abs(lineWidth-matlabDefaultLineWidth) <= m2t.tol + lineOpts = opts_add(lineOpts, 'line width', sprintf('%.1fpt', lineWidth)); + end + + % print no lines + if isNone(lineStyle) || lineWidth==0 + lineOpts = opts_add(lineOpts, 'draw', 'none'); + lineOpts = opts_add(lineOpts, 'only marks'); + end +end +% ============================================================================== +function list = configureSemanticLineWidths(semanticLineWidths) + % Defines the default semantic options of pgfplots and updates it when applicable + + if isnan(semanticLineWidths) + % Remove the list + list = {}; + return; + end + + % Pgf/TikZ defaults (see pgfmanual 3.0.1a section 15.3.1 / page 166) + list = {'ultra thin', 0.1; + 'very thin', 0.2; + 'thin', 0.4; + 'semithick', 0.6; + 'thick', 0.8; + 'very thick', 1.2; + 'ultra thick', 1.6 }; + + % Update defaults or append the user provided setting + for ii = 1:size(semanticLineWidths, 1) + % Check for redefinitions of defaults + [isOverride, idx] = ismember(semanticLineWidths{ii, 1}, list{:, 1}) + if isOverride + list{idx, 2} = semanticLineWidths{ii, 2}; + else + list{end+1} = semanticLineWidths{ii, :}; + end + end +end +% ============================================================================== +function [m2t, drawOptions] = getMarkerOptions(m2t, h) + % Handles the marker properties of a line (or any other) plot. + drawOptions = opts_new(); + + marker = getOrDefault(h, 'Marker', 'none'); + + if ~isNone(marker) + markerSize = get(h, 'MarkerSize'); + lineStyle = get(h, 'LineStyle'); + lineWidth = get(h, 'LineWidth'); + + [tikzMarkerSize, isDefault] = ... + translateMarkerSize(m2t, marker, markerSize); + + % take over the marker size in any case when in strict mode; + % if not, don't add anything in case of default marker size + % and effectively take Pgfplots' default. + if m2t.args.strict || ~isDefault + drawOptions = opts_add(drawOptions, 'mark size', ... + sprintf('%.1fpt', tikzMarkerSize)); + end + + markOptions = opts_new(); + % make sure that the markers get painted in solid (and not dashed) + % if the 'lineStyle' is not solid (otherwise there is no problem) + if ~strcmpi(lineStyle, 'solid') + markOptions = opts_add(markOptions, 'solid'); + end + + % get the marker color right + markerInfo = getMarkerInfo(m2t, h, markOptions); + + [m2t, markerInfo.options] = setColor(m2t, h, markerInfo.options, 'fill', markerInfo.FaceColor); + + if ~strcmpi(markerInfo.EdgeColor,'auto') + [m2t, markerInfo.options] = setColor(m2t, h, markerInfo.options, 'draw', markerInfo.EdgeColor); + else + if isprop(h,'EdgeColor') + color = get(h, 'EdgeColor'); + else + color = get(h, 'Color'); + end + [m2t, markerInfo.options] = setColor(m2t, h, markerInfo.options, '', color); + end + + % add it all to drawOptions + drawOptions = opts_add(drawOptions, 'mark', markerInfo.tikz); + + if ~isempty(markOptions) + drawOptions = opts_addSubOpts(drawOptions, 'mark options', ... + markerInfo.options); + end + end + + type = getOrDefault(h, 'Type', 'none'); + if strcmp(type, 'errorbar') + %'capSize' -> errorbar marker size + capSize = getOrDefault(h, 'CapSize',get(h,'MarkerSize')); + lineWidth = get(h, 'LineWidth'); + + errStyleOptions = opts_new(); + errStyleOptions = opts_add(errStyleOptions, 'line width',... + sprintf('%.1fpt', lineWidth)); + + drawOptions = opts_addSubOpts(drawOptions, ... + 'error bar style', errStyleOptions); + + errMarkOptions = opts_new(); + errMarkOptions = opts_add(errMarkOptions, 'line width',... + sprintf('%.1fpt', lineWidth)); + errMarkOptions = opts_add(errMarkOptions, 'mark size',... + sprintf('%.1fpt', capSize)); + errMarkOptions = opts_add(errMarkOptions, 'rotate', '90'); + + drawOptions = opts_addSubOpts(drawOptions, ... + 'error mark options', errMarkOptions); + end +end +% ============================================================================== +function [tikzMarkerSize, isDefault] = ... + translateMarkerSize(m2t, matlabMarker, matlabMarkerSize) + % The markersizes of Matlab and TikZ are related, but not equal. This + % is because + % + % 1.) MATLAB uses the MarkerSize property to describe something like + % the diameter of the mark, while TikZ refers to the 'radius', + % 2.) MATLAB and TikZ take different measures (e.g. the + % edge of a square vs. its diagonal). + if(~ischar(matlabMarker)) + error('matlab2tikz:translateMarkerSize', ... + 'Variable matlabMarker is not a string.'); + end + + if(~isnumeric(matlabMarkerSize)) + error('matlab2tikz:translateMarkerSize', ... + 'Variable matlabMarkerSize is not a numeral.'); + end + + % 6pt is the default MATLAB marker size for all markers + defaultMatlabMarkerSize = 6; + isDefault = abs(matlabMarkerSize(1)-defaultMatlabMarkerSize)'} + % for triangles, matlab takes the height + % and tikz the circumcircle radius; + % the triangles are always equiangular + tikzMarkerSize = matlabMarkerSize(:) / 2 * (2/3); + otherwise + error('matlab2tikz:translateMarkerSize', ... + 'Unknown matlabMarker ''%s''.', matlabMarker); + end +end +% ============================================================================== +function [tikzMarker, markOptions] = ... + translateMarker(m2t, matlabMarker, markOptions, faceColorToggle) + % Translates MATLAB markers to their Tikz equivalents + % #COMPLEX: inherently large switch-case + if ~ischar(matlabMarker) + error('matlab2tikz:translateMarker:MarkerNotAString',... + 'matlabMarker is not a string.'); + end + + switch (matlabMarker) + case 'none' + tikzMarker = ''; + case '+' + tikzMarker = '+'; + case 'o' + if faceColorToggle + tikzMarker = '*'; + else + tikzMarker = 'o'; + end + case '.' + tikzMarker = '*'; + case 'x' + tikzMarker = 'x'; + otherwise % the following markers are only available with PGF's + % plotmarks library + signalDependency(m2t, 'tikzlibrary', 'plotmarks'); + hasFilledVariant = true; + switch (matlabMarker) + + case '*' + tikzMarker = 'asterisk'; + hasFilledVariant = false; + + case {'s','square'} + tikzMarker = 'square'; + + case {'d','diamond'} + tikzMarker = 'diamond'; + + case '^' + tikzMarker = 'triangle'; + + case 'v' + tikzMarker = 'triangle'; + markOptions = opts_add(markOptions, 'rotate', '180'); + + case '<' + tikzMarker = 'triangle'; + markOptions = opts_add(markOptions, 'rotate', '90'); + + case '>' + tikzMarker = 'triangle'; + markOptions = opts_add(markOptions, 'rotate', '270'); + + case {'p','pentagram'} + tikzMarker = 'star'; + + case {'h','hexagram'} + userWarning(m2t, 'MATLAB''s marker ''hexagram'' not available in TikZ. Replacing by ''star''.'); + tikzMarker = 'star'; + + otherwise + error('matlab2tikz:translateMarker:unknownMatlabMarker',... + 'Unknown matlabMarker ''%s''.',matlabMarker); + end + if faceColorToggle && hasFilledVariant + tikzMarker = [tikzMarker '*']; + end + end +end +% ============================================================================== +function [m2t, str] = drawConstantLine(m2t, h, custom) + % Draws a 'constantline' object such as those produced by 'yline()' + + interceptaxis = get(h, 'InterceptAxis'); + value = get(h, 'Value'); + xLim = get(h.Parent, 'XLim'); + yLim = get(h.Parent, 'Ylim'); + + % create line from object properties + switch interceptaxis + case 'x' + xdata = [value value]; + ydata = yLim; + case 'y' + xdata = xLim; + ydata = [value value]; + otherwise + warning('ConstantLine: invalid InterceptAxis. Ignoring...') + return + end + l = line(xdata, ydata); + + % Pass on color and line options + l.Color = h.Color; + l.LineStyle = h.LineStyle; + l.LineWidth = h.LineWidth; + + [m2t, str] = drawLine(m2t, l, custom); +end +% ============================================================================== +function [m2t, str] = drawPatch(m2t, handle, custom) + % Draws a 'patch' graphics object (as found in contourf plots, for example). + % + str = ''; + if ~isVisible(handle) + return + end + + % This is for a quirky workaround for stacked bar plots. + m2t.axes{end}.nonbarPlotsPresent = true; + + % Each row of the faces matrix represents a distinct patch + % NOTE: pgfplot uses zero-based indexing into vertices and interpolates + % counter-clockwise + Faces = get(handle,'Faces')-1; + Vertices = get(handle,'Vertices'); + + % 3D vs 2D + is3D = m2t.axes{end}.is3D; + if is3D + columnNames = {'x', 'y', 'z'}; + plotCmd = 'addplot3'; + Vertices = applyHgTransform(m2t, Vertices); + else + columnNames = {'x', 'y'}; + plotCmd = 'addplot'; + Vertices = Vertices(:,1:2); + end + + % Process fill, edge colors and shader + [m2t,patchOptions, s] = shaderOpts(m2t,handle,'patch'); + + % Return empty axes if no face or edge colors + if isNone(s.plotType) + return + end + + % ----------------------------------------------------------------------- + % gather the draw options + % Make sure that legends are shown in area mode. + drawOptions = opts_add(opts_new,'area legend'); + verticesTableOptions = opts_new(); + + % Marker options + [m2t, markerOptions] = getMarkerOptions(m2t, handle); + drawOptions = opts_merge(drawOptions, markerOptions); + + % Line options + [m2t, lineOptions] = getLineOptions(m2t, handle); + drawOptions = opts_merge(drawOptions, lineOptions); + + % If the line is not visible, set edgeColor to none. Otherwise pgfplots + % draws it by default + if ~isLineVisible(handle) + s.edgeColor = 'none'; + end + + % No patch: if one patch and single face/edge color + isFaceColorFlat = isempty(strfind(opts_get(patchOptions, 'shader'),'interp')); + if size(Faces,1) == 1 && s.hasOneEdgeColor && isFaceColorFlat + ptType = ''; + cycle = conditionallyCyclePath(Vertices); + + [m2t, drawOptions] = setColor(m2t, handle, drawOptions, 'draw', ... + s.edgeColor, 'none'); + [m2t, drawOptions] = setColor(m2t, handle, drawOptions, 'fill', ... + s.faceColor); + + [drawOptions] = opts_copy(patchOptions, 'draw opacity', drawOptions); + [drawOptions] = opts_copy(patchOptions, 'fill opacity', drawOptions); + + else % Multiple patches + + % Patch table type + ptType = 'patch table'; + cycle = ''; + drawOptions = opts_add(drawOptions,'table/row sep','crcr'); + % TODO: is the above "crcr" compatible with pgfplots 1.12 ? + % TODO: is a "patch table" externalizable? + + % Enforce 'patch' or cannot use 'patch table=' + if strcmpi(s.plotType,'mesh') + drawOptions = opts_add(drawOptions,'patch'); + end + drawOptions = opts_add(drawOptions,s.plotType); % Eventually add mesh, but after patch! + + drawOptions = getPatchShape(m2t, handle, drawOptions, patchOptions); + + [m2t, drawOptions, Vertices, Faces, verticesTableOptions, ptType, ... + columnNames] = setColorsOfPatches(m2t, handle, drawOptions, ... + Vertices, Faces, verticesTableOptions, ptType, columnNames, ... + isFaceColorFlat, s); + end + + drawOptions = maybeShowInLegend(m2t.currentHandleHasLegend, drawOptions); + m2t = jumpAtUnboundCoords(m2t, Faces(:)); + + % Add Faces table + if ~isempty(ptType) + [m2t, facesTable] = makeTable(m2t, repmat({''},1,size(Faces,2)), Faces); + drawOptions = opts_add(drawOptions, ptType, sprintf('{%s}', facesTable)); + end + + % Plot the actual data. + [m2t, verticesTable, tableOptions] = makeTable(m2t, columnNames, Vertices); + tableOptions = opts_merge(tableOptions, verticesTableOptions); + + % Add custom options + drawOptions = opts_append_userdefined(drawOptions, custom.extraOptions); + + % Print out + drawOpts = opts_print(drawOptions); + tabOpts = opts_print(tableOptions); + str = sprintf('\n\\%s[%s]\ntable[%s] {%s}%s;\n',... + plotCmd, drawOpts, tabOpts, verticesTable, cycle); +end +% ============================================================================== +function [m2t, drawOptions, Vertices, Faces, verticesTableOptions, ptType, ... + columnNames] = setColorsOfPatches(m2t, handle, drawOptions, ... + Vertices, Faces, verticesTableOptions, ptType, columnNames, isFaceColorFlat, s) + % this behemoth does the color setting for patches + + % TODO: this function can probably be split further, just look at all those + % parameters being passed. + + fvCData = get(handle,'FaceVertexCData'); + rowsCData = size(fvCData,1); + + % We have CData for either all faces or vertices + if rowsCData > 1 + + % Add the color map + m2t = m2t_addAxisOption(m2t, matlab2pgfplotsColormap(m2t, m2t.current.colormap)); + + % Determine if mapping is direct or scaled + CDataMapping = get(handle,'CDataMapping'); + if strcmpi(CDataMapping, 'direct') + drawOptions = opts_add(drawOptions, 'colormap access','direct'); + end + + % Switch to face CData if not using interpolated shader + isVerticesCData = rowsCData == size(Vertices,1); + if isFaceColorFlat && isVerticesCData + % Take first vertex color (see FaceColor in Patch Properties) + fvCData = fvCData(Faces(:,1)+ 1,:); + rowsCData = size(fvCData,1); + isVerticesCData = false; + end + + % Point meta as true color CData, i.e. RGB in [0,1] + if size(fvCData,2) == 3 + % Create additional custom colormap + m2t.axes{end}.options(end+1,:) = ... + {matlab2pgfplotsColormap(m2t, fvCData, 'patchmap'), []}; + drawOptions = opts_append(drawOptions, 'colormap name','patchmap'); + + % Index into custom colormap + fvCData = (0:rowsCData-1)'; + end + + % Add pointmeta data to vertices or faces + if isVerticesCData + columnNames{end+1} = 'c'; + verticesTableOptions = opts_add(verticesTableOptions, 'point meta','\thisrow{c}'); + Vertices = [Vertices, fvCData]; + else + ptType = 'patch table with point meta'; + Faces = [Faces fvCData]; + end + + else + % Scalar FaceVertexCData, i.e. one color mapping for all patches, + % used e.g. by Octave in drawing barseries + + [m2t,xFaceColor] = getColor(m2t, handle, s.faceColor, 'patch'); + drawOptions = opts_add(drawOptions, 'fill', xFaceColor); + end +end +% ============================================================================== +function [drawOptions] = maybeShowInLegend(showInLegend, drawOptions) + % sets the appropriate options to show/hide the plot in the legend + if ~showInLegend + % No legend entry found. Don't include plot in legend. + drawOptions = opts_add(drawOptions, 'forget plot'); + end +end +% ============================================================================== +function [m2t, options] = setColor(m2t, handle, options, property, color, noneValue) + % assigns the MATLAB color of the object identified by "handle" to the LaTeX + % property stored in the options array. An optional "noneValue" can be provided + % that is set when the color == 'none' (if it is omitted, the property will not + % be set). + % TODO: probably this should be integrated with getAndCheckDefault etc. + if opts_has(options,property) && isNone(opts_get(options,property)) + return + end + if ~isNone(color) + [m2t, xcolor] = getColor(m2t, handle, color, 'patch'); + if ~isempty(xcolor) + % this may happen when color == 'flat' and CData is Nx3, e.g. in + % scatter plot or in patches + if isempty(property) + options = opts_add(options, xcolor); + else + options = opts_add(options, property, xcolor); + end + end + else + if exist('noneValue','var') + options = opts_add(options, property, noneValue); + end + end +end +% ============================================================================== +function drawOptions = getPatchShape(m2t, h, drawOptions, patchOptions) + % Retrieves the shape options (i.e. number of vertices) of patch objects + % Depending on the number of vertices, patches can be triangular, rectangular + % or polygonal + % See pgfplots 1.12 manual section 5.8.1 "Additional Patch Types" and the + % patchplots library + vertexCount = size(get(h, 'Faces'), 2); + + switch vertexCount + case 3 % triangle (default) + % do nothing special + + case 4 % rectangle + drawOptions = opts_add(drawOptions,'patch type', 'rectangle'); + + otherwise % generic polygon + userInfo(m2t, '\nMake sure to load \\usepgfplotslibrary{patchplots} in the preamble.\n'); + + % Default interpolated shader,not supported by polygon, to faceted + isFaceColorFlat = isempty(strfind(opts_get(patchOptions, 'shader'),'interp')); + if ~isFaceColorFlat + % NOTE: check if pgfplots supports this (or specify version) + userInfo(m2t, '\nPgfplots does not support interpolation for polygons.\n Use patches with at most 4 vertices.\n'); + patchOptions = opts_remove(patchOptions, 'shader'); + patchOptions = opts_add(patchOptions, 'shader', 'faceted'); + end + + % Add draw options + drawOptions = opts_add(drawOptions, 'patch type', 'polygon'); + drawOptions = opts_add(drawOptions, 'vertex count', ... + sprintf('%d', vertexCount)); + end + + drawOptions = opts_merge(drawOptions, patchOptions); +end +% ============================================================================== +function [cycle] = conditionallyCyclePath(data) + % returns "--cycle" when the path should be cyclic in pgfplots + % Mostly, this is the case UNLESS the data record starts or ends with a NaN + % record (i.e. a break in the path) + if any(~isfinite(data([1 end],:))) + cycle = ''; + else + cycle = '--cycle'; + end +end +% ============================================================================== +function m2t = jumpAtUnboundCoords(m2t, data) + % signals the axis to allow discontinuities in the plot at unbounded + % coordinates (i.e. Inf and NaN). + % See also pgfplots 1.12 manual section 4.5.13 "Interrupted Plots". + if any(~isfinite(data(:))) + m2t = needsPgfplotsVersion(m2t, [1 4]); + m2t = m2t_addAxisOption(m2t, 'unbounded coords', 'jump'); + end +end +% ============================================================================== +function [m2t, str] = drawImage(m2t, handle, custom) + str = ''; + if ~isVisible(handle) + return + end + + % read x-, y-, and color-data + xData = get(handle, 'XData'); + yData = get(handle, 'YData'); + cData = get(handle, 'CData'); + + if isempty(cData) + return + end + + if (m2t.args.imagesAsPng) + [m2t, str] = imageAsPNG(m2t, handle, xData, yData, cData, custom); + else + [m2t, str] = imageAsTikZ(m2t, handle, xData, yData, cData, custom); + end + + % Make sure that the axes are still visible above the image. + m2t = m2t_addAxisOption(m2t, 'axis on top'); +end +% ============================================================================== +function [m2t, str] = imageAsPNG(m2t, handle, xData, yData, cData, custom) + [m2t, fileNum] = incrementGlobalCounter(m2t, 'pngFile'); + % ------------------------------------------------------------------------ + % draw a png image + [pngFileName, pngReferencePath] = externalFilename(m2t, fileNum, '.png'); + + % Get color indices for indexed images and truecolor values otherwise + if ndims(cData) == 2 %#ok don't use ismatrix (cfr. #143) + [m2t, colorData] = cdata2colorindex(m2t, cData, handle); + else + colorData = cData; + end + + m = size(cData, 1); + n = size(cData, 2); + + alphaData = normalizedAlphaValues(m2t, get(handle,'AlphaData'), handle); + if numel(alphaData) == 1 + alphaData = alphaData(ones(size(colorData(:,:,1)))); + end + [colorData, alphaData] = flipImageIfAxesReversed(m2t, colorData, alphaData); + + % Write an indexed or a truecolor image + hasAlpha = true; + if isfloat(alphaData) && all(alphaData(:) == 1) + alphaOpts = {}; + hasAlpha = false; + else + alphaOpts = {'Alpha', double(alphaData)}; + end + if (ndims(colorData) == 2) %#ok don't use ismatrix (cfr. #143) + if size(m2t.current.colormap, 1) <= 256 && ~hasAlpha + % imwrite supports maximum 256 values in a colormap (i.e. 8 bit) + % and no alpha channel for indexed PNG images. + imwrite(colorData, m2t.current.colormap, ... + pngFileName, 'png'); + else % use true-color instead + imwrite(ind2rgb(colorData, m2t.current.colormap), ... + pngFileName, 'png', alphaOpts{:}); + end + else + imwrite(colorData, pngFileName, 'png', alphaOpts{:}); + end + % ----------------------------------------------------------------------- + % dimensions of a pixel in axes units + if n == 1 + xLim = get(m2t.current.gca, 'XLim'); + xw = xLim(2) - xLim(1); + else + xw = (xData(end)-xData(1)) / (n-1); + end + if m == 1 + yLim = get(m2t.current.gca, 'YLim'); + yw = yLim(2) - yLim(1); + else + yw = (yData(end)-yData(1)) / (m-1); + end + + opts = opts_new(); + opts = opts_add(opts, 'xmin', sprintf(m2t.ff, xData(1 ) - xw/2)); + opts = opts_add(opts, 'xmax', sprintf(m2t.ff, xData(end) + xw/2)); + opts = opts_add(opts, 'ymin', sprintf(m2t.ff, yData(1 ) - yw/2)); + opts = opts_add(opts, 'ymax', sprintf(m2t.ff, yData(end) + yw/2)); + + opts = opts_append_userdefined(opts, custom.extraOptions); + + % Print out + drawOpts = opts_print(opts); + str = sprintf('\\addplot [forget plot] graphics [%s] {%s};\n', ... + drawOpts, pngReferencePath); + + userInfo(m2t, ... + ['\nA PNG file is stored at ''%s'' for which\n', ... + 'the TikZ file contains a reference to ''%s''.\n', ... + 'You may need to adapt this, depending on the relative\n', ... + 'locations of the master TeX file and the included TikZ file.\n'], ... + pngFileName, pngReferencePath); +end +% ============================================================================== +function [m2t, str] = imageAsTikZ(m2t, handle, xData, yData, cData, custom) + % writes an image as raw TikZ commands (STRONGLY DISCOURAGED) + + % set up cData + if ndims(cData) == 3 + cData = cData(end:-1:1,:,:); + else + cData = cData(end:-1:1,:); + end + + % Generate uniformly distributed X, Y, although xData and yData may be + % non-uniform. + % This is MATLAB(R) behavior. + [X, hX] = constructUniformXYDataForImage(xData, size(cData, 2)); + [Y, hY] = constructUniformXYDataForImage(yData, size(cData, 1)); + [m2t, xcolor] = getColor(m2t, handle, cData, 'image'); + + % The following section takes pretty long to execute, although in + % principle it is discouraged to use TikZ for those; LaTeX will take + % forever to compile. + % Still, a bug has been filed on MathWorks to allow for one-line + % sprintf'ing with (string+num) cells (Request ID: 1-9WHK4W); + % . + % An alternative approach could be to use 'surf' or 'patch' of pgfplots + % with inline tables. + str = ''; + m = length(X); + n = length(Y); + imageString = cell(1, m); + for i = 1:m + subString = cell(1, n); + for j = 1:n + opts = opts_new(); + opts = opts_add(opts, xcolor{n-j+1, i}); + opts = opts_append_userdefined(opts, custom.extraOptions); + subString{j} = sprintf(['\t\\fill [%s] ', ... + '(axis cs:', m2t.ff, ',', m2t.ff, ') rectangle ', ... + '(axis cs:', m2t.ff, ',', m2t.ff, ');\n'], ... + opts_print(opts), ... + X(i)-hX/2, Y(j)-hY/2, ... + X(i)+hX/2, Y(j)+hY/2); + end + imageString{i} = join(m2t, subString, ''); + end + str = join(m2t, [str, imageString], ''); +end +function [XY, delta] = constructUniformXYDataForImage(XYData, expectedLength) + % Generate uniformly distributed X, Y, although xData/yData may be + % non-uniform. Dimension indicates the corresponding dimension in the cData matrix. + switch length(XYData) + case 2 % only the limits given; common for generic image plots + delta = 1; + case expectedLength % specific x/y-data is given + delta = (XYData(end)-XYData(1)) / (length(XYData)-1); + otherwise + error('drawImage:arrayLengthMismatch', ... + 'CData length (%d) does not match X/YData length (%d).', ... + expectedLength, length(XYData)); + end + XY = XYData(1):delta:XYData(end); +end +% ============================================================================== +function [colorData, alphaData] = flipImageIfAxesReversed(m2t, colorData, alphaData) + % flip the image if reversed + if m2t.xAxisReversed + colorData = colorData(:, end:-1:1, :); + alphaData = alphaData(:, end:-1:1); + end + if ~m2t.yAxisReversed % y-axis direction is reversed normally for images, flip otherwise + colorData = colorData(end:-1:1, :, :); + alphaData = alphaData(end:-1:1, :); + end +end +% ============================================================================== +function alpha = normalizedAlphaValues(m2t, alpha, handle) + alphaDataMapping = getOrDefault(handle, 'AlphaDataMapping', 'none'); + switch lower(alphaDataMapping) + case 'none' % no rescaling needed + case 'scaled' + ALim = get(m2t.current.gca, 'ALim'); + AMax = ALim(2); + AMin = ALim(1); + if ~isfinite(AMax) + AMax = max(alpha(:)); %NOTE: is this right? + end + alpha = (alpha - AMin)./(AMax - AMin); + case 'direct' + alpha = ind2rgb(alpha, get(m2t.current.gcf, 'Alphamap')); + otherwise + error('matlab2tikz:UnknownAlphaMapping', ... + 'Unknown alpha mapping "%s"', alphaMapping); + end + + if isfloat(alpha) %important, alpha data can have integer type which should not be scaled + alpha = min(1,max(alpha,0)); % clip at range [0, 1] + end +end +% ============================================================================== +function [m2t, str] = drawContour(m2t, h, custom) + if isHG2() + [m2t, str] = drawContourHG2(m2t, h, custom); + else + % Save legend state for the contour group + hasLegend = m2t.currentHandleHasLegend; + + % Plot children patches + children = allchild(h); + N = numel(children); + str = cell(N,1); + for ii = 1:N + % Plot in reverse order + child = children(N-ii+1); + isContourLabel = strcmpi(get(child,'type'),'text'); + if isContourLabel + [m2t, str{ii}] = handleObject(m2t, child, @drawText); + else + [m2t, str{ii}] = handleObject(m2t, child, @drawPatch); + end + + % Only first child can be in the legend + m2t.currentHandleHasLegend = false; + end + str = strcat(str,sprintf('\n')); + str = [str{:}]; + + % Restore group's legend state + m2t.currentHandleHasLegend = hasLegend; + end +end +% ============================================================================== +function [m2t, str] = drawContourHG2(m2t, h, custom) + str = ''; + if ~isVisible(h) + return + end + + % Retrieve ContourMatrix + contours = get(h,'ContourMatrix')'; + [istart, nrows] = findStartOfContourData(contours); + + % Scale negative contours one level down (for proper coloring) + Levels = contours(istart,1); + LevelList = get(h,'LevelList'); + ineg = Levels < 0; + if any(ineg) && min(LevelList) < min(Levels) + [idx,pos] = ismember(Levels, LevelList); + idx = idx & ineg; + contours(istart(idx)) = LevelList(pos(idx)-1); + end + + % Draw a contour group (MATLAB R2014b and newer only) + isFilled = isOn(get(h,'Fill')); + if isFilled + [m2t, str] = drawFilledContours(m2t, h, contours, istart, nrows, custom); + else + % Add colormap + cmap = m2t.current.colormap; + m2t = m2t_addAxisOption(m2t, matlab2pgfplotsColormap(m2t, cmap)); + + % Contour table in Matlab format + plotOptions = opts_new(); + plotOptions = opts_add(plotOptions,'contour prepared'); + plotOptions = opts_add(plotOptions,'contour prepared format','matlab'); + + % Labels + if isOff(get(h,'ShowText')) + plotOptions = opts_add(plotOptions,'contour/labels','false'); + end + + % Get line properties + [m2t, lineOptions] = getLineOptions(m2t, h); + + % Check for special color settings + [lineColor, isDefaultColor] = getAndCheckDefault('contour', h, 'LineColor', 'flat'); + if ~isDefaultColor + [m2t, lineOptions] = setColor(m2t, h, lineOptions, 'contour/draw color', lineColor, 'none'); + end + + % Merge the line options with the contour plot options + plotOptions = opts_merge(plotOptions, lineOptions); + plotOptions = opts_append_userdefined(plotOptions, custom.extraOptions); + + % Make contour table + [m2t, table, tableOptions] = makeTable(m2t, {'',''}, contours); + + % Print out + plotOpts = opts_print(plotOptions); + tabOpts = opts_print(tableOptions); + str = sprintf('\\addplot[%s] table[%s] {%%\n%s};\n', ... + plotOpts, tabOpts, table); + end +end +% ============================================================================== +function [istart, nrows] = findStartOfContourData(contours) + % Index beginning of contour data (see contourc.m for details) + nrows = size(contours,1); + istart = false(nrows,1); + pos = 1; + while pos < nrows + istart(pos) = true; + pos = pos + contours(pos, 2) + 1; + end + istart = find(istart); +end +% ============================================================================== +function [m2t, str] = drawFilledContours(m2t, h, contours, istart, nrows, custom) + % Loop each contour and plot a filled region + % + % NOTE: + % - we cannot plot from inner to outer contour since the last + % filled area will cover the inner regions. Therefore, we need to + % invert the plotting order in those cases. + % - we need to distinguish between contour groups. A group is + % defined by inclusion, i.e. its members are contained within one + % outer contour. The outer contours of two groups cannot include + % each other. + % FIXME: extract this logic to a function + str = ''; + if ~isVisible(h) + return + end + + % Split contours in cell array + cellcont = mat2cell(contours, diff([istart; nrows+1])); + ncont = numel(cellcont); + + % Determine contour groups and the plotting order. + % The ContourMatrix lists the contours in ascending order by level. + % Hence, if the lowest (first) contour contains any others, then the + % group will be a peak. Otherwise, the group will be a valley, and + % the contours will have to be plotted in reverse order, i.e. from + % highest (largest) to lowest (narrowest). + + %FIXME: close the contours over the border of the domain, see #723. + order = NaN(ncont,1); + ifree = true(ncont,1); + from = 1; + while any(ifree) + % Select peer with lowest level among the free contours, i.e. + % those which do not belong to any group yet + pospeer = find(ifree,1,'first'); + peer = cellcont{pospeer}; + igroup = false(ncont,1); + + % Loop through all contours + for ii = 1:numel(cellcont) + if ~ifree(ii), continue, end + + curr = cellcont{ii}; + % Current contour contained in the peer + if inpolygon(curr(2,1),curr(2,2), peer(2:end,1),peer(2:end,2)) + igroup(ii) = true; + isinverse = false; + % Peer contained in the current + elseif inpolygon(peer(2,1),peer(2,2),curr(2:end,1),curr(2:end,2)) + igroup(ii) = true; + isinverse = true; + end + end + % Order members of group according to the inclusion principle + nmembers = nnz(igroup ~= 0); + if isinverse + order(igroup) = nmembers+from-1:-1:from; + else + order(igroup) = from:nmembers+from-1; + end + + % Continue numbering + from = from + nmembers; + ifree = ifree & ~igroup; + end + + % Reorder the contours + cellcont(order,1) = cellcont; + + % Replace same level contours with hole, i.e. one level down + Levels = contours(istart,1); + LevelList = get(h,'LevelList'); + ireplace = find([false; diff(Levels) == 0]); + [idx, pos] = ismember(Levels(ireplace), LevelList); + for ii = 1:numel(pos) + cellcont{ireplace(ii)}(1) = LevelList(pos(ii)-1); + end + + % Add "contourless" backdrop + xdata = get(h,'XData'); + ydata = get(h,'YData'); + backdrop = [setdiff(LevelList, Levels), 4; + min(xdata(:)), min(ydata(:)); + min(xdata(:)), max(ydata(:)); + max(xdata(:)), max(ydata(:)); + max(xdata(:)), min(ydata(:))]; + cellcont = [backdrop; cellcont]; + + % Plot + columnNames = {'x','y'}; + for ii = 1:ncont + 1 + drawOptions = opts_new(); + + % Get fill color + zval = cellcont{ii}(1,1); + [m2t, xcolor] = getColor(m2t,h,zval,'image'); + drawOptions = opts_add(drawOptions,'fill',xcolor); + + % Get line properties + lineColor = get(h, 'LineColor'); + + [m2t, drawOptions] = setColor(m2t, h, drawOptions, 'draw', lineColor, 'none'); + + [m2t, lineOptions] = getLineOptions(m2t, h); + drawOptions = opts_merge(drawOptions, lineOptions); + + % Toggle legend entry + hasLegend = ii == 1 && m2t.currentHandleHasLegend; + drawOptions = maybeShowInLegend(hasLegend, drawOptions); + drawOptions = opts_append_userdefined(drawOptions, custom.extraOptions); + + % Print table + [m2t, table, tableOptions] = makeTable(m2t, columnNames, cellcont{ii}(2:end,:)); + + % Print out + drawOpts = opts_print(drawOptions); + tabOpts = opts_print(tableOptions); + str = sprintf('%s\\addplot[%s] table[%s] {%%\n%s};\n', ... + str, drawOpts, tabOpts, table); + end +end +% ============================================================================== +function [m2t, str] = drawHggroup(m2t, h, custom) + % Continue according to the plot type. Since the function `handle` is + % not available in Octave, the plot type will be guessed or the fallback type + % 'unknown' used. + % #COMPLEX: big switch-case + warnDoesNotHandleCustomProperties(h, custom); + switch getEnvironment() + case 'MATLAB' + cl = class(handle(h)); + + case 'Octave' + % Function `handle` is not yet implemented in Octave + % Consequently the plot type needs to be guessed. See #645. + cl = guessOctavePlotType(h); + + otherwise + errorUnknownEnvironment(); + end + + switch(cl) + case {'specgraph.barseries', 'matlab.graphics.chart.primitive.Bar'} + % hist plots and friends + [m2t, str] = handleObject(m2t, h, @drawBarseries); + + case {'specgraph.stemseries', 'matlab.graphics.chart.primitive.Stem'} + % stem plots + [m2t, str] = handleObject(m2t, h, @drawStemSeries); + + case {'specgraph.stairseries', 'matlab.graphics.chart.primitive.Stair'} + % stair plots + [m2t, str] = handleObject(m2t, h, @drawStairSeries); + + case {'specgraph.areaseries', 'matlab.graphics.chart.primitive.Area'} + % scatter plots + [m2t, str] = handleObject(m2t, h, @drawAreaSeries); + + case {'specgraph.quivergroup', 'matlab.graphics.chart.primitive.Quiver'} + % quiver arrows + [m2t, str] = handleObject(m2t, h, @drawQuiverGroup); + + case {'specgraph.errorbarseries', 'matlab.graphics.chart.primitive.ErrorBar'} + % error bars + [m2t, str] = handleObject(m2t, h, @drawErrorBars); + + case {'specgraph.scattergroup','matlab.graphics.chart.primitive.Scatter'} + % scatter plots + [m2t, str] = handleObject(m2t, h, @drawScatterPlot); + + case {'specgraph.contourgroup', 'matlab.graphics.chart.primitive.Contour'} + [m2t, str] = handleObject(m2t, h, @drawContour); + + case {'hggroup', 'matlab.graphics.primitive.Group'} + % handle all those the usual way + [m2t, str] = handleAllChildren(m2t, h); + + case 'unknown' + % Octave only: plot type could not be determined + % Fall back to basic plotting + [m2t, str] = handleAllChildren(m2t, h); + + otherwise + userWarning(m2t, 'Don''t know class ''%s''. Default handling.', cl); + try + m2tBackup = m2t; + [m2t, str] = handleAllChildren(m2t, h); + catch ME + userWarning(m2t, 'Default handling for ''%s'' failed. Continuing as if it did not occur. \n Original Message:\n %s', cl, getReport(ME)); + [m2t, str] = deal(m2tBackup, ''); % roll-back + end + end +end +% ============================================================================== +% Function `handle` is not yet implemented in Octave. +% Consequently the plot type needs to be guessed. See #645. +% If the type can not be determined reliably, 'unknown' will be set. +function cl = guessOctavePlotType(h) + % scatter plots + if hasProperties(h, {'marker','sizedata','cdata'}, {}) + cl = 'specgraph.scattergroup'; + + % error bars + elseif hasProperties(h, {'udata','ldata'}, {}) + cl = 'specgraph.errorbarseries'; + + % quiver plots + elseif hasProperties(h, {'udata','vdata'}, {'ldata'}) + cl = 'specgraph.quivergroup'; + + % bar plots + elseif hasProperties(h, {'bargroup','barwidth', 'barlayout'}, {}) + cl = 'specgraph.barseries'; + % unknown plot type + else + cl = 'unknown'; + end +end +% ============================================================================== +function bool = hasProperties(h, fieldsExpectedPresent, fieldsExpectedAbsent) + % Check if object has all of the given properties (case-insensitive). + % h handle to object (e.g. `gcf` or `gca`) + % fieldsExpectedPresent cell array of strings with property names to be present + % fieldsExpectedPresent cell array of strings with property names to be absent + fields = lower(fieldnames(get(h))); + present = all(ismember(lower(fieldsExpectedPresent), fields)); + absent = ~any(ismember(lower(fieldsExpectedAbsent), fields)); + bool = present && absent; +end +% ============================================================================== +function [m2t, str] = handleObject(m2t, h, actualHandler) + % this function wraps the actual drawing handlers and allows the user + % to specify custom options, code, comments or even handler via |m2tcustom|. + assert(isa(actualHandler,'function_handle')); + + custom = m2tcustom(h); % retrieve custom settings + if isfield(custom, 'customHandler') + actualHandler = custom.customHandler; + end + + [m2t, str] = feval(actualHandler, m2t, h, custom); + str = [texcomment(custom.commentsBefore), custom.codeBefore, ... + str, ... + texcomment(custom.commentsAfter), custom.codeAfter]; +end +% ============================================================================== +function warnDoesNotHandleCustomProperties(h, custom, unhandledProperties) +% warns the user about unhandled custom properties + if ~exist('unhandledProperties','var') + unhandledProperties = {'codeInsideFirst', 'codeInsideLast', 'extraOptions'}; + elseif ischar(unhandledProperties) + unhandledProperties = {unhandledProperties}; + end + + customProps = fieldnames(custom); + for iProperty = 1:numel(customProps) + prop = customProps{iProperty}; + if ~isempty(custom.(prop)) && ismember(prop, unhandledProperties) + warning('matlab2tikz:unhandledCustomProperty', ... + 'Custom property "%s" of %s has not been handled.',... + prop, get(h, 'Type')); + end + end +end +% ============================================================================== +function [m2t, str] = drawNothing(m2t, h, custom) %#ok some params unused + % draws nothing + % While it may seem useless, this allows a user to add comments/code + % for objects that matlab2tikz cannot translate (yet) thanks to the + % |handleObject| function together with |m2tcustom|. + str = ''; + warnDoesNotHandleCustomProperties(h, custom); +end +% ============================================================================== +function m2t = drawAnnotations(m2t) + % Draws annotation in Matlab (Octave not supported). + + % In HG1 annotations are children of an invisible axis called scribeOverlay. + % In HG2 annotations are children of annotationPane object which does not + % have any axis properties. Hence, we cannot simply handle it with a + % drawAxes() call. + + % Octave + if strcmpi(getEnvironment,'Octave') + return + end + + % Get annotation handles + if isHG2 + annotPanes = findall(m2t.current.gcf,'Tag','scribeOverlay'); + children = allchild(annotPanes); + %TODO: is this dead code? + if iscell(children) + children = [children{:}]; + end + annotHandles = findall(children,'Visible','on'); + else + annotHandles = findall(m2t.scribeLayer,'-depth',1,'Visible','on'); + end + + % There are no anotations + if isempty(annotHandles) + return + end + + % Create fake simplified axes overlay (no children) + warning('off', 'matlab2tikz:NoChildren') + scribeLayer = axes('Units','Normalized','Position',[0,0,1,1],'Visible','off'); + m2t = drawAxes(m2t, scribeLayer); + warning('on', 'matlab2tikz:NoChildren') + + % Plot in reverse to preserve z-ordering and assign the converted + % annotations to the converted fake overlay + for ii = numel(annotHandles):-1:1 + m2t = drawAnnotationsHelper(m2t, annotHandles(ii)); + end + + % Delete fake overlay graphics object + delete(scribeLayer) +end +% ============================================================================== +function m2t = drawAnnotationsHelper(m2t,h) + % Get class name + try + cl = class(handle(h)); + catch + cl = 'unknown'; + end + + switch cl + + % Line + case {'scribe.line', 'matlab.graphics.shape.Line'} + [m2t, str] = handleObject(m2t, h, @drawLine); + + % Ellipse + case {'scribe.scribeellipse','matlab.graphics.shape.Ellipse'} + [m2t, str] = handleObject(m2t, h, @drawEllipse); + + % Arrows + case {'scribe.arrow', 'scribe.doublearrow'}%,... + %'matlab.graphics.shape.Arrow', 'matlab.graphics.shape.DoubleEndArrow'} + % Annotation: single and double Arrow, line + % TODO: + % - write a drawArrow(). Handle all info info directly + % without using handleAllChildren() since HG2 does not have + % children (so no shortcut). + % - It would be good if drawArrow() was callable on a + % matlab.graphics.shape.TextArrow object to draw the arrow + % part. + [m2t, str] = handleAllChildren(m2t, h); + + % Text box + case {'scribe.textbox','matlab.graphics.shape.TextBox'} + [m2t, str] = handleObject(m2t, h, @drawText); + + % Arrow + case {'matlab.graphics.shape.Arrow','matlab.graphics.shape.DoubleEndArrow'} + [m2t, str] = handleObject(m2t, h, @drawArrow); + + % Text arrow + case {'scribe.textarrow'}%,'matlab.graphics.shape.TextArrow'} + % TODO: rewrite drawTextarrow. Handle all info info directly + % without using handleAllChildren() since HG2 does not + % have children (so no shortcut) as used for + % scribe.textarrow. + [m2t, str] = handleObject(m2t, h, @drawTextarrow); + + % Rectangle + case {'scribe.scriberect', 'matlab.graphics.shape.Rectangle'} + [m2t, str] = handleObject(m2t, h, @drawRectangle); + + case guitypes() + % don't do anything for GUI objects and their children + [m2t, str] = handleObject(m2t, h, @drawNothing); + + otherwise + userWarning(m2t, 'Don''t know annotation ''%s''.', cl); + return + end + + % Add annotation to scribe overlay + m2t.axes{end} = addChildren(m2t.axes{end}, str); +end +% ============================================================================== +function [m2t, str] = drawArrow(m2t, handle, custom) + str = ''; + if ~isLineVisible(handle) + return; % there is nothing to plot + end + + m2t = signalDependency(m2t, 'tikzlibrary', 'arrows.meta'); + % Arrow Style + if isa(handle,'matlab.graphics.shape.DoubleEndArrow') + arrowSpec = '{Stealth}-{Stealth}'; + else + arrowSpec = '-{Stealth}'; + end + + % Color + color = get(handle, 'Color'); + [m2t, xcolor] = getColor(m2t, handle, color, 'patch'); + + % Line options + [m2t, lineOptions] = getLineOptions(m2t, handle); + + drawOptions = opts_new(); + drawOptions = opts_add(drawOptions, arrowSpec); + drawOptions = opts_add(drawOptions, 'color', xcolor); + drawOptions = opts_merge(drawOptions, lineOptions); + drawOptions = opts_append_userdefined(drawOptions, custom.extraOptions); + + [m2t, pos1, pos2] = getPositionOfArrow(m2t, handle); + + styleOpts = opts_print(drawOptions); + str = sprintf('\\draw[%s] %s -- %s;\n', ... + styleOpts, pos1, pos2); +end +% ============================================================================== +function [m2t, pos1, pos2] = getPositionOfArrow(m2t, h) + % makes the tikz position string of an arrow + posX = get(h, 'X'); + posY = get(h, 'Y'); + + type = 'axis cs:'; + fmtUnit = ''; + + pos1{1} = formatDim(posX(1), fmtUnit); + pos1{2} = formatDim(posY(1), fmtUnit); + pos2{1} = formatDim(posX(2), fmtUnit); + pos2{2} = formatDim(posY(2), fmtUnit); + + pos1 = sprintf('(%s%s)',type,join(m2t,pos1,',')); + pos2 = sprintf('(%s%s)',type,join(m2t,pos2,',')); +end +% ============================================================================== +function [m2t, str] = drawSurface(m2t, h, custom) + [m2t, opts, s] = shaderOpts(m2t, h,'surf'); + tableOptions = opts_new(); + + % Allow for empty surf + if isNone(s.plotType) + str = ''; + return + end + + [dx, dy, dz, numrows] = getXYZDataFromSurface(h); + m2t = jumpAtUnboundCoords(m2t, [dx(:); dy(:); dz(:)]); + + [m2t, opts] = addZBufferOptions(m2t, h, opts); + + % Check if 3D + is3D = m2t.axes{end}.is3D; + if is3D + columnNames = {'x','y','z','c'}; + plotCmd = 'addplot3'; + data = applyHgTransform(m2t, [dx(:), dy(:), dz(:)]); + else + columnNames = {'x','y','c'}; + plotCmd = 'addplot'; + data = [dx(:), dy(:)]; + end + + % There are several possibilities of how colors are specified for surface + % plots: + % * explicitly by RGB-values, + % * implicitly through a color map with a point-meta coordinate, + % * implicitly through a color map with a given coordinate (e.g., z). + % + + % Check if we need extra CData. + CData = get(h, 'CData'); + if length(size(CData)) == 3 && size(CData, 3) == 3 + + % Create additional custom colormap + nrows = size(data,1); + CData = reshape(CData, nrows,3); + m2t.axes{end}.options(end+1,:) = ... + {matlab2pgfplotsColormap(m2t, CData, 'patchmap'), []}; + + % Index into custom colormap + color = (0:nrows-1)'; + + tableOptions = opts_add(tableOptions, 'colormap name','surfmap'); + else + opts = opts_add(opts,matlab2pgfplotsColormap(m2t, m2t.current.colormap),''); + % If NaNs are present in the color specifications, don't use them for + % Pgfplots; they may be interpreted as strings there. + % Note: + % Pgfplots actually does a better job than MATLAB in determining what + % colors to use for the patches. The circular test case on + % http://www.mathworks.de/de/help/matlab/ref/pcolor.html, for example + % yields a symmetric setup in Pgfplots (and doesn't in MATLAB). + needsPointmeta = any(xor(isnan(dz(:)), isnan(CData(:)))) ... + || any(abs(CData(:) - dz(:)) > 1.0e-10); + if needsPointmeta + color = CData(:); + else + color = dz(:); % Fallback on the z-values, especially if 2D view + end + end + tableOptions = opts_add(tableOptions, 'point meta','\thisrow{c}'); + + data = [data, color]; + + % Add mesh/rows= for specifying the row data instead of empty + % lines in the data list below. This makes it possible to reduce the + % data writing to one single sprintf() call. + opts = opts_add(opts,'mesh/rows',sprintf('%d', numrows)); + opts = opts_append_userdefined(opts, custom.extraOptions); + + % Print the addplot options + str = sprintf('\n\\%s[%%\n%s,\n%s]', plotCmd, s.plotType, opts_print(opts)); + + % Print the data + [m2t, table, tabOptsExtra] = makeTable(m2t, columnNames, data); + tableOptions = opts_merge(tabOptsExtra, tableOptions); + tabOpts = opts_print(tableOptions); + + % Here is where everything is put together + str = sprintf('%s\ntable[%s] {%%\n%s};\n', ... + str, tabOpts, table); + + % TODO: + % - remove grids in spectrogram by either removing grid command + % or adding: 'grid=none' from/in axis options + % - handling of huge data amounts in LaTeX. + + [m2t, labelString] = addLabel(m2t, h); + str = [str, labelString]; +end +% ============================================================================== +function [m2t, opts] = addZBufferOptions(m2t, h, opts) + % Enforce 'z buffer=sort' if shader is flat and is a 3D plot. It is to + % avoid overlapping e.g. sphere plots and to properly mimic Matlab's + % coloring of faces. + % NOTE: + % - 'z buffer=sort' is computationally more expensive for LaTeX, we + % could try to avoid it in some default situations, e.g. when dx and + % dy are rank-1-matrices. + % - hist3D plots should not be z-sorted or the highest bars will cover + % the shortest one even if positioned in the back + isShaderFlat = isempty(strfind(opts_get(opts, 'shader'), 'interp')); + isHist3D = strcmpi(get(h,'tag'), 'hist3'); + is3D = m2t.axes{end}.is3D; + if is3D && isShaderFlat && ~isHist3D + opts = opts_add(opts, 'z buffer', 'sort'); + % Pgfplots 1.12 contains a bug fix that fixes legend entries when + % 'z buffer=sort' has been set. So, it's easier to always require that + % version anyway. See #504 for more information. + m2t = needsPgfplotsVersion(m2t, [1,12]); + end +end +% ============================================================================== +function [dx, dy, dz, numrows] = getXYZDataFromSurface(h) + % retrieves X, Y and Z data from a Surface plot. The data gets returned in a + % wastefull format where the dimensions of these data vectors is equal, akin + % to the format used by meshgrid. + dx = get(h, 'XData'); + dy = get(h, 'YData'); + dz = get(h, 'ZData'); + [numcols, numrows] = size(dz); + + % If dx or dy are given as vectors, convert them to the (wasteful) matrix + % representation first. This makes sure we can treat the data with one + % single sprintf() command below. + if isvector(dx) + dx = ones(numcols,1) * dx(:)'; + end + if isvector(dy) + dy = dy(:) * ones(1,numrows); + end +end +% ============================================================================== +function [m2t, str] = drawVisibleText(m2t, handle, custom) + % Wrapper for drawText() that only draws visible text + + % There may be some text objects floating around a MATLAB figure which are + % handled by other subfunctions (labels etc.) or don't need to be handled at + % all. + % The HandleVisibility says something about whether the text handle is + % visible as a data structure or not. Typically, a handle is hidden if the + % graphics aren't supposed to be altered, e.g., axis labels. Most of those + % entities are captured by matlab2tikz one way or another, but sometimes they + % are not. This is the case, for example, with polar plots and the axis + % descriptions therein. Also, Matlab treats text objects with a NaN in the + % position as invisible. + if any(isnan(get(handle, 'Position')) | isnan(get(handle, 'Rotation'))) ... + || isOff(get(handle, 'Visible')) ... + || (isOff(get(handle, 'HandleVisibility')) && ... + ~m2t.args.showHiddenStrings) + + str = ''; + return; + end + + [m2t, str] = drawText(m2t, handle, custom); + +end +% ============================================================================== +function [m2t, str] = drawText(m2t, handle, custom) + % Adding text node anywhere in the axes environment. + % Not that, in Pgfplots, long texts get cut off at the axes. This is + % Different from the default MATLAB behavior. To fix this, one could use + % /pgfplots/after end axis/.code. + + if ~exist('custom','var') || isempty(custom) + custom = m2tcustom([]); %FIXME: adjust at all call sites instead + end + + % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + % get required properties + content = get(handle, 'String'); + Interpreter = get(handle, 'Interpreter'); + content = prettyPrint(m2t, content, Interpreter); + % Concatenate multiple lines + content = join(m2t, content, '\\'); + % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + % translate them to pgf style + style = opts_new(); + + bgColor = get(handle,'BackgroundColor'); + [m2t, style] = setColor(m2t, handle, style, 'fill', bgColor); + + style = getXYAlignmentOfText(handle, style); + + style = getRotationOfText(m2t, handle, style); + + [m2t, fontStyle] = getFontStyle(m2t, handle); + style = opts_merge(style, fontStyle); + + EdgeColor = get(handle, 'EdgeColor'); + [m2t, style] = setColor(m2t, handle, style, 'draw', EdgeColor); + + style = opts_append_userdefined(style, custom.extraOptions); + + % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + % plot the thing + [m2t, posString] = getPositionOfText(m2t, handle); + + styleOpts = opts_print(style); + str = sprintf('\\node[%s]\nat %s {%s};\n', ... + styleOpts, posString, content); +end +% ============================================================================== +function [style] = getXYAlignmentOfText(handle, style) + % sets the horizontal and vertical alignment options of a text object + VerticalAlignment = get(handle, 'VerticalAlignment'); + HorizontalAlignment = get(handle, 'HorizontalAlignment'); + + horizontal = ''; + vertical = ''; + switch VerticalAlignment + case {'top', 'cap'} + vertical = 'below'; + case {'baseline', 'bottom'} + vertical = 'above'; + end + switch HorizontalAlignment + case 'left' + horizontal = 'right'; + case 'right' + horizontal = 'left'; + end + alignment = strtrim(sprintf('%s %s', vertical, horizontal)); + if strcmp(VerticalAlignment, 'middle') && strcmp(HorizontalAlignment, 'center') + alignment = 'centered'; + end + + if ~isempty(alignment) + style = opts_add(style, alignment); + end + + % Set 'align' option that is needed for multiline text + style = opts_add(style, 'align', HorizontalAlignment); + + style = opts_add(style, 'inner sep', '0'); +end +% ============================================================================== +function [style] = getRotationOfText(m2t, handle, style) + % Add rotation, if existing + defaultRotation = 0.0; + rot = getOrDefault(handle, 'Rotation', defaultRotation); + if rot ~= defaultRotation + style = opts_add(style, 'rotate', sprintf(m2t.ff, rot)); + end +end +% ============================================================================== +function [m2t,posString] = getPositionOfText(m2t, h) + % makes the tikz position string of a text object + pos = get(h, 'Position'); + units = get(h, 'Units'); + is3D = m2t.axes{end}.is3D; + + % Deduce if text or textbox + type = get(h,'type'); + if isempty(type) || strcmpi(type,'hggroup') + type = get(h,'ShapeType'); % Undocumented property valid from 2008a + end + + switch type + case 'text' + if is3D + pos = applyHgTransform(m2t, pos); + npos = 3; + else + pos = pos(1:2); + npos = 2; + end + case {'textbox','textboxshape'} + % TODO: + % - size of the box (e.g. using node attributes minimum width / height) + % - Alignment of the resized box + pos = pos(1:2); + npos = 2; + + otherwise + error('matlab2tikz:drawText', 'Unrecognized text type: %s.', type); + end + + % Format according to units + switch units + case 'normalized' + type = 'rel axis cs:'; + fmtUnit = ''; + case 'data' + type = 'axis cs:'; + fmtUnit = ''; + % Let Matlab do the conversion of any unit into cm + otherwise + type = ''; + fmtUnit = 'cm'; + if ~strcmpi(units, 'centimeters') + % Save old pos, set units to cm, query pos, reset + % NOTE: cannot use copyobj since it is buggy in R2014a, see + % http://www.mathworks.com/support/bugreports/368385 + oldPos = get(h, 'Position'); + set(h,'Units','centimeters') + pos = get(h, 'Position'); + pos = pos(1:npos); + set(h,'Units',units,'Position',oldPos) + end + end + posString = cell(1,npos); + for ii = 1:npos + posString{ii} = formatDim(pos(ii), fmtUnit); + end + + posString = sprintf('(%s%s)',type,join(m2t,posString,',')); + m2t = disableClippingInCurrentAxes(m2t, pos); + +end +% ============================================================================== +function m2t = disableClippingInCurrentAxes(m2t, pos) + % Disables clipping in the current axes if the `pos` vector lies outside + % the limits of the axes. + xlim = getOrDefault(m2t.current.gca, 'XLim',[-Inf +Inf]); + ylim = getOrDefault(m2t.current.gca, 'YLim',[-Inf +Inf]); + zlim = getOrDefault(m2t.current.gca, 'ZLim',[-Inf +Inf]); + is3D = m2t.axes{end}.is3D; + + xOutOfRange = pos(1) < xlim(1) || pos(1) > xlim(2); + yOutOfRange = pos(2) < ylim(1) || pos(2) > ylim(2); + zOutOfRange = is3D && (pos(3) < zlim(1) || pos(3) > zlim(2)); + if xOutOfRange || yOutOfRange || zOutOfRange + m2t = m2t_addAxisOption(m2t, 'clip', 'false'); + end +end +% ============================================================================== +function [m2t, str] = drawRectangle(m2t, h, custom) + str = ''; + + % there may be some text objects floating around a Matlab figure which + % are handled by other subfunctions (labels etc.) or don't need to be + % handled at all + if ~isVisible(h) || isOff(get(h, 'HandleVisibility')) + return; + end + + % TODO handle Curvature = [0.8 0.4] + + % Get draw options. + [m2t, lineOptions] = getLineOptions(m2t, h); + [m2t, lineOptions] = getRectangleFaceOptions(m2t, h, lineOptions); + [m2t, lineOptions] = getRectangleEdgeOptions(m2t, h, lineOptions); + lineOptions = opts_append_userdefined(lineOptions, custom.extraOptions); + + pos = pos2dims(get(h, 'Position')); + % plot the thing + lineOpts = opts_print(lineOptions); + str = sprintf(['\\draw[%s] (axis cs:',m2t.ff,',',m2t.ff, ')', ... + ' rectangle (axis cs:',m2t.ff,',',m2t.ff,');\n'], ... + lineOpts, pos.left, pos.bottom, pos.right, pos.top); +end +% ============================================================================== +function [m2t, drawOptions] = getRectangleFaceOptions(m2t, h, drawOptions) + % draws the face (i.e. fill) of a Rectangle + faceColor = get(h, 'FaceColor'); + isAnnotation = strcmpi(get(h,'type'),'rectangleshape') || ... + strcmpi(getOrDefault(h,'ShapeType',''),'rectangle'); + isFlatColor = strcmpi(faceColor, 'flat'); + if ~(isNone(faceColor) || (isAnnotation && isFlatColor)) + [m2t, xFaceColor] = getColor(m2t, h, faceColor, 'patch'); + drawOptions = opts_add(drawOptions, 'fill', xFaceColor); + end +end +% ============================================================================== +function [m2t, drawOptions] = getRectangleEdgeOptions(m2t, h, drawOptions) + % draws the edges of a rectangle + edgeColor = get(h, 'EdgeColor'); + lineStyle = get(h, 'LineStyle'); + if isNone(lineStyle) || isNone(edgeColor) + drawOptions = opts_add(drawOptions, 'draw', 'none'); + else + [m2t, drawOptions] = setColor(m2t, h, drawOptions, 'draw', edgeColor); + end +end +% ============================================================================== +function [m2t,opts,s] = shaderOpts(m2t, handle, selectedType) + % SHADEROPTS Returns the shader, fill and draw options for patches, surfs and meshes + % + % SHADEROPTS(M2T, HANDLE, SELECTEDTYPE) Where SELECTEDTYPE should either + % be 'surf' or 'patch' + % + % + % [...,OPTS, S] = SHADEROPTS(...) + % OPTS is a M by 2 cell array with Key/Value pairs + % S is a struct with fields, e.g. 'faceColor', to be re-used by the + % caller + + % Initialize + opts = opts_new; + s.hasOneEdgeColor = false; + s.hasOneFaceColor = false; + + % Get relevant Face and Edge color properties + s.faceColor = get(handle, 'FaceColor'); + s.edgeColor = get(handle, 'EdgeColor'); + + if isNone(s.faceColor) && isNone(s.edgeColor) + s.plotType = 'none'; + s.hasOneEdgeColor = true; + elseif isNone(s.faceColor) + s.plotType = 'mesh'; + s.hasOneFaceColor = true; + [m2t, opts, s] = shaderOptsMesh(m2t, handle, opts, s); + else + s.plotType = selectedType; + [m2t, opts, s] = shaderOptsSurfPatch(m2t, handle, opts, s); + end +end +% ============================================================================== +function [m2t, opts, s] = shaderOptsMesh(m2t, handle, opts, s) + + % Edge 'interp' + if strcmpi(s.edgeColor, 'interp') + opts = opts_add(opts,'shader','flat'); + + % Edge RGB + else + s.hasOneEdgeColor = true; + [m2t, xEdgeColor] = getColor(m2t, handle, s.edgeColor, 'patch'); + opts = opts_add(opts,'color',xEdgeColor); + end +end +% ============================================================================== +function [m2t, opts, s] = shaderOptsSurfPatch(m2t, handle, opts, s) + % gets the shader options for surface patches + + % Set opacity if FaceAlpha < 1 in MATLAB + s.faceAlpha = get(handle, 'FaceAlpha'); + if isnumeric(s.faceAlpha) && s.faceAlpha ~= 1.0 + opts = opts_add(opts,'fill opacity',sprintf(m2t.ff,s.faceAlpha)); + end + + % Set opacity if EdgeAlpha < 1 in MATLAB + s.edgeAlpha = get(handle, 'EdgeAlpha'); + if isnumeric(s.edgeAlpha) && s.edgeAlpha ~= 1.0 + opts = opts_add(opts,'draw opacity',sprintf(m2t.ff,s.edgeAlpha)); + end + + if isNone(s.edgeColor) || isNone(get(handle,'LineStyle')) % Edge 'none' + [m2t, opts, s] = shaderOptsSurfPatchEdgeNone(m2t, handle, opts, s); + + elseif strcmpi(s.edgeColor, 'interp') % Edge 'interp' + [m2t, opts, s] = shaderOptsSurfPatchEdgeInterp(m2t, handle, opts, s); + + elseif strcmpi(s.edgeColor, 'flat') % Edge 'flat' + [m2t, opts, s] = shaderOptsSurfPatchEdgeFlat(m2t, handle, opts, s); + + else % Edge RGB + [m2t, opts, s] = shaderOptsSurfPatchEdgeRGB(m2t, handle, opts, s); + end +end +% ============================================================================== +function [m2t, opts, s] = shaderOptsSurfPatchEdgeNone(m2t, handle, opts, s) + % gets the shader options for surface patches without edges + s.hasOneEdgeColor = true; % consider void as true + if strcmpi(s.faceColor, 'flat') + opts = opts_add(opts,'shader','flat'); + elseif strcmpi(s.faceColor, 'interp') + opts = opts_add(opts,'shader','interp'); + else + s.hasOneFaceColor = true; + [m2t,xFaceColor] = getColor(m2t, handle, s.faceColor, 'patch'); + opts = opts_add(opts,'fill',xFaceColor); + end +end +function [m2t, opts, s] = shaderOptsSurfPatchEdgeInterp(m2t, handle, opts, s) + % gets the shader options for surface patches with interpolated edge colors + if strcmpi(s.faceColor, 'interp') + opts = opts_add(opts,'shader','interp'); + elseif strcmpi(s.faceColor, 'flat') + opts = opts_add(opts,'shader','faceted'); + else + s.hasOneFaceColor = true; + [m2t,xFaceColor] = getColor(m2t, handle, s.faceColor, 'patch'); + opts = opts_add(opts,'fill',xFaceColor); + end +end +function [m2t, opts, s] = shaderOptsSurfPatchEdgeFlat(m2t, handle, opts, s) + % gets the shader options for surface patches with flat edge colors, i.e. the + % vertex color + if strcmpi(s.faceColor, 'flat') + opts = opts_add(opts,'shader','flat corner'); + elseif strcmpi(s.faceColor, 'interp') + warnFacetedInterp(m2t); + opts = opts_add(opts,'shader','faceted interp'); + else + s.hasOneFaceColor = true; + opts = opts_add(opts,'shader','flat corner'); + [m2t,xFaceColor] = getColor(m2t, handle, s.faceColor, 'patch'); + opts = opts_add(opts,'fill',xFaceColor); + end +end +function [m2t, opts, s] = shaderOptsSurfPatchEdgeRGB(m2t, handle, opts, s) + % gets the shader options for surface patches with fixed (RGB) edge color + s.hasOneEdgeColor = true; + [m2t, xEdgeColor] = getColor(m2t, handle, s.edgeColor, 'patch'); + if isnumeric(s.faceColor) + s.hasOneFaceColor = true; + [m2t, xFaceColor] = getColor(m2t, handle, s.faceColor, 'patch'); + opts = opts_add(opts,'fill',xFaceColor); + opts = opts_add(opts,'faceted color',xEdgeColor); + elseif strcmpi(s.faceColor,'interp') + warnFacetedInterp(m2t); + opts = opts_add(opts,'shader','faceted interp'); + opts = opts_add(opts,'faceted color',xEdgeColor); + else + opts = opts_add(opts,'shader','flat corner'); + opts = opts_add(opts,'draw',xEdgeColor); + end +end +% ============================================================================== +function warnFacetedInterp(m2t) + % warn the user about the space implications of "shader=faceted interp" + userWarning(m2t, ... + ['A 3D plot with "shader = faceted interp" is being produced.\n', ... + 'This may produce big and sluggish PDF files.\n', ... + 'See %s and Section 4.6.6 of the pgfplots manual for workarounds.'], ... + issueUrl(m2t, 693, true)); +end +% ============================================================================== +function url = issueUrl(m2t, number, forOutput) + % Produces the URL for an issue report in the GitHub repository. + % When the `forOutput` flag is set, this format the URL for printing to the + % MATLAB terminal. + if ~exist('forOutput','var') || isempty(forOutput) + forOutput = false; + end + url = sprintf('%s/%d', m2t.about.issues, number); + if forOutput + url = clickableUrl(url, sprintf('#%d', number)); + end +end +% ============================================================================== +function url = clickableUrl(url, title) + % Produce a clickable URL for outputting to the MATLAB terminal + if ~exist('title','var') || isempty(title) + title = url; + end + switch getEnvironment() + case 'MATLAB' + url = sprintf('%s', url, title); + case 'Octave' + % just use the URL and discard the title since Octave doesn't + % support HTML tags in its output. + otherwise + errorUnknownEnvironment(); + end +end +% ============================================================================== +function [m2t, str] = drawScatterPlot(m2t, h, custom) + % DRAWSCATTERPLOT Draws a scatter plot + % + % A scatter plot is a plot containing only markers and where the + % size and/or color of each marker can be changed independently. + % + % References for TikZ code: + % - http://tex.stackexchange.com/questions/197270/how-to-plot-scatter-points-using-pgfplots-with-color-defined-from-table-rgb-valu + % - http://tex.stackexchange.com/questions/98646/multiple-different-meta-for-marker-color-and-marker-size + % + % See also: scatter + str = ''; + if ~isVisible(h) + return; % there is nothing to plot + end + + dataInfo = getDataInfo(h, 'X','Y','Z','C','Size'); + markerInfo = getMarkerInfo(m2t, h); + + if isempty(dataInfo.C) && strcmpi(getEnvironment(), 'Octave') + dataInfo.C = get(h, 'MarkerEdgeColor'); + end + + %TODO: check against getMarkerOptions() for duplicated code + + dataInfo.Size = tryToMakeScalar(dataInfo.Size, m2t.tol); + + % Rescale marker size (not definitive, follow discussion in #316) + % Prescale marker size for octave + if strcmpi(getEnvironment(), 'Octave') + dataInfo.Size = dataInfo.Size.^2/2; + end + dataInfo.Size = translateMarkerSize(m2t, markerInfo.style, sqrt(dataInfo.Size)/2); + + drawOptions = opts_new(); + + %% Determine if we are drawing an actual scatter plot + hasDifferentSizes = numel(dataInfo.Size) ~= 1; + hasDifferentColors = numel(dataInfo.C) ~= 3; + isaScatter = hasDifferentSizes || hasDifferentColors; + if isaScatter + drawOptions = opts_add(drawOptions, 'scatter'); + end + %TODO: we need to set the scatter source + drawOptions = opts_add(drawOptions, 'only marks'); + drawOptions = opts_add(drawOptions, 'mark', markerInfo.tikz); + + if length(dataInfo.C) == 3 + % gets options specific to scatter plots with a single color + % No special treatment for the colors or markers are needed. + % All markers have the same color. + [m2t, xcolor, hasFaceColor] = getColorOfMarkers(m2t, h, 'MarkerFaceColor', dataInfo.C); + [m2t, ecolor, hasEdgeColor] = getColorOfMarkers(m2t, h, 'MarkerEdgeColor', dataInfo.C); + + if length(dataInfo.Size) == 1; + drawOptions = opts_addSubOpts(drawOptions, 'mark options', ... + markerInfo.options); + drawOptions = opts_add(drawOptions, 'mark size', ... + sprintf('%.4fpt', dataInfo.Size)); % FIXME: investigate whether to use `m2t.ff` + if hasEdgeColor + drawOptions = opts_add(drawOptions, 'draw', ecolor); + else + drawOptions = opts_add(drawOptions, 'color', xcolor); %TODO: why do we even need this one? + end + if hasFaceColor + drawOptions = opts_add(drawOptions, 'fill', xcolor); + end + else % if changing marker size but same color on all marks + markerOptions = opts_new(); + markerOptions = opts_addSubOpts(markerOptions, 'mark options', ... + markerInfo.options); + if hasEdgeColor + markerOptions = opts_add(markerOptions, 'draw', ecolor); + else + markerOptions = opts_add(markerOptions, 'draw', xcolor); + end + if hasFaceColor + markerOptions = opts_add(markerOptions, 'fill', xcolor); + end + % for changing marker size, the 'scatter' option has to be added + drawOptions = opts_add(drawOptions, 'color', xcolor); + drawOptions = opts_addSubOpts(drawOptions, 'mark options', ... + markerInfo.options); + + if ~hasFaceColor + drawOptions = opts_add(drawOptions, ... + 'scatter/use mapped color', xcolor); + else + drawOptions = opts_addSubOpts(drawOptions, ... + 'scatter/use mapped color', markerOptions); + end + end + elseif size(dataInfo.C,2) == 3 + % scatter plots with each marker a different RGB color (not yet supported!) + userWarning(m2t, 'Pgfplots cannot handle RGB scatter plots yet.'); + % TODO Get this in order as soon as Pgfplots can do "scatter rgb". + % See e.g. http://tex.stackexchange.com/questions/197270 and #433 + else + % scatter plot where the colors are set using a color map + markerOptions = opts_new(); + markerOptions = opts_addSubOpts(markerOptions, 'mark options', ... + markerInfo.options); + if markerInfo.hasEdgeColor && markerInfo.hasFaceColor + [m2t, ecolor] = getColor(m2t, h, markerInfo.EdgeColor, 'patch'); + markerOptions = opts_add(markerOptions, 'draw', ecolor); + else + markerOptions = opts_add(markerOptions, 'draw', 'mapped color'); + end + if markerInfo.hasFaceColor + markerOptions = opts_add(markerOptions, 'fill', 'mapped color'); + end + + if numel(dataInfo.Size) == 1 + drawOptions = opts_add(drawOptions, 'mark size', ... + sprintf('%.4fpt', dataInfo.Size)); % FIXME: investigate whether to use `m2t.ff` + else + %TODO: warn the user about this. It is not currently supported. + end + + drawOptions = opts_add(drawOptions, 'scatter src', 'explicit'); + drawOptions = opts_addSubOpts(drawOptions, 'scatter/use mapped color', ... + markerOptions); + % Add color map. + m2t = m2t_addAxisOption(m2t, matlab2pgfplotsColormap(m2t, m2t.current.colormap), []); + end + % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + % Plot the thing. + [env, data, metaPart, columns] = organizeScatterData(m2t, dataInfo); + + if hasDifferentSizes + drawOptions = opts_append(drawOptions, 'visualization depends on', ... + '{\thisrow{size} \as \perpointmarksize}'); + drawOptions = opts_add(drawOptions, ... + 'scatter/@pre marker code/.append style', ... + '{/tikz/mark size=\perpointmarksize}'); + end + % Add forget plot + drawOptions = maybeShowInLegend(m2t.currentHandleHasLegend, drawOptions); + + % The actual printing. + [m2t, table, tableOptions] = makeTable(m2t, columns, data); + tableOptions = opts_merge(tableOptions, metaPart); + drawOptions = opts_append_userdefined(drawOptions, custom.extraOptions); + + % Print + drawOpts = opts_print(drawOptions); + tabOpts = opts_print(tableOptions); + str = sprintf('\\%s[%s] table[%s]{%s};\n',... + env, drawOpts, tabOpts, table); +end +% ============================================================================== +function dataInfo = getDataInfo(h, varargin) + % retrieves the "*Data fields from a HG object + % When no names are specified, it assumes 'X','Y','Z' is requested + if nargin == 1 + fields = {'X','Y','Z'}; + else + fields = varargin; + end + dataInfo = struct(); + for iField = 1:numel(fields) + name = fields{iField}; + dataInfo.(name) = get(h, [name 'Data']); + end +end +% ============================================================================== +function value = tryToMakeScalar(value, tolerance) + % make a vector into a scalar when all its components are equal + if ~exist('tolerance','var') + tolerance = 0; % do everything perfectly + end + if all(abs(value - value(1)) <= tolerance) + value = value(1); + end +end +% ============================================================================== +function marker = getMarkerInfo(m2t, h, markOptions) + % gets marker-related options as a struct + if ~exist('markOptions','var') || isempty(markOptions) + markOptions = opts_new(); + end + marker = struct(); + marker.style = get(h, 'Marker'); + marker.FaceColor = get(h, 'MarkerFaceColor'); + marker.EdgeColor = get(h, 'MarkerEdgeColor'); + marker.hasFaceColor = ~isNone(marker.FaceColor); + marker.hasEdgeColor = ~isNone(marker.EdgeColor); + [marker.tikz, marker.options] = translateMarker(m2t, marker.style, ... + markOptions, marker.hasFaceColor); +end +% ============================================================================== +function [env, data, metaOptions, columns] = organizeScatterData(m2t, dataInfo) + % reorganizes the {X,Y,Z,S} data into a single matrix + metaOptions = opts_new(); + + + xData = dataInfo.X; + yData = dataInfo.Y; + zData = dataInfo.Z; + cData = dataInfo.C; + sData = dataInfo.Size; + + % add the actual data + if ~m2t.axes{end}.is3D + env = 'addplot'; + columns = {'x','y'}; + data = [xData(:), yData(:)]; + else + env = 'addplot3'; + columns = {'x','y','z'}; + data = applyHgTransform(m2t, [xData(:), yData(:), zData(:)]); + end + + % add marker sizes + if length(sData) ~= 1 + columns = [columns, {'size'}]; + data = [data, sData(:)]; + end + + % add color data + if length(cData) == 3 + % If size(cData,1)==1, then all the colors are the same and have + % already been accounted for above. + + elseif size(cData,2) == 3 + %TODO Hm, can't deal with this? + %[m2t, col] = rgb2colorliteral(m2t, cData(k,:)); + %str = strcat(str, sprintf(' [%s]\n', col)); + columns = [columns, {'R','G','B'}]; + data = [data, cData(:,1), cData(:,2), cData(:,3)]; + else + columns = [columns, {'color'}]; + metaOptions = opts_add(metaOptions, 'meta', 'color'); + data = [data, cData(:)]; + end +end +% ============================================================================== +function [m2t, xcolor, hasColor] = getColorOfMarkers(m2t, h, name, cData) + color = get(h, name); + hasColor = ~isNone(color); + if hasColor && ~strcmpi(color,'flat'); + [m2t, xcolor] = getColor(m2t, h, color, 'patch'); + else + [m2t, xcolor] = getColor(m2t, h, cData, 'patch'); + end +end +% ============================================================================== +function [m2t, str] = drawHistogram(m2t, h, custom) + % Takes care of plots like the ones produced by MATLAB's histogram function. + % The main pillar is Pgfplots's '{x,y}bar' plot. + % + % TODO Get rid of code duplication with 'drawAxes'. + + % Do nothing if plot is invisible + str = ''; + if ~isVisible(h) + return; + end + + % Init drawOptions + drawOptions = opts_new(); + + % Data + binEdges = get(h, 'BinEdges'); + binValue = get(h, 'Values'); + data = [binEdges(:), [binValue(:); binValue(end)]]; + + % Check for orientation of the bars + isHorizontal = ~strcmpi(get(h, 'Orientation'), 'vertical'); + if isHorizontal + drawOptions = opts_add(drawOptions, 'xbar interval'); + data = fliplr(data); + else + drawOptions = opts_add(drawOptions, 'ybar interval'); + end + + % Get the draw options for the bars + [m2t, drawOptions] = getPatchDrawOptions(m2t, h, drawOptions); + drawOptions = opts_append_userdefined(drawOptions, custom.extraOptions); + + % Make table + [m2t, table, tableOptions] = makeTable(m2t, {'x','y'},data); + + % Print out + drawOpts = opts_print(drawOptions); + tabOpts = opts_print(tableOptions); + str = sprintf('\\addplot[%s] table[%s] {%s};\n', ... + drawOpts, tabOpts, table); +end +% ============================================================================== +function [m2t, str] = drawBarseries(m2t, h, custom) + % Takes care of plots like the ones produced by MATLAB's bar function. + % The main pillar is Pgfplots's '{x,y}bar' plot. + % + % TODO Get rid of code duplication with 'drawAxes'. + + % Do nothing if plot is invisible + str = ''; + if ~isVisible(h) + return; + end + + % Init drawOptions + drawOptions = opts_new(); + + % Check for orientation of the bars and their layout + isHorizontal = isOn(get(h, 'Horizontal')); + if isHorizontal + barType = 'xbar'; + else + barType = 'ybar'; + end + + % Get the draw options for the layout + [m2t, drawOptions] = setBarLayoutOfBarSeries(m2t, h, barType, drawOptions); + + % Get the draw options for the bars + [m2t, drawOptions] = getPatchDrawOptions(m2t, h, drawOptions); + + % Add 'log origin = infty' if BaseValue differs from zero (log origin=0 is + % the default behaviour since Pgfplots v1.5). + baseValue = get(h, 'BaseValue'); + if baseValue ~= 0.0 + m2t = m2t_addAxisOption(m2t, 'log origin', 'infty'); + %TODO: wait for pgfplots to implement other base values (see #438) + end + + % Generate the tikz table + xData = get(h, 'XData'); + yData = get(h, 'YData'); + if isHorizontal + [yDataPlot, xDataPlot] = deal(xData, yData); % swap values + else + [xDataPlot, yDataPlot] = deal(xData, yData); + end + [m2t, table, tableOptions] = makeTable(m2t, '', xDataPlot, '', yDataPlot); + + drawOptions = opts_append_userdefined(drawOptions, custom.extraOptions); + % Print out + drawOpts = opts_print(drawOptions); + tabOpts = opts_print(tableOptions); + str = sprintf('\\addplot[%s] table[%s] {%s};\n', ... + drawOpts, tabOpts, table); + % Add a baseline if appropriate + [m2t, baseline] = drawBaseline(m2t,h,isHorizontal); + str = [str, baseline]; +end +% ============================================================================== +function BarWidth = getBarWidthInAbsolutUnits(h) + % determines the width of a bar in a bar plot + XData = get(h,'XData'); + BarWidth = get(h, 'BarWidth'); + if length(XData) > 1 && ~isCategoricalType(XData) + BarWidth = min(diff(XData)) * BarWidth; + end +end +% ============================================================================== +function [m2t, drawOptions] = setBarLayoutOfBarSeries(m2t, h, barType, drawOptions) + % sets the options specific to a bar layour (grouped vs stacked) + barlayout = get(h, 'BarLayout'); + + switch barlayout + case 'grouped' % grouped bar plots + + % Get number of bars series and bar series id + [numBarSeries, barSeriesId] = getNumBarAndId(h); + + % Maximum group width relative to the minimum distance between two + % x-values. See /toolbox/matlab/specgraph/makebars.m + maxGroupWidth = 0.8; + if numBarSeries == 1 + groupWidth = 1.0; + else + groupWidth = min(maxGroupWidth, numBarSeries/(numBarSeries+1.5)); + end + + % Calculate the width of each bar and the center point shift as in + % makebars.m + % Get the shifts of the bar centers. + % In case of numBars==1, this returns 0, + % In case of numBars==2, this returns [-1/4, 1/4], + % In case of numBars==3, this returns [-1/3, 0, 1/3], + % and so forth. + % assumedBarWidth = groupWidth/numBarSeries; % assumption + % barShift = (barSeriesId - 0.5) * assumedBarWidth - groupWidth/2; + % FIXME #785: The previous version of barshift lead to + % regressions, as the bars were stacked. + % Instead remove the calculation of barShift and add x/ybar to + % the axis so that pgf determines it automatically. + + % From http://www.mathworks.com/help/techdoc/ref/bar.html: + % bar(...,width) sets the relative bar width and controls the + % separation of bars within a group. The default width is 0.8, so if + % you do not specify X, the bars within a group have a slight + % separation. If width is 1, the bars within a group touch one + % another. The value of width must be a scalar. + assumedBarWidth = groupWidth/numBarSeries; % assumption + barWidth = getBarWidthInAbsolutUnits(h) * assumedBarWidth; + + % Bar type + drawOptions = opts_add(drawOptions, barType); + + % Bar width + % Relative width does not work with categorical data + if ~isCategoricalType(get(h, 'XData')) + drawOptions = opts_add(drawOptions, 'bar width', formatDim(barWidth, '')); + end + + % The bar shift auto feature was introduced in pgfplots 1.13 + m2t = needsPgfplotsVersion(m2t, [1,13]); + m2t = m2t_addAxisOption(m2t, 'bar shift auto'); + case 'stacked' % stacked plots + % Pass option to parent axis & disallow anything but stacked plots + % Make sure this happens exactly *once*. + + if ~m2t.axes{end}.barAddedAxisOption; + barWidth = getBarWidthInAbsolutUnits(h); + m2t = m2t_addAxisOption(m2t, 'bar width', formatDim(barWidth,'')); + m2t.axes{end}.barAddedAxisOption = true; + end + + % Somewhere between pgfplots 1.5 and 1.8 and starting + % again from 1.11, the option {x|y}bar stacked can be applied to + % \addplot instead of the figure and thus allows to combine stacked + % bar plots and other kinds of plots in the same axis. + % Thus, it is advisable to use pgfplots 1.11. In older versions, the + % plot will only contain a single bar series, but should compile fine. + m2t = needsPgfplotsVersion(m2t, [1,11]); + drawOptions = opts_add(drawOptions, [barType ' stacked']); + otherwise + error('matlab2tikz:drawBarseries', ... + 'Don''t know how to handle BarLayout ''%s''.', barlayout); + end +end +% ============================================================================== +function [numBarSeries, barSeriesId] = getNumBarAndId(h) + % Get number of bars series and bar series id + prop = switchMatOct('BarPeers', 'bargroup'); + bargroup = get(h, prop); + numBarSeries = numel(bargroup); + + if isHG2 + % In HG2, BarPeers are sorted in reverse order wrt HG1 + bargroup = bargroup(end:-1:1); + + elseif strcmpi(getEnvironment, 'MATLAB') + % In HG1, h is a double but bargroup a graphic object. Cast h to a + % graphic object + h = handle(h); + + else + % In Octave, the bargroup is a replicated cell array. Pick first + if iscell(bargroup) + bargroup = bargroup{1}; + end + end + + % Get bar series Id + [dummy, barSeriesId] = ismember(h, bargroup); +end +% ============================================================================== +function [m2t,str] = drawBaseline(m2t,hparent,isVertical) + % DRAWBASELINE Draws baseline for bar and stem plots + % + % Notes: + % - In HG2, the baseline is a specific object child of a bar or stem + % plot. So, handleAllChildren() won't find a line in the axes to plot as + % the baseline. + % - The baseline is horizontal for vertical bar and stem plots and is + % vertical for horixontal barplots. The ISVERTICAL input refers to the + % baseline. + % - We do not plot baselines with a BaseValue different from 0 because + % pgfplots does not support shifts in the BaseValue, e.g. see #438. + % We either implement our own data shifting or wait for pgfplots. + + if ~exist('isVertical','var') + isVertical = false; + end + + str = ''; + baseValue = get(hparent, 'BaseValue'); + if isOff(get(hparent,'ShowBaseLine')) || ~isHG2() || baseValue ~= 0 + return + end + + hBaseLine = get(hparent,'BaseLine'); + + % Line options of the baseline + [m2t, lineOptions] = getLineOptions(m2t, hparent); + color = get(hBaseLine, 'Color'); + [m2t, lineColor] = getColor(m2t, hBaseLine, color, 'patch'); + + drawOptions = opts_new(); + drawOptions = opts_add(drawOptions, 'forget plot'); + drawOptions = opts_add(drawOptions, 'color', lineColor); + drawOptions = opts_merge(drawOptions, lineOptions); + + % Get data + if isVertical + xData = repmat(baseValue,1,2); + yData = get(m2t.current.gca,'Ylim'); + else + xData = get(m2t.current.gca,'Xlim'); + yData = repmat(baseValue,1,2); + end + + [m2t, table, tableOptions] = makeTable(m2t, '', xData, '', yData); + + % Print out + drawOpts = opts_print(drawOptions); + tabOpts = opts_print(tableOptions); + str = sprintf('\\addplot[%s] table[%s] {%s};\n', ... + drawOpts, tabOpts, table); +end +% ============================================================================== +function [m2t, str] = drawAreaSeries(m2t, h, custom) + % Takes care of MATLAB's area plots. + % + % TODO Get rid of code duplication with 'drawAxes'. + + % Do nothing if plot is invisible + str = ''; + if ~isVisible(h) + return; + end + + % Init drawOptions + drawOptions = opts_new(); + + % Get the draw options for the bars + [m2t, drawOptions] = getPatchDrawOptions(m2t, h, drawOptions); + + if ~isfield(m2t, 'addedAreaOption') || isempty(m2t.addedAreaOption) || ~m2t.addedAreaOption + % Add 'area style' to axes options. + m2t = m2t_addAxisOption(m2t, 'area style'); + m2t = m2t_addAxisOption(m2t, 'stack plots', 'y'); + m2t.addedAreaOption = true; + end + + % Toggle legend entry + drawOptions = maybeShowInLegend(m2t.currentHandleHasLegend, drawOptions); + + % Generate the tikz table + xData = get(h, 'XData'); + yData = get(h, 'YData'); + [m2t, table, tableOptions] = makeTable(m2t, '', xData, '', yData); + + drawOptions = opts_append_userdefined(drawOptions, custom.extraOptions); + + % Print out + drawOpts = opts_print(drawOptions); + tabOpts = opts_print(tableOptions); + str = sprintf('\\addplot[%s] table[%s]{%s}\n\\closedcycle;\n',... + drawOpts, tabOpts, table); + %TODO: shouldn't this be "\addplot[] table[] {}" instead? +end +% ============================================================================== +function [m2t, str] = drawStemSeries(m2t, h, custom) + [m2t, str] = drawStemOrStairSeries_(m2t, h, 'ycomb', custom); + + % TODO: handle baseplane with stem3() + if m2t.axes{end}.is3D + return + end + [m2t, baseline] = drawBaseline(m2t,h); + str = [str, baseline]; +end +function [m2t, str] = drawStairSeries(m2t, h, custom) + [m2t, str] = drawStemOrStairSeries_(m2t, h, 'const plot', custom); +end +function [m2t, str] = drawStemOrStairSeries_(m2t, h, plotType, custom) + + % Do nothing if plot is invisible + str = ''; + if ~isLineVisible(h) + return % nothing to plot! + end + + % deal with draw options + color = get(h, 'Color'); + [m2t, plotColor] = getColor(m2t, h, color, 'patch'); + + [m2t, lineOptions] = getLineOptions(m2t, h); + [m2t, markerOptions] = getMarkerOptions(m2t, h); + + drawOptions = opts_new(); + drawOptions = opts_add(drawOptions, plotType); + drawOptions = opts_add(drawOptions, 'color', plotColor); + drawOptions = opts_merge(drawOptions, lineOptions, markerOptions); + + % Toggle legend entry + drawOptions = maybeShowInLegend(m2t.currentHandleHasLegend, drawOptions); + drawOptions = opts_append_userdefined(drawOptions, custom.extraOptions); + + drawOpts = opts_print(drawOptions); + + % Generate the tikz table + xData = get(h, 'XData'); + yData = get(h, 'YData'); + if m2t.axes{end}.is3D + % TODO: account for hgtransform + zData = get(h, 'ZData'); + [m2t, table, tableOptions] = makeTable(m2t, '', xData, '', yData, '', zData); + % Print out + tabOpts = opts_print(tableOptions); + str = sprintf('\\addplot3 [%s]\n table[%s] {%s};\n ', ... + drawOpts, tabOpts, table); + else + [m2t, table, tableOptions] = makeTable(m2t, '', xData, '', yData); + % Print out + tabOpts = opts_print(tableOptions); + str = sprintf('\\addplot[%s] table[%s] {%s};\n', ... + drawOpts, tabOpts, table); + end + +end +% ============================================================================== +function [m2t, str] = drawQuiverGroup(m2t, h, custom) + % Takes care of MATLAB's quiver plots. + str = ''; + + [x,y,z,u,v,w] = getAndRescaleQuivers(m2t,h); + is3D = m2t.axes{end}.is3D; + + % prepare output + if is3D + name = 'addplot3'; + else % 2D plotting + name = 'addplot'; + end + + variables = {'x', 'y', 'z', 'u', 'v', 'w'}; + data = NaN(numel(x),6); + data(:,1) = x; + data(:,2) = y; + data(:,3) = z; + data(:,4) = u; + data(:,5) = v; + data(:,6) = w; + + if ~is3D + data(:,[3 6]) = []; % remove Z-direction + variables([3 6]) = []; + end + + % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + % gather the arrow options + showArrowHead = get(h, 'ShowArrowHead'); + if ~isLineVisible(h) && ~showArrowHead + return + end + + plotOptions = opts_new(); + if showArrowHead + plotOptions = opts_add(plotOptions, '-Straight Barb'); + signalDependency(m2t, 'tikzlibrary', 'arrows.meta'); + else + plotOptions = opts_add(plotOptions, '-'); + end + + % Append the arrow style to the TikZ options themselves. + color = get(h, 'Color'); + [m2t, lineOptions] = getLineOptions(m2t, h); + [m2t, arrowcolor] = getColor(m2t, h, color, 'patch'); + plotOptions = opts_add(plotOptions, 'color', arrowcolor); + plotOptions = opts_merge(plotOptions, lineOptions); + + % Define the quiver settings + quiverOptions = opts_new(); + quiverOptions = opts_add(quiverOptions, 'u', '\thisrow{u}'); + quiverOptions = opts_add(quiverOptions, 'v', '\thisrow{v}'); + if is3D + quiverOptions = opts_add(quiverOptions, 'w', '\thisrow{w}'); + arrowLength = '{sqrt((\thisrow{u})^2+(\thisrow{v})^2+(\thisrow{w})^2)}'; + else + arrowLength = '{sqrt((\thisrow{u})^2+(\thisrow{v})^2)}'; + end + plotOptions = opts_add(plotOptions, 'point meta', arrowLength); + plotOptions = opts_add(plotOptions, 'point meta min', '0'); + + if showArrowHead + arrowHeadOptions = opts_new(); + + % In MATLAB (HG1), the arrow head is constructed to have an angle of + % approximately 18.263 degrees in 2D as can be derived from the + % |quiver| function. + % In 3D, the angle is no longer constant but it is approximately + % the same as for 2D quiver plots. So let's make our life easy. + % |test/examples/example_quivers.m| covers the calculations. + arrowHeadOptions = opts_add(arrowHeadOptions, 'angle''', '18.263'); + + %TODO: scale the arrows more rigorously to match MATLAB behavior + % Currently, this is quite hard to do, since the size of the arrows + % is defined in pgfplots in absolute units (here we specify that those + % should be scaled up/down according to the data), while the data itself + % is in axis coordinates (or some scaled variant). I.e. we need the + % physical dimensions of the axis to compute the correct scaling! + % + % There is a "MaxHeadSize" property that plays a role. + % MaxHeadSize is said to be relative to the length of the quiver in the + % MATLAB documentation. However, in practice, there seems to be a SQRT + % involved somewhere (e.g. if u.^2 + v.^2 == 2, all MHS values > + % 1/sqrt(2) are capped to 1/sqrt(2)). + % + % NOTE: `set(h, 'MaxHeadSize')` is bugged in HG1 (not in HG2 or Octave) + % according to http://www.mathworks.com/matlabcentral/answers/96754 + + userInfo(m2t, ['Please change the "arrowHeadSize" option', ... + ' if the size of the arrows is incorrect.']); + arrowHeadSize = sprintf(m2t.ff, abs(m2t.args.arrowHeadSize)); + + % Write out the actual scaling for TikZ. + % `\pgfplotspointsmetatransformed` is in the range [0, 1000], so + % divide by this span (as is done in the pgfplots manual) to normalize + % the arrow head size. First divide to avoid overflows. + arrowHeadOptions = opts_add(arrowHeadOptions, 'scale', ... + ['{' arrowHeadSize '/1000*\pgfplotspointmetatransformed}']); + + headStyle = ['-{Straight Barb[' opts_print(arrowHeadOptions) ']}']; + quiverOptions = opts_add(quiverOptions, 'every arrow/.append style', ... + ['{' headStyle '}']); + end + plotOptions = opts_addSubOpts(plotOptions, 'quiver', quiverOptions); + plotOptions = opts_append_userdefined(plotOptions, custom.extraOptions); + + [m2t, table, tableOptions] = makeTable(m2t, variables, data); + + % Print out + plotOpts = opts_print(plotOptions); + tabOpts = opts_print(tableOptions); + str = sprintf('\\%s[%s]\n table[%s] {%s};\n', ... + name, plotOpts, tabOpts, table); +end +% ============================================================================== +function [x,y,z,u,v,w] = getAndRescaleQuivers(m2t, h) + % get and rescale the arrows from a quivergroup object + x = get(h, 'XData'); + y = get(h, 'YData'); + z = getOrDefault(h, 'ZData', []); + + u = get(h, 'UData'); + v = get(h, 'VData'); + w = getOrDefault(h, 'WData', []); + + is3D = m2t.axes{end}.is3D; + if ~is3D + z = 0; + w = 0; + end + + % MATLAB uses a scaling algorithm to determine the size of the arrows. + % Before R2014b, the processed coordinates were available. This is no longer + % the case, so we have to re-implement it. In MATLAB it is implemented in + % the |quiver3| (and |quiver|) function. + if any(size(x)==1) + nX = sqrt(numel(x)); nY = nX; + else + [nY, nX] = size(x); + end + range = @(xyzData)(max(xyzData(:)) - min(xyzData(:))); + euclid = @(x,y,z)(sqrt(x.^2 + y.^2 + z.^2)); + dx = range(x)/nX; + dy = range(y)/nY; + dz = range(z)/max(nX,nY); + dd = euclid(dx, dy, dz); + if dd > 0 + vectorLength = euclid(u/dd,v/dd,w/dd); + maxLength = max(vectorLength(:)); + else + maxLength = 1; + end + if isOn(getOrDefault(h, 'AutoScale', 'on')) + scaleFactor = getOrDefault(h,'AutoScaleFactor', 0.9) / maxLength; + else + scaleFactor = 1; + end + x = x(:).'; u = u(:).'*scaleFactor; + y = y(:).'; v = v(:).'*scaleFactor; + z = z(:).'; w = w(:).'*scaleFactor; +end +% ============================================================================== +function [m2t, str] = drawErrorBars(m2t, h, custom) + % Takes care of MATLAB/Octave's error bar plots. + [m2t, str] = drawLine(m2t, h, custom); + % Even though this only calls |drawLine|, let's keep this wrapper + % such that the code is easier to read where it is called. +end +% ============================================================================== +function [xDeviations] = getXDeviations(h) + % Retrieves left/right uncertainty data + + rightDev = getOrDefault(h, 'XPositiveDelta', []); + leftDev = getOrDefault(h, 'XNegativeDelta', []); + + xDeviations = [rightDev(:), leftDev(:)]; +end +% ============================================================================== +function [yDeviations] = getYDeviations(h) + % Retrieves upper/lower uncertainty data + + upDev = getOrDefault(h, 'YPositiveDelta', []); + loDev = getOrDefault(h, 'YNegativeDelta', []); + + if isempty(upDev) + upDev = getOrDefault(h, 'UData', []); + loDev = getOrDefault(h, 'LData', []); + end + + yDeviations = [upDev(:), loDev(:)]; +end +% ============================================================================== +function [m2t, str] = drawEllipse(m2t, handle, custom) + % Takes care of MATLAB's ellipse annotations. + + drawOptions = opts_new(); + + p = get(handle,'position'); + radius = p([3 4]) / 2; + center = p([1 2]) + radius; + + color = get(handle, 'Color'); + [m2t, xcolor] = getColor(m2t, handle, color, 'patch'); + [m2t, lineOptions] = getLineOptions(m2t, handle); + + filling = get(handle, 'FaceColor'); + + % Has a filling? + if isNone(filling) + drawOptions = opts_add(drawOptions, xcolor); + drawCommand = '\draw'; + else + [m2t, xcolorF] = getColor(m2t, handle, filling, 'patch'); + drawOptions = opts_add(drawOptions, 'draw', xcolor); + drawOptions = opts_add(drawOptions, 'fill', xcolorF); + + drawCommand = '\filldraw'; + end + drawOptions = opts_merge(drawOptions, lineOptions); + drawOptions = opts_append_userdefined(drawOptions, custom.extraOptions); + + opt = opts_print(drawOptions); + + str = sprintf('%s [%s] (axis cs:%g,%g) ellipse [x radius=%g, y radius=%g];\n', ... + drawCommand, opt, center, radius); +end +% ============================================================================== +function [m2t, str] = drawTextarrow(m2t, handle, custom) + % Takes care of MATLAB's textarrow annotations. + + % handleAllChildren to draw the arrow + [m2t, str] = handleAllChildren(m2t, handle); + + % handleAllChildren ignores the text, unless hidden strings are shown + if ~m2t.args.showHiddenStrings + child = findall(handle, 'type', 'text'); + [m2t, str{end+1}] = drawText(m2t, child, custom); + end +end +% ============================================================================== +function [m2t, drawOptions] = getPatchDrawOptions(m2t, h, drawOptions) + % Determines the reoccurring draw options usually applied when drawing + % a patch/area/bar. These include EdgeColor, LineType, FaceColor/Alpha + + % Get object for color; + if ~isempty(allchild(h)) + % quite oddly, before MATLAB R2014b this value is stored in a child + % patch and not in the object itself + obj = allchild(h); + else % R2014b and newer + obj = h; + end + + % Get the object type + type = get(h, 'Type'); + + % Face Color (inside of area) + faceColor = get(obj, 'FaceColor'); + [m2t, drawOptions] = setColor(m2t, h, drawOptions, 'fill', faceColor, 'none'); + + % FaceAlpha (Not applicable for MATLAB2014a/b) + faceAlpha = getOrDefault(h, 'FaceAlpha', 'none'); + if ~isNone(faceColor) && isnumeric(faceAlpha) && faceAlpha ~= 1.0 + drawOptions = opts_add(drawOptions, 'fill opacity', sprintf(m2t.ff,faceAlpha)); + end + + % Define linestyle + [lineStyle, isDefaultLS] = getAndCheckDefault(type, h, 'LineStyle', '-'); + if isNone(lineStyle) + drawOptions = opts_add(drawOptions, 'draw', 'none'); + elseif ~isDefaultLS + drawOptions = opts_add(drawOptions, translateLineStyle(lineStyle)); + end + + % Check for the edge color. Only plot it if it is different from the + % face color and if there is a linestyle + edgeColor = get(h, 'EdgeColor'); + if ~isNone(lineStyle) && ~isNone(edgeColor) && ~strcmpi(edgeColor,faceColor) + [m2t, drawOptions] = setColor(m2t, h, drawOptions, 'draw', edgeColor, 'none'); + end + + % Add 'area legend' to the options as otherwise the legend indicators + % will just highlight the edges. + if strcmpi(type, 'bar') || strcmpi(type, 'histogram') + drawOptions = opts_add(drawOptions, 'area legend'); + end +end +% ============================================================================== +function out = linearFunction(X, Y) + % Return the linear function that goes through (X[1], Y[1]), (X[2], Y[2]). + out = @(x) (Y(2,:)*(x-X(1)) + Y(1,:)*(X(2)-x)) / (X(2)-X(1)); +end +% ============================================================================== +function matlabColormap = pgfplots2matlabColormap(points, rgb, numColors) + % Translates a Pgfplots colormap to a MATLAB color map. + matlabColormap = zeros(numColors, 3); + % Point indices between which to interpolate. + I = [1, 2]; + f = linearFunction(points(I), rgb(I,:)); + for k = 1:numColors + x = (k-1)/(numColors-1) * points(end); + if x > points(I(2)) + I = I + 1; + f = linearFunction(points(I), rgb(I,:)); + end + matlabColormap(k,:) = f(x); + end +end +% ============================================================================== +function pgfplotsColormap = matlab2pgfplotsColormap(m2t, matlabColormap, name) + % Translates a MATLAB color map into a Pgfplots colormap. + + if nargin < 3 || isempty(name), name = 'mymap'; end + + % First check if we could use a default Pgfplots color map. + % Unfortunately, MATLAB and Pgfplots color maps will never exactly coincide + % except to the most simple cases such as blackwhite. This is because of a + % slight incompatibility of Pgfplots and MATLAB colormaps: + % In MATLAB, indexing goes from 1 through 64, whereas in Pgfplots you can + % specify any range, the default ones having something like + % (0: red, 1: yellow, 2: blue). + % To specify this exact color map in MATLAB, one would have to put 'red' at + % 1, blue at 64, and yellow in the middle of the two, 32.5 that is. + % Not really sure how MATLAB rounds here: 32, 33? Anyways, it will be + % slightly off and hence not match the Pgfplots color map. + % As a workaround, build the MATLAB-formatted colormaps of Pgfplots default + % color maps, and check if matlabColormap is close to it. If yes, take it. + + % For now, comment out the color maps which haven't landed yet in Pgfplots. + pgfmaps = { %struct('name', 'colormap/autumn', ... + % 'points', [0,1], ... + % 'values', [[1,0,0];[1,1,0]]), ... + %struct('name', 'colormap/bled', ... + % 'points', 0:6, ... + % 'values', [[0,0,0];[43,43,0];[0,85,0];[0,128,128];[0,0,170];[213,0,213];[255,0,0]]/255), ... + %struct('name', 'colormap/bright', ... + % 'points', 0:7, ... + % 'values', [[0,0,0];[78,3,100];[2,74,255];[255,21,181];[255,113,26];[147,213,114];[230,255,0];[255,255,255]]/255), ... + %struct('name', 'colormap/bone', ... + % 'points', [0,3,6,8], ... + % 'values', [[0,0,0];[84,84,116];[167,199,199];[255,255,255]]/255), ... + %struct('name', 'colormap/cold', ... + % 'points', 0:3, ... + % 'values', [[0,0,0];[0,0,1];[0,1,1];[1,1,1]]), ... + %struct('name', 'colormap/copper', ... + % 'points', [0,4,5], ... + % 'values', [[0,0,0];[255,159,101];[255,199,127]]/255), ... + %struct('name', 'colormap/copper2', ... + % 'points', 0:4, ... + % 'values', [[0,0,0];[68,62,63];[170,112,95];[207,194,138];[255,255,255]]/255), ... + %struct('name', 'colormap/hsv', ... + % 'points', 0:6, ... + % 'values', [[1,0,0];[1,1,0];[0,1,0];[0,1,1];[0,0,1];[1,0,1];[1,0,0]]), ... + struct('name', 'colormap/hot', ... + 'points', 0:3, ... + 'values', [[0,0,1];[1,1,0];[1,0.5,0];[1,0,0]]), ... % TODO check this + struct('name', 'colormap/hot2', ... + 'points', [0,3,6,8], ... + 'values', [[0,0,0];[1,0,0];[1,1,0];[1,1,1]]), ... + struct('name', 'colormap/jet', ... + 'points', [0,1,3,5,7,8], ... + 'values', [[0,0,128];[0,0,255];[0,255,255];[255,255,0];[255,0,0];[128,0,0]]/255), ... + struct('name', 'colormap/blackwhite', ... + 'points', [0,1], ... + 'values', [[0,0,0];[1,1,1]]), ... + struct('name', 'colormap/bluered', ... + 'points', 0:5, ... + 'values', [[0,0,180];[0,255,255];[100,255,0];[255,255,0];[255,0,0];[128,0,0]]/255), ... + struct('name', 'colormap/cool', ... + 'points', [0,1,2], ... + 'values', [[255,255,255];[0,128,255];[255,0,255]]/255), ... + struct('name', 'colormap/greenyellow', ... + 'points', [0,1], ... + 'values', [[0,128,0];[255,255,0]]/255), ... + struct('name', 'colormap/redyellow', ... + 'points', [0,1], ... + 'values', [[255,0,0];[255,255,0]]/255), ... + struct('name', 'colormap/violet', ... + 'points', [0,1,2], ... + 'values', [[25,25,122];[255,255,255];[238,140,238]]/255) ... + }; + + % The tolerance is a subjective matter of course. + % Some figures: + % * The norm-distance between MATLAB's gray and bone is 6.8e-2. + % * The norm-distance between MATLAB's jet and Pgfplots's jet is 2.8e-2. + % * The norm-distance between MATLAB's hot and Pgfplots's hot2 is 2.1e-2. + tol = 5.0e-2; + + for map = pgfmaps + numColors = size(matlabColormap, 1); + mmap = pgfplots2matlabColormap(map{1}.points, map{1}.values, numColors); + alpha = norm(matlabColormap - mmap) / sqrt(numColors); + if alpha < tol + userInfo(m2t, 'Found %s to be a pretty good match for your color map (||diff||=%g).', ... + map{1}.name, alpha); + pgfplotsColormap = map{1}.name; + return + end + end + + % Build a custom color map. + % Loop over the data, stop at each spot where the linear + % interpolation is interrupted, and set a color mark there. + m = size(matlabColormap, 1); + steps = [1, 2]; + % A colormap with a single color is valid in MATLAB but an error in + % pgfplots. Repeating the color produces the desired effect in this + % case. + if m==1 + colors=[matlabColormap(1,:);matlabColormap(1,:)]; + else + colors = [matlabColormap(1,:); matlabColormap(2,:)]; + f = linearFunction(steps, colors); + k = 3; + while k <= m + if norm(matlabColormap(k,:) - f(k)) > 1.0e-10 + % Add the previous step to the color list + steps(end) = k-1; + colors(end,:) = matlabColormap(k-1,:); + steps = [steps, k]; + colors = [colors; matlabColormap(k,:)]; + f = linearFunction(steps(end-1:end), colors(end-1:end,:)); + end + k = k+1; + end + steps(end) = m; + colors(end,:) = matlabColormap(m,:); + end + + % Get it in Pgfplots-readable form. + unit = 'pt'; + colSpecs = cell(length(steps), 1); + for k = 1:length(steps) + x = steps(k)-1; + colSpecs{k} = sprintf('rgb(%d%s)=(%g,%g,%g)', x, unit, colors(k,:)); + end + pgfplotsColormap = sprintf('colormap={%s}{[1%s] %s}',name, unit, join(m2t, colSpecs, '; ')); +end +% ============================================================================== +function [m2t, fontStyle] = getFontStyle(m2t, handle) + fontStyle = ''; + if strcmpi(get(handle, 'FontWeight'),'Bold') + fontStyle = sprintf('%s\\bfseries',fontStyle); + end + if strcmpi(get(handle, 'FontAngle'), 'Italic') + fontStyle = sprintf('%s\\itshape',fontStyle); + end + if ~all(get(handle, 'Color')==0) + color = get(handle, 'Color'); + [m2t, col] = getColor(m2t, handle, color, 'patch'); + fontStyle = sprintf('%s\\color{%s}', fontStyle, col); + end + if m2t.args.strictFontSize + fontSize = get(handle,'FontSize'); + fontUnits = matlab2texUnits(get(handle,'FontUnits'), 'pt'); + fontStyle = sprintf('\\fontsize{%d%s}{1em}%s\\selectfont',fontSize,fontUnits,fontStyle); + else + % don't try to be smart and "translate" MATLAB font sizes to proper LaTeX + % ones: it cannot be done. LaTeX uses semantic sizes (e.g. \small) + % whose actual dimensions depend on the document style, context, ... + end + + if ~isempty(fontStyle) + fontStyle = opts_add(opts_new, 'font', fontStyle); + else + fontStyle = opts_new(); + end +end +% ============================================================================== +function axisOptions = getColorbarOptions(m2t, handle) + + % begin collecting axes options + axisOptions = opts_new(); + cbarStyleOptions = opts_new(); + + [cbarTemplate, cbarStyleOptions] = getColorbarPosOptions(handle, ... + cbarStyleOptions); + + % axis label and direction + if isHG2 + % VERSION: Starting from R2014b there is only one field `label`. + % The colorbar's position determines, if it should be a x- or y-label. + + if strcmpi(cbarTemplate, 'horizontal') + labelOption = 'xlabel'; + else + labelOption = 'ylabel'; + end + [m2t, cbarStyleOptions] = getLabel(m2t, handle, cbarStyleOptions, labelOption); + + % direction + dirString = get(handle, 'Direction'); + if ~strcmpi(dirString, 'normal') % only if not 'normal' + if strcmpi(cbarTemplate, 'horizontal') + dirOption = 'x dir'; + else + dirOption = 'y dir'; + end + cbarStyleOptions = opts_add(cbarStyleOptions, dirOption, dirString); + end + + % TODO HG2: colorbar ticks and colorbar tick labels + + else + % VERSION: Up to MATLAB R2014a and OCTAVE + [m2t, xo] = getAxisOptions(m2t, handle, 'x'); + [m2t, yo] = getAxisOptions(m2t, handle, 'y'); + xyo = opts_merge(xo, yo); + xyo = opts_remove(xyo, 'xmin','xmax','xtick','ymin','ymax','ytick'); + + cbarStyleOptions = opts_merge(cbarStyleOptions, xyo); + end + + % title + [m2t, cbarStyleOptions] = getTitle(m2t, handle, cbarStyleOptions); + + if m2t.args.strict + % Sampled colors. + numColors = size(m2t.current.colormap, 1); + axisOptions = opts_add(axisOptions, 'colorbar sampled'); + cbarStyleOptions = opts_add(cbarStyleOptions, 'samples', ... + sprintf('%d', numColors+1)); + + if ~isempty(cbarTemplate) + userWarning(m2t, ... +- 'Pgfplots cannot deal with more than one colorbar option yet.'); + %FIXME: can we get sampled horizontal color bars to work? + %FIXME: sampled colorbars should be inferred, not by using strict! + end + end + + % Merge them together in axisOptions. + axisOptions = opts_add(axisOptions, strtrim(['colorbar ', cbarTemplate])); + + if ~isempty(cbarStyleOptions) + axisOptions = opts_addSubOpts(axisOptions, ... + 'colorbar style', cbarStyleOptions); + end + + % do _not_ handle colorbar's children +end +% ============================================================================== +function [cbarTemplate, cbarStyleOptions] = getColorbarPosOptions(handle, cbarStyleOptions) + % set position, ticks etc. of a colorbar + loc = get(handle, 'Location'); + cbarTemplate = ''; + + switch lower(loc) % case insensitive (MATLAB: CamelCase, Octave: lower case) + case 'north' + cbarTemplate = 'horizontal'; + cbarStyleOptions = opts_add(cbarStyleOptions, 'at',... + '{(0.5,0.97)}'); + cbarStyleOptions = opts_add(cbarStyleOptions, 'anchor',... + 'north'); + cbarStyleOptions = opts_add(cbarStyleOptions,... + 'xticklabel pos', 'lower'); + cbarStyleOptions = opts_add(cbarStyleOptions, 'width',... + '0.97*\pgfkeysvalueof{/pgfplots/parent axis width}'); + case 'south' + cbarTemplate = 'horizontal'; + cbarStyleOptions = opts_add(cbarStyleOptions, 'at',... + '{(0.5,0.03)}'); + cbarStyleOptions = opts_add(cbarStyleOptions, 'anchor', ... + 'south'); + cbarStyleOptions = opts_add(cbarStyleOptions, ... + 'xticklabel pos','upper'); + cbarStyleOptions = opts_add(cbarStyleOptions, 'width',... + '0.97*\pgfkeysvalueof{/pgfplots/parent axis width}'); + case 'east' + cbarTemplate = 'right'; + cbarStyleOptions = opts_add(cbarStyleOptions, 'at',... + '{(0.97,0.5)}'); + cbarStyleOptions = opts_add(cbarStyleOptions, 'anchor', ... + 'east'); + cbarStyleOptions = opts_add(cbarStyleOptions, ... + 'xticklabel pos','left'); + cbarStyleOptions = opts_add(cbarStyleOptions, 'width',... + '0.97*\pgfkeysvalueof{/pgfplots/parent axis width}'); + case 'west' + cbarTemplate = 'left'; + cbarStyleOptions = opts_add(cbarStyleOptions, 'at',... + '{(0.03,0.5)}'); + cbarStyleOptions = opts_add(cbarStyleOptions, 'anchor',... + 'west'); + cbarStyleOptions = opts_add(cbarStyleOptions,... + 'xticklabel pos', 'right'); + cbarStyleOptions = opts_add(cbarStyleOptions, 'width',... + '0.97*\pgfkeysvalueof{/pgfplots/parent axis width}'); + case 'eastoutside' + %cbarTemplate = 'right'; + case 'westoutside' + cbarTemplate = 'left'; + case 'northoutside' + % TODO move to top + cbarTemplate = 'horizontal'; + cbarStyleOptions = opts_add(cbarStyleOptions, 'at',... + '{(0.5,1.03)}'); + cbarStyleOptions = opts_add(cbarStyleOptions, 'anchor',... + 'south'); + cbarStyleOptions = opts_add(cbarStyleOptions,... + 'xticklabel pos', 'upper'); + case 'southoutside' + cbarTemplate = 'horizontal'; + case 'manual' + origUnits = get(handle,'Units'); + assocAxes = get(handle,'Axes'); + origAxesUnits = get(assocAxes,'Units'); + set(handle,'Units','centimeters'); % Make sure we have + set(assocAxes,'Units','centimeters'); % same units + cbarDim = pos2dims(get(handle,'Position')); + cbarAxesDim = pos2dims(get(assocAxes,'Position')); + set(handle,'Units',origUnits); % Restore original + set(assocAxes,'Units',origAxesUnits); % units + + center = @(dims) (dims.left + dims.right)/2; + centerCbar = center(cbarDim); + centerAxes = center(cbarAxesDim); + + % Cases of colorbar axis locations (in or out) depending on center + % of colorbar relative to the center it's associated axes. + % According to matlab manual (R2016a) colorbars with Location 'manual' + % can only be vertical. + axisLoc = getOrDefault(handle, 'AxisLocation', 'out'); + if centerCbar < centerAxes + if strcmp(axisLoc,'in') + cbarTemplate = 'right'; + else + cbarTemplate = 'left'; + end + else + if strcmp(axisLoc,'in') + cbarTemplate = 'left'; + else + cbarTemplate = 'right'; + end + end + + % Using positions relative to associated axes + calcRelPos = @(pos1,pos2,ext2) (pos1-pos2)/ext2; + cbarRelPosX = calcRelPos(cbarDim.left,cbarAxesDim.left,cbarAxesDim.width); + cbarRelPosY = calcRelPos(cbarDim.bottom,cbarAxesDim.bottom,cbarAxesDim.height); + cbarRelHeight = cbarDim.height/cbarAxesDim.height; + + cbarStyleOptions = opts_add(cbarStyleOptions, 'anchor',... + 'south west'); + cbarStyleOptions = opts_add(cbarStyleOptions, 'at',... + ['{(' formatDim(cbarRelPosX) ','... + formatDim(cbarRelPosY) ')}']); + cbarStyleOptions = opts_add(cbarStyleOptions, 'height',... + [formatDim(cbarRelHeight),... + '*\pgfkeysvalueof{/pgfplots/parent axis height}']); + + otherwise + error('matlab2tikz:getColorOptions:unknownLocation',... + 'getColorbarOptions: Unknown ''Location'' %s.', loc) + end +end +% ============================================================================== +function [m2t, xcolor] = getColor(m2t, handle, color, mode) + % Handles MATLAB colors and makes them available to TikZ. + % This includes translation of the color value as well as explicit + % definition of the color if it is not available in TikZ by default. + % + % The variable 'mode' essentially determines what format 'color' can + % have. Possible values are (as strings) 'patch' and 'image'. + + % check if the color is straight given in rgb + % -- notice that we need the extra NaN test with respect to the QUIRK + % below + if isRGBTuple(color) + % everything alright: rgb color here + [m2t, xcolor] = rgb2colorliteral(m2t, color); + else + switch lower(mode) + case 'patch' + [m2t, xcolor] = patchcolor2xcolor(m2t, color, handle); + case 'image' + + m = size(color,1); + n = size(color,2); + xcolor = cell(m, n); + + if ndims(color) == 3 + for i = 1:m + for j = 1:n + [m2t, xc] = rgb2colorliteral(m2t, color(i,j, :)); + xcolor{i, j} = xc; + end + end + elseif ndims(color) <= 2 + [m2t, colorindex] = cdata2colorindex(m2t, color, handle); + for i = 1:m + for j = 1:n + [m2t, xc] = rgb2colorliteral(m2t, m2t.current.colormap(colorindex(i,j), :)); + xcolor{i, j} = xc; + end + end + else + error('matlab2tikz:getColor:image:colorDims',... + 'Image color data cannot have more than 3 dimensions'); + end + otherwise + error(['matlab2tikz:getColor', ... + 'Argument ''mode'' has illegal value ''%s''.'], ... + mode); + end + end +end +% ============================================================================== +function [m2t, xcolor] = patchcolor2xcolor(m2t, color, patchhandle) + % Transforms a color of the edge or the face of a patch to an xcolor literal. + if isnumeric(color) + [m2t, xcolor] = rgb2colorliteral(m2t, color); + elseif ischar(color) + switch color + case 'flat' + cdata = getCDataWithFallbacks(patchhandle); + color1 = cdata(1,1); + % RGB cdata + if ndims(cdata) == 3 && all(size(cdata) == [1,1,3]) + [m2t,xcolor] = rgb2colorliteral(m2t, cdata); + % All same color + elseif all(isnan(cdata) | abs(cdata-color1)<1.0e-10) + [m2t, colorindex] = cdata2colorindex(m2t, color1, patchhandle); + [m2t, xcolor] = rgb2colorliteral(m2t, m2t.current.colormap(colorindex, :)); + else + % Don't return anything meaningful and count on the caller + % to make something of it. + xcolor = []; + end + + case 'auto' + try + color = get(patchhandle, 'Color'); + catch + % From R2014b use an undocumented property if Color is + % not present + color = get(patchhandle, 'AutoColor'); + end + [m2t, xcolor] = rgb2colorliteral(m2t, color); + + case 'none' + % Before, we used to throw an error here. However, probably this + % is not necessary and actually harmful (#739). + xcolor = 'none'; + + otherwise + error('matlab2tikz:anycolor2rgb:UnknownColorModel',... + 'Don''t know how to handle the color model ''%s''.',color); + end + else + error('patchcolor2xcolor:illegalInput', ... + 'Input argument ''color'' not a string or numeric.'); + end +end +% ============================================================================== +function cdata = getCDataWithFallbacks(patchhandle) + % Looks for CData at different places + cdata = getOrDefault(patchhandle, 'CData', []); + + if isempty(cdata) || ~isnumeric(cdata) + child = allchild(patchhandle); + cdata = get(child, 'CData'); + end + if isempty(cdata) || ~isnumeric(cdata) + % R2014b+: CData is implicit by the ordering of the siblings + siblings = allchild(get(patchhandle, 'Parent')); + cdata = find(siblings(end:-1:1)==patchhandle); + end +end +% ============================================================================== +function [m2t, colorindex] = cdata2colorindex(m2t, cdata, imagehandle) + % Transforms a color in CData format to an index in the color map. + % Only does something if CDataMapping is 'scaled', really. + + if ~isnumeric(cdata) && ~islogical(cdata) + error('matlab2tikz:cdata2colorindex:unknownCDataType',... + 'Don''t know how to handle CData ''%s''.',cdata); + end + + axeshandle = m2t.current.gca; + + % ----------------------------------------------------------------------- + % For the following, see, for example, the MATLAB help page for 'image', + % section 'Image CDataMapping'. + try + mapping = get(imagehandle, 'CDataMapping'); + catch + mapping = 'scaled'; + end + switch mapping + case 'scaled' + % need to scale within clim + % see MATLAB's manual page for caxis for details + clim = get(axeshandle, 'clim'); + m = size(m2t.current.colormap, 1); + colorindex = zeros(size(cdata)); + idx1 = cdata <= clim(1); + idx2 = cdata >= clim(2); + idx3 = ~idx1 & ~idx2; + colorindex(idx1) = 1; + colorindex(idx2) = m; + % cdata may be of type uint8. Convert to double to avoid + % getting binary indices + colorindex(idx3) = fix(double(cdata(idx3)-clim(1)) / (clim(2)-clim(1)) *m) ... + + 1; + case 'direct' + % direct index + colorindex = cdata; + + otherwise + error('matlab2tikz:anycolor2rgb:unknownCDataMapping',... + 'Unknown CDataMapping ''%s''.',cdatamapping); + end +end +% ============================================================================== +function [m2t, key, legendOpts] = getLegendOpts(m2t, handle) + lStyle = opts_new(); + + lStyle = getLegendPosition(m2t, handle, lStyle); + lStyle = getLegendOrientation(m2t, handle, lStyle); + lStyle = getLegendEntryAlignment(m2t, handle, lStyle); + + % If the plot has 'legend boxoff', we have the 'not visible' + % property, so turn off line and background fill. + if ~isVisible(handle) || isOff(get(handle,'box')) + lStyle = opts_add(lStyle, 'fill', 'none'); + lStyle = opts_add(lStyle, 'draw', 'none'); + else + % handle colors + [edgeColor, isDfltEdge] = getAndCheckDefault('Legend', handle, ... + 'EdgeColor', [1 1 1]); + if isNone(edgeColor) + lStyle = opts_add(lStyle, 'draw', 'none'); + + elseif ~isDfltEdge + [m2t, col] = getColor(m2t, handle, edgeColor, 'patch'); + lStyle = opts_add(lStyle, 'draw', col); + end + + [fillColor, isDfltFill] = getAndCheckDefault('Legend', handle, ... + 'Color', [1 1 1]); + if isNone(fillColor) + lStyle = opts_add(lStyle, 'fill', 'none'); + + elseif ~isDfltFill + [m2t, col] = getColor(m2t, handle, fillColor, 'patch'); + lStyle = opts_add(lStyle, 'fill', col); + end + end + % - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + key = 'legend style'; + legendOpts = opts_print(lStyle); + legendOpts = ['{', legendOpts, '}']; + %TODO: just pass out the `lStyle` instead of `legendOpts` +end +% ============================================================================== +function [lStyle] = getLegendOrientation(m2t, handle, lStyle) + % handle legend orientation + ori = get(handle, 'Orientation'); + switch lower(ori) + case 'horizontal' + numLegendEntries = sprintf('%d',length(get(handle, 'String'))); + lStyle = opts_add(lStyle, 'legend columns', numLegendEntries); + + case 'vertical' + % Use default. + otherwise + userWarning(m2t, [' Unknown legend orientation ''',ori,'''' ... + '. Choosing default (vertical).']); + end +end +% ============================================================================== +function [lStyle] = getLegendPosition(m2t, handle, lStyle) + % handle legend location + % #COMPLEX: just a big switch-case + loc = get(handle, 'Location'); + dist = 0.03; % distance to to axes in normalized coordinates + % MATLAB(R)'s keywords are camel cased (e.g., 'NorthOutside'), in Octave + % small cased ('northoutside'). Hence, use lower() for uniformity. + switch lower(loc) + case 'northeast' + return % don't do anything in this (default) case + case 'northwest' + position = [dist, 1-dist]; + anchor = 'north west'; + case 'southwest' + position = [dist, dist]; + anchor = 'south west'; + case 'southeast' + position = [1-dist, dist]; + anchor = 'south east'; + case 'north' + position = [0.5, 1-dist]; + anchor = 'north'; + case 'east' + position = [1-dist, 0.5]; + anchor = 'east'; + case 'south' + position = [0.5, dist]; + anchor = 'south'; + case 'west' + position = [dist, 0.5]; + anchor = 'west'; + case 'northoutside' + position = [0.5, 1+dist]; + anchor = 'south'; + case 'southoutside' + position = [0.5, -dist]; + anchor = 'north'; + case 'eastoutside' + position = [1+dist, 0.5]; + anchor = 'west'; + case 'westoutside' + position = [-dist, 0.5]; + anchor = 'east'; + case 'northeastoutside' + position = [1+dist, 1]; + anchor = 'north west'; + case 'northwestoutside' + position = [-dist, 1]; + anchor = 'north east'; + case 'southeastoutside' + position = [1+dist, 0]; + anchor = 'south west'; + case 'southwestoutside' + position = [-dist, 0]; + anchor = 'south east'; + case 'none' + legendPos = get(handle, 'Position'); + unit = get(handle, 'Units'); + if isequal(unit, 'normalized') + position = legendPos(1:2); + else + % Calculate where the legend is located w.r.t. the axes. + axesPos = get(m2t.current.gca, 'Position'); + axesUnit = get(m2t.current.gca, 'Units'); + % Convert to legend unit + axesPos = convertUnits(axesPos, axesUnit, unit); + % By default, the axes position is given w.r.t. to the figure, + % and so is the legend. + position = (legendPos(1:2)-axesPos(1:2)) ./ axesPos(3:4); + end + anchor = 'south west'; + case {'best','bestoutside'} + % TODO: Implement these. + % The position could be determined by means of 'Position' and/or + % 'OuterPosition' of the legend handle; in fact, this could be made + % a general principle for all legend placements. + userWarning(m2t, [sprintf(' Option ''%s'' not yet implemented.',loc), ... + ' Choosing default.']); + return % use defaults + + otherwise + userWarning(m2t, [' Unknown legend location ''',loc,'''' ... + '. Choosing default.']); + return % use defaults + end + + % set legend position + %TODO: shouldn't this include units? + lStyle = opts_add(lStyle, 'at', sprintf('{(%s,%s)}', ... + formatDim(position(1)), formatDim(position(2)))); + lStyle = opts_add(lStyle, 'anchor', anchor); + +end +% ============================================================================== +function [lStyle] = getLegendEntryAlignment(m2t, handle, lStyle) + % determines the text and picture alignment inside a legend + textalign = ''; + pictalign = ''; + switch getEnvironment + case 'Octave' + % Octave allows to change the alignment of legend text and + % pictograms using legend('left') and legend('right') + textpos = get(handle, 'textposition'); + switch lower(textpos) + case 'left' + % pictogram right of flush right text + textalign = 'right'; + pictalign = 'right'; + case 'right' + % pictogram left of flush left text (default) + textalign = 'left'; + pictalign = 'left'; + otherwise + userWarning(m2t, ... + ['Unknown legend text position ''',... + textpos, '''. Choosing default.']); + end + case 'MATLAB' + % does not specify text/pictogram alignment in legends + otherwise + errorUnknownEnvironment(); + end + + % set alignment of legend text and pictograms, if available + if ~isempty(textalign) && ~isempty(pictalign) + lStyle = opts_add(lStyle, 'legend cell align', textalign); + lStyle = opts_add(lStyle, 'align', textalign); + lStyle = opts_add(lStyle, 'legend plot pos', pictalign); + else + % Make sure the entries are flush left (default MATLAB behavior). + % This is also import for multiline legend entries: Without alignment + % specification, the TeX document won't compile. + % 'legend plot pos' is not set explicitly, since 'left' is default. + lStyle = opts_add(lStyle, 'legend cell align', 'left'); + lStyle = opts_add(lStyle, 'align', 'left'); + end +end +% ============================================================================== +function [pTicks, pTickLabels] = ... + matlabTicks2pgfplotsTicks(m2t, ticks, tickLabels, isLogAxis, tickLabelMode) + % Converts MATLAB style ticks and tick labels to pgfplots style (if needed) + if isempty(ticks) + pTicks = '\empty'; + pTickLabels = []; + return + end + + % set ticks + labels + pTicks = join(m2t, num2cell(ticks), ','); + + % if there's no specific labels, return empty + if isempty(tickLabels) || (length(tickLabels)==1 && isempty(tickLabels{1})) + pTickLabels = '\empty'; + return + end + + % sometimes tickLabels are cells, sometimes plain arrays + % -- unify this to cells + if ischar(tickLabels) + tickLabels = strtrim(mat2cell(tickLabels, ... + ones(size(tickLabels,1), 1), ... + size(tickLabels, 2) ... + ) ... + ); + end + + ticks = removeSuperfluousTicks(ticks, tickLabels); + + isNeeded = isTickLabelsNecessary(m2t, ticks, tickLabels, isLogAxis); + + pTickLabels = formatPgfTickLabels(m2t, isNeeded, tickLabels, ... + isLogAxis, tickLabelMode); +end +% ============================================================================== +function bool = isTickLabelsNecessary(m2t, ticks, tickLabels, isLogAxis) + % Check if tickLabels are really necessary (and not already covered by + % the tick values themselves). + bool = false; + + k = find(ticks ~= 0.0, 1); % get an index with non-zero tick value + if isLogAxis || isempty(k) % only a 0-tick + scalingFactor = 1; + else + % When plotting axis, MATLAB might scale the axes by a factor of ten, + % say 10^n, and plot a 'x 10^k' next to the respective axis. This is + % common practice when the tick marks are really large or small + % numbers. + % Unfortunately, MATLAB doesn't contain the information about the + % scaling anywhere in the plot, and at the same time the {x,y}TickLabels + % are given as t*10^k, thus no longer corresponding to the actual + % value t. + % Try to find the scaling factor here. This is then used to check + % whether or not explicit {x,y}TickLabels are really necessary. + s = str2double(tickLabels{k}); + scalingFactor = ticks(k)/s; + % check if the factor is indeed a power of 10 + S = log10(scalingFactor); + if abs(round(S)-S) > m2t.tol + scalingFactor = 1.0; + end + end + + for k = 1:min(length(ticks),length(tickLabels)) + % Don't use str2num here as then, literal strings as 'pi' get + % legally transformed into 3.14... and the need for an explicit + % label will not be recognized. str2double returns a NaN for 'pi'. + if isLogAxis + s = 10^(str2double(tickLabels{k})); + else + s = str2double(tickLabels{k}); + end + if isnan(s) || abs(ticks(k)-s*scalingFactor) > m2t.tol + bool = true; + return; + end + end +end +% ============================================================================== +function pTickLabels = formatPgfTickLabels(m2t, plotLabelsNecessary, ... + tickLabels, isLogAxis, tickLabelMode) + % formats the tick labels for pgfplots + if plotLabelsNecessary + for k = 1:length(tickLabels) + % Turn tickLabels from cells containing a cell into + % cells containing strings + if isnumeric(tickLabels{k}) + tickLabels(k) = num2str(tickLabels{k}); + elseif iscell(tickLabels{k}) + tickLabels(k) = tickLabels{k}; + end + % If the axis is logscaled, MATLAB does not store the labels, + % but the exponents to 10 + if isLogAxis && strcmpi(tickLabelMode,'auto') + tickLabels{k} = sprintf('$10^{%s}$', str); + end + end + tickLabels = cellfun(@(l)(sprintf('{%s}',l)), tickLabels, ... + 'UniformOutput', false); + pTickLabels = join(m2t, tickLabels, ','); + else + pTickLabels = []; + end +end +% ============================================================================== +function ticks = removeSuperfluousTicks(ticks, tickLabels) + % What MATLAB does when the number of ticks and tick labels is not the same, + % is somewhat unclear. Cut of the first entries to fix bug + % https://github.com/matlab2tikz/matlab2tikz/issues/161, + m = length(ticks); + n = length(tickLabels); + if n < m + ticks = ticks(m-n+1:end); + end +end +% ============================================================================== +function tikzLineStyle = translateLineStyle(matlabLineStyle) + if(~ischar(matlabLineStyle)) + error('matlab2tikz:translateLineStyle:NotAString',... + 'Variable matlabLineStyle is not a string.'); + end + + switch (matlabLineStyle) + case 'none' + tikzLineStyle = ''; + case '-' + tikzLineStyle = 'solid'; + case '--' + tikzLineStyle = 'dashed'; + case ':' + tikzLineStyle = 'dotted'; + case '-.' + tikzLineStyle = 'dashdotted'; + otherwise + error('matlab2tikz:translateLineStyle:UnknownLineStyle',... + 'Unknown matlabLineStyle ''%s''.', matlabLineStyle); + end +end +% ============================================================================== +function [m2t, table, opts] = makeTable(m2t, varargin) + % [m2t,table,opts] = makeTable(m2t, 'name1', data1, 'name2', data2, ...) + % [m2t,table,opts] = makeTable(m2t, {'name1','name2',...}, {data1, data2, ...}) + % [m2t,table,opts] = makeTable(m2t, {'name1','name2',...}, [data1(:), data2(:), ...]) + % + % Returns m2t structure, formatted table and table options. + % When all the names are empty, no header is printed + [variables, data] = parseInputsForTable_(varargin{:}); + opts = opts_new(); + + COLSEP = sprintf('\t'); + if m2t.args.externalData + ROWSEP = sprintf('\n'); + else + ROWSEP = sprintf('\\\\\n'); + opts = opts_add(opts, 'row sep','crcr'); + end + + nColumns = numel(data); + nRows = cellfun(@numel, data); + if ~all(nRows==nRows(1)) + error('matlab2tikz:makeTableDifferentNumberOfRows',... + 'Different data lengths [%s].', num2str(nRows)); + end + nRows = nRows(1); + + FORMAT = repmat({m2t.ff}, 1, nColumns); + FORMAT(cellfun(@isCellOrChar, data)) = {'%s'}; + FORMAT(cellfun(@isCategoricalType, data)) = {'%s'}; + FORMAT = join(m2t, FORMAT, COLSEP); + if all(cellfun(@isempty, variables)) + header = {}; + else + header = {join(m2t, variables, COLSEP)}; + end + + table = cell(nRows,1); + for iRow = 1:nRows + thisData = cell(1,nColumns); + for jCol = 1:nColumns + thisData{1,jCol} = data{jCol}(iRow); + end + table{iRow} = sprintf(FORMAT, thisData{:}); + end + % convert NaN and Inf to lower case for TikZ + table = regexprep(table, '\', 'nan'); + table = regexprep(table, '\', 'inf'); + table = [join(m2t, [header;table], ROWSEP) ROWSEP]; + + if m2t.args.externalData + % output data to external file + [m2t, fileNum] = incrementGlobalCounter(m2t, 'tsvFile'); + [filename, latexFilename] = externalFilename(m2t, fileNum, '.tsv'); + + % write the data table to an external file + fid = fileOpenForWrite(m2t, filename); + finally_fclose_fid = onCleanup(@() fclose(fid)); + + fprintf(fid, '%s', table); + + % put the filename in the TikZ output + table = latexFilename; + else + % output data with "%newline" prepended for formatting consistency + % do NOT prepend another newline in the output: LaTeX will crash. + table = sprintf('%%\n%s', table); + end +end +% ============================================================================== +function [variables, data] = parseInputsForTable_(varargin) + % parse input arguments for |makeTable| + if numel(varargin) == 2 % cell syntax + variables = varargin{1}; + data = varargin{2}; + if ischar(variables) + % one variable, one data vector -> (cell, cell) + variables = {variables}; + data = {data}; + elseif iscellstr(variables) && ~iscell(data) + % multiple variables, one data matrix -> (cell, cell) by column + data = num2cell(data, 1); + end + else % key-value syntax + variables = varargin(1:2:end-1); + data = varargin(2:2:end); + end +end +% ============================================================================== +function [path, texpath] = externalFilename(m2t, counter, extension) + % generates a file name for an external data file and its relative TeX path + + [dummy, name] = fileparts(m2t.tikzFileName); %#ok + baseFilename = [name '-' num2str(counter) extension]; + path = fullfile(m2t.dataPath, baseFilename); + texpath = TeXpath(fullfile(m2t.relativeDataPath, baseFilename)); +end +% ============================================================================== +function [names,definitions] = dealColorDefinitions(mergedColorDefs) + if isempty(mergedColorDefs) + mergedColorDefs = {}; + end + [names,definitions] = cellfun(@(x)(deal(x{:})), mergedColorDefs, ... + 'UniformOutput', false); +end +% ============================================================================== +function [m2t, colorLiteral] = rgb2colorliteral(m2t, rgb) + % Translates an rgb value to an xcolor literal + % + % Possible outputs: + % - xcolor literal color, e.g. 'blue' + % - mixture of 2 previously defined colors, e.g. 'red!70!green' + % - a newly defined color, e.g. 'mycolor10' + + % Take a look at xcolor.sty for the color definitions. + % In xcolor.sty some colors are defined in CMYK space and approximated + % crudely for RGB color space. So it is better to redefine those colors + % instead of using xcolor's: + % 'cyan' , 'magenta', 'yellow', 'olive' + % [0,1,1], [1,0,1] , [1,1,0] , [0.5,0.5,0] + + xcolColorNames = {'white', 'black', 'red', 'green', 'blue', ... + 'brown', 'lime', 'orange', 'pink', ... + 'purple', 'teal', 'violet', ... + 'darkgray', 'gray', 'lightgray'}; + xcolColorSpecs = {[1,1,1], [0,0,0], [1,0,0], [0,1,0], [0,0,1], ... + [0.75,0.5,0.25], [0.75,1,0], [1,0.5,0], [1,0.75,0.75], ... + [0.75,0,0.25], [0,0.5,0.5], [0.5,0,0.5], ... + [0.25,0.25,0.25], [0.5,0.5,0.5], [0.75,0.75,0.75]}; + + colorNames = [xcolColorNames, m2t.color.extraNames]; + colorSpecs = [xcolColorSpecs, m2t.color.extraSpecs]; + + %% check if rgb is a predefined color + for kColor = 1:length(colorSpecs) + Ck = colorSpecs{kColor}(:); + if max(abs(Ck - rgb(:))) < m2t.color.precision + colorLiteral = colorNames{kColor}; + return % exact color was predefined + end + end + + %% check if the color is a linear combination of two already defined colors + for iColor = 1:length(colorSpecs) + for jColor = iColor+1:length(colorSpecs) + Ci = colorSpecs{iColor}(:); + Cj = colorSpecs{jColor}(:); + + % solve color mixing equation `Ck = p * Ci + (1-p) * Cj` for p + p = (Ci-Cj) \ (rgb(:)-Cj); + p = round(100*p)/100; % round to a percentage + Ck = p * Ci + (1-p)*Cj; % approximated mixed color + + if p <= 1 && p >= 0 && max(abs(Ck(:) - rgb(:))) < m2t.color.precision + colorLiteral = sprintf('%s!%d!%s', colorNames{iColor}, round(p*100), ... + colorNames{jColor}); + return % linear combination found + end + end + end + + %% Define colors that are not a linear combination of two known colors + colorLiteral = sprintf('mycolor%d', length(m2t.color.extraNames)+1); + m2t.color.extraNames{end+1} = colorLiteral; + m2t.color.extraSpecs{end+1} = rgb; +end +% ============================================================================== +function newstr = join(m2t, cellstr, delimiter) + % This function joins a cell of strings to a single string (with a + % given delimiter in between two strings, if desired). + % + % Example of usage: + % join(m2t, cellstr, ',') + newstr = m2tstrjoin(cellstr, delimiter, m2t.ff); +end +% ============================================================================== +function [width, height, unit] = getNaturalFigureDimension(m2t) + % Returns the size of figure (in inch) + % To stay compatible with getNaturalAxesDimensions, the unit 'in' is + % also returned. + + % Get current figure size + figuresize = get(m2t.current.gcf, 'Position'); + figuresize = figuresize([3 4]); + figureunit = get(m2t.current.gcf, 'Units'); + + % Convert Figure Size + unit = 'in'; + figuresize = convertUnits(figuresize, figureunit, unit); + + % Split size into width and height + width = figuresize(1); + height = figuresize(2); + +end +% ============================================================================== +function dimension = getFigureDimensions(m2t, widthString, heightString) + % Returns the physical dimension of the figure. + + [width, height, unit] = getNaturalFigureDimension(m2t); + + % get the natural width-height ration of the plot + axesWidthHeightRatio = width / height; + % check matlab2tikz arguments + if ~isempty(widthString) + width = extractValueUnit(widthString); + end + if ~isempty(heightString) + height = extractValueUnit(heightString); + end + + % prepare the output + if ~isempty(widthString) && ~isempty(heightString) + dimension.x.unit = width.unit; + dimension.x.value = width.value; + dimension.y.unit = height.unit; + dimension.y.value = height.value; + elseif ~isempty(widthString) + dimension.x.unit = width.unit; + dimension.x.value = width.value; + dimension.y.unit = width.unit; + dimension.y.value = width.value / axesWidthHeightRatio; + elseif ~isempty(heightString) + dimension.y.unit = height.unit; + dimension.y.value = height.value; + dimension.x.unit = height.unit; + dimension.x.value = height.value * axesWidthHeightRatio; + else % neither width nor height given + dimension.x.unit = unit; + dimension.x.value = width; + dimension.y.unit = unit; + dimension.y.value = height; + end +end +% ============================================================================== +function position = getAxesPosition(m2t, handle, widthString, heightString, axesBoundingBox) + % Returns the physical position of the axes. This includes - in difference + % to the Dimension - also an offset to shift the axes inside the figure + % An optional bounding box can be used to omit empty borders. + + % Deal with optional parameter + if nargin < 4 + axesBoundingBox = [0 0 1 1]; + end + + % First get the whole figures size + figDim = getFigureDimensions(m2t, widthString, heightString); + + % Get the relative position of the axis + relPos = getRelativeAxesPosition(m2t, handle, axesBoundingBox); + + position.x.value = relPos(1) * figDim.x.value; + position.x.unit = figDim.x.unit; + position.y.value = relPos(2) * figDim.y.value; + position.y.unit = figDim.y.unit; + position.w.value = relPos(3) * figDim.x.value; + position.w.unit = figDim.x.unit; + position.h.value = relPos(4) * figDim.y.value; + position.h.unit = figDim.y.unit; +end +% ============================================================================== +function [position] = getRelativeAxesPosition(m2t, axesHandles, axesBoundingBox) + % Returns the relative position of axes within the figure. + % Position is an (n,4) matrix with [minX, minY, width, height] for each + % handle. All these values are relative to the figure size, which means + % that [0, 0, 1, 1] covers the whole figure. + % It is possible to add a second parameter with the relative coordinates of + % a bounding box around all axes of the figure (see getRelevantAxes()). In + % this case, relative positions are rescaled so that the bounding box is + % [0, 0, 1, 1] + + % Get Figure Dimension + [figWidth, figHeight, figUnits] = getNaturalFigureDimension(m2t); + + % Initialize position + position = zeros(numel(axesHandles), 4); + % Iterate over all handles + for i = 1:numel(axesHandles) + axesHandle = axesHandles(i); + axesPos = get(axesHandle, 'Position'); + axesUnits = get(axesHandle, 'Units'); + if isequal(lower(axesUnits), 'normalized') + % Position is already relative + position(i,:) = axesPos; + else + % Convert figure size into axes units + figureSize = convertUnits([figWidth, figHeight], figUnits, axesUnits); + % Figure size into axes units to get the relative size + position(i,:) = axesPos ./ [figureSize, figureSize]; + + end + + if strcmpi(get(axesHandle, 'DataAspectRatioMode'), 'manual') ... + || strcmpi(get(axesHandle, 'PlotBoxAspectRatioMode'), 'manual') + + if strcmpi(get(axesHandle,'Projection'),'Perspective') + userWarning(m2t,'Perspective projections are not currently supported') + end + + % project vertices of 3d plot box (this results in 2d coordinates in + % an absolute coordinate system that is scaled proportionally by + % Matlab to fit the axes position box) + switch getEnvironment() + case 'MATLAB' + projection = view(axesHandle); + + case 'Octave' + % Unfortunately, Octave does not have the full `view` + % interface implemented, but the projection matrices are + % available: http://octave.1599824.n4.nabble.com/Implementing-view-td3032041.html + + projection = get(axesHandle, 'x_viewtransform'); + + otherwise + errorUnknownEnvironment(); + end + + + vertices = projection * [0, 1, 0, 0, 1, 1, 0, 1; + 0, 0, 1, 0, 1, 0, 1, 1; + 0, 0, 0, 1, 0, 1, 1, 1; + 1, 1, 1, 1, 1, 1, 1, 1]; + + % each of the columns of vertices represents a vertex of the 3D axes + % but we only need their XY coordinates + verticesXY = vertices([1 2], :); + + % the size of the projected plot box is limited by the long diagonals + % The matrix A determines the connectivity, e.g. the first diagonal runs from vertices(:,3) -> vertices(:,4) + A = [ 0, 0, 0, -1, +1, 0, 0, 0; + 0, 0, -1, 0, 0, +1, 0, 0; + 0, -1, 0, 0, 0, 0, +1, 0; + -1, 0, 0, 0, 0, 0, 0, +1]; + diagonals = verticesXY * A'; + % each of the columns of this matrix contains a the X and Y distance of a diagonal + dimensions = max(abs(diagonals), [], 2); + + % find limiting dimension and adjust position + aspectRatio = dimensions(2) * figWidth / (dimensions(1) * figHeight); + axesAspectRatio = position(i,4) / position(i,3); + if aspectRatio > axesAspectRatio + newWidth = position(i,4) / aspectRatio; + % Center Axis + offset = (position(i,3) - newWidth) / 2; + position(i,1) = position(i,1) + offset; + % Store new width + position(i,3) = newWidth; + else + newHeight = position(i,3) * aspectRatio; + offset = (position(i,4) - newHeight) / 2; + position(i,2) = position(i,2) + offset; + % Store new height + position(i,4) = newHeight; + end + end + end + + %% Rescale if axesBoundingBox is given + if exist('axesBoundingBox','var') + % shift position so that [0, 0] is the lower left corner of the + % bounding box + position(:,1) = position(:,1) - axesBoundingBox(1); + position(:,2) = position(:,2) - axesBoundingBox(2); + % Recale + position(:,[1 3]) = position(:,[1 3]) / max(axesBoundingBox([3 4])); + position(:,[2 4]) = position(:,[2 4]) / max(axesBoundingBox([3 4])); + end +end +% ============================================================================== +function aspectRatio = getPlotBoxAspectRatio(axesHandle) + limits = axis(axesHandle); + if any(isinf(limits)) + aspectRatio = get(axesHandle,'PlotBoxAspectRatio'); + else + % DataAspectRatio has priority + dataAspectRatio = get(axesHandle,'DataAspectRatio'); + nlimits = length(limits)/2; + limits = reshape(limits, 2, nlimits); + aspectRatio = abs(limits(2,:) - limits(1,:))./dataAspectRatio(1:nlimits); + aspectRatio = aspectRatio/min(aspectRatio); + end +end +% ============================================================================== +function texUnits = matlab2texUnits(matlabUnits, fallbackValue) + switch matlabUnits + case 'pixels' + texUnits = 'px'; % only in pdfTex/LuaTeX + case 'centimeters' + texUnits = 'cm'; + case 'characters' + texUnits = 'em'; + case 'points' + texUnits = 'pt'; + case 'inches' + texUnits = 'in'; + otherwise + texUnits = fallbackValue; + end +end +% ============================================================================== +function dstValue = convertUnits(srcValue, srcUnit, dstUnit) + % Converts values between different units. + % srcValue stores a length (or vector of lengths) in srcUnit. + % The resulting dstValue is the converted length into dstUnit. + % + % Currently supported units are: in, cm, px, pt + + % Use tex units, if possible (to make things simple) + srcUnit = matlab2texUnits(lower(srcUnit),lower(srcUnit)); + dstUnit = matlab2texUnits(lower(dstUnit),lower(dstUnit)); + + if isequal(srcUnit, dstUnit) + dstValue = srcValue; + return % conversion to the same unit => factor = 1 + end + + units = {srcUnit, dstUnit}; + factor = ones(1,2); + for ii = 1:numel(factor) % Same code for srcUnit and dstUnit + % Use inches as intermediate unit + % Compute the factor to convert an inch into another unit + switch units{ii} + case 'cm' + factor(ii) = 2.54; + case 'px' + factor(ii) = get(0, 'ScreenPixelsPerInch'); + case 'in' + factor(ii) = 1; + case 'pt' + factor(ii) = 72; + otherwise + warning('MATLAB2TIKZ:UnknownPhysicalUnit',... + 'Can not convert unit ''%s''. Using conversion factor 1.', units{ii}); + end + end + + dstValue = srcValue * factor(2) / factor(1); +end +% ============================================================================== +function out = extractValueUnit(str) + % Decompose m2t.args.width into value and unit. + + % Regular expression to match '4.12cm', '\figurewidth', ... + fp_regex = '[-+]?\d*\.?\d*(?:e[-+]?\d+)?'; + pattern = strcat('(', fp_regex, ')?', '(\\?[a-zA-Z]+)'); + + [dummy,dummy,dummy,dummy,t,dummy] = regexp(str, pattern, 'match'); %#ok + + if length(t)~=1 + error('getAxesDimensions:illegalLength', ... + 'The width string ''%s'' could not be decomposed into value-unit pair.', str); + end + + if length(t{1}) == 1 + out.value = 1.0; % such as in '1.0\figurewidth' + out.unit = strtrim(t{1}{1}); + elseif length(t{1}) == 2 && isempty(t{1}{1}) + % MATLAB(R) does this: + % length(t{1})==2 always, but the first field may be empty. + out.value = 1.0; + out.unit = strtrim(t{1}{2}); + elseif length(t{1}) == 2 + out.value = str2double(t{1}{1}); + out.unit = strtrim(t{1}{2}); + else + error('getAxesDimensions:illegalLength', ... + 'The width string ''%s'' could not be decomposed into value-unit pair.', str); + end +end +% ============================================================================== +function str = escapeCharacters(str) + % Replaces "%" and "\" with respectively "%%" and "\\" + str = strrep(str, '%' , '%%'); + str = strrep(str, '\' , '\\'); +end +% ============================================================================== +function bool = isNone(value) + % Checks whether a value is 'none' + bool = strcmpi(value, 'none'); +end +% ============================================================================== +function bool = isOn(value) + % Checks whether a value is 'on' + bool = strcmpi(value, 'on'); +end +% ============================================================================== +function bool = isOff(value) + % Checks whether a value is 'off'. + % Note that some options are not be solely an on/off boolean, such that `isOn` + % and isOff don't always return the complement of each other and such that we + % need both functions to check the value. + % E.g. `set(0, 'HandleVisibility')` allows the value 'callback'. + bool = strcmpi(value, 'off'); +end +% ============================================================================== +function val = getOrDefault(handle, key, default) + % gets the value or returns the default value if no such property exists + if all(isprop(handle, key)) + val = get(handle, key); + else + val = default; + end +end +% ============================================================================== +function val = getFactoryOrDefault(type, key, fallback) + % get factory default value for a certain type of HG object + % this CANNOT be done using |getOrDefault| as |isprop| doesn't work for + % factory/default settings. Hence, we use a more expensive try-catch instead. + try + groot = 0; + val = get(groot, ['Factory' type key]); + catch + val = fallback; + end +end +% ============================================================================== +function [val, isDefault] = getAndCheckDefault(type, handle, key, default) + % gets the value from a handle of certain type and check the default values + default = getFactoryOrDefault(type, key, default); + val = getOrDefault(handle, key, default); + isDefault = isequal(val, default); +end +% ============================================================================== +function bool = isVisible(handles) + % Determines whether an object is actually visible or not. + bool = isOn(get(handles,'Visible')); + % There's another handle property, 'HandleVisibility', that is unrelated + % to the "physical" visibility of an object. Rather, it sets whether an + % object should be visitable by |findobj|. Hence, it is often switched off + % for non-data objects such as custom axes/grid objects. +end +% ============================================================================== +function [m2t, axesBoundingBox] = getRelevantAxes(m2t, axesHandles) + % Returns relevant axes. These are defines as visible axes that are no + % colorbars. Function 'findPlotAxes()' ensures that 'axesHandles' does not + % contain colorbars. In addition, a bounding box around all relevant Axes is + % computed. This can be used to avoid undesired borders. + % This function is the remaining code of alignSubPlots() in the alternative + % positioning system. + + % List only visible axes + N = numel(axesHandles); + idx = false(N,1); + for ii = 1:N + idx(ii) = isVisibleContainer(axesHandles(ii)); + end + % Store the relevant axes in m2t to simplify querying e.g. positions + % of subplots + m2t.relevantAxesHandles = axesHandles(idx); + + % Compute the bounding box if width or height of the figure are set by + % parameter + if ~isempty(m2t.args.width) || ~isempty(m2t.args.height) + % TODO: check if relevant Axes or all Axes are better. + axesBoundingBox = getRelativeAxesPosition(m2t, m2t.relevantAxesHandles); + % Compute second corner from width and height for each axes + axesBoundingBox(:,[3 4]) = axesBoundingBox(:,[1 2]) + axesBoundingBox(:,[3 4]); + % Combine axes corners to get the bounding box + axesBoundingBox = [min(axesBoundingBox(:,[1 2]),[],1), max(axesBoundingBox(:,[3 4]), [], 1)]; + % Compute width and height of the bounding box + axesBoundingBox(:,[3 4]) = axesBoundingBox(:,[3 4]) - axesBoundingBox(:,[1 2]); + else + % Otherwise take the whole figure as bounding box => lengths are + % not changed in tikz + axesBoundingBox = [0, 0, 1, 1]; + end +end +% ============================================================================== +function userInfo(m2t, message, varargin) + % Display usage information. + if m2t.args.showInfo + mess = sprintf(message, varargin{:}); + + mess = strrep(mess, sprintf('\n'), sprintf('\n *** ')); + fprintf(' *** %s\n', mess); + end +end +% ============================================================================== +function userWarning(m2t, message, varargin) + % Drop-in replacement for warning(). + if m2t.args.showWarnings + warning('matlab2tikz:userWarning', message, varargin{:}); + end +end +% ============================================================================== +function m2t = signalDependency(m2t, dependencyType, name) + % Signals an (optional) dependency to the user + switch lower(dependencyType) + case 'tikzlibrary' + message = 'Make sure to add "\\usetikzlibrary{%s}" to the preamble.'; + otherwise + message = 'Please make sure to load the "%s" dependency'; + end + userInfo(m2t, message, name); +end +% ============================================================================== +function warnAboutParameter(m2t, parameter, isActive, message) + % warn the user about the use of a dangerous parameter + line = ['\n' repmat('=',1,80) '\n']; + if isActive(m2t.args.(parameter)) + userWarning(m2t, [line, 'You are using the "%s" parameter.\n', ... + message line], parameter); + end +end +% ============================================================================== +function parent = addChildren(parent, children) + if isempty(children) + return; + elseif iscell(children) + for k = 1:length(children) + parent = addChildren(parent, children{k}); + end + else + if isempty(parent.children) + parent.children = {children}; + else + parent.children = [parent.children children]; + end + end +end +% ============================================================================== +function printAll(m2t, env, fid) + if isfield(env, 'colors') && ~isempty(env.colors) + fprintf(fid, '%s', env.colors); + end + + if isempty(env.options) + fprintf(fid, '\\begin{%s}\n', env.name); + else + fprintf(fid, '\\begin{%s}[%%\n%s\n]\n', env.name, ... + opts_print(env.options, sprintf(',\n'))); + end + + for item = env.content + fprintf(fid, '%s', char(item)); + end + + for k = 1:length(env.children) + if ischar(env.children{k}) + fprintf(fid, escapeCharacters(env.children{k})); + else + fprintf(fid, '\n'); + printAll(m2t, env.children{k}, fid); + end + end + + % End the tikzpicture environment with an empty comment and no newline + % so no additional space is generated after the tikzpicture in TeX. + if strcmp(env.name, 'tikzpicture') % LaTeX is case sensitive + fprintf(fid, '\\end{%s}%%', env.name); + else + fprintf(fid, '\\end{%s}\n', env.name); + end +end +% ============================================================================== +function c = prettyPrint(m2t, strings, interpreter) + % Some resources on how MATLAB handles rich (TeX) markup: + % http://www.mathworks.com/help/techdoc/ref/text_props.html#String + % http://www.mathworks.com/help/techdoc/creating_plots/f0-4741.html#f0-28104 + % http://www.mathworks.com/help/techdoc/ref/text_props.html#Interpreter + % http://www.mathworks.com/help/techdoc/ref/text.html#f68-481120 + + % If the user set the matlab2tikz parameter 'parseStrings' to false, no + % parsing of strings takes place, thus making the user 100% responsible. + if ~m2t.args.parseStrings + % If strings is an actual string (labels etc) we need to return a + % cell containing the string + c = cellstr(strings); + return + end + + % Make sure we have a valid interpreter set up + if ~any(strcmpi(interpreter, {'latex', 'tex', 'none'})) + userWarning(m2t, 'Don''t know interpreter ''%s''. Default handling.', interpreter); + interpreter = 'tex'; + end + + strings = cellstrOneLinePerCell(strings); + + % Now loop over the strings and return them pretty-printed in c. + c = cell(1, length(strings)); + for k = 1:length(strings) + % linear indexing for independence of cell array dimensions + s = strings{k}; + + % The interpreter property of the text element defines how the string + % is parsed + switch lower(interpreter) + case 'latex' % Basic subset of the LaTeX markup language + + % Replace $$...$$ with $...$ for groups, but otherwise leave + % untouched. + % Displaymath \[...\] seems to be unsupported by TikZ/PGF. + % If this changes, use '\\[$2\\]' as replacement below. + % Do not escape dollar in replacement string (e.g., "\$$2\$"), + % since this is not properly handled by octave 3.8.2. + string = regexprep(s, '(\$\$)(.*?)(\$\$)', '$$2$'); + + case 'tex' % Subset of plain TeX markup language + + % Deal with UTF8 characters. + string = s; + + % degree symbol following "^" or "_" needs to be escaped + string = regexprep(string, '([\^\_])°', '$1{{}^\\circ}'); + string = strrep(string, '°', '^\circ'); + string = strrep(string, '∞', '\infty'); + + % Parse string piece-wise in a separate function. + string = parseTexString(m2t, string); + + case 'none' % Literal characters + % Make special characters TeX compatible + + string = strrep(s, '\', '\textbackslash{}'); + % Note: '{' and '}' can't be converted to '\{' and '\}', + % respectively, via strrep(...) as this would lead to + % backslashes converted to '\textbackslash\{\}' because + % the backslash was converted to '\textbackslash{}' in + % the previous step. Using regular expressions with + % negative look-behind makes sure any braces in 'string' + % were not introduced by escaped backslashes. + % Also keep in mind that escaping braces before backslashes + % would not remedy the issue -- in that case 'string' would + % contain backslashes introduced by brace escaping that are + % not supposed to be printable characters. + repl = switchMatOct('\\{', '\{'); + string = regexprep(string, '(?= sCmd(i) & bracesPos <= eCmd(i)) = []; + end + + % Exclude braces that are preceded by an odd number of backslashes which + % means the brace is escaped and thus to be printed, not a grouping brace + expr = '(? $ \text {(non-}) } $<-end + % ...or when the parsed string is empty + parsed = regexprep(parsed, '^\$\$$', ''); + + % Ensure math mode for pipe symbol (issue #587) + parsed = strrep(parsed, '|', '$|$'); +end +% ============================================================================== +function string = parseTexSubstring(m2t, string) + origstr = string; % keep this for warning messages + + % Font families (italic, bold, etc.) get a trailing '{}' because they may be + % followed by a letter which would produce an error in (La)TeX. + for i = {'it', 'bf', 'rm', 'sl'} + string = strrep(string, ['\' i{:}], ['\' i{:} '{}']); + end + + % The same holds true for special characters like \alpha + % The list of MATLAB-supported TeX characters was taken from + % http://www.mathworks.com/help/techdoc/ref/text_props.html#String + named = {'alpha', 'angle', 'ast', 'beta', 'gamma', 'delta', ... + 'epsilon', 'zeta', 'eta', 'theta', 'vartheta', 'iota', ... + 'kappa', 'lambda', 'mu', 'nu', 'xi', 'pi', 'rho', ... + 'sigma', 'varsigma', 'tau', 'equiv', 'Im', 'otimes', ... + 'cap', 'int', 'rfloor', 'lfloor', 'perp', 'wedge', ... + 'rceil', 'vee', 'langle', 'upsilon', 'phi', 'chi', ... + 'psi', 'omega', 'Gamma', 'Delta', 'Theta', 'Lambda', ... + 'Xi', 'Pi', 'Sigma', 'Upsilon', 'Phi', 'Psi', 'Omega', ... + 'forall', 'exists', 'ni', 'cong', 'approx', 'Re', ... + 'oplus', 'cup', 'subseteq', 'lceil', 'cdot', 'neg', ... + 'times', 'surd', 'varpi', 'rangle', 'sim', 'leq', ... + 'infty', 'clubsuit', 'diamondsuit', 'heartsuit', ... + 'spadesuit', 'leftrightarrow', 'leftarrow', ... + 'Leftarrow', 'uparrow', 'rightarrow', 'Rightarrow', ... + 'downarrow', 'circ', 'pm', 'geq', 'propto', 'partial', ... + 'bullet', 'div', 'neq', 'aleph', 'wp', 'oslash', ... + 'supseteq', 'nabla', 'ldots', 'prime', '0', 'mid', ... + 'copyright' }; + for i = named + string = strrep(string, ['\' i{:}], ['\' i{:} '{}']); + % FIXME: Only append '{}' if there's an odd number of backslashes + % in front of the items from 'named'. If it's an even + % number instead, that means there's an escaped (printable) + % backslash and some text like "alpha" after that. + end + % Some special characters' names are subsets of others, e.g. '\o' is + % a subset of '\omega'. This would produce undesired double-escapes. + % For example if '\o' was converted to '\o{}' after '\omega' has been + % converted to '\omega{}' this would result in '\o{}mega{}' instead of + % '\omega{}'. Had '\o' been converted to '\o{}' _before_ '\omega' is + % converted then the result would be '\o{}mega' and thus also wrong. + % To circumvent the problem all those special character names that are + % subsets of others are now converted using a regular expression that + % uses negative lookahead. The special handling of the backslash is + % required for MATLAB/Octave compatibility. + string = regexprep(string, '(\\)o(?!mega|times|plus|slash)', '$1o{}'); + string = regexprep(string, '(\\)in(?!t|fty)', '$1in{}'); + string = regexprep(string, '(\\)subset(?!eq)', '$1subset{}'); + string = regexprep(string, '(\\)supset(?!eq)', '$1supset{}'); + + % Convert '\0{}' (TeX text mode) to '\emptyset{}' (TeX math mode) + string = strrep(string, '\0{}', '\emptyset{}'); + + % Add skip to \fontsize + % This is required for a successful LaTeX run on the output as in contrast + % to MATLAB/Octave it requires the skip parameter (even if it's zero) + string = regexprep(string, '(\\fontsize\{[^}]*\})', '$1{0}'); + + % Put '\o{}' inside \text{...} as it is a text mode symbol that does not + % exist in math mode (and LaTeX gives a warning if you use it in math mode) + string = strrep(string, '\o{}', '\text{\o{}}'); + + % Put everything that isn't a TeX command inside \text{...} + expr = '(\\[a-zA-Z]+(\[[^\]]*\])?(\{[^}]*\}){1,2})'; + % |( \cmd )( [...]? )( {...}{1,2} )| + % ( subset $1 ) + repl = '}$1\\text{'; + string = regexprep(string, expr, repl); + % ...\alpha{}... -> ...}\alpha{}\text{... + string = ['\text{' string '}']; + % ...}\alpha{}\text{... -> \text{...}\alpha{}\text{...} + + % '_' has to be in math mode so long as it's not escaped as '\_' in which + % case it remains as-is. Extra care has to be taken to make sure any + % backslashes in front of the underscore are not themselves escaped and + % thus printable backslashes. This is the case if there's an even number + % of backslashes in a row. + repl = '$1}_\\text{'; + string = regexprep(string, '(?' has to be either in math mode or needs to be typeset as + % '\textless' and '\textgreater' in textmode + % This is handled better, if 'parseStringsAsMath' is activated + if m2t.args.parseStringsAsMath == 0 + string = regexprep(string, '<', '\\textless{}'); + string = regexprep(string, '>', '\\textgreater{}'); + end + + % Move font styles like \bf into the \text{} command. + expr = '(\\bf|\\it|\\rm|\\fontname)({\w*})+(\\text{)'; + while regexp(string, expr) + string = regexprep(string, expr, '$3$1$2'); + end + + % Replace Fontnames + [dummy, dummy, dummy, dummy, fonts, dummy, subStrings] = regexp(string, '\\fontname{(\w*)}'); %#ok + fonts = fonts2tex(fonts); + subStrings = [subStrings; fonts, {''}]; + string = cell2mat(subStrings(:)'); + + % Merge adjacent \text fields: + string = mergeAdjacentTexCmds(string, '\text'); + + % '\\' has to be escaped to '\textbackslash{}' + % This cannot be done with strrep(...) as it would replace e.g. 4 backslashes + % with three times the replacement string because it finds overlapping matches + % (see http://www.mathworks.de/help/techdoc/ref/strrep.html) + % Note: Octave's backslash handling is broken. Even though its output does + % not resemble MATLAB's, the same m2t code is used for either software. That + % way MATLAB-compatible code produces the same matlab2tikz output no matter + % which software it's executed in. So long as this MATLAB incompatibility + % remains in Octave you're probably better off not using backslashes in TeX + % text anyway. + string = regexprep(string, '(\\)\\', '$1textbackslash{}'); + + % '_', '^', '{', and '}' are already escaped properly, even in MATLAB's TeX + % dialect (and if they're not, that's intentional) + + % Escape "$", "%", and "#" to make them compatible to true TeX while in + % MATLAB/Octave they are not escaped + string = strrep(string, '$', '\$'); + string = strrep(string, '%', '\%'); + string = strrep(string, '#', '\#'); + + % Escape "§" as "\S" since it can give UTF-8 problems otherwise. + % The TeX string 'a_§' in particular lead to problems in Octave 3.6.0. + % m2t transcoded that string into '$\text{a}_\text{*}\text{#}$' with + % * = 0xC2 and # = 0xA7 which corresponds with the two-byte UTF-8 + % encoding. Even though this looks like an Octave bug that shows + % during the '..._\text{abc}' to '..._\text{a}\text{bc}' conversion, + % it's best to include the workaround here. + string = strrep(string, '§', '\S{}'); + + string = escapeAmpersands(m2t, string, origstr); + string = escapeTildes(m2t, string, origstr); + + % Convert '..._\text{abc}' and '...^\text{abc}' to '..._\text{a}\text{bc}' + % and '...^\text{a}\text{bc}', respectively. + % Things get a little more complicated if instead of 'a' it's e.g. '$'. The + % latter has been converted to '\$' by now and simply extracting the first + % character from '\text{\$bc}' would result in '\text{$}\text{$bc}' which + % is syntactically wrong. Instead the whole command '\$' has to be moved in + % front of the \text{...} block, e.g. '..._\text{\$bc}' -> '..._\$\text{bc}'. + % Note that the problem does not occur for the majority of special characters + % like '\alpha' because they use math mode and therefore are never inside a + % \text{...} block to begin with. This means that the number of special + % characters affected by this issue is actually quite small: + % $ # % & _ { } \o § ~ \ ^ + expr = ['(_|\^)(\\text)\{([^}\\]|\\\$|\\#|\\%|\\&|\\_|\\\{|\\\}|', ... + ... % (_/^)(\text) {(non-}\| \$ | \#| \%| \&| \_| \{ | \} | + ... % ($1)( $2 ) ( $3 -> + '\\o\{\}|\\S\{\}|\\textasciitilde\{\}|\\textbackslash\{\}|', ... + ... % \o{} | \S{} | \textasciitilde{} | \textbackslash{} | + ... % <- $3 -> + '\\textasciicircum\{\})']; + % \textasciicircum{} ) + % <- $3 ) + string = regexprep(string, expr, '$1$2{$3}$2{'); + + string = parseStringsAsMath(m2t, string); + + % Clean up: remove empty \text{} + string = strrep(string, '\text{}', ''); + % \text{}\alpha{}\text{...} -> \alpha{}\text{...} + + % Clean up: convert '{}\' to '\' unless it's prefixed by a backslash which + % means the opening brace is escaped and thus a printable character instead + % of a grouping brace. + string = regexprep(string, '(? \alpha\text{...} + + % Clean up: convert '{}}' to '}' unless it's prefixed by a backslash + string = regexprep(string, '(? 'abc ef' + % 'abc&\deltaef' -> 'abc ef' + % 'abc&$ef' -> 'abc ef' + % 'abcdef&' -> 'abcdef' + % Don't remove closing brace after '&' as this would result in + % unbalanced braces + string = regexprep(string, '(?0-9 + expr = '(\\text)\{([^}=\-+/,.()<>0-9]*)([=\-+/,.()<>0-9]+)([^}]*)\}'; + % \text {(any non-"x"/'}'char)( any "x" char )(non-}) } + % ( $1 ) ( $2 )( $3 )( $4) + while regexp(string, expr) + % Iterating is necessary to catch all occurrences. See above. + string = regexprep(string, expr, '$1{$2}$3$1{$4}'); + end + + % \text{ } should be a math-mode space + string = regexprep(string, '\\text\{(\s+)}', '$1'); + + % '<<' probably means 'much smaller than', i.e. '\ll' + repl = switchMatOct('$1\\ll{}$2', '$1\ll{}$2'); + string = regexprep(string, '([^<])<<([^<])', repl); + + % '>>' probably means 'much greater than', i.e. '\gg' + repl = switchMatOct('$1\\gg{}$2', '$1\gg{}$2'); + string = regexprep(string, '([^>])>>([^>])', repl); + + % Single letters are most likely variables and thus should be in math mode + string = regexprep(string, '\\text\{([a-zA-Z])\}', '$1'); + + end +end +% ============================================================================== +function tex = fonts2tex(fonts) + % Returns a tex command for each fontname in the cell array fonts. + if ~iscell(fonts) + error('matlab2tikz:fonts2tex', ... + 'Expecting a cell array as input.'); + end + tex = cell(size(fonts)); + + for ii = 1:numel(fonts) + font = fonts{ii}{1}; + + % List of known fonts. + switch lower(font) + case 'courier' + tex{ii} = '\ttfamily{}'; + case 'times' + tex{ii} = '\rmfamily{}'; + case {'arial', 'helvetica'} + tex{ii} = '\sffamily{}'; + otherwise + warning('matlab2tikz:fonts2tex', ... + 'Unknown font ''%s''. Using tex default font.',font); + % Unknown font -> Switch to standard font. + tex{ii} = '\rm{}'; + end + end +end +% ============================================================================== +function string = mergeAdjacentTexCmds(string, cmd) + % Merges adjacent tex commands like \text into one command + % If necessary, add a backslash + if cmd(1) ~= '\' + cmd = ['\' cmd]; + end + % Link each bracket to the corresponding bracket + link = zeros(size(string)); + pos = [regexp([' ' string], '([^\\]{)'), ... + regexp([' ' string], '([^\\]})')]; + pos = sort(pos); + ii = 1; + while ii <= numel(pos) + if string(pos(ii)) == '}' + link(pos(ii-1)) = pos(ii); + link(pos(ii)) = pos(ii - 1); + pos([ii-1, ii]) = []; + ii = ii - 1; + else + ii = ii + 1; + end + end + % Find dispensable commands + pos = regexp(string, ['}\' cmd '{']); + delete = zeros(0,1); + len = numel(cmd); + for p = pos + l = link(p); + if l > len && isequal(string(l-len:l-1), cmd) + delete(end+1,1) = p; + end + end + % 3. Remove these commands (starting from the back + delete = repmat(delete, 1, len+2) + repmat(0:len+1,numel(delete), 1); + string(delete(:)) = []; +end +function dims = pos2dims(pos) + % Position quadruplet [left, bottom, width, height] to dimension structure + dims = struct('left' , pos(1), 'bottom', pos(2)); + if numel(pos) == 4 + dims.width = pos(3); + dims.height = pos(4); + dims.right = dims.left + dims.width; + dims.top = dims.bottom + dims.height; + end +end +% OPTION ARRAYS ================================================================ +function opts = opts_new() + % create a new options array + opts = cell(0,2); +end +function opts = opts_add(opts, key, value) + % add a key-value pair to an options array (with duplication check) + if ~exist('value','var') + value = ''; + end + value = char(value); + + % Check if the key already exists. + if opts_has(opts, key) + oldValue = opts_get(opts, key); + if isequal(value, oldValue) + return; % no action needed: value already present + else + error('matlab2tikz:opts_add', ... + ['Trying to add (%s, %s) to options, but it already ' ... + 'contains the conflicting key-value pair (%s, %s).'], ... + key, value, key, oldValue); + end + end + opts = opts_append(opts, key, value); +end +function opts = opts_addSubOpts(opts, key, subOpts) + % add a key={Opts} pair to an options array + formatted = ['{' opts_print(subOpts) '}']; + opts = opts_add(opts, key, formatted); +end +function bool = opts_has(opts, key) + % returns true if the options array contains the key + bool = ~isempty(opts) && ismember(key, opts(:,1)); +end +function value = opts_get(opts, key) + % returns the value(s) stored for a key in an options array + idx = find(ismember(opts(:,1), key)); + switch numel(idx) + case 1 + value = opts{idx,2}; % just the value + otherwise + value = opts(idx,2); % as cell array + end +end +function opts = opts_append(opts, key, value) + % append a key-value pair to an options array (duplicate keys allowed) + if ~exist('value','var') + value = ''; + end + value = char(value); + if ~(opts_has(opts, key) && isequal(opts_get(opts, key), value)) + opts = cat(1, opts, {key, value}); + end +end +function opts = opts_append_userdefined(opts, userDefined) + % appends user-defined options to an options array + % the userDefined options can come either as a single string or a cellstr that + % is already TikZ-formatted. The internal 2D cell format is NOT supported. + if ~isempty(userDefined) + if ischar(userDefined) + userDefined = {userDefined}; + end + for k = 1:length(userDefined) + opts = opts_append(opts, userDefined{k}); + end + end +end +function opts = opts_copy(opts_from, name_from, opts, name_to) + % copies an option (if it exists) from one option array to another one + if ~exist('name_to', 'var') || isempty(name_to) + name_to = name_from; + end + if opts_has(opts_from, name_from) + value = opts_get(opts_from, name_from); + opts = opts_append(opts, name_to, value); + end +end +function opts = opts_remove(opts, varargin) + % remove some key-value pairs from an options array + keysToDelete = varargin; + idxToDelete = ismember(opts(:,1), keysToDelete); + opts(idxToDelete, :) = []; +end +function opts = opts_merge(opts, varargin) + % merge multiple options arrays + for jArg = 1:numel(varargin) + opts2 = varargin{jArg}; + for k = 1:size(opts2, 1) + opts = opts_append(opts, opts2{k,1}, opts2{k,2}); + end + end +end +function str = opts_print(opts, sep) + % pretty print an options array + if ~exist('sep','var') || ~ischar(sep) + sep = ', '; + end + nOpts = size(opts,1); + c = cell(1,nOpts); + for k = 1:nOpts + if isempty(opts{k,2}) + c{k} = sprintf('%s', opts{k,1}); + else + c{k} = sprintf('%s=%s', opts{k,1}, opts{k,2}); + end + end + str = m2tstrjoin(c, sep); +end +% ============================================================================== +function m2t = m2t_addAxisOption(m2t, key, value) + % Adds an option to the last axesContainer + if ~exist('value','var') + value = ''; + end + m2t.axes{end}.options = opts_add(m2t.axes{end}.options, key, value); +end +% ============================================================================== +function bool = isHG2() + % Checks if graphics system is HG2 (true) or HG1 (false). + % HG1 : MATLAB up to R2014a and currently all OCTAVE versions + % HG2 : MATLAB starting from R2014b (version 8.4) + [env, envVersion] = getEnvironment(); + bool = strcmpi(env,'MATLAB') && ~isVersionBelow(envVersion, [8,4]); +end +% ============================================================================== +function str = formatAspectRatio(m2t, values) + % format the aspect ratio. Behind the scenes, formatDim is used + strs = arrayfun(@formatDim, values, 'UniformOutput', false); + str = join(m2t, strs, ' '); +end +% ============================================================================== +function str = formatDim(value, unit) + % format the value for use as a TeX dimension + if ~exist('unit','var') || isempty(unit) + unit = ''; + end + tolerance = 1e-7; + value = round(value/tolerance)*tolerance; + if value == 1 && ~isempty(unit) && unit(1) == '\' + str = unit; % just use the unit + else + % LaTeX has support for single precision (about 6.5 decimal places), + % but such accuracy is overkill for positioning. We clip to three + % decimals to overcome numerical rounding issues that tend to be very + % platform and version dependent. See also #604. + str = sprintf('%.3f', value); + str = regexprep(str, '(\d*\.\d*?)0+$', '$1'); % remove trailing zeros + str = regexprep(str, '\.$', ''); % remove trailing period + str = [str unit]; + end +end +% ============================================================================== +function [retval] = switchMatOct(matlabValue, octaveValue) + % Returns a different value for MATLAB and Octave + switch getEnvironment + case 'MATLAB' + retval = matlabValue; + case 'Octave' + retval = octaveValue; + otherwise + errorUnknownEnvironment(); + end +end +% ============================================================================== +function checkDeprecatedEnvironment(minimalVersions) + [env, envVersion] = getEnvironment(); + if isfield(minimalVersions, env) + minVersion = minimalVersions.(env); + envWithVersion = sprintf('%s %s', env, minVersion.name); + + if isVersionBelow(envVersion, minVersion.num) + ID = 'matlab2tikz:deprecatedEnvironment'; + + warningMessage = ['\n', repmat('=',1,80), '\n\n', ... + ' matlab2tikz is tested and developed on %s and newer.\n', ... + ' This script may still be able to handle your plots, but if you\n', ... + ' hit a bug, please consider upgrading your environment first.\n', ... + ' Type "warning off %s" to suppress this warning.\n', ... + '\n', repmat('=',1,80), ]; + warning(ID, warningMessage, envWithVersion, ID); + + end + else + errorUnknownEnvironment(); + end +end +% ============================================================================== +function m2t = needsPgfplotsVersion(m2t, minVersion) + if isVersionBelow(m2t.pgfplotsVersion, minVersion) + m2t.pgfplotsVersion = minVersion; + end +end +% ============================================================================== +function str = formatPgfplotsVersion(version) + version = versionArray(version); + if all(isfinite(version)) + str = sprintf('%d.',version); + str = str(1:end-1); % remove the last period + else + str = 'newest'; + end +end +% ============================================================================== +function [formatted,treeish] = VersionControlIdentifier() + % This function gives the (git) commit ID of matlab2tikz + % + % This assumes the standard directory structure as used by Nico's master branch: + % SOMEPATH/src/matlab2tikz.m with a .git directory in SOMEPATH. + % + % The HEAD of that repository is determined from file system information only + % by following dynamic references (e.g. ref:refs/heds/master) in branch files + % until an absolute commit hash (e.g. 1a3c9d1...) is found. + % NOTE: Packed branch references are NOT supported by this approach + MAXITER = 10; % stop following dynamic references after a while + formatted = ''; + REFPREFIX = 'ref:'; + isReference = @(treeish)(any(strfind(treeish, REFPREFIX))); + treeish = [REFPREFIX 'HEAD']; + try + % get the matlab2tikz directory + m2tDir = fileparts(mfilename('fullpath')); + gitDir = fullfile(m2tDir,'..','.git'); + + nIter = 1; + while isReference(treeish) + refName = treeish(numel(REFPREFIX)+1:end); + branchFile = fullfile(gitDir, refName); + + if exist(branchFile, 'file') && nIter < MAXITER + % The FID is reused in every iteration, so `onCleanup` cannot + % be used to `fclose(fid)`. But since there is very little that + % can go wrong in a single `fscanf`, it's probably best to leave + % this part as it is for the time being. + fid = fopen(branchFile,'r'); + treeish = fscanf(fid,'%s'); + fclose(fid); + nIter = nIter + 1; + else % no branch file or iteration limit reached + treeish = ''; + return; + end + end + catch + treeish = ''; + end + if ~isempty(treeish) + formatted = sprintf('(commit %s)',treeish); + end +end +% ============================================================================== +function bool = isCategoricalType(data) + % This is a wrapper function for iscategorical(), which (as February 2018) + % is not available on GNU Octave. + switch getEnvironment() + case 'MATLAB' + bool = iscategorical(data); + case 'Octave' + bool = false; + otherwise + errorUnknownEnvironment(); + end +end +% ============================================================================== diff --git a/Functions/helper_functions_community/mat2tikz/src/private/errorUnknownEnvironment.m b/Functions/helper_functions_community/mat2tikz/src/private/errorUnknownEnvironment.m new file mode 100644 index 0000000..d0e2775 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/src/private/errorUnknownEnvironment.m @@ -0,0 +1,5 @@ +function errorUnknownEnvironment() +% Throw an error to indicate an unknwon environment (i.e. not MATLAB/Octave). + error('matlab2tikz:unknownEnvironment',... + 'Unknown environment "%s". Need MATLAB(R) or Octave.', getEnvironment); +end diff --git a/Functions/helper_functions_community/mat2tikz/src/private/getEnvironment.m b/Functions/helper_functions_community/mat2tikz/src/private/getEnvironment.m new file mode 100644 index 0000000..4717cb5 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/src/private/getEnvironment.m @@ -0,0 +1,25 @@ +function [env, versionString] = getEnvironment() +% Determine environment (Octave, MATLAB) and version string +% TODO: Unify private `getEnvironment` functions + persistent cache + + if isempty(cache) + isOctave = exist('OCTAVE_VERSION', 'builtin') ~= 0; + if isOctave + env = 'Octave'; + versionString = OCTAVE_VERSION; + else + env = 'MATLAB'; + vData = ver(env); + versionString = vData.Version; + end + + % store in cache + cache.env = env; + cache.versionString = versionString; + + else + env = cache.env; + versionString = cache.versionString; + end +end diff --git a/Functions/helper_functions_community/mat2tikz/src/private/guitypes.m b/Functions/helper_functions_community/mat2tikz/src/private/guitypes.m new file mode 100644 index 0000000..b6b47de --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/src/private/guitypes.m @@ -0,0 +1,19 @@ +function types = guitypes() +% GUITYPES returns a cell array of MATLAB/Octave GUI object types +% +% Syntax +% types = guitypes() +% +% These types are ignored by matlab2tikz and figure2dot. +% +% See also: matlab2tikz, figure2dot + +types = {'uitoolbar', 'uimenu', 'uicontextmenu', 'uitoggletool',... + 'uitogglesplittool', 'uipushtool', 'hgjavacomponent',... + 'matlab.graphics.shape.internal.Button', ... + 'matlab.ui.controls.ToolbarPushButton', ... + 'matlab.ui.controls.ToolbarStateButton', ... + 'matlab.ui.controls.ToolbarDropdown', ... + 'matlab.ui.controls.AxesToolbar', ... + }; +end diff --git a/Functions/helper_functions_community/mat2tikz/src/private/isAxis3D.m b/Functions/helper_functions_community/mat2tikz/src/private/isAxis3D.m new file mode 100644 index 0000000..084d7a8 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/src/private/isAxis3D.m @@ -0,0 +1,5 @@ +function bool = isAxis3D(axisHandle) +% Check if elevation is not orthogonal to xy plane + axisView = get(axisHandle,'view'); + bool = ~ismember(axisView(2),[90,-90]); +end diff --git a/Functions/helper_functions_community/mat2tikz/src/private/isVersionBelow.m b/Functions/helper_functions_community/mat2tikz/src/private/isVersionBelow.m new file mode 100644 index 0000000..dfdc827 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/src/private/isVersionBelow.m @@ -0,0 +1,13 @@ +function isBelow = isVersionBelow(versionA, versionB) +% Checks if versionA is smaller than versionB + vA = versionArray(versionA); + vB = versionArray(versionB); + n = min(length(vA), length(vB)); + deltaAB = vA(1:n) - vB(1:n); + difference = find(deltaAB, 1, 'first'); + if isempty(difference) + isBelow = false; % equal versions + else + isBelow = (deltaAB(difference) < 0); + end +end diff --git a/Functions/helper_functions_community/mat2tikz/src/private/m2tUpdater.m b/Functions/helper_functions_community/mat2tikz/src/private/m2tUpdater.m new file mode 100644 index 0000000..52720d9 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/src/private/m2tUpdater.m @@ -0,0 +1,320 @@ +function m2tUpdater(about, verbose) +%UPDATER Auto-update matlab2tikz. +% Only for internal usage. + +% Copyright (c) 2012--2014, Nico Schlömer +% All rights reserved. +% +% Redistribution and use in source and binary forms, with or without +% modification, are permitted provided that the following conditions are +% met: +% +% * Redistributions of source code must retain the above copyright +% notice, this list of conditions and the following disclaimer. +% * Redistributions in binary form must reproduce the above copyright +% notice, this list of conditions and the following disclaimer in +% the documentation and/or other materials provided with the distribution +% +% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +% POSSIBILITY OF SUCH DAMAGE. +% ========================================================================= + fileExchangeUrl = about.website; + version = about.version; + + mostRecentVersion = determineLatestRelease(version, fileExchangeUrl); + if askToUpgrade(mostRecentVersion, version, verbose) + tryToUpgrade(fileExchangeUrl, verbose); + userInfo(verbose, ''); + end +end +% ============================================================================== +function shouldUpgrade = askToUpgrade(mostRecentVersion, version, verbose) + shouldUpgrade = false; + if ~isempty(mostRecentVersion) + userInfo(verbose, '**********************************************\n'); + userInfo(verbose, 'New version (%s) available!\n', mostRecentVersion); + userInfo(verbose, '**********************************************\n'); + + warnAboutUpgradeImplications(version, mostRecentVersion, verbose); + askToShowChangelog(version); + reply = input(' *** Would you like to upgrade? y/n [n]:','s'); + shouldUpgrade = ~isempty(reply) && strcmpi(reply(1),'y'); + if ~shouldUpgrade + userInfo(verbose, ['\nTo disable the self-updater in the future, add ' ... + '"''checkForUpdates'',false" to the parameters.\n'] ); + end + end +end +% ============================================================================== +function tryToUpgrade(fileExchangeUrl, verbose) + % Download the files and unzip its contents into two folders + % above the folder that contains the current script. + % This assumes that the file structure is something like + % + % src/matlab2tikz.m + % src/[...] + % src/private/m2tUpdater + % src/private/[...] + % AUTHORS + % ChangeLog + % [...] + % + % on the hard drive and the zip file. In particular, this assumes + % that the folder on the hard drive is writable by the user + % and that matlab2tikz.m is not symlinked from some other place. + pathstr = fileparts(mfilename('fullpath')); + targetPath = fullfile(pathstr, '..', '..'); + + % Let the user know where the .zip is downloaded to + userInfo(verbose, 'Downloading and unzipping to ''%s'' ...', targetPath); + + % Try upgrading + try + % List current folder structure. Will use last for cleanup + currentFolderFiles = rdirfiles(targetPath); + + % The FEX now forwards the download request to Github. + % Go through the forwarding to update the download count and + % unzip + html = urlread([fileExchangeUrl, '?download=true']); + expression = '(?<=\redirected)'; + url = regexp(html, expression,'match','once'); + unzippedFiles = unzip(url, targetPath); + + % The folder structure is additionally packed into the + % 'MATLAB Search Path' folder defined in FEX. Retrieve the + % top folder name + tmp = strrep(unzippedFiles,[targetPath, filesep],''); + tmp = regexp(tmp, filesep,'split','once'); + tmp = cat(1,tmp{:}); + topZipFolder = unique(tmp(:,1)); + + % If packed into the top folder, overwrite files into m2t + % main directory + if numel(topZipFolder) == 1 + unzippedFilesTarget = fullfile(targetPath, tmp(:,2)); + for ii = 1:numel(unzippedFiles) + movefile(unzippedFiles{ii}, unzippedFilesTarget{ii}) + end + % Add topZipFolder to current folder structure + currentFolderFiles = [currentFolderFiles; fullfile(targetPath, topZipFolder{1})]; + end + + cleanupOldFiles(currentFolderFiles, unzippedFilesTarget); + + userInfo(verbose, 'Upgrade has completed successfully.'); + catch + err = lasterror(); %#ok needed for Octave + + userInfo(verbose, ... + ['Upgrade has failed with error message "%s".\n', ... + 'Please install the latest version manually from %s !'], ... + err.message, fileExchangeUrl); + end +end +% ============================================================================== +function cleanupOldFiles(currentFolderFiles, unzippedFilesTarget) +% Delete files that were there in the old folder, but that are no longer +% present in the new release. + newFolderStructure = [getFolders(unzippedFilesTarget); unzippedFilesTarget]; + deleteFolderFiles = setdiff(currentFolderFiles, newFolderStructure); + for ii = 1:numel(deleteFolderFiles) + x = deleteFolderFiles{ii}; + if exist(x, 'dir') == 7 + % First check for directories since + % `exist(x, 'file')` also checks for directories! + rmdir(x,'s'); + elseif exist(x, 'file') == 2 + delete(x); + end + end +end +% ============================================================================== +function mostRecentVersion = determineLatestRelease(version, fileExchangeUrl) + % Read in the Github releases page + url = 'https://github.com/matlab2tikz/matlab2tikz/releases/'; + try + html = urlread(url); + catch %#ok + % Couldn't load the URL -- never mind. + html = ''; + warning('m2tUpdate:siteNotFound', ... + ['Cannot determine the latest version.\n', ... + 'Either your internet is down or something went wrong.\n', ... + 'You might want to check for updates by hand at %s.\n'], ... + fileExchangeUrl); + end + + % Parse tag names which are the version number in the format ##.##.## + % It assumes that releases will always be tagged with the version number + expression = '(?<=matlab2tikz\/matlab2tikz\/releases\/tag\/)\d+\.\d+\.\d+'; + tags = regexp(html, expression, 'match'); + ntags = numel(tags); + + % Keep only new releases + inew = false(ntags,1); + for ii = 1:ntags + inew(ii) = isVersionBelow(version, tags{ii}); + end + nnew = nnz(inew); + + % One new release + if nnew == 1 + mostRecentVersion = tags{inew}; + % Several new release, pick latest + elseif nnew > 1 + tags = tags(inew); + tagnum = zeros(nnew,1); + for ii = 1:nnew + tagnum(ii) = [10000,100,1] * versionArray(tags{ii}); + end + [~, imax] = max(tagnum); + mostRecentVersion = tags{imax}; + % No new + else + mostRecentVersion = ''; + end +end +% ============================================================================== +function askToShowChangelog(currentVersion) +% Asks whether the user wants to see the changelog and then shows it. + reply = input(' *** Would you like to see the changelog? y/n [y]:' ,'s'); + shouldShow = isempty(reply) || ~strcmpi(reply(1),'n') ; + if shouldShow + fprintf(1, '\n%s\n', changelogUntilVersion(currentVersion)); + end +end +% ============================================================================== +function changelog = changelogUntilVersion(currentVersion) +% This function retrieves the chunk of the changelog until the current version. + URL = 'https://github.com/matlab2tikz/matlab2tikz/raw/master/CHANGELOG.md'; + changelog = urlread(URL); + currentVersion = versionString(currentVersion); + + % Header is "# YYYY-MM-DD Version major.minor.patch [Manager](email)" + % Just match for the part until the version number. Here, we're actually + % matching a tiny bit too broad due to the periods in the version number + % but the outcome should be the same if we keep the changelog format + % identical. + pattern = ['\#\s*[\d-]+\s*Version\s*' currentVersion]; + idxVersion = regexpi(changelog, pattern); + if ~isempty(idxVersion) + changelog = changelog(1:idxVersion-1); + else + % Just show the whole changelog if we don't find the old version. + end + changelog = replaceIssuesWithUrls(changelog); +end +% ============================================================================== +function changelog = replaceIssuesWithUrls(changelog) +% Replaces GitHub issues ("#...") with URLs + baseurl = 'https://github.com/matlab2tikz/matlab2tikz/issues/'; + if strcmpi(getEnvironment(), 'MATLAB') + replacement = sprintf('#$1', baseurl); + changelog = regexprep(changelog, '\#(\d+)', replacement); + end +end +% ============================================================================== +function warnAboutUpgradeImplications(currentVersion, latestVersion, verbose) +% This warns the user about the implications of upgrading as dictated by +% Semantic Versioning. + switch upgradeSize(currentVersion, latestVersion); + case 'major' + % The API might have changed in a backwards incompatible way. + userInfo(verbose, 'This is a MAJOR upgrade!\n'); + userInfo(verbose, ' - New features may have been introduced.'); + userInfo(verbose, ' - Some old code/options may no longer work!\n'); + + case 'minor' + % The API may NOT have changed in a backwards incompatible way. + userInfo(verbose, 'This is a MINOR upgrade.\n'); + userInfo(verbose, ' - New features may have been introduced.'); + userInfo(verbose, ' - Some options may have been deprecated.'); + userInfo(verbose, ' - Old code should continue to work but might produce warnings.\n'); + + case 'patch' + % No new functionality is introduced + userInfo(verbose, 'This is a PATCH.\n'); + userInfo(verbose, ' - Only bug fixes are included in this upgrade.'); + userInfo(verbose, ' - Old code should continue to work as before.') + end + userInfo(verbose, 'Please check the changelog for detailed information.\n'); + userWarn(verbose, '\n!! By upgrading you will lose any custom changes !!\n'); +end +% ============================================================================== +function cls = upgradeSize(currentVersion, latestVersion) +% Determines whether the upgrade is major, minor or a patch. + currentVersion = versionArray(currentVersion); + latestVersion = versionArray(latestVersion); + description = {'major', 'minor', 'patch'}; + for ii = 1:numel(description) + if latestVersion(ii) > currentVersion(ii) + cls = description{ii}; + return + end + end + cls = 'unknown'; +end +% ============================================================================== +function userInfo(verbose, message, varargin) + % Display information (i.e. to stdout) + if verbose + userPrint(1, message, varargin{:}); + end +end +function userWarn(verbose, message, varargin) + % Display warnings (i.e. to stderr) + if verbose + userPrint(2, message, varargin{:}); + end +end +function userPrint(fid, message, varargin) + % Print messages (info/warnings) to a stream/file. + mess = sprintf(message, varargin{:}); + + % Replace '\n' by '\n *** ' and print. + mess = strrep( mess, sprintf('\n'), sprintf('\n *** ') ); + fprintf(fid, ' *** %s\n', mess ); +end +% ========================================================================= +function list = rdirfiles(rootdir) + % Recursive files listing + s = dir(rootdir); + list = {s.name}'; + + % Exclude .git, .svn, . and .. + [list, idx] = setdiff(list, {'.git','.svn','.','..'}); + + % Add root + list = fullfile(rootdir, list); + + % Loop for sub-directories + pdir = find([s(idx).isdir]); + for ii = pdir + list = [list; rdirfiles(list{ii})]; %#ok + end + + % Drop directories + list(pdir) = []; +end +% ========================================================================= +function list = getFolders(list) + % Extract the folder structure from a list of files and folders + + for ii = 1:numel(list) + if exist(list{ii},'file') == 2 + list{ii} = fileparts(list{ii}); + end + end + list = unique(list); +end +% ========================================================================= diff --git a/Functions/helper_functions_community/mat2tikz/src/private/m2tstrjoin.m b/Functions/helper_functions_community/mat2tikz/src/private/m2tstrjoin.m new file mode 100644 index 0000000..43efb54 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/src/private/m2tstrjoin.m @@ -0,0 +1,35 @@ +function newstr = m2tstrjoin(cellstr, delimiter, floatFormat) +% This function joins a cell of strings to a single string (with a +% given delimiter in between two strings, if desired). + if ~exist('delimiter','var') || isempty(delimiter) + delimiter = ''; + end + if ~exist('floatFormat','var') || isempty(floatFormat) + floatFormat = '%g'; + end + if isempty(cellstr) + newstr = ''; + return + end + + % convert all values to strings first + nElem = numel(cellstr); + for k = 1:nElem + if isnumeric(cellstr{k}) + cellstr{k} = sprintf(floatFormat, cellstr{k}); + elseif iscell(cellstr{k}) + cellstr{k} = m2tstrjoin(cellstr{k}, delimiter, floatFormat); + % this will fail for heavily nested cells + elseif ~ischar(cellstr{k}) + error('matlab2tikz:join:NotCellstrOrNumeric',... + 'Expected cellstr or numeric.'); + end + end + + % inspired by strjoin of recent versions of MATLAB + newstr = cell(2,nElem); + newstr(1,:) = reshape(cellstr, 1, nElem); + newstr(2,1:nElem-1) = {delimiter}; % put delimiters in-between the elements + newstr(2,end) = {''}; % for Octave 4 compatibility + newstr = [newstr{:}]; +end diff --git a/Functions/helper_functions_community/mat2tikz/src/private/versionArray.m b/Functions/helper_functions_community/mat2tikz/src/private/versionArray.m new file mode 100644 index 0000000..96af769 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/src/private/versionArray.m @@ -0,0 +1,18 @@ +function arr = versionArray(str) +% Converts a version string to an array. + if ischar(str) + % Translate version string from '2.62.8.1' to [2; 62; 8; 1]. + switch getEnvironment + case 'MATLAB' + split = regexp(str, '\.', 'split'); % compatibility MATLAB < R2013a + case 'Octave' + split = strsplit(str, '.'); + otherwise + errorUnknownEnvironment(); + end + arr = str2num(char(split)); %#ok + else + arr = str; + end + arr = arr(:)'; +end diff --git a/Functions/helper_functions_community/mat2tikz/src/private/versionString.m b/Functions/helper_functions_community/mat2tikz/src/private/versionString.m new file mode 100644 index 0000000..5f82c29 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/src/private/versionString.m @@ -0,0 +1,9 @@ +function str = versionString(arr) +% Converts a version array to string + if ischar(arr) + str = arr; + elseif isnumeric(arr) + str = sprintf('%d.', arr); + str = str(1:end-1); % remove final period + end +end diff --git a/Functions/helper_functions_community/mat2tikz/test/README.md b/Functions/helper_functions_community/mat2tikz/test/README.md new file mode 100644 index 0000000..162d01f --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/README.md @@ -0,0 +1,101 @@ +This test module is part of matlab2tikz. + +Its use is mainly of interest to the matlab2tikz developers to assert that the produced output is good. +Ideally, the tests should be run on every supported environment, i.e.: + + * MATLAB R2014a/8.3 (or an older version) + * MATLAB R2014b/8.4 (or a newer version) + * Octave 3.8 + +Preparing your environment +========================== + +Before you can run the tests, you need to make sure that you have all relevant +functions available on your path. From within the `/test` directory run the +following code in your MATLAB/Octave console: + +```matlab +addpath(pwd); % for the test harness +addpath(fullfile(pwd,'..','src')); % for matlab2tikz +addpath(fullfile(pwd,'suites')); % for the test suites +``` + +Running the tests +================= + +We have two kinds of tests runners available that each serve a slightly different +purpose. + + * "Graphical" tests produce an output report that graphically shows test + figures as generated by MATLAB/Octave and our TikZ output. + * "Headless" tests do not produce graphical output, but instead check the MD5 + hash of the generated TikZ files to make sure that the same output + as before is generated. + +It is recommended to run the headless tests first and check the problems in +the graphical tests afterwards. + +Headless tests +-------------- +These tests check that the TikZ output file produced by `matlab2tikz` matches +a reference output. The actual checking is done by hashing the file and the +corresponding hashes are stored in `.md5` files next to the test suites. +For each environment, different reference hashes can be stored. + +The headless tests can be invoked using +```matlab +testHeadless; +``` +, or, equivalently, +```matlab +makeTravisReport(testHeadless) +``` + +There are some caveats for this method of testing: + + * The MD5 hash is extremely brittle to small details in the output: e.g. + extra whitespace or some other characters will change the hash. + * This automated test does NOT test whether the output is desirable or not. + It only checks whether the previous output is not altered! + * Hence, when structural changes are made, the reference hash should be changed. + This SHOULD be motivated in the pull request (e.g. with a picture)! + +Graphical tests +--------------- +These tests allow easy comparison of a native PDF `print` output and the +output produced by `matlab2tikz`. For the large amount of cases, however, +this comparison has become somewhat unwieldy. + +You can execute the tests using +```matlab +testGraphical; +``` +or, equivalently, +```matlab +makeLatexReport(testGraphical) +``` +This generates a LaTeX report in `test/output/current/acid.tex` which can then be compiled. +Compilation of this file can be done using the Makefile `test/output/current/Makefile` if you are on a Unix-like system (OS X, Linux) or have [Cygwin](https://www.cygwin.com) installed on Windows. + +If all goes well, the result will be the file `test/output/current/acid.pdf` that contains +a list of the test figures, exported as PDF and right next to it the matlab2tikz generated plot. + +Advanced Use +------------ + +Both `testHeadless` and `testGraphical` can take multiple arguments, those are documented in the raw test runner `testMatlab2tikz` that is used behind the scenes. Note that this file sits in a private directory, so `help testMatlab2tikz` will not work! + +Also, both can be called with a single output argument, for programmatical +access to the test results as +```matlab +status = testHeadless() +``` +These test results in `status` can be passed to `saveHashTable` for updating the hash tables. +Obviously, this should be done with the due diligence! + +Automated Tests +=============== + +The automated tests run on [Travis-CI](https://travis-ci.org) for Octave and on a [personal Jenkins server](https://github.com/matlab2tikz/matlab2tikz/wiki/Jenkins) for MATLAB. +These are effectively the "headless" tests that get called by the `runMatlab2TikzTests` function. +Without verification of those automated tests, a pull request is unlikely to get merged. diff --git a/Functions/helper_functions_community/mat2tikz/test/codeReport.m b/Functions/helper_functions_community/mat2tikz/test/codeReport.m new file mode 100644 index 0000000..ccac19b --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/codeReport.m @@ -0,0 +1,280 @@ +function [ report ] = codeReport( varargin ) +%CODEREPORT Builds a report of the code health +% +% This function generates a Markdown report on the code health. At the moment +% this is limited to the McCabe (cyclomatic) complexity of a function and its +% subfunctions. +% +% This makes use of |checkcode| in MATLAB. +% +% Usage: +% +% CODEREPORT('function', functionName) to determine which function is +% analyzed. (default: matlab2tikz) +% +% CODEREPORT('complexityThreshold', integer ) to set above which complexity, a +% function is added to the report (default: 10) +% +% CODEREPORT('stream', stream) to set to which stream/file to output the report +% (default: 1, i.e. stdout). The stream is used only when no output argument +% for `codeReport` is specified!. +% +% See also: checkcode, mlint + + SM = StreamMaker(); + %% input options + ipp = m2tInputParser(); + ipp = ipp.addParamValue(ipp, 'function', 'matlab2tikz', @ischar); + ipp = ipp.addParamValue(ipp, 'complexityThreshold', 10, @isnumeric); + ipp = ipp.addParamValue(ipp, 'stream', 1, SM.isStream); + ipp = ipp.parse(ipp, varargin{:}); + + stream = SM.make(ipp.Results.stream, 'w'); + + %% generate report data + data = checkcode(ipp.Results.function,'-cyc','-struct'); + [complexityAll, mlintMessages] = splitCycloComplexity(data); + + %% analyze cyclomatic complexity + categorizeComplexity = @(x) categoryOfComplexity(x, ... + ipp.Results.complexityThreshold, ... + ipp.Results.function); + + complexityAll = arrayfun(@parseCycloComplexity, complexityAll); + complexityAll = arrayfun(categorizeComplexity, complexityAll); + + complexity = filter(complexityAll, @(x) strcmpi(x.category, 'Bad')); + complexity = sortBy(complexity, 'line', 'ascend'); + complexity = sortBy(complexity, 'complexity', 'descend'); + + [complexityStats] = complexityStatistics(complexityAll); + + %% analyze other messages + %TODO: handle all mlint messages and/or other metrics of the code + + %% format report + dataStr = complexity; + dataStr = arrayfun(@(d) mapField(d, 'function', @markdownInlineCode), dataStr); + if ~isempty(dataStr) + dataStr = addFooterRow(dataStr, 'complexity', @sum, {'line',0, 'function',bold('Total')}); + end + dataStr = arrayfun(@(d) mapField(d, 'line', @integerToString), dataStr); + dataStr = arrayfun(@(d) mapField(d, 'complexity', @integerToString), dataStr); + + report = makeTable(dataStr, {'function', 'complexity'}, ... + {'Function', 'Complexity'}); + + %% command line usage + if nargout == 0 + if ismember(stream.name, {'stdout','stderr'}) + stream.print('%s\n', codelinks(report, ipp.Results.function)); + else + stream.print('%s\n', report); + end + + figure('name',sprintf('Complexity statistics of %s', ipp.Results.function)); + h = statisticsPlot(complexityStats, 'Complexity', 'Number of functions'); + for hh = h + plot(hh, [1 1]*ipp.Results.complexityThreshold, ylim(hh), ... + 'k--','DisplayName','Threshold'); + end + legend(h(1),'show','Location','NorthEast'); + + clear report + end + +end +%% CATEGORIZATION ============================================================== +function [complexity, others] = splitCycloComplexity(list) +% splits codereport into McCabe complexity and others + filter = @(l) ~isempty(strfind(l.message, 'McCabe complexity')); + idxComplexity = arrayfun(filter, list); + complexity = list( idxComplexity); + others = list(~idxComplexity); +end +function [data] = categoryOfComplexity(data, threshold, mainFunc) +% categorizes the complexity as "Good", "Bad" or "Accepted" + TOKEN = '#COMPLEX'; % token to signal allowed complexity + + try %#ok + helpStr = help(sprintf('%s>%s', mainFunc, data.function)); + if ~isempty(strfind(helpStr, TOKEN)) + data.category = 'Accepted'; + return; + end + end + if data.complexity > threshold + data.category = 'Bad'; + else + data.category = 'Good'; + end +end + +%% PARSING ===================================================================== +function [out] = parseCycloComplexity(in) +% converts McCabe complexity report strings into a better format + out = regexp(in.message, ... + 'The McCabe complexity of ''(?[A-Za-z0-9_]+)'' is (?[0-9]+).', ... + 'names'); + out.complexity = str2double(out.complexity); + out.line = in.line; +end + +%% DATA PROCESSING ============================================================= +function selected = filter(list, filterFunc) +% filters an array according to a binary function + idx = logical(arrayfun(filterFunc, list)); + selected = list(idx); +end +function [data] = mapField(data, field, mapping) + data.(field) = mapping(data.(field)); +end +function sorted = sortBy(list, fieldName, mode) +% sorts a struct array by a single field +% extra arguments are as for |sort| + values = arrayfun(@(m)m.(fieldName), list); + [dummy, idxSorted] = sort(values(:), 1, mode); %#ok + sorted = list(idxSorted); +end + +function [stat] = complexityStatistics(list) +% calculate some basic statistics of the complexities + + stat.values = arrayfun(@(c)(c.complexity), list); + stat.binCenter = sort(unique(stat.values)); + + categoryPerElem = {list.category}; + stat.categories = unique(categoryPerElem); + nCategories = numel(stat.categories); + + groupedHist = zeros(numel(stat.binCenter), nCategories); + for iCat = 1:nCategories + category = stat.categories{iCat}; + idxCat = ismember(categoryPerElem, category); + groupedHist(:,iCat) = hist(stat.values(idxCat), stat.binCenter); + end + + stat.histogram = groupedHist; + stat.median = median(stat.values); +end +function [data] = addFooterRow(data, column, func, otherFields) +% adds a footer row to data table based on calculations of a single column +footer = data(end); +for iField = 1:2:numel(otherFields) + field = otherFields{iField}; + value = otherFields{iField+1}; + footer.(field) = value; +end +footer.(column) = func([data(:).(column)]); +data(end+1) = footer; +end + +%% FORMATTING ================================================================== +function str = integerToString(value) +% convert integer to string + str = sprintf('%d',value); +end +function str = markdownInlineCode(str) +% format as inline code for markdown + str = sprintf('`%s`', str); +end +function str = makeTable(data, fields, header) +% make a markdown table from struct array + nData = numel(data); + str = ''; + if nData == 0 + return; % empty input + end + %TODO: use gfmTable from makeTravisReport instead to do the formatting + + % determine column sizes + nFields = numel(fields); + table = cell(nFields, nData); + columnWidth = zeros(1,nFields); + for iField = 1:nFields + field = fields{iField}; + table(iField, :) = {data(:).(field)}; + columnWidth(iField) = max(cellfun(@numel, table(iField, :))); + end + columnWidth = max(columnWidth, cellfun(@numel, header)); + columnWidth = columnWidth + 2; % empty space left and right + columnWidth([1,end]) = columnWidth([1,end]) - 1; % except at the edges + + % format table inside cell array + table = [header; table']; + for iField = 1:nFields + FORMAT = ['%' int2str(columnWidth(iField)) 's']; + + for jData = 1:size(table, 1) + table{jData, iField} = strjust(sprintf(FORMAT, ... + table{jData, iField}), 'center'); + end + end + + % insert separator + table = [table(1,:) + arrayfun(@(n) repmat('-',1,n), columnWidth, 'UniformOutput',false) + table(2:end,:)]'; + + % convert cell array to string + FORMAT = ['%s' repmat('|%s', 1,nFields-1) '\n']; + str = sprintf(FORMAT, table{:}); + +end + +function str = codelinks(str, functionName) +% replaces inline functions with clickable links in MATLAB +str = regexprep(str, '`([A-Za-z0-9_]+)`', ... + ['`$1`']); +%NOTE: editing function>subfunction will focus on that particular subfunction +% in the editor (this also works for the main function) +end +function str = bold(str) +str = ['**' str '**']; +end + +%% PLOTTING ==================================================================== +function h = statisticsPlot(stat, xLabel, yLabel) +% plot a histogram and box plot + nCategories = numel(stat.categories); + colors = colorscheme; + + h(1) = subplot(5,1,1:4); + hold all; + hb = bar(stat.binCenter, stat.histogram, 'stacked'); + + for iCat = 1:nCategories + category = stat.categories{iCat}; + + set(hb(iCat), 'DisplayName', category, 'FaceColor', colors.(category), ... + 'LineStyle','none'); + end + + %xlabel(xLabel); + ylabel(yLabel); + + h(2) = subplot(5,1,5); + hold all; + + boxplot(stat.values,'orientation','horizontal',... + 'boxstyle', 'outline', ... + 'symbol', 'o', ... + 'colors', colors.All); + xlabel(xLabel); + + xlims = [min(stat.binCenter)-1 max(stat.binCenter)+1]; + c = 1; + ylims = (ylim(h(2)) - c)/3 + c; + + set(h,'XTickMode','manual','XTick',stat.binCenter,'XLim',xlims); + set(h(1),'XTickLabel',''); + set(h(2),'YTickLabel','','YLim',ylims); + linkaxes(h, 'x'); +end +function colors = colorscheme() +% recognizable color scheme for the categories + colors.All = [ 0 113 188]/255; + colors.Good = [118 171 47]/255; + colors.Bad = [161 19 46]/255; + colors.Accepted = [236 176 31]/255; +end diff --git a/Functions/helper_functions_community/mat2tikz/test/compareTimings.m b/Functions/helper_functions_community/mat2tikz/test/compareTimings.m new file mode 100644 index 0000000..021167c --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/compareTimings.m @@ -0,0 +1,256 @@ +function compareTimings(statusBefore, statusAfter) +% COMPARETIMINGS compare timing of matlab2tikz test suite runs +% +% This function plots some analysis plots of the timings of different test +% cases. When the test suite is run repeatedly, the median statistics are +% reported as well as the individual runs. +% +% Usage: +% COMPARETIMINGS(statusBefore, statusAfter) +% +% Parameters: +% - statusBefore and statusAfter are expected to be +% N x R cell arrays, each cell contains a status of a test case +% where there are N test cases, repeated R times each. +% +% You can build such cells, e.g. with the following snippet. +% +% suite = @ACID +% N = numel(suite(0)); % number of test cases +% R = 10; % number of repetitions of each test case +% +% statusBefore = cell(N, R); +% for r = 1:R +% statusBefore(:, r) = testHeadless; +% end +% +% % now check out the after commit +% +% statusAfter = cell(N, R); +% for r = 1:R +% statusAfter(:, r) = testHeadless; +% end +% +% compareTimings(statusBefore, statusAfter) +% +% See also: testHeadless + +%% Extract timing information +time_cf = extract(statusBefore, statusAfter, @(s) s.tikzStage.cleanfigure_time); +time_m2t = extract(statusBefore, statusAfter, @(s) s.tikzStage.m2t_time); +%% Construct plots +hax(1) = subplot(3,2,1); +histograms(time_cf, 'cleanfigure'); +legend('show') + +hax(2) = subplot(3,2,3); +histograms(time_m2t, 'matlab2tikz'); +legend('show') +linkaxes(hax([1 2]),'x'); + +hax(3) = subplot(3,2,5); +histogramSpeedup('cleanfigure', time_cf, 'matlab2tikz', time_m2t); +legend('show'); + +hax(4) = subplot(3,2,2); +plotByTestCase(time_cf, 'cleanfigure'); +legend('show') + +hax(5) = subplot(3,2,4); +plotByTestCase(time_m2t, 'matlab2tikz'); +legend('show') + +hax(6) = subplot(3,2,6); +plotSpeedup('cleanfigure', time_cf, 'matlab2tikz', time_m2t); +legend('show'); + +linkaxes(hax([4 5 6]), 'x'); + +% ------------------------------------------------------------------------------ +end +%% Data processing +function timing = extract(statusBefore, statusAfter, func) + otherwiseNaN = {'ErrorHandler', @(varargin) NaN}; + + timing.before = cellfun(func, statusBefore, otherwiseNaN{:}); + timing.after = cellfun(func, statusAfter, otherwiseNaN{:}); +end +function [names,timings] = splitNameTiming(vararginAsCell) + names = vararginAsCell(1:2:end-1); + timings = vararginAsCell(2:2:end); +end + +%% Plot subfunctions +function [h] = histograms(timing, name) + % plot histogram of time measurements + colors = colorscheme; + histostyle = {'DisplayStyle', 'bar',... + 'Normalization','pdf',... + 'EdgeColor','none',... + 'BinWidth',0.025}; + + hold on; + h{1} = myHistogram(timing.before, histostyle{:}, ... + 'FaceColor', colors.before, ... + 'DisplayName', 'Before'); + h{2} = myHistogram(timing.after , histostyle{:}, ... + 'FaceColor', colors.after,... + 'DisplayName', 'After'); + + xlabel(sprintf('%s runtime [s]',name)) + ylabel('Empirical PDF'); +end +function [h] = histogramSpeedup(varargin) + % plot histogram of observed speedup + histostyle = {'DisplayStyle', 'bar',... + 'Normalization','pdf',... + 'EdgeColor','none'}; + + [names,timings] = splitNameTiming(varargin); + nData = numel(timings); + h = cell(nData, 1); + minTime = NaN; maxTime = NaN; + for iData = 1:nData + name = names{iData}; + timing = timings{iData}; + + hold on; + speedup = computeSpeedup(timing); + color = colorOptionsOfName(name, 'FaceColor'); + + h{iData} = myHistogram(speedup, histostyle{:}, color{:},... + 'DisplayName', name); + + [minTime, maxTime] = minAndMax(speedup, minTime, maxTime); + end + xlabel('Speedup') + ylabel('Empirical PDF'); + set(gca,'XScale','log', 'XLim', [minTime, maxTime].*[0.9 1.1]); +end +function [h] = plotByTestCase(timing, name) + % plot all time measurements per test case + colors = colorscheme; + hold on; + if size(timing.before, 2) > 1 + h{3} = plot(timing.before, '.',... + 'Color', colors.before, 'HandleVisibility', 'off'); + h{4} = plot(timing.after, '.',... + 'Color', colors.after, 'HandleVisibility', 'off'); + end + h{1} = plot(median(timing.before, 2), '-',... + 'LineWidth', 2, ... + 'Color', colors.before, ... + 'DisplayName', 'Before'); + h{2} = plot(median(timing.after, 2), '-',... + 'LineWidth', 2, ... + 'Color', colors.after,... + 'DisplayName', 'After'); + + ylabel(sprintf('%s runtime [s]', name)); + set(gca,'YScale','log') +end +function [h] = plotSpeedup(varargin) + % plot speed up per test case + [names, timings] = splitNameTiming(varargin); + + nDatasets = numel(names); + minTime = NaN; + maxTime = NaN; + h = cell(nDatasets, 1); + for iData = 1:nDatasets + name = names{iData}; + timing = timings{iData}; + color = colorOptionsOfName(name); + + hold on + [speedup, medSpeedup] = computeSpeedup(timing); + if size(speedup, 2) > 1 + plot(speedup, '.', color{:}, 'HandleVisibility','off'); + end + h{iData} = plot(medSpeedup, color{:}, 'DisplayName', name, ... + 'LineWidth', 2); + + [minTime, maxTime] = minAndMax(speedup, minTime, maxTime); + end + + nTests = size(speedup, 1); + plot([-nTests nTests*2], ones(2,1), 'k','HandleVisibility','off'); + + legend('show', 'Location','NorthWest') + set(gca,'YScale','log','YLim', [minTime, maxTime].*[0.9 1.1], ... + 'XLim', [0 nTests+1]) + xlabel('Test case'); + ylabel('Speed-up (t_{before}/t_{after})'); +end + +%% Histogram wrapper +function [h] = myHistogram(data, varargin) +% this is a very crude wrapper that mimics Histogram in R2014a and older + if ~isempty(which('histogram')) + h = histogram(data, varargin{:}); + else % no "histogram" available + options = struct(varargin{:}); + + minData = min(data(:)); + maxData = max(data(:)); + if isfield(options, 'BinWidth') + numBins = ceil((maxData-minData)/options.BinWidth); + elseif isfield(options, 'NumBins') + numBins = options.NumBins; + else + numBins = 10; + end + [counts, bins] = hist(data(:), numBins); + if isfield(options,'Normalization') && strcmp(options.Normalization,'pdf') + binWidth = mean(diff(bins)); + counts = counts./sum(counts)/binWidth; + end + h = bar(bins, counts, 1); + + % transfer properties as well + names = fieldnames(options); + for iName = 1:numel(names) + option = names{iName}; + if isprop(h, option) + set(h, option, options.(option)); + end + end + set(allchild(h),'FaceAlpha', 0.75); % only supported with OpenGL renderer + % but this should look a bit similar with matlab2tikz then... + end +end + +%% Calculations +function [speedup, medSpeedup] = computeSpeedup(timing) + % computes the timing speedup (and median speedup) + dRep = 2; % dimension containing the repeated tests + speedup = timing.before ./ timing.after; + medSpeedup = median(timing.before, dRep) ./ median(timing.after, dRep); +end +function [minTime, maxTime] = minAndMax(speedup, minTime, maxTime) + % calculates the minimum/maximum time in an array and peviously + % computed min/max times + minTime = min([minTime; speedup(:)]); + maxTime = min([maxTime; speedup(:)]); +end +%% Color scheme +function colors = colorscheme() +% defines the color scheme + colors.matlab2tikz = [161 19 46]/255; + colors.cleanfigure = [ 0 113 188]/255; + colors.before = [236 176 31]/255; + colors.after = [118 171 47]/255; +end +function color = colorOptionsOfName(name, keyword) +% returns a cell array with a keyword (default: 'Color') and a named color +% if it exists in the colorscheme + if ~exist('keyword','var') || isempty(keyword) + keyword = 'Color'; + end + colors = colorscheme; + if isfield(colors,name) + color = {keyword, colors.(name)}; + else + color = {}; + end +end diff --git a/Functions/helper_functions_community/mat2tikz/test/examples/example_bar_plot.m b/Functions/helper_functions_community/mat2tikz/test/examples/example_bar_plot.m new file mode 100644 index 0000000..fa347c7 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/examples/example_bar_plot.m @@ -0,0 +1,53 @@ +function example_bar_plot() +test_data =[18 0; 20 0; 21 2; 30 14; 35 34; 40 57; 45 65; 50 46; 55 9; 60 2; 65 1; 70 0]; + +% Create figure +figure1 = figure('Color',[1 1 1]); + +subplot(1,2,1) + + +hb=barh(test_data(:,1),test_data(:,2),'DisplayName','Test Data'); + +ylabel('parameter [units]'); +xlabel('#'); +legend('show','Location','northwest'); +subplot(1,2,2) + + +hb=bar(test_data(:,1),test_data(:,2),'DisplayName','Test Data'); + +xlabel('parameter [units]'); +ylabel('#'); +legend('show','Location','northwest'); + + +xdata=test_data(:,1); +barWidth=test_getBarWidthInAbsolutUnits(hb); + +x_l=xdata-barWidth/2; +x_u=xdata+barWidth/2; +max_y=max(test_data(:,2))*1.2; +x=[]; +y=[]; +for i=1:length(x_l) + x = [x , x_l(i),x_l(i),nan,x_u(i),x_u(i),nan]; + y = [y, 0,max_y ,nan,0 ,max_y ,nan]; + + +end +hold on +plot(x,y,'r'); + +matlab2tikz('figurehandle',figure1,'filename','example_v_bar_plot.tex' ,'standalone', true); + + + function BarWidth=test_getBarWidthInAbsolutUnits(h) + % astimates the width of a bar plot + XData_bar=get(h,'XData'); + length_bar = length(XData_bar); + BarWidth= get(h, 'BarWidth'); + if length_bar > 1 + BarWidth = min(diff(XData_bar))*BarWidth; + end + diff --git a/Functions/helper_functions_community/mat2tikz/test/examples/example_quivers.m b/Functions/helper_functions_community/mat2tikz/test/examples/example_quivers.m new file mode 100644 index 0000000..a881f94 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/examples/example_quivers.m @@ -0,0 +1,80 @@ +%% Quiver calculations +% These are calculations for the quiver dimensions as implemented in MATLAB +% (HG1) as in the |quiver.m| function. +% +% For HG2 and Octave, the situation might be different. +% +% A single quiver is defined as: +% +% C +% \ +% \ +% A ----------------- B +% / +% / +% D +% +% To know the dimensions of the arrow head, MATLAB defines the quantities +% alpha = beta = 0.33 that determine the coordinates of C and D as given below. + +clc; +clear variables; +close all; + +%% Parameters +try + syms x y z u v w alpha beta epsilon real +catch + warning('Symbolic toolbox not found. Interpret the values with care!'); + x = randn(); y = randn(); z = randn(); + u = randn(); v = randn(); w = randn(); +end +alpha = 0.33; +beta = alpha; +epsilon = 0; +is2D = true; + +%% Coordinates as defined in MATLAB +% Note that in 3D, the arrow head is oriented in a weird way. Let' just ignore +% that and only focus on 2D and use the same in 3D. Due to the lack +% of [u,v,w]-symmetry in those equations, the angle is bound to depend on the +% length of |delta|, i.e. something we don't know beforehand. +A = [x y z].'; +delta = [u v w].'; +B = A + delta; +C = B - alpha*[u+beta*(v+epsilon); + v-beta*(u+epsilon) + w]; +D = B - alpha*[u-beta*(v+epsilon); + v+beta*(u+epsilon) + w]; + +if is2D + A = A(1:2); + B = B(1:2); + C = C(1:2); + D = D(1:2); + delta = delta(1:2); +end + +%% Calculating the angle of the arrowhead +% Calculate the cos(angle) using the inner product +unitVector = @(v) v/norm(v); +cosAngleBetween = @(a,b,c) unitVector(a-b).' * unitVector(c-b); + +cosTwiceTheta = cosAngleBetween(C,B,D); +if isa(cosTwiceTheta, 'sym') + cosTwiceTheta = simplify(cosTwiceTheta); +end + +theta = acos(cosTwiceTheta) / 2 + +radToDeg = @(rads) (rads * 180 / pi); + +thetaVal = radToDeg(theta) +try + thetaVal = double(thetaVal) +end + +% For the MATLAB parameters alpha=beta=0.33, we get theta = 18.263 degrees. + diff --git a/Functions/helper_functions_community/mat2tikz/test/makeLatexReport.m b/Functions/helper_functions_community/mat2tikz/test/makeLatexReport.m new file mode 100644 index 0000000..f91361b --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/makeLatexReport.m @@ -0,0 +1,222 @@ +function makeLatexReport(status, output) +% generate a LaTeX report +% +% + if ~exist('output','var') + output = m2troot('test','output','current'); + end + % first, initialize the tex output + SM = StreamMaker(); + stream = SM.make(fullfile(output, 'acid.tex'), 'w'); + + texfile_init(stream); + + printFigures(stream, status); + printSummaryTable(stream, status); + printErrorMessages(stream, status); + printEnvironmentInfo(stream, status); + + texfile_finish(stream); +end +% ========================================================================= +function texfile_init(stream) + + stream.print(['\\documentclass[landscape]{scrartcl}\n' , ... + '\\pdfminorversion=6\n\n' , ... + '\\usepackage{amsmath} %% required for $\\text{xyz}$\n\n', ... + '\\usepackage{hyperref}\n' , ... + '\\usepackage{graphicx}\n' , ... + '\\usepackage{epstopdf}\n' , ... + '\\usepackage{tikz}\n' , ... + '\\usetikzlibrary{plotmarks}\n\n' , ... + '\\usepackage{pgfplots}\n' , ... + '\\pgfplotsset{compat=newest}\n\n' , ... + '\\usepackage[margin=0.5in]{geometry}\n' , ... + '\\newlength\\figurewidth\n' , ... + '\\setlength\\figurewidth{0.4\\textwidth}\n\n' , ... + '\\begin{document}\n\n']); + +end +% ========================================================================= +function texfile_finish(stream) + stream.print('\\end{document}'); +end +% ========================================================================= +function printFigures(stream, status) + for k = 1:length(status) + texfile_addtest(stream, status{k}); + end +end +% ========================================================================= +function printSummaryTable(stream, status) + texfile_tab_completion_init(stream) + for k = 1:length(status) + stat = status{k}; + testNumber = stat.index; + % Break table up into pieces if it gets too long for one page + %TODO: use booktabs instead + %TODO: maybe just write a function to construct the table at once + % from a cell array (see makeTravisReport for GFM counterpart) + if ~mod(k,35) + texfile_tab_completion_finish(stream); + texfile_tab_completion_init(stream); + end + + stream.print('%d & \\texttt{%s}', testNumber, name2tex(stat.function)); + if stat.skip + stream.print(' & --- & skipped & ---'); + else + for err = [stat.plotStage.error, ... + stat.saveStage.error, ... + stat.tikzStage.error] + if err + stream.print(' & \\textcolor{red}{failed}'); + else + stream.print(' & \\textcolor{green!50!black}{passed}'); + end + end + end + stream.print(' \\\\\n'); + end + texfile_tab_completion_finish(stream); +end +% ========================================================================= +function printErrorMessages(stream, status) + if errorHasOccurred(status) + stream.print('\\section*{Error messages}\n\\scriptsize\n'); + for k = 1:length(status) + stat = status{k}; + testNumber = stat.index; + if isempty(stat.plotStage.message) && ... + isempty(stat.saveStage.message) && ... + isempty(stat.tikzStage.message) + continue % No error messages for this test case + end + + stream.print('\n\\subsection*{Test case %d: \\texttt{%s}}\n', testNumber, name2tex(stat.function)); + print_verbatim_information(stream, 'Plot generation', stat.plotStage.message); + print_verbatim_information(stream, 'PDF generation' , stat.saveStage.message); + print_verbatim_information(stream, 'matlab2tikz' , stat.tikzStage.message); + end + stream.print('\n\\normalsize\n\n'); + end +end +% ========================================================================= +function printEnvironmentInfo(stream, status) + [env,versionString] = getEnvironment(); + + testsuites = unique(cellfun(@(s) func2str(s.testsuite) , status, ... + 'UniformOutput', false)); + testsuites = name2tex(m2tstrjoin(testsuites, ', ')); + + stream.print(['\\newpage\n',... + '\\begin{tabular}{ll}\n',... + ' Suite & ' testsuites ' \\\\ \n', ... + ' Created & ' datestr(now) ' \\\\ \n', ... + ' OS & ' OSVersion ' \\\\ \n',... + ' ' env ' & ' versionString ' \\\\ \n', ... + VersionControlIdentifier, ... + ' TikZ & \\expandafter\\csname ver@tikz.sty\\endcsname \\\\ \n',... + ' Pgfplots & \\expandafter\\csname ver@pgfplots.sty\\endcsname \\\\ \n',... + '\\end{tabular}\n']); + +end +% ========================================================================= +function print_verbatim_information(stream, title, contents) + if ~isempty(contents) + stream.print(['\\subsubsection*{%s}\n', ... + '\\begin{verbatim}\n%s\\end{verbatim}\n'], ... + title, contents); + end +end +% ========================================================================= +function texfile_addtest(stream, status) +% Actually add the piece of LaTeX code that'll later be used to display +% the given test. + if ~status.skip + + ref_error = status.plotStage.error; + gen_error = status.tikzStage.error; + + ref_file = status.saveStage.texReference; + gen_file = status.tikzStage.pdfFile; + + stream.print(... + ['\\begin{figure}\n' , ... + ' \\centering\n' , ... + ' \\begin{tabular}{cc}\n' , ... + ' %s & %s \\\\\n' , ... + ' reference rendering & generated\n' , ... + ' \\end{tabular}\n' , ... + ' \\caption{%s \\texttt{%s}, \\texttt{%s(%d)}.%s}\n', ... + '\\end{figure}\n' , ... + '\\clearpage\n\n'],... + include_figure(ref_error, 'includegraphics', ref_file), ... + include_figure(gen_error, 'includegraphics', gen_file), ... + status.description, ... + name2tex(status.function), name2tex(status.testsuite), status.index, ... + formatIssuesForTeX(status.issues)); + end +end +% ========================================================================= +function str = include_figure(errorOccured, command, filename) + if errorOccured + str = sprintf(['\\tikz{\\draw[red,thick] ', ... + '(0,0) -- (\\figurewidth,\\figurewidth) ', ... + '(0,\\figurewidth) -- (\\figurewidth,0);}']); + else + switch command + case 'includegraphics' + strFormat = '\\includegraphics[width=\\figurewidth]{%s}'; + case 'input' + strFormat = '\\input{%s}'; + otherwise + error('Matlab2tikz_acidtest:UnknownFigureCommand', ... + 'Unknown figure command "%s"', command); + end + str = sprintf(strFormat, filename); + end +end +% ========================================================================= +function texfile_tab_completion_init(stream) + + stream.print(['\\clearpage\n\n' , ... + '\\begin{table}\n' , ... + '\\centering\n' , ... + '\\caption{Test case completion summary}\n' , ... + '\\begin{tabular}{rlccc}\n' , ... + 'No. & Test case & Plot & PDF & TikZ \\\\\n' , ... + '\\hline\n']); + +end +% ========================================================================= +function texfile_tab_completion_finish(stream) + + stream.print( ['\\end{tabular}\n' , ... + '\\end{table}\n\n' ]); + +end +% ========================================================================= +function texName = name2tex(matlabIdentifier) +% convert a MATLAB identifier/function handle to a TeX string + if isa(matlabIdentifier, 'function_handle') + matlabIdentifier = func2str(matlabIdentifier); + end + texName = strrep(matlabIdentifier, '_', '\_'); +end +% ========================================================================= +function str = formatIssuesForTeX(issues) +% make links to GitHub issues for the LaTeX output + issues = issues(:)'; + if isempty(issues) + str = ''; + return + end + BASEURL = 'https://github.com/matlab2tikz/matlab2tikz/issues/'; + SEPARATOR = sprintf(' \n'); + strs = arrayfun(@(n) sprintf(['\\href{' BASEURL '%d}{\\#%d}'], n,n), issues, ... + 'UniformOutput', false); + strs = [strs; repmat({SEPARATOR}, 1, numel(strs))]; + str = sprintf('{\\color{blue} \\texttt{%s}}', [strs{:}]); +end +% ============================================================================== diff --git a/Functions/helper_functions_community/mat2tikz/test/makeTapReport.m b/Functions/helper_functions_community/mat2tikz/test/makeTapReport.m new file mode 100644 index 0000000..73b68a0 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/makeTapReport.m @@ -0,0 +1,74 @@ +function makeTapReport(status, varargin) +% Makes a Test Anything Protocol report +% +% This function produces a testing report of HEADLESS tests for +% display on Jenkins (or any other TAP-compatible system) +% +% MAKETAPREPORT(status) produces the report from the `status` output of +% `testHeadless`. +% +% MAKETAPREPORT(status, 'stream', FID, ...) changes the filestream to use +% to output the report to. (Default: 1 (stdout)). +% +% TAP Specification: https://testanything.org +% +% See also: testHeadless, makeTravisReport, makeLatexReport + + %% Parse input arguments + SM = StreamMaker(); + ipp = m2tInputParser(); + + ipp = ipp.addRequired(ipp, 'status', @iscell); + ipp = ipp.addParamValue(ipp, 'stream', 1, SM.isStream); + + ipp = ipp.parse(ipp, status, varargin{:}); + arg = ipp.Results; + + %% Construct stream + stream = SM.make(arg.stream, 'w'); + + %% build report + printTAPVersion(stream); + printTAPPlan(stream, status); + for iStatus = 1:numel(status) + printTAPReport(stream, status{iStatus}, iStatus); + end +end +% ============================================================================== +function printTAPVersion(stream) + % prints the TAP version + stream.print('TAP version 13\n'); +end +function printTAPPlan(stream, statuses) + % prints the TAP test plan + firstTest = 1; + lastTest = numel(statuses); + stream.print('%d..%d\n', firstTest, lastTest); +end +function printTAPReport(stream, status, testNum) + % prints a TAP test case report + message = status.function; + + if hasTestFailed(status) + result = 'not ok'; + else + result = 'ok'; + end + directives = getTAPDirectives(status); + + stream.print('%s %d %s %s\n', result, testNum, message, directives); + + %TODO: we can provide more information on the failure using YAML syntax +end +function directives = getTAPDirectives(status) + % add TAP directive (a todo or skip) to the test directives + directives = {}; + if status.skip + directives{end+1} = '# SKIP skipped'; + end + if status.unreliable + directives{end+1} = '# TODO unreliable'; + end + directives = strtrim(m2tstrjoin(directives, ' ')); +end +% ============================================================================== diff --git a/Functions/helper_functions_community/mat2tikz/test/makeTravisReport.m b/Functions/helper_functions_community/mat2tikz/test/makeTravisReport.m new file mode 100644 index 0000000..60446cf --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/makeTravisReport.m @@ -0,0 +1,360 @@ +function [nErrors] = makeTravisReport(status, varargin) +% Makes a readable report for Travis/Github of test results +% +% This function produces a testing report of HEADLESS tests for +% display on GitHub and Travis. +% +% MAKETRAVISREPORT(status) produces the report from the `status` output of +% `testHeadless`. +% +% MAKETRAVISREPORT(status, 'stream', FID, ...) changes the filestream to use +% to output the report to. (Default: 1 (stdout)). +% +% MAKETRAVISREPORT(status, 'length', CHAR, ...) changes the report length. +% A few values are possible that cover different aspects in less/more detail. +% - 'default': all unreliable tests, failed & skipped tests and summary +% - 'short' : only show the brief summary +% - 'long' : all tests + summary +% +% See also: testHeadless, makeLatexReport + + SM = StreamMaker(); + %% Parse input arguments + ipp = m2tInputParser(); + + ipp = ipp.addRequired(ipp, 'status', @iscell); + ipp = ipp.addParamValue(ipp, 'stream', 1, SM.isStream); + ipp = ipp.addParamValue(ipp, 'length', 'default', @isReportLength); + + ipp = ipp.parse(ipp, status, varargin{:}); + arg = ipp.Results; + arg.length = lower(arg.length); + stream = SM.make(arg.stream, 'w'); + + %% transform status data into groups + S = splitStatuses(status); + + %% build report + stream.print(gfmHeader(describeEnvironment)); + reportUnreliableTests(stream, arg, S); + reportReliableTests(stream, arg, S); + displayTestSummary(stream, S); + + %% set output arguments if needed + if nargout >= 1 + nErrors = countNumberOfErrors(S.reliable); + end +end +% == INPUT VALIDATOR FUNCTIONS ================================================= +function bool = isReportLength(val) + % validates the report length + bool = ismember(lower(val), {'default','short','long'}); +end +% == GITHUB-FLAVORED MARKDOWN FUNCTIONS ======================================== +function str = gfmTable(data, header, alignment) + % Construct a Github-flavored Markdown table + % + % Arguments: + % - data: nRows x nCols cell array that represents the data + % - header: cell array with the (nCol) column headers + % - alignment: alignment specification per column + % * 'l': left-aligned (default) + % * 'c': centered + % * 'r': right-aligned + % When not enough entries are specified, the specification is repeated + % cyclically. + % + % Output: table as a string + % + % See https://help.github.com/articles/github-flavored-markdown/#tables + + % input argument validation and normalization + nCols = size(data, 2); + if ~exist('alignment','var') || isempty(alignment) + alignment = 'l'; + end + if numel(alignment) < nCols + % repeat the alignment specifications along the columns + alignment = repmat(alignment, 1, nCols); + alignment = alignment(1:nCols); + end + + % calculate the required column width + cellWidth = cellfun(@length, [header(:)' ;data]); + columnWidth = max(max(cellWidth, [], 1),3); % use at least 3 places + + % prepare the table format + COLSEP = '|'; ROWSEP = sprintf('\n'); + rowformat = [COLSEP sprintf([' %%%ds ' COLSEP], columnWidth) ROWSEP]; + alignmentRow = formatAlignment(alignment, columnWidth); + + % actually print the table + fullTable = [header; alignmentRow; data]; + strs = cell(size(fullTable,1), 1); + for iRow = 1:numel(strs) + thisRow = fullTable(iRow,:); + %TODO: maybe preprocess thisRow with strjust first + strs{iRow} = sprintf(rowformat, thisRow{:}); + end + str = [strs{:}]; + + %--------------------------------------------------------------------------- + function alignRow = formatAlignment(alignment, columnWidth) + % Construct a row of dashes to specify the alignment of each column + % See https://help.github.com/articles/github-flavored-markdown/#tables + DASH = '-'; COLON = ':'; + N = numel(columnWidth); + alignRow = arrayfun(@(w) repmat(DASH, 1, w), columnWidth, ... + 'UniformOutput', false); + for iColumn = 1:N + thisAlign = alignment(iColumn); + thisSpec = alignRow{iColumn}; + switch lower(thisAlign) + case 'l' + thisSpec(1) = COLON; + case 'r' + thisSpec(end) = COLON; + case 'c' + thisSpec([1 end]) = COLON; + otherwise + error('gfmTable:BadAlignment','Unknown alignment "%s"',... + thisAlign); + end + alignRow{iColumn} = thisSpec; + end + end +end +function str = gfmCode(str, inline, language) + % Construct a GFM code fragment + % + % Arguments: + % - str: code to be displayed + % - inline: - true -> formats inline + % - false -> formats as code block + % - [] -> automatic mode (default): picks one of the above + % - language: which language the code is (enforces a code block) + % + % Output: GFM formatted string + % + % See https://help.github.com/articles/github-flavored-markdown + if ~exist('inline','var') + inline = []; + end + if ~exist('language','var') || isempty(language) + language = ''; + else + inline = false; % highlighting is not supported for inline code + end + if isempty(inline) + inline = isempty(strfind(str, sprintf('\n'))); + end + + if inline + prefix = '`'; + postfix = '`'; + else + prefix = sprintf('\n```%s\n', language); + postfix = sprintf('\n```\n'); + if str(end) == sprintf('\n') + postfix = postfix(2:end); % remove extra endline + end + end + + str = sprintf('%s%s%s', prefix, str, postfix); +end +function str = gfmHeader(str, level) + % Constructs a GFM/Markdown header + if ~exist('level','var') + level = 1; + end + str = sprintf('\n%s %s\n', repmat('#', 1, level), str); +end +function symbols = githubEmoji() + % defines the emojis to signal the test result + symbols = struct('pass', ':white_check_mark:', ... + 'fail', ':heavy_exclamation_mark:', ... + 'skip', ':grey_question:'); +end +% ============================================================================== +function S = splitStatuses(status) + % splits a cell array of statuses into a struct of cell arrays + % of statuses according to their value of "skip", "reliable" and whether + % an error has occured. + % See also: splitUnreliableTests, splitPassFailSkippedTests + S = struct('all', {status}); % beware of cell array assignment to structs! + + [S.reliable, S.unreliable] = splitUnreliableTests(status); + [S.passR, S.failR, S.skipR] = splitPassFailSkippedTests(S.reliable); + [S.passU, S.failU, S.skipU] = splitPassFailSkippedTests(S.unreliable); +end +% ============================================================================== +function [short, long] = describeEnvironment() + % describes the environment in a short and long format + [env, ver] = getEnvironment; + [dummy, VCID] = VersionControlIdentifier(); %#ok + if ~isempty(VCID) + VCID = [' commit ' VCID(1:10)]; + end + OS = OSVersion; + short = sprintf('%s %s (%s)', env, ver, OS, VCID); + long = sprintf('Test results for m2t%s running with %s %s on %s.', ... + VCID, env, ver, OS); +end +% ============================================================================== +function reportUnreliableTests(stream, arg, S) + % report on the unreliable tests + if ~isempty(S.unreliable) && ~strcmpi(arg.length, 'short') + stream.print(gfmHeader('Unreliable tests',2)); + stream.print('These do not cause the build to fail.\n\n'); + displayTestResults(stream, S.unreliable); + end +end +function reportReliableTests(stream, arg, S) + % report on the reliable tests + switch arg.length + case 'long' + tests = S.reliable; + message = ''; + case 'default' + tests = [S.failR; S.skipR]; + message = 'Passing tests are not shown (only failed and skipped tests).\n\n'; + case 'short' + return; % don't show this part + end + + stream.print(gfmHeader('Reliable tests',2)); + stream.print('Only the reliable tests determine the build outcome.\n'); + stream.print(message); + displayTestResults(stream, tests); +end +% ============================================================================== +function displayTestResults(stream, status) + % display a table of specific test outcomes + headers = {'Testcase', 'Name', 'OK', 'Status'}; + data = cell(numel(status), numel(headers)); + symbols = githubEmoji; + for iTest = 1:numel(status) + data(iTest,:) = fillTestResultRow(status{iTest}, symbols); + end + str = gfmTable(data, headers, 'llcl'); + stream.print('%s', str); +end +function row = fillTestResultRow(oneStatus, symbol) + % format the status of a single test for the summary table + testNumber = oneStatus.index; + testSuite = func2str(oneStatus.testsuite); + summary = ''; + if oneStatus.skip + summary = 'SKIPPED'; + passOrFail = symbol.skip; + else + stages = getStagesFromStatus(oneStatus); + for jStage = 1:numel(stages) + thisStage = oneStatus.(stages{jStage}); + if ~thisStage.error + continue; + end + stageName = strrep(stages{jStage},'Stage',''); + switch stageName + case 'plot' + summary = sprintf('%s plot failed', summary); + case 'tikz' + summary = sprintf('%s m2t failed', summary); + case 'hash' + summary = sprintf('new hash %32s != expected (%32s) %s', ... + thisStage.found, thisStage.expected, summary); + otherwise + summary = sprintf('%s %s FAILED', summary, thisStage); + end + end + if isempty(summary) + passOrFail = symbol.pass; + else + passOrFail = symbol.fail; + end + summary = strtrim(summary); + end + row = { gfmCode(sprintf('%s(%d)', testSuite, testNumber)), ... + gfmCode(oneStatus.function), ... + passOrFail, ... + summary}; +end +% ============================================================================== +function displayTestSummary(stream, S) + % display a table of # of failed/passed/skipped tests vs (un)reliable + + % compute number of cases per category + reliableSummary = cellfun(@numel, {S.passR, S.failR, S.skipR}); + unreliableSummary = cellfun(@numel, {S.passU, S.failU, S.skipU}); + + % make summary table + calculate totals + summary = [unreliableSummary numel(S.unreliable); + reliableSummary numel(S.reliable); + reliableSummary+unreliableSummary numel(S.all)]; + + % put results into cell array with proper layout + summary = arrayfun(@(v) sprintf('%d',v), summary, 'UniformOutput', false); + table = repmat({''}, 3, 5); + header = {'','Pass','Fail','Skip','Total'}; + table(:,1) = {'Unreliable','Reliable','Total'}; + table(:,2:end) = summary; + + % print table + [envShort, envDescription] = describeEnvironment(); %#ok + stream.print(gfmHeader('Test summary', 2)); + stream.print('%s\n', envDescription); + stream.print('%s\n', gfmCode(generateCode(S),false,'matlab')); + stream.print(gfmTable(table, header, 'lrrrr')); + + % print overall outcome + symbol = githubEmoji; + nErrors = numel(S.failR); + if nErrors == 0 + stream.print('\nBuild passes. %s\n', symbol.pass); + else + stream.print('\nBuild fails with %d errors. %s\n', nErrors, symbol.fail); + end +end +function code = generateCode(S) + % generates some MATLAB code to easily replicate the results + code = sprintf('%s = %s;\n', ... + 'suite', ['@' func2str(S.all{1}.testsuite)], ... + 'alltests', testNumbers(S.all), ... + 'reliable', testNumbers(S.reliable), ... + 'unreliable', testNumbers(S.unreliable), ... + 'failReliable', testNumbers(S.failR), ... + 'passUnreliable', testNumbers(S.passU), ... + 'skipped', testNumbers([S.skipR; S.skipU])); + % -------------------------------------------------------------------------- + function str = testNumbers(status) + str = intelligentVector( cellfun(@(s) s.index, status) ); + end +end +function str = intelligentVector(numbers) + % Produce a string that is an intelligent vector notation of its arguments + % e.g. when numbers = [ 1 2 3 4 6 7 8 9 ], it should return '[ 1:4 6:9 ]' + % The order in the vector is not retained! + + if isempty(numbers) + str = '[]'; + else + numbers = sort(numbers(:).'); + delta = diff([numbers(1)-1 numbers]); + % place virtual bounds at the first element and beyond the last one + bounds = [1 find(delta~=1) numel(numbers)+1]; + idx = 1:(numel(bounds)-1); % start index of each segment + start = numbers(bounds(idx ) ); + stop = numbers(bounds(idx+1)-1); + parts = arrayfun(@formatRange, start, stop, 'UniformOutput', false); + str = sprintf('[%s]', strtrim(sprintf('%s ', parts{:}))); + end +end +function str = formatRange(start, stop) + % format a range [start:stop] of integers in MATLAB syntax + if start==stop + str = sprintf('%d',start); + else + str = sprintf('%d:%d',start, stop); + end +end +% ============================================================================== diff --git a/Functions/helper_functions_community/mat2tikz/test/output/.gitignore b/Functions/helper_functions_community/mat2tikz/test/output/.gitignore new file mode 100644 index 0000000..7e0545b --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/output/.gitignore @@ -0,0 +1,4 @@ +# This is a directory for testing output. Nothing in here should ever +# be committed, except the .gitignore to enforce all of this. +* +!.gitignore diff --git a/Functions/helper_functions_community/mat2tikz/test/private/OSVersion.m b/Functions/helper_functions_community/mat2tikz/test/private/OSVersion.m new file mode 100644 index 0000000..9efc47a --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/private/OSVersion.m @@ -0,0 +1,36 @@ +function [formatted, OSType, OSVersion] = OSVersion() + % determines the OS type and its (kernel) version number + if ismac + OSType = 'Mac OS'; + [dummy, OSVersion] = system('sw_vers -productVersion'); %#ok + % Output like "10.10.4" for OS X Yosemite + + elseif ispc + OSType = 'Windows'; + [dummy, rawVersion] = system('ver'); %#ok + % Output like "Microsoft Windows [Version 6.3.9600]" for Win8.1 + pattern = '(?<=Version )[0-9.]+'; + OSVersion = regexpi(rawVersion, pattern, 'match', 'once'); + + elseif isunix + [dummy, OSType] = system('uname -s'); %#ok + % This returns the kernal name + % e.g. "Linux" on Linux, "Darwin" on Mac, "SunOS" on Solaris + [dummy, OSVersion] = system('uname -r'); %#ok + % Returns the kernel version. Many Linux distributions + % include an identifier, e.g. "4.0.7-2-ARCH" on Arch Linux + + % TODO: also use `lsb_release` in Linux for distro info + else + warning('OSVersion:UnknownOS', 'Could not recognize OS.'); + OSType = 'Unknown OS'; + OSVersion = ''; + + end + + EOL = sprintf('\n'); + OSType = strrep(OSType, EOL, ''); + OSVersion = strrep(OSVersion, EOL, ''); + + formatted = strtrim([OSType ' ' OSVersion]); +end diff --git a/Functions/helper_functions_community/mat2tikz/test/private/StreamMaker.m b/Functions/helper_functions_community/mat2tikz/test/private/StreamMaker.m new file mode 100644 index 0000000..a5b1b66 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/private/StreamMaker.m @@ -0,0 +1,74 @@ +function SM = StreamMaker() +% StreamMaker (Factory for fie/input/output Streams) +% +% A StreamMaker can make Stream PseudoObjects based on either +% an "fid" or "filename" (and extra arguments for `fopen`). +% The StreamMaker also contains a method `isStream` to validate whether +% the value passed is a valid stream specifier. +% +% Usage +% +% SM = StreamMaker; +% +% Stream = SM.make(fid) +% Stream = SM.make(filename, ...) +% +% This returns a PseudoObject Stream with the following properties: +% - name: (file) name of the stream +% - fid: handle (fid) of the stream +% +% and methods: +% - print: prints to the stream, i.e. fprintf +% - close: closes the stream, i.e. fclose +% +% It may also contain a field to automatically close the Stream when it goes +% out of scope. +% + SM = PseudoObject('StreamMaker', ... + 'isStream', @isStream, ... + 'make', @constructStream); +end + +function PseudoObj = PseudoObject(T, varargin) +% construct a Pseudo-Object with type T (no other fields yet) + PseudoObj = struct('Type', T, varargin{:}); +end + +function bool = isStream(value) + bool = ischar(value) || ismember(value, [1,2,fopen('all')]); + %TODO: allow others kinds of streams + % Stream -> clipboard (write on close) + % Stream -> string variable + % e.g. a quick-and-dirty way would be to write the file to `tempname` + % putting a flag to read that file back upon completion. +end + +function Stream = constructStream(streamSpecifier, varargin) + % this is the actual constructor of a stream + if ~isStream(streamSpecifier) + error('StreamMaker:NotAStream', 'Invalid stream specifier "%s"', ... + streamSpecifier); + end + + Stream = PseudoObject('Stream'); + closeAfterUse = false; + if ischar(streamSpecifier) + Stream.name = streamSpecifier; + Stream.fid = fopen(Stream.name, varargin{:}); + closeAfterUse = true; + elseif isnumeric(streamSpecifier) + Stream.fid = streamSpecifier; + Stream.name = fopen(Stream.fid); + end + + if Stream.fid == -1 + error('Stream:InvalidStream', ... + 'Unable to create stream "%s"!', streamSpecifier); + end + + Stream.print = @(varargin) fprintf(Stream.fid, varargin{:}); + Stream.close = @() fclose(Stream.fid); + if closeAfterUse + Stream.closeAfterUse = onCleanup(Stream.close); + end +end diff --git a/Functions/helper_functions_community/mat2tikz/test/private/VersionControlIdentifier.m b/Functions/helper_functions_community/mat2tikz/test/private/VersionControlIdentifier.m new file mode 100644 index 0000000..b96955e --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/private/VersionControlIdentifier.m @@ -0,0 +1,47 @@ +function [formatted,treeish] = VersionControlIdentifier() +% This function gives the (git) commit ID of matlab2tikz +% +% This assumes the standard directory structure as used by Nico's master branch: +% SOMEPATH/src/matlab2tikz.m with a .git directory in SOMEPATH. +% +% The HEAD of that repository is determined from file system information only +% by following dynamic references (e.g. ref:refs/heds/master) in branch files +% until an absolute commit hash (e.g. 1a3c9d1...) is found. +% NOTE: Packed branch references are NOT supported by this approach + MAXITER = 10; % stop following dynamic references after a while + formatted = ''; + REFPREFIX = 'ref:'; + isReference = @(treeish)(any(strfind(treeish, REFPREFIX))); + treeish = [REFPREFIX 'HEAD']; + try + % get the matlab2tikz directory + privateDir = fileparts(mfilename('fullpath')); + gitDir = fullfile(privateDir,'..','..','.git'); + + nIter = 1; + while isReference(treeish) + refName = treeish(numel(REFPREFIX)+1:end); + branchFile = fullfile(gitDir, refName); + + if exist(branchFile, 'file') && nIter < MAXITER + % The FID is reused in every iteration, so `onCleanup` cannot + % be used to `fclose(fid)`. But since there is very little that + % can go wrong in a single `fscanf`, it's probably best to leave + % this part as it is for the time being. + fid = fopen(branchFile,'r'); + treeish = fscanf(fid,'%s'); + fclose(fid); + nIter = nIter + 1; + else % no branch file or iteration limit reached + treeish = ''; + return; + end + end + catch %#ok + treeish = ''; + end + if ~isempty(treeish) + formatted = [' Commit & ' treeish ' \\\\ \n']; + end + %TODO: do the formatting somewhere else! +end diff --git a/Functions/helper_functions_community/mat2tikz/test/private/calculateMD5Hash.m b/Functions/helper_functions_community/mat2tikz/test/private/calculateMD5Hash.m new file mode 100644 index 0000000..88c6308 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/private/calculateMD5Hash.m @@ -0,0 +1,35 @@ +function hash = calculateMD5Hash(filename) +% CALCULATEMD5HASH calculate a MD5 hash of a file +% +% This functionality is built-in into Octave but uses Java in MATLAB. + + switch getEnvironment + case 'Octave' + hash = md5sum(filename); + + case 'MATLAB' + % There are some MD5 implementations in MATLAB, but those + % tend to be slow and licensing is unclear. + % Rolling our own implementation is unwanted, especially since this + % is a cryptographic hash, even though its security has been + % broken. Instead we make use of the Java libraries. + % Unless the "-nojvm" flag is specified, this should work well. + + MD5 = java.security.MessageDigest.getInstance('MD5'); + + % Open the file + fid = fopen(filename, 'r'); + + % Make sure fid is closed + finally_close = onCleanup(@()fclose(fid)); + + % Faster file digest based on code by Jan Simon as in + % http://www.mathworks.com/matlabcentral/fileexchange/31272-datahash + data = fread(fid, '*uint8'); + MD5.update(data); + + hash = reshape(dec2hex(typecast(MD5.digest(),'uint8')).', 1, 32); + end + + hash = lower(hash); +end diff --git a/Functions/helper_functions_community/mat2tikz/test/private/cleanFiles.m b/Functions/helper_functions_community/mat2tikz/test/private/cleanFiles.m new file mode 100644 index 0000000..6d4fcb8 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/private/cleanFiles.m @@ -0,0 +1,19 @@ +function cleanFiles(cleanBefore) +% clean output files in ./tex using make +%FIXME: this file appears to be unused (but it is useful) +%FIXME: adapt this file to take the output directory into account + if cleanBefore && exist(fullfile('tex','Makefile'),'file') + fprintf(1, 'Cleaning output files...\n'); + cwd = pwd; + try + cd('tex'); + [exitCode, output] = system('make distclean'); + fprintf(1,'%s\n', output); + assert(exitCode==0, 'Exit code 0 means correct execution'); + catch + % This might happen when make is not present + fprintf(2, '\tNot completed succesfully\n\n'); + end + cd(cwd); + end +end diff --git a/Functions/helper_functions_community/mat2tikz/test/private/countNumberOfErrors.m b/Functions/helper_functions_community/mat2tikz/test/private/countNumberOfErrors.m new file mode 100644 index 0000000..8089cf2 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/private/countNumberOfErrors.m @@ -0,0 +1,4 @@ +function nErrors = countNumberOfErrors(status) +% counts the number of errors in a status cell array + nErrors = sum(hasTestFailed(status)); +end diff --git a/Functions/helper_functions_community/mat2tikz/test/private/emptyStage.m b/Functions/helper_functions_community/mat2tikz/test/private/emptyStage.m new file mode 100644 index 0000000..34d892a --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/private/emptyStage.m @@ -0,0 +1,4 @@ +function stage = emptyStage() +% constructs an empty (workflow) stage struct + stage = struct('message', '', 'error' , false); +end diff --git a/Functions/helper_functions_community/mat2tikz/test/private/emptyStatus.m b/Functions/helper_functions_community/mat2tikz/test/private/emptyStatus.m new file mode 100644 index 0000000..0118d4a --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/private/emptyStatus.m @@ -0,0 +1,24 @@ +function defaultStatus = emptyStatus(testsuite, testNumber) +% constructs an empty status struct + defaultStatus = struct(... + 'function', '', ... + 'description', '',... + 'testsuite', testsuite ,... + 'index', testNumber, ... + 'issues', [],... + 'unreliable', false, ... + 'skip', false, ... % skipped this test? + 'closeall', false, ... % call close all after? + 'extraOptions', {cell(0)}, ... + 'extraCleanfigureOptions',{cell(0)}, ... + 'plotStage', emptyStage(), ... + 'saveStage', emptyStage(), ... + 'tikzStage', emptyStage(), ... + 'hashStage', emptyStage() ... + ); + + % for reliable tests explicitly define width and height, see #659 + % TODO: Remove explicitly setting this option. + % After #641 is merged, this might be not needed anyhow. + defaultStatus.extraCleanfigureOptions = {'targetResolution', [1000,500]}; +end diff --git a/Functions/helper_functions_community/mat2tikz/test/private/errorHandler.m b/Functions/helper_functions_community/mat2tikz/test/private/errorHandler.m new file mode 100644 index 0000000..ab7a32b --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/private/errorHandler.m @@ -0,0 +1,41 @@ +function [stage, errorHasOccurred] = errorHandler(e) +% common error handler code: save and print to console + errorHasOccurred = true; + stage = emptyStage(); + stage.message = format_error_message(e); + stage.error = errorHasOccurred; + + disp_error_message(stage.message); +end +% ============================================================================== +function msg = format_error_message(e) + msg = ''; + if ~isempty(e.message) + msg = sprintf('%serror: %s\n', msg, e.message); + end + if ~isempty(e.identifier) + if strfind(lower(e.identifier),'testmatlab2tikz:') + % When "errors" occur in the test framework, i.e. a hash mismatch + % or no hash provided, there is no need to be very verbose. + % So we don't return the msgid and the stack trace in those cases! + return % only return the message + end + msg = sprintf('%serror: %s\n', msg, e.identifier); + end + if ~isempty(e.stack) + msg = sprintf('%serror: called from:\n', msg); + for ee = e.stack(:)' + msg = sprintf('%serror: %s at line %d, in function %s\n', ... + msg, ee.file, ee.line, ee.name); + end + end +end +% ============================================================================== +function disp_error_message(msg) + stderr = 2; + % The error message should not contain any more escape sequences and + % hence can be output literally to stderr. + + fprintf(stderr, '%s', msg); +end +% ============================================================================== diff --git a/Functions/helper_functions_community/mat2tikz/test/private/errorHasOccurred.m b/Functions/helper_functions_community/mat2tikz/test/private/errorHasOccurred.m new file mode 100644 index 0000000..e73c0f1 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/private/errorHasOccurred.m @@ -0,0 +1,16 @@ +function errorOccurred = errorHasOccurred(status) +% determines whether an error has occurred from a status struct OR cell array +% of status structs + errorOccurred = false; + if iscell(status) + for iStatus = 1:numel(status) + errorOccurred = errorOccurred || errorHasOccurred(status{iStatus}); + end + else + stages = getStagesFromStatus(status); + for iStage = 1:numel(stages) + thisStage = status.(stages{iStage}); + errorOccurred = errorOccurred || thisStage.error; + end + end +end diff --git a/Functions/helper_functions_community/mat2tikz/test/private/execute_hash_stage.m b/Functions/helper_functions_community/mat2tikz/test/private/execute_hash_stage.m new file mode 100644 index 0000000..1e268f5 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/private/execute_hash_stage.m @@ -0,0 +1,34 @@ +function [status] = execute_hash_stage(status, ipp) + % test stage: check recorded hash checksum + calculated = ''; + expected = ''; + try + expected = getReferenceHash(status, ipp); + calculated = calculateMD5Hash(status.tikzStage.texFile); + + % do the actual check + if ~strcmpi(expected, calculated) + % throw an error to signal the testing framework + error('testMatlab2tikz:HashMismatch', ... + 'The hash "%s" does not match the reference hash "%s"', ... + calculated, expected); + end + catch %#ok + e = lasterror('reset'); %#ok + [status.hashStage] = errorHandler(e); + end + status.hashStage.expected = expected; + status.hashStage.found = calculated; +end +% ============================================================================== +function hash = getReferenceHash(status, ipp) + % retrieves a reference hash from a hash table + % WARNING: do not make `hashTable` persistent, since this is slower + + hashTable = loadHashTable(ipp.Results.testsuite); + if isfield(hashTable.contents, status.function) + hash = hashTable.contents.(status.function); + else + hash = ''; + end +end diff --git a/Functions/helper_functions_community/mat2tikz/test/private/execute_plot_stage.m b/Functions/helper_functions_community/mat2tikz/test/private/execute_plot_stage.m new file mode 100644 index 0000000..ff1519e --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/private/execute_plot_stage.m @@ -0,0 +1,45 @@ +function [status] = execute_plot_stage(defaultStatus, ipp) +% plot a test figure + testsuite = ipp.Results.testsuite; + testNumber = defaultStatus.index; + + % open a window + fig_handle = figure('visible',ipp.Results.figureVisible); + errorHasOccurred = false; + + % plot the figure + try + status = testsuite(testNumber); + + catch %#ok + e = lasterror('reset'); %#ok + + status.description = '\textcolor{red}{Error during plot generation.}'; + [status.plotStage, errorHasOccurred] = errorHandler(e); + + % Automaticall mark the test as unreliable + % + % Since metadata is not set in this case, also stat.unreliable is + % not returned. So ideally, we should + % FIXME: implement #484 to get access to the meta data + % but we can work around this issue by forcefully setting that value. + % The rationale for setting this to true: + % - the plot part is not the main task of M2T + % (so breaking a single test is less severe in this case), + % - if the plotting fails, the test is not really reliable anyway, + % - this allows to get full green on Travis. + status.unreliable = true; + + end + + status = fillStruct(status, defaultStatus); + if isempty(status.function) + allFuncs = testsuite(0); + status.function = func2str(allFuncs{testNumber}); + end + status.plotStage.fig_handle = fig_handle; + + if status.skip || errorHasOccurred + close(fig_handle); + end +end diff --git a/Functions/helper_functions_community/mat2tikz/test/private/execute_save_stage.m b/Functions/helper_functions_community/mat2tikz/test/private/execute_save_stage.m new file mode 100644 index 0000000..0835ac4 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/private/execute_save_stage.m @@ -0,0 +1,63 @@ +function [status] = execute_save_stage(status, ipp) +% save stage: saves the figure to EPS/PDF depending on env + testNumber = status.index; + + basepath = fullfile(ipp.Results.output,'data','reference'); + reference_eps = fullfile(basepath, sprintf('test%d-reference.eps', testNumber)); + reference_pdf = fullfile(basepath, sprintf('test%d-reference.pdf', testNumber)); + % the reference below is for inclusion in LaTeX! Use UNIX conventions! + reference_fig = sprintf('data/reference/test%d-reference', testNumber); + + % Save reference output as PDF + try + switch getEnvironment + case 'MATLAB' + % MATLAB does not generate properly cropped PDF files. + % So, we generate EPS files that are converted later on. + print(gcf, '-depsc2', reference_eps); + + fixLineEndingsInWindows(reference_eps); + + case 'Octave' + % In Octave, figures are properly cropped when using print(). + print(reference_pdf, '-dpdf', '-S415,311', '-r150'); + pause(1.0) + otherwise + error('matlab2tikz:UnknownEnvironment', ... + 'Unknown environment. Need MATLAB(R) or GNU Octave.') + end + catch %#ok + e = lasterror('reset'); %#ok + [status.saveStage] = errorHandler(e); + end + status.saveStage.epsFile = reference_eps; + status.saveStage.pdfFile = reference_pdf; + status.saveStage.texReference = reference_fig; +end +% ============================================================================== +function fixLineEndingsInWindows(filename) +% On R2014b Win, line endings in .eps are Unix style (LF) instead of Windows +% style (CR+LF). This causes problems in the MikTeX `epstopdf` for some files +% as dicussed in: +% * https://github.com/matlab2tikz/matlab2tikz/issues/370 +% * http://tex.stackexchange.com/questions/208179 + if ispc + fid = fopen(filename,'r+'); + finally_fclose_fid = onCleanup(@() fclose(fid)); + testline = fgets(fid); + CRLF = sprintf('\r\n'); + endOfLine = testline(end-1:end); + if ~strcmpi(endOfLine, CRLF) + endOfLine = testline(end); % probably an LF + + % Rewind, read the whole + fseek(fid,0,'bof'); + str = fread(fid,'*char')'; + + % Replace, overwrite and close + str = strrep(str, endOfLine, CRLF); + fseek(fid,0,'bof'); + fprintf(fid,'%s',str); + end + end +end diff --git a/Functions/helper_functions_community/mat2tikz/test/private/execute_tikz_stage.m b/Functions/helper_functions_community/mat2tikz/test/private/execute_tikz_stage.m new file mode 100644 index 0000000..fa82463 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/private/execute_tikz_stage.m @@ -0,0 +1,43 @@ +function [status] = execute_tikz_stage(status, ipp) +% test stage: TikZ file generation + testNumber = status.index; + datapath = fullfile(ipp.Results.output,'data','converted'); + gen_tex = fullfile(datapath, sprintf('test%d-converted.tex', testNumber)); + % the value below is for inclusion into LaTeX report! Use UNIX convention. + gen_pdf = sprintf('data/converted/test%d-converted.pdf', testNumber); + cleanfigure_time = NaN; + m2t_time = NaN; + + % now, test matlab2tikz + try + %TODO: remove this once text removal has been removed + oldWarn = warning('off','cleanfigure:textRemoval'); + + cleanfigure_time = tic; + cleanfigure(status.extraCleanfigureOptions{:}); + cleanfigure_time = toc(cleanfigure_time); + + warning(oldWarn); + + m2t_time = tic; + matlab2tikz('filename', gen_tex, ... + 'showInfo', false, ... + 'checkForUpdates', false, ... + 'dataPath', datapath, ... + 'standalone', true, ... + ipp.Results.extraOptions{:}, ... + status.extraOptions{:} ... + ); + m2t_time = toc(m2t_time); + catch %#ok + e = lasterror('reset'); %#ok + % Remove (corrupted) output file. This is necessary to avoid that the + % Makefile tries to compile it and fails. + delete(gen_tex) + [status.tikzStage] = errorHandler(e); + end + status.tikzStage.texFile = gen_tex; + status.tikzStage.pdfFile = gen_pdf; + status.tikzStage.m2t_time = m2t_time; + status.tikzStage.cleanfigure_time = cleanfigure_time; +end diff --git a/Functions/helper_functions_community/mat2tikz/test/private/execute_type_stage.m b/Functions/helper_functions_community/mat2tikz/test/private/execute_type_stage.m new file mode 100644 index 0000000..a9d4324 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/private/execute_type_stage.m @@ -0,0 +1,15 @@ +function [status] = execute_type_stage(status, ipp) + try + filename = status.tikzStage.texFile; + stream = 1; % stdout + if errorHasOccurred(status) && exist(filename, 'file') + shortname = strrep(filename, m2troot, '$(M2TROOT)'); + fprintf(stream, '\n%%%%%%%% BEGIN FILE "%s" %%%%%%%%\n', shortname); + type(filename); + fprintf(stream, '\n%%%%%%%% END FILE "%s" %%%%%%%%\n', shortname); + end + catch + e = lasterror('reset'); + [status.typeStage] = errorHandler(e); + end +end diff --git a/Functions/helper_functions_community/mat2tikz/test/private/fillStruct.m b/Functions/helper_functions_community/mat2tikz/test/private/fillStruct.m new file mode 100644 index 0000000..1ddc6d8 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/private/fillStruct.m @@ -0,0 +1,10 @@ +function [status] = fillStruct(status, defaultStatus) +% fills non-existant fields of |data| with those of |defaultData| + fields = fieldnames(defaultStatus); + for iField = 1:numel(fields) + field = fields{iField}; + if ~isfield(status,field) + status.(field) = defaultStatus.(field); + end + end +end diff --git a/Functions/helper_functions_community/mat2tikz/test/private/getEnvironment.m b/Functions/helper_functions_community/mat2tikz/test/private/getEnvironment.m new file mode 100644 index 0000000..4717cb5 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/private/getEnvironment.m @@ -0,0 +1,25 @@ +function [env, versionString] = getEnvironment() +% Determine environment (Octave, MATLAB) and version string +% TODO: Unify private `getEnvironment` functions + persistent cache + + if isempty(cache) + isOctave = exist('OCTAVE_VERSION', 'builtin') ~= 0; + if isOctave + env = 'Octave'; + versionString = OCTAVE_VERSION; + else + env = 'MATLAB'; + vData = ver(env); + versionString = vData.Version; + end + + % store in cache + cache.env = env; + cache.versionString = versionString; + + else + env = cache.env; + versionString = cache.versionString; + end +end diff --git a/Functions/helper_functions_community/mat2tikz/test/private/getStagesFromStatus.m b/Functions/helper_functions_community/mat2tikz/test/private/getStagesFromStatus.m new file mode 100644 index 0000000..7ca6669 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/private/getStagesFromStatus.m @@ -0,0 +1,5 @@ +function stages = getStagesFromStatus(status) +% retrieves the different (names of) stages of a status struct + fields = fieldnames(status); + stages = fields(cellfun(@(f) ~isempty(strfind(f,'Stage')), fields)); +end diff --git a/Functions/helper_functions_community/mat2tikz/test/private/hasTestFailed.m b/Functions/helper_functions_community/mat2tikz/test/private/hasTestFailed.m new file mode 100644 index 0000000..3981dfd --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/private/hasTestFailed.m @@ -0,0 +1,13 @@ +function bool = hasTestFailed(status) + % returns true when the test has failed + + if iscell(status) % allow for vectorization of the call + bool = cellfun(@hasTestFailed, status, 'UniformOutput', true); + else + stages = getStagesFromStatus(status); + bool = false; + for jStage = 1:numel(stages) + bool = bool || status.(stages{jStage}).error; + end + end +end diff --git a/Functions/helper_functions_community/mat2tikz/test/private/hashTableName.m b/Functions/helper_functions_community/mat2tikz/test/private/hashTableName.m new file mode 100644 index 0000000..faa1c6d --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/private/hashTableName.m @@ -0,0 +1,55 @@ +function filename = hashTableName(suite) + % determines the file name of a hash table + % + % The MD5 file is assumed to be in the same directory as the test suite. + % It has a file name "$SUITE.$ENV.$VER.md5" + % where the following fields are filled: + % $ENV: the environment (either "MATLAB" or "Octave") + % $VER: the version (e.g. "3.8.0" for Octave, "8.3" for MATLAB 2014a) + % $SUITE: the name (and path) of the test suite + % + % For the $VER-part, a fall-back mechanism is present that prefers the exact + % version but will use the closest available file if such file does not + % exist. + [pathstr,name, ext] = fileparts(which(func2str(suite))); + [env, version] = getEnvironment(); + ext = sprintf('.%s.%s.md5', env, version); + relFilename = [name ext]; + filename = fullfile(pathstr, relFilename); + + if ~exist(filename,'file') + % To avoid having to create a file for each release of the environment, + % also other versions are tried. The file for different releases are checked + % in the following order: + % 1. the currently running version (handled above!) + % 2. the newest older version (e.g. use R2014b's file in R2015a) + % 3. the oldest newer version (e.g. use R2014a's file in R2013a) + pattern = sprintf('%s.%s.*.md5', name, env); + candidates = dir(fullfile(pathstr, pattern)); + + % We just need the file names. + filenames = arrayfun(@(c)c.name, candidates, 'UniformOutput', false); + + % Add the expected version to the results, and sort the names by + % version (this is the same as alphabetically). + filenames = sort([filenames; {relFilename}]); + nFiles = numel(filenames); + iCurrent = find(ismember(filenames, relFilename)); + % determine the fall-back candidates: + iNewestOlder = iCurrent - 1; + iOldestNewer = iCurrent + 1; + + inRange = @(idx)(idx <= nFiles && idx >= 1); + if inRange(iNewestOlder) + % use the newest older version + relFilename = filenames{iNewestOlder}; + elseif inRange(iOldestNewer) + % use the oldest newer version + relFilename = filenames{iOldestNewer}; + else + % use the exact version anyhow + end + + filename = fullfile(pathstr, relFilename); + end +end diff --git a/Functions/helper_functions_community/mat2tikz/test/private/initializeGlobalState.m b/Functions/helper_functions_community/mat2tikz/test/private/initializeGlobalState.m new file mode 100644 index 0000000..0ecac82 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/private/initializeGlobalState.m @@ -0,0 +1,99 @@ +function [orig] = initializeGlobalState() +% Initialize global state. Set working directory and various properties of +% the graphical root to ensure reliable output of the ACID testsuite. +% See #542 and #552 +% +% 1. Working directory +% 2. Bring get(0,'Default') in line with get(0,'Factory') +% 3. Set specific properties, required by matlab2tikz + fprintf('Initialize global state...\n'); + orig = struct(); + + %--- Extract user defined default properties and set factory state + default = get(0,'Default'); + factory = get(0,'Factory'); + + f = fieldnames(default); % fields of user's default state + for i = 1:length(f) + factory_property_name = strrep(f{i},'default','factory'); + factory_property_value = factory.(factory_property_name); + orig.(f{i}).val = ... + swapPropertyState(0, f{i}, factory_property_value); + end + + %--- Define desired global state properties + % defaultAxesColorOrder: on HG1 'default' and 'factory' differ and + % HG1 differs from HG2. Consequently use HG2 colors (the new standard). + new.defaultAxesColorOrder.val = [0.000 0.447 0.741; ... + 0.850 0.325 0.098; ... + 0.929 0.694 0.125; ... + 0.494 0.184 0.556; ... + 0.466 0.674 0.188; ... + 0.301 0.745 0.933; ... + 0.635 0.0780 0.184]; + new.defaultAxesColorOrder.ignore= false; + + % defaultFigurePosition: width and height influence cleanfigure() and + % the number/location of axis ticks + new.defaultFigurePosition.val = [300,200,560,420]; + new.defaultFigurePosition.ignore= false; + + % ScreenPixelsPerInch: TODO: determine, if necessary + % (probably needed for new line simplification algorithm) + % not possible in octave + new.ScreenPixelsPerInch.val = 96; + new.ScreenPixelsPerInch.ignore = strcmpi(getEnvironment,'octave'); + + % MATLAB's factory values differ from their default values of a clean + % MATLAB installation (observed on R2014a, Linux) + new.defaultAxesColor.val = [1 1 1]; + new.defaultAxesColor.ignore = false; + new.defaultLineColor.val = [0 0 0]; + new.defaultLineColor.ignore = false; + new.defaultTextColor.val = [0 0 0]; + new.defaultTextColor.ignore = false; + new.defaultAxesXColor.val = [0 0 0]; + new.defaultAxesXColor.ignore = false; + new.defaultAxesYColor.val = [0 0 0]; + new.defaultAxesYColor.ignore = false; + new.defaultAxesZColor.val = [0 0 0]; + new.defaultAxesZColor.ignore = false; + new.defaultFigureColor.val = [0.8 0.8 0.8]; + new.defaultFigureColor.ignore = false; + new.defaultPatchEdgeColor.val = [0 0 0]; + new.defaultPatchEdgeColor.ignore = false; + new.defaultPatchFaceColor.val = [0 0 0]; + new.defaultPatchFaceColor.ignore = false; + new.defaultFigurePaperType.val = 'A4'; + new.defaultFigurePaperType.ignore = false; + new.defaultFigurePaperSize.val = [20.9840 29.6774]; + new.defaultFigurePaperSize.ignore = false; + new.defaultFigurePaperUnits.val = 'centimeters'; + new.defaultFigurePaperUnits.ignore = false; + + %--- Extract relevant properties and set desired state + f = fieldnames(new); % fields of new state + for i = 1:length(f) + % ignore property on specified environments + if ~new.(f{i}).ignore + val = swapPropertyState(0, f{i}, new.(f{i}).val); + + % store original value only, if not set by user's defaults + if ~isfield(orig,f{i}) + orig.(f{i}).val = val; + end + end + end +end +% ========================================================================= +function old = swapPropertyState(h, property, new) + % read current property of graphical object + % set new value, if not empty + if nargin < 3, new = []; end + + old = get(h, property); + + if ~isempty(new) + set(h, property, new); + end +end diff --git a/Functions/helper_functions_community/mat2tikz/test/private/loadHashTable.m b/Functions/helper_functions_community/mat2tikz/test/private/loadHashTable.m new file mode 100644 index 0000000..3d88017 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/private/loadHashTable.m @@ -0,0 +1,19 @@ +function hashTable = loadHashTable(suite) + % loads a reference hash table from disk + hashTable.suite = suite; + hashTable.contents = struct(); + filename = hashTableName(suite); + if exist(filename, 'file') + fid = fopen(filename, 'r'); + finally_fclose_fid = onCleanup(@() fclose(fid)); + + data = textscan(fid, '%s : %s'); + if ~isempty(data) && ~all(cellfun(@isempty, data)) + functions = cellfun(@strtrim, data{1},'UniformOutput', false); + hashes = cellfun(@strtrim, data{2},'UniformOutput', false); + for iFunc = 1:numel(functions) + hashTable.contents.(functions{iFunc}) = hashes{iFunc}; + end + end + end +end diff --git a/Functions/helper_functions_community/mat2tikz/test/private/m2troot.m b/Functions/helper_functions_community/mat2tikz/test/private/m2troot.m new file mode 100644 index 0000000..475789b --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/private/m2troot.m @@ -0,0 +1,30 @@ +function rootpath = m2troot(varargin) + % M2TROOT produces paths inside the matlab2tikz repository + % + % Usage: + % There are two ways to call this function, the base syntax is: + % + % * rootpath = m2troot() + % + % where |rootpath| points towards the root of the repository. + % + % The other syntax: + % + % * path = m2troot(...) + % + % is equivalent to |fullfile(m2troot, ...)| and as such allows to + % easily produce a path to any file within the repository. + + m2t = which('matlab2tikz'); + if isempty(m2t) + error('M2TRoot:NotFound', 'Matlab2tikz was not found on the PATH!') + end + + [srcpath] = fileparts(m2t); % this should be $(m2troot)/src + [rootpath, srcdir] = fileparts(srcpath); % this should be $(m2troot) + assert(strcmpi(srcdir,'src')); + + if nargin >= 1 + rootpath = fullfile(rootpath, varargin{:}); + end +end diff --git a/Functions/helper_functions_community/mat2tikz/test/private/m2tstrjoin.m b/Functions/helper_functions_community/mat2tikz/test/private/m2tstrjoin.m new file mode 100644 index 0000000..70c7267 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/private/m2tstrjoin.m @@ -0,0 +1,24 @@ +function [ newstr ] = m2tstrjoin( cellstr, delimiter ) +%M2TSTRJOIN This function joins a cellstr with a separator +% +% This is an alternative implementation for MATLAB's `strjoin`, since that +% one is not available before R2013a. +% +% See also: strjoin + + %TODO: Unify the private `m2tstrjoin` functions + %FIXME: differs from src/private/m2tstrjoin in functionality !!! + + nElem = numel(cellstr); + if nElem == 0 + newstr = ''; + return % m2tstrjoin({}, ...) -> '' + end + + newstr = cell(2,nElem); + newstr(1,:) = reshape(cellstr, 1, nElem); + newstr(2,1:nElem-1) = {delimiter}; % put delimiters in-between the elements + newstr(2, end) = {''}; % for Octave 4 compatibility + newstr = [newstr{:}]; + +end diff --git a/Functions/helper_functions_community/mat2tikz/test/private/restoreGlobalState.m b/Functions/helper_functions_community/mat2tikz/test/private/restoreGlobalState.m new file mode 100644 index 0000000..404b9c4 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/private/restoreGlobalState.m @@ -0,0 +1,11 @@ +function restoreGlobalState(orig) +% Restore original properties of global state. +% See #542 and #552 + fprintf('Restore global state...\n'); + + % Restore relevant properties + state_fields = fieldnames(orig); + for i = 1:length(state_fields) + set(0, state_fields{i}, orig.(state_fields{i}).val); + end +end diff --git a/Functions/helper_functions_community/mat2tikz/test/private/splitPassFailSkippedTests.m b/Functions/helper_functions_community/mat2tikz/test/private/splitPassFailSkippedTests.m new file mode 100644 index 0000000..92aed64 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/private/splitPassFailSkippedTests.m @@ -0,0 +1,10 @@ +function [passedTests, failedTests, skippedTests] = splitPassFailSkippedTests(status) + % splits tests between passed, failed and skippedtests + skipped = cellfun(@(s) s.skip, status); + status_notSkipped = status(~skipped); + failed = hasTestFailed(status_notSkipped); + + passedTests = status_notSkipped(~failed); + failedTests = status_notSkipped(failed); + skippedTests = status(skipped); +end diff --git a/Functions/helper_functions_community/mat2tikz/test/private/splitUnreliableTests.m b/Functions/helper_functions_community/mat2tikz/test/private/splitUnreliableTests.m new file mode 100644 index 0000000..950b553 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/private/splitUnreliableTests.m @@ -0,0 +1,7 @@ +function [reliableTests, unreliableTests] = splitUnreliableTests(status) + % splits tests between reliable and unreliable tests + knownToFail = cellfun(@(s)s.unreliable, status); + + unreliableTests = status( knownToFail); + reliableTests = status(~knownToFail); +end diff --git a/Functions/helper_functions_community/mat2tikz/test/private/testMatlab2tikz.m b/Functions/helper_functions_community/mat2tikz/test/private/testMatlab2tikz.m new file mode 100644 index 0000000..f1612ac --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/private/testMatlab2tikz.m @@ -0,0 +1,146 @@ +function [status, parameters] = testMatlab2tikz(varargin) +%TESTMATLAB2TIKZ unit test driver for matlab2tikz +% +% This function should NOT be called directly by the user (or even developer). +% If you are a developer, please use some of the following functions instead: +% * `testHeadless` +% * `testGraphical` +% +% The following arguments are supported, also for the functions above. +% +% TESTMATLAB2TIKZ('testFunctionIndices', INDICES, ...) or +% TESTMATLAB2TIKZ(INDICES, ...) runs the test only for the specified +% indices. When empty, all tests are run. (Default: []). +% +% TESTMATLAB2TIKZ('extraOptions', {'name',value, ...}, ...) +% passes the cell array of options to MATLAB2TIKZ. Default: {} +% +% TESTMATLAB2TIKZ('figureVisible', LOGICAL, ...) +% plots the figure visibly during the test process. Default: false +% +% TESTMATLAB2TIKZ('testsuite', FUNCTION_HANDLE, ...) +% Determines which test suite is to be run. Default: @ACID +% A test suite is a function that takes a single integer argument, which: +% when 0: returns a cell array containing the N function handles to the tests +% when >=1 and <=N: runs the appropriate test function +% when >N: throws an error +% +% TESTMATLAB2TIKZ('output', DIRECTORY, ...) +% Sets the output directory where the output files are places. +% The default directory is $M2TROOT/test/output/current +% +% See also matlab2tikz, ACID + + % In which environment are we? + env = getEnvironment(); + + % ----------------------------------------------------------------------- + ipp = m2tInputParser; + + ipp = ipp.addOptional(ipp, 'testFunctionIndices', [], @isfloat); + ipp = ipp.addParamValue(ipp, 'extraOptions', {}, @iscell); + ipp = ipp.addParamValue(ipp, 'figureVisible', false, @islogical); + ipp = ipp.addParamValue(ipp, 'actionsToExecute', @(varargin) varargin{1}, @isFunction); + ipp = ipp.addParamValue(ipp, 'testsuite', @ACID, @isFunction ); + ipp = ipp.addParamValue(ipp, 'output', m2troot('test','output','current'), @ischar); + + ipp = ipp.parse(ipp, varargin{:}); + + ipp = sanitizeInputs(ipp); + parameters = ipp.Results; + + % ----------------------------------------------------------------------- + if strcmpi(env, 'Octave') + if ~ipp.Results.figureVisible + % Use the gnuplot backend to work around an fltk bug, see + % . + graphics_toolkit gnuplot + end + + if ispc + % Prevent three digit exponent on Windows Octave + % See https://github.com/matlab2tikz/matlab2tikz/pull/602 + setenv ('PRINTF_EXPONENT_DIGITS', '2') + end + end + + % copy output template into output directory + if ~exist(ipp.Results.output,'dir') + mkdir(ipp.Results.output); + end + template = m2troot('test','template'); + copyfile(fullfile(template,'*'), ipp.Results.output); + + % start overall timing + elapsedTimeOverall = tic; + status = runIndicatedTests(ipp); + + % print out overall timing + elapsedTimeOverall = toc(elapsedTimeOverall); + stdout = 1; + fprintf(stdout, 'overall time: %4.2fs\n\n', elapsedTimeOverall); +end +% INPUT VALIDATION ============================================================= +function bool = isFunction(f) + bool = isa(f,'function_handle'); +end +function ipp = sanitizeInputs(ipp) + % sanitize all input arguments + ipp = sanitizeFunctionIndices(ipp); + ipp = sanitizeFigureVisible(ipp); +end +function ipp = sanitizeFunctionIndices(ipp) +% sanitize the passed function indices to the range of the test suite + % query the number of test functions + testsuite = ipp.Results.testsuite; + n = length(testsuite(0)); + + if ~isempty(ipp.Results.testFunctionIndices) + indices = ipp.Results.testFunctionIndices; + % kick out the illegal stuff + I = find(indices>=1 & indices<=n); + indices = indices(I); %#ok + else + indices = 1:n; + end + ipp.Results.testFunctionIndices = indices; +end +function ipp = sanitizeFigureVisible(ipp) + % sanitizes the figure visible option from boolean to ON/OFF + if ipp.Results.figureVisible + ipp.Results.figureVisible = 'on'; + else + ipp.Results.figureVisible = 'off'; + end +end +% TEST RUNNER ================================================================== +function status = runIndicatedTests(ipp) +% run all indicated tests in the test suite + % cell array to accomodate different structure + indices = ipp.Results.testFunctionIndices; + testsuite = ipp.Results.testsuite; + testsuiteName = func2str(testsuite); + stdout = 1; + status = cell(length(indices), 1); + + for k = 1:length(indices) + testNumber = indices(k); + + fprintf(stdout, 'Executing %s test no. %d...\n', testsuiteName, indices(k)); + + status{k} = emptyStatus(testsuite, testNumber); + + elapsedTime = tic; + + status{k} = feval(ipp.Results.actionsToExecute, status{k}, ipp); + + elapsedTime = toc(elapsedTime); + status{k}.elapsedTime = elapsedTime; + fprintf(stdout, '%s ', status{k}.function); + if status{k}.skip + fprintf(stdout, 'skipped (%4.2fs).\n\n', elapsedTime); + else + fprintf(stdout, 'done (%4.2fs).\n\n', elapsedTime); + end + end +end diff --git a/Functions/helper_functions_community/mat2tikz/test/runMatlab2TikzTests.m b/Functions/helper_functions_community/mat2tikz/test/runMatlab2TikzTests.m new file mode 100644 index 0000000..9c5c47b --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/runMatlab2TikzTests.m @@ -0,0 +1,38 @@ +function statusAll = runMatlab2TikzTests(varargin) +%% This file runs the complete MATLAB2TIKZ test suite. +% It is mainly used for testing on a continuous integration server, but it can +% also be used on a development machine. + +CI_MODE = strcmpi(getenv('CONTINUOUS_INTEGRATION'),'true') || strcmp(getenv('CI'),'true'); +isJenkins = ~isempty(getenv('JENKINS_URL')); + +%% Set path +addpath(fullfile(pwd,'..','src')); +addpath(fullfile(pwd,'suites')); + +%% Select functions to run +suite = @ACID; +allTests = 1:numel(suite(0)); + +%% Prepare environment +if strcmpi(getEnvironment(), 'Octave') + % Ensure that paging is disabled + % https://www.gnu.org/software/octave/doc/interpreter/Paging-Screen-Output.html + more off +end + +%% Run tests +status = testHeadless('testFunctionIndices', allTests,... + 'testsuite', suite, varargin{:}); + +if isJenkins + makeTapReport(status, 'stream', 'results.test.tap'); + makeTravisReport(status, 'stream', 'results.test.md'); +end + +nErrors = makeTravisReport(status); + +%% Calculate exit code +if CI_MODE + exit(nErrors); +end diff --git a/Functions/helper_functions_community/mat2tikz/test/saveHashTable.m b/Functions/helper_functions_community/mat2tikz/test/saveHashTable.m new file mode 100644 index 0000000..2d56ef1 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/saveHashTable.m @@ -0,0 +1,164 @@ +function saveHashTable(status, varargin) +% SAVEHASHTABLE saves the references hashes for the Matlab2Tikz tests +% +% Usage: +% SAVEHASHTABLE(status) +% +% SAVEHASHTABLE(status, 'dryrun', BOOL, ...) determines whether or not to +% write the constructed hash table to file (false) or to stdout (true). +% Default: false +% +% SAVEHASHTABLE(status, 'removedTests', CHAR, ...) specifies which action to +% execute on "removed tests" (i.e. test that have a hash recorded in the file, +% but which are not present in `status`). Three values are possible: +% - 'ask' (default): Ask what to do for each such test. +% - 'remove': Remove the test from the file. +% This is appropriate if the test has been removed from the suite. +% - 'keep': Keep the test hash in the file. +% This is appropriate when the test has not executed all tests. +% +% Inputs: +% - status: output cell array of the testing functions +% +% See also: runMatlab2TikzTests, testMatlab2tikz + ipp = m2tInputParser(); + ipp = ipp.addRequired(ipp, 'status', @iscell); + ipp = ipp.addParamValue(ipp, 'dryrun', false, @islogical); + ipp = ipp.addParamValue(ipp, 'removedTests', 'ask', @isValidAction); + ipp = ipp.parse(ipp, status, varargin{:}); + + %% settings + suite = status{1}.testsuite; %TODO: handle multiple test suites in a single array + filename = hashTableName(suite); + READFORMAT = '%s : %s'; + WRITEFORMAT = [READFORMAT '\n']; + + %% process the hash table + oldHashes = readHashesFromFile(filename); + newHashes = updateHashesFromStatus(oldHashes, status); + writeHashesToFile(filename, newHashes); + + % -------------------------------------------------------------------------- + function hashes = updateHashesFromStatus(hashes, status) + % update hashes from the test results in status + oldFunctions = fieldnames(hashes); + newFunctions = cellfun(@(s) s.function, status, 'UniformOutput', false); + + % add hashes from all executed tests + for iFunc = 1:numel(status) + S = status{iFunc}; + thisFunc = S.function; + thisHash = ''; + if isfield(S.hashStage,'found') + thisHash = S.hashStage.found; + elseif S.skip + if isfield(hashes, thisFunc) + % Test skipped, but reference hash present in file + % Probably this means that the developer doesn't have access + % to a certain toolbox. + warning('SaveHashTable:CannotUpdateSkippedTest', ... + 'Test "%s" was skipped. Cannot update hash!',... + thisFunc); + else + % Test skipped and reference hash absent. + % Probably the test is skipped because something is tested + % that relies on HG1/HG2/Octace-specific features and we are + % in the wrong environment for the test. + end + else + warning('SaveHashTable:NoHashFound',... + 'No hash found for "%s"!', thisFunc); + end + if ~isempty(thisHash) + hashes.(thisFunc) = thisHash; + end + end + + % ask what to do with tests for which we have a hash, but no test results + removedTests = setdiff(oldFunctions, newFunctions); + if ~isempty(removedTests) + fprintf(1, 'Some tests in the file were not in the build status.\n'); + end + for iTest = 1:numel(removedTests) + thisTest = removedTests{iTest}; + + action = askActionToPerformOnRemovedTest(thisTest); + switch action + case 'remove' + % useful for test that no longer exist + fprintf(1, 'Removed hash for "%s"\n', thisTest); + hashes = rmfield(hashes, thisTest); + + case 'keep' + % useful when not all tests were executed by the tester + fprintf(1, 'Kept hash for "%s"\n', thisTest); + + end + end + end + function action = askActionToPerformOnRemovedTest(testName) + % ask which action to carry out on a removed test + action = lower(ipp.Results.removedTests); + while ~isActualAction(action) + query = sprintf('Keep or remove "%s"? [Kr]:', testName); + answer = strtrim(input(query,'s')); + + if isempty(answer) || strcmpi(answer(1), 'K') + action = 'keep'; + elseif strcmpi(answer(1), 'R') + action = 'remove'; + else + action = 'ask again'; + % just keep asking until we get a reasonable answer + end + end + end + function writeHashesToFile(filename, hashes) + % write hashes to a file (or stdout when dry-running) + if ~ipp.Results.dryrun + fid = fopen(filename, 'w+'); + finally_fclose_fid = onCleanup(@() fclose(fid)); + else + fid = 1; % Use stdout to print everything + fprintf(fid, '\n\n Output: \n\n'); + end + + funcNames = sort(fieldnames(hashes)); + for iFunc = 1:numel(funcNames) + func = funcNames{iFunc}; + fprintf(fid, WRITEFORMAT, func, hashes.(func)); + end + end + function hashes = readHashesFromFile(filename) + % read hashes from a file + if exist(filename,'file') + fid = fopen(filename, 'r'); + finally_fclose_fid = onCleanup(@() fclose(fid)); + + data = textscan(fid, READFORMAT); + % data is now a cell array with 2 elements, each a (row) cell array + % - the first is all the function names + % - the second is all the hashes + + % Transform `data` into {function1, hash1, function2, hash2, ...}' + % First step is to transpose the data concatenate both fields under + % each other. Since MATLAB indexing uses "column major order", + % traversing the concatenated array is in the order we want. + data = [data{:}]'; + allValues = data(:)'; + else + allValues = {}; + end + hashes = struct(allValues{:}); + end +end +% ============================================================================== +function bool = isValidAction(str) + % returns true for valid actions (keep/remove/ask) on "removedTests": + bool = ismember(lower(str), {'keep','remove','ask'}); +end +function bool = isActualAction(str) + % returns true for actual actions (keep/remove) on "removedTests" + bool = ismember(lower(str), {'keep','remove'}); +end +% ============================================================================== diff --git a/Functions/helper_functions_community/mat2tikz/test/suites/ACID.MATLAB.8.3.md5 b/Functions/helper_functions_community/mat2tikz/test/suites/ACID.MATLAB.8.3.md5 new file mode 100644 index 0000000..c476ebd --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/suites/ACID.MATLAB.8.3.md5 @@ -0,0 +1,104 @@ +alphaImage : c1f655e08814b6737d14fc62401464d2 +alphaTest : 636ce0a35bfc181f47970031f00f6b72 +annotationAll : b600c6654bf983288dd25a24d17b277b +annotationSubplots : c6c07fabe6ef6fc5fb4d0a00f3a98bd2 +annotationText : 0e479d484171cbe71208f132319a9dfb +annotationTextUnits : 212301f6fab8fc44936a1d6c8dd3ac59 +areaPlot : 53cc00dd9f6059d734722dc7d521be39 +axesColors : 863530544e1bd5d2ac2a40d115b0d89c +axesLocation : e7dac4ed9f58c31612496b3dd191b2fe +bars : b6c9b6bd0884fd9041c50ce57486a730 +besselImage : 2478d41afe2dbb5e3c7c3ca35433df4b +bodeplots : e58a307dacc26ad38259cff98c141352 +clownImage : 50cf1ed3d5954ea574ee1dcf850d2291 +colorbarLabelTitle : bb5e997230a6b1330c749d737b73f03b +colorbarLogplot : 4cb17bffe972526eac63da33a9222f6d +colorbarManualLocationLeftIn : 34c149317117365e93c2d4bc87342a36 +colorbarManualLocationLeftOut : 61df87739df57ce09e1ff941cede9b11 +colorbarManualLocationRightIn : 43929d243b1ce12aecfda3d130edf359 +colorbarManualLocationRightOut : ec8ed1290c5a3f45d4cb3795a66c0f1f +colorbars : 271fdd43648c30b49861e5ac492bfe37 +compassplot : 746fe6816c8f5e91bc30b748937ddf5f +contourPenny : bd9fe77201617c2aeec42680cf3db0cb +croppedImage : a7df6401799a09cb2868babe95ce4c77 +customLegend : cd7e76a0e4394ae7e166b0a38c81be2b +decayingharmonic : 5f430cbacf89268a100b9f0bc6b92874 +double_axes : b8d4a8ac79d6de3ff26a8e7b4e1f4cb2 +double_axes2 : 481f3f1c2e52f55a82193a39c6f8de28 +double_colorbar : 95740167b4b9fe7e082f5e6ff075b0f1 +errorBars : e156bb17b2c4938e683d1415cfd539b4 +errorBars2 : ca4bd1a1f0e835cb6628c92082534205 +fill3plot : 801300e07b52eb484e67abe761b6a0a4 +freqResponsePlot : d18c2174642550ca7373be8e207a43e7 +herrorbarPlot : f383b91e61b3fbc586b5c02ce2bafbfc +hgTransformPlot : e32beea57daf9940ffd74ae8b1a83403 +hist3d : 867f35c157a81cf207d63e558cf167e5 +imageOrientation_PNG : c90e5e7bd1575ab8f504a8370a43a21e +imageOrientation_inline : 9f8624bfd743ea0c3c19557e3b8319c5 +imagescplot : d1a7958ac53afd332e6fa934eee963f5 +imagescplot2 : fad5b247a605964440b150202d4c5a7a +latexInterpreter : 061001fe023e6e404d4fe024ce28d245 +latexmath2 : 082e5416e213056d6ebe4a1bccd4286d +legendplot : b005a57b62d43c3808abc57693090ddc +legendplotBoxoff : a6552f6438efa5ae5c1e1b2bd3742d09 +legendsubplots : e656f45706bd476cf2401824d1014f8c +linesWithOutliers : 0aa11947614995837eb1ce45265b45f5 +logbaseline : 23e4ebf3d9457aa26f940aca27fc32dd +logicalImage : 843c396ce40a2c255d915b87b9b6bc53 +logplot : 15455204620fc850b94d721f1f79cacc +mandrillImage : c3d5f63087be1e587af0f3b1f5efa573 +manualAlignment : a66a4d684ec5af5b3b967fbd9f9eba40 +many_random_points : 44f79b07c32fe2d334cb4d41bb245a2a +markerSizes : c80e1e82fcd9d7cbcb52265cd966273b +markerSizes2 : 2bc71206cca5fecdd89ee0ca6e7dec9a +meshPlot : 390a65be331e6aeab8f4164b36b85e30 +mixedBarLine : 1562c32dfa41afadd5e890cc844ce139 +multiline_labels : 31fbbf1dcc63516bafa8479c14812706 +multipleAxes : c7c64c5f627f8633ee89ae02ba0b487e +multiplePatches : d20f5bcb061e68e4e7848f2115d8589b +myBoxplot : 1811640f9161ebbed0cec285ba423f7d +overlappingPlots : 7e9618c8abc6eef40000202f12e201f8 +pColorPlot : edba1f3b39503095cace3d7053ad22a6 +parameterCurve3d : 79b6bfee183a996894c7964a023e312f +parameterSurf : f65ce01c7e7a560205ee69e01d318b14 +peaks_contour : f6f964a8759939d5948e8fad796d481a +peaks_contourf : 9e960b9d128dfbd102a8bbe2c5ab15ef +pixelLegend : 3ac112865f3b1a50c62fb0b5f19d4215 +plain_cos : 9a8455b8cc710436572bc825de0cec78 +plotyyLegends : 9ed8515333235d8f84692a7d874e1d57 +polarplot : 35bcc693ce44029933edc6039ee4e652 +quiver3plot : 49ff594d23510463b75103fd099acf62 +quiveroverlap : 2e63a73822cc65d8086d90e23d52cbcf +quiverplot : 7dbf0db4142bdbf7d421e83a66c149a5 +randomWithLines : 5227e4b38ad2503e9bae3ec6e566ac4a +rectanglePlot : 0d9b3ef5dd01905fa987d81d139ec5fa +removeOutsideMarker : c5c22d6ad18cb05dff6e0ef9bf0f20bf +rlocusPlot : b2f0237a3bc42ca1f9305dc411437949 +roseplot : 3c61d3d9cd107a135ac958e3a2aa5d6c +scatter3Plot : a3c58f7801a9cd7805a0bf888321d570 +scatterPlot : c7f71e9961a43df1047e938ff00f45cd +scatterPlotMarkers : 4035e2da2a8e1ddeb4badeede264858c +scatterPlotRandom : 60feab8df90afa2b60d79f98e98b58bc +sine_with_annotation : a770a5ab9552941d0a663d7850785807 +sine_with_markers : 003c40a83b2dfaf7b6dfbcb9d74a505d +spectro : 330dde4cc92550a49ea3df07455e6d9c +spherePlot : 93eeb7042de0a8a18a59208d0e50df23 +stackedBarsWithOther : 708cb63f69815f47a31b9918c9c2c715 +stairsplot : 95ff3a04e9d5fec297d4b52dc895779d +stemplot : e1d5c43f90a3c34b0c0463c10dcd8edd +stemplot2 : 8cbf11bd5d2f388b4b572844f6960e60 +subplot2x2b : 71c072af504b2bb208c8d7743ae0e5a2 +subplotCustom : 606bc283da5b9c0319c4105910393050 +superkohle : 84ab937a68f0969598247b7b19b18cc3 +surfPlot : 652c4e1f83e0165199f952b8f1c412bb +surfPlot2 : 026acc60aab65d0567052cbf870a9704 +texInterpreter : b67b59f407a551196fa1448d9c35e9e7 +texcolor : 7a8b8c01525fc11ac4f1186c803748a9 +texrandom : 4cc05e583bb3cfbc9884ed5f03f10e5a +textAlignment : 6d51ee2120803e85237a57a25b6b43e7 +textext : 5be451a1b3314c54ee8ba519f6d2cae9 +xAxisReversed : 610fc5e22fa2c7efbdf788115cc7eb2d +ylabels : 6daba91575bb4a6c2ff58d8d9a171526 +zoom : 4b140788bbd7c2e0f1a4d8b46640c9eb +zplanePlot1 : 44a119eb5b2266b5e765bac21e6a488e +zplanePlot2 : 95db146feb3d517f9e2540a551a50659 diff --git a/Functions/helper_functions_community/mat2tikz/test/suites/ACID.MATLAB.8.4.md5 b/Functions/helper_functions_community/mat2tikz/test/suites/ACID.MATLAB.8.4.md5 new file mode 100644 index 0000000..cf4a1cf --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/suites/ACID.MATLAB.8.4.md5 @@ -0,0 +1,104 @@ +alphaImage : 14978e97c02baf045fb05ed6b4a5b391 +alphaTest : d194e2297b5dd5a0bb1282f9142ddda7 +annotationAll : 0a8a1578d17a16b3a5db57434069c6fc +annotationSubplots : 30c06f8d5e3d30bbf1ccf8df29576f16 +annotationText : 9f293eb4bb31ac29c44d1c772b05ac74 +annotationTextUnits : 68f570a8fa1d3a0cec44ef2a1d58aa3b +areaPlot : 3c36bd59014ca6346d6cbbbb6f78943a +axesColors : 863530544e1bd5d2ac2a40d115b0d89c +axesLocation : e7dac4ed9f58c31612496b3dd191b2fe +bars : aa8ef10df99c7d750c92c7ba95a73814 +besselImage : c5a0696550fda36e7d2eba7d91643716 +bodeplots : a2d8208c81d7f449b51107907c43e053 +clownImage : 50cf1ed3d5954ea574ee1dcf850d2291 +colorbarLabelTitle : 3ff06d0ee178a052429194d184ee0182 +colorbarManualLocationLeftIn : fce39178f29dcdab800d6c99edc70bed +colorbarManualLocationLeftOut : 0b35797bac696ec978b1dcac2c5baa20 +colorbarManualLocationRightIn : 776fbc67733cbd2104384d76de954eda +colorbarManualLocationRightOut : fd1a74e61c95b0c185ac26d1cffee4a3 +colorbars : 0795ee171bf5954ff7e36d3ddc3867d5 +compassplot : bfdceefee37557845bbc0b4c7609ff50 +contourPenny : 11b3f1178d9585112b4d40f47a334de0 +croppedImage : c3a4a5e6ac11450d797c259a867dfd72 +customLegend : cc43f95e60cd8ff447c61fdebbaef18f +decayingharmonic : 2d9be9791b36efc6e1d052769dba3369 +double_axes : af07a7caf0effe5eb70fd23f21137ebb +double_axes2 : 6cd1b9ea3b7ae29801738674446e5f64 +double_colorbar : 47eaadfd2b100239e63c3175382c0598 +errorBars : 9a959889b00bdc80a18aae4cef6fff24 +errorBars2 : ee08727841633566b12ee1553f587b98 +fill3plot : 801300e07b52eb484e67abe761b6a0a4 +freqResponsePlot : f307eac45a335d76278beb4209b7f335 +herrorbarPlot : 9f7e3880d3d77af312f5af20ab45d427 +hgTransformPlot : 8c0a136c4bb0ec5ff02a344bc9ef9bf0 +hist3d : 867f35c157a81cf207d63e558cf167e5 +histogramPlot : a96b7898c7c426edc4d8ba680dd4b4c2 +imageOrientation_PNG : 94b2656bb5235d842d65085094f0dfc7 +imageOrientation_inline : 8f6b04de03cdc80a3ba1b5a35832e8a7 +imagescplot : d1a7958ac53afd332e6fa934eee963f5 +imagescplot2 : fad5b247a605964440b150202d4c5a7a +latexInterpreter : 827313b6ff9dceb1ca27e814e5d7cc81 +latexmath2 : 5359827e1d218311bf80228f89deffdf +legendplot : a61471a19448f4d097c3053de5d4ca13 +legendplotBoxoff : a6552f6438efa5ae5c1e1b2bd3742d09 +legendsubplots : 474a38fa9ddcf06bfab08a53ddc42032 +linesWithOutliers : 0aa11947614995837eb1ce45265b45f5 +logbaseline : 86f6627cae4fd5ca8891de0fff521ff4 +logicalImage : 843c396ce40a2c255d915b87b9b6bc53 +logplot : 5d5c676b2a3338558939d0407171edc6 +mandrillImage : c3d5f63087be1e587af0f3b1f5efa573 +manualAlignment : a66a4d684ec5af5b3b967fbd9f9eba40 +many_random_points : 44f79b07c32fe2d334cb4d41bb245a2a +markerSizes : c80e1e82fcd9d7cbcb52265cd966273b +markerSizes2 : 2bc71206cca5fecdd89ee0ca6e7dec9a +meshPlot : 390a65be331e6aeab8f4164b36b85e30 +mixedBarLine : dfcba35ebc32c940566368ea83323add +multiline_labels : d81ea3e1c07d9010027a40854f4e0e4d +multipleAxes : c7c64c5f627f8633ee89ae02ba0b487e +multiplePatches : e8a187bc7c133435cfad346eb7957a9d +myBoxplot : abdb5d198d82e79940999cf61563099f +overlappingPlots : 27f112e843ef7c8835cee164c70f2b1e +pColorPlot : 0dbd6a9f0cc5dd2691d7d3767b831f68 +parameterCurve3d : 6aead7a8377449e056856f5b7d6fb9ed +parameterSurf : 0bc50b54c2bff6e22ddf7e9a2a479f7c +peaks_contour : 5e6873909d68b0e397c14eb2deb58e4f +peaks_contourf : 8532510c96c15f96b9a8ad3843446c20 +pixelLegend : 3ac112865f3b1a50c62fb0b5f19d4215 +plain_cos : 3b0a06de2ae24b7242eef8840acfaca2 +plotyyLegends : 9ed8515333235d8f84692a7d874e1d57 +polarplot : e232124afee0fdf260dea1cdbb497275 +quiver3plot : 5dd5d89ff4c5d3de0c000a999814d10a +quiveroverlap : f977afb99d073b7ea4a54bd7cc254300 +quiverplot : 95e36d3db46c861ccfcca981af846aa6 +randomWithLines : 9857814e260c4a929f5f0e9e92ba896d +rectanglePlot : 0d9b3ef5dd01905fa987d81d139ec5fa +removeOutsideMarker : f9330795ff85ba1ce4753a700825ab8c +rlocusPlot : 03d2da6924f15785a9f803e0cbe88252 +roseplot : b095b86b171a56d817834d803e9e4b6a +scatter3Plot : ed343b46e0c71f8b87b71bd32e645a17 +scatterPlot : 5d0b1fca0e65b2f8b16bfa5872f3ba1a +scatterPlotMarkers : 4035e2da2a8e1ddeb4badeede264858c +scatterPlotRandom : 60feab8df90afa2b60d79f98e98b58bc +sine_with_annotation : 98cd4228e6a58210dc10768bc3d48298 +sine_with_markers : 003c40a83b2dfaf7b6dfbcb9d74a505d +spectro : 954d57a5d3e0a6b71ed4b530295829c7 +spherePlot : a0728a993418536015482b6e2262f530 +stackedBarsWithOther : 24f65175516e4af409e0b5a344e69590 +stairsplot : 681cd9b7ec346c36ae53de4294148153 +stemplot : b20a8531bc0a24b7b9ed161e150c15ff +stemplot2 : ce57617afde9d61311536fb018fc2ace +subplot2x2b : 55c61c78e5b58bea889f6dc4c53cad8e +subplotCustom : ebba99c098189a9e90a69925b4f842c2 +superkohle : 6f3903621fbfc0e52c6e5ef379e6cadc +surfPlot : 4b3c88fd47161c784ce117e08e0a4c8c +surfPlot2 : ea785ea64ce8763913edff0ff1846ed1 +texInterpreter : b67b59f407a551196fa1448d9c35e9e7 +texcolor : 7a8b8c01525fc11ac4f1186c803748a9 +texrandom : 111d9b06c1440d1ecf80df9498fb4daf +textAlignment : 8b0a62113daf8ec03a711d1dc7f47e2b +textext : 5be451a1b3314c54ee8ba519f6d2cae9 +xAxisReversed : 610fc5e22fa2c7efbdf788115cc7eb2d +ylabels : 0f03d59d01b31956b87861e5705c28f2 +zoom : 731634a7fd3efbe0bfc72405614ae3fd +zplanePlot1 : 4d974fa9a55b6298d0637654fd7d86ce +zplanePlot2 : 6b7848acad9ce16f9d3ce303ff85cc17 diff --git a/Functions/helper_functions_community/mat2tikz/test/suites/ACID.Octave.3.8.0.md5 b/Functions/helper_functions_community/mat2tikz/test/suites/ACID.Octave.3.8.0.md5 new file mode 100644 index 0000000..7109ac9 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/suites/ACID.Octave.3.8.0.md5 @@ -0,0 +1,87 @@ +alphaImage : a51ff2c6514fb5606ee2fcb732edc7ea +alphaTest : bd2d78b8d373342d5285132b607a2f46 +annotationAll : 719ee5b81452f375afa95b757226f6ec +annotationSubplots : aa261262dff0a1f26487d4a0f06cd828 +annotationText : c7185fe122b2e3fa5335fbc51bb89103 +annotationTextUnits : 1d29851134c715342efc36a690d5b396 +areaPlot : e04438af8498d2aa6fba5d242a8290ed +axesColors : 45751a2a4fd30e0c888237721c0ae018 +axesLocation : 5189186c3185e8b3fa52622af950a0fd +bars : 15a269e905602896394eabd367040ce1 +besselImage : 0ffc3e10adb87029b44cbeb4bc435e07 +colorbarLabelTitle : 272a4d2f645b296e43c01f4ab2ae25a4 +colorbarLogplot : cb326bbb54e486a40d387acfe82f1b72 +colorbarManualLocationLeftIn : d2d90ddaa56b9f13d009a595c21e9601 +colorbarManualLocationLeftOut : dea8de2bdf0d866dc3f4f2b8327ea494 +colorbarManualLocationRightIn : c8866d76fee1a0876cf3f46aa5bb5761 +colorbarManualLocationRightOut : 81b67065a6281c3de6c1a9181d4dfcbc +colorbars : 13b6b31da1f9aef218ef68a6a2c4bbc5 +compassplot : 94efa27f5503d3a56e6b40a0ca0f75d1 +contourPenny : 3dab076bb0f089b4c30f321a8b732331 +customLegend : 7b0d1d80a2f72bc920d869151ffe96c1 +decayingharmonic : faaac45ba8f9262cf8a879323d3bf253 +double_axes : 243d26b1446d316955cded4098ef76df +double_axes2 : 1fb2d96e2845da4935e251d2e860135c +errorBars : 50b3272a0eff1a540911265dc86840e7 +errorBars2 : af50a7241d97ac9e2c78a4f157e056a2 +herrorbarPlot : aca7b04d007f2da41617eb027c15b0ef +imageOrientation_PNG : 8086dd5610c028cf75501954cbaccf2c +imageOrientation_inline : 02c0fb3e810aab39cb690e0fa3f8b7eb +imagescplot : 88328b4f32c5ed1cc4e0ad09ea7f47cb +imagescplot2 : 39ce8c49f28f3ce90a6bd5a9d0f1cd42 +latexInterpreter : e02f1a2468683a3893b0ba5622ba556d +latexmath2 : 0453c91a38ab2e002b7744825c1aa0e9 +legendplot : 5f351094a8e4cd2192fe7cbb2859fd5c +legendplotBoxoff : 9b2f5fb5630b53c4f7e0cbade5c8a719 +legendsubplots : 17c08345bafc58e1562984b37d4e01c9 +linesWithOutliers : 21dd1eb9763a12d1367a43f2e4314057 +logbaseline : 8d62e25d03732cdfc8cb2dd419e0b1b5 +logicalImage : 82e9e5ed998aa1ecd78b514d801dc914 +logplot : ecb205ea014ba6d0042579c3d608d978 +manualAlignment : 91b81ccba3a733f1660de692f1a24ead +many_random_points : 465f2cfaf13757239404db65e9c49da8 +markerSizes : f0853e39d2cc6c67ca23306fdb32b6c0 +markerSizes2 : ee9ceeb6e9d413b80c070f267914f479 +meshPlot : 0ed5e09884b72b670f4d67b134f714f2 +mixedBarLine : 124b6efc0a9b4d350e0f179bdcc794c9 +multiline_labels : 113098f446ba5b37be05b2e495753a38 +multipleAxes : 33ea385933a0e54967fdfd2a763116d6 +multiplePatches : 5136db15994352b537d5261586efb68e +overlappingPlots : 6372bbcaaf0f9831a9a371c154c68510 +pColorPlot : ea8de8ae9a060827a15589640a7c2af2 +parameterCurve3d : d74674982f8de1a07e2d91690442c640 +peaks_contour : a08b52735b11941aef4194d42c50ca95 +peaks_contourf : bae99cfb0c6d76fbb9eda7cf3253c861 +pixelLegend : 88bcddd20a3c5626ef158e2252685ec9 +plain_cos : 83d0268e8b3115003942bd1bdaf2672f +plotyyLegends : 4195fadd2c55b022391f8addbbfc2dd5 +polarplot : a663c1b56d3073601536d4dacae5e8a0 +quiver3plot : 2262caf036feb9b926af20dccee4674d +quiveroverlap : b565fbfd750ff33aa3e24500218458c4 +quiverplot : 5183781255b220415251eb7bcb5d8707 +randomWithLines : 152796f6be5be2a94832bfb5ea126483 +rectanglePlot : 0a6f4e29891fdd8492d821df65ed727f +removeOutsideMarker : c26260549046c7a885617d03c1a6e714 +roseplot : 526bbf61758a3ac158b2f1de6a39de73 +scatter3Plot : 85a3d342f218f8ec4b0734bbefa13d0c +scatterPlotMarkers : 5dada75b14206174059f357028f82de5 +scatterPlotRandom : a8bc3cbcd74de1e38783b974586d875d +sine_with_annotation : 0b0f9823c6c36cae49d5053d8b984884 +sine_with_markers : d8761700c3dbfb3a496dc962c9d3ec64 +spherePlot : 42ab300428e3248d03105b83cac2b312 +stackedBarsWithOther : ac3c350f5af365630e0aaf4352108dd8 +stairsplot : 9338e729f5569578228a7bac2d123a9a +stemplot : 557cad38e474e0cd7878b4680c33d291 +stemplot2 : 7450fcbc9332ecbd8ac9e801aad4d8fc +subplot2x2b : 7db858e541e4edc35e9721226d8a0f5a +subplotCustom : 588c7323ed9a6a69cbf71849a84d961f +surfPlot : 5f6735d7f3ae960526c10335d3b232b6 +surfPlot2 : 30a4abfa8829ce6562f6bceb9e702b74 +texInterpreter : 64e4b7edcb6f21f14f3b60544a1f48bc +texcolor : 8b5363038aa4454372cf03ae13f3a91a +texrandom : f85439ecf43d7dc6f150a9977f1900ff +textAlignment : 5e75059b1f661be10d3d790983f20f40 +textext : 730e7111a60f2f5fa0a9aa1a4445e242 +xAxisReversed : b7c279800afb84deb37cb9208a657a24 +ylabels : b7180ba11dd0428064340ab39935e9b1 +zoom : 7777afa78e6ee6169841ccd5a894c942 diff --git a/Functions/helper_functions_community/mat2tikz/test/suites/ACID.Octave.4.2.0.md5 b/Functions/helper_functions_community/mat2tikz/test/suites/ACID.Octave.4.2.0.md5 new file mode 100644 index 0000000..0571694 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/suites/ACID.Octave.4.2.0.md5 @@ -0,0 +1,87 @@ +alphaImage : a51ff2c6514fb5606ee2fcb732edc7ea +alphaTest : 0aabea57e8351373e2c3090fa1929435 +annotationAll : 5139104b005a70d3650c6dedb8f1dc7e +annotationSubplots : 3da2ff3cadd2167ab1726c88f18a8a63 +annotationText : efe23b1567345ec24564b598c0d2c146 +annotationTextUnits : 0c874e12ee2167ffe041b48ab2124417 +areaPlot : cdb88d8449ea9275c7eeed21c236e055 +axesColors : 45751a2a4fd30e0c888237721c0ae018 +axesLocation : 27dd4473cb5871188a20d7e6cbc0d171 +bars : 15a269e905602896394eabd367040ce1 +besselImage : 0ffc3e10adb87029b44cbeb4bc435e07 +colorbarLabelTitle : 272a4d2f645b296e43c01f4ab2ae25a4 +colorbarLogplot : cb326bbb54e486a40d387acfe82f1b72 +colorbarManualLocationLeftIn : d2d90ddaa56b9f13d009a595c21e9601 +colorbarManualLocationLeftOut : dea8de2bdf0d866dc3f4f2b8327ea494 +colorbarManualLocationRightIn : c8866d76fee1a0876cf3f46aa5bb5761 +colorbarManualLocationRightOut : 81b67065a6281c3de6c1a9181d4dfcbc +colorbars : 13b6b31da1f9aef218ef68a6a2c4bbc5 +compassplot : 94efa27f5503d3a56e6b40a0ca0f75d1 +contourPenny : fbd639280f4c31dcce7d463e4eac27f4 +customLegend : 7b0d1d80a2f72bc920d869151ffe96c1 +decayingharmonic : a5551e12b79edd8f32bd5d91ae5fa561 +double_axes : 125855bab62dbd8a599bfb6d9d2a041f +double_axes2 : 6cd1b9ea3b7ae29801738674446e5f64 +errorBars : 5d31dee2340e12e5b48aad2d93f89b3f +errorBars2 : 85a015a7afc2c6a7046053c887cce43a +herrorbarPlot : f2eaee609ad23ad02d03e78912a5f23a +imageOrientation_PNG : 8086dd5610c028cf75501954cbaccf2c +imageOrientation_inline : 02c0fb3e810aab39cb690e0fa3f8b7eb +imagescplot : 88328b4f32c5ed1cc4e0ad09ea7f47cb +imagescplot2 : 39ce8c49f28f3ce90a6bd5a9d0f1cd42 +latexInterpreter : 73b51aed59a6644c15f1561e51ecbf6d +latexmath2 : 5359827e1d218311bf80228f89deffdf +legendplot : 5f351094a8e4cd2192fe7cbb2859fd5c +legendplotBoxoff : 2654de1ee49ac8f4d2546eb6f254952f +legendsubplots : 17c08345bafc58e1562984b37d4e01c9 +linesWithOutliers : 0aa11947614995837eb1ce45265b45f5 +logbaseline : f23182b1e230b1b21244ad9364475863 +logicalImage : 82e9e5ed998aa1ecd78b514d801dc914 +logplot : 5d5c676b2a3338558939d0407171edc6 +manualAlignment : ff6a5307120276310c01fb6f9c223d77 +many_random_points : 46b09dca92fc4f2517a198427f44b874 +markerSizes : e0d2628d90419f38457b7ee71d178bb1 +markerSizes2 : 560cc9e62fe361aa5456ef87fb715649 +meshPlot : 390a65be331e6aeab8f4164b36b85e30 +mixedBarLine : 124b6efc0a9b4d350e0f179bdcc794c9 +multiline_labels : 113098f446ba5b37be05b2e495753a38 +multipleAxes : c7c64c5f627f8633ee89ae02ba0b487e +multiplePatches : 4d77a30b4a6b14772505e0e9c2a727cf +overlappingPlots : 714f8029c2ad4fc86a8fb5323f35b0e4 +pColorPlot : ce623ad5a9ebf4b3c74d5954027f0631 +parameterCurve3d : ce2a304c64eb5283a40779f6f410d6ba +peaks_contour : a08b52735b11941aef4194d42c50ca95 +peaks_contourf : bfe49f12ff8710df9fbf6bd9672ca124 +pixelLegend : 2afe53d2be6f01a41b74c5bd892fda83 +plain_cos : defebe18ad95f9f1b1f823ad96bd12bd +plotyyLegends : 96cb3c09f470251db4ddf24f59e8e9f4 +polarplot : a663c1b56d3073601536d4dacae5e8a0 +quiver3plot : 5dd5d89ff4c5d3de0c000a999814d10a +quiveroverlap : f446d413254f3f1e8e4ec449b8a7f813 +quiverplot : 22d70477697462b313c3d56ef78ac9c8 +randomWithLines : e96d8488671951033bc98d94163a06cb +rectanglePlot : 045df69771942ea322873279a61c7b9f +removeOutsideMarker : f0606cd40ecafc645b10b706d84aa874 +roseplot : 526bbf61758a3ac158b2f1de6a39de73 +scatter3Plot : c9cc13e983b56272dcbb3c90edd1f8c1 +scatterPlotMarkers : a5ec68437ee9e103ec849bdd1ab6657c +scatterPlotRandom : 3dc985fcb9773a82a156321e12e6460b +sine_with_annotation : 0b0f9823c6c36cae49d5053d8b984884 +sine_with_markers : 2cf4b72e72482e4b192012c4165ef5b5 +spherePlot : 5b0e48733101f43996cc70c2962b52d8 +stackedBarsWithOther : ac3c350f5af365630e0aaf4352108dd8 +stairsplot : 73051cf170b77661c4ea464718af1ded +stemplot : fcf6f58d74e51684f5aedd090506b562 +stemplot2 : 7450fcbc9332ecbd8ac9e801aad4d8fc +subplot2x2b : 7db858e541e4edc35e9721226d8a0f5a +subplotCustom : 14bbb2edbd657c7386139bfd7085f8c9 +surfPlot : 2aadedca0c9e2fe866252a5969fdd08a +surfPlot2 : 30a4abfa8829ce6562f6bceb9e702b74 +texInterpreter : b67b59f407a551196fa1448d9c35e9e7 +texcolor : 7a8b8c01525fc11ac4f1186c803748a9 +texrandom : 4dacf9590338ff73553c4a82a78e09bf +textAlignment : 5e75059b1f661be10d3d790983f20f40 +textext : 5be451a1b3314c54ee8ba519f6d2cae9 +xAxisReversed : 1448dd92ba4ca7c492f060e3d585d340 +ylabels : 9e68a9002553bcfe576a22d60ec7c5e9 +zoom : 7777afa78e6ee6169841ccd5a894c942 diff --git a/Functions/helper_functions_community/mat2tikz/test/suites/ACID.Octave.4.2.2.md5 b/Functions/helper_functions_community/mat2tikz/test/suites/ACID.Octave.4.2.2.md5 new file mode 100644 index 0000000..2ddeafe --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/suites/ACID.Octave.4.2.2.md5 @@ -0,0 +1,87 @@ +alphaImage : a51ff2c6514fb5606ee2fcb732edc7ea +alphaTest : 0aabea57e8351373e2c3090fa1929435 +annotationAll : 5139104b005a70d3650c6dedb8f1dc7e +annotationSubplots : 3da2ff3cadd2167ab1726c88f18a8a63 +annotationText : efe23b1567345ec24564b598c0d2c146 +annotationTextUnits : 0c874e12ee2167ffe041b48ab2124417 +areaPlot : cdb88d8449ea9275c7eeed21c236e055 +axesColors : 45751a2a4fd30e0c888237721c0ae018 +axesLocation : 27dd4473cb5871188a20d7e6cbc0d171 +bars : 15a269e905602896394eabd367040ce1 +besselImage : 0ffc3e10adb87029b44cbeb4bc435e07 +colorbarLabelTitle : 272a4d2f645b296e43c01f4ab2ae25a4 +colorbarLogplot : cb326bbb54e486a40d387acfe82f1b72 +colorbarManualLocationLeftIn : d2d90ddaa56b9f13d009a595c21e9601 +colorbarManualLocationLeftOut : dea8de2bdf0d866dc3f4f2b8327ea494 +colorbarManualLocationRightIn : c8866d76fee1a0876cf3f46aa5bb5761 +colorbarManualLocationRightOut : 81b67065a6281c3de6c1a9181d4dfcbc +colorbars : 13b6b31da1f9aef218ef68a6a2c4bbc5 +compassplot : 94efa27f5503d3a56e6b40a0ca0f75d1 +contourPenny : fbd639280f4c31dcce7d463e4eac27f4 +customLegend : 7b0d1d80a2f72bc920d869151ffe96c1 +decayingharmonic : a5551e12b79edd8f32bd5d91ae5fa561 +double_axes : 125855bab62dbd8a599bfb6d9d2a041f +double_axes2 : 6cd1b9ea3b7ae29801738674446e5f64 +errorBars : 52e1abac8c4ba10d9427cebc9e6845db +errorBars2 : c0750114ad1821a37a833e378217ef5b +herrorbarPlot : 92c6a3ebd7e6194362b31117b1e52d92 +imageOrientation_PNG : 8086dd5610c028cf75501954cbaccf2c +imageOrientation_inline : 02c0fb3e810aab39cb690e0fa3f8b7eb +imagescplot : 88328b4f32c5ed1cc4e0ad09ea7f47cb +imagescplot2 : 39ce8c49f28f3ce90a6bd5a9d0f1cd42 +latexInterpreter : 73b51aed59a6644c15f1561e51ecbf6d +latexmath2 : 5359827e1d218311bf80228f89deffdf +legendplot : 5f351094a8e4cd2192fe7cbb2859fd5c +legendplotBoxoff : 2654de1ee49ac8f4d2546eb6f254952f +legendsubplots : 17c08345bafc58e1562984b37d4e01c9 +linesWithOutliers : 0aa11947614995837eb1ce45265b45f5 +logbaseline : f23182b1e230b1b21244ad9364475863 +logicalImage : 82e9e5ed998aa1ecd78b514d801dc914 +logplot : 5d5c676b2a3338558939d0407171edc6 +manualAlignment : ff6a5307120276310c01fb6f9c223d77 +many_random_points : 44f79b07c32fe2d334cb4d41bb245a2a +markerSizes : c80e1e82fcd9d7cbcb52265cd966273b +markerSizes2 : 2bc71206cca5fecdd89ee0ca6e7dec9a +meshPlot : 390a65be331e6aeab8f4164b36b85e30 +mixedBarLine : 124b6efc0a9b4d350e0f179bdcc794c9 +multiline_labels : 113098f446ba5b37be05b2e495753a38 +multipleAxes : c7c64c5f627f8633ee89ae02ba0b487e +multiplePatches : 4d77a30b4a6b14772505e0e9c2a727cf +overlappingPlots : 714f8029c2ad4fc86a8fb5323f35b0e4 +pColorPlot : ce623ad5a9ebf4b3c74d5954027f0631 +parameterCurve3d : ce2a304c64eb5283a40779f6f410d6ba +peaks_contour : a08b52735b11941aef4194d42c50ca95 +peaks_contourf : bae99cfb0c6d76fbb9eda7cf3253c861 +pixelLegend : 2afe53d2be6f01a41b74c5bd892fda83 +plain_cos : defebe18ad95f9f1b1f823ad96bd12bd +plotyyLegends : 96cb3c09f470251db4ddf24f59e8e9f4 +polarplot : a663c1b56d3073601536d4dacae5e8a0 +quiver3plot : 5dd5d89ff4c5d3de0c000a999814d10a +quiveroverlap : f446d413254f3f1e8e4ec449b8a7f813 +quiverplot : 22d70477697462b313c3d56ef78ac9c8 +randomWithLines : 9857814e260c4a929f5f0e9e92ba896d +rectanglePlot : 045df69771942ea322873279a61c7b9f +removeOutsideMarker : f9330795ff85ba1ce4753a700825ab8c +roseplot : 526bbf61758a3ac158b2f1de6a39de73 +scatter3Plot : cede4e9f5248013f47b95e589d805ea8 +scatterPlotMarkers : b9f6262dba1b2035eb14abda46194ba7 +scatterPlotRandom : 4b47c90b5493c5e53c3e60a4681e93fd +sine_with_annotation : 0b0f9823c6c36cae49d5053d8b984884 +sine_with_markers : 2cf4b72e72482e4b192012c4165ef5b5 +spherePlot : 5b0e48733101f43996cc70c2962b52d8 +stackedBarsWithOther : ac3c350f5af365630e0aaf4352108dd8 +stairsplot : 73051cf170b77661c4ea464718af1ded +stemplot : 983fded97cd2f6cb3286bbcbc5002473 +stemplot2 : 7450fcbc9332ecbd8ac9e801aad4d8fc +subplot2x2b : 7db858e541e4edc35e9721226d8a0f5a +subplotCustom : 14bbb2edbd657c7386139bfd7085f8c9 +surfPlot : 2aadedca0c9e2fe866252a5969fdd08a +surfPlot2 : 30a4abfa8829ce6562f6bceb9e702b74 +texInterpreter : b67b59f407a551196fa1448d9c35e9e7 +texcolor : 7a8b8c01525fc11ac4f1186c803748a9 +texrandom : 4dacf9590338ff73553c4a82a78e09bf +textAlignment : 5e75059b1f661be10d3d790983f20f40 +textext : 5be451a1b3314c54ee8ba519f6d2cae9 +xAxisReversed : 1448dd92ba4ca7c492f060e3d585d340 +ylabels : 9e68a9002553bcfe576a22d60ec7c5e9 +zoom : 7777afa78e6ee6169841ccd5a894c942 diff --git a/Functions/helper_functions_community/mat2tikz/test/suites/ACID.m b/Functions/helper_functions_community/mat2tikz/test/suites/ACID.m new file mode 100644 index 0000000..a3c23c5 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/suites/ACID.m @@ -0,0 +1,2819 @@ +% ========================================================================= +% *** FUNCTION ACID +% *** +% *** MATLAB2TikZ ACID test functions +% *** +% ========================================================================= +function [status] = ACID(k) + + % assign the functions to test + testfunction_handles = { ... + @multiline_labels , ... + @plain_cos , ... + @sine_with_markers , ... + @markerSizes , ... + @markerSizes2 , ... + @sine_with_annotation, ... + @linesWithOutliers , ... + @peaks_contour , ... + @contourPenny , ... + @peaks_contourf , ... + @many_random_points , ... + @double_colorbar , ... + @randomWithLines , ... + @double_axes , ... + @double_axes2 , ... + @logplot , ... + @colorbarLogplot , ... + @legendplot , ... + @legendplotBoxoff , ... + @plotyyLegends , ... + @zoom , ... + @quiveroverlap , ... + @quiverplot , ... + @quiver3plot , ... + @logicalImage , ... + @imagescplot , ... + @imagescplot2 , ... + @stairsplot , ... + @polarplot , ... + @roseplot , ... + @compassplot , ... + @stemplot , ... + @stemplot2 , ... + @bars , ... + @xAxisReversed , ... + @errorBars , ... + @errorBars2 , ... + @subplot2x2b , ... + @manualAlignment , ... + @subplotCustom , ... + @legendsubplots , ... + @bodeplots , ... + @rlocusPlot , ... + @mandrillImage , ... + @besselImage , ... + @clownImage , ... + @zplanePlot1 , ... + @zplanePlot2 , ... + @freqResponsePlot , ... + @axesLocation , ... + @axesColors , ... + @multipleAxes , ... + @scatterPlotRandom , ... + @scatterPlot , ... + @scatter3Plot , ... + @spherePlot , ... + @surfPlot , ... + @surfPlot2 , ... + @superkohle , ... + @meshPlot , ... + @ylabels , ... + @spectro , ... % takes pretty long to LuaLaTeX-compile + @mixedBarLine , ... + @decayingharmonic , ... + @texcolor , ... + @textext , ... + @texrandom , ... + @latexInterpreter , ... + @latexmath2 , ... + @parameterCurve3d , ... + @parameterSurf , ... + @fill3plot , ... + @rectanglePlot , ... + @herrorbarPlot , ... + @hist3d , ... + @myBoxplot , ... + @areaPlot , ... + @customLegend , ... + @pixelLegend , ... + @croppedImage , ... + @pColorPlot , ... + @hgTransformPlot , ... + @scatterPlotMarkers , ... + @multiplePatches , ... + @logbaseline , ... + @alphaImage , ... + @annotationAll , ... + @annotationSubplots , ... + @annotationText , ... + @annotationTextUnits , ... + @imageOrientation_PNG, ... + @imageOrientation_inline, ... + @texInterpreter , ... + @stackedBarsWithOther, ... + @colorbarLabelTitle , ... + @textAlignment , ... + @overlappingPlots , ... + @histogramPlot , ... + @alphaTest , ... + @removeOutsideMarker , ... + @colorbars , ... + @colorbarManualLocationRightOut , ... + @colorbarManualLocationRightIn , ... + @colorbarManualLocationLeftOut , ... + @colorbarManualLocationLeftIn + }; + + + numFunctions = length( testfunction_handles ); + + if (k<=0) + status = testfunction_handles; + return; % This is used for querying numFunctions. + + elseif (k<=numFunctions) + status = testfunction_handles{k}(); + status.function = func2str(testfunction_handles{k}); + + else + error('testfunctions:outOfBounds', ... + 'Out of bounds (number of testfunctions=%d)', numFunctions); + end + +end +% ========================================================================= +function data = ACID_data() + % Data to be used for various ACID tests + % This ensures the tests don't rely on functions that yield + % non-deterministic output, e.g. `rand` and `svd`. + data = [ 11 11 9 + 7 13 11 + 14 17 20 + 11 13 9 + 43 51 69 + 38 46 76 + 61 132 186 + 75 135 180 + 38 88 115 + 28 36 55 + 12 12 14 + 18 27 30 + 18 19 29 + 17 15 18 + 19 36 48 + 32 47 10 + 42 65 92 + 57 66 151 + 44 55 90 + 114 145 257 + 35 58 68 + 11 12 15 + 13 9 15 + 10 9 7]; +end +% ========================================================================= +function [stat] = multiline_labels() + stat.description = 'Test multiline labels and plot some points.'; + stat.unreliable = isOctave || isMATLAB(); %FIXME: `width` is inconsistent, see #552 + + m = [0 1 1.5 1 -1]; + plot(m,'*-'); hold on; + plot(m(end:-1:1)-0.5,'x--'); + + title({'multline','title'}); + legend({sprintf('multi-line legends\ndo work 2^2=4'), ... + sprintf('second\nplot')}); + xlabel(sprintf('one\ntwo\nthree')); + ylabel({'one','° ∞', 'three'}); + + set(gca,'YTick', []); + set(gca,'XTickLabel',{}); +end +% ========================================================================= +function [stat] = plain_cos() + stat.description = 'Plain cosine function.'; + + t = linspace(0, 2*pi, 1e5); + x = cos(t); + + % Explicitely cut the line into segments + x([2e4, 5e4, 8e4]) = NaN; + + % Plot the cosine + plot(t, x); + xlim([0, 2*pi]); + + % also add some patches to test their border color reproduction + hold on; + h(1) = fill(pi*[1/4 1/4 1/2 1/2] , [-2 1 1 -2], 'y'); + h(2) = fill(pi*[1/4 1/4 1/2 1/2]+pi, -[-2 1 1 -2], 'y'); + + set(h(1), 'EdgeColor', 'none', 'FaceColor', 0.8*[1 1 1]); + set(h(2), 'EdgeColor', 'k', 'FaceColor', 0.5*[1 1 1]); + + if isMATLAB + uistack(h, 'bottom'); % patches below the line plot + % this is not supported in Octave + end + + % add some minor ticks + set(gca, 'XMinorTick', 'on'); + set(gca, 'YTick', []); + + % Adjust the aspect ratio when in MATLAB(R) or Octave >= 3.4. + if isOctave('<=', [3,4]) + % Octave < 3.4 doesn't have daspect unfortunately. + else + daspect([ 1 2 1 ]) + end +end +% ========================================================================= +function [stat] = sine_with_markers () + % Standard example plot from MATLAB's help pages. + stat.description = [ 'Twisted plot of the sine function. ' ,... + 'Pay particular attention to how markers and Infs/NaNs are treated.' ]; + + x = -pi:pi/10:pi; + y = sin(x); + y(3) = NaN; + y(7) = Inf; + y(11) = -Inf; + plot(x,y,'--o', 'Color', [0.6,0.2,0.0], ... + 'LineWidth', 1*360/127,... + 'MarkerEdgeColor','k',... + 'MarkerFaceColor',[0.3,0.1,0.0],... + 'MarkerSize', 5*360/127 ); + + set( gca, 'Color', [0.9 0.9 1], ... + 'XTickLabel', [], ... + 'YTickLabel', [] ... + ); + + set(gca,'XTick',[0]); + set(gca,'XTickLabel',{'null'}); +end +% ========================================================================= +function [stat] = markerSizes() + stat.description = 'Marker sizes.'; + + hold on; + + h = fill([1 1 2 2],[1 2 2 1],'r'); + set(h,'LineWidth',10); + + plot([0],[0],'go','Markersize',14,'LineWidth',10) + plot([0],[0],'bo','Markersize',14,'LineWidth',1) +end +% ========================================================================= +function [stat] = markerSizes2() + stat.description = 'Line plot with with different marker sizes.'; + + hold on; + grid on; + + n = 1:10; + d = 10; + s = round(linspace(6,25,10)); + e = d * ones(size(n)); + style = {'bx','rd','go','c.','m+','y*','bs','mv','k^','r<','g>','cp','bh'}; + nStyles = numel(style); + + for ii = 1:nStyles + for jj = 1:10 + plot(n(jj), ii * e(jj),style{ii},'MarkerSize',s(jj)); + end + end + xlim([min(n)-1 max(n)+1]); + ylim([0 d*(nStyles+1)]); + set(gca,'XTick',n,'XTickLabel',s,'XTickLabelMode','manual'); +end +% ========================================================================= +function [stat] = sine_with_annotation () + stat.description = [ 'Plot of the sine function. ',... + 'Pay particular attention to how titles and annotations are treated.' ]; + stat.unreliable = isOctave || isMATLAB('>=',[8,4]) ... %FIXME: investigate + || isMATLAB('<=', [8,3]); %FIXME: broken since decd496 (mac vs linux) + + x = -pi:.1:pi; %TODO: the 0.1 step is probably a bad idea (not representable in float) + y = sin(x); + h = plot(x,y); + set(gca,'XTick',-pi:pi/2:pi); + + set(gca,'XTickLabel',{'-pi','-pi/2','0','pi/2','pi'}); + + xlabel('-\pi \leq \Theta \leq \pi'); + ylabel('sin(\Theta)'); + title({'Plot of sin(\Theta)','subtitle','and here''s one really long subtitle' }); + text(-pi/4,sin(-pi/4),'\leftarrow sin(-\pi\div4)',... + 'HorizontalAlignment','left'); + + % Doesn't work in Octave + %set(findobj(gca,'Type','line','Color',[0 0 1]),... + % 'Color','red',... + % 'LineWidth',10); + +end +% ========================================================================= +function [stat] = linesWithOutliers() + stat.description = 'Lines with outliers.'; + stat.issues = [392,400]; + + far = 200; + x = [ -far, -1, -1, -far, -10, -0.5, 0.5, 10, far, 1, 1, far, 10, 0.5, -0.5, -10, -far ]; + y = [ -10, -0.5, 0.5, 10, far, 1, 1, far, 10, 0.5, -0.5, -10, -far, -1, -1, -far, -0.5 ]; + plot( x, y,'o-'); + axis( [-2,2,-2,2] ); +end +% ========================================================================= +function [stat] = peaks_contour() + stat.description = 'Test contour plots.'; + stat.unreliable = isMATLAB('<', [8,4]) || isOctave; %R2014a and older + % FIXME: see #604; contour() produces inconsistent output + + subplot(121) + [C, h] = contour(peaks(20),10); + clabel(C, h); + + % remove y-ticks + set(gca,'YTickLabel',[]); + set(gca,'YTick',[]); + + colormap winter; + + % Contour layers with predefined color + subplot(122) + contour(peaks(20), 10,'r', 'LineWidth', 5) + set(gca,'YTickLabel',[]); + set(gca,'YTick',[]); +end +% ========================================================================= +function [stat] = contourPenny() + stat.description = 'Contour plot of a US\$ Penny.'; + stat.unreliable = isMATLAB('<', [8,4]); + % FIXME: see #604; contour() produces inconsistent output (mac/windows of PeterPablo) + stat.issues = [49 404]; + + if ~exist('penny.mat','file') + fprintf( 'penny data set not found. Skipping.\n\n' ); + stat.skip = true; + return; + end + + load penny; + contour(flipud(P)); + axis square; + +end +% ========================================================================= +function [stat] = peaks_contourf () + stat.description = 'Test the contourfill plots.'; + stat.unreliable = isMATLAB('>=', [8,4]); % FIXME: inspect this + stat.issues = 582; + + [trash, h] = contourf(peaks(20), 10); + hold on + plot(1:20) + colorbar(); + legend(h, 'Contour'); + colormap hsv; +end +% ========================================================================= +function [stat] = double_colorbar() + stat.description = 'Double colorbar.'; + + if isOctave() + fprintf( 'Octave can''t handle tight axes.\n\n' ); + stat.skip = true; + return + end + + vspace = linspace(-40,40,20); + speed_map = magic(20).'; + Q1_map = magic(20); + + subplot(1, 2, 1); + contour(vspace(9:17),vspace(9:17),speed_map(9:17,9:17),20) + colorbar + axis tight + axis square + xlabel('$v_{2d}$') + ylabel('$v_{2q}$') + + subplot(1, 2, 2) + contour(vspace(9:17),vspace(9:17),Q1_map(9:17,9:17),20) + colorbar + axis tight + axis square + xlabel('$v_{2d}$') + ylabel('$v_{2q}$') +end +% ========================================================================= +function [stat] = randomWithLines() + stat.description = 'Lissajous points with lines.'; + + beta = 42.42; + t = 1:150; + X = [sin(t); cos(beta * t)].'; + + X(:,1) = (X(:,1) * 90) + 75; + plot(X(:,1),X(:,2),'o'); + hold on; + M(1)=min(X(:,1)); + M(2)=max(X(:,1)); + mn = mean(X(:,2)); + s = std(X(:,2)); + plot(M,[mean(X(:,2)) mean(X(:,2))],'k-'); + plot(M,mn + 1*[s s],'--'); + plot(M,mn - 2*[s s],'--'); + axis('tight'); +end +% ========================================================================= +function [stat] = many_random_points () + stat.description = 'Test the performance when drawing many points.'; + + n = 1e3; + alpha = 1024; + beta = 1; + gamma = 5.47; + + x = cos( (1:n) * alpha ); + y = sin( (1:n) * beta + gamma); + + plot ( x, y, '.r' ); + axis([ 0, 1, 0, 1 ]) +end +% ========================================================================= +function [stat] = double_axes() + stat.description = 'Double axes'; + + dyb = 0.1; % normalized units, bottom offset + dyt = 0.1; % separation between subsequent axes bottoms + + x = [0; 24; 48; 72; 96;]; + y = [7.653 7.473 7.637 7.652 7.651]; + + grid on + h1 = plot(x,y,'Color','k'); + + % following code is taken from `floatAxisX.m' + + % get position of axes + allAxes = findobj(gcf,'type','axes'); + naxes = length(allAxes); + ax1Pos = get(allAxes(naxes),'position'); + + % rescale and reposition all axes to handle additional axes + for an=1:naxes-1 + if isequal(rem(an,2),0) + % even ones in array of axes handles represent axes on which lines are plotted + set(allAxes(an),'Position',[ax1Pos(1,1) ax1Pos(1,2)+dyb ax1Pos(1,3) ax1Pos(1,4)-dyt]) + else + % odd ones in array of axes handles represent axes on which floating x-axss exist + axPos = get(allAxes(an),'Position'); + set(allAxes(an),'Position',[axPos(1,1) axPos(1,2)+dyb axPos(1,3) axPos(1,4)]) + end + end + % first axis a special case (doesn't fall into even/odd scenario of figure children) + set(allAxes(naxes),'Position',[ax1Pos(1,1) ax1Pos(1,2)+dyb ax1Pos(1,3) ax1Pos(1,4)-dyt]) + ylimit1 = get(allAxes(naxes),'Ylim'); + + % get new position for plotting area of figure + ax1Pos = get(allAxes(naxes),'position'); + + % axis to which the floating axes will be referenced + ref_axis = allAxes(1); + refPosition = get(ref_axis,'position'); + + % overlay new axes on the existing one + ax2 = axes('Position',ax1Pos); + % plot data and return handle for the line + hl1 = plot(x,y,'k'); + % make the new axes invisible, leaving only the line visible + set(ax2,'visible','off','ylim',ylimit1) + + % set the axis limit mode so that it does not change if the + % user resizes the figure window + set(ax2,'xLimMode','manual') + + % set up another set of axes to act as floater + ax3 = axes('Position',[refPosition(1) refPosition(2)-dyb refPosition(3) 0.01]); + + set(ax3,'box','off','ycolor','w','yticklabel',[],'ytick',[]) + set(ax3,'XMinorTick','on','color','none','xcolor',get(hl1,'color')) + + xlabel('secondary axis') +end +% ========================================================================= +function [stat] = double_axes2() + stat.description = 'Double overlayed axes with a flip.' ; + + ah1=axes; + ph=plot([0 1],[0 1]); + + title('Title') + ylabel('y') + xlabel('x') + + % add a new set of axes + % to make a gray grid + ah2=axes; + % make the background transparent + set(ah1,'color','none') + % move these axes to the back + set(gcf,'Children',flipud(get(gcf,'Children'))) +end +% ========================================================================= +function [stat] = logplot() + stat.description = 'Test logscaled axes.'; + % This was once unreliable (and linked to #590). Mac and Linux seem fine. + + x = logspace(-1,2); + y = exp(x); + loglog(x, y, '-s') + + ylim([1 1e45]); + grid on; + if isprop(gca,'GridColor') + set(gca, 'GridColor', 'red'); + set(gca, 'MinorGridColor', 'blue'); + else + %TODO equivalent HG1 settings (if those exist) + end +end +% ========================================================================= +function [stat] = colorbarLogplot() + stat.description = 'Logscaled colorbar.'; + stat.unreliable = isOctave; % FIXME: investigate (Travis differs from Linux/Mac octave) + % https://github.com/matlab2tikz/matlab2tikz/pull/641#issuecomment-120481564 + + imagesc([1 10 100]); + try + set(colorbar(), 'YScale', 'log'); + catch + warning('M2TAcid:LogColorBar',... + 'Logarithmic Colorbars are not documented in MATLAB R2014b and Octave'); + stat.skip = true; + end +end +% ========================================================================= +function [stat] = legendplot() + stat.description = 'Test inserting of legends.'; + stat.unreliable = isMATLAB || isOctave; % FIXME: investigate + +% x = -pi:pi/20:pi; +% plot(x,cos(x),'-ro',x,sin(x),'-.b'); +% h = legend('one pretty long legend cos_x','sin_x',2); +% set(h,'Interpreter','none'); + + x = linspace(0, 2*pi, 1e5); + plot( x, sin(x), 'b', ... + x, cos(x), 'r' ); + xlim( [0 2*pi] ) + ylim( [-0.9 0.9] ) + title( '{tikz test}' ) + xlabel( '{x-Values}' ) + ylabel( '{y-Values}' ) + legend( 'sin(x)', 'cos(x)', 'Location','NorthOutside', ... + 'Orientation', 'Horizontal' ); + grid on; +end +% ========================================================================= +function [stat] = legendplotBoxoff () + stat.description = 'Test inserting of legends.'; + stat.issues = [607,609]; + + x = -pi:pi/20:pi; + l = plot(x, cos(x),'-ro',... + x, sin(x),'-.b'); + h = legend(l(2), 'one pretty long legend sin_x (dash-dot)', 'Location', 'northeast'); + set(h, 'Interpreter', 'none'); + legend boxoff +end +% ========================================================================= +function [stat] = plotyyLegends() + stat.description = 'More legends.'; + + x = 0:.1:7; + y1 = sin(x); + y2 = cos(x); + [ax,h1,h2] = plotyy(x,y1,x,y2); + legend([h1;h2],'Sine','Cosine'); +end +% ========================================================================= +function [stat] = zoom() + stat.description = ['Test function \texttt{pruneOutsideBox()} ', ... + 'and \texttt{movePointsCloser()} ', ... + 'of \texttt{cleanfigure()}.']; + stat.unreliable = isOctave; %FIXME: investigate + stat.issues = [226,392,400]; + + % Setup + subplot(311) + plot(1:10,10:-1:1,'-r*',1:15,repmat(9,1,15),'-g*',[5.5,5.5],[1,9],'-b*') + hold on; + stairs(1:10,'-m*'); + plot([2,8.5,8.5,2,2],[2,2,7.5,7.5,2],'--k'); + title('setup'); + legend('cross with points','no cross','cross no points','stairs','zoom area'); + + % Last comes before simple zoomin due to cleanfigure + subplot(313) + plot(1:10,10:-1:1,'-r*',1:10,repmat(9,1,10),'-g*',[5.5,5.5],[1,9],'-b*'); + hold on; + stairs(1:10,'-m*'); + xlim([2, 8.5]), ylim([2,7.5]); + cleanfigure(); % FIXME: this generates many "division by zero" in Octave + plot([2,8.5,8.5,2,2],[2,2,7.5,7.5,2],'--k'); + xlim([0, 15]), ylim([0,10]); + title('zoom in, cleanfigure, zoom out'); + + % Simple zoom in + subplot(312) + plot(1:10,10:-1:1,'-r*',1:10,repmat(9,1,10),'-g*',[5.5,5.5],[1,9],'-b*'); + hold on; + stairs(1:10,'-m*'); + xlim([2, 8.5]), ylim([2,7.5]); + title('zoom in'); +end +% ========================================================================= +function [stat] = bars() + stat.description = '2x2 Subplot with different bars'; + stat.unreliable = isOctave || isMATLAB('>=', [8,4]) || ... % FIXME: investigate + isMATLAB('<=', [8,3]); %FIXME: #749 (Jenkins) + + % dataset grouped + bins = 10 * (-0.5:0.1:0.5); + numEntries = length(bins); + + alpha = [13 11 7]; + numBars = numel(alpha); + plotData = zeros(numEntries, numBars); + for iBar = 1:numBars + plotData(:,iBar) = abs(round(100*sin(alpha(iBar)*(1:numEntries)))); + end + + % dataset stacked + data = ACID_data; + Y = round(abs(data(2:6,1:3))/10); + + subplot(2,2,1); + b1 = bar(bins,plotData,'grouped','BarWidth',1.5); + set(gca,'XLim',[1.25*min(bins) 1.25*max(bins)]); + + subplot(2,2,2); + barh(bins, plotData, 'grouped', 'BarWidth', 1.3); + + subplot(2,2,3); + bar(Y, 'stacked'); + + subplot(2,2,4); + b2= barh(Y,'stacked','BarWidth', 0.75); + + set(b1(1),'FaceColor','m','EdgeColor','none') + set(b2(1),'FaceColor','c','EdgeColor','none') + +end +% ========================================================================= +function [stat] = stemplot() + stat.description = 'A simple stem plot.' ; + + x = 0:25; + y = [exp(-.07*x).*cos(x); + exp(.05*x).*cos(x)]'; + h = stem(x, y); + legend( 'exp(-.07x)*cos(x)', 'exp(.05*x)*cos(x)', 'Location', 'NorthWest'); + set(h(1),'MarkerFaceColor','blue'); + set(h(2),'MarkerFaceColor','red','Marker','square'); + + % Octave 4 has some smart behavior: it only prints a single baseline. + % Let's mimick this behavior everywhere else. + baselines = findall(gca, 'Type', 'line', 'Color', [0 0 0]); + if numel(baselines) > 1 + % We only need the last line in Octave 3.8, as that is where + % Octave 4.0 places the baseline + delete(baselines(1:end-1)); + end +end +% ========================================================================= +function [stat] = stemplot2() + stat.description = 'Another simple stem plot.'; + stat.unreliable = isOctave('>=', 4); %FIXME: see #759, #757/#759 and #687 + + x = 0:25; + y = [exp(-.07*x).*cos(x); + exp(.05*x).*cos(x)]'; + h = stem(x, y, 'filled'); + legend( 'exp(-.07x)*cos(x)', 'exp(.05*x)*cos(x)', 'Location', 'NorthWest'); +end +% ========================================================================= +function [stat] = stairsplot() + stat.description = 'A simple stairs plot.' ; + + X = linspace(-2*pi,2*pi,40)'; + Yconst = [zeros(10,1); 0.5*ones(20,1);-0.5*ones(10,1)]; + Y = [sin(X), 0.2*cos(X), Yconst]; + h = stairs(Y); + legend(h(2),'second entry') +end +% ========================================================================= +function [stat] = quiverplot() + stat.description = 'A combined quiver/contour plot of $x\exp(-x^2-y^2)$.' ; + stat.extraOptions = {'arrowHeadSize', 2}; + + [X,Y] = meshgrid(-2:.2:2); + Z = X.*exp(-X.^2 - Y.^2); + [DX,DY] = gradient(Z,.2,.2); + contour(X,Y,Z); + hold on + quiver(X,Y,DX,DY); + %TODO: also show a `quiver(X,Y,DX,DY,0);` to test without scaling + colormap hsv; + hold off +end +% ========================================================================= +function [stat] = quiver3plot() + stat.description = 'Three-dimensional quiver plot.' ; + stat.unreliable = isMATLAB(); %FIXME: #590 + + vz = 10; % Velocity + a = -32; % Acceleration + + t = 0:.1:1; + z = vz*t + 1/2*a*t.^2; + + vx = 2; + x = vx*t; + vy = 3; + y = vy*t; + + u = gradient(x); + v = gradient(y); + w = gradient(z); + scale = 0; + quiver3(x,y,z,u,v,w,scale) + view([70 18]) +end +% ========================================================================= +function [stat] = quiveroverlap () + stat.description = 'Quiver plot with avoided overlap.'; + stat.issues = [679]; + % TODO: As indicated in #679, the native quiver scaling algorithm still isn't + % perfect. As such, in MATLAB the arrow heads may appear extremely tiny. + % In Octave, they look fine though. Once the scaling has been done decently, + % this reminder can be removed. + if isOctave + stat.extraOptions = {'arrowHeadSize', 20}; + end + + x = [0 1]; + y = [0 0]; + u = [1 -1]; + v = [1 1]; + + hold all; + qvr1 = quiver(x,y,u,v); + qvr2 = quiver(x,y,2*u,2*v); + set(qvr2, 'MaxHeadSize', get(qvr1, 'MaxHeadSize')/2); +end +% ========================================================================= +function [stat] = polarplot () + stat.description = 'A simple polar plot.' ; + stat.extraOptions = {'showHiddenStrings',true}; + stat.unreliable = isOctave('>=', 4) || ... %FIXME: see #759, #757/#759 and #687 + isMATLAB('<=', [8,3]); %FIXME: broken since decd496 (mac vs linux) + t = 0:.01:2*pi; + polar(t,sin(2*t).*cos(2*t),'--r') +end +% ========================================================================= +function [stat] = roseplot () + stat.description = 'A simple rose plot.' ; + stat.extraOptions = {'showHiddenStrings',true}; + stat.unreliable = isOctave('>=', 4) || ... %FIXME: see #759, #757/#759 and #687 + isMATLAB('<=', [8,3]); %FIXME: broken since decd496 (mac vs linux) + + theta = 2*pi*sin(linspace(0,8,100)); + rose(theta); +end +% ========================================================================= +function [stat] = compassplot () + stat.description = 'A simple compass plot.' ; + stat.extraOptions = {'showHiddenStrings',true}; + stat.unreliable = isOctave('>=', 4) || ... %FIXME: see #759, #757/#759 and #687 + isMATLAB('<=', [8,3]); %FIXME: broken since decd496 (mac vs linux) + + Z = (1:20).*exp(1i*2*pi*cos(1:20)); + compass(Z); +end +% ========================================================================= +function [stat] = logicalImage() + stat.description = 'An image plot of logical matrix values.' ; + stat.unreliable = isOctave; %FIXME: investigate + % different `width`, see issue #552# (comment 76918634); (Travis differs from Linux/Mac octave) + + plotData = magic(10); + imagesc(plotData > mean(plotData(:))); + colormap('hot'); +end +% ========================================================================= +function [stat] = imagescplot() + stat.description = 'An imagesc plot of $\sin(x)\cos(y)$.'; + stat.unreliable = isOctave; %FIXME: investigate (Travis differs from Linux/Mac octave) + + pointsX = 10; + pointsY = 20; + x = 0:1/pointsX:1; + y = 0:1/pointsY:1; + z = sin(x)'*cos(y); + imagesc(x,y,z); +end +% ========================================================================= +function [stat] = imagescplot2() + stat.description = 'A trimmed imagesc plot.'; + stat.unreliable = isOctave; %FIXME: investigate (Travis differs from Linux/Mac octave) + + a=magic(10); + x=-5:1:4; + y=10:19; + imagesc(x,y,a) + + xlim([-3,2]) + ylim([12,15]) + + grid on; +end +% ========================================================================= +function [stat] = xAxisReversed () + stat.description = 'Reversed axes with legend.' ; + + n = 100; + x = (0:1/n:1); + y = exp(x); + plot(x,y); + set(gca,'XDir','reverse'); + set(gca,'YDir','reverse'); + if isOctave('<=', [3,8]) + % TODO: see whether we can unify this syntax for all environments + % at the moment, the generic syntax doesn't seem to work for Octave + % 3.8 (it doesn't even show a legend in gnuplut). + legend( 'data1', 'Location', 'SouthWest' ); + else + legend( 'Location', 'SouthWest' ); + end +end +% ========================================================================= +function [stat] = subplot2x2b () + stat.description = 'Three aligned subplots on a $2\times 2$ subplot grid.' ; + stat.unreliable = isOctave || isMATLAB(); + % FIXME: this test is unreliable because the automatic axis limits + % differ on different test platforms. Reckon this by creating the figure + % using `ACID(97)` and then manually slightly modify the window size. + % We should not set the axis limits explicitly rather find a better way. + % #591 + + x = (1:5); + + subplot(2,2,1); + y = sin(x.^3); + plot(x,y); + + subplot(2,2,2); + y = cos(x.^3); + plot(x,y); + + subplot(2,2,3:4); + y = tan(x); + plot(x,y); +end +% ========================================================================= +function [stat] = manualAlignment() + stat.description = 'Manually aligned figures.'; + + xrange = linspace(-3,4,2*1024); + + axes('Position', [0.1 0.1 0.85 0.15]); + plot(xrange); + ylabel('$n$'); + xlabel('$x$'); + + axes('Position', [0.1 0.25 0.85 0.6]); + plot(xrange); + set(gca,'XTick',[]); +end +% ========================================================================= +function [stat] = subplotCustom () + stat.description = 'Three customized aligned subplots.'; + stat.unreliable = isMATLAB(); % FIXME: #590 + + x = (1:5); + + y = cos(sqrt(x)); + subplot( 'Position', [0.05 0.1 0.3 0.3] ) + plot(x,y); + + y = sin(sqrt(x)); + subplot( 'Position', [0.35 0.5 0.3 0.3] ) + plot(x,y); + + y = tan(sqrt(x)); + subplot( 'Position', [0.65 0.1 0.3 0.3] ) + plot(x,y); +end +% ========================================================================= +function [stat] = errorBars() + stat.description = 'Generic error bar plot.'; + + data = ACID_data; + plotData = 1:10; + + eH = abs(data(1:10,1))/10; + eL = abs(data(1:10,3))/50; + + x = 1:10; + hold all; + errorbar(x, plotData, eL, eH, '.') + h = errorbar(x+0.5, plotData, eL, eH); + set(h, 'LineStyle', 'none'); + % Octave 3.8 doesn't support passing extra options to |errorbar|, but + % it does allow for changing it after the fact +end +% ========================================================================= +function [stat] = errorBars2() + stat.description = 'Another error bar example.'; + data = ACID_data; + y = mean( data, 2 ); + e = std( data, 1, 2 ); + errorbar( y, e, 'xr' ); +end +% ========================================================================= +function [stat] = legendsubplots() + stat.description = [ 'Subplots with legends. ' , ... + 'Increase value of "length" in the code to stress-test your TeX installation.' ]; + stat.unreliable = isOctave; %FIXME: investigate + stat.issues = 609; + + % size of upper subplot + rows = 4; + % number of points. A large number here (eg 1000) will stress-test + % matlab2tikz and your TeX installation. Be prepared for it to run out of + % memory + length = 100; + + % generate some spurious data + t = 0:(4*pi)/length:4*pi; + x = t; + a = t; + y = sin(t) + 0.1*sin(134*t.^2); + b = sin(t) + 0.1*cos(134*t.^2) + 0.05*cos(2*t); + + % plot the top figure + subplot(rows+2,1,1:rows); + + % first line + sigma1 = std(y); + tracey = mean(y,1); + plot123 = plot(x,tracey,'b-'); + + hold on + + % second line + sigma2 = std(b); + traceb = mean(b,1); + plot456 = plot(a,traceb,'r-'); + + spec0 = ['Mean V(t)_A (\sigma \approx ' num2str(sigma1,'%0.4f') ')']; + spec1 = ['Mean V(t)_B (\sigma \approx ' num2str(sigma2,'%0.4f') ')']; + + hold off + %plot123(1:2) + legend([plot123; plot456],spec0,spec1) + legend boxoff + xlabel('Time/s') + ylabel('Voltage/V') + title('Time traces'); + + % now plot a differential trace + subplot(rows+2,1,rows+1:rows+2) + plot7 = plot(a,traceb-tracey,'k'); + + legend(plot7,'\Delta V(t)') + legend boxoff + xlabel('Time/s') + ylabel('\Delta V') + title('Differential time traces'); +end +% ========================================================================= +function [stat] = bodeplots() + stat.description = 'Bode plots with legends.'; + stat.unreliable = isMATLAB(); % FIXME: inconsistent axis limits and + % tick positions; see #641 (issuecomment-106241711) + + if isempty(which('tf')) + fprintf( 'function "tf" not found. Skipping.\n\n' ); + stat.skip = true; + return + end + + Rc=1; + C=1.5e-6; %F + + % Set inductors + L1=4e-3; + L2=0.8e-3; + + % Resistances of inductors + R1=4; + R2=2; + + % Transfer functions + % Building transfer functions + s=tf('s'); + Zc=1/(s*C)+Rc; + Z1=s*L1+R1; + Z2=s*L2+R2; + LCLd=(Z2+Zc)/(Z1+Zc); + LCL=(s^2*C*L2+1)/(s^2*C*L1+1); + + t=logspace(3,5,1000); + bode(LCL,t) + hold on + bode(LCLd,t) + title('Voltage transfer function of a LCL filter') + set(findall(gcf,'type','line'),'linewidth',1.5) + grid on + + legend('Perfect LCL',' Real LCL','Location','SW') + + % Work around a peculiarity in MATLAB: when the figure is invisible, + % the XData/YData of all plots is NaN. It gets set to the proper values when + % the figure is actually displayed. To do so, we temporarily toggle this + % option. This triggers the call-back (and might flicker the figure). + isVisible = get(gcf,'visible'); + set(gcf,'visible','on') + set(gcf,'visible',isVisible); +end +% ========================================================================= +function [stat] = rlocusPlot() + stat.description = 'rlocus plot.'; + stat.unreliable = isMATLAB(); % FIXME: radial grid is not present on all + % environments (see #641) + + if isempty(which('tf')) + fprintf( 'function "tf" not found. Skipping.\n\n' ); + stat.skip = true; + return + end + + if isMATLAB('<', [8,4]) + % in MATLAB R2014a and below, `rlocus` plots with no background color + % are not supported. So, force that color to white to work around + % that bug. Newer versions don't suffer from this. + set(gca, 'Color', 'w'); + end + + rlocus(tf([1 1],[4 3 1])) + + % Work around a peculiarity in MATLAB: when the figure is invisible, + % the XData/YData of all plots is NaN. It gets set to the proper values when + % the figure is actually displayed. To do so, we temporarily toggle this + % option. This triggers the call-back (and might flicker the figure). + isVisible = get(gcf,'visible'); + set(gcf,'visible','on') + set(gcf,'visible',isVisible); +end +% ========================================================================= +function [stat] = mandrillImage() + stat.description = 'Picture of a mandrill.'; + + if ~exist('mandrill.mat','file') + fprintf( 'mandrill data set not found. Skipping.\n\n' ); + stat.skip = true; + return + end + + data = load( 'mandrill' ); + image( data.X ) % show image + colormap( data.map ) % adapt colormap + axis image % pixels should be square + axis off % disable axis +end +% ========================================================================= +function [stat] = besselImage() + stat.description = 'Bessel function.'; + stat.unreliable = isOctave(); % FIXME (Travis differs from Linux/Mac octave) + + nu = -5:0.25:5; + beta = 0:0.05:2.5; + + m = length(beta); + n = length(nu); + trace = zeros(m,n); + for i=1:length(beta); + for j=1:length(nu) + if (floor(nu(j))==nu(j)) + trace(i,j)=abs(besselj(nu(j),beta(i))); + end + end + end + + imagesc(nu,beta,trace); + colorbar() + xlabel('Order') + ylabel('\beta') + set(gca,'YDir','normal') +end +% ========================================================================= +function [stat] = clownImage() + stat.description = 'Picture of a clown.'; + + if ~exist('clown.mat','file') + fprintf( 'clown data set not found. Skipping.\n\n' ); + stat.skip = true; + return + end + + data = load( 'clown' ); + imagesc( data.X ) + colormap( gray ) +end +% ========================================================================= +function [stat] = zplanePlot1() + stat.description = 'Representation of the complex plane with zplane.'; + stat.unreliable = isMATLAB('<', [8,4]); % FIXME: investigate + + % check of the signal processing toolbox is installed + verInfo = ver('signal'); + if isempty(verInfo) || isempty(verInfo.Name) + fprintf( 'Signal toolbox not found. Skip.\n\n' ); + stat.skip = true; + + return + end + + [z,p] = ellip(4,3,30,200/500); + zplane(z,p); + title('4th-Order Elliptic Lowpass Digital Filter'); +end +% ========================================================================= +function [stat] = zplanePlot2() + stat.description = 'Representation of the complex plane with zplane.'; + stat.unreliable = isMATLAB; % FIXME: #604; only difference is `width` + stat.closeall = true; + + % check of the signal processing toolbox is installed + verInfo = ver('signal'); + if isempty(verInfo) || isempty(verInfo.Name) + fprintf( 'Signal toolbox not found. Skip.\n\n' ); + stat.skip = true; + return + end + + [b,a] = ellip(4,3,30,200/500); + Hd = dfilt.df1(b,a); + zplane(Hd) % FIXME: This opens a new figure that doesn't get closed automatically +end +% ========================================================================= +function [stat] = freqResponsePlot() + stat.description = 'Frequency response plot.'; + stat.closeall = true; + stat.issues = [409]; + stat.unreliable = isMATLAB(); % FIXME: investigate + % See also: https://github.com/matlab2tikz/matlab2tikz/pull/759#issuecomment-138477207 + % and https://gist.github.com/PeterPablo/b01cbe8572a9e5989037 (R2014b) + + % check of the signal processing toolbox is installed + verInfo = ver('signal'); + if isempty(verInfo) || isempty(verInfo.Name) + fprintf( 'Signal toolbox not found. Skip.\n\n' ); + stat.skip = true; + return + end + + b = fir1(80,0.5,kaiser(81,8)); + hd = dfilt.dffir(b); + freqz(hd); % FIXME: This opens a new figure that doesn't get closed automatically +end +% ========================================================================= +function [stat] = axesLocation() + stat.description = 'Swapped axis locations.'; + stat.issues = 259; + + plot(cos(1:10)); + set(gca,'XAxisLocation','top'); + set(gca,'YAxisLocation','right'); +end +% ========================================================================= +function [stat] = axesColors() + stat.description = 'Custom axes colors.'; + + plot(sin(1:15)); + set(gca,'XColor','g','YColor','b'); +% set(gca,'XColor','b','YColor','k'); + box off; +end +% ========================================================================= +function [stat] = multipleAxes() + stat.description = 'Multiple axes.'; + + x1 = 0:.1:40; + y1 = 4.*cos(x1)./(x1+2); + x2 = 1:.2:20; + y2 = x2.^2./x2.^3; + + line(x1,y1,'Color','r'); + ax1 = gca; + set(ax1,'XColor','r','YColor','r') + + ax2 = axes('Position',get(ax1,'Position'),... + 'XAxisLocation','top',... + 'YAxisLocation','right',... + 'Color','none',... + 'XColor','k','YColor','k'); + + line(x2,y2,'Color','k','Parent',ax2); + + xlimits = get(ax1,'XLim'); + ylimits = get(ax1,'YLim'); + xinc = (xlimits(2)-xlimits(1))/5; + yinc = (ylimits(2)-ylimits(1))/5; + + % Now set the tick mark locations. + set(ax1,'XTick',xlimits(1):xinc:xlimits(2) ,... + 'YTick',ylimits(1):yinc:ylimits(2) ) +end +% ========================================================================= +function [stat] = scatterPlotRandom() + stat.description = 'Generic scatter plot.'; + + n = 1:100; + + % MATLAB: Use the default area of 36 points squared. The units for the + % marker area is points squared. + % octave: If s is not given, [...] a default value of 8 points is used. + % Try obtain similar behavior and thus apply square root: sqrt(36) vs. 8 + sArea = 1000*(1+cos(n.^1.5)); % scatter size in unit points squared + sRadius = sqrt(sArea*pi); + if isMATLAB() + s = sArea; % unit: points squared + elseif isOctave() + s = sRadius; % unit: points + end + + scatter(n, n, s, n.^8); + colormap autumn; +end +% ========================================================================= +function [stat] = scatterPlot() + stat.description = 'Scatter plot with MATLAB(R) stat.'; + if ~exist('seamount.mat','file') + fprintf( 'seamount data set not found. Skipping.\n\n' ); + stat.skip = true; + return + end + + data = load( 'seamount' ); + scatter( data.x, data.y, 5, data.z, '^' ); +end +% ========================================================================= +function [stat] = scatterPlotMarkers() + stat.description = 'Scatter plot with with different marker sizes and legend.'; + % FIXME: octave: Output is empty?! Potentially fixed by #669 + + n = 1:10; + d = 10; + e = d * ones(size(n)); + + % MATLAB: Use the default area of 36 points squared. The units for the + % marker area is points squared. + % octave: If s is not given, [...] a default value of 8 points is used. + % Try obtain similar behavior and thus apply square root: sqrt(36) vs. 8 + sArea = d^2 * n; % scatter size in unit points squared + sRadius = sqrt(sArea); + if isMATLAB() + s = sArea; % unit: points squared + elseif isOctave() + s = sRadius; % unit: points + end + + grid on; + hold on; + + style = {'bx','rd','go','c.','m+','y*','bs','mv','k^','r<','g>','cp','bh'}; + names = {'bx','rd','go','c.','m plus','y star','bs','mv',... + 'k up triangle','r left triangle','g right triangle','cp','bh'}; + + nStyles = numel(style); + for ii = 1:nStyles + curr = style{ii}; + scatter(n, ii * e, s, curr(1), curr(2)); + end + xlim([min(n)-1 max(n)+1]); + ylim([0 d*(nStyles+1)]); + set(gca,'XTick',n,'XTickLabel',sArea,'XTickLabelMode','manual'); +end +% ========================================================================= +function [stat] = scatter3Plot() + stat.description = 'Scatter3 plot with MATLAB(R) stat.'; + + [x,y,z] = sphere(16); + X = [x(:)*.5 x(:)*.75 x(:)]; + Y = [y(:)*.5 y(:)*.75 y(:)]; + Z = [z(:)*.5 z(:)*.75 z(:)]; + S = repmat([1 .75 .5]*10,numel(x),1); + C = repmat([1 2 3],numel(x),1); + scatter3(X(:),Y(:),Z(:),S(:),C(:),'filled'), view(-60,60) + view(40,35) +end +% ========================================================================= +function [stat] = spherePlot() + stat.description = 'Stretched sphere with unequal axis limits.'; + stat.issues = 560; + + sphere(30); + title('a sphere: x^2+y^2+z^2'); + xlabel('x'); + ylabel('y'); + zlabel('z'); + set(gca,'DataAspectRatio',[1,1,.5],'xlim',[-1 2], 'zlim',[-1 0.8]) +end +% ========================================================================= +function [stat] = surfPlot() + stat.description = 'Surface plot.'; + + [X,Y,Z] = peaks(30); + surf(X,Y,Z) + colormap hsv + axis([-3 3 -3 3 -10 5]) + set(gca,'View',[-37.5,36]); + + hc = colorbar('YTickLabel', ... + {'Freezing','Cold','Cool','Neutral',... + 'Warm','Hot','Burning','Nuclear'}); + set(get(hc,'Xlabel'),'String','Multitude'); + set(get(hc,'Ylabel'),'String','Magnitude'); + set(hc,'YTick',0:0.7:7); + set(hc,'YTickLabel',... + {'-0.8' '-0.6' '-0.4' '-0.2' '0.0' ... + '0.2' '0.4' '0.6' '0.8' '0.10' '0.12'}); + + set(get(hc,'Title'),... + 'String', 'k(u,v)', ... + 'FontSize', 12, ... + 'interpreter', 'tex'); + + xlabel( 'x' ) + ylabel( 'y' ) + zlabel( 'z' ) +end +% ========================================================================= +function [stat] = surfPlot2() + stat.description = 'Another surface plot.'; + stat.unreliable = isMATLAB || isOctave; % FIXME: investigate + + z = [ ones(15, 5) zeros(15,5); + zeros(5, 5) zeros( 5,5)]; + + surf(abs(fftshift(fft2(z))) + 1); + set(gca,'ZScale','log'); + + legend( 'legendary', 'Location', 'NorthEastOutside' ); +end +% ========================================================================= +function [stat] = superkohle() + stat.description = 'Superkohle plot.'; + stat.unreliable = isMATLAB('<=', [8,3]); %FIXME: broken since decd496 (mac vs linux) + + if ~exist('initmesh') + fprintf( 'initmesh() not found. Skipping.\n\n' ); + stat.skip = true; + return; + end + + x1=0; + x2=pi; + y1=0; + y2=pi; + omegashape = [2 2 2 2 % 2 = line segment; 1 = circle segment; 4 = elipse segment + x1 x2 x2 x1 % start point x + x2 x2 x1 x1 % end point x + y1 y1 y2 y2 % start point y + y1 y2 y2 y1 % end point y + 1 1 1 1 + 0 0 0 0]; + [xy,edges,tri] = initmesh(omegashape,'Hgrad',1.05); + mmin = 1; + while size(xy,2) < mmin + [xy,edges,tri] = refinemesh(omegashape,xy,edges,tri); + end + m = size(xy,2); + x = xy(1,:)'; + y = xy(2,:)'; + y0 = cos(x).*cos(y); + + pdesurf(xy,tri,y0(:,1)); + title('y_0'); + xlabel('x1 axis'); + ylabel('x2 axis'); + axis([0 pi 0 pi -1 1]); + grid on; +end +% ========================================================================= +function [stat] = meshPlot() + stat.description = 'Mesh plot.'; + + [X,Y,Z] = peaks(30); + mesh(X,Y,Z) + colormap hsv + axis([-3 3 -3 3 -10 5]) + + xlabel( 'x' ) + ylabel( 'y' ) + zlabel( 'z' ) +end +% ========================================================================= +function [stat] = ylabels() + stat.description = 'Separate y-labels.'; + + x = 0:.01:2*pi; + H = plotyy(x,sin(x),x,3*cos(x)); + + ylabel(H(1),'sin(x)'); + ylabel(H(2),'3cos(x)'); + + xlabel(H(1),'time'); +end +% ========================================================================= +function [stat] = spectro() + stat.description = 'Spectrogram plot'; + stat.unreliable = isMATLAB('<', [8,4]); % FIXME: investigate + + % In the original test case, this is 0:0.001:2, but that takes forever + % for LaTeX to process. + if isempty(which('chirp')) + fprintf( 'chirp() not found. Skipping.\n\n' ); + stat.description = []; + stat.skip = true; + return + end + + T = 0:0.005:2; + X = chirp(T,100,1,200,'q'); + spectrogram(X,128,120,128,1E3); + title('Quadratic Chirp'); +end +% ========================================================================= +function [stat] = mixedBarLine() + stat.description = 'Mixed bar/line plot.'; + stat.unreliable = isOctave; %FIXME: investigate (octave of egon) + % unreliable, see issue #614 (comment 92263263) + + data = ACID_data; + x = data(:); + hist(x,10) + y = ylim; + hold on; + plot([mean(x) mean(x)], y, '-r'); + hold off; +end +% ========================================================================= +function [stat] = decayingharmonic() + stat.description = 'Decaying harmonic oscillation with \TeX{} title.'; + stat.issues = 587; + + % Based on an example from + % http://www.mathworks.com/help/techdoc/creating_plots/f0-4741.html#f0-28104 + A = 0.25; + alpha = 0.007; + beta = 0.17; + t = 0:901; + y = A * exp(-alpha*t) .* sin(beta*t); + plot(t, y) + title('{\itAe}^{-\alpha\itt}sin\beta{\itt}, \alpha<<\beta, \beta>>\alpha, \alpha<\beta, \beta>\alpha, b>a') + xlabel('Time \musec.') + ylabel('Amplitude |X|') +end +% ========================================================================= +function [stat] = texcolor() + stat.description = 'Multi-colored text using \TeX{} commands.'; + + % Taken from an example at + % http://www.mathworks.com/help/techdoc/creating_plots/f0-4741.html#f0-28104 + text(.1, .5, ['\fontsize{16}black {\color{magenta}magenta '... + '\color[rgb]{0 .5 .5}teal \color{red}red} black again']) +end +% ========================================================================= +function [stat] = textext() + stat.description = 'Formatted text and special characters using \TeX{}.'; + + % Taken from an example at + % http://www.mathworks.com/help/techdoc/creating_plots/f0-4741.html#f0-28303 + txstr(1) = { 'Each cell is a quoted string' }; + txstr(2) = { 'You can specify how the string is aligned' }; + txstr(3) = { 'You can use LaTeX symbols like \pi \chi \Xi' }; + txstr(4) = { '\bfOr use bold \rm\itor italic font\rm' }; + txstr(5) = { '\fontname{courier}Or even change fonts' }; + txstr(5) = { 'and use umlauts like äöüßÄÖÜ and accents éèêÅőŰűç' }; + plot( 0:6, sin(0:6) ) + text( 5.75, sin(2.5), txstr, 'HorizontalAlignment', 'right' ) +end +% ========================================================================= +function [stat] = texrandom() + stat.description = 'Random TeX symbols'; + + try + rng(42); %fix seed + %TODO: fully test tex conversion instead of a random subsample! + catch + rand('seed', 42); %#ok (this is deprecated in MATLAB) + end + + num = 20; % number of symbols per line + symbols = {'\it', '\bf', '\rm', '\sl', ... + '\alpha', '\angle', '\ast', '\beta', '\gamma', '\delta', ... + '\epsilon', '\zeta', '\eta', '\theta', '\vartheta', ... + '\iota', '\kappa', '\lambda', '\mu', '\nu', '\xi', '\pi', ... + '\rho', '\sigma', '\varsigma', '\tau', '\equiv', '\Im', ... + '\otimes', '\cap', '{\int}', '\rfloor', '\lfloor', '\perp',... + '\wedge', '\rceil', '\vee', '\langle', '\upsilon', '\phi', ... + '\chi', '\psi', '\omega', '\Gamma', '\Delta', '\Theta', ... + '\Lambda', '\Xi', '\Pi', '\Sigma', '\Upsilon', '\Phi', ... + '\Psi', '\Omega', '\forall', '\exists', '\ni', '{\cong}', ... + '\approx', '\Re', '\oplus', '\cup', '\subseteq', '\lceil', ... + '\cdot', '\neg', '\times', '\surd', '\varpi', '\rangle', ... + '\sim', '\leq', '\infty', '\clubsuit', '\diamondsuit', ... + '\heartsuit', '\spadesuit', '\leftrightarrow', ... + '\leftarrow', '\Leftarrow', '\uparrow', '\rightarrow', ... + '\Rightarrow', '\downarrow', '\circ', '\pm', '\geq', ... + '\propto', '\partial', '\bullet', '\div', '\neq', ... + '\aleph', '\wp', '\oslash', '\supseteq', '\nabla', ... + '{\ldots}', '\prime', '\0', '\mid', '\copyright', ... + '\o', '\in', '\subset', '\supset', ... + '\_', '\^', '\{', '\}', '$', '%', '#', ... + '(', ')', '+', '-', '=', '/', ',', '.', '<', '>', ... + '!', '?', ':', ';', '*', '[', ']', '§', '"', '''', ... + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ... + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', ... + 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', ... + 'w', 'x', 'y', 'z', ... + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', ... + 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', ... + 'W', 'X', 'Y', 'Z' ... + }; + % Note: Instead of '\ldots' the list of symbols contains the entry + % '{\ldots}'. This is because TeX gives an error if it + % encounters the sequence '$a_\ldots$' or '$a^\ldots$'. It + % looks like that is a TeX bug. Nevertheless this sequence + % could appear in the random output, therefore \ldots is + % wrapped in braces since '$a_{\ldots}$' and '$a^{\ldots}$' + % don't crash TeX. + % Same thing with '\cong' and '\int'. + % \color{red} etc. isn't included + % \fontname{Times} etc. isn't included + % \fontsize{12} etc. isn't included + + switch getEnvironment + case 'MATLAB' + % MATLAB expects tilde and ampersand to be un-escaped and backslashes + % to be escaped + symbols = [ symbols, {'~', '&', '\\'} ]; + case 'Octave' + % Octave expects tilde and ampersand to be escaped for regular + % output. If either are used un-escaped, that creates odd output in + % Octave itself, but since matlab2tikz should be able to handle + % those cases, let's include the un-escaped symbols in the list. + symbols = [ symbols, {'\~', '\&', '~', '&'} ]; + % Octave's backslash handling is weird to say the least. However, + % matlab2tikz treats backslashes the same in Octave as it does in + % MATLAB. Therefore, let's add an escaped backslash to the list + symbols = [ symbols, {'\\'} ]; + otherwise + error( 'Unknown environment. Need MATLAB(R) or Octave.' ) + end + + for ypos = [0.9:-.2:.1] + % Generate `num' random indices to the list of symbols + index = max(ceil(rand(1, num)*length(symbols)), 1); + % Assemble symbols into one cell string array + string = symbols(index); + + % Add random amount of balanced braces in random positions to `string'. + % By potentially generating more than one set of braces randomly, it's + % possible to create more complex patterns of nested braces. Increase + % `braceprob' to get more braces, but don't use values greater than or + % equal 1 which would result in an infinite loop. + braceprob = 0.6; + while rand(1,1) < braceprob + % Generate two random numbers ranging from 1 to n with n = number + % of symbols in `string' + bracepos = max(ceil(rand(1, 2)*length(string)), 1); + % Modify `string' so that an opening brace is inserted before + % min(bracepos) symbols and a closing brace after max(bracepos) + % symbols. That way any number of symbols from one to all in + % `string' are wrapped in braces for min(bracepos) == max(bracepos) + % and min(bracepos) == 1 && max(bracepos) == length(string), + % respectively. + string = [string(1:min(bracepos)-1), {'{'}, ... + string(min(bracepos):max(bracepos)), ... + {'}'}, string(max(bracepos)+1:end) ]; + end + % Clean up: remove '{}', '{{}}', etc. + clean = false; + while clean == false + clean = true; + for i = 1:length(string)-1 + if strcmp( string(i), '{' ) && strcmp( string(i+1), '}' ) + string = [string(1:i-1), string(i+2:end)]; + clean = false; + break + end + end + end + + % Subscripts '_' and superscripts '^' in TeX are tricky in that certain + % combinations are not allowed and there are some subtleties in regard + % to more complicated combinations of sub/superscripts: + % - ^a or _a at the beginning of a TeX math expression is permitted. + % - a^ or a_ at the end of a TeX math expression is not. + % - a__b, a_^b, a^_b, or a^^b is not allowed, as is any number of + % consecutive sub/superscript operators. Actually a^^b does not + % crash TeX, but it produces seemingly random output instead of `b', + % therefore it should be avoided, too. + % - a^b^c or a_b_c is not allowed as it results in a "double subscript/ + % superscript" error. + % - a^b_c or a_b^c, however, does work. + % - a^bc^d or a_bc_d also works. + % - a^b_c^d or a_b^c_d is not allowed and results in a "double + % subscript/superscript" error. + % - a{_}b, a{^}b, {a_}b or {a^}b is not permitted. + % - a{_b} or a{^b} is valid TeX code. + % - {a_b}_c produces the same output as a_{bc}. Likewise for '^'. + % - a_{b_c} results in "a index b sub-index c". Likewise for '^'. + % - a^{b}^c or a_{b}_c is not allowed as it results in a "double + % subscript/superscript" error. + % + % From this we can derive a number of rules: + % 1) The last symbol in a TeX string must not be '^' or '_'. + % 2a) There must be at least one non-brace symbol between any '^' and '_'. + % 2b) There must be at least one non-brace symbol between any '_' and '^'. + % 3a) There must either be at least two non-brace, non-'_' symbols or at + % least one non-brace, non-'_' symbol and one brace (opening or + % closing) between any two '^'. + % 3b) There must either be at least two non-brace, non-'^' symbols or at + % least one brace (opening or closing) between any two '_'. + % 4) '^' or '_' must not appear directly before '}'. + % 5) '^' or '_' must not appear directly after '}'. + % 6) Whenever braces were mentioned, that refers to non-empty braces, + % i.e. '{}' counts as nothing. Printable/escaped braces '\{' and '\}' + % also don't count as braces but as regular symbols. + % 7) '^' or '_' must not appear directly before '\it', '\bf', '\rm', or + % '\sl'. + % 8) '^' or '_' must not appear directly after '\it', '\bf', '\rm', or + % '\sl'. + % + % A few test cases: + % Permitted: ^a... _a... a^b_c a_b^c a^bc^d a_bc_d a{_b} a{^b} + % {a_b}_c a_{bc} {a^b}^c a^{bc} a_{b_c} a^{b^c} + % Forbidden: ...z^ ...z_ a__b a_^b a^_b [a^^b] a^b^c a_b_c + % a^b_c^d a_b^c_d a{_}b a{^}b {a_}b {a^}b + % a^{_b} a_{^b} a^{b}^c a_{b}_c + % + % Now add sub/superscripts according to these rules + subsupprob = 0.1; % Probability for insertion of a sub/superscript + caretdist = Inf; % Distance to the last caret + underscdist = Inf; % Distance to the last underscore + bracedist = Inf; % Distance to the last brace (opening or closing) + pos = 0; + % Making sure the post-update `pos' in the while loop is less than the + % number of symbols in `string' enforces rule 1: The last symbol in + % a TeX string must not be '^' or '_'. + while pos+1 < length(string) + % Move one symbol further + pos = pos + 1; + % Enforce rule 7: No sub/superscript directly before '\it', '\bf', + % '\rm', or '\sl'. + if strcmp( string(pos), '\it' ) || strcmp( string(pos), '\bf' ) ... + || strcmp( string(pos), '\rm' ) || strcmp( string(pos), '\sl' ) + continue + end + % Enforce rule 8: No sub/superscript directly after '\it', '\bf', + % '\rm', or '\sl'. + if (pos > 1) ... + && ( strcmp( string(pos-1), '\it' ) ... + || strcmp( string(pos-1), '\bf' ) ... + || strcmp( string(pos-1), '\rm' ) ... + || strcmp( string(pos-1), '\sl' ) ... + ) + continue + end + bracedist = bracedist + 1; + % Enforce rule 4: No sub/superscript directly before '}' + if strcmp( string(pos), '}' ) + bracedist = 0; % Also update braces distance + continue + end + % Enforce rule 5: No sub/superscript directly after '}' + if (pos > 1) && strcmp( string(pos-1), '}' ) + continue + end + % Update distances for either braces or caret/underscore depending + % on whether the symbol currently under scrutiny is a brace or not. + if strcmp( string(pos), '{' ) + bracedist = 0; + else + caretdist = caretdist + 1; + underscdist = underscdist + 1; + end + % Generate two random numbers, then check if any of them is low + % enough, so that with probability `subsupprob' a sub/superscript + % operator is inserted into `string' at the current position. In + % case both random numbers are below the threshold, whether a + % subscript or superscript operator is to be inserted depends on + % which of the two numbers is smaller. + randomnums = rand(1, 2); + if min(randomnums) < subsupprob + if randomnums(1) < randomnums(2) + % Enforce rule 2b: There must be at least one non-brace + % symbol between previous '_' and to-be-inserted '^'. + if underscdist < 1 + continue + end + % Enforce rule 3a: There must either be at least two + % non-brace, non-'_' symbols or at least one brace (opening + % or closing) between any two '^'. + if ~( ((caretdist >= 2) && (underscdist >= 2)) ... + || ((bracedist < 2) && (caretdist >= 2)) ) + continue + end + % Insert '^' before `pos'th symbol in `string' now that + % we've made sure all rules are honored. + string = [ string(1:pos-1), {'^'}, string(pos:end) ]; + caretdist = 0; + pos = pos + 1; + else + % Enforce rule 2a: There must be at least one non-brace + % symbol between previous '^' and to-be-inserted '_'. + if caretdist < 1 + continue + end + % Enforce rule 3b: There must either be at least two + % non-brace, non-'^' symbols or at least one brace (opening + % or closing) between any two '_'. + if ~( ((caretdist >= 2) && (underscdist >= 2)) ... + || ((bracedist < 2) && (underscdist >= 2)) ) + continue + end + % Insert '_' before `pos'th symbol in `string' now that + % we've made sure all rules are honored. + string = [ string(1:pos-1), {'_'}, string(pos:end) ]; + underscdist = 0; + pos = pos + 1; + end + end + end % while pos+1 < length(string) + + % Now convert the cell string array of symbols into one regular string + string = [string{:}]; + % Print the string in the figure to be converted by matlab2tikz + text( .05, ypos, string, 'interpreter', 'tex' ) + % And print it to the console, too, in order to enable analysis of + % failed tests + fprintf( 'Original string: %s\n', string ) + end + + title('Random TeX symbols \\\{\}\_\^$%#&') +end +% ========================================================================= +function [stat] = latexInterpreter() + stat.description = '\LaTeX{} interpreter test (display math not working)'; + stat.issues = 448; + stat.unreliable = isMATLAB('<=', [8,3]); %FIXME: broken since decd496 (mac vs linux) + + plot(magic(3),'-x'); + + % Adapted from an example at + % http://www.mathworks.com/help/techdoc/ref/text_props.html#Interpreter + text(1.5, 2.0, ... + '$$\int_0^x\!\int_{\Omega} \mathrm{d}F(u,v) \mathrm{d}\omega$$', ... + 'Interpreter', 'latex', ... + 'FontSize', 26); + + title(['display math old: $$\alpha$$ and $$\sum_\alpha^\Omega$$; ', ... + 'inline math: $\alpha$ and $\sum_\alpha^\Omega$'],'Interpreter','latex'); +end +% ========================================================================= +function [stat] = latexmath2() + stat.description = 'Some nice-looking formulas typeset using the \LaTeX{} interpreter.'; + stat.issues = 637; + + % Adapted from an example at + % http://www.mathworks.com/help/techdoc/creating_plots/f0-4741.html#bq558_t + set(gcf, 'color', 'white') + set(gcf, 'units', 'inches') + set(gcf, 'position', [2 2 4 6.5]) + set(gca, 'visible', 'off') + + % Note: The matrices in h(1) and h(2) cannot be compiled inside pgfplots. + % They are therefore disabled. +% h(1) = text( 'units', 'inch', 'position', [.2 5], ... +% 'fontsize', 14, 'interpreter', 'latex', 'string', ... +% [ '$$\hbox {magic(3) is } \left( {\matrix{ 8 & 1 & 6 \cr' ... +% '3 & 5 & 7 \cr 4 & 9 & 2 } } \right)$$' ]); +% h(2) = text( 'units', 'inch', 'position', [.2 4], ... +% 'fontsize', 14, 'interpreter', 'latex', 'string', ... +% [ '$$\left[ {\matrix{\cos(\phi) & -\sin(\phi) \cr' ... +% '\sin(\phi) & \cos(\phi) \cr}} \right]' ... +% '\left[ \matrix{x \cr y} \right]$$' ]); + h(3) = text( 'units', 'inches', 'position', [.2 3], ... + 'fontsize', 14, 'interpreter', 'latex', 'string', ... + [ '$$L\{f(t)\} \equiv F(s) = \int_0^\infty\!\!{e^{-st}' ... + 'f(t)dt}$$' ]); + h(4) = text( 'units', 'inches', 'position', [.2 2], ... + 'fontsize', 14, 'interpreter', 'latex', 'string', ... + '$$e = \sum_{k=0}^\infty {\frac{1}{k!}} $$' ); + h(5) = text( 'units', 'inches', 'position', [.2 1], ... + 'fontsize', 14, 'interpreter', 'latex', 'string', ... + [ '$$m \ddot y = -m g + C_D \cdot {\frac{1}{2}}' ... + '\rho {\dot y}^2 \cdot A$$' ]); + h(6) = text( 'units', 'inches', 'position', [.2 0], ... + 'fontsize', 14, 'interpreter', 'latex', 'string', ... + '$$\int_{0}^{\infty} x^2 e^{-x^2} dx = \frac{\sqrt{\pi}}{4}$$' ); +end +% ========================================================================= +function [stat] = parameterCurve3d() + stat.description = 'Parameter curve in 3D with text boxes in-/outside axis.'; + stat.issues = [378, 790] ; + t = linspace(0, 20*pi, 1e5); + plot3(t, sin(t), 50 * cos(t)); + text(0.5, 0.5, 10, 'text inside axis limits'); + text(5.0, 1.5, 50, 'text outside axis (will be removed by cleanfigure())'); +end +% ========================================================================= +function [stat] = parameterSurf() + stat.description = 'Parameter and surface plot.'; + stat.unreliable = isMATLAB('<', [8,4]); % FIXME: investigate + + if ~exist('TriScatteredInterp') + fprintf( 'TriScatteredInterp() not found. Skipping.\n\n' ); + stat.skip = true; + return; + end + + t = (1:100).'; + t1 = cos(5.75352*t).^2; + t2 = abs(sin(t)); + + x = t1*4 - 2; + y = t2*4 - 2; + z = x.*exp(-x.^2 - y.^2); + + %TODO: do we really need this TriScatteredInterp? + % It will be removed from MATLAB + + % Construct the interpolant + F = TriScatteredInterp(x,y,z,'linear'); + + % Evaluate the interpolant at the locations (qx, qy), qz + % is the corresponding value at these locations. + ti = -2:.25:2; + [qx,qy] = meshgrid(ti,ti); + qz = F(qx,qy); + + hold on + surf(qx,qy,qz) + plot3(x,y,z,'o') + view(gca,[-69 14]); + hold off +end +% ========================================================================= +function [stat] = fill3plot() + stat.description = 'fill3 plot.'; + + if ~exist('fill3','builtin') + fprintf( 'fill3() not found. Skipping.\n\n' ); + stat.skip = true; + return + end + + x1 = -10:0.1:10; + x2 = -10:0.1:10; + p = sin(x1); + d = zeros(1,numel(p)); + d(2:2:end) = 1; + h = p.*d; + grid on; + fill3(x1,x2,h,'k'); + view(45,22.5); + box on; +end +% ========================================================================= +function [stat] = rectanglePlot() + stat.unreliable = isMATLAB('<=', [8,3]); %FIXME: #749 (Jenkins) + stat.description = 'Rectangle handle.'; + + rectangle('Position', [0.59,0.35,3.75,1.37],... + 'Curvature', [0.8,0.4],... + 'LineWidth', 2, ... + 'LineStyle', '--' ... + ); + daspect([1,1,1]); +end +% ========================================================================= +function [stat] = herrorbarPlot() + stat.description = 'herrorbar plot.'; + % FIXME: octave is missing the legend + + hold on; + X = 1:10; + Y = 1:10; + err = repmat(0.2, 1, 10); + h1 = errorbar(X, Y, err+X/30, 'r'); + h_vec = herrorbar(X, Y, err); + for h=h_vec + set(h, 'color', [1 0 0]); + end + h2 = errorbar(X, Y+1, err, 'g'); + h_vec = herrorbar(X, Y+1, err+Y/40); + for h=h_vec + set(h, 'color', [0 1 0]); + end + legend([h1 h2], {'test1', 'test2'}) +end +% ========================================================================= +function [stat] = hist3d() + stat.description = '3D histogram plot.'; + + if ~exist('hist3','builtin') && isempty(which('hist3')) + fprintf( 'Statistics toolbox not found. Skipping.\n\n' ); + stat.skip = true; + return + end + +% load carbig +% X = [MPG,Weight]; +% hist3(X,[7 7]); +% xlabel('MPG'); ylabel('Weight'); +% set(get(gca,'child'),'FaceColor','interp','CDataMode','auto'); + + load carbig + X = [MPG,Weight]; + hist3(X,[7 7]); + xlabel('MPG'); ylabel('Weight'); + hist3(X,[7 7],'FaceAlpha',.65); + xlabel('MPG'); ylabel('Weight'); + % Linux crashed with OpenGL. + %%set(gcf,'renderer','opengl'); + +% load seamount +% dat = [-y,x]; % Grid corrected for negative y-values +% n = hist3(dat); % Extract histogram data; +% % default to 10x10 bins +% view([-37.5, 30]); +end +% ========================================================================= +function [stat] = myBoxplot() + stat.description = 'Boxplot.'; + stat.unreliable = isMATLAB('<', [8,4]); % R2014a; #552 #414 + + if ~exist('boxplot','builtin') && isempty(which('boxplot')) + fprintf( 'Statistics toolbox not found. Skipping.\n\n' ); + stat.skip = true; + return + end + + errors =[ + 0.810000 3.200000 0.059500 + 0.762500 -3.200000 0.455500 + 0.762500 4.000000 0.901000 + 0.762500 3.600000 0.406000 + 0.192500 3.600000 0.307000 + 0.810000 -3.600000 0.604000 + 1.000000 -2.400000 0.505000 + 0.430000 -2.400000 0.455500 + 1.000000 3.200000 0.158500 + ]; + + boxplot(errors); +end +% ========================================================================= +function [stat] = areaPlot() + stat.description = 'Area plot.'; + + M = magic(5); + M = M(1:3,2:4); + h = area(1:3, M); + legend(h([1,3]),'foo', 'foobar'); +end +% ========================================================================= +function [stat] = customLegend() + stat.description = 'Custom legend.'; + stat.unreliable = isMATLAB('<', [8,4]) || isOctave; %FIXME: investigate (Travis differs from Linux/Mac octave) + + x = -pi:pi/10:pi; + y = tan(sin(x)) - sin(tan(x)); + plot(x,y,'--rs'); + + lh=legend('y'); + set(lh,'Location','West') + set(lh,'color','g') + set(lh,'edgecolor','r') + set(lh, 'position',[.5 .6 .1 .05]) +end +% ========================================================================= +function [stat] = pixelLegend() + stat.description = 'Legend with pixel position.'; + + x = linspace(0,1); + plot(x, [x;x.^2]); + set(gca, 'units', 'pixels') + lh=legend('1', '2'); + set(lh, 'units','pixels','position', [100 200 65 42]) +end +% ========================================================================= +function [stat] = croppedImage() + stat.description = 'Custom legend.'; + + if ~exist('flujet.mat','file') + fprintf( 'flujet data set not found. Skipping.\n\n' ); + stat.skip = true; + return; + end + + load('flujet','X','map'); + image(X) + colormap(map) + %axis off + axis image + xlim([50 200]) + ylim([50 200]) + % colorbar at top + colorbar('north'); + set(gca,'Units','normalized'); +end +% ========================================================================= +function [stat] = pColorPlot() + stat.description = 'pcolor() plot.'; + + ylim([-1 1]); xlim([-1 1]); hold on; % prevent error on octave + n = 6; + r = (0:n)'/n; + theta = pi*(-n:n)/n; + X = r*cos(theta); + Y = r*sin(theta); + C = r*cos(2*theta); + pcolor(X,Y,C) + axis equal tight +end +% ========================================================================= +function [stat] = multiplePatches() + stat.description = 'Multiple patches.'; + + xdata = [2 2 0 2 5; + 2 8 2 4 5; + 8 8 2 4 8]; + ydata = [4 4 4 2 0; + 8 4 6 2 2; + 4 0 4 0 0]; + cdata = [15 0 4 6 10; + 1 2 5 7 9; + 2 3 0 8 3]; + p = patch(xdata,ydata,cdata,'Marker','o',... + 'MarkerFaceColor','flat',... + 'FaceColor','none'); +end +% ========================================================================= +function [stat] = hgTransformPlot() + stat.description = 'hgtransform() plot.'; + + if isOctave + % Octave (3.8.0) has no implementation of `hgtransform` + stat.skip = true; + return; + end + % Check out + % http://www.mathworks.de/de/help/matlab/ref/hgtransform.html. + + ax = axes('XLim',[-2 1],'YLim',[-2 1],'ZLim',[-1 1]); + view(3); + grid on; + axis equal; + + [x,y,z] = cylinder([.2 0]); + h(1) = surface(x,y,z,'FaceColor','red'); + h(2) = surface(x,y,-z,'FaceColor','green'); + h(3) = surface(z,x,y,'FaceColor','blue'); + h(4) = surface(-z,x,y,'FaceColor','cyan'); + h(5) = surface(y,z,x,'FaceColor','magenta'); + h(6) = surface(y,-z,x,'FaceColor','yellow'); + + t1 = hgtransform('Parent',ax); + t2 = hgtransform('Parent',ax); + + set(h,'Parent',t1); + h2 = copyobj(h,t2); + + Txy = makehgtform('translate',[-1.5 -1.5 0]); + set(t2,'Matrix',Txy) + drawnow +end +% ========================================================================= +function [stat] = logbaseline() + stat.description = 'Logplot with modified baseline.'; + + bar([0 1 2], [1 1e-2 1e-5],'basevalue', 1e-6); + set(gca,'YScale','log'); +end +% ========================================================================= +function [stat] = alphaImage() + stat.description = 'Images with alpha channel.'; + stat.unreliable = isOctave; %FIXME: investigate + + subplot(2,1,1); + title('Scaled Alpha Data'); + N = 20; + h_imsc = imagesc(repmat(1:N, N, 1)); + mask = zeros(N); + mask(N/4:3*N/4, N/4:3*N/4) = 1; + set(h_imsc, 'AlphaData', double(~mask)); + set(h_imsc, 'AlphaDataMapping', 'scaled'); + set(gca, 'ALim', [-1,1]); + title(''); + + subplot(2,1,2); + title('Integer Alpha Data'); + N = 2; + line([0 N]+0.5, [0 N]+0.5, 'LineWidth', 2, 'Color','k'); + line([0 N]+0.5, [N 0]+0.5, 'LineWidth', 2, 'Color','k'); + hold on + imagesc([0,1;2,3],'AlphaData',uint8([64,128;192,256])) +end +% ========================================================================= +function stat = annotationAll() + stat.description = 'All possible annotations with edited properties'; + stat.unreliable = isMATLAB('<', [8,4]); % TODO: R2014a and older: #604 + + if isempty(which('annotation')) + fprintf( 'annotation() not found. Skipping.\n\n' ); + stat.skip = true; + return; + end + + % Create plot + X1 = -5:0.1:5; + plot(X1,log(X1.^2+1)); + + % Create line + annotation('line',[0.21 0.26], [0.63 0.76], 'Color',[0.47 0.3 0.44],... + 'LineWidth',4, 'LineStyle',':'); + + % Create arrow + if isOctave('>=', 4) + headStyle = 'vback3'; %Octave does not support cback2 yet (2015-09) + else + headStyle = 'cback2'; + end + + annotation('arrow',[0.25 0.22], [0.96 0.05], 'LineStyle','-.',... + 'HeadStyle', headStyle); + + % Create textarrow + annotation('textarrow',[0.46 0.35], [0.41 0.50],... + 'Color',[0.92 0.69 0.12], 'TextBackgroundColor',[0.92 0.83 0.83],... + 'String',{'something'}, 'LineWidth',2, 'FontWeight','bold',... + 'FontSize',20, 'FontName','Helvetica'); + + % Create doublearrow + annotation('doublearrow',[0.33 0.7], [0.56 0.55]); + + % Create textbox + annotation('textbox', [0.41 0.69 0.17 0.10], 'String',{'something'},... + 'FitBoxToText','off'); + + % Create ellipse + if isOctave(4) + colorSpec = 'EdgeColor'; + else + colorSpec = 'Color'; + end + annotation('ellipse', [0.70 0.44 0.15 0.51], ... + colorSpec, [0.63 0.07 0.18],... + 'LineWidth', 3, 'FaceColor',[0.80 0.87 0.96]); + + % Create rectangle + annotation('rectangle', [0.3 0.26 0.53 0.58], 'LineWidth',8,... + 'LineStyle',':'); +end +% ========================================================================= +function [stat] = annotationSubplots() + stat.description = 'Annotated and unaligned subplots'; + + if isempty(which('annotation')) + fprintf( 'annotation() not found. Skipping.\n\n' ); + stat.skip = true; + return; + end + + X1 = 0:0.01:1; + Y1 = X1.^2; + Y2 = Y1.^2; + Y3 = X1.^(1/4); + + set(gcf, 'Position', [100 100 1500 600]); + + axes1 = axes('Parent',gcf, 'Position',[0.07 0.4015 0.2488 0.5146]); + box(axes1,'on'); + hold(axes1,'all'); + + title('f(x)=x^2'); + + plot(X1,Y1,'Parent',axes1, 'DisplayName','(0:0.05:1).^2 vs 0:0.05:1'); + + axes2 = axes('Parent',gcf, 'OuterPosition',[0.4062 0 0.2765 0.6314]); + box(axes2,'on'); + hold(axes2,'all'); + + plot(X1,Y2,'Parent',axes2,'DisplayName','(0:0.05:1).^4 vs 0:0.05:1'); + + axes3 = axes('Parent',gcf, 'Position',[0.7421 0.3185 0.21 0.5480]); + box(axes3,'on'); + hold(axes3,'all'); + + plot(X1,Y3,'Parent',axes3,'DisplayName','(0:0.05:1).^(1/4) vs 0:0.05:1'); + + annotation(gcf,'textbox',[0.3667 0.5521 0.0124 0.0393], ... + 'String',{'f^2'}, 'FitBoxToText','off'); + + annotation(gcf,'arrow',[0.3263 0.4281], [0.6606 0.3519]); + + annotation(gcf,'textarrow',[0.6766 0.7229], [0.3108 0.6333],... + 'TextEdgeColor','none', 'HorizontalAlignment','center', ... + 'String',{'invert'}); +end +% ========================================================================= +function [stat] = annotationText() + stat.description = 'Variations of textual annotations'; + stat.unreliable = isMATLAB('<', [8,4]); % FIXME: investigate + + if ~exist('annotation') + fprintf( 'annotation() not found. Skipping.\n\n' ); + stat.skip = true; + return; + end + + X1 = -5:0.1:5; + Y1 = log(X1.^2+1); + + % Resize figure to fit all text inside + set(gcf,'Position', [100 100 1000 700]); + + % Otherwise the axes is plotted wrongly + drawnow(); + + % Create axes + axes1 = axes('Parent',gcf); + hold(axes1,'all'); + + % Create plot + plot(X1,Y1); + + % Create text + text('Parent',axes1,'String',' \leftarrow some point on the curve',... + 'Position',[-2.01811125485123 1.5988219895288 7.105427357601e-15]); + + % Create text + text('Parent',axes1,'String','another point \rightarrow',... + 'Position',[1 0.693147180559945 0],... + 'HorizontalAlignment','right'); + + % Create textbox + annotation(gcf,'textbox',... + [0.305611222444885 0.292803442287824 0.122244488977956 0.0942562592047128],... + 'String',{'This boxes size','should adjust to','the text size'}); + + % Create textbox + annotation(gcf,'textbox',... + [0.71643086172344 0.195876288659794 0.10020240480962 0.209240982129118],... + 'String',{'Multiple Lines due to fixed width'},... + 'FitBoxToText','off'); + + % Create textbox + annotation(gcf,'textbox',... + [0.729456913827655 0.608247422680412 0.0851723446893787 0.104257797902974],... + 'String',{'Overlapping','and italic'},... + 'FontAngle','italic',... + 'FitBoxToText','off',... + 'BackgroundColor',[0.756862759590149 0.866666674613953 0.776470601558685]); + + % Create textbox + annotation(gcf,'textbox',... + [0.420000437011093 0.680170575692964 0.155149863590109 0.192171438527209],... + 'VerticalAlignment','middle',... + 'String',{'Text with a','thick and','dotted','border'},... + 'HorizontalAlignment','center',... + 'FitBoxToText','off',... + 'LineStyle',':',... + 'LineWidth',4); + + % Create textarrow + annotation(gcf,'textarrow',[0.21943887775551 0.2625250501002],... + [0.371002132196162 0.235640648011782],'TextEdgeColor','none',... + 'TextBackgroundColor',[0.678431391716003 0.921568632125854 1],... + 'TextRotation',30,... + 'VerticalAlignment','bottom',... + 'HorizontalAlignment','center',... + 'String',{'Rotated Text'}); + + % Create textarrow + annotation(gcf,'textarrow',[0.238436873747493 0.309619238476953],... + [0.604315828808828 0.524300441826215],'TextEdgeColor','none',... + 'TextColor',[1 1 1],... + 'TextBackgroundColor',[0 0 1],... + 'TextRotation',30,... + 'VerticalAlignment','bottom',... + 'HorizontalAlignment','center',... + 'String',{'Rotated Text 2'},... + 'HeadStyle','diamond',... + 'Color',[1 0 0]); +end +% ========================================================================= +function [stat] = annotationTextUnits() + stat.description = 'Text with changed Units'; + stat.unreliable = isMATLAB('<', [8,4]); % FIXME: investigate + + if ~exist('annotation') + fprintf( 'annotation() not found. Skipping.\n\n' ); + stat.skip = true; + return; + end + + X1 = -5:0.1:5; + Y1 = log(X1.^2+1); + + % Resize figure to fit all text inside + set(gcf,'Units', 'inches'); + set(gcf,'Position', [1.03125, 1.03125, 10.416666666666666, 7.291666666666666 ]); + + % Otherwise the axes is plotted wrongly + drawnow(); + + % Create axes + axes1 = axes('Parent',gcf,'Units','centimeters',... + 'Position',[3.4369697916666664, 2.035743645833333 20.489627604166664 15.083009739583332]); + hold(axes1,'all'); + + % Create plot + plot(X1,Y1); + + % Create text + text('Parent',axes1,'Units','normalized',... + 'String',' \leftarrow some point on the curve',... + 'Position',[0.295865633074935 0.457364341085271 0]); + + % Create text + text('Parent',axes1,'Units','centimeters',... + 'String','another point \rightarrow',... + 'Position',[12.2673383333333 2.98751989583333 0],... + 'HorizontalAlignment','right'); + + % Create textbox + annotation(gcf,'textbox',... + [0.305611222444885 0.292803442287824 0.122244488977956 0.0942562592047128],... + 'String',{'This boxes size','should adjust to','the text size'},... + 'FitBoxToText','off',... + 'Units','pixels'); + + + % Create textarrow + annotation(gcf,'textarrow',[0.21943887775551 0.2625250501002],... + [0.371002132196162 0.235640648011782],'TextEdgeColor','none',... + 'TextBackgroundColor',[0.678431391716003 0.921568632125854 1],... + 'TextRotation',30,... + 'HorizontalAlignment','center',... + 'String',{'Rotated Text'},... + 'Units','points'); + + % Create textarrow + annotation(gcf,'textarrow',[0.238436873747493 0.309619238476953],... + [0.604315828808828 0.524300441826215],'TextEdgeColor','none',... + 'TextColor',[1 1 1],... + 'TextBackgroundColor',[0 0 1],... + 'TextRotation',30,... + 'HorizontalAlignment','center',... + 'String',{'Rotated Text 2'},... + 'HeadStyle','diamond',... + 'Color',[1 0 0]); + + % Create textbox + if ~isOctave(4) + annotation(gcf,'textbox',... + [0.71643086172344 0.195876288659794 0.10020240480962 0.209240982129118],... + 'String',{'Multiple Lines due to fixed width'},... + 'FitBoxToText','off',... + 'Units','characters'); + else + % Octave 4 doesn't seem to like the "'Units','Characters'" in there + % so just remove the object altogether. + % This is strange, since it is documented: https://www.gnu.org/software/octave/doc/interpreter/Plot-Annotations.html#Plot-Annotations + end + + % Create textbox + annotation(gcf,'textbox',... + [0.420000437011093 0.680170575692964 0.155149863590109 0.192171438527209],... + 'VerticalAlignment','middle',... + 'String',{'Text with a','thick and','dotted','border'},... + 'HorizontalAlignment','center',... + 'FitBoxToText','off',... + 'LineStyle',':',... + 'LineWidth',4); + + % Create textbox + annotation(gcf,'textbox',... + [0.729456913827655 0.608247422680412 0.0851723446893787 0.104257797902974],... + 'String',{'Overlapping','and italic'},... + 'FontAngle','italic',... + 'FitBoxToText','off',... + 'BackgroundColor',[0.756862759590149 0.866666674613953 0.776470601558685]); +end +% ========================================================================= +function [stat] = imageOrientation_inline() +% Run test and save pictures as inline TikZ code + [stat] = imageOrientation(false); + stat.unreliable = isOctave; % FIXME +end +function [stat] = imageOrientation_PNG() +% Run test and save pictures as external PNGs + [stat] = imageOrientation(true); + stat.unreliable = isOctave; % FIXME +end +function [stat] = imageOrientation(imagesAsPng) +% Parameter 'imagesAsPng' is boolean + stat.description = ['Systematic test of different axis', ... + ' orientations and visibility (imagesAsPng = ', ... + num2str(imagesAsPng), ').']; + stat.extraOptions = {'imagesAsPng', imagesAsPng}; + + data = magic(3); + data = [[0,0,9]; data]; % ensure non-quadratic matrix + + subplot(3,2,1); + imagesc(data); colormap(hot); + set(gca,'XDir','normal'); + xlabel('XDir normal'); + set(gca,'YDir','normal'); + ylabel('YDir normal'); + + subplot(3,2,2); + imagesc(data); colormap(hot); + set(gca,'XDir','reverse'); + xlabel('XDir reverse'); + set(gca,'YDir','normal'); + ylabel('YDir normal'); + + subplot(3,2,3); + imagesc(data); colormap(hot); + set(gca,'XDir','normal'); + xlabel('XDir normal'); + set(gca,'YDir','reverse'); + ylabel('YDir reverse'); + + subplot(3,2,4); + imagesc(data); colormap(hot); + set(gca,'XDir','reverse'); + xlabel('XDir reverse'); + set(gca,'YDir','reverse'); + ylabel('YDir reverse'); + + subplot(3,2,5); + imagesc(data); colormap(hot); + set(gca,'XDir','normal'); + xlabel('XDir normal'); + set(gca,'YDir','reverse'); + ylabel('YDir reverse'); + axis off; + title('like above, but axis off'); + + subplot(3,2,6); + imagesc(data); colormap(hot); + set(gca,'XDir','reverse'); + xlabel('XDir reverse'); + set(gca,'YDir','reverse'); + ylabel('YDir reverse'); + axis off; + title('like above, but axis off'); +end +% ========================================================================= +function [stat] = texInterpreter() + stat.description = 'Combinations of tex commands'; + axes + text(0.1,0.9, {'\bfBold text before \alpha and also afterwards.', 'Even the next line is bold \itand a bit italic.'}); + text(0.1,0.75, {'Changing \bfthe\fontname{Courier} font or \color[rgb]{0,0.75,0}color doesn''t', 'change the style. Resetting \rmthe style', 'doesn''t change the font or color.'}); + text(0.1,0.6, 'Styles can be {\bflimited} using \{ and \}.'); + text(0.1,0.45, {'But what happens to the output if there is', '{\bfuse an \alpha inside} the limitted style.'}); + text(0.1,0.3, 'Or if the\fontsize{14} size\color{red} and color are \fontsize{10}changed at different\color{blue} points.'); + text(0.1,0.15, {'Also_{some \bf subscripts} and^{superscripts} are possible.', 'Without brackets, it l^o_oks like t_his.' }); +end +% ========================================================================= +function [stat] = stackedBarsWithOther() + stat.description = 'stacked bar plots and other plots'; + stat.issues = [442,648]; + stat.unreliable = isOctave || isMATLAB(); % FIXME: #614 + % details: https://github.com/matlab2tikz/matlab2tikz/pull/614#issuecomment-91844506 + + % dataset stacked + data = ACID_data; + Y = round(abs(data(7:-1:3,1:3))/10); + n = size(Y,1); + xVals = (1:n).'; + yVals = min((xVals).^2, sum(Y,2)); + + subplot(2,1,1); hold on; + bar(Y,'stacked'); + plot(xVals, yVals, 'Color', 'r', 'LineWidth', 2); + legend('show'); + + subplot(2,1,2); hold on; + b2 = barh(Y,'stacked','BarWidth', 0.75); + plot(yVals, xVals, 'Color', 'b', 'LineWidth', 2); + + set(b2(1),'FaceColor','c','EdgeColor','none') +end +% ========================================================================= +function [stat] = colorbarLabelTitle() + stat.description = 'colorbar with label and title'; + stat.unreliable = isOctave; %FIXME: investigate + stat.issues = 429; + + % R2014b handles colorbars smart: `XLabel` and `YLabel` merged into `Label` + % Use colormap 'jet' to create comparable output with MATLAB R2014b + % * Check horizontal/vertical colorbar (subplots) + % * Check if 'direction' is respected + % * Check if multiline label and title works + % * Check if latex interpreter works in label and title + + subplot(1,2,1) + imagesc(magic(3)); + hc = colorbar; + colormap('jet'); + title(hc,'title $\beta$','Interpreter','latex'); + ylabel(hc,'label $a^2$','Interpreter','latex'); + set(hc,'YDir','reverse'); + + subplot(1,2,2) + label_multiline = {'first','second','third'}; + title_multiline = {'title 1','title 2'}; + imagesc(magic(3)); + hc = colorbar('southoutside'); + colormap('jet'); + title(hc,title_multiline); + xlabel(hc,label_multiline); +end +% ========================================================================= +function [stat] = textAlignment() + stat.description = 'alignment of text boxes and position relative to axis'; + stat.issues = 378; + stat.unreliable = isOctave; %FIXME: investigate + + plot([0.0 2.0], [1.0 1.0],'k'); hold on; + plot([0.0 2.0], [0.5 0.5],'k'); + plot([0.0 2.0], [1.5 1.5],'k'); + plot([1.0 1.0], [0.0 2.0],'k'); + plot([1.5 1.5], [0.0 2.0],'k'); + plot([0.5 0.5], [0.0 2.0],'k'); + + text(1.0,1.0,'h=c, v=m', ... + 'HorizontalAlignment','center','VerticalAlignment','middle'); + text(1.5,1.0,'h=l, v=m', ... + 'HorizontalAlignment','left','VerticalAlignment','middle'); + text(0.5,1.0,'h=r, v=m', ... + 'HorizontalAlignment','right','VerticalAlignment','middle'); + + text(0.5,1.5,'h=r, v=b', ... + 'HorizontalAlignment','right','VerticalAlignment','bottom'); + text(1.0,1.5,'h=c, v=b', ... + 'HorizontalAlignment','center','VerticalAlignment','bottom'); + text(1.5,1.5,'h=l, v=b', ... + 'HorizontalAlignment','left','VerticalAlignment','bottom'); + + text(0.5,0.5,'h=r, v=t', ... + 'HorizontalAlignment','right','VerticalAlignment','top'); + text(1.0,0.5,'h=c, v=t', ... + 'HorizontalAlignment','center','VerticalAlignment','top'); + h_t = text(1.5,0.5,{'h=l, v=t','multiline'}, ... + 'HorizontalAlignment','left','VerticalAlignment','top'); + set(h_t,'BackgroundColor','g'); + + text(0.5,2.1, 'text outside axis (will be removed by cleanfigure())'); + text(1.8,0.7, {'text overlapping', 'axis limits'}); + text(-0.2,0.7, {'text overlapping', 'axis limits'}); + text(0.9,0.0, {'text overlapping', 'axis limits'}); + h_t = text(0.9,2.0, {'text overlapping', 'axis limits'}); + + % Set different units to test if they are properly handled + set(h_t, 'Units', 'centimeters'); +end +% ========================================================================= +function [stat] = overlappingPlots() + stat.description = 'Overlapping plots with zoomed data and varying background.'; + stat.unreliable = isMATLAB(); + % FIXME: this test is unreliable because the automatic axis limits of `ax2` + % differ on different test platforms. Reckon this by creating the figure + % using `ACID(97)` and then manually slightly modify the window size. + % We should not set the axis limits explicitly rather find a better way. + % Workaround: Slightly adapt width and height of `ax2`. + % #591, #641 (issuecomment-106241711) + stat.issues = 6; + + % create pseudo random data and convert it from matrix to vector + l = 256; + l_zoom = 64; + wave = sin(linspace(1,10*2*pi,l)); + + % plot data + ax1 = axes(); + plot(ax1, wave); + + % overlapping plots with zoomed data + ax3 = axes('Position', [0.2, 0.6, 0.3, 0.4]); + ax4 = axes('Position', [0.7, 0.2, 0.2, 0.4]); + ax2 = axes('Position', [0.25, 0.3, 0.3, 0.4]); + + plot(ax2, 1:l_zoom, wave(1:l_zoom), 'r'); + plot(ax3, 1:l_zoom, wave(1:l_zoom), 'k'); + plot(ax4, 1:l_zoom, wave(1:l_zoom), 'k'); + + % set x-axis limits of main plot and first subplot + xlim(ax1, [1,l]); + xlim(ax3, [1,l_zoom]); + + % axis background color: ax2 = default, ax3 = green, ax4 = transparent + set(ax3, 'Color', 'green'); + set(ax4, 'Color', 'none'); +end +% ========================================================================= +function [stat] = histogramPlot() + if isOctave || isMATLAB('<', [8,4]) + % histogram() was introduced in Matlab R2014b. + % TODO: later replace by 'isHG2()' + fprintf('histogram() not found. Skipping.\n' ); + stat.skip = true; + return; + end + stat.description = 'overlapping histogram() plots and custom size bins'; + stat.issues = 525; + + x = [-0.2, -0.484, 0.74, 0.632, -1.344, 0.921, -0.598, -0.727,... + -0.708, 1.045, 0.37, -1.155, -0.807, 1.027, 0.053, 0.863,... + 1.131, 0.134, -0.017, -0.316]; + y = x.^2; + edges = [-2 -1:0.25:3]; + histogram(x,edges); + hold on + h = histogram(y); + set(h, 'orientation', 'horizontal'); +end +% ========================================================================= +function [stat] = alphaTest() + stat.description = 'overlapping objects with transparency and other properties'; + stat.issues = 593; + + contourf(peaks(5)); hold on; % background + + % rectangular patch with different properties + h = fill([2 2 4 4], [2 3 3 2], 'r'); + set(h, 'FaceColor', 'r'); + set(h, 'FaceAlpha', 0.2); + set(h, 'EdgeColor', 'g'); + set(h, 'EdgeAlpha', 0.4); + set(h, 'LineStyle', ':'); + set(h, 'LineWidth', 4); + set(h, 'Marker', 'x'); + set(h, 'MarkerSize', 16); + set(h, 'MarkerEdgeColor', [1 0.5 0]); + set(h, 'MarkerFaceColor', [1 0 0]); % has no visual effect + + % line with different properties + h = line([3 3.5], [1.5 3.5]); + set(h, 'Color', [1 1 1]); + if isMATLAB('>=', [8,4]) + % TODO: later replace by 'isHG2()' + fprintf('Note: RGBA (with alpha channel) only in HG2.\n' ); + set(h, 'Color', [1 1 1 0.3]); + end + set(h, 'LineStyle', ':'); + set(h, 'LineWidth', 6); + set(h, 'Marker', 'o'); + set(h, 'MarkerSize', 14); + set(h, 'MarkerEdgeColor', [1 1 0]); + set(h, 'MarkerFaceColor', [1 0 0]); +end +% ========================================================================= +function [stat] = removeOutsideMarker() + stat.description = 'remove markers outside of the box'; + stat.issues = 788; + + % Create the data and plot it + xdata = -1 : 0.5 : 1.5; + ydata_marker = 1.5 * ones(size(xdata)); + ydata_line = 1 * ones(size(xdata)); + ydata_combined = 0.5 * ones(size(xdata)); + plot(xdata, ydata_marker, '*', ... + xdata, ydata_line, '-', ... + xdata, ydata_combined, '*-'); + title('Markers at -1 and 0.5 should be removed, the line shortened'); + + % Change the limits, so one marker is outside the box + ylim([0, 2]); + xlim([0, 2]); + + % Remove it + cleanfigure; + + % Change the limits back to check result + xlim([-1, 2]); +end +% ========================================================================= +function [stat] = colorbars() + stat.description = 'Manual positioning of colorbars'; + stat.issues = [933 937]; + stat.unreliable = isOctave(); %FIXME: positions differ between Octave 3.2 and 4.0. + + shift = [0.2 0.8 0.2 0.8]; + axLoc = {'in','out','out','in'}; + + for iAx = 1:4 + hAx(iAx) = subplot(2,2,iAx); + axPos = get(hAx(iAx), 'Position'); + cbPos = [axPos(1)+shift(iAx)*axPos(3), axPos(2), 0.02, 0.2]; + + hCb(iAx) = colorbar('Position', cbPos); + try + % only in HG2 + set(hCb(iAx), 'AxisLocation', axLoc{iAx}); + end + title(['AxisLocation = ' axLoc{iAx}]); + grid('on'); + end +end +% ========================================================================= +function [stat] = colorbarManualLocationRightOut() + stat.description = 'Manual positioning of colorbars - Right Out'; + stat.issues = [933 937]; + + axLoc = 'out'; + figPos = [1 , 1, 11 ,10]; + axPos(1,:) = [1 , 1, 8 , 3]; + axPos(2,:) = [1 , 5, 8 , 3]; + cbPos = [9.5, 1, 0.5, 7]; + + colorbarManualLocationHelper_(figPos, axPos, cbPos, axLoc); +end +function [stat] = colorbarManualLocationRightIn() + stat.description = 'Manual positioning of colorbars - Right In'; + stat.issues = [933 937]; + + axLoc = 'in'; + figPos = [ 1 , 1, 11 ,10]; + axPos(1,:) = [ 1 , 1, 8 , 3]; + axPos(2,:) = [ 1 , 5, 8 , 3]; + cbPos = [10.5, 1, 0.5, 7]; + + colorbarManualLocationHelper_(figPos, axPos, cbPos, axLoc); +end +function [stat] = colorbarManualLocationLeftOut() + stat.description = 'Manual positioning of colorbars - Left Out'; + stat.issues = [933 937]; + + axLoc = 'out'; + figPos = [1 , 1, 11 , 10]; + axPos(1,:) = [2.5, 1, 8 , 3]; + axPos(2,:) = [2.5, 5, 8 , 3]; + cbPos = [1.5, 1, 0.5, 7]; + + colorbarManualLocationHelper_(figPos, axPos, cbPos, axLoc); +end +function [stat] = colorbarManualLocationLeftIn() + stat.description = 'Manual positioning of colorbars - Left In'; + stat.issues = [933 937]; + + axLoc = 'in'; + figPos = [1 , 1, 11 , 10]; + axPos(1,:) = [2.5, 1, 8 , 3]; + axPos(2,:) = [2.5, 5, 8 , 3]; + cbPos = [0.5, 1, 0.5, 7]; + + colorbarManualLocationHelper_(figPos, axPos, cbPos, axLoc); +end +function colorbarManualLocationHelper_(figPos, axPos, cbPos, axLoc) + % this is a helper function, not a test case + set(gcf, 'Units','centimeters','Position', figPos); + + hAx(1) = axes('Units', 'centimeters', 'Position', axPos(1,:)); + imagesc([1,2,3], [4,5,6], magic(3)/9, [0,1]); + + hAx(2) = axes('Units', 'centimeters', 'Position', axPos(2,:)); + imagesc([1,2,3], [4,5,6], magic(3)/9, [0,1]); + + hCb = colorbar('Units', 'centimeters', 'Position', cbPos); + try + % only in HG2 + %TODO: check if there are HG1 / Octave counterparts for this property + set(hCb, 'AxisLocation', axLoc); + end + + labelProperty = {'Label', 'YLabel'}; %YLabel as fallback for + idxLabel = find(cellfun(@(p) isprop(hCb, p), labelProperty), 1); + if ~isempty(idxLabel) + hLabel = get(hCb, labelProperty{idxLabel}); + set(hLabel, 'String', ['AxisLocation = ' axLoc]); + end +end +% ========================================================================= diff --git a/Functions/helper_functions_community/mat2tikz/test/suites/issues.m b/Functions/helper_functions_community/mat2tikz/test/suites/issues.m new file mode 100644 index 0000000..79dc69f --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/suites/issues.m @@ -0,0 +1,43 @@ +function [ status ] = issues( k ) +%ISSUES M2T Test cases related to issues +% +% Issue-related test cases for matlab2tikz +% +% See also: ACID, matlab2tikz_acidtest + testfunction_handles = { + @scatter3Plot3 + }; + + numFunctions = length( testfunction_handles ); + + if (k<=0) + status = testfunction_handles; + return; % This is used for querying numFunctions. + + elseif (k<=numFunctions) + status = testfunction_handles{k}(); + status.function = func2str(testfunction_handles{k}); + + else + error('issues:outOfBounds', ... + 'Out of bounds (number of testfunctions=%d)', numFunctions); + end + +end + +% ========================================================================= +function [stat] = scatter3Plot3() + stat.description = 'Scatter3 plot with 2 colors'; + stat.issues = 292; + + hold on; + x = sin(1:5); + y = cos(3.4 *(1:5)); + z = x.*y; + scatter3(x,y,z,150,... + 'MarkerEdgeColor','none','MarkerFaceColor','k'); + scatter3(-x,y,z,150,... + 'MarkerEdgeColor','none','MarkerFaceColor','b'); +end + +% ========================================================================= diff --git a/Functions/helper_functions_community/mat2tikz/test/suites/private/getEnvironment.m b/Functions/helper_functions_community/mat2tikz/test/suites/private/getEnvironment.m new file mode 100644 index 0000000..4717cb5 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/suites/private/getEnvironment.m @@ -0,0 +1,25 @@ +function [env, versionString] = getEnvironment() +% Determine environment (Octave, MATLAB) and version string +% TODO: Unify private `getEnvironment` functions + persistent cache + + if isempty(cache) + isOctave = exist('OCTAVE_VERSION', 'builtin') ~= 0; + if isOctave + env = 'Octave'; + versionString = OCTAVE_VERSION; + else + env = 'MATLAB'; + vData = ver(env); + versionString = vData.Version; + end + + % store in cache + cache.env = env; + cache.versionString = versionString; + + else + env = cache.env; + versionString = cache.versionString; + end +end diff --git a/Functions/helper_functions_community/mat2tikz/test/suites/private/herrorbar.m b/Functions/helper_functions_community/mat2tikz/test/suites/private/herrorbar.m new file mode 100644 index 0000000..9f4ad19 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/suites/private/herrorbar.m @@ -0,0 +1,167 @@ +function hh = herrorbar(x, y, l, u, symbol) +%HERRORBAR Horizontal Error bar plot. +% HERRORBAR(X,Y,L,R) plots the graph of vector X vs. vector Y with +% horizontal error bars specified by the vectors L and R. L and R contain the +% left and right error ranges for each point in X. Each error bar +% is L(i) + R(i) long and is drawn a distance of L(i) to the right and R(i) +% to the right the points in (X,Y). The vectors X,Y,L and R must all be +% the same length. If X,Y,L and R are matrices then each column +% produces a separate line. +% +% HERRORBAR(X,Y,E) or HERRORBAR(Y,E) plots X with error bars [X-E X+E]. +% HERRORBAR(...,'LineSpec') uses the color and linestyle specified by +% the string 'LineSpec'. See PLOT for possibilities. +% +% H = HERRORBAR(...) returns a vector of line handles. +% +% Example: +% x = 1:10; +% y = sin(x); +% e = std(y)*ones(size(x)); +% herrorbar(x,y,e) +% draws symmetric horizontal error bars of unit standard deviation. +% +% This code is based on ERRORBAR provided in MATLAB. +% +% See also ERRORBAR + +% Jos van der Geest +% email: jos@jasen.nl +% +% File history: +% August 2006 (Jos): I have taken back ownership. I like to thank Greg Aloe from +% The MathWorks who originally introduced this piece of code to the +% Matlab File Exchange. +% September 2003 (Greg Aloe): This code was originally provided by Jos +% from the newsgroup comp.soft-sys.matlab: +% http://newsreader.mathworks.com/WebX?50@118.fdnxaJz9btF^1@.eea3ff9 +% After unsuccessfully attempting to contact the orignal author, I +% decided to take ownership so that others could benefit from finding it +% on the MATLAB Central File Exchange. + +if min(size(x))==1, + npt = length(x); + x = x(:); + y = y(:); + if nargin > 2, + if ~ischar(l), + l = l(:); + end + if nargin > 3 + if ~ischar(u) + u = u(:); + end + end + end +else + [npt,n] = size(x); +end + +if nargin == 3 + if ~ischar(l) + u = l; + symbol = '-'; + else + symbol = l; + l = y; + u = y; + y = x; + [m,n] = size(y); + x(:) = (1:npt)'*ones(1,n);; + end +end + +if nargin == 4 + if ischar(u), + symbol = u; + u = l; + else + symbol = '-'; + end +end + +if nargin == 2 + l = y; + u = y; + y = x; + [m,n] = size(y); + x(:) = (1:npt)'*ones(1,n);; + symbol = '-'; +end + +u = abs(u); +l = abs(l); + +if ischar(x) || ischar(y) || ischar(u) || ischar(l) + error('Arguments must be numeric.') +end + +if ~isequal(size(x),size(y)) || ~isequal(size(x),size(l)) || ~isequal(size(x),size(u)), + error('The sizes of X, Y, L and U must be the same.'); +end + +tee = (max(y(:))-min(y(:)))/100; % make tee .02 x-distance for error bars +% changed from errorbar.m +xl = x - l; +xr = x + u; +ytop = y + tee; +ybot = y - tee; +n = size(y,2); +% end change + +% Plot graph and bars +hold_state = ishold; +cax = newplot; +next = lower(get(cax,'NextPlot')); + +% build up nan-separated vector for bars +% changed from errorbar.m +xb = zeros(npt*9,n); +xb(1:9:end,:) = xl; +xb(2:9:end,:) = xl; +xb(3:9:end,:) = NaN; +xb(4:9:end,:) = xl; +xb(5:9:end,:) = xr; +xb(6:9:end,:) = NaN; +xb(7:9:end,:) = xr; +xb(8:9:end,:) = xr; +xb(9:9:end,:) = NaN; + +yb = zeros(npt*9,n); +yb(1:9:end,:) = ytop; +yb(2:9:end,:) = ybot; +yb(3:9:end,:) = NaN; +yb(4:9:end,:) = y; +yb(5:9:end,:) = y; +yb(6:9:end,:) = NaN; +yb(7:9:end,:) = ytop; +yb(8:9:end,:) = ybot; +yb(9:9:end,:) = NaN; +% end change + + +[ls,col,mark,msg] = colstyle(symbol); +if ~isempty(msg) + error(msg); +end +if isempty(col) + col = ''; +end +symbol = [ls mark col]; % Use marker only on data part +esymbol = ['-' col]; % Make sure bars are solid +if ~isempty(strfind(symbol,'none')) + symbol = 'none'; +end +if ~isempty(strfind(esymbol,'none')) + esymbol = 'none'; +end +h = plot(xb,yb,'LineStyle',esymbol); hold on +h = [h;plot(x,y,'LineStyle',symbol)]; + +if ~hold_state + hold off; +end + +if nargout>0 + hh = h; +end diff --git a/Functions/helper_functions_community/mat2tikz/test/suites/private/isEnvironment.m b/Functions/helper_functions_community/mat2tikz/test/suites/private/isEnvironment.m new file mode 100644 index 0000000..b5f315a --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/suites/private/isEnvironment.m @@ -0,0 +1,46 @@ +function bool = isEnvironment(wantedEnvironment, varargin) +% ISENVIRONMENT check for a particular environment (MATLAB/Octave) +% +% This function returns TRUE when it is run within the "wantedEnvironment" +% (e.g. MATLAB or Octave). This environment can be tested to be a particular +% version or be older/newer than a specified version. +% +% Usage: +% +% ISENVIRONMENT(ENV) +% ISENVIRONMENT(ENV, VERSION) +% ISENVIRONMENT(ENV, OP, VERSION) +% +% Parameters: +% - `ENV`: the expected environment (e.g. 'MATLAB' or 'Octave') +% - `VERSION`: a version number or string to compare against +% e.g. "3.4" or equivalently [3,4] +% - `OP`: comparison operator (e.g. '==', '<=', '<', ...) to define a range +% of version numbers that return a TRUE value +% +% When `OP` is not specified, "==" is used. +% When no `VERSION` is specified, all versions pass the check. +% +% See also: isMATLAB, isOctave, versionCompare + [env, thisVersion] = getEnvironment(); + bool = strcmpi(env, wantedEnvironment); + + switch numel(varargin) + case 0 % nothing to be done + return + + case 1 % check equality + version = varargin{1}; + operator = '=='; + bool = bool && versionCompare(thisVersion, operator, version); + + case 2 + operator = varargin{1}; + version = varargin{2}; + bool = bool && versionCompare(thisVersion, operator, version); + + otherwise + error('isEnvironment:BadNumberOfArguments', ... + '"isEnvironment" was called with an incorrect number of arguments.'); + end +end diff --git a/Functions/helper_functions_community/mat2tikz/test/suites/private/isMATLAB.m b/Functions/helper_functions_community/mat2tikz/test/suites/private/isMATLAB.m new file mode 100644 index 0000000..962ff77 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/suites/private/isMATLAB.m @@ -0,0 +1,4 @@ +function bool = isMATLAB(varargin) +%ISMATLAB Determines whether (a certain) version of MATLAB is being used +% See also: isEnvironment, isOctave +bool = isEnvironment('MATLAB', varargin{:}); diff --git a/Functions/helper_functions_community/mat2tikz/test/suites/private/isOctave.m b/Functions/helper_functions_community/mat2tikz/test/suites/private/isOctave.m new file mode 100644 index 0000000..28936e3 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/suites/private/isOctave.m @@ -0,0 +1,5 @@ +function bool = isOctave(varargin) +%ISOCTAVE Determines whether (a certain) version of Octave is being used +% +% See also: isEnvironment, isMATLAB +bool = isEnvironment('Octave', varargin{:}); diff --git a/Functions/helper_functions_community/mat2tikz/test/suites/private/isVersionBelow.m b/Functions/helper_functions_community/mat2tikz/test/suites/private/isVersionBelow.m new file mode 100644 index 0000000..c078b26 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/suites/private/isVersionBelow.m @@ -0,0 +1,38 @@ +function isBelow = isVersionBelow(versionA, versionB) +% Checks if versionA is smaller than versionB + vA = versionArray(versionA); + vB = versionArray(versionB); + n = min(length(vA), length(vB)); + deltaAB = vA(1:n) - vB(1:n); + difference = find(deltaAB, 1, 'first'); + if isempty(difference) + isBelow = false; % equal versions + else + isBelow = (deltaAB(difference) < 0); + end +end +% ============================================================================== +function arr = versionArray(str) +% Converts a version string to an array. + if ischar(str) + % Translate version string from '2.62.8.1' to [2; 62; 8; 1]. + switch getEnvironment + case 'MATLAB' + split = regexp(str, '\.', 'split'); % compatibility MATLAB < R2013a + case 'Octave' + split = strsplit(str, '.'); + otherwise + errorUnknownEnvironment(); + end + arr = str2num(char(split)); %#ok + else + arr = str; + end + arr = arr(:)'; +end +% ============================================================================== +function errorUnknownEnvironment() +error('matlab2tikz:unknownEnvironment',... + 'Unknown environment "%s". Need MATLAB(R) or Octave.', getEnvironment); +end +% ============================================================================== diff --git a/Functions/helper_functions_community/mat2tikz/test/suites/private/versionCompare.m b/Functions/helper_functions_community/mat2tikz/test/suites/private/versionCompare.m new file mode 100644 index 0000000..6ef6096 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/suites/private/versionCompare.m @@ -0,0 +1,20 @@ +function bool = versionCompare( vA, operator, vB ) +%VERSIONCOMPARE Performs a version comparison operation + switch operator + case '<' + bool = isVersionBelow(vA, vB); + case '>' + bool = isVersionBelow(vB, vA); + case {'<=', '=<'} + bool = ~isVersionBelow(vB, vA); + case {'>=', '=>'} + bool = ~isVersionBelow(vA, vB); + case {'=', '=='} + bool = ~isVersionBelow(vA, vB) && ~isVersionBelow(vB, vA); + case {'~=', '!='} + bool = isVersionBelow(vA, vB) || isVersionBelow(vB, vA); + otherwise + error('versionCompare:UnknownOperator',... + '"%s" is not a known comparison operator', operator); + end +end diff --git a/Functions/helper_functions_community/mat2tikz/test/suites/testPatches.m b/Functions/helper_functions_community/mat2tikz/test/suites/testPatches.m new file mode 100644 index 0000000..6cd9454 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/suites/testPatches.m @@ -0,0 +1,121 @@ +function status = testPatches(k) +% TESTPATCHES Test suite for patches +% +% See also: ACID, matlab2tikz_acidtest + +testfunction_handles = { + @patch01; + @patch02; + @patch03; + @patch04; + @patch05; + @patch06; + @patch07; + @patch08; + }; + +numFunctions = length( testfunction_handles ); + +if nargin < 1 || isempty(k) || k <= 0 + status = testfunction_handles; + return; % This is used for querying numFunctions. + +elseif (k<=numFunctions) + status = testfunction_handles{k}(); + status.function = func2str(testfunction_handles{k}); + +else + error('patchTests:outOfBounds', ... + 'Out of bounds (number of testfunctions=%d)', numFunctions); +end + +end + +% ========================================================================= +function p = patch00() +% DO NOT INCLUDE IN ACID LIST +% Base patch plot for following tests +xdata = [2 2 0 2 5; 2 8 2 4 5; 8 8 2 4 8]; +ydata = [4 4 4 2 0; 8 4 6 2 2; 4 0 4 0 0]; +zdata = ones(3,5)*2; +p = patch(xdata,ydata,zdata); +end +% ========================================================================= +function stat = patch01() +stat.description = 'Set face color red'; + +p = patch00(); +set(p,'FaceColor','r') +end +% ========================================================================= +function stat = patch02() +stat.description = 'Flat face colors scaled in clim [0,40]'; + +p = patch00(); +set(gca,'CLim',[0 40]) +cdata = [15 30 25 2 60]; +set(p,'FaceColor','flat','CData',cdata,'CDataMapping','scaled') +end +% ========================================================================= +function stat = patch03() +stat.description = 'Flat face colors direct in clim [0,40]'; + +p = patch00(); +set(gca,'CLim',[0 40]) +cdata = [15 30 25 2 60]; +set(p,'FaceColor','flat','CData',cdata,'CDataMapping','direct') +end +% ========================================================================= +function stat = patch04() +stat.description = 'Flat face colors with 3D (truecolor) CData'; + +p = patch00(); +cdata(:,:,1) = [0 0 1 0 0.8]; +cdata(:,:,2) = [0 0 0 0 0.8]; +cdata(:,:,3) = [1 1 1 0 0.8]; +set(p,'FaceColor','flat','CData',cdata) +end +% ========================================================================= +function stat = patch05() +stat.description = 'Flat face color, scaled edge colors in clim [0,40]'; + +p = patch00(); +set(gca,'CLim',[0 40]) +cdata = [15 30 25 2 60; 12 23 40 13 26; 24 8 1 65 42]; +set(p,'FaceColor','flat','CData',cdata,'EdgeColor','flat','LineWidth',5,'CDataMapping','scaled') +end +% ========================================================================= +function stat = patch06() +stat.description = 'Flat face color, direct edge colors in clim [0,40]'; + +p = patch00(); +set(gca,'CLim',[0 40]) +cdata = [15 30 25 2 60; 12 23 40 13 26; 24 8 1 65 42]; +set(p,'FaceColor','flat','CData',cdata,'EdgeColor','flat','LineWidth',5,'CDataMapping','direct') +end +% ========================================================================= +function stat = patch07() +stat.description = 'Flat face color with 3D CData and interp edge colors'; + +p = patch00(); +cdata(:,:,1) = [0 0 1 0 0.8; + 0 0 1 0.2 0.6; + 0 1 0 0.4 1]; +cdata(:,:,2) = [0 0 0 0 0.8; + 1 1 1 0.2 0.6; + 1 0 0 0.4 0]; +cdata(:,:,3) = [1 1 1 0 0.8; + 0 1 0 0.2 0.6; + 1 0 1 0.4 0]; +set(p,'FaceColor','flat','CData',cdata,'EdgeColor','interp','LineWidth',5) +end +% ========================================================================= +function stat = patch08() +stat.description = 'Interp face colors, flat edges, scaled CData in clims [0,40]'; + +p = patch00(); +set(gca,'CLim',[0 40]) +cdata = [15 30 25 2 60; 12 23 40 13 26; 24 8 1 65 42]; +set(p,'FaceColor','interp','CData',cdata,'EdgeColor','flat','LineWidth',5,'CDataMapping','scaled') +end +% ========================================================================= diff --git a/Functions/helper_functions_community/mat2tikz/test/suites/testSurfshader.m b/Functions/helper_functions_community/mat2tikz/test/suites/testSurfshader.m new file mode 100644 index 0000000..15ac25d --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/suites/testSurfshader.m @@ -0,0 +1,102 @@ +function status = testSurfshader(k) +% TESTSURFSHADER Test suite for Surf/mesh shaders (coloring) +% +% See also: ACID, matlab2tikz_acidtest + + testfunction_handles = { + @surfShader1; + @surfShader2; + @surfShader3; + @surfShader4; + @surfShader5; + @surfNoShader; + @surfNoPlot; + @surfMeshInterp; + @surfMeshRGB; + }; + + numFunctions = length( testfunction_handles ); + + if nargin < 1 || isempty(k) || k <= 0 + status = testfunction_handles; + return; % This is used for querying numFunctions. + + elseif (k<=numFunctions) + status = testfunction_handles{k}(); + status.function = func2str(testfunction_handles{k}); + + else + error('patchTests:outOfBounds', ... + 'Out of bounds (number of testfunctions=%d)', numFunctions); + end + +end + +% ========================================================================= +function [stat] = surfShader1() + stat.description = 'shader=flat/(flat mean) | Fc: flat | Ec: none'; + + [X,Y,Z] = peaks(5); + surf(X,Y,Z,'FaceColor','flat','EdgeColor','none') +end +% ========================================================================= +function [stat] = surfShader2() + stat.description = 'shader=interp | Fc: interp | Ec: none'; + + [X,Y,Z] = peaks(5); + surf(X,Y,Z,'FaceColor','interp','EdgeColor','none') +end +% ========================================================================= +function [stat] = surfShader3() + stat.description = 'shader=faceted | Fc: flat | Ec: RGB'; + + [X,Y,Z] = peaks(5); + surf(X,Y,Z,'FaceColor','flat','EdgeColor','green') +end +% ========================================================================= +function [stat] = surfShader4() +stat.description = 'shader=faceted | Fc: RGB | Ec: interp'; +if isMATLAB('<', [8,4]); %R2014a and older + warning('m2t:ACID:surfShader4',... + 'The MATLAB EPS export may behave strangely for this case'); +end + +[X,Y,Z] = peaks(5); +surf(X,Y,Z,'FaceColor','blue','EdgeColor','interp') +end +% ========================================================================= +function [stat] = surfShader5() +stat.description = 'shader=faceted interp | Fc: interp | Ec: flat'; + +[X,Y,Z] = peaks(5); +surf(X,Y,Z,'FaceColor','interp','EdgeColor','flat') +end +% ========================================================================= +function [stat] = surfNoShader() +stat.description = 'no shader | Fc: RGB | Ec: RGB'; + +[X,Y,Z] = peaks(5); +surf(X,Y,Z,'FaceColor','blue','EdgeColor','yellow') +end +% ========================================================================= +function [stat] = surfNoPlot() +stat.description = 'no plot | Fc: none | Ec: none'; + +[X,Y,Z] = peaks(5); +surf(X,Y,Z,'FaceColor','none','EdgeColor','none') +end +% ========================================================================= +function [stat] = surfMeshInterp() +stat.description = 'mesh | Fc: none | Ec: interp'; + +[X,Y,Z] = peaks(5); +surf(X,Y,Z,'FaceColor','none','EdgeColor','interp') +end +% ========================================================================= +function [stat] = surfMeshRGB() +stat.description = 'mesh | Fc: none | Ec: RGB'; + +[X,Y,Z] = peaks(5); +surf(X,Y,Z,'FaceColor','none','EdgeColor','green') +end +% ========================================================================= diff --git a/Functions/helper_functions_community/mat2tikz/test/template/.gitignore b/Functions/helper_functions_community/mat2tikz/test/template/.gitignore new file mode 100644 index 0000000..8878867 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/template/.gitignore @@ -0,0 +1,6 @@ +# just ignore all generate files +acid.* +*.log +*.aux +*.pdf +*.tex diff --git a/Functions/helper_functions_community/mat2tikz/test/template/Makefile b/Functions/helper_functions_community/mat2tikz/test/template/Makefile new file mode 100644 index 0000000..72f9d4f --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/template/Makefile @@ -0,0 +1,33 @@ +# ./Makefile + +ECHOCMD:=/bin/echo -e +LATEX:=pdflatex --shell-escape +TARGET:=acid + +main: + cd data/reference/ && $(MAKE) + cd data/converted/ && $(MAKE) + @$(LATEX) $(TARGET) + +.PHONY: clean + +clean: + @rm -f $(TARGET).aux \ + $(TARGET).log \ + $(TARGET).nav \ + $(TARGET).out \ + $(TARGET).snm \ + $(TARGET).toc \ + $(TARGET).vrb \ + $(TARGET).pdf \ + $(TARGET).dvi \ + $(TARGET).ps \ + missfont.log + @rm -f *~ + cd data/reference/ && $(MAKE) clean + cd data/converted/ && $(MAKE) clean + +distclean: clean + @rm -f $(TARGET).tex + cd data/reference/ && $(MAKE) distclean + cd data/converted/ && $(MAKE) distclean diff --git a/Functions/helper_functions_community/mat2tikz/test/template/data/.gitignore b/Functions/helper_functions_community/mat2tikz/test/template/data/.gitignore new file mode 100644 index 0000000..e511b9b --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/template/data/.gitignore @@ -0,0 +1,8 @@ +# just ignore all generated files +*.log +*.aux +*.pdf +*.eps +*.png +*.tex +*.tsv diff --git a/Functions/helper_functions_community/mat2tikz/test/template/data/converted/Makefile b/Functions/helper_functions_community/mat2tikz/test/template/data/converted/Makefile new file mode 100644 index 0000000..3197373 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/template/data/converted/Makefile @@ -0,0 +1,23 @@ +# ./Makefile + +ECHOCMD:=/bin/echo -e +LATEX:=pdflatex --shell-escape -interaction=batchmode + +TEST_SRCS:=$(wildcard test*-converted.tex) +TEST_PDFS:=$(TEST_SRCS:.tex=.pdf) + +default: $(TEST_PDFS) + +%.pdf: %.tex + @$(LATEX) $< + +.PHONY: clean + +clean: + rm -f test*-converted.aux \ + test*-converted.log \ + test*-converted.pdf + +distclean: clean + rm -f test*-converted.tex \ + test*-converted*.png diff --git a/Functions/helper_functions_community/mat2tikz/test/template/data/reference/Makefile b/Functions/helper_functions_community/mat2tikz/test/template/data/reference/Makefile new file mode 100644 index 0000000..1c39fac --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/template/data/reference/Makefile @@ -0,0 +1,19 @@ +# ./Makefile + +EPSTOPDF:=epstopdf + +REFERENCE_EPSS:=$(wildcard test*-reference.eps) +REFERENCE_PDFS:=$(REFERENCE_EPSS:.eps=.pdf) + +default: $(REFERENCE_PDFS) + +%.pdf: %.eps + $(EPSTOPDF) $< + +.PHONY: clean + +clean: + rm -f test*-reference.pdf + +distclean: clean + rm -f test*-reference.eps diff --git a/Functions/helper_functions_community/mat2tikz/test/testGraphical.m b/Functions/helper_functions_community/mat2tikz/test/testGraphical.m new file mode 100644 index 0000000..27e943d --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/testGraphical.m @@ -0,0 +1,47 @@ +function [ status ] = testGraphical( varargin ) +%TESTGRAPHICAL Runs the M2T test suite to produce graphical output +% +% This is quite a thin wrapper around testMatlab2tikz to run the test suite to +% produce a PDF side-by-side report. +% +% Its allowed arguments are the same as those of testMatlab2tikz. +% +% Usage: +% +% status = testGraphical(...) % gives programmatical access to the data +% +% testGraphical(...); % automatically invokes makeLatexReport afterwards +% +% See also: testMatlab2tikz, testHeadless, makeLatexReport + + [state] = initializeGlobalState(); + finally_restore_state = onCleanup(@() restoreGlobalState(state)); + + [status, args] = testMatlab2tikz('actionsToExecute', @actionsToExecute, ... + varargin{:}); + + if nargout == 0 + makeLatexReport(status, args.output); + end +end +% ============================================================================== +function status = actionsToExecute(status, ipp) + status = execute_plot_stage(status, ipp); + + if status.skip + return + end + + status = execute_save_stage(status, ipp); + status = execute_tikz_stage(status, ipp); + %status = execute_hash_stage(status, ipp); %cannot work with files in + %standalone mode! + status = execute_type_stage(status, ipp); + + if ~status.closeall && ~isempty(status.plotStage.fig_handle) + close(status.plotStage.fig_handle); + else + close all; + end +end +% ============================================================================== diff --git a/Functions/helper_functions_community/mat2tikz/test/testHeadless.m b/Functions/helper_functions_community/mat2tikz/test/testHeadless.m new file mode 100644 index 0000000..b88b5a8 --- /dev/null +++ b/Functions/helper_functions_community/mat2tikz/test/testHeadless.m @@ -0,0 +1,62 @@ +function [ status ] = testHeadless( varargin ) +%TESTGRAPHICAL Runs the M2T test suite without graphical output +% +% This is quite a thin wrapper around testMatlab2tikz to run the test suite to +% produce a textual report and checks for regressions by checking the MD5 hash +% of the output +% +% Its allowed arguments are the same as those of testMatlab2tikz. +% +% Usage: +% +% status = TESTHEADLESS(...) % gives programmatical access to the data +% +% TESTHEADLESS(...); % automatically invokes makeTravisReport afterwards +% +% See also: testMatlab2tikz, testGraphical, makeTravisReport + +% The width and height are specified to circumvent different DPIs in developer +% machines. The float format reduces the probability that numerical differences +% in the order of numerical precision disrupt the output. + extraOptions = {'width' ,'\figureWidth', ... + 'height','\figureHeight',... + 'floatFormat', '%4g', ... % see #604 + 'extraCode',{ ... + '\newlength\figureHeight \setlength{\figureHeight}{6cm}', ... + '\newlength\figureWidth \setlength{\figureWidth}{10cm}'} + }; + + [state] = initializeGlobalState(); + finally_restore_state = onCleanup(@() restoreGlobalState(state)); + + status = testMatlab2tikz('extraOptions', extraOptions, ... + 'actionsToExecute', @actionsToExecute, ... + varargin{:}); + + if nargout == 0 + makeTravisReport(status); + end +end +% ============================================================================== +function status = actionsToExecute(status, ipp) + status = execute_plot_stage(status, ipp); + + if status.skip + return + end + + status = execute_tikz_stage(status, ipp); + status = execute_hash_stage(status, ipp); + status = execute_type_stage(status, ipp); + + if ~status.closeall && ~isempty(status.plotStage.fig_handle) + try + close(status.plotStage.fig_handle); + catch + close('all'); + end + else + close all; + end +end +% ============================================================================== diff --git a/Functions/helper_functions_community/png/nt_.png b/Functions/helper_functions_community/png/nt_.png new file mode 100644 index 0000000000000000000000000000000000000000..f55bb4754bf9e0c07869057a6228e18c81c57248 GIT binary patch literal 58301 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

g;3^S$mMhPdCf_ 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 literal 0 HcmV?d00001 diff --git a/Functions/helper_functions_community/boundedlines/Inpaint_nans/demo/html/inpaint_nans_demo_06.png b/Functions/helper_functions_community/boundedlines/Inpaint_nans/demo/html/inpaint_nans_demo_06.png new file mode 100644 index 0000000000000000000000000000000000000000..546508188968aa3fdb57b56110e30ec91e21fd0d GIT binary patch literal 9398 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 literal 0 HcmV?d00001 diff --git a/Functions/helper_functions_community/boundedlines/Inpaint_nans/inpaint_nans.m b/Functions/helper_functions_community/boundedlines/Inpaint_nans/inpaint_nans.m new file mode 100644 index 0000000..2460b51 --- /dev/null +++ b/Functions/helper_functions_community/boundedlines/Inpaint_nans/inpaint_nans.m @@ -0,0 +1 @@ +function B=inpaint_nans(A,method) % INPAINT_NANS: in-paints over nans in an array % usage: B=INPAINT_NANS(A) % default method % usage: B=INPAINT_NANS(A,method) % specify method used % % Solves approximation to one of several pdes to % interpolate and extrapolate holes in an array % % arguments (input): % A - nxm array with some NaNs to be filled in % % method - (OPTIONAL) scalar numeric flag - specifies % which approach (or physical metaphor to use % for the interpolation.) All methods are capable % of extrapolation, some are better than others. % There are also speed differences, as well as % accuracy differences for smooth surfaces. % % methods {0,1,2} use a simple plate metaphor. % method 3 uses a better plate equation, % but may be much slower and uses % more memory. % method 4 uses a spring metaphor. % method 5 is an 8 neighbor average, with no % rationale behind it compared to the % other methods. I do not recommend % its use. % % method == 0 --> (DEFAULT) see method 1, but % this method does not build as large of a % linear system in the case of only a few % NaNs in a large array. % Extrapolation behavior is linear. % % method == 1 --> simple approach, applies del^2 % over the entire array, then drops those parts % of the array which do not have any contact with % NaNs. Uses a least squares approach, but it % does not modify known values. % In the case of small arrays, this method is % quite fast as it does very little extra work. % Extrapolation behavior is linear. % % method == 2 --> uses del^2, but solving a direct % linear system of equations for nan elements. % This method will be the fastest possible for % large systems since it uses the sparsest % possible system of equations. Not a least % squares approach, so it may be least robust % to noise on the boundaries of any holes. % This method will also be least able to % interpolate accurately for smooth surfaces. % Extrapolation behavior is linear. % % Note: method 2 has problems in 1-d, so this % method is disabled for vector inputs. % % method == 3 --+ See method 0, but uses del^4 for % the interpolating operator. This may result % in more accurate interpolations, at some cost % in speed. % % method == 4 --+ Uses a spring metaphor. Assumes % springs (with a nominal length of zero) % connect each node with every neighbor % (horizontally, vertically and diagonally) % Since each node tries to be like its neighbors, % extrapolation is as a constant function where % this is consistent with the neighboring nodes. % % method == 5 --+ See method 2, but use an average % of the 8 nearest neighbors to any element. % This method is NOT recommended for use. % % % arguments (output): % B - nxm array with NaNs replaced % % % Example: % [x,y] = meshgrid(0:.01:1); % z0 = exp(x+y); % znan = z0; % znan(20:50,40:70) = NaN; % znan(30:90,5:10) = NaN; % znan(70:75,40:90) = NaN; % % z = inpaint_nans(znan); % % % See also: griddata, interp1 % % Author: John D'Errico % e-mail address: woodchips@rochester.rr.com % Release: 2 % Release date: 4/15/06 % I always need to know which elements are NaN, % and what size the array is for any method [n,m]=size(A); A=A(:); nm=n*m; k=isnan(A(:)); % list the nodes which are known, and which will % be interpolated nan_list=find(k); known_list=find(~k); % how many nans overall nan_count=length(nan_list); % convert NaN indices to (r,c) form % nan_list==find(k) are the unrolled (linear) indices % (row,column) form [nr,nc]=ind2sub([n,m],nan_list); % both forms of index in one array: % column 1 == unrolled index % column 2 == row index % column 3 == column index nan_list=[nan_list,nr,nc]; % supply default method if (nargin<2) || isempty(method) method = 0; elseif ~ismember(method,0:5) error 'If supplied, method must be one of: {0,1,2,3,4,5}.' end % for different methods switch method case 0 % The same as method == 1, except only work on those % elements which are NaN, or at least touch a NaN. % is it 1-d or 2-d? if (m == 1) || (n == 1) % really a 1-d case work_list = nan_list(:,1); work_list = unique([work_list;work_list - 1;work_list + 1]); work_list(work_list <= 1) = []; work_list(work_list >= nm) = []; nw = numel(work_list); u = (1:nw)'; fda = sparse(repmat(u,1,3),bsxfun(@plus,work_list,-1:1), ... repmat([1 -2 1],nw,1),nw,nm); else % a 2-d case % horizontal and vertical neighbors only talks_to = [-1 0;0 -1;1 0;0 1]; neighbors_list=identify_neighbors(n,m,nan_list,talks_to); % list of all nodes we have identified all_list=[nan_list;neighbors_list]; % generate sparse array with second partials on row % variable for each element in either list, but only % for those nodes which have a row index > 1 or < n L = find((all_list(:,2) > 1) & (all_list(:,2) < n)); nl=length(L); if nl>0 fda=sparse(repmat(all_list(L,1),1,3), ... repmat(all_list(L,1),1,3)+repmat([-1 0 1],nl,1), ... repmat([1 -2 1],nl,1),nm,nm); else fda=spalloc(n*m,n*m,size(all_list,1)*5); end % 2nd partials on column index L = find((all_list(:,3) > 1) & (all_list(:,3) < m)); nl=length(L); if nl>0 fda=fda+sparse(repmat(all_list(L,1),1,3), ... repmat(all_list(L,1),1,3)+repmat([-n 0 n],nl,1), ... repmat([1 -2 1],nl,1),nm,nm); end end % eliminate knowns rhs=-fda(:,known_list)*A(known_list); k=find(any(fda(:,nan_list(:,1)),2)); % and solve... B=A; B(nan_list(:,1))=fda(k,nan_list(:,1))\rhs(k); case 1 % least squares approach with del^2. Build system % for every array element as an unknown, and then % eliminate those which are knowns. % Build sparse matrix approximating del^2 for % every element in A. % is it 1-d or 2-d? if (m == 1) || (n == 1) % a 1-d case u = (1:(nm-2))'; fda = sparse(repmat(u,1,3),bsxfun(@plus,u,0:2), ... repmat([1 -2 1],nm-2,1),nm-2,nm); else % a 2-d case % Compute finite difference for second partials % on row variable first [i,j]=ndgrid(2:(n-1),1:m); ind=i(:)+(j(:)-1)*n; np=(n-2)*m; fda=sparse(repmat(ind,1,3),[ind-1,ind,ind+1], ... repmat([1 -2 1],np,1),n*m,n*m); % now second partials on column variable [i,j]=ndgrid(1:n,2:(m-1)); ind=i(:)+(j(:)-1)*n; np=n*(m-2); fda=fda+sparse(repmat(ind,1,3),[ind-n,ind,ind+n], ... repmat([1 -2 1],np,1),nm,nm); end % eliminate knowns rhs=-fda(:,known_list)*A(known_list); k=find(any(fda(:,nan_list),2)); % and solve... B=A; B(nan_list(:,1))=fda(k,nan_list(:,1))\rhs(k); case 2 % Direct solve for del^2 BVP across holes % generate sparse array with second partials on row % variable for each nan element, only for those nodes % which have a row index > 1 or < n % is it 1-d or 2-d? if (m == 1) || (n == 1) % really just a 1-d case error('Method 2 has problems for vector input. Please use another method.') else % a 2-d case L = find((nan_list(:,2) > 1) & (nan_list(:,2) < n)); nl=length(L); if nl>0 fda=sparse(repmat(nan_list(L,1),1,3), ... repmat(nan_list(L,1),1,3)+repmat([-1 0 1],nl,1), ... repmat([1 -2 1],nl,1),n*m,n*m); else fda=spalloc(n*m,n*m,size(nan_list,1)*5); end % 2nd partials on column index L = find((nan_list(:,3) > 1) & (nan_list(:,3) < m)); nl=length(L); if nl>0 fda=fda+sparse(repmat(nan_list(L,1),1,3), ... repmat(nan_list(L,1),1,3)+repmat([-n 0 n],nl,1), ... repmat([1 -2 1],nl,1),n*m,n*m); end % fix boundary conditions at extreme corners % of the array in case there were nans there if ismember(1,nan_list(:,1)) fda(1,[1 2 n+1])=[-2 1 1]; end if ismember(n,nan_list(:,1)) fda(n,[n, n-1,n+n])=[-2 1 1]; end if ismember(nm-n+1,nan_list(:,1)) fda(nm-n+1,[nm-n+1,nm-n+2,nm-n])=[-2 1 1]; end if ismember(nm,nan_list(:,1)) fda(nm,[nm,nm-1,nm-n])=[-2 1 1]; end % eliminate knowns rhs=-fda(:,known_list)*A(known_list); % and solve... B=A; k=nan_list(:,1); B(k)=fda(k,k)\rhs(k); end case 3 % The same as method == 0, except uses del^4 as the % interpolating operator. % del^4 template of neighbors talks_to = [-2 0;-1 -1;-1 0;-1 1;0 -2;0 -1; ... 0 1;0 2;1 -1;1 0;1 1;2 0]; neighbors_list=identify_neighbors(n,m,nan_list,talks_to); % list of all nodes we have identified all_list=[nan_list;neighbors_list]; % generate sparse array with del^4, but only % for those nodes which have a row & column index % >= 3 or <= n-2 L = find( (all_list(:,2) >= 3) & ... (all_list(:,2) <= (n-2)) & ... (all_list(:,3) >= 3) & ... (all_list(:,3) <= (m-2))); nl=length(L); if nl>0 % do the entire template at once fda=sparse(repmat(all_list(L,1),1,13), ... repmat(all_list(L,1),1,13) + ... repmat([-2*n,-n-1,-n,-n+1,-2,-1,0,1,2,n-1,n,n+1,2*n],nl,1), ... repmat([1 2 -8 2 1 -8 20 -8 1 2 -8 2 1],nl,1),nm,nm); else fda=spalloc(n*m,n*m,size(all_list,1)*5); end % on the boundaries, reduce the order around the edges L = find((((all_list(:,2) == 2) | ... (all_list(:,2) == (n-1))) & ... (all_list(:,3) >= 2) & ... (all_list(:,3) <= (m-1))) | ... (((all_list(:,3) == 2) | ... (all_list(:,3) == (m-1))) & ... (all_list(:,2) >= 2) & ... (all_list(:,2) <= (n-1)))); nl=length(L); if nl>0 fda=fda+sparse(repmat(all_list(L,1),1,5), ... repmat(all_list(L,1),1,5) + ... repmat([-n,-1,0,+1,n],nl,1), ... repmat([1 1 -4 1 1],nl,1),nm,nm); end L = find( ((all_list(:,2) == 1) | ... (all_list(:,2) == n)) & ... (all_list(:,3) >= 2) & ... (all_list(:,3) <= (m-1))); nl=length(L); if nl>0 fda=fda+sparse(repmat(all_list(L,1),1,3), ... repmat(all_list(L,1),1,3) + ... repmat([-n,0,n],nl,1), ... repmat([1 -2 1],nl,1),nm,nm); end L = find( ((all_list(:,3) == 1) | ... (all_list(:,3) == m)) & ... (all_list(:,2) >= 2) & ... (all_list(:,2) <= (n-1))); nl=length(L); if nl>0 fda=fda+sparse(repmat(all_list(L,1),1,3), ... repmat(all_list(L,1),1,3) + ... repmat([-1,0,1],nl,1), ... repmat([1 -2 1],nl,1),nm,nm); end % eliminate knowns rhs=-fda(:,known_list)*A(known_list); k=find(any(fda(:,nan_list(:,1)),2)); % and solve... B=A; B(nan_list(:,1))=fda(k,nan_list(:,1))\rhs(k); case 4 % Spring analogy % interpolating operator. % list of all springs between a node and a horizontal % or vertical neighbor hv_list=[-1 -1 0;1 1 0;-n 0 -1;n 0 1]; hv_springs=[]; for i=1:4 hvs=nan_list+repmat(hv_list(i,:),nan_count,1); k=(hvs(:,2)>=1) & (hvs(:,2)<=n) & (hvs(:,3)>=1) & (hvs(:,3)<=m); hv_springs=[hv_springs;[nan_list(k,1),hvs(k,1)]]; end % delete replicate springs hv_springs=unique(sort(hv_springs,2),'rows'); % build sparse matrix of connections, springs % connecting diagonal neighbors are weaker than % the horizontal and vertical springs nhv=size(hv_springs,1); springs=sparse(repmat((1:nhv)',1,2),hv_springs, ... repmat([1 -1],nhv,1),nhv,nm); % eliminate knowns rhs=-springs(:,known_list)*A(known_list); % and solve... B=A; B(nan_list(:,1))=springs(:,nan_list(:,1))\rhs; case 5 % Average of 8 nearest neighbors % generate sparse array to average 8 nearest neighbors % for each nan element, be careful around edges fda=spalloc(n*m,n*m,size(nan_list,1)*9); % -1,-1 L = find((nan_list(:,2) > 1) & (nan_list(:,3) > 1)); nl=length(L); if nl>0 fda=fda+sparse(repmat(nan_list(L,1),1,2), ... repmat(nan_list(L,1),1,2)+repmat([-n-1, 0],nl,1), ... repmat([1 -1],nl,1),n*m,n*m); end % 0,-1 L = find(nan_list(:,3) > 1); nl=length(L); if nl>0 fda=fda+sparse(repmat(nan_list(L,1),1,2), ... repmat(nan_list(L,1),1,2)+repmat([-n, 0],nl,1), ... repmat([1 -1],nl,1),n*m,n*m); end % +1,-1 L = find((nan_list(:,2) < n) & (nan_list(:,3) > 1)); nl=length(L); if nl>0 fda=fda+sparse(repmat(nan_list(L,1),1,2), ... repmat(nan_list(L,1),1,2)+repmat([-n+1, 0],nl,1), ... repmat([1 -1],nl,1),n*m,n*m); end % -1,0 L = find(nan_list(:,2) > 1); nl=length(L); if nl>0 fda=fda+sparse(repmat(nan_list(L,1),1,2), ... repmat(nan_list(L,1),1,2)+repmat([-1, 0],nl,1), ... repmat([1 -1],nl,1),n*m,n*m); end % +1,0 L = find(nan_list(:,2) < n); nl=length(L); if nl>0 fda=fda+sparse(repmat(nan_list(L,1),1,2), ... repmat(nan_list(L,1),1,2)+repmat([1, 0],nl,1), ... repmat([1 -1],nl,1),n*m,n*m); end % -1,+1 L = find((nan_list(:,2) > 1) & (nan_list(:,3) < m)); nl=length(L); if nl>0 fda=fda+sparse(repmat(nan_list(L,1),1,2), ... repmat(nan_list(L,1),1,2)+repmat([n-1, 0],nl,1), ... repmat([1 -1],nl,1),n*m,n*m); end % 0,+1 L = find(nan_list(:,3) < m); nl=length(L); if nl>0 fda=fda+sparse(repmat(nan_list(L,1),1,2), ... repmat(nan_list(L,1),1,2)+repmat([n, 0],nl,1), ... repmat([1 -1],nl,1),n*m,n*m); end % +1,+1 L = find((nan_list(:,2) < n) & (nan_list(:,3) < m)); nl=length(L); if nl>0 fda=fda+sparse(repmat(nan_list(L,1),1,2), ... repmat(nan_list(L,1),1,2)+repmat([n+1, 0],nl,1), ... repmat([1 -1],nl,1),n*m,n*m); end % eliminate knowns rhs=-fda(:,known_list)*A(known_list); % and solve... B=A; k=nan_list(:,1); B(k)=fda(k,k)\rhs(k); end % all done, make sure that B is the same shape as % A was when we came in. B=reshape(B,n,m); % ==================================================== % end of main function % ==================================================== % ==================================================== % begin subfunctions % ==================================================== function neighbors_list=identify_neighbors(n,m,nan_list,talks_to) % identify_neighbors: identifies all the neighbors of % those nodes in nan_list, not including the nans % themselves % % arguments (input): % n,m - scalar - [n,m]=size(A), where A is the % array to be interpolated % nan_list - array - list of every nan element in A % nan_list(i,1) == linear index of i'th nan element % nan_list(i,2) == row index of i'th nan element % nan_list(i,3) == column index of i'th nan element % talks_to - px2 array - defines which nodes communicate % with each other, i.e., which nodes are neighbors. % % talks_to(i,1) - defines the offset in the row % dimension of a neighbor % talks_to(i,2) - defines the offset in the column % dimension of a neighbor % % For example, talks_to = [-1 0;0 -1;1 0;0 1] % means that each node talks only to its immediate % neighbors horizontally and vertically. % % arguments(output): % neighbors_list - array - list of all neighbors of % all the nodes in nan_list if ~isempty(nan_list) % use the definition of a neighbor in talks_to nan_count=size(nan_list,1); talk_count=size(talks_to,1); nn=zeros(nan_count*talk_count,2); j=[1,nan_count]; for i=1:talk_count nn(j(1):j(2),:)=nan_list(:,2:3) + ... repmat(talks_to(i,:),nan_count,1); j=j+nan_count; end % drop those nodes which fall outside the bounds of the % original array L = (nn(:,1)<1)|(nn(:,1)>n)|(nn(:,2)<1)|(nn(:,2)>m); nn(L,:)=[]; % form the same format 3 column array as nan_list neighbors_list=[sub2ind([n,m],nn(:,1),nn(:,2)),nn]; % delete replicates in the neighbors list neighbors_list=unique(neighbors_list,'rows'); % and delete those which are also in the list of NaNs. neighbors_list=setdiff(neighbors_list,nan_list,'rows'); else neighbors_list=[]; end \ No newline at end of file diff --git a/Functions/helper_functions_community/boundedlines/Inpaint_nans/inpaint_nans_bc.m b/Functions/helper_functions_community/boundedlines/Inpaint_nans/inpaint_nans_bc.m new file mode 100644 index 0000000..d6ae57e --- /dev/null +++ b/Functions/helper_functions_community/boundedlines/Inpaint_nans/inpaint_nans_bc.m @@ -0,0 +1 @@ +function B=inpaint_nans_bc(A,method,bcclass) % INPAINT_NANS_BC: in-paints over nans in an array, with spherical or toroidal boundary conditions % usage: B=inpaint_nsns_bc(A) % default method % usage: B=inpaint_nsns_bc(A,method) % specify method used % usage: B=inpaint_nsns_bc(A,method,bcclass) % specify class of boundary conditions applied % % Solves approximation to one of several pdes to % interpolate and extrapolate holes in an array. % Depending upon the boundary conditions specified, % the array will effectively be treated as if it lies % on either the surface of a sphere or a toroid. % % arguments (input): % A - nxm array with some NaNs to be filled in % % method - (OPTIONAL) scalar numeric flag - specifies % which approach (or physical metaphor to use % for the interpolation.) All methods are capable % of extrapolation, some are better than others. % There are also speed differences, as well as % accuracy differences for smooth surfaces. % % The methods employed here are a subset of the % methods of the original inpaint_nans. % % methods {0,1} use a simple plate metaphor. % method 4 uses a spring metaphor. % % method == 0 --> (DEFAULT) see method 1, but % this method does not build as large of a % linear system in the case of only a few % NaNs in a large array. % Extrapolation behavior is linear. % % method == 1 --> simple approach, applies del^2 % over the entire array, then drops those parts % of the array which do not have any contact with % NaNs. Uses a least squares approach, but it % does not modify known values. % In the case of small arrays, this method is % quite fast as it does very little extra work. % Extrapolation behavior is linear. % % method == 4 --> Uses a spring metaphor. Assumes % springs (with a nominal length of zero) % connect each node with every neighbor % (horizontally, vertically and diagonally) % Since each node tries to be like its neighbors, % extrapolation is as a constant function where % this is consistent with the neighboring nodes. % % DEFAULT: 0 % % bcclass - (OPTIONAL) character flag, indicating how % the array boundaries will be treated in the % inpainting operation. bcclass may be either % 'sphere' or 'toroid', or any simple contraction % of these words. % % bcclass = 'sphere' --> The first and last rows % of the array will be treated as if they are % at the North and South poles of a sphere. % Adjacent to those rows will be singular % phantom nodes at each pole. % % bcclass = 'toroid' --> The first and last rows % of the array will be treated as if they are % adjacent to ech other. As well, the first and % last columns will be adjacent to each other. % % DEFAULT: 'sphere' % % arguments (output): % B - nxm array with NaNs replaced % % % Example: % [x,y] = meshgrid(0:.01:1); % z0 = exp(x+y); % znan = z0; % znan(20:50,40:70) = NaN; % znan(30:90,5:10) = NaN; % znan(70:75,40:90) = NaN; % % z = inpaint_nans(znan); % % % See also: griddata, interp1 % % Author: John D'Errico % e-mail address: woodchips@rochester.rr.com % Release: 2 % Release date: 4/15/06 % I always need to know which elements are NaN, % and what size the array is for any method [n,m]=size(A); A=A(:); nm=n*m; k=isnan(A(:)); % list those nodes which are known, and which will % be interpolated nan_list=find(k); known_list=find(~k); % how many nans overall nan_count=length(nan_list); % convert NaN indices to (r,c) form % nan_list==find(k) are the unrolled (linear) indices % (row,column) form [nr,nc]=ind2sub([n,m],nan_list); % both forms of index in one array: % column 1 == unrolled index % column 2 == row index % column 3 == column index nan_list=[nan_list,nr,nc]; % supply default method if (nargin<2) || isempty(method) method = 0; elseif ~ismember(method,[0 1 4]) error('INPAINT_NANS_BC:improperargument', ... 'If supplied, method must be one of: {0,1,4}.') end % supply default value for bcclass if (nargin < 3) || isempty(bcclass) bcclass = 'sphere'; elseif ~ischar(bcclass) error('INPAINT_NANS_BC:improperargument', ... 'If supplied, bcclass must be ''sphere'' or ''toroid''') else % it was a character string valid = {'sphere' 'toroid'}; % check to see if it is valid [bcclass,errorclass] = validstring(arg,valid); if ~isempty(errorclass) error('INPAINT_NANS_BC:improperargument', ... 'If supplied, bcclass must be ''sphere'' or ''toroid''') end end % choice of methods switch method case 0 % The same as method == 1, except only work on those % elements which are NaN, or at least touch a NaN. % horizontal and vertical neighbors only talks_to = [-1 0;0 -1;1 0;0 1]; neighbors_list=identify_neighbors(n,m,nan_list,talks_to); % list of all nodes we have identified all_list=[nan_list;neighbors_list]; % generate sparse array with second partials on row % variable for each element in either list, but only % for those nodes which have a row index > 1 or < n L = find((all_list(:,2) > 1) & (all_list(:,2) < n)); nl=length(L); if nl>0 fda=sparse(repmat(all_list(L,1),1,3), ... repmat(all_list(L,1),1,3)+repmat([-1 0 1],nl,1), ... repmat([1 -2 1],nl,1),nm,nm); else fda=spalloc(n*m,n*m,size(all_list,1)*5); end % 2nd partials on column index L = find((all_list(:,3) > 1) & (all_list(:,3) < m)); nl=length(L); if nl>0 fda=fda+sparse(repmat(all_list(L,1),1,3), ... repmat(all_list(L,1),1,3)+repmat([-n 0 n],nl,1), ... repmat([1 -2 1],nl,1),nm,nm); end % eliminate knowns rhs=-fda(:,known_list)*A(known_list); k=find(any(fda(:,nan_list(:,1)),2)); % and solve... B=A; B(nan_list(:,1))=fda(k,nan_list(:,1))\rhs(k); case 1 % least squares approach with del^2. Build system % for every array element as an unknown, and then % eliminate those which are knowns. % Build sparse matrix approximating del^2 for % every element in A. % Compute finite difference for second partials % on row variable first [i,j]=ndgrid(1:n,1:m); ind=i(:)+(j(:)-1)*n; np=n*m; switch bcclass case 'sphere' % we need to have two phantom nodes at the poles np = np + 2; end fda=sparse(repmat(ind,1,3),[ind-1,ind,ind+1], ... repmat([1 -2 1],np,1),n*m,n*m); % now second partials on column variable [i,j]=ndgrid(1:n,2:(m-1)); ind=i(:)+(j(:)-1)*n; np=n*(m-2); fda=fda+sparse(repmat(ind,1,3),[ind-n,ind,ind+n], ... repmat([1 -2 1],np,1),nm,nm); % eliminate knowns rhs=-fda(:,known_list)*A(known_list); k=find(any(fda(:,nan_list),2)); % and solve... B=A; B(nan_list(:,1))=fda(k,nan_list(:,1))\rhs(k); case 4 % Spring analogy % interpolating operator. % list of all springs between a node and a horizontal % or vertical neighbor hv_list=[-1 -1 0;1 1 0;-n 0 -1;n 0 1]; hv_springs=[]; for i=1:4 hvs=nan_list+repmat(hv_list(i,:),nan_count,1); k=(hvs(:,2)>=1) & (hvs(:,2)<=n) & (hvs(:,3)>=1) & (hvs(:,3)<=m); hv_springs=[hv_springs;[nan_list(k,1),hvs(k,1)]]; end % delete replicate springs hv_springs=unique(sort(hv_springs,2),'rows'); % build sparse matrix of connections, springs % connecting diagonal neighbors are weaker than % the horizontal and vertical springs nhv=size(hv_springs,1); springs=sparse(repmat((1:nhv)',1,2),hv_springs, ... repmat([1 -1],nhv,1),nhv,nm); % eliminate knowns rhs=-springs(:,known_list)*A(known_list); % and solve... B=A; B(nan_list(:,1))=springs(:,nan_list(:,1))\rhs; end % all done, make sure that B is the same shape as % A was when we came in. B=reshape(B,n,m); end % mainline % ==================================================== % end of main function % ==================================================== % ==================================================== % begin subfunctions % ==================================================== function neighbors_list=identify_neighbors(n,m,nan_list,talks_to) % identify_neighbors: identifies all the neighbors of % those nodes in nan_list, not including the nans % themselves % % arguments (input): % n,m - scalar - [n,m]=size(A), where A is the % array to be interpolated % nan_list - array - list of every nan element in A % nan_list(i,1) == linear index of i'th nan element % nan_list(i,2) == row index of i'th nan element % nan_list(i,3) == column index of i'th nan element % talks_to - px2 array - defines which nodes communicate % with each other, i.e., which nodes are neighbors. % % talks_to(i,1) - defines the offset in the row % dimension of a neighbor % talks_to(i,2) - defines the offset in the column % dimension of a neighbor % % For example, talks_to = [-1 0;0 -1;1 0;0 1] % means that each node talks only to its immediate % neighbors horizontally and vertically. % % arguments(output): % neighbors_list - array - list of all neighbors of % all the nodes in nan_list if ~isempty(nan_list) % use the definition of a neighbor in talks_to nan_count=size(nan_list,1); talk_count=size(talks_to,1); nn=zeros(nan_count*talk_count,2); j=[1,nan_count]; for i=1:talk_count nn(j(1):j(2),:)=nan_list(:,2:3) + ... repmat(talks_to(i,:),nan_count,1); j=j+nan_count; end % form the same format 3 column array as nan_list neighbors_list=[sub2ind([n,m],nn(:,1),nn(:,2)),nn]; % delete replicates in the neighbors list neighbors_list=unique(neighbors_list,'rows'); % and delete those which are also in the list of NaNs. neighbors_list=setdiff(neighbors_list,nan_list,'rows'); else neighbors_list=[]; end end % function identify_neighbors function [str,errorclass] = validstring(arg,valid) % validstring: compares a string against a set of valid options % usage: [str,errorclass] = validstring(arg,valid) % % If a direct hit, or any unambiguous shortening is found, that % string is returned. Capitalization is ignored. % % arguments: (input) % arg - character string, to be tested against a list % of valid choices. Capitalization is ignored. % % valid - cellstring array of alternative choices % % Arguments: (output) % str - string - resulting choice resolved from the % list of valid arguments. If no unambiguous % choice can be resolved, then str will be empty. % % errorclass - string - A string argument that explains % the error. It will be one of the following % possibilities: % % '' --> No error. An unambiguous match for arg % was found among the choices. % % 'No match found' --> No match was found among % the choices provided in valid. % % 'Ambiguous argument' --> At least two ambiguous % matches were found among those provided % in valid. % % % Example: % valid = {'off' 'on' 'The sky is falling'} % % % See also: parse_pv_pairs, strmatch, strcmpi % % Author: John D'Errico % e-mail: woodchips@rochester.rr.com % Release: 1.0 % Release date: 3/25/2010 ind = strmatch(lower(arg),lower(valid)); if isempty(ind) % No hit found errorclass = 'No match found'; str = ''; elseif (length(ind) > 1) % Ambiguous arg, hitting more than one of the valid options errorclass = 'Ambiguous argument'; str = ''; return else errorclass = ''; str = valid{ind}; end end % function validstring \ No newline at end of file diff --git a/Functions/helper_functions_community/boundedlines/Inpaint_nans/inpaint_nans_demo.m b/Functions/helper_functions_community/boundedlines/Inpaint_nans/inpaint_nans_demo.m new file mode 100644 index 0000000..2163cc0 --- /dev/null +++ b/Functions/helper_functions_community/boundedlines/Inpaint_nans/inpaint_nans_demo.m @@ -0,0 +1,43 @@ +%% Surface Fit Artifact Removal + +%% Construct the Surface +[x,y] = meshgrid(0:.01:1); +z0 = exp(x+y); + +close all +figure +surf(z0) +title 'Original surface' + +znan = z0; +znan(20:50,40:70) = NaN; +znan(30:90,5:10) = NaN; +znan(70:75,40:90) = NaN; + +figure +surf(znan) +title 'Artifacts (large holes) in surface' + +%% In-paint Over NaNs +z = inpaint_nans(znan,3); +figure +surf(z) +title 'Inpainted surface' + +figure +surf(z-z0) +title 'Inpainting error surface (Note z-axis scale)' + +%% Comapre to GRIDDATA +k = isnan(znan); +zk = griddata(x(~k),y(~k),z(~k),x(k),y(k)); +zg = znan; +zg(k) = zk; + +figure +surf(zg) +title(['Griddata inpainting (',num2str(sum(isnan(zg(:)))),' NaNs remain)']) + +figure +surf(zg-z0) +title 'Griddata error surface' diff --git a/Functions/helper_functions_community/boundedlines/Inpaint_nans/license.txt b/Functions/helper_functions_community/boundedlines/Inpaint_nans/license.txt new file mode 100644 index 0000000..80c19d9 --- /dev/null +++ b/Functions/helper_functions_community/boundedlines/Inpaint_nans/license.txt @@ -0,0 +1,24 @@ +Copyright (c) 2009, John D'Errico +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/Functions/helper_functions_community/boundedlines/Inpaint_nans/monet_adresse.jpg b/Functions/helper_functions_community/boundedlines/Inpaint_nans/monet_adresse.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bceff7e9ca36a3aab4feed60951f1074f3a3b530 GIT binary patch literal 148268 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