pax_global_header 0000666 0000000 0000000 00000000064 13002224477 0014513 g ustar 00root root 0000000 0000000 52 comment=816f8754804cd45d8b41b3adf3ff9709a29cf173
matlab2tikz-1.1.0/ 0000775 0000000 0000000 00000000000 13002224477 0013736 5 ustar 00root root 0000000 0000000 matlab2tikz-1.1.0/.gitignore 0000664 0000000 0000000 00000000104 13002224477 0015721 0 ustar 00root root 0000000 0000000 *.sublime-workspace
*.tap
test/*.test.*
*.asv
*.m~
octave-workspace
matlab2tikz-1.1.0/.travis.yml 0000664 0000000 0000000 00000001300 13002224477 0016041 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/AUTHORS.md 0000664 0000000 0000000 00000005260 13002224477 0015410 0 ustar 00root root 0000000 0000000 # 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
* 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
* [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
* [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), [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.
matlab2tikz-1.1.0/CHANGELOG.md 0000664 0000000 0000000 00000036710 13002224477 0015556 0 ustar 00root root 0000000 0000000 # 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
matlab2tikz-1.1.0/CONTRIBUTING.md 0000664 0000000 0000000 00000012341 13002224477 0016170 0 ustar 00root root 0000000 0000000 # 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).
matlab2tikz-1.1.0/LICENSE.md 0000664 0000000 0000000 00000002435 13002224477 0015346 0 ustar 00root root 0000000 0000000 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.
matlab2tikz-1.1.0/README.md 0000664 0000000 0000000 00000007453 13002224477 0015226 0 ustar 00root root 0000000 0000000 **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!**
[](https://travis-ci.org/matlab2tikz/matlab2tikz) [](http://dx.doi.org/10.5281/zenodo.18605)

`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).
matlab2tikz-1.1.0/logos/ 0000775 0000000 0000000 00000000000 13002224477 0015061 5 ustar 00root root 0000000 0000000 matlab2tikz-1.1.0/logos/matlab2tikz.svg 0000664 0000000 0000000 00000013511 13002224477 0020027 0 ustar 00root root 0000000 0000000
matlab2tikz-1.1.0/matlab2tikz.sublime-project 0000664 0000000 0000000 00000002330 13002224477 0021206 0 ustar 00root root 0000000 0000000 {
"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":{}
}
}
matlab2tikz-1.1.0/runtests.sh 0000775 0000000 0000000 00000004731 13002224477 0016171 0 ustar 00root root 0000000 0000000 #!/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
matlab2tikz-1.1.0/src/ 0000775 0000000 0000000 00000000000 13002224477 0014525 5 ustar 00root root 0000000 0000000 matlab2tikz-1.1.0/src/cleanfigure.m 0000664 0000000 0000000 00000134052 13002224477 0017174 0 ustar 00root root 0000000 0000000 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
% =========================================================================
matlab2tikz-1.1.0/src/dev/ 0000775 0000000 0000000 00000000000 13002224477 0015303 5 ustar 00root root 0000000 0000000 matlab2tikz-1.1.0/src/dev/formatWhitespace.m 0000664 0000000 0000000 00000006057 13002224477 0020776 0 ustar 00root root 0000000 0000000 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 matlab2tikz-1.1.0/src/figure2dot.m 0000664 0000000 0000000 00000010646 13002224477 0016764 0 ustar 00root root 0000000 0000000 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
% ==============================================================================
matlab2tikz-1.1.0/src/m2tInputParser.m 0000664 0000000 0000000 00000017236 13002224477 0017613 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/src/matlab2tikz.m 0000664 0000000 0000000 00001042675 13002224477 0017146 0 ustar 00root root 0000000 0000000 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.
%
% 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
addComments(fid, 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 addComments(fid, comment)
% prints TeX comments to file stream |fid|
if ~isempty(comment)
newline = sprintf('\n');
newlineTeX = sprintf('\n%%');
fprintf(fid, '%% %s\n', strrep(comment, newline, newlineTeX));
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] = drawLine(m2t, child);
case 'patch'
[m2t, str] = drawPatch(m2t, child);
case 'image'
[m2t, str] = drawImage(m2t, child);
case {'hggroup', 'matlab.graphics.primitive.Group', ...
'scatter', 'bar', 'stair', 'stem' ,'errorbar', 'area', ...
'quiver','contour'}
[m2t, str] = drawHggroup(m2t, child);
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] = drawSurface(m2t, child);
case 'text'
[m2t, str] = drawVisibleText(m2t, child);
case 'rectangle'
[m2t, str] = drawRectangle(m2t, child);
case 'histogram'
[m2t, str] = drawHistogram(m2t, child);
case guitypes()
% don't do anything for GUI objects and their children
str = '';
case 'light'
% These objects are not supported and should not/cannot be
% supported by matlab2tikz or pgfplots.
case ''
% No children found for handle. (It has only a title and/or
% labels). Carrying on as if nothing happened
otherwise
error('matlab2tikz:handleAllChildren', ...
'I don''t know how to handle this object: %s\n', ...
get(child, 'Type'));
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 = getEnvironment();
switch env
case 'Octave'
% Make sure that m2t.legendHandles is a row vector.
for lhandle = m2t.legendHandles(:)'
ud = get(lhandle, 'UserData');
% Empty if no legend and multiple handles if plotyy
if ~isempty(ud) && any(axisHandle == ud.handle)
legendhandle = lhandle;
break
end
end
case 'MATLAB'
legendhandle = legend(axisHandle);
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'
% See set(hlegend, "deletefcn", {@deletelegend2, ca, [], [], t1, hplots}); in legend.m
delfun = get(legendHandle,'deletefcn');
entries = delfun{6};
% 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'
bool = ~isempty(getappdata(h, 'LegendPeerHandle'));
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');
% 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
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 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
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
% ==============================================================================
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 options = setAxisLimits(m2t, handle, axis, options)
% set the upper/lower limit of an axis
limits = get(handle, [upper(axis),'Lim']);
if isfinite(limits(1))
options = opts_add(options, [axis,'min'], sprintf(m2t.ff, limits(1)));
end
if isfinite(limits(2))
options = opts_add(options, [axis,'max'], sprintf(m2t.ff, limits(2)));
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)
% 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);
drawOptions = opts_new();
drawOptions = opts_add(drawOptions, 'color', xcolor);
drawOptions = opts_merge(drawOptions, lineOptions, markerOptions);
% 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);
yDeviation = getYDeviations(h);
if ~isempty(yDeviation)
data = [data, yDeviation];
end
% Check if any value is infinite/NaN. In that case, add appropriate option.
m2t = jumpAtUnboundCoords(m2t, data);
[m2t, dataString] = writePlotData(m2t, data, drawOptions);
[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)
% 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 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');
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, '', 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
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] = drawPatch(m2t, handle)
% 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);
% 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)
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 (m2t.args.imagesAsPng)
[m2t, str] = imageAsPNG(m2t, handle, xData, yData, cData);
else
[m2t, str] = imageAsTikZ(m2t, handle, xData, yData, cData);
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)
[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', 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));
% 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)
% 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
subString{j} = sprintf(['\t\\fill [%s] ', ...
'(axis cs:', m2t.ff,',', m2t.ff,') rectangle ', ...
'(axis cs:', m2t.ff,',',m2t.ff,');\n'], ...
xcolor{n-j+1,i}, ...
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)
if isHG2()
[m2t, str] = drawContourHG2(m2t, h);
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}] = drawText(m2t,child);
else
[m2t, str{ii}] = drawPatch(m2t,child);
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)
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);
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);
% 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)
% 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.
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;
% Add zero level fill
xdata = get(h,'XData');
ydata = get(h,'YData');
%FIXME: determine the contour at the zero level not just its bounding box
% See also: #721
zerolevel = [0, 4;
min(xdata(:)), min(ydata(:));
min(xdata(:)), max(ydata(:));
max(xdata(:)), max(ydata(:));
max(xdata(:)), min(ydata(:))];
cellcont = [zerolevel; 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);
% 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)
% 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
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] = drawBarseries(m2t, h);
case {'specgraph.stemseries', 'matlab.graphics.chart.primitive.Stem'}
% stem plots
[m2t, str] = drawStemSeries(m2t, h);
case {'specgraph.stairseries', 'matlab.graphics.chart.primitive.Stair'}
% stair plots
[m2t, str] = drawStairSeries(m2t, h);
case {'specgraph.areaseries', 'matlab.graphics.chart.primitive.Area'}
% scatter plots
[m2t,str] = drawAreaSeries(m2t, h);
case {'specgraph.quivergroup', 'matlab.graphics.chart.primitive.Quiver'}
% quiver arrows
[m2t, str] = drawQuiverGroup(m2t, h);
case {'specgraph.errorbarseries', 'matlab.graphics.chart.primitive.ErrorBar'}
% error bars
[m2t,str] = drawErrorBars(m2t, h);
case {'specgraph.scattergroup','matlab.graphics.chart.primitive.Scatter'}
% scatter plots
[m2t,str] = drawScatterPlot(m2t, h);
case {'specgraph.contourgroup', 'matlab.graphics.chart.primitive.Contour'}
[m2t,str] = drawContour(m2t, h);
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 = 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] = drawLine(m2t, h);
% Ellipse
case {'scribe.scribeellipse','matlab.graphics.shape.Ellipse'}
[m2t, str] = drawEllipse(m2t, h);
% 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] = drawText(m2t, h);
% Tetx 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] = drawTextarrow(m2t, h);
% Rectangle
case {'scribe.scriberect', 'matlab.graphics.shape.Rectangle'}
[m2t, str] = drawRectangle(m2t, h);
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] = drawSurface(m2t, h)
[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));
% 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)
% 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);
end
% ==============================================================================
function [m2t, str] = drawText(m2t, handle)
% 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.
% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% 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);
% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
% 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 ~isempty(alignment)
style = opts_add(style, alignment);
end
% Set 'align' option that is needed for multiline text
style = opts_add(style, 'align', HorizontalAlignment);
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)
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);
% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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) % 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)
% 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
% The actual printing.
[m2t, table, tableOptions] = makeTable(m2t, columns, data);
tableOptions = opts_merge(tableOptions, metaPart);
% 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)
% 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);
% 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)
% 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);
% 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
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
drawOptions = opts_add(drawOptions, 'bar width', formatDim(barWidth, ''));
% 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)
% 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);
% 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)
[m2t, str] = drawStemOrStairSeries_(m2t, h, 'ycomb');
% 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)
[m2t, str] = drawStemOrStairSeries_(m2t, h, 'const plot');
end
function [m2t, str] = drawStemOrStairSeries_(m2t, h, plotType)
% 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);
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)
% 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);
[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)
% Takes care of MATLAB's error bar plots.
% Octave's error bar plots are handled as well.
[m2t, str] = drawLine(m2t, h);
% 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 [yDeviations] = getYDeviations(h)
% Retrieves upper/lower uncertainty data
upDev = getOrDefault(h, 'UData', []);
loDev = getOrDefault(h, 'LData', []);
yDeviations = [upDev(:), loDev(:)];
end
% ==============================================================================
function [m2t, str] = drawEllipse(m2t, handle)
% 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);
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)
% 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);
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 = 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
table = lower(table); % convert NaN and Inf to lower case for TikZ
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 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
% ==============================================================================
matlab2tikz-1.1.0/src/private/ 0000775 0000000 0000000 00000000000 13002224477 0016177 5 ustar 00root root 0000000 0000000 matlab2tikz-1.1.0/src/private/errorUnknownEnvironment.m 0000664 0000000 0000000 00000000366 13002224477 0023320 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/src/private/getEnvironment.m 0000664 0000000 0000000 00000001234 13002224477 0021361 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/src/private/guitypes.m 0000664 0000000 0000000 00000000552 13002224477 0020230 0 ustar 00root root 0000000 0000000 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'};
end
matlab2tikz-1.1.0/src/private/isAxis3D.m 0000664 0000000 0000000 00000000263 13002224477 0020005 0 ustar 00root root 0000000 0000000 function bool = isAxis3D(axisHandle)
% Check if elevation is not orthogonal to xy plane
axisView = get(axisHandle,'view');
bool = ~ismember(axisView(2),[90,-90]);
end
matlab2tikz-1.1.0/src/private/isVersionBelow.m 0000664 0000000 0000000 00000000673 13002224477 0021335 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/src/private/m2tUpdater.m 0000664 0000000 0000000 00000030625 13002224477 0020412 0 ustar 00root root 0000000 0000000 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
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
% =========================================================================
matlab2tikz-1.1.0/src/private/m2tstrjoin.m 0000664 0000000 0000000 00000002344 13002224477 0020473 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/src/private/versionArray.m 0000664 0000000 0000000 00000001046 13002224477 0021042 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/src/private/versionString.m 0000664 0000000 0000000 00000000334 13002224477 0021231 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/test/ 0000775 0000000 0000000 00000000000 13002224477 0014715 5 ustar 00root root 0000000 0000000 matlab2tikz-1.1.0/test/README.md 0000664 0000000 0000000 00000007743 13002224477 0016207 0 ustar 00root root 0000000 0000000 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.
matlab2tikz-1.1.0/test/codeReport.m 0000664 0000000 0000000 00000022727 13002224477 0017213 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/test/compareTimings.m 0000664 0000000 0000000 00000020132 13002224477 0020052 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/test/examples/ 0000775 0000000 0000000 00000000000 13002224477 0016533 5 ustar 00root root 0000000 0000000 matlab2tikz-1.1.0/test/examples/example_bar_plot.m 0000664 0000000 0000000 00000002421 13002224477 0022225 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/test/examples/example_quivers.m 0000664 0000000 0000000 00000004005 13002224477 0022121 0 ustar 00root root 0000000 0000000 %% 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.
matlab2tikz-1.1.0/test/makeLatexReport.m 0000664 0000000 0000000 00000022761 13002224477 0020212 0 ustar 00root root 0000000 0000000 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
% ==============================================================================
matlab2tikz-1.1.0/test/makeTapReport.m 0000664 0000000 0000000 00000004314 13002224477 0017653 0 ustar 00root root 0000000 0000000 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
% ==============================================================================
matlab2tikz-1.1.0/test/makeTravisReport.m 0000664 0000000 0000000 00000032645 13002224477 0020407 0 ustar 00root root 0000000 0000000 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
% ==============================================================================
matlab2tikz-1.1.0/test/output/ 0000775 0000000 0000000 00000000000 13002224477 0016255 5 ustar 00root root 0000000 0000000 matlab2tikz-1.1.0/test/output/.gitignore 0000664 0000000 0000000 00000000222 13002224477 0020241 0 ustar 00root root 0000000 0000000 # This is a directory for testing output. Nothing in here should ever
# be committed, except the .gitignore to enforce all of this.
*
!.gitignore
matlab2tikz-1.1.0/test/private/ 0000775 0000000 0000000 00000000000 13002224477 0016367 5 ustar 00root root 0000000 0000000 matlab2tikz-1.1.0/test/private/OSVersion.m 0000664 0000000 0000000 00000002377 13002224477 0020445 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/test/private/StreamMaker.m 0000664 0000000 0000000 00000004525 13002224477 0020766 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/test/private/VersionControlIdentifier.m 0000664 0000000 0000000 00000003624 13002224477 0023543 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/test/private/calculateMD5Hash.m 0000664 0000000 0000000 00000002465 13002224477 0021623 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/test/private/cleanFiles.m 0000664 0000000 0000000 00000001302 13002224477 0020606 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/test/private/countNumberOfErrors.m 0000664 0000000 0000000 00000000222 13002224477 0022524 0 ustar 00root root 0000000 0000000 function nErrors = countNumberOfErrors(status)
% counts the number of errors in a status cell array
nErrors = sum(hasTestFailed(status));
end
matlab2tikz-1.1.0/test/private/emptyStage.m 0000664 0000000 0000000 00000000205 13002224477 0020664 0 ustar 00root root 0000000 0000000 function stage = emptyStage()
% constructs an empty (workflow) stage struct
stage = struct('message', '', 'error' , false);
end
matlab2tikz-1.1.0/test/private/emptyStatus.m 0000664 0000000 0000000 00000002077 13002224477 0021115 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/test/private/errorHandler.m 0000664 0000000 0000000 00000003071 13002224477 0021175 0 ustar 00root root 0000000 0000000 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
% ==============================================================================
matlab2tikz-1.1.0/test/private/errorHasOccurred.m 0000664 0000000 0000000 00000001052 13002224477 0022017 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/test/private/execute_hash_stage.m 0000664 0000000 0000000 00000002307 13002224477 0022377 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/test/private/execute_plot_stage.m 0000664 0000000 0000000 00000002752 13002224477 0022436 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/test/private/execute_save_stage.m 0000664 0000000 0000000 00000004714 13002224477 0022416 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/test/private/execute_tikz_stage.m 0000664 0000000 0000000 00000003061 13002224477 0022433 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/test/private/execute_type_stage.m 0000664 0000000 0000000 00000001064 13002224477 0022434 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/test/private/fillStruct.m 0000664 0000000 0000000 00000000507 13002224477 0020702 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/test/private/getEnvironment.m 0000664 0000000 0000000 00000001234 13002224477 0021551 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/test/private/getStagesFromStatus.m 0000664 0000000 0000000 00000000334 13002224477 0022523 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/test/private/hasTestFailed.m 0000664 0000000 0000000 00000000632 13002224477 0021266 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/test/private/hashTableName.m 0000664 0000000 0000000 00000004506 13002224477 0021246 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/test/private/initializeGlobalState.m 0000664 0000000 0000000 00000010040 13002224477 0023023 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/test/private/loadHashTable.m 0000664 0000000 0000000 00000001315 13002224477 0021240 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/test/private/m2troot.m 0000664 0000000 0000000 00000001651 13002224477 0020156 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/test/private/m2tstrjoin.m 0000664 0000000 0000000 00000001353 13002224477 0020662 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/test/private/restoreGlobalState.m 0000664 0000000 0000000 00000000507 13002224477 0022354 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/test/private/splitPassFailSkippedTests.m 0000664 0000000 0000000 00000000644 13002224477 0023672 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/test/private/splitUnreliableTests.m 0000664 0000000 0000000 00000000422 13002224477 0022724 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/test/private/testMatlab2tikz.m 0000664 0000000 0000000 00000012057 13002224477 0021636 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/test/runMatlab2TikzTests.m 0000775 0000000 0000000 00000002066 13002224477 0020776 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/test/saveHashTable.m 0000664 0000000 0000000 00000015161 13002224477 0017611 0 ustar 00root root 0000000 0000000 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
% ==============================================================================
matlab2tikz-1.1.0/test/suites/ 0000775 0000000 0000000 00000000000 13002224477 0016231 5 ustar 00root root 0000000 0000000 matlab2tikz-1.1.0/test/suites/ACID.MATLAB.8.3.md5 0000664 0000000 0000000 00000011726 13002224477 0020615 0 ustar 00root root 0000000 0000000 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 : c8876a76613b4588e4fb3883039d4d1f
errorBars2 : c0750114ad1821a37a833e378217ef5b
fill3plot : 801300e07b52eb484e67abe761b6a0a4
freqResponsePlot : d18c2174642550ca7373be8e207a43e7
herrorbarPlot : 62d64cf001ac8050493a95fc2090305c
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
matlab2tikz-1.1.0/test/suites/ACID.MATLAB.8.4.md5 0000664 0000000 0000000 00000011724 13002224477 0020614 0 ustar 00root root 0000000 0000000 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 : c8876a76613b4588e4fb3883039d4d1f
errorBars2 : c0750114ad1821a37a833e378217ef5b
fill3plot : 801300e07b52eb484e67abe761b6a0a4
freqResponsePlot : f307eac45a335d76278beb4209b7f335
herrorbarPlot : 62d64cf001ac8050493a95fc2090305c
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
matlab2tikz-1.1.0/test/suites/ACID.Octave.3.8.0.md5 0000664 0000000 0000000 00000010267 13002224477 0021233 0 ustar 00root root 0000000 0000000 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 : f9dde746fb03a6ee379114f60323a623
errorBars2 : 6fe024087a11d937035b71d54330dbe7
herrorbarPlot : 31f82d3a44f9c1f00f0fa60afe640322
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
matlab2tikz-1.1.0/test/suites/ACID.m 0000664 0000000 0000000 00000276730 13002224477 0017126 0 ustar 00root root 0000000 0000000 % =========================================================================
% *** 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',4);
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
% =========================================================================
matlab2tikz-1.1.0/test/suites/issues.m 0000664 0000000 0000000 00000002305 13002224477 0017722 0 ustar 00root root 0000000 0000000 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
% =========================================================================
matlab2tikz-1.1.0/test/suites/private/ 0000775 0000000 0000000 00000000000 13002224477 0017703 5 ustar 00root root 0000000 0000000 matlab2tikz-1.1.0/test/suites/private/getEnvironment.m 0000664 0000000 0000000 00000001234 13002224477 0023065 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/test/suites/private/herrorbar.m 0000664 0000000 0000000 00000010043 13002224477 0022045 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/test/suites/private/isEnvironment.m 0000664 0000000 0000000 00000003034 13002224477 0022721 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/test/suites/private/isMATLAB.m 0000664 0000000 0000000 00000000275 13002224477 0021361 0 ustar 00root root 0000000 0000000 function bool = isMATLAB(varargin)
%ISMATLAB Determines whether (a certain) version of MATLAB is being used
% See also: isEnvironment, isOctave
bool = isEnvironment('MATLAB', varargin{:});
matlab2tikz-1.1.0/test/suites/private/isOctave.m 0000664 0000000 0000000 00000000277 13002224477 0021644 0 ustar 00root root 0000000 0000000 function bool = isOctave(varargin)
%ISOCTAVE Determines whether (a certain) version of Octave is being used
%
% See also: isEnvironment, isMATLAB
bool = isEnvironment('Octave', varargin{:});
matlab2tikz-1.1.0/test/suites/private/isVersionBelow.m 0000664 0000000 0000000 00000002564 13002224477 0023042 0 ustar 00root root 0000000 0000000 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
% ==============================================================================
matlab2tikz-1.1.0/test/suites/private/versionCompare.m 0000664 0000000 0000000 00000001337 13002224477 0023061 0 ustar 00root root 0000000 0000000 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
matlab2tikz-1.1.0/test/suites/testPatches.m 0000664 0000000 0000000 00000007362 13002224477 0020706 0 ustar 00root root 0000000 0000000 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
% =========================================================================
matlab2tikz-1.1.0/test/suites/testSurfshader.m 0000664 0000000 0000000 00000006254 13002224477 0021424 0 ustar 00root root 0000000 0000000 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
% =========================================================================
matlab2tikz-1.1.0/test/template/ 0000775 0000000 0000000 00000000000 13002224477 0016530 5 ustar 00root root 0000000 0000000 matlab2tikz-1.1.0/test/template/.gitignore 0000664 0000000 0000000 00000000100 13002224477 0020507 0 ustar 00root root 0000000 0000000 # just ignore all generate files
acid.*
*.log
*.aux
*.pdf
*.tex
matlab2tikz-1.1.0/test/template/Makefile 0000664 0000000 0000000 00000001116 13002224477 0020167 0 ustar 00root root 0000000 0000000 # ./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
matlab2tikz-1.1.0/test/template/data/ 0000775 0000000 0000000 00000000000 13002224477 0017441 5 ustar 00root root 0000000 0000000 matlab2tikz-1.1.0/test/template/data/.gitignore 0000664 0000000 0000000 00000000114 13002224477 0021425 0 ustar 00root root 0000000 0000000 # just ignore all generated files
*.log
*.aux
*.pdf
*.eps
*.png
*.tex
*.tsv
matlab2tikz-1.1.0/test/template/data/converted/ 0000775 0000000 0000000 00000000000 13002224477 0021432 5 ustar 00root root 0000000 0000000 matlab2tikz-1.1.0/test/template/data/converted/Makefile 0000664 0000000 0000000 00000000603 13002224477 0023071 0 ustar 00root root 0000000 0000000 # ./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
matlab2tikz-1.1.0/test/template/data/reference/ 0000775 0000000 0000000 00000000000 13002224477 0021377 5 ustar 00root root 0000000 0000000 matlab2tikz-1.1.0/test/template/data/reference/Makefile 0000664 0000000 0000000 00000000427 13002224477 0023042 0 ustar 00root root 0000000 0000000 # ./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
matlab2tikz-1.1.0/test/testGraphical.m 0000664 0000000 0000000 00000002777 13002224477 0017702 0 ustar 00root root 0000000 0000000 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
% ==============================================================================
matlab2tikz-1.1.0/test/testHeadless.m 0000664 0000000 0000000 00000004243 13002224477 0017526 0 ustar 00root root 0000000 0000000 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
% ==============================================================================